mirror of
https://github.com/IgorTimofeev/MineOS.git
synced 2025-12-20 02:59:20 +01:00
1913 lines
59 KiB
Lua
Executable File
1913 lines
59 KiB
Lua
Executable File
|
||
local filesystem = require("Filesystem")
|
||
local screen = require("Screen")
|
||
local event = require("Event")
|
||
local keyboard = require("Keyboard")
|
||
local GUI = require("GUI")
|
||
local internet = require("Internet")
|
||
local system = require("System")
|
||
local paths = require("Paths")
|
||
local text = require("Text")
|
||
local number = require("Number")
|
||
|
||
------------------------------------------------------------
|
||
|
||
local config = {
|
||
leftTreeViewWidth = 23,
|
||
syntaxColorScheme = GUI.LUA_SYNTAX_COLOR_SCHEME,
|
||
scrollSpeed = 8,
|
||
cursorColor = 0x00A8FF,
|
||
cursorSymbol = "┃",
|
||
cursorBlinkDelay = 0.5,
|
||
doubleClickDelay = 0.4,
|
||
enableAutoBrackets = true,
|
||
syntaxHighlight = true,
|
||
enableAutocompletion = true,
|
||
linesToShowOpenProgress = 150,
|
||
}
|
||
|
||
local openBrackets = {
|
||
["{"] = "}",
|
||
["["] = "]",
|
||
["("] = ")",
|
||
["\""] = "\"",
|
||
["\'"] = "\'"
|
||
}
|
||
|
||
local closeBrackets = {
|
||
["}"] = "{",
|
||
["]"] = "[",
|
||
[")"] = "(",
|
||
["\""] = "\"",
|
||
["\'"] = "\'"
|
||
}
|
||
|
||
local luaKeywords = {
|
||
["do"] = true,
|
||
["local"] = true,
|
||
["return"] = true,
|
||
["while"] = true,
|
||
["repeat"] = true,
|
||
["until"] = true,
|
||
["for"] = true,
|
||
["in"] = true,
|
||
["if"] = true,
|
||
["then"] = true,
|
||
["else"] = true,
|
||
["elseif"] = true,
|
||
["end"] = true,
|
||
["function"] = true,
|
||
["true"] = true,
|
||
["false"] = true,
|
||
["nil"] = true,
|
||
["not"] = true,
|
||
["and"] = true,
|
||
["or" ] = true,
|
||
}
|
||
|
||
local lines = {""}
|
||
local cursorPositionSymbol = 1
|
||
local cursorPositionLine = 1
|
||
local cursorBlinkState = false
|
||
|
||
local saveContextMenuItem
|
||
local cursorUptime = computer.uptime()
|
||
local scriptCoroutine
|
||
local currentScriptDirectory = filesystem.path(system.getCurrentScript())
|
||
local configPath = paths.user.applicationData .. "MineCode IDE/Config9.cfg"
|
||
local localization = system.getLocalization(currentScriptDirectory .. "Localizations/")
|
||
local findStartFrom
|
||
local clipboard
|
||
local breakpointLines
|
||
local lastErrorLine
|
||
local autocompleteDatabase
|
||
local autoCompleteWordStart, autoCompleteWordEnd
|
||
local continue, showBreakpointMessage, showTip
|
||
|
||
------------------------------------------------------------
|
||
|
||
if filesystem.exists(configPath) then
|
||
config = filesystem.readTable(configPath)
|
||
end
|
||
|
||
local workspace, window, menu = system.addWindow(GUI.window(1, 1, 120, 30))
|
||
menu:removeChildren()
|
||
|
||
local codeView = window:addChild(GUI.codeView(1, 1, 1, 1, 1, 1, 1, {}, {}, GUI.LUA_SYNTAX_PATTERNS, config.syntaxColorScheme, config.syntaxHighlight, lines))
|
||
|
||
local function convertScreenCoordinatesToTextPosition(x, y)
|
||
return
|
||
x - codeView.codeAreaPosition + codeView.fromSymbol - 1,
|
||
y - codeView.y + codeView.fromLine
|
||
end
|
||
|
||
local overrideCodeViewDraw = codeView.draw
|
||
codeView.draw = function(...)
|
||
overrideCodeViewDraw(...)
|
||
|
||
if cursorBlinkState and GUI.focusedObject == window then
|
||
local x, y = codeView.codeAreaPosition + cursorPositionSymbol - codeView.fromSymbol + 1, codeView.y + cursorPositionLine - codeView.fromLine
|
||
if
|
||
x >= codeView.codeAreaPosition + 1 and
|
||
y >= codeView.y and
|
||
x <= codeView.codeAreaPosition + codeView.codeAreaWidth - 2 and
|
||
y <= codeView.y + codeView.height - (codeView.horizontalScrollBar.hidden and 1 or 2)
|
||
then
|
||
screen.drawText(x, y, config.cursorColor, config.cursorSymbol)
|
||
end
|
||
end
|
||
end
|
||
|
||
local function saveConfig()
|
||
filesystem.writeTable(configPath, config)
|
||
end
|
||
|
||
local topToolBar = window:addChild(GUI.container(1, 1, 1, 3))
|
||
local topToolBarPanel = topToolBar:addChild(GUI.panel(1, 1, 1, 3, 0xE1E1E1))
|
||
|
||
local topLayout = topToolBar:addChild(GUI.layout(1, 1, 1, 3, 1, 1))
|
||
topLayout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL)
|
||
topLayout:setSpacing(1, 1, 2)
|
||
topLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP)
|
||
|
||
local autocomplete = window:addChild(GUI.autoComplete(1, 1, 36, 7, 0xE1E1E1, 0xA5A5A5, 0x3C3C3C, 0x3C3C3C, 0xA5A5A5, 0xE1E1E1, 0xC3C3C3, 0x4B4B4B))
|
||
|
||
local addBreakpointButton = topLayout:addChild(GUI.adaptiveButton(1, 1, 3, 1, 0x878787, 0xE1E1E1, 0xD2D2D2, 0x4B4B4B, "x"))
|
||
|
||
local syntaxHighlightingButton = topLayout:addChild(GUI.adaptiveButton(1, 1, 3, 1, 0xD2D2D2, 0x4B4B4B, 0x696969, 0xE1E1E1, "◈"))
|
||
syntaxHighlightingButton.switchMode = true
|
||
syntaxHighlightingButton.pressed = codeView.syntaxHighlight
|
||
|
||
local runButton = topLayout:addChild(GUI.adaptiveButton(1, 1, 3, 1, 0x4B4B4B, 0xE1E1E1, 0xD2D2D2, 0x4B4B4B, "▷"))
|
||
|
||
local title = topLayout:addChild(GUI.object(1, 1, 1, 3))
|
||
local titleLines = {}
|
||
local titleDebugMode = false
|
||
title.eventHandler = nil
|
||
title.draw = function()
|
||
local sides = titleDebugMode and 0xCC4940 or 0x5A5A5A
|
||
screen.drawRectangle(title.x, title.y, 1, title.height, sides, 0x0, " ")
|
||
screen.drawRectangle(title.x + title.width - 1, title.y, 1, title.height, sides, 0x0, " ")
|
||
screen.drawRectangle(title.x + 1, title.y, title.width - 2, 3, titleDebugMode and 0x880000 or 0x3C3C3C, 0x969696, " ")
|
||
|
||
if titleDebugMode then
|
||
local text = lastErrorLine and localization.runtimeError or localization.debugging .. (_G.MineCodeIDEDebugInfo and _G.MineCodeIDEDebugInfo.line or "N/A")
|
||
screen.drawText(math.floor(title.x + title.width / 2 - unicode.len(text) / 2), title.y + 1, 0xE1E1E1, text)
|
||
else
|
||
for i = 1, #titleLines do
|
||
screen.drawText(math.floor(title.x + title.width / 2 - unicode.len(titleLines[i]) / 2), title.y + i - 1, 0x969696, titleLines[i])
|
||
end
|
||
end
|
||
end
|
||
|
||
local toggleLeftToolBarButton = topLayout:addChild(GUI.adaptiveButton(1, 1, 3, 1, 0xD2D2D2, 0x4B4B4B, 0x4B4B4B, 0xE1E1E1, "⇦"))
|
||
toggleLeftToolBarButton.switchMode, toggleLeftToolBarButton.pressed = true, true
|
||
|
||
local toggleBottomToolBarButton = topLayout:addChild(GUI.adaptiveButton(1, 1, 3, 1, 0xD2D2D2, 0x4B4B4B, 0x696969, 0xE1E1E1, "⇩"))
|
||
toggleBottomToolBarButton.switchMode, toggleBottomToolBarButton.pressed = true, false
|
||
|
||
local toggleTopToolBarButton = topLayout:addChild(GUI.adaptiveButton(1, 1, 3, 1, 0xD2D2D2, 0x4B4B4B, 0x878787, 0xE1E1E1, "⇧"))
|
||
toggleTopToolBarButton.switchMode, toggleTopToolBarButton.pressed = true, true
|
||
|
||
local actionButtons = window:addChild(GUI.actionButtons(2, 2, true))
|
||
actionButtons.close.onTouch = function()
|
||
window:remove()
|
||
end
|
||
actionButtons.maximize.onTouch = function()
|
||
window:maximize()
|
||
end
|
||
actionButtons.minimize.onTouch = function()
|
||
window:minimize()
|
||
end
|
||
|
||
local bottomToolBar = window:addChild(GUI.container(1, 1, 1, 3))
|
||
bottomToolBar.hidden = true
|
||
|
||
local caseSensitiveButton = bottomToolBar:addChild(GUI.adaptiveButton(1, 1, 2, 1, 0x3C3C3C, 0xE1E1E1, 0xB4B4B4, 0x2D2D2D, "Aa"))
|
||
caseSensitiveButton.switchMode = true
|
||
|
||
local searchInput = bottomToolBar:addChild(GUI.input(7, 1, 10, 3, 0xE1E1E1, 0x969696, 0x969696, 0xE1E1E1, 0x2D2D2D, "", localization.findSomeShit))
|
||
|
||
local searchButton = bottomToolBar:addChild(GUI.adaptiveButton(1, 1, 3, 1, 0x3C3C3C, 0xE1E1E1, 0xB4B4B4, 0x2D2D2D, localization.find))
|
||
|
||
local leftTreeView = window:addChild(GUI.filesystemTree(1, 1, config.leftTreeViewWidth, 1, 0xD2D2D2, 0x3C3C3C, 0x3C3C3C, 0x969696, 0x3C3C3C, 0xE1E1E1, 0xB4B4B4, 0xA5A5A5, 0xB4B4B4, 0x4B4B4B, GUI.IO_MODE_BOTH, GUI.IO_MODE_FILE))
|
||
|
||
local leftTreeViewResizer = window:addChild(GUI.resizer(1, 1, 3, 5, 0x696969, 0x0))
|
||
|
||
local function updateHighlights()
|
||
codeView.highlights = {}
|
||
|
||
if breakpointLines then
|
||
for i = 1, #breakpointLines do
|
||
codeView.highlights[breakpointLines[i]] = 0x990000
|
||
end
|
||
end
|
||
|
||
if lastErrorLine then
|
||
codeView.highlights[lastErrorLine] = 0xFF4940
|
||
end
|
||
end
|
||
|
||
local function updateTitle()
|
||
if not topToolBar.hidden then
|
||
titleLines[1] = text.limit(leftTreeView.selectedItem or "...", title.width - 4, "left")
|
||
titleLines[2] = text.limit(localization.cursor .. math.floor(cursorPositionLine) .. localization.line .. math.floor(cursorPositionSymbol) .. localization.symbol, title.width - 4)
|
||
|
||
if codeView.selections[1] then
|
||
local countOfSelectedLines, countOfSelectedSymbols = codeView.selections[1].to.line - codeView.selections[1].from.line + 1
|
||
|
||
if codeView.selections[1].from.line == codeView.selections[1].to.line then
|
||
countOfSelectedSymbols = unicode.len(unicode.sub(lines[codeView.selections[1].from.line], codeView.selections[1].from.symbol, codeView.selections[1].to.symbol))
|
||
else
|
||
countOfSelectedSymbols = unicode.len(unicode.sub(lines[codeView.selections[1].from.line], codeView.selections[1].from.symbol, -1))
|
||
|
||
for line = codeView.selections[1].from.line + 1, codeView.selections[1].to.line - 1 do
|
||
countOfSelectedSymbols = countOfSelectedSymbols + unicode.len(lines[line])
|
||
end
|
||
|
||
countOfSelectedSymbols = countOfSelectedSymbols + unicode.len(unicode.sub(lines[codeView.selections[1].to.line], 1, codeView.selections[1].to.symbol))
|
||
end
|
||
|
||
titleLines[3] = text.limit(localization.selection .. math.floor(countOfSelectedLines) .. localization.lines .. math.floor(countOfSelectedSymbols) .. localization.symbols, title.width - 4)
|
||
else
|
||
titleLines[3] = text.limit(localization.selection .. localization.none, title.width - 4)
|
||
end
|
||
end
|
||
end
|
||
|
||
local function tick(state)
|
||
cursorBlinkState = state
|
||
updateTitle()
|
||
workspace:draw()
|
||
|
||
cursorUptime = computer.uptime()
|
||
end
|
||
|
||
local function clearAutocompleteDatabaseFromLine(line)
|
||
for word in lines[line]:gmatch("[%a%d%_]+") do
|
||
if not word:match("^%d+$") then
|
||
autocompleteDatabase[word] = (autocompleteDatabase[word] or 0) - 1
|
||
if autocompleteDatabase[word] < 1 then
|
||
autocompleteDatabase[word] = nil
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
local function updateAutocompleteDatabaseFromLine(line)
|
||
for word in lines[line]:gmatch("[%a%d%_]+") do
|
||
if not word:match("^%d+$") then
|
||
autocompleteDatabase[word] = (autocompleteDatabase[word] or 0) + 1
|
||
end
|
||
end
|
||
end
|
||
|
||
local function updateAutocompleteDatabaseFromAllLines()
|
||
if config.enableAutocompletion then
|
||
autocompleteDatabase = {}
|
||
for line = 1, #lines do
|
||
updateAutocompleteDatabaseFromLine(line)
|
||
end
|
||
end
|
||
end
|
||
|
||
local function getautoCompleteWordStartAndEnding(fromSymbol)
|
||
local shittySymbolsRegexp, from, to = "[%s%c%p]"
|
||
|
||
for i = fromSymbol, 1, -1 do
|
||
if unicode.sub(lines[cursorPositionLine], i, i):match(shittySymbolsRegexp) then break end
|
||
from = i
|
||
end
|
||
|
||
for i = fromSymbol, unicode.len(lines[cursorPositionLine]) do
|
||
if unicode.sub(lines[cursorPositionLine], i, i):match(shittySymbolsRegexp) then break end
|
||
to = i
|
||
end
|
||
|
||
return from, to
|
||
end
|
||
|
||
local function aplhabeticalSort(t)
|
||
table.sort(t, function(a, b) return a[1] < b[1] end)
|
||
end
|
||
|
||
local function showAutocomplete()
|
||
if config.enableAutocompletion then
|
||
autoCompleteWordStart, autoCompleteWordEnd = getautoCompleteWordStartAndEnding(cursorPositionSymbol - 1)
|
||
if autoCompleteWordStart then
|
||
local word = unicode.sub(lines[cursorPositionLine], autoCompleteWordStart, autoCompleteWordEnd)
|
||
if not luaKeywords[word] then
|
||
autocomplete:match(autocompleteDatabase, word, true)
|
||
|
||
if #autocomplete.items > 0 then
|
||
autocomplete.fromItem, autocomplete.selectedItem = 1, 1
|
||
autocomplete.localX = codeView.localX + codeView.lineNumbersWidth + autoCompleteWordStart - codeView.fromSymbol
|
||
autocomplete.localY = codeView.localY + cursorPositionLine - codeView.fromLine + 1
|
||
autocomplete.hidden = false
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
local function toggleEnableAutocompleteDatabase()
|
||
config.enableAutocompletion = not config.enableAutocompletion
|
||
autocompleteDatabase = {}
|
||
saveConfig()
|
||
end
|
||
|
||
local function calculateSizes()
|
||
if leftTreeView.hidden then
|
||
codeView.localX, codeView.width = 1, window.width
|
||
bottomToolBar.localX, bottomToolBar.width = codeView.localX, codeView.width
|
||
else
|
||
codeView.localX, codeView.width = leftTreeView.width + 1, window.width - leftTreeView.width
|
||
bottomToolBar.localX, bottomToolBar.width = codeView.localX, codeView.width
|
||
end
|
||
|
||
if topToolBar.hidden then
|
||
leftTreeView.localY, leftTreeView.height = 1, window.height
|
||
codeView.localY, codeView.height = 1, window.height
|
||
else
|
||
leftTreeView.localY, leftTreeView.height = 4, window.height - 3
|
||
codeView.localY, codeView.height = 4, window.height - 3
|
||
end
|
||
|
||
if not bottomToolBar.hidden then
|
||
codeView.height = codeView.height - 3
|
||
end
|
||
|
||
leftTreeViewResizer.localX = leftTreeView.width
|
||
leftTreeViewResizer.localY = math.floor(leftTreeView.localY + leftTreeView.height / 2 - leftTreeViewResizer.height / 2)
|
||
|
||
bottomToolBar.localY = window.height - 2
|
||
searchButton.localX = bottomToolBar.width - searchButton.width + 1
|
||
searchInput.width = bottomToolBar.width - searchInput.localX - searchButton.width + 1
|
||
|
||
topToolBar.width, topToolBarPanel.width, topLayout.width = window.width, window.width, window.width
|
||
title.width = math.floor(topToolBar.width * 0.32)
|
||
|
||
-- topMenu.width = window.width
|
||
end
|
||
|
||
local function gotoLine(line)
|
||
codeView.fromLine = math.ceil(line - codeView.height / 2)
|
||
if codeView.fromLine < 1 then
|
||
codeView.fromLine = 1
|
||
elseif codeView.fromLine > #lines then
|
||
codeView.fromLine = #lines
|
||
end
|
||
end
|
||
|
||
local function clearSelection()
|
||
codeView.selections[1] = nil
|
||
end
|
||
|
||
local function clearBreakpoints()
|
||
breakpointLines = nil
|
||
updateHighlights()
|
||
end
|
||
|
||
local function addBreakpoint()
|
||
breakpointLines = breakpointLines or {}
|
||
|
||
local lineExists
|
||
for i = 1, #breakpointLines do
|
||
if breakpointLines[i] == cursorPositionLine then
|
||
lineExists = i
|
||
break
|
||
end
|
||
end
|
||
|
||
if lineExists then
|
||
table.remove(breakpointLines, lineExists)
|
||
else
|
||
table.insert(breakpointLines, cursorPositionLine)
|
||
end
|
||
|
||
if #breakpointLines > 0 then
|
||
table.sort(breakpointLines, function(a, b) return a < b end)
|
||
else
|
||
breakpointLines = nil
|
||
end
|
||
|
||
updateHighlights()
|
||
end
|
||
|
||
local function fixFromLineByCursorPosition()
|
||
local offset = codeView.horizontalScrollBar.hidden and 1 or 2
|
||
if codeView.fromLine > cursorPositionLine then
|
||
codeView.fromLine = cursorPositionLine
|
||
elseif codeView.fromLine + codeView.height - offset < cursorPositionLine then
|
||
codeView.fromLine = cursorPositionLine - codeView.height + offset
|
||
end
|
||
end
|
||
|
||
local function fixFromSymbolByCursorPosition()
|
||
if codeView.fromSymbol > cursorPositionSymbol then
|
||
codeView.fromSymbol = cursorPositionSymbol
|
||
elseif codeView.fromSymbol + codeView.codeAreaWidth - 3 < cursorPositionSymbol then
|
||
codeView.fromSymbol = cursorPositionSymbol - codeView.codeAreaWidth + 3
|
||
end
|
||
end
|
||
|
||
local function fixCursorPosition(symbol, line)
|
||
if line < 1 then
|
||
line = 1
|
||
elseif line > #lines then
|
||
line = #lines
|
||
end
|
||
|
||
local lineLength = unicode.len(lines[line])
|
||
if symbol < 1 or lineLength == 0 then
|
||
symbol = 1
|
||
elseif symbol > lineLength then
|
||
symbol = lineLength + 1
|
||
end
|
||
|
||
return math.floor(symbol), math.floor(line)
|
||
end
|
||
|
||
local function setCursorPosition(symbol, line)
|
||
cursorPositionSymbol, cursorPositionLine = fixCursorPosition(symbol, line)
|
||
fixFromLineByCursorPosition()
|
||
fixFromSymbolByCursorPosition()
|
||
autocomplete.hidden = true
|
||
end
|
||
|
||
local function setCursorPositionAndClearSelection(symbol, line)
|
||
setCursorPosition(symbol, line)
|
||
clearSelection()
|
||
end
|
||
|
||
local function moveCursor(symbolOffset, lineOffset, ignoreHidden)
|
||
if autocomplete.hidden or ignoreHidden then
|
||
if codeView.selections[1] then
|
||
if symbolOffset < 0 or lineOffset < 0 then
|
||
setCursorPositionAndClearSelection(codeView.selections[1].from.symbol, codeView.selections[1].from.line)
|
||
else
|
||
setCursorPositionAndClearSelection(codeView.selections[1].to.symbol, codeView.selections[1].to.line)
|
||
end
|
||
else
|
||
local newSymbol, newLine = cursorPositionSymbol + symbolOffset, cursorPositionLine + lineOffset
|
||
|
||
if symbolOffset < 0 and newSymbol < 1 then
|
||
newLine, newSymbol = newLine - 1, math.huge
|
||
elseif symbolOffset > 0 and newSymbol > unicode.len(lines[newLine] or "") + 1 then
|
||
newLine, newSymbol = newLine + 1, 1
|
||
end
|
||
|
||
setCursorPosition(newSymbol, newLine)
|
||
end
|
||
end
|
||
end
|
||
|
||
local function swapLinesAndMoveCursor (lineOffset)
|
||
if not autocomplete.hidden then
|
||
return
|
||
end
|
||
|
||
-- Проверяем, не заходит ли свап за рамки файла
|
||
if cursorPositionLine + lineOffset > #lines or cursorPositionLine + lineOffset < 1 then
|
||
return
|
||
end
|
||
|
||
-- Ебитесь сами со своим выделением, хехе
|
||
if codeView.selections[1] then
|
||
clearSelection ()
|
||
end
|
||
|
||
-- Свапаем строки
|
||
lines[cursorPositionLine], lines[cursorPositionLine + lineOffset] = lines[cursorPositionLine + lineOffset], lines[cursorPositionLine]
|
||
|
||
-- Ставим курсор на новую позицию
|
||
moveCursor (0, lineOffset, false)
|
||
end
|
||
|
||
local function setCursorPositionToHome()
|
||
setCursorPositionAndClearSelection(1, cursorPositionLine)
|
||
end
|
||
|
||
local function setCursorPositionToEnd()
|
||
setCursorPositionAndClearSelection(unicode.len(lines[cursorPositionLine]) + 1, cursorPositionLine)
|
||
end
|
||
|
||
local function scroll(direction, speed)
|
||
if direction == 1 then
|
||
if codeView.fromLine > speed then
|
||
codeView.fromLine = codeView.fromLine - speed
|
||
else
|
||
codeView.fromLine = 1
|
||
end
|
||
else
|
||
if codeView.fromLine < #lines - speed then
|
||
codeView.fromLine = codeView.fromLine + speed
|
||
else
|
||
codeView.fromLine = #lines
|
||
end
|
||
end
|
||
end
|
||
|
||
local function pageUp()
|
||
scroll(1, codeView.height - 2)
|
||
end
|
||
|
||
local function pageDown()
|
||
scroll(-1, codeView.height - 2)
|
||
end
|
||
|
||
local function selectWord()
|
||
local from, to = getautoCompleteWordStartAndEnding(cursorPositionSymbol)
|
||
if from and to then
|
||
codeView.selections[1] = {
|
||
from = {symbol = from, line = cursorPositionLine},
|
||
to = {symbol = to, line = cursorPositionLine},
|
||
}
|
||
cursorPositionSymbol = to
|
||
end
|
||
end
|
||
|
||
local function optimizeString(s)
|
||
return s:gsub("\t", string.rep(" ", codeView.indentationWidth)):gsub("\r\n", "\n")
|
||
end
|
||
|
||
local function addBackgroundContainer(title)
|
||
return GUI.addBackgroundContainer(workspace, true, true, title)
|
||
end
|
||
|
||
local function addInputFadeContainer(title, placeholder)
|
||
local container = addBackgroundContainer(title)
|
||
container.input = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xC3C3C3, 0x787878, 0x787878, 0xC3C3C3, 0x2D2D2D, "", placeholder))
|
||
|
||
return container
|
||
end
|
||
|
||
local function newFile()
|
||
autocompleteDatabase = {}
|
||
lines = {""}
|
||
codeView.lines = lines
|
||
codeView.maximumLineLength = 1
|
||
leftTreeView.selectedItem = nil
|
||
setCursorPositionAndClearSelection(1, 1)
|
||
clearBreakpoints()
|
||
updateTitle()
|
||
end
|
||
|
||
local function openFile(path)
|
||
local file, reason = filesystem.open(path, "r")
|
||
|
||
if file then
|
||
newFile()
|
||
|
||
leftTreeView.selectedItem = path
|
||
codeView.hidden = true
|
||
|
||
local container = window:addChild(GUI.container(codeView.localX, codeView.localY, codeView.width, codeView.height))
|
||
container:addChild(GUI.panel(1, 1, container.width, container.height, 0x1E1E1E))
|
||
|
||
local layout = container:addChild(GUI.layout(1, 1, container.width, container.height, 1, 1))
|
||
|
||
layout:addChild(GUI.label(1, 1, layout.width, 1, 0xD2D2D2, localization.openingFile .. " " .. path):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP))
|
||
local progressBar = layout:addChild(GUI.progressBar(1, 1, 36, 0x969696, 0x2D2D2D, 0x787878, 0, true, true, "", "%"))
|
||
|
||
local counter, currentSize, totalSize = 1, 0, filesystem.size(path)
|
||
for line in file:lines() do
|
||
counter, currentSize = counter + 1, currentSize + #line + 1
|
||
|
||
line = optimizeString(line)
|
||
table.insert(lines, line)
|
||
codeView.maximumLineLength = math.max(codeView.maximumLineLength, unicode.len(line))
|
||
|
||
if counter % config.linesToShowOpenProgress == 0 then
|
||
progressBar.value = math.floor(currentSize / totalSize * 100)
|
||
computer.pullSignal(0)
|
||
workspace:draw()
|
||
end
|
||
end
|
||
|
||
file:close()
|
||
|
||
if counter > config.linesToShowOpenProgress then
|
||
progressBar.value = 100
|
||
workspace:draw()
|
||
end
|
||
|
||
if #lines > 1 then
|
||
table.remove(lines, 1)
|
||
end
|
||
|
||
codeView.hidden = false
|
||
container:remove()
|
||
updateAutocompleteDatabaseFromAllLines()
|
||
updateTitle()
|
||
saveContextMenuItem.disabled = false
|
||
else
|
||
GUI.alert(reason)
|
||
end
|
||
end
|
||
|
||
local function saveFile(path)
|
||
filesystem.makeDirectory(filesystem.path(path))
|
||
local file, reason = filesystem.open(path, "w")
|
||
if file then
|
||
for line = 1, #lines do
|
||
file:write(lines[line], "\n")
|
||
end
|
||
file:close()
|
||
|
||
saveContextMenuItem.disabled = false
|
||
else
|
||
GUI.alert("Failed to open file for writing: " .. tostring(reason))
|
||
end
|
||
end
|
||
|
||
local function gotoLineWindow()
|
||
local container = addInputFadeContainer(localization.gotoLine, localization.lineNumber)
|
||
|
||
container.input.onInputFinished = function()
|
||
if container.input.text:match("%d+") then
|
||
gotoLine(tonumber(container.input.text))
|
||
container:remove()
|
||
workspace:draw()
|
||
end
|
||
end
|
||
|
||
workspace:draw()
|
||
end
|
||
|
||
local function openFileWindow()
|
||
local filesystemDialog = GUI.addFilesystemDialog(workspace, true, 50, math.floor(window.height * 0.8), "Open", "Cancel", "File name", "/")
|
||
filesystemDialog:setMode(GUI.IO_MODE_OPEN, GUI.IO_MODE_FILE)
|
||
filesystemDialog.onSubmit = function(path)
|
||
openFile(path)
|
||
workspace:draw()
|
||
end
|
||
filesystemDialog:show()
|
||
end
|
||
|
||
local function saveFileAsWindow()
|
||
local filesystemDialog = GUI.addFilesystemDialog(workspace, true, 50, math.floor(window.height * 0.8), "Save", "Cancel", "File name", "/")
|
||
filesystemDialog:setMode(GUI.IO_MODE_SAVE, GUI.IO_MODE_FILE)
|
||
filesystemDialog.onSubmit = function(path)
|
||
saveFile(path)
|
||
leftTreeView:updateFileList()
|
||
leftTreeView.selectedItem = (leftTreeView.workPath .. path):gsub("/+", "/")
|
||
|
||
updateTitle()
|
||
workspace:draw()
|
||
end
|
||
filesystemDialog:show()
|
||
end
|
||
|
||
local function saveFileWindow()
|
||
saveFile(leftTreeView.selectedItem)
|
||
leftTreeView:updateFileList()
|
||
end
|
||
|
||
local function splitStringIntoLines(s)
|
||
s = optimizeString(s)
|
||
|
||
local lines, index, maximumLineLength, starting = {s}, 1, 0
|
||
repeat
|
||
starting = lines[index]:find("\n")
|
||
if starting then
|
||
table.insert(lines, lines[index]:sub(starting + 1, -1))
|
||
lines[index] = lines[index]:sub(1, starting - 1)
|
||
maximumLineLength = math.max(maximumLineLength, unicode.len(lines[index]))
|
||
|
||
index = index + 1
|
||
end
|
||
until not starting
|
||
|
||
return lines, maximumLineLength
|
||
end
|
||
|
||
local function downloadFileFromWeb()
|
||
local container = addInputFadeContainer(localization.getFromWeb, localization.url)
|
||
|
||
container.input.onInputFinished = function()
|
||
if #container.input.text > 0 then
|
||
container.input:remove()
|
||
container.layout:addChild(GUI.text(1, 1, 0x969696, localization.downloading))
|
||
workspace:draw()
|
||
|
||
local result, reason = internet.request(container.input.text)
|
||
if result then
|
||
newFile()
|
||
lines, codeView.maximumLineLength = splitStringIntoLines(result)
|
||
codeView.lines = lines
|
||
updateAutocompleteDatabaseFromAllLines()
|
||
else
|
||
GUI.alert("Failed to connect to URL: " .. tostring(reason))
|
||
end
|
||
end
|
||
|
||
container:remove()
|
||
workspace:draw()
|
||
end
|
||
|
||
workspace:draw()
|
||
end
|
||
|
||
local function getVariables(codePart)
|
||
local variables = {}
|
||
-- Сначала мы проверяем участок кода на наличие комментариев
|
||
if
|
||
not codePart:match("^%-%-") and
|
||
not codePart:match("^[\t%s]+%-%-")
|
||
then
|
||
-- Затем заменяем все строковые куски в участке кода на "ничего", чтобы наш "прекрасный" парсер не искал переменных в строках
|
||
codePart = codePart:gsub("\"[^\"]+\"", "")
|
||
-- Потом разбиваем код на отдельные буквенно-цифровые слова, не забыв точечку с двоеточием
|
||
for word in codePart:gmatch("[%a%d%.%:%_]+") do
|
||
-- Далее проверяем, не совпадает ли это слово с одним из луа-шаблонов, то бишь, не является ли оно частью синтаксиса
|
||
if
|
||
not luaKeywords[word] and
|
||
-- Также проверяем, не число ли это в чистом виде
|
||
not word:match("^[%d%.]+$") and
|
||
not word:match("^0x%x+$") and
|
||
-- Или символ конкатенации, например
|
||
not word:match("^%.+$")
|
||
then
|
||
variables[word] = true
|
||
end
|
||
end
|
||
end
|
||
|
||
return variables
|
||
end
|
||
|
||
continue = function(...)
|
||
-- Готовим экран к запуску
|
||
local oldResolutionX, oldResolutionY = screen.getResolution()
|
||
|
||
-- Запускаем
|
||
_G.MineCodeIDEDebugInfo = nil
|
||
local coroutineResumeSuccess, coroutineResumeReason = coroutine.resume(scriptCoroutine, ...)
|
||
|
||
-- Анализируем результат запуска
|
||
if coroutineResumeSuccess then
|
||
if coroutine.status(scriptCoroutine) == "dead" then
|
||
screen.setResolution(oldResolutionX, oldResolutionY)
|
||
workspace:draw(true)
|
||
else
|
||
-- Тест на пидора, мало ли у чувака в проге тоже есть yield
|
||
if _G.MineCodeIDEDebugInfo then
|
||
screen.setResolution(oldResolutionX, oldResolutionY)
|
||
workspace:draw(true)
|
||
gotoLine(_G.MineCodeIDEDebugInfo.line)
|
||
showBreakpointMessage(_G.MineCodeIDEDebugInfo.variables)
|
||
end
|
||
end
|
||
else
|
||
screen.setResolution(oldResolutionX, oldResolutionY)
|
||
showTip(debug.traceback(scriptCoroutine, coroutineResumeReason), "%:(%d+)%: in main chunk", true, true)
|
||
end
|
||
end
|
||
|
||
local function run(...)
|
||
-- Инсертим брейкпоинты
|
||
if breakpointLines then
|
||
local offset = 0
|
||
for i = 1, #breakpointLines do
|
||
local variables = getVariables(lines[breakpointLines[i] + offset])
|
||
|
||
local breakpointMessage = "_G.MineCodeIDEDebugInfo = {variables = {"
|
||
for variable in pairs(variables) do
|
||
breakpointMessage = breakpointMessage .. "[\"" .. variable .. "\"] = type(" .. variable .. ") == 'string' and '\"' .. " .. variable .. " .. '\"' or tostring(" .. variable .. "), "
|
||
end
|
||
breakpointMessage = breakpointMessage .. "}, line = " .. breakpointLines[i] .. "}; coroutine.yield()"
|
||
|
||
table.insert(lines, breakpointLines[i] + offset, breakpointMessage)
|
||
offset = offset + 1
|
||
end
|
||
end
|
||
|
||
-- Лоадим кодыч
|
||
local loadSuccess, loadReason = load(table.concat(lines, "\n"), leftTreeView.selectedItem and ("=" .. leftTreeView.selectedItem))
|
||
|
||
-- Чистим дерьмо вилочкой, чистим
|
||
if breakpointLines then
|
||
for i = 1, #breakpointLines do
|
||
table.remove(lines, breakpointLines[i])
|
||
end
|
||
end
|
||
|
||
-- Запускаем кодыч
|
||
if loadSuccess then
|
||
scriptCoroutine = coroutine.create(loadSuccess)
|
||
continue(...)
|
||
else
|
||
showTip(loadReason, "%:(%d+)%:", true)
|
||
end
|
||
end
|
||
|
||
local function zalupa()
|
||
local container = window:addChild(GUI.container(1, 1, window.width, window.height))
|
||
|
||
container.close = function()
|
||
lastErrorLine = nil
|
||
titleDebugMode = false
|
||
updateHighlights()
|
||
|
||
container:remove()
|
||
workspace:draw()
|
||
end
|
||
|
||
container:addChild(GUI.object(1, 1, window.width, window.height)).eventHandler = function(workspace, object, e1)
|
||
if e1 == "touch" or e1 == "key_down" then
|
||
container.close()
|
||
end
|
||
end
|
||
|
||
return container
|
||
end
|
||
|
||
showTip = function(errorCode, matchCode, beep, force)
|
||
-- Извлекаем ошибочную строку текущего скрипта
|
||
lastErrorLine = tonumber(errorCode:match(matchCode))
|
||
if lastErrorLine then
|
||
-- Делаем поправку на количество брейкпоинтов в виде вставленных дебаг-строк
|
||
if breakpointLines then
|
||
local countOfBreakpointsBeforeLastErrorLine = 0
|
||
for i = 1, #breakpointLines do
|
||
if breakpointLines[i] < lastErrorLine then
|
||
countOfBreakpointsBeforeLastErrorLine = countOfBreakpointsBeforeLastErrorLine + 1
|
||
else
|
||
break
|
||
end
|
||
end
|
||
|
||
lastErrorLine = lastErrorLine - countOfBreakpointsBeforeLastErrorLine
|
||
end
|
||
|
||
gotoLine(lastErrorLine)
|
||
end
|
||
|
||
updateHighlights()
|
||
|
||
local container = zalupa()
|
||
local tip, tipLines = container:addChild(GUI.object(1, 1, 40))
|
||
|
||
tip.passScreenEvents = true
|
||
tip.draw = function()
|
||
screen.drawText(math.floor(tip.x + tip.width / 2 - 1), tip.y, 0xE1E1E1, "◢◣")
|
||
screen.drawRectangle(tip.x, tip.y + 1, tip.width, tip.height - 1, 0xE1E1E1, 0x2D2D2D, " ")
|
||
for i = 1, #tipLines do
|
||
screen.drawText(tip.x + 1, tip.y + i + 1, 0x2D2D2D, tipLines[i])
|
||
end
|
||
end
|
||
|
||
tipLines = text.wrap(errorCode, tip.width - 2)
|
||
tip.height = #tipLines + 3
|
||
|
||
local minX = codeView.localX + codeView.codeAreaPosition - codeView.x
|
||
local maxX = minX + codeView.width - tip.width - 5
|
||
|
||
tip.localX = math.min(maxX, math.max(minX + 1, number.round(minX + unicode.len(lines[lastErrorLine]) / 2 - tip.width / 2)))
|
||
tip.localY = codeView.localY + lastErrorLine - codeView.fromLine + 1
|
||
|
||
workspace:draw(force)
|
||
|
||
if beep then
|
||
computer.beep(1500, 0.08)
|
||
end
|
||
end
|
||
|
||
showBreakpointMessage = function(variables)
|
||
local lines = {}
|
||
|
||
for variable, value in pairs(variables) do
|
||
table.insert(lines, variable .. " = " .. value)
|
||
end
|
||
|
||
if #lines > 0 then
|
||
table.insert(lines, 1, {text = localization.variables, color = 0x0})
|
||
table.insert(lines, 2, " ")
|
||
else
|
||
table.insert(lines, 1, {text = localization.variablesNotAvailable, color = 0x0})
|
||
end
|
||
|
||
local container = zalupa()
|
||
local errorContainer = container:addChild(GUI.container(title.localX, topToolBar.hidden and 1 or 4, title.width, #lines + 3))
|
||
local panel = errorContainer:addChild(GUI.panel(1, 1, errorContainer.width, errorContainer.height, 0xE1E1E1))
|
||
local textBox = errorContainer:addChild(GUI.textBox(3, 2, errorContainer.width - 4, #lines, nil, 0x4B4B4B, lines, 1))
|
||
textBox:setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP)
|
||
|
||
local exitButton = errorContainer:addChild(GUI.button(1, errorContainer.height, math.floor(errorContainer.width / 2), 1, 0x3C3C3C, 0xC3C3C3, 0x2D2D2D, 0x878787, localization.finishDebug))
|
||
exitButton.animated = false
|
||
exitButton.onTouch = function()
|
||
scriptCoroutine = nil
|
||
container.close()
|
||
end
|
||
|
||
local continueButton = errorContainer:addChild(GUI.button(exitButton.width + 1, exitButton.localY, errorContainer.width - exitButton.width, 1, 0x4B4B4B, 0xC3C3C3, 0x2D2D2D, 0x878787, localization.continueDebug))
|
||
continueButton.animated = false
|
||
continueButton.onTouch = function()
|
||
container.close()
|
||
continue()
|
||
end
|
||
|
||
titleDebugMode = true
|
||
workspace:draw()
|
||
|
||
computer.beep(1500, 0.08)
|
||
end
|
||
|
||
local function launchWithArgumentsWindow()
|
||
local container = addInputFadeContainer(localization.launchWithArguments, localization.arguments)
|
||
|
||
container.input.onInputFinished = function()
|
||
local arguments = {}
|
||
container.input.text = container.input.text:gsub(",%s+", ",")
|
||
for argument in container.input.text:gmatch("[^,]+") do
|
||
table.insert(arguments, argument)
|
||
end
|
||
|
||
container:remove()
|
||
workspace:draw()
|
||
|
||
run(table.unpack(arguments))
|
||
end
|
||
|
||
workspace:draw()
|
||
end
|
||
|
||
local function deleteLine(line)
|
||
clearAutocompleteDatabaseFromLine(line)
|
||
|
||
if #lines > 1 then
|
||
table.remove(lines, line)
|
||
else
|
||
lines[1] = ""
|
||
end
|
||
|
||
setCursorPositionAndClearSelection(1, cursorPositionLine)
|
||
end
|
||
|
||
local function deleteSpecifiedData(fromSymbol, fromLine, toSymbol, toLine)
|
||
clearAutocompleteDatabaseFromLine(fromLine)
|
||
|
||
lines[fromLine] = unicode.sub(lines[fromLine], 1, fromSymbol - 1) .. unicode.sub(lines[toLine], toSymbol + 1, -1)
|
||
for line = fromLine + 1, toLine do
|
||
clearAutocompleteDatabaseFromLine(fromLine + 1)
|
||
|
||
table.remove(lines, fromLine + 1)
|
||
end
|
||
|
||
setCursorPositionAndClearSelection(fromSymbol, fromLine)
|
||
updateAutocompleteDatabaseFromLine(fromLine)
|
||
end
|
||
|
||
local function deleteSelectedData()
|
||
if codeView.selections[1] then
|
||
deleteSpecifiedData(
|
||
codeView.selections[1].from.symbol,
|
||
codeView.selections[1].from.line,
|
||
codeView.selections[1].to.symbol,
|
||
codeView.selections[1].to.line
|
||
)
|
||
|
||
clearSelection()
|
||
end
|
||
end
|
||
|
||
local function copy()
|
||
if codeView.selections[1] then
|
||
if codeView.selections[1].to.line == codeView.selections[1].from.line then
|
||
clipboard = { unicode.sub(lines[codeView.selections[1].from.line], codeView.selections[1].from.symbol, codeView.selections[1].to.symbol) }
|
||
else
|
||
clipboard = { unicode.sub(lines[codeView.selections[1].from.line], codeView.selections[1].from.symbol, -1) }
|
||
for line = codeView.selections[1].from.line + 1, codeView.selections[1].to.line - 1 do
|
||
table.insert(clipboard, lines[line])
|
||
end
|
||
table.insert(clipboard, unicode.sub(lines[codeView.selections[1].to.line], 1, codeView.selections[1].to.symbol))
|
||
end
|
||
end
|
||
end
|
||
|
||
local function cut()
|
||
if codeView.selections[1] then
|
||
copy()
|
||
deleteSelectedData()
|
||
end
|
||
end
|
||
|
||
local function paste(data, notTable)
|
||
if codeView.selections[1] then
|
||
deleteSelectedData()
|
||
end
|
||
|
||
local firstPart = unicode.sub(lines[cursorPositionLine], 1, cursorPositionSymbol - 1)
|
||
local secondPart = unicode.sub(lines[cursorPositionLine], cursorPositionSymbol, -1)
|
||
|
||
if notTable then
|
||
clearAutocompleteDatabaseFromLine(cursorPositionLine)
|
||
|
||
lines[cursorPositionLine] = firstPart .. data .. secondPart
|
||
setCursorPositionAndClearSelection(cursorPositionSymbol + unicode.len(data), cursorPositionLine)
|
||
|
||
updateAutocompleteDatabaseFromLine(cursorPositionLine)
|
||
else
|
||
if #data == 1 then
|
||
clearAutocompleteDatabaseFromLine(cursorPositionLine)
|
||
|
||
lines[cursorPositionLine] = firstPart .. data[1] .. secondPart
|
||
setCursorPositionAndClearSelection(unicode.len(firstPart .. data[1]) + 1, cursorPositionLine)
|
||
|
||
updateAutocompleteDatabaseFromLine(cursorPositionLine)
|
||
else
|
||
clearAutocompleteDatabaseFromLine(cursorPositionLine)
|
||
|
||
lines[cursorPositionLine] = firstPart .. data[1]
|
||
updateAutocompleteDatabaseFromLine(cursorPositionLine)
|
||
|
||
if #data > 2 then
|
||
for pasteLine = #data - 1, 2, -1 do
|
||
table.insert(lines, cursorPositionLine + 1, data[pasteLine])
|
||
|
||
updateAutocompleteDatabaseFromLine(cursorPositionLine + 1)
|
||
end
|
||
end
|
||
table.insert(lines, cursorPositionLine + #data - 1, data[#data] .. secondPart)
|
||
updateAutocompleteDatabaseFromLine(cursorPositionLine + #data - 1)
|
||
|
||
setCursorPositionAndClearSelection(unicode.len(data[#data]) + 1, cursorPositionLine + #data - 1)
|
||
end
|
||
end
|
||
end
|
||
|
||
local function selectAndPasteColor()
|
||
local startColor = 0xFF0000
|
||
if codeView.selections[1] and codeView.selections[1].from.line == codeView.selections[1].to.line then
|
||
startColor = tonumber(unicode.sub(lines[codeView.selections[1].from.line], codeView.selections[1].from.symbol, codeView.selections[1].to.symbol)) or startColor
|
||
end
|
||
|
||
local palette = window:addChild(GUI.palette(1, 1, startColor))
|
||
palette.localX, palette.localY = math.floor(window.width / 2 - palette.width / 2), math.floor(window.height / 2 - palette.height / 2)
|
||
|
||
palette.cancelButton.onTouch = function()
|
||
palette:remove()
|
||
workspace:draw()
|
||
end
|
||
|
||
palette.submitButton.onTouch = function()
|
||
paste(string.format("0x%06X", palette.color.integer), true)
|
||
palette.cancelButton.onTouch()
|
||
end
|
||
end
|
||
|
||
local function convertCase(method)
|
||
if codeView.selections[1] then
|
||
local from, to = codeView.selections[1].from, codeView.selections[1].to
|
||
if from.line == to.line then
|
||
lines[from.line] = unicode.sub(lines[from.line], 1, from.symbol - 1) .. unicode[method](unicode.sub(lines[from.line], from.symbol, to.symbol)) .. unicode.sub(lines[from.line], to.symbol + 1, -1)
|
||
else
|
||
lines[from.line] = unicode.sub(lines[from.line], 1, from.symbol - 1) .. unicode[method](unicode.sub(lines[from.line], from.symbol, -1))
|
||
lines[to.line] = unicode[method](unicode.sub(lines[to.line], 1, to.symbol)) .. unicode.sub(lines[to.line], to.symbol + 1, -1)
|
||
for line = from.line + 1, to.line - 1 do
|
||
lines[line] = unicode[method](lines[line])
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
local function pasteRegularChar(unicodeByte, char)
|
||
if not keyboard.isControl(unicodeByte) then
|
||
paste(char, true)
|
||
-- if char == " " then
|
||
-- updateAutocompleteDatabaseFromAllLines()
|
||
-- end
|
||
showAutocomplete()
|
||
end
|
||
end
|
||
|
||
local function pasteAutoBrackets(unicodeByte)
|
||
local char = unicode.char(unicodeByte)
|
||
local currentSymbol = unicode.sub(lines[cursorPositionLine], cursorPositionSymbol, cursorPositionSymbol)
|
||
|
||
-- Если у нас вообще врублен режим автоскобок, то чекаем их
|
||
if config.enableAutoBrackets then
|
||
-- Ситуация, когда курсор находится на закрывающей скобке, и нехуй ее еще раз вставлять
|
||
if closeBrackets[char] and currentSymbol == char then
|
||
deleteSelectedData()
|
||
setCursorPosition(cursorPositionSymbol + 1, cursorPositionLine)
|
||
-- Если нажата открывающая скобка
|
||
elseif openBrackets[char] then
|
||
-- А вот тут мы берем в скобочки уже выделенный текст
|
||
if codeView.selections[1] then
|
||
local firstPart = unicode.sub(lines[codeView.selections[1].from.line], 1, codeView.selections[1].from.symbol - 1)
|
||
local secondPart = unicode.sub(lines[codeView.selections[1].from.line], codeView.selections[1].from.symbol, -1)
|
||
lines[codeView.selections[1].from.line] = firstPart .. char .. secondPart
|
||
codeView.selections[1].from.symbol = codeView.selections[1].from.symbol + 1
|
||
|
||
if codeView.selections[1].to.line == codeView.selections[1].from.line then
|
||
codeView.selections[1].to.symbol = codeView.selections[1].to.symbol + 1
|
||
end
|
||
|
||
firstPart = unicode.sub(lines[codeView.selections[1].to.line], 1, codeView.selections[1].to.symbol)
|
||
secondPart = unicode.sub(lines[codeView.selections[1].to.line], codeView.selections[1].to.symbol + 1, -1)
|
||
lines[codeView.selections[1].to.line] = firstPart .. openBrackets[char] .. secondPart
|
||
cursorPositionSymbol = cursorPositionSymbol + 2
|
||
-- А тут мы делаем двойную автоскобку, если можем
|
||
elseif openBrackets[char] and not currentSymbol:match("[%a%d%_]") then
|
||
paste(char .. openBrackets[char], true)
|
||
setCursorPosition(cursorPositionSymbol - 1, cursorPositionLine)
|
||
cursorBlinkState = false
|
||
-- Ну, и если нет ни выделений, ни можем ебануть автоскобочку по регулярке
|
||
else
|
||
pasteRegularChar(unicodeByte, char)
|
||
end
|
||
-- Если мы вообще на скобку не нажимали
|
||
else
|
||
pasteRegularChar(unicodeByte, char)
|
||
end
|
||
-- Если оффнуты афтоскобки
|
||
else
|
||
pasteRegularChar(unicodeByte, char)
|
||
end
|
||
end
|
||
|
||
local function delete()
|
||
if codeView.selections[1] then
|
||
deleteSelectedData()
|
||
else
|
||
if cursorPositionSymbol < unicode.len(lines[cursorPositionLine]) + 1 then
|
||
deleteSpecifiedData(cursorPositionSymbol, cursorPositionLine, cursorPositionSymbol, cursorPositionLine)
|
||
else
|
||
if cursorPositionLine > 1 and lines[cursorPositionLine + 1] then
|
||
deleteSpecifiedData(unicode.len(lines[cursorPositionLine]) + 1, cursorPositionLine, 0, cursorPositionLine + 1)
|
||
end
|
||
end
|
||
|
||
showAutocomplete()
|
||
end
|
||
end
|
||
|
||
local function selectAll()
|
||
codeView.selections[1] = {
|
||
from = {
|
||
symbol = 1, line = 1
|
||
},
|
||
to = {
|
||
symbol = unicode.len(lines[#lines]), line = #lines
|
||
}
|
||
}
|
||
end
|
||
|
||
local function isLineCommented(line)
|
||
if lines[line] == "" or lines[line]:match("%-%-%s?") then return true end
|
||
end
|
||
|
||
local function commentLine(line)
|
||
lines[line] = "-- " .. lines[line]
|
||
end
|
||
|
||
local function uncommentLine(line)
|
||
local countOfReplaces
|
||
lines[line], countOfReplaces = lines[line]:gsub("%-%-%s?", "", 1)
|
||
return countOfReplaces
|
||
end
|
||
|
||
local function toggleComment()
|
||
if codeView.selections[1] then
|
||
local allLinesAreCommented = true
|
||
|
||
for line = codeView.selections[1].from.line, codeView.selections[1].to.line do
|
||
if not isLineCommented(line) then
|
||
allLinesAreCommented = false
|
||
break
|
||
end
|
||
end
|
||
|
||
for line = codeView.selections[1].from.line, codeView.selections[1].to.line do
|
||
if allLinesAreCommented then
|
||
uncommentLine(line)
|
||
else
|
||
commentLine(line)
|
||
end
|
||
end
|
||
|
||
local modifyer = 3
|
||
if allLinesAreCommented then modifyer = -modifyer end
|
||
setCursorPosition(cursorPositionSymbol + modifyer, cursorPositionLine)
|
||
codeView.selections[1].from.symbol, codeView.selections[1].to.symbol = codeView.selections[1].from.symbol + modifyer, codeView.selections[1].to.symbol + modifyer
|
||
else
|
||
if isLineCommented(cursorPositionLine) then
|
||
if uncommentLine(cursorPositionLine) > 0 then
|
||
setCursorPositionAndClearSelection(cursorPositionSymbol - 3, cursorPositionLine)
|
||
end
|
||
else
|
||
commentLine(cursorPositionLine)
|
||
setCursorPositionAndClearSelection(cursorPositionSymbol + 3, cursorPositionLine)
|
||
end
|
||
end
|
||
end
|
||
|
||
local function indentLine(line)
|
||
lines[line] = string.rep(" ", codeView.indentationWidth) .. lines[line]
|
||
end
|
||
|
||
local function unindentLine(line)
|
||
lines[line], countOfReplaces = string.gsub(lines[line], "^" .. string.rep("%s", codeView.indentationWidth), "")
|
||
return countOfReplaces
|
||
end
|
||
|
||
local function indentOrUnindent(isIndent)
|
||
if codeView.selections[1] then
|
||
local countOfReplacesInFirstLine, countOfReplacesInLastLine
|
||
|
||
for line = codeView.selections[1].from.line, codeView.selections[1].to.line do
|
||
if isIndent then
|
||
indentLine(line)
|
||
else
|
||
local countOfReplaces = unindentLine(line)
|
||
if line == codeView.selections[1].from.line then
|
||
countOfReplacesInFirstLine = countOfReplaces
|
||
elseif line == codeView.selections[1].to.line then
|
||
countOfReplacesInLastLine = countOfReplaces
|
||
end
|
||
end
|
||
end
|
||
|
||
if isIndent then
|
||
setCursorPosition(cursorPositionSymbol + codeView.indentationWidth, cursorPositionLine)
|
||
codeView.selections[1].from.symbol, codeView.selections[1].to.symbol = codeView.selections[1].from.symbol + codeView.indentationWidth, codeView.selections[1].to.symbol + codeView.indentationWidth
|
||
else
|
||
if countOfReplacesInFirstLine > 0 then
|
||
codeView.selections[1].from.symbol = codeView.selections[1].from.symbol - codeView.indentationWidth
|
||
if cursorPositionLine == codeView.selections[1].from.line then
|
||
setCursorPosition(cursorPositionSymbol - codeView.indentationWidth, cursorPositionLine)
|
||
end
|
||
end
|
||
|
||
if countOfReplacesInLastLine > 0 then
|
||
codeView.selections[1].to.symbol = codeView.selections[1].to.symbol - codeView.indentationWidth
|
||
if cursorPositionLine == codeView.selections[1].to.line then
|
||
setCursorPosition(cursorPositionSymbol - codeView.indentationWidth, cursorPositionLine)
|
||
end
|
||
end
|
||
end
|
||
else
|
||
if isIndent then
|
||
paste(string.rep(" ", codeView.indentationWidth), true)
|
||
else
|
||
if unindentLine(cursorPositionLine) > 0 then
|
||
setCursorPositionAndClearSelection(cursorPositionSymbol - codeView.indentationWidth, cursorPositionLine)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
local function find()
|
||
if not bottomToolBar.hidden and searchInput.text ~= "" then
|
||
findStartFrom = findStartFrom + 1
|
||
|
||
for line = findStartFrom, #lines do
|
||
local whereToFind, whatToFind = lines[line], searchInput.text
|
||
if not caseSensitiveButton.pressed then
|
||
whereToFind, whatToFind = unicode.lower(whereToFind), unicode.lower(whatToFind)
|
||
end
|
||
|
||
local success, starting, ending = pcall(text.unicodeFind, whereToFind, whatToFind)
|
||
if success then
|
||
if starting then
|
||
codeView.selections[1] = {
|
||
from = {symbol = starting, line = line},
|
||
to = {symbol = ending, line = line},
|
||
color = 0xCC9200
|
||
}
|
||
findStartFrom = line
|
||
gotoLine(line)
|
||
return
|
||
end
|
||
else
|
||
GUI.alert("Wrong searching regex")
|
||
end
|
||
end
|
||
|
||
findStartFrom = 0
|
||
end
|
||
end
|
||
|
||
local function findFromFirstDisplayedLine()
|
||
findStartFrom = codeView.fromLine
|
||
find()
|
||
end
|
||
|
||
local function toggleBottomToolBar()
|
||
bottomToolBar.hidden = not bottomToolBar.hidden
|
||
toggleBottomToolBarButton.pressed = not bottomToolBar.hidden
|
||
calculateSizes()
|
||
|
||
if not bottomToolBar.hidden then
|
||
workspace:draw()
|
||
findFromFirstDisplayedLine()
|
||
end
|
||
end
|
||
|
||
local function toggleTopToolBar()
|
||
topToolBar.hidden = not topToolBar.hidden
|
||
toggleTopToolBarButton.pressed = not topToolBar.hidden
|
||
calculateSizes()
|
||
end
|
||
|
||
local function createEditOrRightClickMenu(menu)
|
||
menu:addItem(localization.cut, not codeView.selections[1], "^X").onTouch = function()
|
||
cut()
|
||
end
|
||
|
||
menu:addItem(localization.copy, not codeView.selections[1], "^C").onTouch = function()
|
||
copy()
|
||
end
|
||
|
||
menu:addItem(localization.paste, not clipboard, "^V").onTouch = function()
|
||
paste(clipboard)
|
||
end
|
||
|
||
menu:addSeparator()
|
||
|
||
menu:addItem(localization.selectWord).onTouch = function()
|
||
selectWord()
|
||
end
|
||
|
||
menu:addItem(localization.selectAll, false, "^A").onTouch = function()
|
||
selectAll()
|
||
end
|
||
|
||
menu:addSeparator()
|
||
|
||
menu:addItem(localization.comment, false, "^/").onTouch = function()
|
||
toggleComment()
|
||
end
|
||
|
||
menu:addItem(localization.indent, false, "Tab").onTouch = function()
|
||
indentOrUnindent(true)
|
||
end
|
||
|
||
menu:addItem(localization.unindent, false, "⇧Tab").onTouch = function()
|
||
indentOrUnindent(false)
|
||
end
|
||
|
||
menu:addItem(localization.deleteLine, false, "^Del").onTouch = function()
|
||
deleteLine(cursorPositionLine)
|
||
end
|
||
|
||
menu:addItem(localization.selectAndPasteColor, false, "^⇧C").onTouch = function()
|
||
selectAndPasteColor()
|
||
end
|
||
|
||
local subMenu = menu:addSubMenuItem(localization.convertCase)
|
||
|
||
subMenu:addItem(localization.toUpperCase, false, "^▲").onTouch = function()
|
||
convertCase("upper")
|
||
end
|
||
|
||
subMenu:addItem(localization.toLowerCase, false, "^▼").onTouch = function()
|
||
convertCase("lower")
|
||
end
|
||
|
||
menu:addSeparator()
|
||
|
||
menu:addItem(localization.addBreakpoint, false, "F9").onTouch = function()
|
||
addBreakpoint()
|
||
workspace:draw()
|
||
end
|
||
|
||
menu:addItem(localization.clearBreakpoints, not breakpointLines, "^F9").onTouch = function()
|
||
clearBreakpoints()
|
||
end
|
||
end
|
||
|
||
local function checkScrollbar(y)
|
||
return codeView.horizontalScrollBar.hidden or y >= codeView.y + codeView.height - 1
|
||
end
|
||
|
||
local uptime = computer.uptime()
|
||
codeView.eventHandler = function(workspace, object, e1, e2, e3, e4, e5)
|
||
if e1 == "touch" then
|
||
e3, e4 = math.ceil(e3), math.ceil(e4)
|
||
|
||
if checkScrollbar(e4) then
|
||
return
|
||
end
|
||
|
||
if e5 == 1 then
|
||
createEditOrRightClickMenu(GUI.addContextMenu(workspace, e3, e4))
|
||
else
|
||
setCursorPositionAndClearSelection(convertScreenCoordinatesToTextPosition(e3, e4))
|
||
end
|
||
|
||
tick(true)
|
||
|
||
elseif e1 == "double_touch" then
|
||
selectWord()
|
||
tick(true)
|
||
|
||
elseif e1 == "drag" then
|
||
e3, e4 = math.ceil(e3), math.ceil(e4)
|
||
|
||
if checkScrollbar(e4) then
|
||
return
|
||
end
|
||
|
||
codeView.selections[1] = codeView.selections[1] or {from = {}, to = {}}
|
||
codeView.selections[1].from.symbol, codeView.selections[1].from.line = cursorPositionSymbol, cursorPositionLine
|
||
codeView.selections[1].to.symbol, codeView.selections[1].to.line = fixCursorPosition(convertScreenCoordinatesToTextPosition(e3, e4))
|
||
|
||
if codeView.selections[1].from.line > codeView.selections[1].to.line then
|
||
codeView.selections[1].from.line, codeView.selections[1].to.line = codeView.selections[1].to.line, codeView.selections[1].from.line
|
||
codeView.selections[1].from.symbol, codeView.selections[1].to.symbol = codeView.selections[1].to.symbol, codeView.selections[1].from.symbol
|
||
|
||
elseif codeView.selections[1].from.line == codeView.selections[1].to.line then
|
||
if codeView.selections[1].from.symbol > codeView.selections[1].to.symbol then
|
||
codeView.selections[1].from.symbol, codeView.selections[1].to.symbol = codeView.selections[1].to.symbol, codeView.selections[1].from.symbol
|
||
end
|
||
end
|
||
|
||
tick(true)
|
||
|
||
elseif e1 == "key_down" and GUI.focusedObject == window then
|
||
-- Ctrl or CMD
|
||
if keyboard.isControlDown() or keyboard.isCommandDown() then
|
||
-- Slash
|
||
if e4 == 53 then
|
||
toggleComment()
|
||
-- ]
|
||
elseif e4 == 27 then
|
||
config.enableAutoBrackets = not config.enableAutoBrackets
|
||
saveConfig()
|
||
-- I
|
||
elseif e4 == 23 then
|
||
toggleEnableAutocompleteDatabase()
|
||
-- A
|
||
elseif e4 == 30 then
|
||
selectAll()
|
||
-- C
|
||
elseif e4 == 46 then
|
||
-- Shift
|
||
if keyboard.isKeyDown(42) then
|
||
selectAndPasteColor()
|
||
else
|
||
copy()
|
||
end
|
||
-- V
|
||
elseif e4 == 47 and clipboard then
|
||
paste(clipboard)
|
||
-- X
|
||
elseif e4 == 45 then
|
||
if codeView.selections[1] then
|
||
cut()
|
||
else
|
||
deleteLine(cursorPositionLine)
|
||
end
|
||
-- N
|
||
elseif e4 == 49 then
|
||
newFile()
|
||
-- O
|
||
elseif e4 == 24 then
|
||
openFileWindow()
|
||
-- U
|
||
elseif e4 == 22 and component.isAvailable("internet") then
|
||
downloadFileFromWeb()
|
||
-- Arrow UP
|
||
elseif e4 == 200 then
|
||
convertCase("upper")
|
||
-- Arrow DOWN
|
||
elseif e4 == 208 then
|
||
convertCase("lower")
|
||
-- S
|
||
elseif e4 == 31 then
|
||
-- Shift
|
||
if leftTreeView.selectedItem and not keyboard.isKeyDown(42) then
|
||
saveFileWindow()
|
||
else
|
||
saveFileAsWindow()
|
||
end
|
||
-- F
|
||
elseif e4 == 33 then
|
||
toggleBottomToolBar()
|
||
-- G
|
||
elseif e4 == 34 then
|
||
find()
|
||
-- L
|
||
elseif e4 == 38 then
|
||
gotoLineWindow()
|
||
-- Backspace
|
||
elseif e4 == 14 then
|
||
deleteLine(cursorPositionLine)
|
||
-- Delete
|
||
elseif e4 == 211 then
|
||
deleteLine(cursorPositionLine)
|
||
-- F5
|
||
elseif e4 == 63 then
|
||
launchWithArgumentsWindow()
|
||
end
|
||
-- Arrows up, down, left, right
|
||
elseif e4 == 200 then
|
||
-- Alt
|
||
if keyboard.isKeyDown (56) then
|
||
-- Swap current line or selection with upper
|
||
swapLinesAndMoveCursor (-1)
|
||
else
|
||
moveCursor(0, -1)
|
||
end
|
||
elseif e4 == 208 then
|
||
-- Alt
|
||
if keyboard.isKeyDown (56) then
|
||
-- Swap current line or selection with lower
|
||
swapLinesAndMoveCursor (1)
|
||
else
|
||
moveCursor(0, 1)
|
||
end
|
||
elseif e4 == 203 then
|
||
moveCursor(-1, 0, true)
|
||
elseif e4 == 205 then
|
||
moveCursor(1, 0, true)
|
||
-- Tab
|
||
elseif e4 == 15 then
|
||
if keyboard.isKeyDown(42) then
|
||
indentOrUnindent(false)
|
||
else
|
||
indentOrUnindent(true)
|
||
end
|
||
-- Backspace
|
||
elseif e4 == 14 then
|
||
if codeView.selections[1] then
|
||
deleteSelectedData()
|
||
else
|
||
if cursorPositionSymbol > 1 then
|
||
-- Удаляем автоскобочки))0
|
||
if config.enableAutoBrackets and unicode.sub(lines[cursorPositionLine], cursorPositionSymbol, cursorPositionSymbol) == openBrackets[unicode.sub(lines[cursorPositionLine], cursorPositionSymbol - 1, cursorPositionSymbol - 1)] then
|
||
deleteSpecifiedData(cursorPositionSymbol - 1, cursorPositionLine, cursorPositionSymbol, cursorPositionLine)
|
||
else
|
||
-- Удаляем индентацию
|
||
local match = unicode.sub(lines[cursorPositionLine], 1, cursorPositionSymbol - 1):match("^(%s+)$")
|
||
if match and #match % codeView.indentationWidth == 0 then
|
||
deleteSpecifiedData(cursorPositionSymbol - codeView.indentationWidth, cursorPositionLine, cursorPositionSymbol - 1, cursorPositionLine)
|
||
-- Удаляем обычные символы
|
||
else
|
||
deleteSpecifiedData(cursorPositionSymbol - 1, cursorPositionLine, cursorPositionSymbol - 1, cursorPositionLine)
|
||
end
|
||
end
|
||
else
|
||
-- Удаление типа с обратным энтером
|
||
if cursorPositionLine > 1 then
|
||
deleteSpecifiedData(unicode.len(lines[cursorPositionLine - 1]) + 1, cursorPositionLine - 1, 0, cursorPositionLine)
|
||
end
|
||
end
|
||
|
||
showAutocomplete()
|
||
end
|
||
-- Enter
|
||
elseif e4 == 28 then
|
||
if autocomplete.hidden then
|
||
if codeView.selections[1] then
|
||
deleteSelectedData()
|
||
end
|
||
|
||
local secondPart = unicode.sub(lines[cursorPositionLine], cursorPositionSymbol, -1)
|
||
|
||
local match = lines[cursorPositionLine]:match("^(%s+)")
|
||
if match then
|
||
secondPart = match .. secondPart
|
||
end
|
||
|
||
lines[cursorPositionLine] = unicode.sub(lines[cursorPositionLine], 1, cursorPositionSymbol - 1)
|
||
table.insert(lines, cursorPositionLine + 1, secondPart)
|
||
|
||
setCursorPositionAndClearSelection(match and #match + 1 or 1, cursorPositionLine + 1)
|
||
else
|
||
autocomplete.hidden = true
|
||
end
|
||
-- F5
|
||
elseif e4 == 63 then
|
||
run()
|
||
-- F9
|
||
elseif e4 == 67 then
|
||
-- Shift
|
||
if keyboard.isKeyDown(42) then
|
||
clearBreakpoints()
|
||
else
|
||
addBreakpoint()
|
||
end
|
||
-- Home
|
||
elseif e4 == 199 then
|
||
setCursorPositionToHome()
|
||
-- End
|
||
elseif e4 == 207 then
|
||
setCursorPositionToEnd()
|
||
-- Page Up
|
||
elseif e4 == 201 then
|
||
pageUp()
|
||
-- Page Down
|
||
elseif e4 == 209 then
|
||
pageDown()
|
||
-- Delete
|
||
elseif e4 == 211 then
|
||
-- Shift
|
||
if keyboard.isKeyDown (42) then
|
||
deleteLine (cursorPositionLine)
|
||
else
|
||
delete()
|
||
end
|
||
else
|
||
pasteAutoBrackets(e3)
|
||
end
|
||
|
||
tick(true)
|
||
|
||
elseif e1 == "scroll" then
|
||
scroll(e5, config.scrollSpeed)
|
||
tick(cursorBlinkState)
|
||
|
||
elseif e1 == "clipboard" then
|
||
local lines = splitStringIntoLines(e3)
|
||
paste(lines)
|
||
|
||
tick(cursorBlinkState)
|
||
|
||
elseif not e1 and cursorUptime + config.cursorBlinkDelay < computer.uptime() then
|
||
tick(not cursorBlinkState)
|
||
end
|
||
end
|
||
|
||
leftTreeView.onItemSelected = function(path)
|
||
workspace:draw()
|
||
openFile(path)
|
||
workspace:draw()
|
||
end
|
||
|
||
local MineCodeContextMenu = menu:addContextMenuItem("MineCode", 0x0)
|
||
MineCodeContextMenu:addItem(localization.about).onTouch = function()
|
||
local container = addBackgroundContainer(localization.about)
|
||
|
||
local about = {
|
||
"MineCode IDE",
|
||
"Copyright © 2014-2018 ECS Inc.",
|
||
" ",
|
||
"Developers:",
|
||
" ",
|
||
"Timofeev Igor, vk.com/id7799889",
|
||
"Trifonov Gleb, vk.com/id88323331",
|
||
" ",
|
||
"Testers:",
|
||
" ",
|
||
"Semyonov Semyon, vk.com/id92656626",
|
||
"Prosin Mihail, vk.com/id75667079",
|
||
"Shestakov Timofey, vk.com/id113499693",
|
||
"Bogushevich Victoria, vk.com/id171497518",
|
||
"Vitvitskaya Yana, vk.com/id183425349",
|
||
"Golovanova Polina, vk.com/id226251826",
|
||
}
|
||
|
||
local textBox = container.layout:addChild(GUI.textBox(1, 1, 36, #about, nil, 0xB4B4B4, about, 1, 0, 0, true, false))
|
||
textBox:setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP)
|
||
textBox.eventHandler = nil
|
||
|
||
workspace:draw()
|
||
end
|
||
|
||
local fileContextMenu = menu:addContextMenuItem(localization.file)
|
||
fileContextMenu:addItem(localization.new, false, "^N").onTouch = function()
|
||
newFile()
|
||
workspace:draw()
|
||
end
|
||
|
||
fileContextMenu:addItem(localization.open, false, "^O").onTouch = function()
|
||
openFileWindow()
|
||
end
|
||
|
||
fileContextMenu:addItem(localization.getFromWeb, not component.isAvailable("internet"), "^U").onTouch = function()
|
||
downloadFileFromWeb()
|
||
end
|
||
|
||
|
||
fileContextMenu:addSeparator()
|
||
|
||
saveContextMenuItem = fileContextMenu:addItem(localization.save, not leftTreeView.selectedItem, "^S")
|
||
saveContextMenuItem.onTouch = function()
|
||
saveFileWindow()
|
||
end
|
||
|
||
fileContextMenu:addItem(localization.saveAs, false, "^⇧S").onTouch = function()
|
||
saveFileAsWindow()
|
||
end
|
||
|
||
fileContextMenu:addItem(localization.flashEEPROM, not component.isAvailable("eeprom")).onTouch = function()
|
||
local container = addBackgroundContainer(localization.flashEEPROM)
|
||
container.layout:addChild(GUI.label(1, 1, container.width, 1, 0x969696, localization.flashingEEPROM .. "...")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP)
|
||
workspace:draw()
|
||
|
||
component.get("eeprom").set(table.concat(lines, "\n"))
|
||
|
||
container:remove()
|
||
workspace:draw()
|
||
end
|
||
|
||
fileContextMenu:addSeparator()
|
||
|
||
fileContextMenu:addItem(localization.launchWithArguments, false, "^F5").onTouch = function()
|
||
launchWithArgumentsWindow()
|
||
end
|
||
|
||
local topMenuEdit = menu:addContextMenuItem(localization.edit)
|
||
createEditOrRightClickMenu(topMenuEdit)
|
||
|
||
local gotoContextMenu = menu:addContextMenuItem(localization.gotoCyka)
|
||
gotoContextMenu:addItem(localization.pageUp, false, "PgUp").onTouch = function()
|
||
pageUp()
|
||
end
|
||
|
||
gotoContextMenu:addItem(localization.pageDown, false, "PgDn").onTouch = function()
|
||
pageDown()
|
||
end
|
||
|
||
gotoContextMenu:addItem(localization.gotoStart, false, "Home").onTouch = function()
|
||
setCursorPositionToHome()
|
||
end
|
||
|
||
gotoContextMenu:addItem(localization.gotoEnd, false, "End").onTouch = function()
|
||
setCursorPositionToEnd()
|
||
end
|
||
|
||
gotoContextMenu:addSeparator()
|
||
|
||
gotoContextMenu:addItem(localization.gotoLine, false, "^L").onTouch = function()
|
||
gotoLineWindow()
|
||
end
|
||
|
||
local propertiesContextMenu = menu:addContextMenuItem(localization.properties)
|
||
propertiesContextMenu:addItem(localization.colorScheme).onTouch = function()
|
||
local container = GUI.addBackgroundContainer(workspace, true, false, localization.colorScheme)
|
||
|
||
local colorSelectorsCount, colorSelectorCountX = 0, 4; for key in pairs(config.syntaxColorScheme) do colorSelectorsCount = colorSelectorsCount + 1 end
|
||
local colorSelectorCountY = math.ceil(colorSelectorsCount / colorSelectorCountX)
|
||
local colorSelectorWidth, colorSelectorHeight, colorSelectorSpaceX, colorSelectorSpaceY = math.floor(container.width / colorSelectorCountX * 0.8), 3, 2, 1
|
||
|
||
local startX, y = math.floor(container.width / 2 - (colorSelectorCountX * (colorSelectorWidth + colorSelectorSpaceX) - colorSelectorSpaceX) / 2), math.floor(container.height / 2 - (colorSelectorCountY * (colorSelectorHeight + colorSelectorSpaceY) - colorSelectorSpaceY + 3) / 2)
|
||
container:addChild(GUI.label(1, y, container.width, 1, 0xFFFFFF, localization.colorScheme)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP); y = y + 3
|
||
local x, counter = startX, 1
|
||
|
||
local colors = {}
|
||
for key in pairs(config.syntaxColorScheme) do
|
||
table.insert(colors, {key})
|
||
end
|
||
|
||
aplhabeticalSort(colors)
|
||
|
||
for i = 1, #colors do
|
||
local colorSelector = container:addChild(GUI.colorSelector(x, y, colorSelectorWidth, colorSelectorHeight, config.syntaxColorScheme[colors[i][1]], colors[i][1]))
|
||
colorSelector.onColorSelected = function()
|
||
config.syntaxColorScheme[colors[i][1]] = colorSelector.color
|
||
saveConfig()
|
||
end
|
||
|
||
x, counter = x + colorSelectorWidth + colorSelectorSpaceX, counter + 1
|
||
if counter > colorSelectorCountX then
|
||
x, y, counter = startX, y + colorSelectorHeight + colorSelectorSpaceY, 1
|
||
end
|
||
end
|
||
|
||
workspace:draw()
|
||
end
|
||
|
||
propertiesContextMenu:addItem(localization.cursorProperties).onTouch = function()
|
||
local container = addBackgroundContainer(localization.cursorProperties)
|
||
|
||
local input = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xC3C3C3, 0x787878, 0x787878, 0xC3C3C3, 0x2D2D2D, config.cursorSymbol, localization.cursorSymbol))
|
||
input.onInputFinished = function()
|
||
if #input.text == 1 then
|
||
config.cursorSymbol = input.text
|
||
saveConfig()
|
||
end
|
||
end
|
||
|
||
local colorSelector = container.layout:addChild(GUI.colorSelector(1, 1, 36, 3, config.cursorColor, localization.cursorColor))
|
||
colorSelector.onColorSelected = function()
|
||
config.cursorColor = colorSelector.color
|
||
saveConfig()
|
||
end
|
||
|
||
local slider = container.layout:addChild(GUI.slider(1, 1, 36, 0xFFDB80, 0x000000, 0xFFDB40, 0xDDDDDD, 1, 1000, config.cursorBlinkDelay * 1000, false, localization.cursorBlinkDelay .. ": ", " ms"))
|
||
slider.onValueChanged = function()
|
||
config.cursorBlinkDelay = slider.value / 1000
|
||
saveConfig()
|
||
end
|
||
|
||
workspace:draw()
|
||
end
|
||
|
||
propertiesContextMenu:addItem(localization.toggleTopToolBar).onTouch = function()
|
||
toggleTopToolBar()
|
||
end
|
||
|
||
propertiesContextMenu:addSeparator()
|
||
|
||
propertiesContextMenu:addItem(localization.toggleSyntaxHighlight).onTouch = function()
|
||
syntaxHighlightingButton.pressed = not syntaxHighlightingButton.pressed
|
||
syntaxHighlightingButton.onTouch()
|
||
end
|
||
|
||
propertiesContextMenu:addItem(localization.toggleAutoBrackets, false, "^]").onTouch = function()
|
||
config.enableAutoBrackets = not config.enableAutoBrackets
|
||
saveConfig()
|
||
end
|
||
|
||
propertiesContextMenu:addItem(localization.toggleAutocompletion, false, "^I").onTouch = function()
|
||
toggleEnableAutocompleteDatabase()
|
||
end
|
||
|
||
leftTreeViewResizer.onResize = function(dragWidth, dragHeight)
|
||
leftTreeView.width = leftTreeView.width + dragWidth
|
||
calculateSizes()
|
||
end
|
||
|
||
leftTreeViewResizer.onResizeFinished = function()
|
||
config.leftTreeViewWidth = leftTreeView.width
|
||
saveConfig()
|
||
end
|
||
|
||
addBreakpointButton.onTouch = function()
|
||
addBreakpoint()
|
||
workspace:draw()
|
||
end
|
||
|
||
syntaxHighlightingButton.onTouch = function()
|
||
config.syntaxHighlight = not config.syntaxHighlight
|
||
codeView.syntaxHighlight = config.syntaxHighlight
|
||
workspace:draw()
|
||
saveConfig()
|
||
end
|
||
|
||
toggleLeftToolBarButton.onTouch = function()
|
||
leftTreeView.hidden = not toggleLeftToolBarButton.pressed
|
||
leftTreeViewResizer.hidden = leftTreeView.hidden
|
||
calculateSizes()
|
||
workspace:draw()
|
||
end
|
||
|
||
toggleBottomToolBarButton.onTouch = function()
|
||
bottomToolBar.hidden = not toggleBottomToolBarButton.pressed
|
||
calculateSizes()
|
||
workspace:draw()
|
||
end
|
||
|
||
toggleTopToolBarButton.onTouch = function()
|
||
topToolBar.hidden = not toggleTopToolBarButton.pressed
|
||
calculateSizes()
|
||
workspace:draw()
|
||
end
|
||
|
||
codeView.verticalScrollBar.onTouch = function()
|
||
codeView.fromLine = math.ceil(codeView.verticalScrollBar.value)
|
||
end
|
||
|
||
codeView.horizontalScrollBar.onTouch = function()
|
||
codeView.fromSymbol = math.ceil(codeView.horizontalScrollBar.value)
|
||
end
|
||
|
||
runButton.onTouch = function()
|
||
run()
|
||
end
|
||
|
||
autocomplete.onItemSelected = function(workspace, object, e1)
|
||
local firstPart = unicode.sub(lines[cursorPositionLine], 1, autoCompleteWordStart - 1)
|
||
local secondPart = unicode.sub(lines[cursorPositionLine], autoCompleteWordEnd + 1, -1)
|
||
local middle = firstPart .. autocomplete.items[autocomplete.selectedItem]
|
||
lines[cursorPositionLine] = middle .. secondPart
|
||
|
||
setCursorPositionAndClearSelection(unicode.len(middle) + 1, cursorPositionLine)
|
||
|
||
if e1 == "key_down" then
|
||
autocomplete.hidden = false
|
||
end
|
||
|
||
tick(true)
|
||
end
|
||
|
||
window.onResize = function(width, height)
|
||
calculateSizes()
|
||
workspace:draw()
|
||
end
|
||
|
||
searchInput.onInputFinished = findFromFirstDisplayedLine
|
||
caseSensitiveButton.onTouch = find
|
||
searchButton.onTouch = find
|
||
|
||
------------------------------------------------------------
|
||
|
||
autocomplete:moveToFront()
|
||
leftTreeView:updateFileList()
|
||
calculateSizes()
|
||
workspace:draw()
|
||
|
||
local initialPath = select(1, ...)
|
||
if initialPath and filesystem.exists(initialPath) then
|
||
openFile(initialPath)
|
||
else
|
||
newFile()
|
||
end
|
||
|
||
workspace:draw()
|