MineOS/Applications/MineCodeIDE/MineCodeIDE.lua
2017-01-25 09:47:50 +03:00

1691 lines
65 KiB
Lua
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---------------------------------------------------- Libraries ----------------------------------------------------
-- "/MineOS/Applications/MineCode IDE.app/MineCode IDE.lua" open /OS.lua
-- package.loaded.syntax = nil
-- package.loaded.ECSAPI = nil
-- package.loaded.GUI = nil
-- package.loaded.windows = nil
-- package.loaded.MineOSCore = nil
require("advancedLua")
local computer = require("computer")
local component = require("component")
local fs = require("filesystem")
local buffer = require("doubleBuffering")
local GUI = require("GUI")
local windows = require("windows")
local MineOSCore = require("MineOSCore")
local event = require("event")
local syntax = require("syntax")
local unicode = require("unicode")
local ecs = require("ECSAPI")
local image = require("image")
local keyboard = require("keyboard")
local palette = require("palette")
local term = require("term")
---------------------------------------------------- Constants ----------------------------------------------------
local args = {...}
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 = {
syntaxColorScheme = syntax.colorScheme,
scrollSpeed = 8,
cursorColor = 0x00A8FF,
cursorSymbol = "",
cursorBlinkDelay = 0.5,
doubleClickDelay = 0.4,
screenScale = 1,
enableAutoBrackets = true,
highlightLuaSyntax = true,
enableAutocompletion = true,
}
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,
},
},
leftTreeView = {
background = 0xCCCCCC,
},
highlights = {
onError = 0xFF4940,
onBreakpoint = 0x990000,
}
}
local possibleBrackets = {
openers = {
["{"] = "}",
["["] = "]",
["("] = ")",
["\""] = "\"",
["\'"] = "\'"
},
closers = {
["}"] = "{",
["]"] = "[",
[")"] = "(",
["\""] = "\"",
["\'"] = "\'"
}
}
local cursor = {
position = {
symbol = 1,
line = 1
},
blinkState = false
}
local resourcesPath = MineOSCore.getCurrentApplicationResourcesDirectory()
local configPath = resourcesPath .. "Config.cfg"
local localization = MineOSCore.getLocalization(resourcesPath .. "Localization/")
local findStartFrom
local clipboard
local lastErrorLine
local breakpointLine = 5
local lastClickUptime = computer.uptime()
local mainWindow = {}
local autocompleteDatabase
------------------------------------------------------------------------------------------------------------------
local function convertTextPositionToScreenCoordinates(symbol, line)
return
mainWindow.codeView.codeAreaPosition + symbol - mainWindow.codeView.fromSymbol + 1,
mainWindow.codeView.y + line - mainWindow.codeView.fromLine
end
local function convertScreenCoordinatesToTextPosition(x, y)
return x - mainWindow.codeView.codeAreaPosition + mainWindow.codeView.fromSymbol - 1, y - mainWindow.codeView.y + mainWindow.codeView.fromLine
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, #mainWindow.codeView.lines do
updateAutocompleteDatabaseFromString(mainWindow.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(mainWindow.codeView.lines[cursor.position.line], i, i):match(shittySymbolsRegexp) then break end
from = i
end
for i = fromSymbol, unicode.len(mainWindow.codeView.lines[cursor.position.line]) do
if unicode.sub(mainWindow.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()
mainWindow.autocompleteWindow.isHidden = true
end
local function showAutocompleteWindow()
if config.enableAutocompletion then
mainWindow.autocompleteWindow.currentWordStarting, mainWindow.autocompleteWindow.currentWordEnding = getCurrentWordStartingAndEnding(cursor.position.symbol - 1)
if mainWindow.autocompleteWindow.currentWordStarting then
mainWindow.autocompleteWindow.matches = getAutocompleteDatabaseMatches(
unicode.sub(
mainWindow.codeView.lines[cursor.position.line],
mainWindow.autocompleteWindow.currentWordStarting,
mainWindow.autocompleteWindow.currentWordEnding
)
)
if #mainWindow.autocompleteWindow.matches > 0 then
mainWindow.autocompleteWindow.fromMatch, mainWindow.autocompleteWindow.currentMatch = 1, 1
mainWindow.autocompleteWindow.isHidden = false
else
hideAutocompleteWindow()
end
else
hideAutocompleteWindow()
end
end
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 calculateSizes()
mainWindow.width, mainWindow.height = buffer.screen.width, buffer.screen.height
mainWindow.leftTreeView.width = math.floor(mainWindow.width * 0.16)
if mainWindow.leftTreeView.isHidden then
mainWindow.codeView.localPosition.x, mainWindow.codeView.width = 1, mainWindow.width
mainWindow.bottomToolBar.localPosition.x, mainWindow.bottomToolBar.width = mainWindow.codeView.localPosition.x, mainWindow.codeView.width
else
mainWindow.codeView.localPosition.x, mainWindow.codeView.width = mainWindow.leftTreeView.width + 1, mainWindow.width - mainWindow.leftTreeView.width
mainWindow.bottomToolBar.localPosition.x, mainWindow.bottomToolBar.width = mainWindow.codeView.localPosition.x, mainWindow.codeView.width
end
if mainWindow.topToolBar.isHidden then
mainWindow.leftTreeView.localPosition.y, mainWindow.leftTreeView.height = 2, mainWindow.height - 1
mainWindow.codeView.localPosition.y, mainWindow.codeView.height = 2, mainWindow.height - 1
mainWindow.errorMessage.localPosition.y = 2
else
mainWindow.leftTreeView.localPosition.y, mainWindow.leftTreeView.height = 5, mainWindow.height - 4
mainWindow.codeView.localPosition.y, mainWindow.codeView.height = 5, mainWindow.height - 4
mainWindow.errorMessage.localPosition.y = 5
end
if mainWindow.bottomToolBar.isHidden then
else
mainWindow.codeView.height = mainWindow.codeView.height - 3
end
mainWindow.settingsContainer.width, mainWindow.settingsContainer.height = mainWindow.width, mainWindow.height
mainWindow.settingsContainer.backgroundPanel.width, mainWindow.settingsContainer.backgroundPanel.height = mainWindow.settingsContainer.width, mainWindow.settingsContainer.height
mainWindow.bottomToolBar.localPosition.y = mainWindow.height - 2
mainWindow.bottomToolBar.findButton.localPosition.x = mainWindow.bottomToolBar.width - mainWindow.bottomToolBar.findButton.width + 1
mainWindow.bottomToolBar.inputTextBox.width = mainWindow.bottomToolBar.width - mainWindow.bottomToolBar.inputTextBox.localPosition.x - mainWindow.bottomToolBar.findButton.width + 1
mainWindow.topToolBar.width, mainWindow.topToolBar.backgroundPanel.width = mainWindow.width, mainWindow.width
mainWindow.titleTextBox.width = math.floor(mainWindow.topToolBar.width * 0.32)
mainWindow.titleTextBox.localPosition.x = math.floor(mainWindow.topToolBar.width / 2 - mainWindow.titleTextBox.width / 2)
mainWindow.runButton.localPosition.x = mainWindow.titleTextBox.localPosition.x - mainWindow.runButton.width - 2
mainWindow.toggleSyntaxHighlightingButton.localPosition.x = mainWindow.runButton.localPosition.x - mainWindow.toggleSyntaxHighlightingButton.width - 2
mainWindow.addBreakpointButton.localPosition.x = mainWindow.toggleSyntaxHighlightingButton.localPosition.x - mainWindow.addBreakpointButton.width - 2
mainWindow.toggleLeftToolBarButton.localPosition.x = mainWindow.titleTextBox.localPosition.x + mainWindow.titleTextBox.width + 2
mainWindow.toggleBottomToolBarButton.localPosition.x = mainWindow.toggleLeftToolBarButton.localPosition.x + mainWindow.toggleLeftToolBarButton.width + 2
mainWindow.toggleTopToolBarButton.localPosition.x = mainWindow.toggleBottomToolBarButton.localPosition.x + mainWindow.toggleBottomToolBarButton.width + 2
mainWindow.RAMUsageProgressBar.localPosition.x = mainWindow.toggleTopToolBarButton.localPosition.x + mainWindow.toggleTopToolBarButton.width + 3
mainWindow.RAMUsageProgressBar.width = mainWindow.topToolBar.width - mainWindow.RAMUsageProgressBar.localPosition.x - 3
mainWindow.errorMessage.localPosition.x, mainWindow.errorMessage.width = mainWindow.titleTextBox.localPosition.x, mainWindow.titleTextBox.width
mainWindow.errorMessage.backgroundPanel.width, mainWindow.errorMessage.errorTextBox.width = mainWindow.errorMessage.width, mainWindow.errorMessage.width - 4
mainWindow.topMenu.width = mainWindow.width
end
local function changeScale(newScale)
buffer.changeResolution(ecs.getScaledResolution(newScale))
calculateSizes()
mainWindow:draw()
buffer.draw()
config.screenScale = newScale
end
local function scalePlus()
if config.screenScale > 0.3 then changeScale(config.screenScale - 0.1); saveConfig() end
end
local function scaleMinus()
if config.screenScale < 1 then changeScale(config.screenScale + 0.1); saveConfig() end
end
local function updateTitle()
if not mainWindow.topToolBar.isHidden then
if mainWindow.errorMessage.isHidden then
mainWindow.titleTextBox.lines[1] = string.limit(localization.file .. ": " .. (mainWindow.leftTreeView.currentFile or localization.none), mainWindow.titleTextBox.width - 4)
mainWindow.titleTextBox.lines[2] = string.limit(localization.cursor .. cursor.position.line .. localization.line .. cursor.position.symbol .. localization.symbol, mainWindow.titleTextBox.width - 4)
if mainWindow.codeView.selections[1] then
local countOfSelectedLines = mainWindow.codeView.selections[1].to.line - mainWindow.codeView.selections[1].from.line + 1
local countOfSelectedSymbols
if mainWindow.codeView.selections[1].from.line == mainWindow.codeView.selections[1].to.line then
countOfSelectedSymbols = unicode.len(unicode.sub(mainWindow.codeView.lines[mainWindow.codeView.selections[1].from.line], mainWindow.codeView.selections[1].from.symbol, mainWindow.codeView.selections[1].to.symbol))
else
countOfSelectedSymbols = unicode.len(unicode.sub(mainWindow.codeView.lines[mainWindow.codeView.selections[1].from.line], mainWindow.codeView.selections[1].from.symbol, -1))
for line = mainWindow.codeView.selections[1].from.line + 1, mainWindow.codeView.selections[1].to.line - 1 do
countOfSelectedSymbols = countOfSelectedSymbols + unicode.len(mainWindow.codeView.lines[line])
end
countOfSelectedSymbols = countOfSelectedSymbols + unicode.len(unicode.sub(mainWindow.codeView.lines[mainWindow.codeView.selections[1].to.line], 1, mainWindow.codeView.selections[1].to.symbol))
end
mainWindow.titleTextBox.lines[3] = string.limit(localization.selection .. countOfSelectedLines .. localization.lines .. countOfSelectedSymbols .. localization.symbols, mainWindow.titleTextBox.width - 4)
else
mainWindow.titleTextBox.lines[3] = string.limit(localization.selection .. localization.none, mainWindow.titleTextBox.width - 4)
end
else
mainWindow.titleTextBox.lines[1], mainWindow.titleTextBox.lines[3] = " ", " "
if breakpointLine then
mainWindow.titleTextBox.lines[2] = localization.debugging .. cursor.position.line
else
mainWindow.titleTextBox.lines[2] = localization.runtimeError
end
end
end
end
local function calculateErrorMessageSizeAndBeep()
mainWindow.errorMessage.height = 2 + #mainWindow.errorMessage.errorTextBox.lines
mainWindow.errorMessage.backgroundPanel.height = mainWindow.errorMessage.height
mainWindow.errorMessage.errorTextBox.height = mainWindow.errorMessage.height - 2
updateTitle()
mainWindow:draw()
buffer.draw()
for i = 1, 3 do component.computer.beep(1500, 0.08) end
end
local function showBreakpointMessage(variables)
mainWindow.titleTextBox.colors.background, mainWindow.titleTextBox.colors.text = colors.title.onError.background, colors.title.onError.text
mainWindow.errorMessage.isHidden = false
mainWindow.errorMessage.errorTextBox:setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top)
mainWindow.errorMessage.errorTextBox.lines = {}
for variable, value in pairs(variables) do
table.insert(mainWindow.errorMessage.errorTextBox.lines, variable .. " = " .. value)
end
if #mainWindow.errorMessage.errorTextBox.lines > 0 then
table.insert(mainWindow.errorMessage.errorTextBox.lines, 1, " ")
table.insert(mainWindow.errorMessage.errorTextBox.lines, 1, {text = localization.variables, color = 0x0})
else
table.insert(mainWindow.errorMessage.errorTextBox.lines, 1, {text = localization.variablesNotAvailable, color = 0x0})
end
calculateErrorMessageSizeAndBeep()
end
local function showErrorMessage(text)
mainWindow.titleTextBox.colors.background, mainWindow.titleTextBox.colors.text = colors.title.onError.background, colors.title.onError.text
mainWindow.errorMessage.isHidden = false
mainWindow.errorMessage.errorTextBox:setAlignment(GUI.alignment.horizontal.left, GUI.alignment.vertical.top)
mainWindow.errorMessage.errorTextBox.lines = string.wrap({text}, mainWindow.errorMessage.errorTextBox.width)
calculateErrorMessageSizeAndBeep()
end
local function hideErrorMessage()
mainWindow.titleTextBox.colors.background, mainWindow.titleTextBox.colors.text = colors.title.default.background, colors.title.default.text
mainWindow.errorMessage.isHidden = true
end
local function hideSettingsContainer()
for childIndex = 2, #mainWindow.settingsContainer.children do mainWindow.settingsContainer.children[childIndex] = nil end
mainWindow.settingsContainer.isHidden = true
mainWindow:draw()
buffer.draw()
end
local function clearHighlights()
if lastErrorLine then
mainWindow.codeView.highlights[lastErrorLine] = nil
lastErrorLine = nil
end
if breakpointLine then
mainWindow.codeView.highlights[breakpointLine] = nil
breakpointLine = nil
end
end
local function clearSelection()
mainWindow.codeView.selections[1] = nil
end
local function fixFromLineByCursorPosition()
if mainWindow.codeView.fromLine > cursor.position.line then
mainWindow.codeView.fromLine = cursor.position.line
elseif mainWindow.codeView.fromLine + mainWindow.codeView.height - 2 < cursor.position.line then
mainWindow.codeView.fromLine = cursor.position.line - mainWindow.codeView.height + 2
end
end
local function fixFromSymbolByCursorPosition()
if mainWindow.codeView.fromSymbol > cursor.position.symbol then
mainWindow.codeView.fromSymbol = cursor.position.symbol
elseif mainWindow.codeView.fromSymbol + mainWindow.codeView.codeAreaWidth - 3 < cursor.position.symbol then
mainWindow.codeView.fromSymbol = cursor.position.symbol - mainWindow.codeView.codeAreaWidth + 3
end
end
local function fixCursorPosition(symbol, line)
if line < 1 then
line = 1
elseif line > #mainWindow.codeView.lines then
line = #mainWindow.codeView.lines
end
local lineLength = unicode.len(mainWindow.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()
clearHighlights()
hideErrorMessage()
hideAutocompleteWindow()
end
local function setCursorPositionAndClearSelection(symbol, line)
setCursorPosition(symbol, line)
clearSelection()
end
local function isClickedOnCodeArea(x, y)
return
x >= mainWindow.codeView.codeAreaPosition and
y >= mainWindow.codeView.y and
x < mainWindow.width and
y < mainWindow.codeView.y + mainWindow.codeView.height - 1
end
local function moveCursor(symbolOffset, lineOffset)
if mainWindow.autocompleteWindow.isHidden or lineOffset == 0 then
if mainWindow.codeView.selections[1] then
if symbolOffset < 0 or lineOffset < 0 then
setCursorPositionAndClearSelection(mainWindow.codeView.selections[1].from.symbol, mainWindow.codeView.selections[1].from.line)
else
setCursorPositionAndClearSelection(mainWindow.codeView.selections[1].to.symbol, mainWindow.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(mainWindow.codeView.lines[newLine] or "") + 1 then
newLine, newSymbol = newLine + 1, 1
end
setCursorPositionAndClearSelection(newSymbol, newLine)
end
elseif not mainWindow.autocompleteWindow.isHidden then
mainWindow.autocompleteWindow.currentMatch = mainWindow.autocompleteWindow.currentMatch + lineOffset
if mainWindow.autocompleteWindow.currentMatch < 1 then
mainWindow.autocompleteWindow.currentMatch = 1
elseif mainWindow.autocompleteWindow.currentMatch > #mainWindow.autocompleteWindow.matches then
mainWindow.autocompleteWindow.currentMatch = #mainWindow.autocompleteWindow.matches
elseif mainWindow.autocompleteWindow.currentMatch < mainWindow.autocompleteWindow.fromMatch then
mainWindow.autocompleteWindow.fromMatch = mainWindow.autocompleteWindow.currentMatch
elseif mainWindow.autocompleteWindow.currentMatch > mainWindow.autocompleteWindow.fromMatch + mainWindow.autocompleteWindow.height - 1 then
mainWindow.autocompleteWindow.fromMatch = mainWindow.autocompleteWindow.currentMatch - mainWindow.autocompleteWindow.height + 1
end
end
end
local function setCursorPositionToHome()
setCursorPositionAndClearSelection(1, 1)
end
local function setCursorPositionToEnd()
setCursorPositionAndClearSelection(unicode.len(mainWindow.codeView.lines[#mainWindow.codeView.lines]) + 1, #mainWindow.codeView.lines)
end
local function scroll(direction, speed)
if direction == 1 then
if mainWindow.codeView.fromLine > speed then
mainWindow.codeView.fromLine = mainWindow.codeView.fromLine - speed
else
mainWindow.codeView.fromLine = 1
end
else
if mainWindow.codeView.fromLine < #mainWindow.codeView.lines - speed then
mainWindow.codeView.fromLine = mainWindow.codeView.fromLine + speed
else
mainWindow.codeView.fromLine = #mainWindow.codeView.lines
end
end
end
local function pageUp()
scroll(1, mainWindow.codeView.height - 2)
end
local function pageDown()
scroll(-1, mainWindow.codeView.height - 2)
end
local function gotoLine(line)
mainWindow.codeView.fromLine = math.floor(line - mainWindow.codeView.height / 2) + 1
if mainWindow.codeView.fromLine < 1 then
mainWindow.codeView.fromLine = 1
elseif mainWindow.codeView.fromLine > #mainWindow.codeView.lines then
mainWindow.codeView.fromLine = #mainWindow.codeView.lines
end
end
local function selectWord()
local from, to = getCurrentWordStartingAndEnding(cursor.position.symbol)
if from and to then
mainWindow.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(" ", mainWindow.codeView.indentationWidth))
return result
end
local function removeWindowsLineEndings(text)
local result = text:gsub("\r\n", "\n")
return result
end
local function createInputTextBoxForSettingsWindow(title, placeholder)
mainWindow.settingsContainer.isHidden = false
local elementWidth = math.floor(mainWindow.width * 0.3)
local x, y = math.floor(mainWindow.width / 2 - elementWidth / 2), math.floor(mainWindow.height / 2) - 3
mainWindow.settingsContainer:addLabel(1, y, mainWindow.settingsContainer.width, 1, 0xFFFFFF, title):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top); y = y + 3
return mainWindow.settingsContainer:addInputTextBox(x, y, elementWidth, 3, 0xCCCCCC, 0x777777, 0xCCCCCC, 0x2D2D2D, "", placeholder)
end
local function newFile()
autocompleteDatabase = {}
mainWindow.codeView.lines = {""}
mainWindow.codeView.maximumLineLength = 1
mainWindow.leftTreeView.currentFile = nil
setCursorPositionAndClearSelection(1, 1)
end
local function loadFile(path)
newFile()
local file = io.open(path, "r")
for line in file:lines() do
line = removeWindowsLineEndings(removeTabs(line))
table.insert(mainWindow.codeView.lines, line)
mainWindow.codeView.maximumLineLength = math.max(mainWindow.codeView.maximumLineLength, unicode.len(line))
end
file:close()
mainWindow.leftTreeView.currentFile = path
updateAutocompleteDatabaseFromFile()
end
local function saveFile(path)
fs.makeDirectory(fs.path(path))
local file = io.open(path, "w")
for line = 1, #mainWindow.codeView.lines do
file:write(mainWindow.codeView.lines[line], "\n")
end
file:close()
end
local function open()
local inputTextBox = createInputTextBoxForSettingsWindow(localization.openFile, localization.pathToFile)
inputTextBox.validator = function(text)
if fs.exists(text) then return true end
end
inputTextBox.onInputFinished = function()
loadFile(inputTextBox.text)
hideSettingsContainer()
end
end
local function saveAs()
local inputTextBox = createInputTextBoxForSettingsWindow(localization.saveAs, localization.pathToFile)
inputTextBox.onInputFinished = function()
saveFile(inputTextBox.text)
mainWindow.leftTreeView.currentFile = inputTextBox.text
mainWindow.leftTreeView:updateFileList()
hideSettingsContainer()
end
end
local function save()
saveFile(mainWindow.leftTreeView.currentFile)
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 downloadFromWeb()
local inputTextBox = createInputTextBoxForSettingsWindow(localization.getFromWeb, localization.url)
inputTextBox.onInputFinished = function()
local success, reason = ecs.internetRequest(inputTextBox.text)
if success then
newFile()
mainWindow.codeView.lines, mainWindow.codeView.maximumLineLength = splitStringIntoLines(reason)
else
GUI.error(reason, {title = {color = 0xFFDB40, text = "Failed to connect to URL"}})
end
hideSettingsContainer()
end
end
------------------------------------------------------------------------------------------------------------------
local function addErrorLine(line)
lastErrorLine = line
mainWindow.codeView.highlights[line] = colors.highlights.onError
end
local function addBreakpoint()
clearHighlights()
breakpointLine = cursor.position.line
mainWindow.codeView.highlights[breakpointLine] = colors.highlights.onBreakpoint
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 createBreakpointError(variables)
local errorMessage = "error({variables={"
for variable in pairs(variables) do
errorMessage = errorMessage .. "[\"" .. variable .. "\"] = type(" .. variable .. ") == \"string\" and \"\\\"\" .. " .. variable .. " .. \"\\\"\" or tostring(" .. variable .. "),"
end
return errorMessage .. "}})"
end
local function run()
if breakpointLine then
table.insert(mainWindow.codeView.lines, breakpointLine, createBreakpointError(getVariables(mainWindow.codeView.lines[breakpointLine])))
end
local loadSuccess, loadReason = load(table.concat(mainWindow.codeView.lines, "\n"))
if loadSuccess then
local oldResolutionX, oldResolutionY = component.gpu.getResolution()
component.gpu.setBackground(0x1B1B1B)
component.gpu.setForeground(0xFFFFFF)
component.gpu.fill(1, 1, oldResolutionX, oldResolutionY, " ")
term.setCursor(1, 1)
local xpcallSuccess, xpcallReason = xpcall(loadSuccess, debug.traceback)
local xpcallReasonType = type(xpcallReason)
if breakpointLine then
table.remove(mainWindow.codeView.lines, breakpointLine)
end
if xpcallSuccess or (xpcallReasonType == "table" and xpcallReason.terminated == true) then
MineOSCore.waitForPressingAnyKey()
end
buffer.changeResolution(oldResolutionX, oldResolutionY)
if not xpcallSuccess then
if xpcallReasonType == "table" then
if xpcallReason.variables then
gotoLine(breakpointLine)
showBreakpointMessage(xpcallReason.variables)
end
else
showErrorMessage(xpcallReason)
end
end
mainWindow:draw()
buffer:draw()
else
addErrorLine(tonumber(loadReason:match("^%[.+%]%:(%d+)%:")))
gotoLine(lastErrorLine)
showErrorMessage(loadReason)
end
end
local function deleteLine(line)
if #mainWindow.codeView.lines > 1 then
table.remove(mainWindow.codeView.lines, line)
setCursorPositionAndClearSelection(1, cursor.position.line)
updateAutocompleteDatabaseFromFile()
end
end
local function deleteSpecifiedData(fromSymbol, fromLine, toSymbol, toLine)
local upperLine = unicode.sub(mainWindow.codeView.lines[fromLine], 1, fromSymbol - 1)
local lowerLine = unicode.sub(mainWindow.codeView.lines[toLine], toSymbol + 1, -1)
for line = fromLine + 1, toLine do
table.remove(mainWindow.codeView.lines, fromLine + 1)
end
mainWindow.codeView.lines[fromLine] = upperLine .. lowerLine
setCursorPositionAndClearSelection(fromSymbol, fromLine)
updateAutocompleteDatabaseFromFile()
end
local function deleteSelectedData()
if mainWindow.codeView.selections[1] then
deleteSpecifiedData(
mainWindow.codeView.selections[1].from.symbol,
mainWindow.codeView.selections[1].from.line,
mainWindow.codeView.selections[1].to.symbol,
mainWindow.codeView.selections[1].to.line
)
clearSelection()
end
end
local function copy()
if mainWindow.codeView.selections[1] then
if mainWindow.codeView.selections[1].to.line == mainWindow.codeView.selections[1].from.line then
clipboard = { unicode.sub(mainWindow.codeView.lines[mainWindow.codeView.selections[1].from.line], mainWindow.codeView.selections[1].from.symbol, mainWindow.codeView.selections[1].to.symbol) }
else
clipboard = { unicode.sub(mainWindow.codeView.lines[mainWindow.codeView.selections[1].from.line], mainWindow.codeView.selections[1].from.symbol, -1) }
for line = mainWindow.codeView.selections[1].from.line + 1, mainWindow.codeView.selections[1].to.line - 1 do
table.insert(clipboard, mainWindow.codeView.lines[line])
end
table.insert(clipboard, unicode.sub(mainWindow.codeView.lines[mainWindow.codeView.selections[1].to.line], 1, mainWindow.codeView.selections[1].to.symbol))
end
end
end
local function cut()
if mainWindow.codeView.selections[1] then
copy()
deleteSelectedData()
end
end
local function pasteSelectedAutocompletion()
local firstPart = unicode.sub(mainWindow.codeView.lines[cursor.position.line], 1, mainWindow.autocompleteWindow.currentWordStarting - 1)
local secondPart = unicode.sub(mainWindow.codeView.lines[cursor.position.line], mainWindow.autocompleteWindow.currentWordEnding + 1, -1)
mainWindow.codeView.lines[cursor.position.line] = firstPart .. mainWindow.autocompleteWindow.matches[mainWindow.autocompleteWindow.currentMatch][1] .. secondPart
setCursorPositionAndClearSelection(unicode.len(firstPart .. mainWindow.autocompleteWindow.matches[mainWindow.autocompleteWindow.currentMatch][1]) + 1, cursor.position.line)
hideAutocompleteWindow()
end
local function paste(pasteLines)
if pasteLines then
if mainWindow.codeView.selections[1] then
deleteSelectedData()
end
local firstPart = unicode.sub(mainWindow.codeView.lines[cursor.position.line], 1, cursor.position.symbol - 1)
local secondPart = unicode.sub(mainWindow.codeView.lines[cursor.position.line], cursor.position.symbol, -1)
if #pasteLines == 1 then
mainWindow.codeView.lines[cursor.position.line] = firstPart .. pasteLines[1] .. secondPart
setCursorPositionAndClearSelection(cursor.position.symbol + unicode.len(pasteLines[1]), cursor.position.line)
else
mainWindow.codeView.lines[cursor.position.line] = firstPart .. pasteLines[1]
for pasteLine = #pasteLines - 1, 2, -1 do
table.insert(mainWindow.codeView.lines, cursor.position.line + 1, pasteLines[pasteLine])
end
table.insert(mainWindow.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 mainWindow.codeView.selections[1] and mainWindow.codeView.selections[1].from.line == mainWindow.codeView.selections[1].to.line then
startColor = tonumber(unicode.sub(mainWindow.codeView.lines[mainWindow.codeView.selections[1].from.line], mainWindow.codeView.selections[1].from.symbol, mainWindow.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(mainWindow.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 mainWindow.codeView.selections[1] then
local firstPart = unicode.sub(mainWindow.codeView.lines[mainWindow.codeView.selections[1].from.line], 1, mainWindow.codeView.selections[1].from.symbol - 1)
local secondPart = unicode.sub(mainWindow.codeView.lines[mainWindow.codeView.selections[1].from.line], mainWindow.codeView.selections[1].from.symbol, -1)
mainWindow.codeView.lines[mainWindow.codeView.selections[1].from.line] = firstPart .. char .. secondPart
mainWindow.codeView.selections[1].from.symbol = mainWindow.codeView.selections[1].from.symbol + 1
if mainWindow.codeView.selections[1].to.line == mainWindow.codeView.selections[1].from.line then
mainWindow.codeView.selections[1].to.symbol = mainWindow.codeView.selections[1].to.symbol + 1
end
firstPart = unicode.sub(mainWindow.codeView.lines[mainWindow.codeView.selections[1].to.line], 1, mainWindow.codeView.selections[1].to.symbol)
secondPart = unicode.sub(mainWindow.codeView.lines[mainWindow.codeView.selections[1].to.line], mainWindow.codeView.selections[1].to.symbol + 1, -1)
mainWindow.codeView.lines[mainWindow.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(mainWindow.codeView.lines[cursor.position.line], cursor.position.symbol - 1, cursor.position.symbol - 1)
local currentSymbol = unicode.sub(mainWindow.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 mainWindow.codeView.selections[1] then
deleteSelectedData()
else
if cursor.position.symbol < unicode.len(mainWindow.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(mainWindow.codeView.lines[cursor.position.line]) + 1, cursor.position.line, 0, cursor.position.line + 1)
end
end
-- updateAutocompleteDatabaseFromFile()
showAutocompleteWindow()
end
end
local function backspace()
if mainWindow.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(mainWindow.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(mainWindow.codeView.lines[cursor.position.line], 1, cursor.position.symbol - 1)
local secondPart = unicode.sub(mainWindow.codeView.lines[cursor.position.line], cursor.position.symbol, -1)
mainWindow.codeView.lines[cursor.position.line] = firstPart
table.insert(mainWindow.codeView.lines, cursor.position.line + 1, secondPart)
setCursorPositionAndClearSelection(1, cursor.position.line + 1)
end
local function selectAll()
mainWindow.codeView.selections[1] = {
from = {
symbol = 1, line = 1
},
to = {
symbol = unicode.len(mainWindow.codeView.lines[#mainWindow.codeView.lines]), line = #mainWindow.codeView.lines
}
}
end
local function isLineCommented(line)
return mainWindow.codeView.lines[line]:match("%-%-[^%-]")
end
local function commentLine(line)
mainWindow.codeView.lines[line] = "-- " .. mainWindow.codeView.lines[line]
end
local function uncommentLine(line)
mainWindow.codeView.lines[line], countOfReplaces = mainWindow.codeView.lines[line]:gsub("%-%-%s", "", 1)
return countOfReplaces
end
local function toggleComment()
if mainWindow.codeView.selections[1] then
local allLinesAreCommented = true
for line = mainWindow.codeView.selections[1].from.line, mainWindow.codeView.selections[1].to.line do
if not isLineCommented(line) then
allLinesAreCommented = false
end
end
for line = mainWindow.codeView.selections[1].from.line, mainWindow.codeView.selections[1].to.line do
if allLinesAreCommented then
uncommentLine(line)
else
commentLine(line)
end
end
local modifyer = 3
if allLinesAreCommented then
modifyer = -3
end
setCursorPosition(cursor.position.symbol + modifyer, cursor.position.line)
mainWindow.codeView.selections[1].from.symbol, mainWindow.codeView.selections[1].to.symbol = mainWindow.codeView.selections[1].from.symbol + modifyer, mainWindow.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)
mainWindow.codeView.lines[line] = string.rep(" ", mainWindow.codeView.indentationWidth) .. mainWindow.codeView.lines[line]
end
local function unindentLine(line)
mainWindow.codeView.lines[line], countOfReplaces = string.gsub(mainWindow.codeView.lines[line], "^" .. string.rep("%s", mainWindow.codeView.indentationWidth), "")
return countOfReplaces
end
local function indentOrUnindent(isIndent)
if mainWindow.codeView.selections[1] then
local countOfReplacesInFirstLine, countOfReplacesInLastLine
for line = mainWindow.codeView.selections[1].from.line, mainWindow.codeView.selections[1].to.line do
if isIndent then
indentLine(line)
else
local countOfReplaces = unindentLine(line)
if line == mainWindow.codeView.selections[1].from.line then
countOfReplacesInFirstLine = countOfReplaces
elseif line == mainWindow.codeView.selections[1].to.line then
countOfReplacesInLastLine = countOfReplaces
end
end
end
if isIndent then
setCursorPosition(cursor.position.symbol + mainWindow.codeView.indentationWidth, cursor.position.line)
mainWindow.codeView.selections[1].from.symbol, mainWindow.codeView.selections[1].to.symbol = mainWindow.codeView.selections[1].from.symbol + mainWindow.codeView.indentationWidth, mainWindow.codeView.selections[1].to.symbol + mainWindow.codeView.indentationWidth
else
if countOfReplacesInFirstLine > 0 then
mainWindow.codeView.selections[1].from.symbol = mainWindow.codeView.selections[1].from.symbol - mainWindow.codeView.indentationWidth
if cursor.position.line == mainWindow.codeView.selections[1].from.line then
setCursorPosition(cursor.position.symbol - mainWindow.codeView.indentationWidth, cursor.position.line)
end
end
if countOfReplacesInLastLine > 0 then
mainWindow.codeView.selections[1].to.symbol = mainWindow.codeView.selections[1].to.symbol - mainWindow.codeView.indentationWidth
if cursor.position.line == mainWindow.codeView.selections[1].to.line then
setCursorPosition(cursor.position.symbol - mainWindow.codeView.indentationWidth, cursor.position.line)
end
end
end
else
if isIndent then
indentLine(cursor.position.line)
setCursorPositionAndClearSelection(cursor.position.symbol + mainWindow.codeView.indentationWidth, cursor.position.line)
else
if unindentLine(cursor.position.line) > 0 then
setCursorPositionAndClearSelection(cursor.position.symbol - mainWindow.codeView.indentationWidth, cursor.position.line)
end
end
end
end
local function updateRAMProgressBar()
if not mainWindow.topToolBar.isHidden then
local totalMemory = computer.totalMemory()
mainWindow.RAMUsageProgressBar.value = math.ceil((totalMemory - computer.freeMemory()) / totalMemory * 100)
end
end
local function find()
if not mainWindow.bottomToolBar.isHidden and mainWindow.bottomToolBar.inputTextBox.text ~= "" then
findStartFrom = findStartFrom + 1
for line = findStartFrom, #mainWindow.codeView.lines do
local whereToFind, whatToFind = mainWindow.codeView.lines[line], mainWindow.bottomToolBar.inputTextBox.text
if not mainWindow.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
mainWindow.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", {title = {color = 0xFFDB40, text = "Warning"}})
end
end
findStartFrom = 0
end
end
local function findFromFirstDisplayedLine()
findStartFrom = mainWindow.codeView.fromLine
find()
end
local function toggleBottomToolBar()
mainWindow.bottomToolBar.isHidden = not mainWindow.bottomToolBar.isHidden
mainWindow.toggleBottomToolBarButton.pressed = not mainWindow.bottomToolBar.isHidden
calculateSizes()
if not mainWindow.bottomToolBar.isHidden then
mainWindow:draw()
mainWindow.bottomToolBar.inputTextBox:input()
findFromFirstDisplayedLine()
end
end
local function toggleTopToolBar()
mainWindow.topToolBar.isHidden = not mainWindow.topToolBar.isHidden
mainWindow.toggleTopToolBarButton.pressed = not mainWindow.topToolBar.isHidden
calculateSizes()
end
local function toggleLeftToolBar()
mainWindow.leftTreeView.isHidden = not mainWindow.leftTreeView.isHidden
mainWindow.toggleLeftToolBarButton.pressed = not mainWindow.leftTreeView.isHidden
calculateSizes()
end
local function createWindow()
mainWindow = windows.fullScreen()
mainWindow.codeView = mainWindow:addCodeView(1, 1, 1, 1, {""}, 1, 1, 1, {}, {}, config.highlightLuaSyntax, 2)
mainWindow.codeView.scrollBars.vertical.onTouch = function()
mainWindow.codeView.fromLine = mainWindow.codeView.scrollBars.vertical.value
end
mainWindow.codeView.scrollBars.horizontal.onTouch = function()
mainWindow.codeView.fromSymbol = mainWindow.codeView.scrollBars.horizontal.value
end
local editOrRightClickMenu = GUI.contextMenu(1, 1)
editOrRightClickMenu:addItem(localization.cut, not mainWindow.codeView.selections[1], "^X").onTouch = function()
cut()
end
editOrRightClickMenu:addItem(localization.copy, not mainWindow.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:addSeparator()
editOrRightClickMenu:addItem(localization.deleteLine, false, "^Del").onTouch = function()
deleteLine(cursor.position.line)
end
editOrRightClickMenu:addItem(localization.selectAndPasteColor, false, "^⇧C").onTouch = function()
selectAndPasteColor()
end
editOrRightClickMenu:addSeparator()
editOrRightClickMenu:addItem(localization.selectWord).onTouch = function()
selectWord()
end
editOrRightClickMenu:addItem(localization.selectAll, false, "^A").onTouch = function()
selectAll()
end
mainWindow.topMenu = mainWindow:addMenu(1, 1, 1, colors.topMenu.backgroundColor, colors.topMenu.textColor, colors.topMenu.backgroundPressedColor, colors.topMenu.textPressedColor)
local item1 = mainWindow.topMenu:addItem("MineCode", 0x0)
item1.onTouch = function()
local menu = GUI.contextMenu(item1.x, item1.y + 1)
menu:addItem(localization.about).onTouch = function()
mainWindow.settingsContainer.isHidden = false
local y = math.floor(mainWindow.settingsContainer.height / 2 - #about / 2)
mainWindow.settingsContainer:addTextBox(1, y, mainWindow.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()
mainWindow:close()
end
menu:show()
end
local item2 = mainWindow.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()
end
menu:addItem(localization.open, false, "^O").onTouch = function()
open()
end
if component.isAvailable("internet") then
menu:addItem(localization.getFromWeb, false, "^U").onTouch = function()
downloadFromWeb()
end
end
menu:addSeparator()
menu:addItem(localization.save, not mainWindow.leftTreeView.currentFile, "^S").onTouch = function()
save()
end
menu:addItem(localization.saveAs, false, "^⇧S").onTouch = function()
saveAs()
end
menu:show()
end
local item3 = mainWindow.topMenu:addItem(localization.edit)
item3.onTouch = function()
editOrRightClickMenu.x, editOrRightClickMenu.y = item3.x, item3.y + 1
editOrRightClickMenu:show()
end
local item4 = mainWindow.topMenu:addItem(localization.properties)
item4.onTouch = function()
local menu = GUI.contextMenu(item4.x, item4.y + 1)
menu:addItem(localization.colorScheme).onTouch = function()
mainWindow.settingsContainer.isHidden = 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(mainWindow.settingsContainer.width / colorSelectorCountX * 0.8), 3, 2, 1
local startX, y = math.floor(mainWindow.settingsContainer.width / 2 - (colorSelectorCountX * (colorSelectorWidth + colorSelectorSpaceX) - colorSelectorSpaceX) / 2), math.floor(mainWindow.settingsContainer.height / 2 - (colorSelectorCountY * (colorSelectorHeight + colorSelectorSpaceY) - colorSelectorSpaceY + 3) / 2)
mainWindow.settingsContainer:addLabel(1, y, mainWindow.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 = mainWindow.settingsContainer:addColorSelector(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()
mainWindow.settingsContainer.isHidden = false
local elementWidth = math.floor(mainWindow.width * 0.3)
local x, y = math.floor(mainWindow.width / 2 - elementWidth / 2), math.floor(mainWindow.height / 2) - 7
mainWindow.settingsContainer:addLabel(1, y, mainWindow.settingsContainer.width, 1, 0xFFFFFF, localization.cursorProperties):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top); y = y + 3
local inputTextBox = mainWindow.settingsContainer:addInputTextBox(x, y, elementWidth, 3, 0xCCCCCC, 0x777777, 0xCCCCCC, 0x2D2D2D, config.cursorSymbol, localization.cursorSymbol); y = y + 5
inputTextBox.validator = function(text)
if unicode.len(text) == 1 then return true end
end
inputTextBox.onInputFinished = function()
config.cursorSymbol = inputTextBox.text; saveConfig()
end
local colorSelector = mainWindow.settingsContainer:addColorSelector(x, y, elementWidth, 3, config.cursorColor, localization.cursorColor); y = y + 5
colorSelector.onTouch = function()
config.cursorColor = colorSelector.color; saveConfig()
end
local horizontalSlider = mainWindow.settingsContainer:addHorizontalSlider(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 mainWindow.topToolBar.isHidden 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()
config.enableAutocompletion = not config.enableAutocompletion
saveConfig()
end
menu:addSeparator()
menu:addItem(localization.scalePlus, false, "^+").onTouch = function()
scalePlus()
end
menu:addItem(localization.scaleMinus, false, "^-").onTouch = function()
scaleMinus()
end
menu:show()
end
local item5 = mainWindow.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:show()
end
mainWindow.topToolBar = mainWindow:addContainer(1, 2, 1, 3)
mainWindow.topToolBar.backgroundPanel = mainWindow.topToolBar:addPanel(1, 1, 1, 3, colors.topToolBar)
mainWindow.titleTextBox = mainWindow.topToolBar:addTextBox(1, 1, 1, 3, 0x0, 0x0, {}, 1):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top)
local titleTextBoxOldDraw = mainWindow.titleTextBox.draw
mainWindow.titleTextBox.draw = function(titleTextBox)
titleTextBoxOldDraw(titleTextBox)
local sidesColor = mainWindow.errorMessage.isHidden 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
mainWindow.RAMUsageProgressBar = mainWindow.topToolBar:addProgressBar(1, 2, 1, 0x777777, 0xBBBBBB, 0xAAAAAA, 50, true, true, "RAM: ", "%")
--☯◌☺
mainWindow.addBreakpointButton = mainWindow.topToolBar:addAdaptiveButton(1, 1, 3, 1, 0x878787, 0xEEEEEE, 0xCCCCCC, 0x444444, "x")
mainWindow.addBreakpointButton.onTouch = function()
addBreakpoint()
end
mainWindow.toggleSyntaxHighlightingButton = mainWindow.topToolBar:addAdaptiveButton(1, 1, 3, 1, 0xCCCCCC, 0x444444, 0x696969, 0xEEEEEE, "")
mainWindow.toggleSyntaxHighlightingButton.switchMode, mainWindow.toggleSyntaxHighlightingButton.pressed = true, true
mainWindow.toggleSyntaxHighlightingButton.onTouch = function()
mainWindow.codeView.highlightLuaSyntax = not mainWindow.codeView.highlightLuaSyntax
config.highlightLuaSyntax = mainWindow.codeView.highlightLuaSyntax
saveConfig()
end
mainWindow.runButton = mainWindow.topToolBar:addAdaptiveButton(1, 1, 3, 1, 0x4B4B4B, 0xEEEEEE, 0xCCCCCC, 0x444444, "")
mainWindow.runButton.onTouch = function()
run()
end
mainWindow.toggleLeftToolBarButton = mainWindow.topToolBar:addAdaptiveButton(1, 1, 3, 1, 0xCCCCCC, 0x444444, 0x4B4B4B, 0xEEEEEE, "")
mainWindow.toggleLeftToolBarButton.switchMode, mainWindow.toggleLeftToolBarButton.pressed = true, true
mainWindow.toggleLeftToolBarButton.onTouch = function()
mainWindow.leftTreeView.isHidden = not mainWindow.toggleLeftToolBarButton.pressed
calculateSizes()
end
mainWindow.toggleBottomToolBarButton = mainWindow.topToolBar:addAdaptiveButton(1, 1, 3, 1, 0xCCCCCC, 0x444444, 0x696969, 0xEEEEEE, "")
mainWindow.toggleBottomToolBarButton.switchMode, mainWindow.toggleBottomToolBarButton.pressed = true, false
mainWindow.toggleBottomToolBarButton.onTouch = function()
mainWindow.bottomToolBar.isHidden = not mainWindow.toggleBottomToolBarButton.pressed
calculateSizes()
end
mainWindow.toggleTopToolBarButton = mainWindow.topToolBar:addAdaptiveButton(1, 1, 3, 1, 0xCCCCCC, 0x444444, 0x878787, 0xEEEEEE, "")
mainWindow.toggleTopToolBarButton.switchMode, mainWindow.toggleTopToolBarButton.pressed = true, true
mainWindow.toggleTopToolBarButton.onTouch = function()
mainWindow.topToolBar.isHidden = not mainWindow.toggleTopToolBarButton.pressed
calculateSizes()
end
mainWindow.bottomToolBar = mainWindow:addContainer(1, 1, 1, 1)
mainWindow.bottomToolBar.caseSensitiveButton = mainWindow.bottomToolBar:addAdaptiveButton(1, 1, 2, 1, 0x3C3C3C, 0xEEEEEE, 0xBBBBBB, 0x2D2D2D, "Aa")
mainWindow.bottomToolBar.caseSensitiveButton.switchMode = true
mainWindow.bottomToolBar.onTouch = function()
find()
end
mainWindow.bottomToolBar.inputTextBox = mainWindow.bottomToolBar:addInputTextBox(7, 1, 10, 3, 0xCCCCCC, 0x999999, 0xCCCCCC, 0x2D2D2D, "", localization.findSomeShit)
mainWindow.bottomToolBar.inputTextBox.onInputFinished = function()
findFromFirstDisplayedLine()
end
mainWindow.bottomToolBar.findButton = mainWindow.bottomToolBar:addAdaptiveButton(1, 1, 3, 1, 0x3C3C3C, 0xEEEEEE, 0xBBBBBB, 0x2D2D2D, localization.find)
mainWindow.bottomToolBar.findButton.onTouch = function()
find()
end
mainWindow.bottomToolBar.isHidden = true
mainWindow.leftTreeView = mainWindow:addTreeView(1, 1, 1, 1, colors.leftTreeView.background, 0x3C3C3C, 0x3C3C3C, 0xEEEEEE, 0x888888, 0x444444, 0x00DBFF, "/")
mainWindow.leftTreeView.onFileSelected = function(path)
loadFile(path)
end
mainWindow.errorMessage = mainWindow:addContainer(1, 1, 1, 1)
mainWindow.errorMessage.backgroundPanel = mainWindow.errorMessage:addPanel(1, 1, 1, 1, 0xFFFFFF, 30)
mainWindow.errorMessage.errorTextBox = mainWindow.errorMessage:addTextBox(3, 2, 1, 1, nil, 0x4B4B4B, {}, 1)
hideErrorMessage()
mainWindow.settingsContainer = mainWindow:addContainer(1, 1, 1, 1)
mainWindow.settingsContainer.backgroundPanel = mainWindow.settingsContainer:addPanel(1, 1, mainWindow.settingsContainer.width, mainWindow.settingsContainer.height, 0x0, 30)
mainWindow.settingsContainer.backgroundPanel.onTouch = hideSettingsContainer
mainWindow.settingsContainer.isHidden = true
mainWindow.autocompleteWindow = mainWindow:addObject(1, 1, 40, 1)
mainWindow.autocompleteWindow.maximumHeight = 8
mainWindow.autocompleteWindow.matches = {}
mainWindow.autocompleteWindow.fromMatch = 1
mainWindow.autocompleteWindow.currentMatch = 1
mainWindow.autocompleteWindow.isHidden = true
mainWindow.autocompleteWindow.draw = function(object)
mainWindow.autocompleteWindow.x, mainWindow.autocompleteWindow.y = convertTextPositionToScreenCoordinates(mainWindow.autocompleteWindow.currentWordStarting, cursor.position.line)
mainWindow.autocompleteWindow.x, mainWindow.autocompleteWindow.y = mainWindow.autocompleteWindow.x, mainWindow.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, 0x777777
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
mainWindow.onAnyEvent = function(eventData)
if eventData[1] == "touch" and isClickedOnCodeArea(eventData[3], eventData[4]) then
cursor.blinkState = true
if eventData[5] == 1 then
editOrRightClickMenu.x, editOrRightClickMenu.y = eventData[3], eventData[4]
editOrRightClickMenu:show()
else
setCursorPositionAndClearSelection(convertScreenCoordinatesToTextPosition(eventData[3], eventData[4]))
local newUptime = computer.uptime()
if newUptime - lastClickUptime <= config.doubleClickDelay then selectWord() end
lastClickUptime = newUptime
end
elseif eventData[1] == "drag" and isClickedOnCodeArea(eventData[3], eventData[4]) then
cursor.blinkState = true
if eventData[5] ~= 1 then
mainWindow.codeView.selections[1] = mainWindow.codeView.selections[1] or {from = {}, to = {}}
mainWindow.codeView.selections[1].from.symbol, mainWindow.codeView.selections[1].from.line = cursor.position.symbol, cursor.position.line
mainWindow.codeView.selections[1].to.symbol, mainWindow.codeView.selections[1].to.line = fixCursorPosition(convertScreenCoordinatesToTextPosition(eventData[3], eventData[4]))
if mainWindow.codeView.selections[1].from.line > mainWindow.codeView.selections[1].to.line then
mainWindow.codeView.selections[1].from.line, mainWindow.codeView.selections[1].to.line = swap(mainWindow.codeView.selections[1].from.line, mainWindow.codeView.selections[1].to.line)
mainWindow.codeView.selections[1].from.symbol, mainWindow.codeView.selections[1].to.symbol = swap(mainWindow.codeView.selections[1].from.symbol, mainWindow.codeView.selections[1].to.symbol)
elseif mainWindow.codeView.selections[1].from.line == mainWindow.codeView.selections[1].to.line then
if mainWindow.codeView.selections[1].from.symbol > mainWindow.codeView.selections[1].to.symbol then
mainWindow.codeView.selections[1].from.symbol, mainWindow.codeView.selections[1].to.symbol = swap(mainWindow.codeView.selections[1].from.symbol, mainWindow.codeView.selections[1].to.symbol)
end
end
end
elseif eventData[1] == "key_down" then
cursor.blinkState = true
-- 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
config.enableAutocompletion = not config.enableAutocompletion
saveConfig()
-- 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
mainWindow:close()
-- N
elseif eventData[4] == 49 then
newFile()
-- O
elseif eventData[4] == 24 then
open()
-- U
elseif eventData[4] == 22 and component.isAvailable("internet") then
downloadFromWeb()
-- S
elseif eventData[4] == 31 then
-- Shift
if mainWindow.leftTreeView.currentFile and not keyboard.isKeyDown(42) then
save()
else
saveAs()
end
-- F
elseif eventData[4] == 33 then
toggleBottomToolBar()
-- G
elseif eventData[4] == 34 then
find()
-- Backspace
elseif eventData[4] == 14 then
deleteLine(cursor.position.line)
-- Delete
elseif eventData[4] == 211 then
deleteLine(cursor.position.line)
-- +
elseif eventData[4] == 13 then
scalePlus()
-- -
elseif eventData[4] == 12 then
scaleMinus()
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 mainWindow.autocompleteWindow.isHidden then
enter()
else
pasteSelectedAutocompletion()
end
-- F5
elseif eventData[4] == 63 then
run()
-- 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
elseif eventData[1] == "clipboard" then
paste(splitStringIntoLines(eventData[3]))
elseif eventData[1] == "scroll" then
if isClickedOnCodeArea(eventData[3], eventData[4]) then
scroll(eventData[5], config.scrollSpeed)
end
elseif eventData[1] == "component_added" or eventData[1] == "component_removed" then
if eventData[3] == "screen" then
os.sleep(0.5)
changeScale(config.screenScale)
end
elseif not eventData[1] then
cursor.blinkState = not cursor.blinkState
end
updateTitle()
updateRAMProgressBar()
mainWindow:draw()
if cursor.blinkState then
-- local x, y = mainWindow.codeView.codeAreaPosition + cursor.position.symbol - mainWindow.codeView.fromSymbol + 1, mainWindow.codeView.y + cursor.position.line - mainWindow.codeView.fromLine
local x, y = convertTextPositionToScreenCoordinates(cursor.position.symbol, cursor.position.line)
if
x >= mainWindow.codeView.codeAreaPosition + 1 and
x <= mainWindow.codeView.codeAreaPosition + mainWindow.codeView.codeAreaWidth - 2 and
y >= mainWindow.codeView.y and
y <= mainWindow.codeView.y + mainWindow.codeView.height - 2
then
buffer.text(x, y, config.cursorColor, config.cursorSymbol)
end
end
buffer.draw()
end
end
---------------------------------------------------- RUSH B! ----------------------------------------------------
loadConfig()
createWindow()
changeScale(config.screenScale)
updateTitle()
updateRAMProgressBar()
mainWindow:draw()
if args[1] == "open" and fs.exists(args[2] or "") then
loadFile(args[2])
else
newFile()
end
mainWindow:draw()
buffer.draw()
mainWindow:handleEvents(config.cursorBlinkDelay)