mirror of
https://github.com/IgorTimofeev/MineOS.git
synced 2025-12-20 11:09:21 +01:00
1860 lines
73 KiB
Lua
Executable File
1860 lines
73 KiB
Lua
Executable File
|
||
---------------------------------------------------- Libraries ----------------------------------------------------
|
||
|
||
-- "/MineOS/Applications/MineCode IDE.app/MineCode IDE.lua" -o /OS.lua
|
||
|
||
-- package.loaded.syntax = nil
|
||
-- package.loaded.ECSAPI = nil
|
||
-- package.loaded.GUI = nil
|
||
-- package.loaded.MineOSCore = nil
|
||
|
||
local args = {...}
|
||
require("advancedLua")
|
||
local computer = require("computer")
|
||
local component = require("component")
|
||
local fs = require("filesystem")
|
||
local buffer = require("doubleBuffering")
|
||
local event = require("event")
|
||
local syntax = require("syntax")
|
||
local unicode = require("unicode")
|
||
local web = require("web")
|
||
local image = require("image")
|
||
local keyboard = require("keyboard")
|
||
local palette = require("palette")
|
||
local GUI = require("GUI")
|
||
local MineOSPaths = require("MineOSPaths")
|
||
local MineOSCore = require("MineOSCore")
|
||
local MineOSInterface = require("MineOSInterface")
|
||
|
||
---------------------------------------------------- Constants ----------------------------------------------------
|
||
|
||
local about = {
|
||
"MineCode IDE",
|
||
"Copyright © 2014-2017 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 config = {
|
||
leftTreeViewWidth = 26,
|
||
syntaxColorScheme = syntax.colorScheme,
|
||
scrollSpeed = 8,
|
||
cursorColor = 0x00A8FF,
|
||
cursorSymbol = "┃",
|
||
cursorBlinkDelay = 0.5,
|
||
doubleClickDelay = 0.4,
|
||
screenResolution = {},
|
||
enableAutoBrackets = true,
|
||
highlightLuaSyntax = true,
|
||
enableAutocompletion = true,
|
||
}
|
||
config.screenResolution.width, config.screenResolution.height = component.gpu.getResolution()
|
||
|
||
local colors = {
|
||
topToolBar = 0xDDDDDD,
|
||
bottomToolBar = {
|
||
background = 0x3C3C3C,
|
||
buttons = 0x2D2D2D,
|
||
buttonsText = 0xFFFFFF,
|
||
},
|
||
topMenu = {
|
||
backgroundColor = 0xEEEEEE,
|
||
textColor = 0x444444,
|
||
backgroundPressedColor = 0x3366CC,
|
||
textPressedColor = 0xFFFFFF,
|
||
},
|
||
title = {
|
||
default = {
|
||
sides = 0x555555,
|
||
background = 0x3C3C3C,
|
||
text = 0xEEEEEE,
|
||
},
|
||
onError = {
|
||
sides = 0xCC4940,
|
||
background = 0x880000,
|
||
text = 0xEEEEEE,
|
||
},
|
||
},
|
||
highlights = {
|
||
onError = 0xFF4940,
|
||
onBreakpoint = 0x990000,
|
||
}
|
||
}
|
||
|
||
local possibleBrackets = {
|
||
openers = {
|
||
["{"] = "}",
|
||
["["] = "]",
|
||
["("] = ")",
|
||
["\""] = "\"",
|
||
["\'"] = "\'"
|
||
},
|
||
closers = {
|
||
["}"] = "{",
|
||
["]"] = "[",
|
||
[")"] = "(",
|
||
["\""] = "\"",
|
||
["\'"] = "\'"
|
||
}
|
||
}
|
||
|
||
local cursor = {
|
||
position = {
|
||
symbol = 1,
|
||
line = 1
|
||
},
|
||
blinkState = false
|
||
}
|
||
|
||
local scriptCoroutine
|
||
local resourcesPath = MineOSCore.getCurrentApplicationResourcesDirectory()
|
||
local configPath = MineOSPaths.applicationData .. "MineCode IDE/Config.cfg"
|
||
local localization = MineOSCore.getLocalization(resourcesPath .. "Localization/")
|
||
local findStartFrom
|
||
local clipboard
|
||
local breakpointLines
|
||
local lastErrorLine
|
||
local autocompleteDatabase
|
||
|
||
------------------------------------------------------------------------------------------------------------------
|
||
|
||
local function convertTextPositionToScreenCoordinates(symbol, line)
|
||
return
|
||
mainContainer.codeView.codeAreaPosition + symbol - mainContainer.codeView.fromSymbol + 1,
|
||
mainContainer.codeView.y + line - mainContainer.codeView.fromLine
|
||
end
|
||
|
||
local function convertScreenCoordinatesToTextPosition(x, y)
|
||
return x - mainContainer.codeView.codeAreaPosition + mainContainer.codeView.fromSymbol - 1, y - mainContainer.codeView.y + mainContainer.codeView.fromLine
|
||
end
|
||
|
||
------------------------------------------------------------------------------------------------------------------
|
||
|
||
local function saveConfig()
|
||
table.toFile(configPath, config)
|
||
end
|
||
|
||
local function loadConfig()
|
||
if fs.exists(configPath) then
|
||
config = table.fromFile(configPath)
|
||
syntax.colorScheme = config.syntaxColorScheme
|
||
else
|
||
saveConfig()
|
||
end
|
||
end
|
||
|
||
------------------------------------------------------------------------------------------------------------------
|
||
|
||
local function updateAutocompleteDatabaseFromString(str, value)
|
||
for word in str:gmatch("[%a%d%_]+") do
|
||
if not word:match("^%d+$") then
|
||
autocompleteDatabase[word] = value
|
||
end
|
||
end
|
||
end
|
||
|
||
local function updateAutocompleteDatabaseFromFile()
|
||
if config.enableAutocompletion then
|
||
autocompleteDatabase = {}
|
||
for line = 1, #mainContainer.codeView.lines do
|
||
updateAutocompleteDatabaseFromString(mainContainer.codeView.lines[line], true)
|
||
end
|
||
end
|
||
end
|
||
|
||
local function getCurrentWordStartingAndEnding(fromSymbol)
|
||
local shittySymbolsRegexp, from, to = "[%s%c%p]"
|
||
|
||
for i = fromSymbol, 1, -1 do
|
||
if unicode.sub(mainContainer.codeView.lines[cursor.position.line], i, i):match(shittySymbolsRegexp) then break end
|
||
from = i
|
||
end
|
||
|
||
for i = fromSymbol, unicode.len(mainContainer.codeView.lines[cursor.position.line]) do
|
||
if unicode.sub(mainContainer.codeView.lines[cursor.position.line], 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 getAutocompleteDatabaseMatches(stringToSearch)
|
||
local matches = {}
|
||
|
||
for word in pairs(autocompleteDatabase) do
|
||
if word ~= stringToSearch then
|
||
local match = word:match("^" .. stringToSearch)
|
||
if match then
|
||
table.insert(matches, { word, match })
|
||
end
|
||
end
|
||
end
|
||
|
||
aplhabeticalSort(matches)
|
||
return matches
|
||
end
|
||
|
||
local function hideAutocompleteWindow()
|
||
mainContainer.autocompleteWindow.hidden = true
|
||
end
|
||
|
||
local function showAutocompleteWindow()
|
||
if config.enableAutocompletion then
|
||
mainContainer.autocompleteWindow.currentWordStarting, mainContainer.autocompleteWindow.currentWordEnding = getCurrentWordStartingAndEnding(cursor.position.symbol - 1)
|
||
|
||
if mainContainer.autocompleteWindow.currentWordStarting then
|
||
mainContainer.autocompleteWindow.matches = getAutocompleteDatabaseMatches(
|
||
unicode.sub(
|
||
mainContainer.codeView.lines[cursor.position.line],
|
||
mainContainer.autocompleteWindow.currentWordStarting,
|
||
mainContainer.autocompleteWindow.currentWordEnding
|
||
)
|
||
)
|
||
|
||
if #mainContainer.autocompleteWindow.matches > 0 then
|
||
mainContainer.autocompleteWindow.fromMatch, mainContainer.autocompleteWindow.currentMatch = 1, 1
|
||
mainContainer.autocompleteWindow.hidden = false
|
||
else
|
||
hideAutocompleteWindow()
|
||
end
|
||
else
|
||
hideAutocompleteWindow()
|
||
end
|
||
end
|
||
end
|
||
|
||
local function toggleEnableAutocompleteDatabase()
|
||
config.enableAutocompletion = not config.enableAutocompletion
|
||
autocompleteDatabase = {}
|
||
saveConfig()
|
||
end
|
||
|
||
------------------------------------------------------------------------------------------------------------------
|
||
|
||
local function calculateSizes()
|
||
mainContainer.width, mainContainer.height = buffer.getResolution()
|
||
|
||
if mainContainer.leftTreeView.hidden then
|
||
mainContainer.codeView.localX, mainContainer.codeView.width = 1, mainContainer.width
|
||
mainContainer.bottomToolBar.localX, mainContainer.bottomToolBar.width = mainContainer.codeView.localX, mainContainer.codeView.width
|
||
else
|
||
mainContainer.codeView.localX, mainContainer.codeView.width = mainContainer.leftTreeView.width + 1, mainContainer.width - mainContainer.leftTreeView.width
|
||
mainContainer.bottomToolBar.localX, mainContainer.bottomToolBar.width = mainContainer.codeView.localX, mainContainer.codeView.width
|
||
end
|
||
|
||
if mainContainer.topToolBar.hidden then
|
||
mainContainer.leftTreeView.localY, mainContainer.leftTreeView.height = 2, mainContainer.height - 1
|
||
mainContainer.codeView.localY, mainContainer.codeView.height = 2, mainContainer.height - 1
|
||
mainContainer.errorContainer.localY = 2
|
||
else
|
||
mainContainer.leftTreeView.localY, mainContainer.leftTreeView.height = 5, mainContainer.height - 4
|
||
mainContainer.codeView.localY, mainContainer.codeView.height = 5, mainContainer.height - 4
|
||
mainContainer.errorContainer.localY = 5
|
||
end
|
||
|
||
if mainContainer.bottomToolBar.hidden then
|
||
|
||
else
|
||
mainContainer.codeView.height = mainContainer.codeView.height - 3
|
||
end
|
||
|
||
mainContainer.leftTreeViewResizer.localX = mainContainer.leftTreeView.width - 2
|
||
mainContainer.leftTreeViewResizer.localY = math.floor(mainContainer.leftTreeView.localY + mainContainer.leftTreeView.height / 2 - mainContainer.leftTreeViewResizer.height / 2)
|
||
|
||
mainContainer.settingsContainer.width, mainContainer.settingsContainer.height = mainContainer.width, mainContainer.height
|
||
mainContainer.settingsContainer.backgroundPanel.width, mainContainer.settingsContainer.backgroundPanel.height = mainContainer.settingsContainer.width, mainContainer.settingsContainer.height
|
||
|
||
mainContainer.bottomToolBar.localY = mainContainer.height - 2
|
||
mainContainer.bottomToolBar.findButton.localX = mainContainer.bottomToolBar.width - mainContainer.bottomToolBar.findButton.width + 1
|
||
mainContainer.bottomToolBar.inputField.width = mainContainer.bottomToolBar.width - mainContainer.bottomToolBar.inputField.localX - mainContainer.bottomToolBar.findButton.width + 1
|
||
|
||
mainContainer.topToolBar.width, mainContainer.topToolBar.backgroundPanel.width = mainContainer.width, mainContainer.width
|
||
mainContainer.titleTextBox.width = math.floor(mainContainer.topToolBar.width * 0.32)
|
||
mainContainer.titleTextBox.localX = math.floor(mainContainer.topToolBar.width / 2 - mainContainer.titleTextBox.width / 2)
|
||
mainContainer.runButton.localX = mainContainer.titleTextBox.localX - mainContainer.runButton.width - 2
|
||
mainContainer.toggleSyntaxHighlightingButton.localX = mainContainer.runButton.localX - mainContainer.toggleSyntaxHighlightingButton.width - 2
|
||
mainContainer.addBreakpointButton.localX = mainContainer.toggleSyntaxHighlightingButton.localX - mainContainer.addBreakpointButton.width - 2
|
||
mainContainer.toggleLeftToolBarButton.localX = mainContainer.titleTextBox.localX + mainContainer.titleTextBox.width + 2
|
||
mainContainer.toggleBottomToolBarButton.localX = mainContainer.toggleLeftToolBarButton.localX + mainContainer.toggleLeftToolBarButton.width + 2
|
||
mainContainer.toggleTopToolBarButton.localX = mainContainer.toggleBottomToolBarButton.localX + mainContainer.toggleBottomToolBarButton.width + 2
|
||
|
||
mainContainer.RAMUsageProgressBar.localX = mainContainer.toggleTopToolBarButton.localX + mainContainer.toggleTopToolBarButton.width + 3
|
||
mainContainer.RAMUsageProgressBar.width = mainContainer.topToolBar.width - mainContainer.RAMUsageProgressBar.localX - 3
|
||
|
||
mainContainer.errorContainer.localX, mainContainer.errorContainer.width = mainContainer.titleTextBox.localX, mainContainer.titleTextBox.width
|
||
mainContainer.errorContainer.backgroundPanel.width, mainContainer.errorContainer.errorTextBox.width = mainContainer.errorContainer.width, mainContainer.errorContainer.width - 4
|
||
|
||
mainContainer.topMenu.width = mainContainer.width
|
||
end
|
||
|
||
local function updateTitle()
|
||
if not mainContainer.topToolBar.hidden then
|
||
if mainContainer.errorContainer.hidden then
|
||
mainContainer.titleTextBox.lines[1] = string.limit(localization.file .. ": " .. (mainContainer.leftTreeView.selectedItem or localization.none), mainContainer.titleTextBox.width - 4)
|
||
mainContainer.titleTextBox.lines[2] = string.limit(localization.cursor .. cursor.position.line .. localization.line .. cursor.position.symbol .. localization.symbol, mainContainer.titleTextBox.width - 4)
|
||
if mainContainer.codeView.selections[1] then
|
||
local countOfSelectedLines = mainContainer.codeView.selections[1].to.line - mainContainer.codeView.selections[1].from.line + 1
|
||
local countOfSelectedSymbols
|
||
if mainContainer.codeView.selections[1].from.line == mainContainer.codeView.selections[1].to.line then
|
||
countOfSelectedSymbols = unicode.len(unicode.sub(mainContainer.codeView.lines[mainContainer.codeView.selections[1].from.line], mainContainer.codeView.selections[1].from.symbol, mainContainer.codeView.selections[1].to.symbol))
|
||
else
|
||
countOfSelectedSymbols = unicode.len(unicode.sub(mainContainer.codeView.lines[mainContainer.codeView.selections[1].from.line], mainContainer.codeView.selections[1].from.symbol, -1))
|
||
for line = mainContainer.codeView.selections[1].from.line + 1, mainContainer.codeView.selections[1].to.line - 1 do
|
||
countOfSelectedSymbols = countOfSelectedSymbols + unicode.len(mainContainer.codeView.lines[line])
|
||
end
|
||
countOfSelectedSymbols = countOfSelectedSymbols + unicode.len(unicode.sub(mainContainer.codeView.lines[mainContainer.codeView.selections[1].to.line], 1, mainContainer.codeView.selections[1].to.symbol))
|
||
end
|
||
mainContainer.titleTextBox.lines[3] = string.limit(localization.selection .. countOfSelectedLines .. localization.lines .. countOfSelectedSymbols .. localization.symbols, mainContainer.titleTextBox.width - 4)
|
||
else
|
||
mainContainer.titleTextBox.lines[3] = string.limit(localization.selection .. localization.none, mainContainer.titleTextBox.width - 4)
|
||
end
|
||
else
|
||
mainContainer.titleTextBox.lines[1], mainContainer.titleTextBox.lines[3] = " ", " "
|
||
if lastErrorLine then
|
||
mainContainer.titleTextBox.lines[2] = localization.runtimeError
|
||
else
|
||
mainContainer.titleTextBox.lines[2] = localization.debugging .. (_G.MineCodeIDEDebugInfo and _G.MineCodeIDEDebugInfo.line or "N/A")
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
local function gotoLine(line)
|
||
mainContainer.codeView.fromLine = math.ceil(line - mainContainer.codeView.height / 2)
|
||
if mainContainer.codeView.fromLine < 1 then
|
||
mainContainer.codeView.fromLine = 1
|
||
elseif mainContainer.codeView.fromLine > #mainContainer.codeView.lines then
|
||
mainContainer.codeView.fromLine = #mainContainer.codeView.lines
|
||
end
|
||
end
|
||
|
||
local function updateHighlights()
|
||
mainContainer.codeView.highlights = {}
|
||
|
||
if breakpointLines then
|
||
for i = 1, #breakpointLines do
|
||
mainContainer.codeView.highlights[breakpointLines[i]] = colors.highlights.onBreakpoint
|
||
end
|
||
end
|
||
|
||
if lastErrorLine then
|
||
mainContainer.codeView.highlights[lastErrorLine] = colors.highlights.onError
|
||
end
|
||
end
|
||
|
||
local function calculateErrorContainerSizeAndBeep(hideBreakpointButtons, frequency, times)
|
||
mainContainer.errorContainer.errorTextBox.height = #mainContainer.errorContainer.errorTextBox.lines
|
||
mainContainer.errorContainer.height = 2 + mainContainer.errorContainer.errorTextBox.height
|
||
mainContainer.errorContainer.backgroundPanel.height = mainContainer.errorContainer.height
|
||
|
||
mainContainer.errorContainer.breakpointExitButton.hidden, mainContainer.errorContainer.breakpointContinueButton.hidden = hideBreakpointButtons, hideBreakpointButtons
|
||
if not hideBreakpointButtons then
|
||
mainContainer.errorContainer.height = mainContainer.errorContainer.height + 1
|
||
mainContainer.errorContainer.breakpointExitButton.localY, mainContainer.errorContainer.breakpointContinueButton.localY = mainContainer.errorContainer.height, mainContainer.errorContainer.height
|
||
mainContainer.errorContainer.breakpointExitButton.width = math.floor(mainContainer.errorContainer.width / 2)
|
||
mainContainer.errorContainer.breakpointContinueButton.localX, mainContainer.errorContainer.breakpointContinueButton.width = mainContainer.errorContainer.breakpointExitButton.width + 1, mainContainer.errorContainer.width - mainContainer.errorContainer.breakpointExitButton.width
|
||
end
|
||
|
||
updateTitle()
|
||
mainContainer:draw()
|
||
buffer.draw()
|
||
|
||
for i = 1, times do component.computer.beep(frequency, 0.08) end
|
||
end
|
||
|
||
local function showBreakpointMessage(variables)
|
||
mainContainer.titleTextBox.colors.background, mainContainer.titleTextBox.colors.text = colors.title.onError.background, colors.title.onError.text
|
||
mainContainer.errorContainer.hidden = false
|
||
|
||
mainContainer.errorContainer.errorTextBox:setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top)
|
||
mainContainer.errorContainer.errorTextBox.lines = {}
|
||
|
||
for variable, value in pairs(variables) do
|
||
table.insert(mainContainer.errorContainer.errorTextBox.lines, variable .. " = " .. value)
|
||
end
|
||
|
||
if #mainContainer.errorContainer.errorTextBox.lines > 0 then
|
||
table.insert(mainContainer.errorContainer.errorTextBox.lines, 1, " ")
|
||
table.insert(mainContainer.errorContainer.errorTextBox.lines, 1, {text = localization.variables, color = 0x0})
|
||
else
|
||
table.insert(mainContainer.errorContainer.errorTextBox.lines, 1, {text = localization.variablesNotAvailable, color = 0x0})
|
||
end
|
||
|
||
calculateErrorContainerSizeAndBeep(false, 1800, 1)
|
||
end
|
||
|
||
local function showErrorContainer(errorCode)
|
||
mainContainer.titleTextBox.colors.background, mainContainer.titleTextBox.colors.text = colors.title.onError.background, colors.title.onError.text
|
||
mainContainer.errorContainer.hidden = false
|
||
|
||
mainContainer.errorContainer.errorTextBox:setAlignment(GUI.alignment.horizontal.left, GUI.alignment.vertical.top)
|
||
mainContainer.errorContainer.errorTextBox.lines = string.wrap({errorCode}, mainContainer.errorContainer.errorTextBox.width)
|
||
|
||
-- Извлекаем ошибочную строку текущего скрипта
|
||
lastErrorLine = tonumber(errorCode:match("%:(%d+)%: in main chunk"))
|
||
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()
|
||
calculateErrorContainerSizeAndBeep(true, 1500, 3)
|
||
end
|
||
|
||
local function hideErrorContainer()
|
||
mainContainer.titleTextBox.colors.background, mainContainer.titleTextBox.colors.text = colors.title.default.background, colors.title.default.text
|
||
mainContainer.errorContainer.hidden = true
|
||
lastErrorLine, scriptCoroutine = nil, nil
|
||
updateHighlights()
|
||
end
|
||
|
||
local function hideSettingsContainer()
|
||
for childIndex = 2, #mainContainer.settingsContainer.children do mainContainer.settingsContainer.children[childIndex] = nil end
|
||
mainContainer.settingsContainer.hidden = true
|
||
mainContainer:draw()
|
||
buffer.draw()
|
||
end
|
||
|
||
local function clearSelection()
|
||
mainContainer.codeView.selections[1] = nil
|
||
end
|
||
|
||
local function clearBreakpoints()
|
||
breakpointLines = nil
|
||
updateHighlights()
|
||
end
|
||
|
||
local function addBreakpoint()
|
||
hideErrorContainer()
|
||
breakpointLines = breakpointLines or {}
|
||
|
||
local lineExists
|
||
for i = 1, #breakpointLines do
|
||
if breakpointLines[i] == cursor.position.line then
|
||
lineExists = i
|
||
break
|
||
end
|
||
end
|
||
|
||
if lineExists then
|
||
table.remove(breakpointLines, lineExists)
|
||
else
|
||
table.insert(breakpointLines, cursor.position.line)
|
||
end
|
||
|
||
if #breakpointLines > 0 then
|
||
table.sort(breakpointLines, function(a, b) return a < b end)
|
||
else
|
||
breakpointLines = nil
|
||
end
|
||
|
||
updateHighlights()
|
||
end
|
||
|
||
local function fixFromLineByCursorPosition()
|
||
if mainContainer.codeView.fromLine > cursor.position.line then
|
||
mainContainer.codeView.fromLine = cursor.position.line
|
||
elseif mainContainer.codeView.fromLine + mainContainer.codeView.height - 2 < cursor.position.line then
|
||
mainContainer.codeView.fromLine = cursor.position.line - mainContainer.codeView.height + 2
|
||
end
|
||
end
|
||
|
||
local function fixFromSymbolByCursorPosition()
|
||
if mainContainer.codeView.fromSymbol > cursor.position.symbol then
|
||
mainContainer.codeView.fromSymbol = cursor.position.symbol
|
||
elseif mainContainer.codeView.fromSymbol + mainContainer.codeView.codeAreaWidth - 3 < cursor.position.symbol then
|
||
mainContainer.codeView.fromSymbol = cursor.position.symbol - mainContainer.codeView.codeAreaWidth + 3
|
||
end
|
||
end
|
||
|
||
local function fixCursorPosition(symbol, line)
|
||
if line < 1 then
|
||
line = 1
|
||
elseif line > #mainContainer.codeView.lines then
|
||
line = #mainContainer.codeView.lines
|
||
end
|
||
|
||
local lineLength = unicode.len(mainContainer.codeView.lines[line])
|
||
if symbol < 1 or lineLength == 0 then
|
||
symbol = 1
|
||
elseif symbol > lineLength then
|
||
symbol = lineLength + 1
|
||
end
|
||
|
||
return symbol, line
|
||
end
|
||
|
||
local function setCursorPosition(symbol, line)
|
||
cursor.position.symbol, cursor.position.line = fixCursorPosition(symbol, line)
|
||
fixFromLineByCursorPosition()
|
||
fixFromSymbolByCursorPosition()
|
||
hideAutocompleteWindow()
|
||
hideErrorContainer()
|
||
end
|
||
|
||
local function setCursorPositionAndClearSelection(symbol, line)
|
||
setCursorPosition(symbol, line)
|
||
clearSelection()
|
||
end
|
||
|
||
local function moveCursor(symbolOffset, lineOffset)
|
||
if mainContainer.autocompleteWindow.hidden or lineOffset == 0 then
|
||
if mainContainer.codeView.selections[1] then
|
||
if symbolOffset < 0 or lineOffset < 0 then
|
||
setCursorPositionAndClearSelection(mainContainer.codeView.selections[1].from.symbol, mainContainer.codeView.selections[1].from.line)
|
||
else
|
||
setCursorPositionAndClearSelection(mainContainer.codeView.selections[1].to.symbol, mainContainer.codeView.selections[1].to.line)
|
||
end
|
||
else
|
||
local newSymbol, newLine = cursor.position.symbol + symbolOffset, cursor.position.line + lineOffset
|
||
|
||
if symbolOffset < 0 and newSymbol < 1 then
|
||
newLine, newSymbol = newLine - 1, math.huge
|
||
elseif symbolOffset > 0 and newSymbol > unicode.len(mainContainer.codeView.lines[newLine] or "") + 1 then
|
||
newLine, newSymbol = newLine + 1, 1
|
||
end
|
||
|
||
setCursorPositionAndClearSelection(newSymbol, newLine)
|
||
end
|
||
elseif not mainContainer.autocompleteWindow.hidden then
|
||
mainContainer.autocompleteWindow.currentMatch = mainContainer.autocompleteWindow.currentMatch + lineOffset
|
||
|
||
if mainContainer.autocompleteWindow.currentMatch < 1 then
|
||
mainContainer.autocompleteWindow.currentMatch = 1
|
||
elseif mainContainer.autocompleteWindow.currentMatch > #mainContainer.autocompleteWindow.matches then
|
||
mainContainer.autocompleteWindow.currentMatch = #mainContainer.autocompleteWindow.matches
|
||
elseif mainContainer.autocompleteWindow.currentMatch < mainContainer.autocompleteWindow.fromMatch then
|
||
mainContainer.autocompleteWindow.fromMatch = mainContainer.autocompleteWindow.currentMatch
|
||
elseif mainContainer.autocompleteWindow.currentMatch > mainContainer.autocompleteWindow.fromMatch + mainContainer.autocompleteWindow.height - 1 then
|
||
mainContainer.autocompleteWindow.fromMatch = mainContainer.autocompleteWindow.currentMatch - mainContainer.autocompleteWindow.height + 1
|
||
end
|
||
end
|
||
end
|
||
|
||
local function setCursorPositionToHome()
|
||
setCursorPositionAndClearSelection(1, 1)
|
||
end
|
||
|
||
local function setCursorPositionToEnd()
|
||
setCursorPositionAndClearSelection(unicode.len(mainContainer.codeView.lines[#mainContainer.codeView.lines]) + 1, #mainContainer.codeView.lines)
|
||
end
|
||
|
||
local function scroll(direction, speed)
|
||
if direction == 1 then
|
||
if mainContainer.codeView.fromLine > speed then
|
||
mainContainer.codeView.fromLine = mainContainer.codeView.fromLine - speed
|
||
else
|
||
mainContainer.codeView.fromLine = 1
|
||
end
|
||
else
|
||
if mainContainer.codeView.fromLine < #mainContainer.codeView.lines - speed then
|
||
mainContainer.codeView.fromLine = mainContainer.codeView.fromLine + speed
|
||
else
|
||
mainContainer.codeView.fromLine = #mainContainer.codeView.lines
|
||
end
|
||
end
|
||
end
|
||
|
||
local function pageUp()
|
||
scroll(1, mainContainer.codeView.height - 2)
|
||
end
|
||
|
||
local function pageDown()
|
||
scroll(-1, mainContainer.codeView.height - 2)
|
||
end
|
||
|
||
local function selectWord()
|
||
local from, to = getCurrentWordStartingAndEnding(cursor.position.symbol)
|
||
if from and to then
|
||
mainContainer.codeView.selections[1] = {
|
||
from = {symbol = from, line = cursor.position.line},
|
||
to = {symbol = to, line = cursor.position.line},
|
||
}
|
||
cursor.position.symbol = to
|
||
end
|
||
end
|
||
|
||
local function removeTabs(text)
|
||
local result = text:gsub("\t", string.rep(" ", mainContainer.codeView.indentationWidth))
|
||
return result
|
||
end
|
||
|
||
local function removeWindowsLineEndings(text)
|
||
local result = text:gsub("\r\n", "\n")
|
||
return result
|
||
end
|
||
|
||
local function changeResolution(width, height)
|
||
buffer.setResolution(width, height)
|
||
calculateSizes()
|
||
mainContainer:draw()
|
||
buffer.draw()
|
||
config.screenResolution.width = width
|
||
config.screenResolution.height = height
|
||
end
|
||
|
||
local function changeResolutionWindow()
|
||
mainContainer.settingsContainer.hidden = false
|
||
local textBoxesWidth = math.floor(mainContainer.width * 0.3)
|
||
local textBoxWidth, x, y = math.floor(textBoxesWidth / 2), math.floor(mainContainer.width / 2 - textBoxesWidth / 2), math.floor(mainContainer.height / 2) - 3
|
||
|
||
mainContainer.settingsContainer:addChild(GUI.label(1, y, mainContainer.width, 1, 0xFFFFFF, localization.changeResolution)):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top); y = y + 3
|
||
local inputFieldWidth = mainContainer.settingsContainer:addChild(GUI.input(x, y, textBoxWidth, 3, 0xCCCCCC, 0x777777, 0x777777, 0xCCCCCC, 0x2D2D2D, tostring(config.screenResolution.width))); x = x + textBoxWidth + 2
|
||
local inputFieldHeight = mainContainer.settingsContainer:addChild(GUI.input(x, y, textBoxWidth, 3, 0xCCCCCC, 0x777777, 0x777777, 0xCCCCCC, 0x2D2D2D, tostring(config.screenResolution.height)))
|
||
|
||
local maxResolutionWidth, maxResolutionHeight = component.gpu.maxResolution()
|
||
inputFieldWidth.validator = function(text)
|
||
local number = tonumber(text)
|
||
if number and number >= 1 and number <= maxResolutionWidth then return true end
|
||
end
|
||
inputFieldHeight.validator = function(text)
|
||
local number = tonumber(text)
|
||
if number and number >= 1 and number <= maxResolutionHeight then return true end
|
||
end
|
||
|
||
mainContainer.settingsContainer.backgroundPanel.eventHandler = function(mainContainer, object, eventData)
|
||
if eventData[1] == "touch" then
|
||
config.screenResolution.width, config.screenResolution.height = tonumber(inputFieldWidth.text), tonumber(inputFieldHeight.text)
|
||
saveConfig()
|
||
hideSettingsContainer()
|
||
changeResolution(config.screenResolution.width, config.screenResolution.height)
|
||
end
|
||
end
|
||
end
|
||
|
||
local function createInputTextBoxForSettingsWindow(title, placeholder, onInputFinishedMethod, validatorMethod)
|
||
mainContainer.settingsContainer.hidden = false
|
||
local textBoxWidth = math.floor(mainContainer.width * 0.3)
|
||
local x, y = math.floor(mainContainer.width / 2 - textBoxWidth / 2), math.floor(mainContainer.height / 2) - 3
|
||
|
||
mainContainer.settingsContainer:addChild(GUI.label(1, y, mainContainer.width, 1, 0xFFFFFF, title)):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top); y = y + 3
|
||
mainContainer.settingsContainer.inputField = mainContainer.settingsContainer:addChild(GUI.input(x, y, textBoxWidth, 3, 0xCCCCCC, 0x777777, 0x777777, 0xCCCCCC, 0x2D2D2D, "", placeholder))
|
||
|
||
mainContainer.settingsContainer.inputField.validator = validatorMethod
|
||
mainContainer.settingsContainer.inputField.onInputFinished = function(...)
|
||
onInputFinishedMethod(...)
|
||
hideSettingsContainer()
|
||
end
|
||
end
|
||
|
||
local function newFile()
|
||
autocompleteDatabase = {}
|
||
mainContainer.codeView.lines = {""}
|
||
mainContainer.codeView.maximumLineLength = 1
|
||
setCursorPositionAndClearSelection(1, 1)
|
||
mainContainer.leftTreeView.selectedItem = nil
|
||
clearBreakpoints()
|
||
end
|
||
|
||
local function loadFile(path)
|
||
newFile()
|
||
|
||
local file = io.open(path, "r")
|
||
for line in file:lines() do
|
||
line = removeWindowsLineEndings(removeTabs(line))
|
||
table.insert(mainContainer.codeView.lines, line)
|
||
mainContainer.codeView.maximumLineLength = math.max(mainContainer.codeView.maximumLineLength, unicode.len(line))
|
||
end
|
||
file:close()
|
||
|
||
if #mainContainer.codeView.lines > 1 then
|
||
table.remove(mainContainer.codeView.lines, 1)
|
||
end
|
||
|
||
mainContainer.leftTreeView.selectedItem = path
|
||
updateAutocompleteDatabaseFromFile()
|
||
end
|
||
|
||
local function saveFile(path)
|
||
fs.makeDirectory(fs.path(path))
|
||
local file, reason = io.open(path, "w")
|
||
if file then
|
||
for line = 1, #mainContainer.codeView.lines do
|
||
file:write(mainContainer.codeView.lines[line], "\n")
|
||
end
|
||
file:close()
|
||
else
|
||
GUI.error("Failed to open file for writing: " .. tostring(reason))
|
||
end
|
||
end
|
||
|
||
local function gotoLineWindow()
|
||
createInputTextBoxForSettingsWindow(localization.gotoLine, localization.lineNumber,
|
||
function()
|
||
gotoLine(tonumber(mainContainer.settingsContainer.inputField.text))
|
||
end,
|
||
function()
|
||
if mainContainer.settingsContainer.inputField.text:match("%d+") then return true end
|
||
end
|
||
)
|
||
end
|
||
|
||
local function openFileWindow()
|
||
createInputTextBoxForSettingsWindow(localization.openFile, localization.pathToFile,
|
||
function()
|
||
loadFile(mainContainer.settingsContainer.inputField.text)
|
||
end,
|
||
function()
|
||
if fs.exists(mainContainer.settingsContainer.inputField.text) then return true end
|
||
end
|
||
)
|
||
end
|
||
|
||
local function saveFileAsWindow()
|
||
createInputTextBoxForSettingsWindow(localization.saveAs, localization.pathToFile,
|
||
function()
|
||
if unicode.len(mainContainer.settingsContainer.inputField.text or "") > 0 then
|
||
saveFile(mainContainer.settingsContainer.inputField.text)
|
||
mainContainer.leftTreeView:updateFileList()
|
||
mainContainer.leftTreeView.selectedItem = mainContainer.leftTreeView.workPath .. mainContainer.settingsContainer.inputField.text
|
||
|
||
updateTitle()
|
||
mainContainer:draw()
|
||
buffer.draw()
|
||
end
|
||
end
|
||
)
|
||
end
|
||
|
||
local function saveFileWindow()
|
||
saveFile(mainContainer.leftTreeView.selectedItem)
|
||
mainContainer.leftTreeView:updateFileList()
|
||
end
|
||
|
||
local function splitStringIntoLines(s)
|
||
s = removeWindowsLineEndings(removeTabs(s))
|
||
|
||
local lines, searchLineEndingFrom, maximumLineLength, lineEndingFoundAt, line = {}, 1, 0
|
||
repeat
|
||
lineEndingFoundAt = string.unicodeFind(s, "\n", searchLineEndingFrom)
|
||
if lineEndingFoundAt then
|
||
line = unicode.sub(s, searchLineEndingFrom, lineEndingFoundAt - 1)
|
||
searchLineEndingFrom = lineEndingFoundAt + 1
|
||
else
|
||
line = unicode.sub(s, searchLineEndingFrom, -1)
|
||
end
|
||
|
||
table.insert(lines, line)
|
||
maximumLineLength = math.max(maximumLineLength, unicode.len(line))
|
||
until not lineEndingFoundAt
|
||
|
||
return lines, maximumLineLength
|
||
end
|
||
|
||
local function downloadFileFromWeb()
|
||
createInputTextBoxForSettingsWindow(localization.getFromWeb, localization.url,
|
||
function()
|
||
local result, reason = web.request(mainContainer.settingsContainer.inputField.text)
|
||
if result then
|
||
newFile()
|
||
mainContainer.codeView.lines, mainContainer.codeView.maximumLineLength = splitStringIntoLines(result)
|
||
else
|
||
GUI.error("Failed to connect to URL: " .. tostring(reason))
|
||
end
|
||
hideSettingsContainer()
|
||
end
|
||
)
|
||
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
|
||
word ~= "local" and
|
||
word ~= "return" and
|
||
word ~= "while" and
|
||
word ~= "repeat" and
|
||
word ~= "until" and
|
||
word ~= "for" and
|
||
word ~= "in" and
|
||
word ~= "do" and
|
||
word ~= "if" and
|
||
word ~= "then" and
|
||
word ~= "else" and
|
||
word ~= "elseif" and
|
||
word ~= "end" and
|
||
word ~= "function" and
|
||
word ~= "true" and
|
||
word ~= "false" and
|
||
word ~= "nil" and
|
||
word ~= "not" and
|
||
word ~= "and" and
|
||
word ~= "or" 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
|
||
|
||
local function continue()
|
||
-- Готовим экран к запуску
|
||
local oldResolutionX, oldResolutionY = component.gpu.getResolution()
|
||
MineOSInterface.clearTerminal()
|
||
|
||
-- Запускаем
|
||
_G.MineCodeIDEDebugInfo = nil
|
||
local coroutineResumeSuccess, coroutineResumeReason = coroutine.resume(scriptCoroutine)
|
||
|
||
-- Анализируем результат запуска
|
||
if coroutineResumeSuccess then
|
||
if coroutine.status(scriptCoroutine) == "dead" then
|
||
MineOSInterface.waitForPressingAnyKey()
|
||
hideErrorContainer()
|
||
buffer.setResolution(oldResolutionX, oldResolutionY); mainContainer:draw(); buffer.draw(true)
|
||
else
|
||
-- Тест на пидора, мало ли у чувака в проге тоже есть yield
|
||
if _G.MineCodeIDEDebugInfo then
|
||
buffer.setResolution(oldResolutionX, oldResolutionY); mainContainer:draw(); buffer.draw(true)
|
||
gotoLine(_G.MineCodeIDEDebugInfo.line)
|
||
showBreakpointMessage(_G.MineCodeIDEDebugInfo.variables)
|
||
end
|
||
end
|
||
else
|
||
buffer.setResolution(oldResolutionX, oldResolutionY); mainContainer:draw(); buffer.draw(true)
|
||
showErrorContainer(debug.traceback(scriptCoroutine, coroutineResumeReason))
|
||
end
|
||
end
|
||
|
||
local function run()
|
||
hideErrorContainer()
|
||
|
||
-- Инсертим брейкпоинты
|
||
if breakpointLines then
|
||
local offset = 0
|
||
for i = 1, #breakpointLines do
|
||
local variables = getVariables(mainContainer.codeView.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(mainContainer.codeView.lines, breakpointLines[i] + offset, breakpointMessage)
|
||
offset = offset + 1
|
||
end
|
||
end
|
||
|
||
-- Лоадим кодыч
|
||
local loadSuccess, loadReason = load(table.concat(mainContainer.codeView.lines, "\n"))
|
||
|
||
-- Чистим дерьмо вилочкой, чистим
|
||
if breakpointLines then
|
||
for i = 1, #breakpointLines do
|
||
table.remove(mainContainer.codeView.lines, breakpointLines[i])
|
||
end
|
||
end
|
||
|
||
-- Запускаем кодыч
|
||
if loadSuccess then
|
||
scriptCoroutine = coroutine.create(loadSuccess)
|
||
continue()
|
||
else
|
||
showErrorContainer(loadReason)
|
||
end
|
||
end
|
||
|
||
local function deleteLine(line)
|
||
if #mainContainer.codeView.lines > 1 then
|
||
table.remove(mainContainer.codeView.lines, line)
|
||
setCursorPositionAndClearSelection(1, cursor.position.line)
|
||
|
||
updateAutocompleteDatabaseFromFile()
|
||
end
|
||
end
|
||
|
||
local function deleteSpecifiedData(fromSymbol, fromLine, toSymbol, toLine)
|
||
local upperLine = unicode.sub(mainContainer.codeView.lines[fromLine], 1, fromSymbol - 1)
|
||
local lowerLine = unicode.sub(mainContainer.codeView.lines[toLine], toSymbol + 1, -1)
|
||
for line = fromLine + 1, toLine do
|
||
table.remove(mainContainer.codeView.lines, fromLine + 1)
|
||
end
|
||
mainContainer.codeView.lines[fromLine] = upperLine .. lowerLine
|
||
setCursorPositionAndClearSelection(fromSymbol, fromLine)
|
||
|
||
updateAutocompleteDatabaseFromFile()
|
||
end
|
||
|
||
local function deleteSelectedData()
|
||
if mainContainer.codeView.selections[1] then
|
||
deleteSpecifiedData(
|
||
mainContainer.codeView.selections[1].from.symbol,
|
||
mainContainer.codeView.selections[1].from.line,
|
||
mainContainer.codeView.selections[1].to.symbol,
|
||
mainContainer.codeView.selections[1].to.line
|
||
)
|
||
|
||
clearSelection()
|
||
end
|
||
end
|
||
|
||
local function copy()
|
||
if mainContainer.codeView.selections[1] then
|
||
if mainContainer.codeView.selections[1].to.line == mainContainer.codeView.selections[1].from.line then
|
||
clipboard = { unicode.sub(mainContainer.codeView.lines[mainContainer.codeView.selections[1].from.line], mainContainer.codeView.selections[1].from.symbol, mainContainer.codeView.selections[1].to.symbol) }
|
||
else
|
||
clipboard = { unicode.sub(mainContainer.codeView.lines[mainContainer.codeView.selections[1].from.line], mainContainer.codeView.selections[1].from.symbol, -1) }
|
||
for line = mainContainer.codeView.selections[1].from.line + 1, mainContainer.codeView.selections[1].to.line - 1 do
|
||
table.insert(clipboard, mainContainer.codeView.lines[line])
|
||
end
|
||
table.insert(clipboard, unicode.sub(mainContainer.codeView.lines[mainContainer.codeView.selections[1].to.line], 1, mainContainer.codeView.selections[1].to.symbol))
|
||
end
|
||
end
|
||
end
|
||
|
||
local function cut()
|
||
if mainContainer.codeView.selections[1] then
|
||
copy()
|
||
deleteSelectedData()
|
||
end
|
||
end
|
||
|
||
local function pasteSelectedAutocompletion()
|
||
local firstPart = unicode.sub(mainContainer.codeView.lines[cursor.position.line], 1, mainContainer.autocompleteWindow.currentWordStarting - 1)
|
||
local secondPart = unicode.sub(mainContainer.codeView.lines[cursor.position.line], mainContainer.autocompleteWindow.currentWordEnding + 1, -1)
|
||
mainContainer.codeView.lines[cursor.position.line] = firstPart .. mainContainer.autocompleteWindow.matches[mainContainer.autocompleteWindow.currentMatch][1] .. secondPart
|
||
setCursorPositionAndClearSelection(unicode.len(firstPart .. mainContainer.autocompleteWindow.matches[mainContainer.autocompleteWindow.currentMatch][1]) + 1, cursor.position.line)
|
||
hideAutocompleteWindow()
|
||
end
|
||
|
||
local function paste(pasteLines)
|
||
if pasteLines then
|
||
if mainContainer.codeView.selections[1] then
|
||
deleteSelectedData()
|
||
end
|
||
|
||
local firstPart = unicode.sub(mainContainer.codeView.lines[cursor.position.line], 1, cursor.position.symbol - 1)
|
||
local secondPart = unicode.sub(mainContainer.codeView.lines[cursor.position.line], cursor.position.symbol, -1)
|
||
|
||
if #pasteLines == 1 then
|
||
mainContainer.codeView.lines[cursor.position.line] = firstPart .. pasteLines[1] .. secondPart
|
||
setCursorPositionAndClearSelection(cursor.position.symbol + unicode.len(pasteLines[1]), cursor.position.line)
|
||
else
|
||
mainContainer.codeView.lines[cursor.position.line] = firstPart .. pasteLines[1]
|
||
for pasteLine = #pasteLines - 1, 2, -1 do
|
||
table.insert(mainContainer.codeView.lines, cursor.position.line + 1, pasteLines[pasteLine])
|
||
end
|
||
table.insert(mainContainer.codeView.lines, cursor.position.line + #pasteLines - 1, pasteLines[#pasteLines] .. secondPart)
|
||
setCursorPositionAndClearSelection(unicode.len(pasteLines[#pasteLines]) + 1, cursor.position.line + #pasteLines - 1)
|
||
end
|
||
|
||
updateAutocompleteDatabaseFromFile()
|
||
end
|
||
end
|
||
|
||
local function selectAndPasteColor()
|
||
local startColor = 0xFF0000
|
||
if mainContainer.codeView.selections[1] and mainContainer.codeView.selections[1].from.line == mainContainer.codeView.selections[1].to.line then
|
||
startColor = tonumber(unicode.sub(mainContainer.codeView.lines[mainContainer.codeView.selections[1].from.line], mainContainer.codeView.selections[1].from.symbol, mainContainer.codeView.selections[1].to.symbol)) or startColor
|
||
end
|
||
|
||
local selectedColor = require("palette").show("auto", "auto", startColor)
|
||
if selectedColor then
|
||
paste({string.format("0x%06X", selectedColor)})
|
||
end
|
||
end
|
||
|
||
local function pasteRegularChar(unicodeByte, char)
|
||
if not keyboard.isControl(unicodeByte) then
|
||
paste({char})
|
||
if char == " " then
|
||
updateAutocompleteDatabaseFromFile()
|
||
end
|
||
showAutocompleteWindow()
|
||
end
|
||
end
|
||
|
||
local function pasteAutoBrackets(unicodeByte)
|
||
local char = unicode.char(unicodeByte)
|
||
local currentSymbol = unicode.sub(mainContainer.codeView.lines[cursor.position.line], cursor.position.symbol, cursor.position.symbol)
|
||
|
||
-- Если у нас вообще врублен режим автоскобок, то чекаем их
|
||
if config.enableAutoBrackets then
|
||
-- Ситуация, когда курсор находится на закрывающей скобке, и нехуй ее еще раз вставлять
|
||
if possibleBrackets.closers[char] and currentSymbol == char then
|
||
deleteSelectedData()
|
||
setCursorPosition(cursor.position.symbol + 1, cursor.position.line)
|
||
-- Если нажата открывающая скобка
|
||
elseif possibleBrackets.openers[char] then
|
||
-- А вот тут мы берем в скобочки уже выделенный текст
|
||
if mainContainer.codeView.selections[1] then
|
||
local firstPart = unicode.sub(mainContainer.codeView.lines[mainContainer.codeView.selections[1].from.line], 1, mainContainer.codeView.selections[1].from.symbol - 1)
|
||
local secondPart = unicode.sub(mainContainer.codeView.lines[mainContainer.codeView.selections[1].from.line], mainContainer.codeView.selections[1].from.symbol, -1)
|
||
mainContainer.codeView.lines[mainContainer.codeView.selections[1].from.line] = firstPart .. char .. secondPart
|
||
mainContainer.codeView.selections[1].from.symbol = mainContainer.codeView.selections[1].from.symbol + 1
|
||
|
||
if mainContainer.codeView.selections[1].to.line == mainContainer.codeView.selections[1].from.line then
|
||
mainContainer.codeView.selections[1].to.symbol = mainContainer.codeView.selections[1].to.symbol + 1
|
||
end
|
||
|
||
firstPart = unicode.sub(mainContainer.codeView.lines[mainContainer.codeView.selections[1].to.line], 1, mainContainer.codeView.selections[1].to.symbol)
|
||
secondPart = unicode.sub(mainContainer.codeView.lines[mainContainer.codeView.selections[1].to.line], mainContainer.codeView.selections[1].to.symbol + 1, -1)
|
||
mainContainer.codeView.lines[mainContainer.codeView.selections[1].to.line] = firstPart .. possibleBrackets.openers[char] .. secondPart
|
||
cursor.position.symbol = cursor.position.symbol + 2
|
||
-- А тут мы делаем двойную автоскобку, если можем
|
||
elseif possibleBrackets.openers[char] and not currentSymbol:match("[%a%d%_]") then
|
||
paste({char .. possibleBrackets.openers[char]})
|
||
setCursorPosition(cursor.position.symbol - 1, cursor.position.line)
|
||
cursor.blinkState = false
|
||
-- Ну, и если нет ни выделений, ни можем ебануть автоскобочку по регулярке
|
||
else
|
||
pasteRegularChar(unicodeByte, char)
|
||
end
|
||
-- Если мы вообще на скобку не нажимали
|
||
else
|
||
pasteRegularChar(unicodeByte, char)
|
||
end
|
||
-- Если оффнуты афтоскобки
|
||
else
|
||
pasteRegularChar(unicodeByte, char)
|
||
end
|
||
end
|
||
|
||
local function backspaceAutoBrackets()
|
||
local previousSymbol = unicode.sub(mainContainer.codeView.lines[cursor.position.line], cursor.position.symbol - 1, cursor.position.symbol - 1)
|
||
local currentSymbol = unicode.sub(mainContainer.codeView.lines[cursor.position.line], cursor.position.symbol, cursor.position.symbol)
|
||
if config.enableAutoBrackets and possibleBrackets.openers[previousSymbol] and possibleBrackets.openers[previousSymbol] == currentSymbol then
|
||
deleteSpecifiedData(cursor.position.symbol, cursor.position.line, cursor.position.symbol, cursor.position.line)
|
||
end
|
||
end
|
||
|
||
local function delete()
|
||
if mainContainer.codeView.selections[1] then
|
||
deleteSelectedData()
|
||
else
|
||
if cursor.position.symbol < unicode.len(mainContainer.codeView.lines[cursor.position.line]) + 1 then
|
||
deleteSpecifiedData(cursor.position.symbol, cursor.position.line, cursor.position.symbol, cursor.position.line)
|
||
else
|
||
if cursor.position.line > 1 then
|
||
deleteSpecifiedData(unicode.len(mainContainer.codeView.lines[cursor.position.line]) + 1, cursor.position.line, 0, cursor.position.line + 1)
|
||
end
|
||
end
|
||
|
||
-- updateAutocompleteDatabaseFromFile()
|
||
showAutocompleteWindow()
|
||
end
|
||
end
|
||
|
||
local function backspace()
|
||
if mainContainer.codeView.selections[1] then
|
||
deleteSelectedData()
|
||
else
|
||
if cursor.position.symbol > 1 then
|
||
backspaceAutoBrackets()
|
||
deleteSpecifiedData(cursor.position.symbol - 1, cursor.position.line, cursor.position.symbol - 1, cursor.position.line)
|
||
else
|
||
if cursor.position.line > 1 then
|
||
deleteSpecifiedData(unicode.len(mainContainer.codeView.lines[cursor.position.line - 1]) + 1, cursor.position.line - 1, 0, cursor.position.line)
|
||
end
|
||
end
|
||
|
||
-- updateAutocompleteDatabaseFromFile()
|
||
showAutocompleteWindow()
|
||
end
|
||
end
|
||
|
||
local function enter()
|
||
local firstPart = unicode.sub(mainContainer.codeView.lines[cursor.position.line], 1, cursor.position.symbol - 1)
|
||
local secondPart = unicode.sub(mainContainer.codeView.lines[cursor.position.line], cursor.position.symbol, -1)
|
||
mainContainer.codeView.lines[cursor.position.line] = firstPart
|
||
table.insert(mainContainer.codeView.lines, cursor.position.line + 1, secondPart)
|
||
setCursorPositionAndClearSelection(1, cursor.position.line + 1)
|
||
end
|
||
|
||
local function selectAll()
|
||
mainContainer.codeView.selections[1] = {
|
||
from = {
|
||
symbol = 1, line = 1
|
||
},
|
||
to = {
|
||
symbol = unicode.len(mainContainer.codeView.lines[#mainContainer.codeView.lines]), line = #mainContainer.codeView.lines
|
||
}
|
||
}
|
||
end
|
||
|
||
local function isLineCommented(line)
|
||
if mainContainer.codeView.lines[line] == "" or mainContainer.codeView.lines[line]:match("%-%-%s?") then return true end
|
||
end
|
||
|
||
local function commentLine(line)
|
||
mainContainer.codeView.lines[line] = "-- " .. mainContainer.codeView.lines[line]
|
||
end
|
||
|
||
local function uncommentLine(line)
|
||
local countOfReplaces
|
||
mainContainer.codeView.lines[line], countOfReplaces = mainContainer.codeView.lines[line]:gsub("%-%-%s?", "", 1)
|
||
return countOfReplaces
|
||
end
|
||
|
||
local function toggleComment()
|
||
if mainContainer.codeView.selections[1] then
|
||
local allLinesAreCommented = true
|
||
|
||
for line = mainContainer.codeView.selections[1].from.line, mainContainer.codeView.selections[1].to.line do
|
||
if not isLineCommented(line) then
|
||
allLinesAreCommented = false
|
||
break
|
||
end
|
||
end
|
||
|
||
for line = mainContainer.codeView.selections[1].from.line, mainContainer.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(cursor.position.symbol + modifyer, cursor.position.line)
|
||
mainContainer.codeView.selections[1].from.symbol, mainContainer.codeView.selections[1].to.symbol = mainContainer.codeView.selections[1].from.symbol + modifyer, mainContainer.codeView.selections[1].to.symbol + modifyer
|
||
else
|
||
if isLineCommented(cursor.position.line) then
|
||
if uncommentLine(cursor.position.line) > 0 then
|
||
setCursorPositionAndClearSelection(cursor.position.symbol - 3, cursor.position.line)
|
||
end
|
||
else
|
||
commentLine(cursor.position.line)
|
||
setCursorPositionAndClearSelection(cursor.position.symbol + 3, cursor.position.line)
|
||
end
|
||
end
|
||
end
|
||
|
||
local function indentLine(line)
|
||
mainContainer.codeView.lines[line] = string.rep(" ", mainContainer.codeView.indentationWidth) .. mainContainer.codeView.lines[line]
|
||
end
|
||
|
||
local function unindentLine(line)
|
||
mainContainer.codeView.lines[line], countOfReplaces = string.gsub(mainContainer.codeView.lines[line], "^" .. string.rep("%s", mainContainer.codeView.indentationWidth), "")
|
||
return countOfReplaces
|
||
end
|
||
|
||
local function indentOrUnindent(isIndent)
|
||
if mainContainer.codeView.selections[1] then
|
||
local countOfReplacesInFirstLine, countOfReplacesInLastLine
|
||
|
||
for line = mainContainer.codeView.selections[1].from.line, mainContainer.codeView.selections[1].to.line do
|
||
if isIndent then
|
||
indentLine(line)
|
||
else
|
||
local countOfReplaces = unindentLine(line)
|
||
if line == mainContainer.codeView.selections[1].from.line then
|
||
countOfReplacesInFirstLine = countOfReplaces
|
||
elseif line == mainContainer.codeView.selections[1].to.line then
|
||
countOfReplacesInLastLine = countOfReplaces
|
||
end
|
||
end
|
||
end
|
||
|
||
if isIndent then
|
||
setCursorPosition(cursor.position.symbol + mainContainer.codeView.indentationWidth, cursor.position.line)
|
||
mainContainer.codeView.selections[1].from.symbol, mainContainer.codeView.selections[1].to.symbol = mainContainer.codeView.selections[1].from.symbol + mainContainer.codeView.indentationWidth, mainContainer.codeView.selections[1].to.symbol + mainContainer.codeView.indentationWidth
|
||
else
|
||
if countOfReplacesInFirstLine > 0 then
|
||
mainContainer.codeView.selections[1].from.symbol = mainContainer.codeView.selections[1].from.symbol - mainContainer.codeView.indentationWidth
|
||
if cursor.position.line == mainContainer.codeView.selections[1].from.line then
|
||
setCursorPosition(cursor.position.symbol - mainContainer.codeView.indentationWidth, cursor.position.line)
|
||
end
|
||
end
|
||
|
||
if countOfReplacesInLastLine > 0 then
|
||
mainContainer.codeView.selections[1].to.symbol = mainContainer.codeView.selections[1].to.symbol - mainContainer.codeView.indentationWidth
|
||
if cursor.position.line == mainContainer.codeView.selections[1].to.line then
|
||
setCursorPosition(cursor.position.symbol - mainContainer.codeView.indentationWidth, cursor.position.line)
|
||
end
|
||
end
|
||
end
|
||
else
|
||
if isIndent then
|
||
indentLine(cursor.position.line)
|
||
setCursorPositionAndClearSelection(cursor.position.symbol + mainContainer.codeView.indentationWidth, cursor.position.line)
|
||
else
|
||
if unindentLine(cursor.position.line) > 0 then
|
||
setCursorPositionAndClearSelection(cursor.position.symbol - mainContainer.codeView.indentationWidth, cursor.position.line)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
local function updateRAMProgressBar()
|
||
if not mainContainer.topToolBar.hidden then
|
||
local totalMemory = computer.totalMemory()
|
||
mainContainer.RAMUsageProgressBar.value = math.ceil((totalMemory - computer.freeMemory()) / totalMemory * 100)
|
||
end
|
||
end
|
||
|
||
local function find()
|
||
if not mainContainer.bottomToolBar.hidden and mainContainer.bottomToolBar.inputField.text ~= "" then
|
||
findStartFrom = findStartFrom + 1
|
||
|
||
for line = findStartFrom, #mainContainer.codeView.lines do
|
||
local whereToFind, whatToFind = mainContainer.codeView.lines[line], mainContainer.bottomToolBar.inputField.text
|
||
if not mainContainer.bottomToolBar.caseSensitiveButton.pressed then
|
||
whereToFind, whatToFind = unicode.lower(whereToFind), unicode.lower(whatToFind)
|
||
end
|
||
|
||
local success, starting, ending = pcall(string.unicodeFind, whereToFind, whatToFind)
|
||
if success then
|
||
if starting then
|
||
mainContainer.codeView.selections[1] = {
|
||
from = {symbol = starting, line = line},
|
||
to = {symbol = ending, line = line},
|
||
color = 0xCC9200
|
||
}
|
||
findStartFrom = line
|
||
gotoLine(line)
|
||
return
|
||
end
|
||
else
|
||
GUI.error("Wrong searching regex")
|
||
end
|
||
end
|
||
|
||
findStartFrom = 0
|
||
end
|
||
end
|
||
|
||
local function findFromFirstDisplayedLine()
|
||
findStartFrom = mainContainer.codeView.fromLine
|
||
find()
|
||
end
|
||
|
||
local function toggleBottomToolBar()
|
||
mainContainer.bottomToolBar.hidden = not mainContainer.bottomToolBar.hidden
|
||
mainContainer.toggleBottomToolBarButton.pressed = not mainContainer.bottomToolBar.hidden
|
||
calculateSizes()
|
||
|
||
if not mainContainer.bottomToolBar.hidden then
|
||
mainContainer:draw()
|
||
findFromFirstDisplayedLine()
|
||
end
|
||
end
|
||
|
||
local function toggleTopToolBar()
|
||
mainContainer.topToolBar.hidden = not mainContainer.topToolBar.hidden
|
||
mainContainer.toggleTopToolBarButton.pressed = not mainContainer.topToolBar.hidden
|
||
calculateSizes()
|
||
end
|
||
|
||
local function createEditOrRightClickMenu(x, y)
|
||
local editOrRightClickMenu = GUI.contextMenu(x, y)
|
||
editOrRightClickMenu:addItem(localization.cut, not mainContainer.codeView.selections[1], "^X").onTouch = function()
|
||
cut()
|
||
end
|
||
editOrRightClickMenu:addItem(localization.copy, not mainContainer.codeView.selections[1], "^C").onTouch = function()
|
||
copy()
|
||
end
|
||
editOrRightClickMenu:addItem(localization.paste, not clipboard, "^V").onTouch = function()
|
||
paste(clipboard)
|
||
end
|
||
editOrRightClickMenu:addSeparator()
|
||
editOrRightClickMenu:addItem(localization.comment, false, "^/").onTouch = function()
|
||
toggleComment()
|
||
end
|
||
editOrRightClickMenu:addItem(localization.indent, false, "Tab").onTouch = function()
|
||
indentOrUnindent(true)
|
||
end
|
||
editOrRightClickMenu:addItem(localization.unindent, false, "⇧Tab").onTouch = function()
|
||
indentOrUnindent(false)
|
||
end
|
||
editOrRightClickMenu:addItem(localization.deleteLine, false, "^Del").onTouch = function()
|
||
deleteLine(cursor.position.line)
|
||
end
|
||
editOrRightClickMenu:addSeparator()
|
||
editOrRightClickMenu:addItem(localization.addBreakpoint, false, "F9").onTouch = function()
|
||
addBreakpoint()
|
||
mainContainer:draw()
|
||
buffer.draw()
|
||
end
|
||
editOrRightClickMenu:addItem(localization.clearBreakpoints, not breakpointLines, "^F9").onTouch = function()
|
||
clearBreakpoints()
|
||
end
|
||
editOrRightClickMenu:addSeparator()
|
||
editOrRightClickMenu:addItem(localization.selectAndPasteColor, false, "^⇧C").onTouch = function()
|
||
selectAndPasteColor()
|
||
end
|
||
editOrRightClickMenu:addItem(localization.selectWord).onTouch = function()
|
||
selectWord()
|
||
end
|
||
editOrRightClickMenu:addItem(localization.selectAll, false, "^A").onTouch = function()
|
||
selectAll()
|
||
end
|
||
editOrRightClickMenu:show()
|
||
end
|
||
|
||
local function tick()
|
||
updateTitle()
|
||
updateRAMProgressBar()
|
||
mainContainer:draw()
|
||
|
||
if cursor.blinkState and mainContainer.settingsContainer.hidden then
|
||
local x, y = convertTextPositionToScreenCoordinates(cursor.position.symbol, cursor.position.line)
|
||
if
|
||
x >= mainContainer.codeView.codeAreaPosition + 1 and
|
||
y >= mainContainer.codeView.y and
|
||
x <= mainContainer.codeView.codeAreaPosition + mainContainer.codeView.codeAreaWidth - 2 and
|
||
y <= mainContainer.codeView.y + mainContainer.codeView.height - 2
|
||
then
|
||
buffer.text(x, y, config.cursorColor, config.cursorSymbol)
|
||
end
|
||
end
|
||
|
||
buffer.draw()
|
||
end
|
||
|
||
local function createMainContainer()
|
||
mainContainer = GUI.fullScreenContainer()
|
||
|
||
mainContainer.codeView = mainContainer:addChild(GUI.codeView(1, 1, 1, 1, {""}, 1, 1, 1, {}, {}, config.highlightLuaSyntax, 2))
|
||
mainContainer.codeView.scrollBars.vertical.onTouch = function()
|
||
mainContainer.codeView.fromLine = mainContainer.codeView.scrollBars.vertical.value
|
||
end
|
||
mainContainer.codeView.scrollBars.horizontal.onTouch = function()
|
||
mainContainer.codeView.fromSymbol = mainContainer.codeView.scrollBars.horizontal.value
|
||
end
|
||
|
||
mainContainer.topMenu = mainContainer:addChild(GUI.menu(1, 1, 1, colors.topMenu.backgroundColor, colors.topMenu.textColor, colors.topMenu.backgroundPressedColor, colors.topMenu.textPressedColor))
|
||
|
||
local item1 = mainContainer.topMenu:addItem("MineCode", 0x0)
|
||
item1.onTouch = function()
|
||
local menu = GUI.contextMenu(item1.x, item1.y + 1)
|
||
menu:addItem(localization.about).onTouch = function()
|
||
mainContainer.settingsContainer.hidden = false
|
||
local y = math.floor(mainContainer.settingsContainer.height / 2 - #about / 2)
|
||
mainContainer.settingsContainer:addChild(GUI.textBox(1, y, mainContainer.settingsContainer.width, #about, nil, 0xEEEEEE, about, 1)):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top)
|
||
end
|
||
menu:addItem(localization.quit, false, "^W").onTouch = function()
|
||
mainContainer:stopEventHandling()
|
||
end
|
||
menu:show()
|
||
end
|
||
|
||
local item2 = mainContainer.topMenu:addItem(localization.file)
|
||
item2.onTouch = function()
|
||
local menu = GUI.contextMenu(item2.x, item2.y + 1)
|
||
menu:addItem(localization.new, false, "^N").onTouch = function()
|
||
newFile()
|
||
mainContainer:draw()
|
||
buffer.draw()
|
||
end
|
||
menu:addItem(localization.open, false, "^O").onTouch = function()
|
||
openFileWindow()
|
||
end
|
||
if component.isAvailable("internet") then
|
||
menu:addItem(localization.getFromWeb, false, "^U").onTouch = function()
|
||
downloadFileFromWeb()
|
||
end
|
||
end
|
||
menu:addSeparator()
|
||
menu:addItem(localization.save, not mainContainer.leftTreeView.selectedItem, "^S").onTouch = function()
|
||
saveFileWindow()
|
||
end
|
||
menu:addItem(localization.saveAs, false, "^⇧S").onTouch = function()
|
||
saveFileAsWindow()
|
||
end
|
||
menu:show()
|
||
end
|
||
|
||
local item3 = mainContainer.topMenu:addItem(localization.edit)
|
||
item3.onTouch = function()
|
||
createEditOrRightClickMenu(item3.x, item3.y + 1)
|
||
end
|
||
|
||
local item4 = mainContainer.topMenu:addItem(localization.properties)
|
||
item4.onTouch = function()
|
||
local menu = GUI.contextMenu(item4.x, item4.y + 1)
|
||
menu:addItem(localization.colorScheme).onTouch = function()
|
||
mainContainer.settingsContainer.hidden = false
|
||
|
||
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(mainContainer.settingsContainer.width / colorSelectorCountX * 0.8), 3, 2, 1
|
||
|
||
local startX, y = math.floor(mainContainer.settingsContainer.width / 2 - (colorSelectorCountX * (colorSelectorWidth + colorSelectorSpaceX) - colorSelectorSpaceX) / 2), math.floor(mainContainer.settingsContainer.height / 2 - (colorSelectorCountY * (colorSelectorHeight + colorSelectorSpaceY) - colorSelectorSpaceY + 3) / 2)
|
||
mainContainer.settingsContainer:addChild(GUI.label(1, y, mainContainer.settingsContainer.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 = mainContainer.settingsContainer:addChild(GUI.colorSelector(x, y, colorSelectorWidth, colorSelectorHeight, config.syntaxColorScheme[colors[i][1]], colors[i][1]))
|
||
colorSelector.onTouch = function()
|
||
config.syntaxColorScheme[colors[i][1]] = colorSelector.color
|
||
syntax.colorScheme = config.syntaxColorScheme
|
||
saveConfig()
|
||
end
|
||
|
||
x, counter = x + colorSelectorWidth + colorSelectorSpaceX, counter + 1
|
||
if counter > colorSelectorCountX then
|
||
x, y, counter = startX, y + colorSelectorHeight + colorSelectorSpaceY, 1
|
||
end
|
||
end
|
||
end
|
||
menu:addItem(localization.cursorProperties).onTouch = function()
|
||
mainContainer.settingsContainer.hidden = false
|
||
|
||
local elementWidth = math.floor(mainContainer.width * 0.3)
|
||
local x, y = math.floor(mainContainer.width / 2 - elementWidth / 2), math.floor(mainContainer.height / 2) - 7
|
||
mainContainer.settingsContainer:addChild(GUI.label(1, y, mainContainer.settingsContainer.width, 1, 0xFFFFFF, localization.cursorProperties)):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top); y = y + 3
|
||
local inputField = mainContainer.settingsContainer:addChild(GUI.input(x, y, elementWidth, 3, 0xCCCCCC, 0x777777, 0x777777, 0xCCCCCC, 0x2D2D2D, config.cursorSymbol, localization.cursorSymbol)); y = y + 5
|
||
inputField.validator = function(text)
|
||
if unicode.len(text) == 1 then return true end
|
||
end
|
||
inputField.onInputFinished = function()
|
||
config.cursorSymbol = inputField.text; saveConfig()
|
||
end
|
||
local colorSelector = mainContainer.settingsContainer:addChild(GUI.colorSelector(x, y, elementWidth, 3, config.cursorColor, localization.cursorColor)); y = y + 5
|
||
colorSelector.onTouch = function()
|
||
config.cursorColor = colorSelector.color; saveConfig()
|
||
end
|
||
local horizontalSlider = mainContainer.settingsContainer:addChild(GUI.slider(x, y, elementWidth, 0xFFDB80, 0x000000, 0xFFDB40, 0xDDDDDD, 1, 1000, config.cursorBlinkDelay * 1000, false, localization.cursorBlinkDelay .. ": ", " ms"))
|
||
horizontalSlider.onValueChanged = function()
|
||
config.cursorBlinkDelay = horizontalSlider.value / 1000; saveConfig()
|
||
end
|
||
end
|
||
|
||
if mainContainer.topToolBar.hidden then
|
||
menu:addItem(localization.toggleTopToolBar).onTouch = function()
|
||
toggleTopToolBar()
|
||
end
|
||
end
|
||
menu:addSeparator()
|
||
menu:addItem(config.enableAutoBrackets and localization.disableAutoBrackets or localization.enableAutoBrackets, false, "^]").onTouch = function()
|
||
config.enableAutoBrackets = not config.enableAutoBrackets
|
||
saveConfig()
|
||
end
|
||
menu:addItem(config.enableAutocompletion and localization.disableAutocompletion or localization.enableAutocompletion, false, "^I").onTouch = function()
|
||
toggleEnableAutocompleteDatabase()
|
||
end
|
||
menu:addSeparator()
|
||
menu:addItem(localization.changeResolution, false, "^R").onTouch = function()
|
||
changeResolutionWindow()
|
||
end
|
||
menu:show()
|
||
end
|
||
|
||
local item5 = mainContainer.topMenu:addItem(localization.gotoCyka)
|
||
item5.onTouch = function()
|
||
local menu = GUI.contextMenu(item5.x, item5.y + 1)
|
||
menu:addItem(localization.pageUp, false, "PgUp").onTouch = function()
|
||
pageUp()
|
||
end
|
||
menu:addItem(localization.pageDown, false, "PgDn").onTouch = function()
|
||
pageDown()
|
||
end
|
||
menu:addItem(localization.gotoStart, false, "Home").onTouch = function()
|
||
setCursorPositionToHome()
|
||
end
|
||
menu:addItem(localization.gotoEnd, false, "End").onTouch = function()
|
||
setCursorPositionToEnd()
|
||
end
|
||
menu:addSeparator()
|
||
menu:addItem(localization.gotoLine, false, "^L").onTouch = function()
|
||
gotoLineWindow()
|
||
end
|
||
menu:show()
|
||
end
|
||
|
||
mainContainer.topToolBar = mainContainer:addChild(GUI.container(1, 2, 1, 3))
|
||
mainContainer.topToolBar.backgroundPanel = mainContainer.topToolBar:addChild(GUI.panel(1, 1, 1, 3, colors.topToolBar))
|
||
mainContainer.titleTextBox = mainContainer.topToolBar:addChild(GUI.textBox(1, 1, 1, 3, 0x0, 0x0, {}, 1):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top))
|
||
local titleTextBoxOldDraw = mainContainer.titleTextBox.draw
|
||
mainContainer.titleTextBox.draw = function(titleTextBox)
|
||
titleTextBoxOldDraw(titleTextBox)
|
||
local sidesColor = mainContainer.errorContainer.hidden and colors.title.default.sides or colors.title.onError.sides
|
||
buffer.square(titleTextBox.x, titleTextBox.y, 1, titleTextBox.height, sidesColor, titleTextBox.colors.text, " ")
|
||
buffer.square(titleTextBox.x + titleTextBox.width - 1, titleTextBox.y, 1, titleTextBox.height, sidesColor, titleTextBox.colors.text, " ")
|
||
end
|
||
|
||
mainContainer.RAMUsageProgressBar = mainContainer.topToolBar:addChild(GUI.progressBar(1, 2, 1, 0x777777, 0xBBBBBB, 0xAAAAAA, 50, true, true, "RAM: ", "%"))
|
||
|
||
--☯◌☺
|
||
mainContainer.addBreakpointButton = mainContainer.topToolBar:addChild(GUI.adaptiveButton(1, 1, 3, 1, 0x878787, 0xEEEEEE, 0xCCCCCC, 0x444444, "x"))
|
||
mainContainer.addBreakpointButton.onTouch = function()
|
||
addBreakpoint()
|
||
mainContainer:draw()
|
||
buffer.draw()
|
||
end
|
||
|
||
mainContainer.toggleSyntaxHighlightingButton = mainContainer.topToolBar:addChild(GUI.adaptiveButton(1, 1, 3, 1, 0xCCCCCC, 0x444444, 0x696969, 0xEEEEEE, "◌"))
|
||
mainContainer.toggleSyntaxHighlightingButton.switchMode, mainContainer.toggleSyntaxHighlightingButton.pressed = true, true
|
||
mainContainer.toggleSyntaxHighlightingButton.onTouch = function()
|
||
mainContainer.codeView.highlightLuaSyntax = not mainContainer.codeView.highlightLuaSyntax
|
||
config.highlightLuaSyntax = mainContainer.codeView.highlightLuaSyntax
|
||
saveConfig()
|
||
mainContainer:draw()
|
||
buffer.draw()
|
||
end
|
||
|
||
mainContainer.runButton = mainContainer.topToolBar:addChild(GUI.adaptiveButton(1, 1, 3, 1, 0x4B4B4B, 0xEEEEEE, 0xCCCCCC, 0x444444, "▷"))
|
||
mainContainer.runButton.onTouch = function()
|
||
run()
|
||
end
|
||
|
||
mainContainer.toggleLeftToolBarButton = mainContainer.topToolBar:addChild(GUI.adaptiveButton(1, 1, 3, 1, 0xCCCCCC, 0x444444, 0x4B4B4B, 0xEEEEEE, "⇦"))
|
||
mainContainer.toggleLeftToolBarButton.switchMode, mainContainer.toggleLeftToolBarButton.pressed = true, true
|
||
mainContainer.toggleLeftToolBarButton.onTouch = function()
|
||
mainContainer.leftTreeView.hidden = not mainContainer.toggleLeftToolBarButton.pressed
|
||
mainContainer.leftTreeViewResizer.hidden = mainContainer.leftTreeView.hidden
|
||
calculateSizes()
|
||
mainContainer:draw()
|
||
buffer.draw()
|
||
end
|
||
|
||
mainContainer.toggleBottomToolBarButton = mainContainer.topToolBar:addChild(GUI.adaptiveButton(1, 1, 3, 1, 0xCCCCCC, 0x444444, 0x696969, 0xEEEEEE, "⇩"))
|
||
mainContainer.toggleBottomToolBarButton.switchMode, mainContainer.toggleBottomToolBarButton.pressed = true, false
|
||
mainContainer.toggleBottomToolBarButton.onTouch = function()
|
||
mainContainer.bottomToolBar.hidden = not mainContainer.toggleBottomToolBarButton.pressed
|
||
calculateSizes()
|
||
mainContainer:draw()
|
||
buffer.draw()
|
||
end
|
||
|
||
mainContainer.toggleTopToolBarButton = mainContainer.topToolBar:addChild(GUI.adaptiveButton(1, 1, 3, 1, 0xCCCCCC, 0x444444, 0x878787, 0xEEEEEE, "⇧"))
|
||
mainContainer.toggleTopToolBarButton.switchMode, mainContainer.toggleTopToolBarButton.pressed = true, true
|
||
mainContainer.toggleTopToolBarButton.onTouch = function()
|
||
mainContainer.topToolBar.hidden = not mainContainer.toggleTopToolBarButton.pressed
|
||
calculateSizes()
|
||
mainContainer:draw()
|
||
buffer.draw()
|
||
end
|
||
|
||
mainContainer.bottomToolBar = mainContainer:addChild(GUI.container(1, 1, 1, 3))
|
||
mainContainer.bottomToolBar.caseSensitiveButton = mainContainer.bottomToolBar:addChild(GUI.adaptiveButton(1, 1, 2, 1, 0x3C3C3C, 0xEEEEEE, 0xBBBBBB, 0x2D2D2D, "Aa"))
|
||
mainContainer.bottomToolBar.caseSensitiveButton.switchMode = true
|
||
mainContainer.bottomToolBar.onTouch = function()
|
||
find()
|
||
end
|
||
mainContainer.bottomToolBar.inputField = mainContainer.bottomToolBar:addChild(GUI.input(7, 1, 10, 3, 0xCCCCCC, 0x999999, 0x999999, 0xCCCCCC, 0x2D2D2D, "", localization.findSomeShit))
|
||
mainContainer.bottomToolBar.inputField.onInputFinished = function()
|
||
findFromFirstDisplayedLine()
|
||
end
|
||
mainContainer.bottomToolBar.findButton = mainContainer.bottomToolBar:addChild(GUI.adaptiveButton(1, 1, 3, 1, 0x3C3C3C, 0xEEEEEE, 0xBBBBBB, 0x2D2D2D, localization.find))
|
||
mainContainer.bottomToolBar.findButton.onTouch = function()
|
||
find()
|
||
end
|
||
mainContainer.bottomToolBar.hidden = true
|
||
|
||
mainContainer.leftTreeView = mainContainer:addChild(GUI.filesystemTree(1, 1, config.leftTreeViewWidth, 1, 0xCCCCCC, 0x3C3C3C, 0x3C3C3C, 0x999999, 0x3C3C3C, 0xE1E1E1, 0xBBBBBB, 0xAAAAAA, 0xBBBBBB, 0x444444, GUI.filesystemModes.both, GUI.filesystemModes.file))
|
||
mainContainer.leftTreeView.onItemSelected = function(path)
|
||
loadFile(path)
|
||
|
||
updateTitle()
|
||
mainContainer:draw()
|
||
buffer.draw()
|
||
end
|
||
mainContainer.leftTreeView:updateFileList()
|
||
mainContainer.leftTreeViewResizer = mainContainer:addChild(GUI.resizer(1, 1, 3, 5, 0x888888, 0x0))
|
||
mainContainer.leftTreeViewResizer.onResize = function(mainContainer, object, eventData, dragWidth, dragHeight)
|
||
mainContainer.leftTreeView.width = mainContainer.leftTreeView.width + dragWidth
|
||
calculateSizes()
|
||
|
||
mainContainer:draw()
|
||
buffer.draw()
|
||
end
|
||
|
||
mainContainer.leftTreeViewResizer.onResizeFinished = function()
|
||
config.leftTreeViewWidth = mainContainer.leftTreeView.width
|
||
saveConfig()
|
||
end
|
||
|
||
mainContainer.errorContainer = mainContainer:addChild(GUI.container(1, 1, 1, 1))
|
||
mainContainer.errorContainer.backgroundPanel = mainContainer.errorContainer:addChild(GUI.panel(1, 1, 1, 1, 0xFFFFFF, 0.3))
|
||
mainContainer.errorContainer.errorTextBox = mainContainer.errorContainer:addChild(GUI.textBox(3, 2, 1, 1, nil, 0x4B4B4B, {}, 1))
|
||
mainContainer.errorContainer.breakpointExitButton = mainContainer.errorContainer:addChild(GUI.button(1, 1, 1, 1, 0x3C3C3C, 0xCCCCCC, 0x2D2D2D, 0x888888, localization.finishDebug))
|
||
mainContainer.errorContainer.breakpointContinueButton = mainContainer.errorContainer:addChild(GUI.button(1, 1, 1, 1, 0x444444, 0xCCCCCC, 0x2D2D2D, 0x888888, localization.continueDebug))
|
||
mainContainer.errorContainer.breakpointExitButton.onTouch = hideErrorContainer
|
||
mainContainer.errorContainer.breakpointContinueButton.onTouch = continue
|
||
hideErrorContainer()
|
||
|
||
mainContainer.settingsContainer = mainContainer:addChild(GUI.container(1, 1, 1, 1))
|
||
mainContainer.settingsContainer.backgroundPanel = mainContainer.settingsContainer:addChild(GUI.panel(1, 1, mainContainer.settingsContainer.width, mainContainer.settingsContainer.height, 0x0, 0.3))
|
||
mainContainer.settingsContainer.backgroundPanel.eventHandler = function(mainContainer, object, eventData)
|
||
if eventData[1] == "touch" then
|
||
hideSettingsContainer()
|
||
end
|
||
end
|
||
mainContainer.settingsContainer.hidden = true
|
||
|
||
mainContainer.autocompleteWindow = mainContainer:addChild(GUI.object(1, 1, 40, 1))
|
||
mainContainer.autocompleteWindow.maximumHeight = 8
|
||
mainContainer.autocompleteWindow.matches = {}
|
||
mainContainer.autocompleteWindow.fromMatch = 1
|
||
mainContainer.autocompleteWindow.currentMatch = 1
|
||
mainContainer.autocompleteWindow.hidden = true
|
||
mainContainer.autocompleteWindow.draw = function(object)
|
||
mainContainer.autocompleteWindow.x, mainContainer.autocompleteWindow.y = convertTextPositionToScreenCoordinates(mainContainer.autocompleteWindow.currentWordStarting, cursor.position.line)
|
||
mainContainer.autocompleteWindow.x, mainContainer.autocompleteWindow.y = mainContainer.autocompleteWindow.x, mainContainer.autocompleteWindow.y + 1
|
||
|
||
object.height = object.maximumHeight
|
||
if object.height > #object.matches then object.height = #object.matches end
|
||
|
||
buffer.square(object.x, object.y, object.width, object.height, 0xFFFFFF, 0x0, " ")
|
||
|
||
local y = object.y
|
||
for i = object.fromMatch, #object.matches do
|
||
local firstColor, secondColor = 0x3C3C3C, 0x999999
|
||
|
||
if i == object.currentMatch then
|
||
buffer.square(object.x, y, object.width, 1, 0x2D2D2D, 0xEEEEEE, " ")
|
||
firstColor, secondColor = 0xEEEEEE, 0x999999
|
||
end
|
||
|
||
buffer.text(object.x + 1, y, secondColor, unicode.sub(object.matches[i][1], 1, object.width - 2))
|
||
buffer.text(object.x + 1, y, firstColor, unicode.sub(object.matches[i][2], 1, object.width - 2))
|
||
|
||
y = y + 1
|
||
if y > object.y + object.height - 1 then break end
|
||
end
|
||
|
||
if object.height < #object.matches then
|
||
GUI.scrollBar(object.x + object.width - 1, object.y, 1, object.height, 0x444444, 0x00DBFF, 1, #object.matches, object.currentMatch, object.height, 1, true):draw()
|
||
end
|
||
end
|
||
|
||
mainContainer.codeView.eventHandler = function(mainContainer, object, eventData)
|
||
if eventData[1] == "touch" then
|
||
if eventData[5] == 1 then
|
||
createEditOrRightClickMenu(eventData[3], eventData[4])
|
||
else
|
||
setCursorPositionAndClearSelection(convertScreenCoordinatesToTextPosition(eventData[3], eventData[4]))
|
||
end
|
||
|
||
cursor.blinkState = true
|
||
tick()
|
||
elseif eventData[1] == "double_touch" then
|
||
cursor.blinkState = true
|
||
selectWord()
|
||
|
||
mainContainer:draw()
|
||
buffer.draw()
|
||
elseif eventData[1] == "drag" then
|
||
if eventData[5] ~= 1 then
|
||
mainContainer.codeView.selections[1] = mainContainer.codeView.selections[1] or {from = {}, to = {}}
|
||
mainContainer.codeView.selections[1].from.symbol, mainContainer.codeView.selections[1].from.line = cursor.position.symbol, cursor.position.line
|
||
mainContainer.codeView.selections[1].to.symbol, mainContainer.codeView.selections[1].to.line = fixCursorPosition(convertScreenCoordinatesToTextPosition(eventData[3], eventData[4]))
|
||
|
||
if mainContainer.codeView.selections[1].from.line > mainContainer.codeView.selections[1].to.line then
|
||
mainContainer.codeView.selections[1].from.line, mainContainer.codeView.selections[1].to.line = swap(mainContainer.codeView.selections[1].from.line, mainContainer.codeView.selections[1].to.line)
|
||
mainContainer.codeView.selections[1].from.symbol, mainContainer.codeView.selections[1].to.symbol = swap(mainContainer.codeView.selections[1].from.symbol, mainContainer.codeView.selections[1].to.symbol)
|
||
elseif mainContainer.codeView.selections[1].from.line == mainContainer.codeView.selections[1].to.line then
|
||
if mainContainer.codeView.selections[1].from.symbol > mainContainer.codeView.selections[1].to.symbol then
|
||
mainContainer.codeView.selections[1].from.symbol, mainContainer.codeView.selections[1].to.symbol = swap(mainContainer.codeView.selections[1].from.symbol, mainContainer.codeView.selections[1].to.symbol)
|
||
end
|
||
end
|
||
end
|
||
|
||
cursor.blinkState = true
|
||
tick()
|
||
elseif eventData[1] == "key_down" then
|
||
-- Ctrl or CMD
|
||
if keyboard.isKeyDown(29) or keyboard.isKeyDown(219) then
|
||
-- Slash
|
||
if eventData[4] == 53 then
|
||
toggleComment()
|
||
-- ]
|
||
elseif eventData[4] == 27 then
|
||
config.enableAutoBrackets = not config.enableAutoBrackets
|
||
saveConfig()
|
||
-- I
|
||
elseif eventData[4] == 23 then
|
||
toggleEnableAutocompleteDatabase()
|
||
-- A
|
||
elseif eventData[4] == 30 then
|
||
selectAll()
|
||
-- C
|
||
elseif eventData[4] == 46 then
|
||
-- Shift
|
||
if keyboard.isKeyDown(42) then
|
||
selectAndPasteColor()
|
||
else
|
||
copy()
|
||
end
|
||
-- V
|
||
elseif eventData[4] == 47 then
|
||
paste(clipboard)
|
||
-- X
|
||
elseif eventData[4] == 45 then
|
||
cut()
|
||
-- W
|
||
elseif eventData[4] == 17 then
|
||
mainContainer:stopEventHandling()
|
||
-- N
|
||
elseif eventData[4] == 49 then
|
||
newFile()
|
||
-- O
|
||
elseif eventData[4] == 24 then
|
||
openFileWindow()
|
||
-- U
|
||
elseif eventData[4] == 22 and component.isAvailable("internet") then
|
||
downloadFileFromWeb()
|
||
-- S
|
||
elseif eventData[4] == 31 then
|
||
-- Shift
|
||
if mainContainer.leftTreeView.selectedItem and not keyboard.isKeyDown(42) then
|
||
saveFileWindow()
|
||
else
|
||
saveFileAsWindow()
|
||
end
|
||
-- F
|
||
elseif eventData[4] == 33 then
|
||
toggleBottomToolBar()
|
||
-- G
|
||
elseif eventData[4] == 34 then
|
||
find()
|
||
-- L
|
||
elseif eventData[4] == 38 then
|
||
gotoLineWindow()
|
||
-- Backspace
|
||
elseif eventData[4] == 14 then
|
||
deleteLine(cursor.position.line)
|
||
-- Delete
|
||
elseif eventData[4] == 211 then
|
||
deleteLine(cursor.position.line)
|
||
-- R
|
||
elseif eventData[4] == 19 then
|
||
changeResolutionWindow()
|
||
end
|
||
-- Arrows up, down, left, right
|
||
elseif eventData[4] == 200 then
|
||
moveCursor(0, -1)
|
||
elseif eventData[4] == 208 then
|
||
moveCursor(0, 1)
|
||
elseif eventData[4] == 203 then
|
||
moveCursor(-1, 0)
|
||
elseif eventData[4] == 205 then
|
||
moveCursor(1, 0)
|
||
-- Backspace
|
||
elseif eventData[4] == 14 then
|
||
backspace()
|
||
-- Tab
|
||
elseif eventData[4] == 15 then
|
||
if keyboard.isKeyDown(42) then
|
||
indentOrUnindent(false)
|
||
else
|
||
indentOrUnindent(true)
|
||
end
|
||
-- Enter
|
||
elseif eventData[4] == 28 then
|
||
if mainContainer.autocompleteWindow.hidden then
|
||
enter()
|
||
else
|
||
pasteSelectedAutocompletion()
|
||
end
|
||
-- F5
|
||
elseif eventData[4] == 63 then
|
||
run()
|
||
-- F9
|
||
elseif eventData[4] == 67 then
|
||
-- Shift
|
||
if keyboard.isKeyDown(42) then
|
||
clearBreakpoints()
|
||
else
|
||
addBreakpoint()
|
||
end
|
||
-- Home
|
||
elseif eventData[4] == 199 then
|
||
setCursorPositionToHome()
|
||
-- End
|
||
elseif eventData[4] == 207 then
|
||
setCursorPositionToEnd()
|
||
-- Page Up
|
||
elseif eventData[4] == 201 then
|
||
pageUp()
|
||
-- Page Down
|
||
elseif eventData[4] == 209 then
|
||
pageDown()
|
||
-- Delete
|
||
elseif eventData[4] == 211 then
|
||
delete()
|
||
else
|
||
pasteAutoBrackets(eventData[3])
|
||
end
|
||
|
||
cursor.blinkState = true
|
||
tick()
|
||
elseif eventData[1] == "scroll" then
|
||
scroll(eventData[5], config.scrollSpeed)
|
||
tick()
|
||
elseif eventData[1] == "clipboard" then
|
||
paste(splitStringIntoLines(eventData[3]))
|
||
tick()
|
||
elseif not eventData[1] then
|
||
cursor.blinkState = not cursor.blinkState
|
||
tick()
|
||
end
|
||
end
|
||
end
|
||
|
||
---------------------------------------------------- RUSH B! ----------------------------------------------------
|
||
|
||
loadConfig()
|
||
createMainContainer()
|
||
changeResolution(config.screenResolution.width, config.screenResolution.height)
|
||
updateTitle()
|
||
updateRAMProgressBar()
|
||
mainContainer:draw()
|
||
|
||
if args[1] and fs.exists(args[1]) then
|
||
loadFile(args[1])
|
||
else
|
||
newFile()
|
||
end
|
||
|
||
mainContainer:draw()
|
||
buffer.draw()
|
||
mainContainer:startEventHandling(config.cursorBlinkDelay)
|
||
|
||
|