MineOS/Applications/MineCodeIDE/MineCodeIDE.lua
Igor Timofeev cb965a83cd aefaef
2017-01-14 06:09:29 +03:00

505 lines
19 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 ----------------------------------------------------
-- package.loaded.syntax = nil
-- package.loaded.GUI = nil
-- package.loaded.windows = nil
-- package.loaded.MineOSCore = nil
require("advancedLua")
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 keyboard = require("keyboard")
---------------------------------------------------- Constants ----------------------------------------------------
-- "/MineOS/Desktop/MineCode IDE.app/MineCode IDE.lua"
local clipboard
local cursor = {
position = {
symbol = 20,
line = 8
},
color = 0x00A8FF,
symbol = "",
blinkDelay = 0.4,
blinkState = false,
}
local mainWindow = {}
local config = {
indentaionWidth = 2,
colorScheme = {
topToolBar = 0xBBBBBB,
topMenu = {
backgroundColor = 0xEEEEEE,
textColor = 0x444444,
backgroundPressedColor = 0x3366CC,
textPressedColor = 0xFFFFFF,
},
},
scrollSpeed = 8,
}
---------------------------------------------------- File processing methods ----------------------------------------------------
local function loadFile(path)
mainWindow.codeView.fromLine, mainWindow.codeView.fromSymbol, mainWindow.codeView.lines, mainWindow.codeView.maximumLineLength = 1, 1, {}, 0
local file = io.open(path, "r")
for line in file:lines() do
line = line:gsub("\t", string.rep(" ", config.indentaionWidth))
table.insert(mainWindow.codeView.lines, line)
mainWindow.codeView.maximumLineLength = math.max(mainWindow.codeView.maximumLineLength, unicode.len(line))
end
file:close()
mainWindow.titleTextBox.lines = {
"File: " .. path,
"Cursor position: 1x1",
"Zalupa, penis"
}
end
---------------------------------------------------- Cursor methods ----------------------------------------------------
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()
end
local function convertScreenCoordinatesToCursorPosition(x, y)
return x - mainWindow.codeView.codeAreaPosition + mainWindow.codeView.fromSymbol - 1, y - mainWindow.codeView.y + mainWindow.codeView.fromLine
end
local function clearSelection()
mainWindow.codeView.selections[1] = nil
end
local function moveCursor(symbolOffset, lineOffset)
local newSymbol, newLine = cursor.position.symbol + symbolOffset, cursor.position.line + lineOffset
if newSymbol < 1 then
newLine, newSymbol = newLine - 1, math.huge
elseif newSymbol > unicode.len(mainWindow.codeView.lines[newLine] or "") + 1 then
newLine, newSymbol = newLine + 1, 1
end
setCursorPosition(newSymbol, newLine)
end
---------------------------------------------------- Text processing methods ----------------------------------------------------
local function deleteLine(line)
if #lines > 0 then
table.remove(mainWindow.codeView.lines, line)
setCursorPosition(1, cursor.position.line)
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 i = fromLine + 1, toLine do
table.remove(mainWindow.codeView.lines, fromLine + 1)
end
mainWindow.codeView.lines[fromLine] = upperLine .. lowerLine
setCursorPosition(fromSymbol, fromLine)
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 paste(pasteLines)
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
setCursorPosition(cursor.position.symbol + unicode.len(pasteLines[1]), cursor.position.line)
else
local line = cursor.position.line
mainWindow.codeView.lines[line] = firstPart .. pasteLines[1]
line = line + 1
for i = #pasteLines - 1, 2, -1 do
table.insert(mainWindow.codeView.lines, line, pasteLines[i])
line = line + 1
end
mainWindow.codeView.lines[line] = pasteLines[#pasteLines] .. secondPart
setCursorPosition(unicode.len(pasteLines[#pasteLines]) + 1, cursor.position.line + #pasteLines - 1)
end
end
local function backspace()
if mainWindow.codeView.selections[1] then
deleteSelectedData()
else
if cursor.position.symbol > 1 then
deleteSpecifiedData(cursor.position.symbol - 1, cursor.position.line, cursor.position.symbol - 1, cursor.position.line)
else
deleteSpecifiedData(unicode.len(mainWindow.codeView.lines[cursor.position.line - 1]) + 1, cursor.position.line - 1, 0, cursor.position.line)
end
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)
setCursorPosition(1, cursor.position.line + 1)
end
---------------------------------------------------- Text comments-related methods ----------------------------------------------------
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
setCursorPosition(cursor.position.symbol - 3, cursor.position.line)
end
else
commentLine(cursor.position.line)
setCursorPosition(cursor.position.symbol + 3, cursor.position.line)
end
end
end
---------------------------------------------------- Text indentation-related methods ----------------------------------------------------
local function indentLine(line)
mainWindow.codeView.lines[line] = string.rep(" ", config.indentaionWidth) .. mainWindow.codeView.lines[line]
end
local function unindentLine(line)
mainWindow.codeView.lines[line], countOfReplaces = string.gsub(mainWindow.codeView.lines[line], "^" .. string.rep("%s", config.indentaionWidth), "")
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 + config.indentaionWidth, cursor.position.line)
mainWindow.codeView.selections[1].from.symbol, mainWindow.codeView.selections[1].to.symbol = mainWindow.codeView.selections[1].from.symbol + config.indentaionWidth, mainWindow.codeView.selections[1].to.symbol + config.indentaionWidth
else
if countOfReplacesInFirstLine > 0 then
mainWindow.codeView.selections[1].from.symbol = mainWindow.codeView.selections[1].from.symbol - config.indentaionWidth
if cursor.position.line == mainWindow.codeView.selections[1].from.line then
setCursorPosition(cursor.position.symbol - config.indentaionWidth, cursor.position.line)
end
end
if countOfReplacesInLastLine > 0 then
mainWindow.codeView.selections[1].to.symbol = mainWindow.codeView.selections[1].to.symbol - config.indentaionWidth
if cursor.position.line == mainWindow.codeView.selections[1].to.line then
setCursorPosition(cursor.position.symbol - config.indentaionWidth, cursor.position.line)
end
end
end
else
if isIndent then
indentLine(cursor.position.line)
setCursorPosition(cursor.position.symbol + config.indentaionWidth, cursor.position.line)
else
if unindentLine(cursor.position.line) > 0 then
setCursorPosition(cursor.position.symbol - config.indentaionWidth, cursor.position.line)
end
end
end
end
---------------------------------------------------- Main window related methods ----------------------------------------------------
local function calculateSizes()
if mainWindow.topToolBar.isHidden then
mainWindow.codeView.localPosition.y = 2
else
mainWindow.codeView.localPosition.y = 5
mainWindow.topToolBar.width = mainWindow.width
mainWindow.topToolBar.backgroundPanel.width = mainWindow.width
mainWindow.titleTextBox.width = mainWindow.topToolBar.width * 0.25
mainWindow.titleTextBox.localPosition.x = math.floor(mainWindow.topToolBar.width / 2 - mainWindow.titleTextBox.width / 2)
end
mainWindow.topMenu.width = mainWindow.width
mainWindow.codeView.width, mainWindow.codeView.height = mainWindow.width, mainWindow.height - 4
end
local function createWindow()
mainWindow = windows.fullScreen()
mainWindow.codeView = mainWindow:addCodeView(1, 1, 1, 1, {}, 1, 1, 1, {}, {}, true)
mainWindow.topMenu = mainWindow:addMenu(1, 1, 1, config.colorScheme.topMenu.backgroundColor, config.colorScheme.topMenu.textColor, config.colorScheme.topMenu.backgroundPressedColor, config.colorScheme.topMenu.textPressedColor)
mainWindow.topMenu:addItem("MineCode", 0x0)
mainWindow.topMenu:addItem("File")
mainWindow.topMenu:addItem("View")
mainWindow.topMenu:addItem("Properties")
mainWindow.topToolBar = mainWindow:addContainer(1, 2, 1, 3)
mainWindow.topToolBar.backgroundPanel = mainWindow.topToolBar:addPanel(1, 1, 1, 3, config.colorScheme.topToolBar)
mainWindow.titleTextBox = mainWindow.topToolBar:addTextBox(1, 1, 1, 3, 0xDDDDDD, 0x444444, {}, 1):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top)
mainWindow.runButton = mainWindow.topToolBar:addAdaptiveButton(1, 1, 2, 1, 0x444444, 0xFFFFFF, 0xFFFFFF, 0x444444, "Run")
mainWindow.runButton.onTouch = function()
GUI.error("RUN ВАСЯ РАН")
end
mainWindow.toggleSyntaxHighlightingButton = mainWindow.topToolBar:addAdaptiveButton(8, 1, 2, 1, 0xDDDDDD, 0x262626, 0x262626, 0xDDDDDD, "Syntax")
mainWindow.toggleSyntaxHighlightingButton.onTouch = function()
mainWindow.codeView.highlightLuaSyntax = not mainWindow.codeView.highlightLuaSyntax
end
mainWindow.onAnyEvent = function(eventData)
cursor.blinkState = not cursor.blinkState
local oldCursorState = cursor.blinkState
cursor.blinkState = true
if eventData[1] == "touch" and mainWindow.codeView:isClicked(eventData[3], eventData[4]) then
if eventData[5] == 1 then
local menu = GUI.contextMenu(eventData[3], eventData[4])
menu:addItem("Copy", false, "^C")
menu:addItem("Paste", false, "^V")
menu:addItem("Delete")
menu:addSeparator()
menu:addItem("Select all", false, "^A")
menu:show()
else
clearSelection()
setCursorPosition(convertScreenCoordinatesToCursorPosition(eventData[3], eventData[4]))
end
elseif eventData[1] == "drag" then
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(convertScreenCoordinatesToCursorPosition(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)
end
end
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()
-- A
elseif eventData[4] == 30 then
mainWindow.codeView.selections[1] = {from = {}, to = {}}
mainWindow.codeView.selections[1].from.symbol, mainWindow.codeView.selections[1].from.line = 1, 1
mainWindow.codeView.selections[1].to.symbol, mainWindow.codeView.selections[1].to.line = unicode.len(mainWindow.codeView.lines[#mainWindow.codeView.lines]), #mainWindow.codeView.lines
-- C
elseif eventData[4] == 46 then
copy()
-- V
elseif eventData[4] == 47 then
paste(clipboard)
-- X
elseif eventData[4] == 45 then
cut()
-- W
elseif eventData[4] == 17 then
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
enter()
else
if not keyboard.isControl(eventData[3]) then
deleteSelectedData()
paste({unicode.char(eventData[3])})
end
end
elseif eventData[1] == "clipboard" then
paste({eventData[3]})
elseif eventData[1] == "scroll" then
if mainWindow.codeView:isClicked(eventData[3], eventData[4]) then
if eventData[5] == 1 then
if mainWindow.codeView.fromLine > config.scrollSpeed then
mainWindow.codeView.fromLine = mainWindow.codeView.fromLine - config.scrollSpeed
else
mainWindow.codeView.fromLine = 1
end
else
if mainWindow.codeView.fromLine < #mainWindow.codeView.lines - config.scrollSpeed then
mainWindow.codeView.fromLine = mainWindow.codeView.fromLine + config.scrollSpeed
else
mainWindow.codeView.fromLine = #mainWindow.codeView.lines
end
end
end
elseif not eventData[1] then
cursor.blinkState = oldCursorState
end
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
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, cursor.color, cursor.symbol)
end
end
buffer.draw()
end
end
-----------------------------------------------------------------------------------------------------------------------------
buffer.start()
createWindow()
calculateSizes()
loadFile("/OS.lua")
mainWindow.drawShadow = true
mainWindow:draw()
buffer.draw()
mainWindow.drawShadow = false
setCursorPosition(1, 1)
mainWindow:handleEvents(cursor.blinkDelay)