mirror of
https://github.com/IgorTimofeev/MineOS.git
synced 2025-12-20 11:09:21 +01:00
257 lines
13 KiB
Lua
257 lines
13 KiB
Lua
local gpu = require("component").gpu
|
||
local unicode = require("unicode")
|
||
local syntax = {}
|
||
|
||
----------------------------------------------------------------------------------------------------------------
|
||
|
||
--Стандартные цветовые схемы
|
||
local colorSchemes = {
|
||
["midnight"] = {
|
||
["recommendedBackground"] = 0x262626,
|
||
["text"] = 0xffffff,
|
||
["strings"] = 0xff2024,
|
||
["loops"] = 0xffff98,
|
||
["comments"] = 0xa2ffb7,
|
||
["boolean"] = 0xffcc66,
|
||
["logic"] = 0xffcc66,
|
||
["numbers"] = 0x24c0ff,
|
||
["functions"] = 0xffcc66,
|
||
["compares"] = 0xffff98,
|
||
},
|
||
}
|
||
|
||
--Текущая цветовая схема
|
||
local currentColorScheme = {}
|
||
--Шаблоны поиска
|
||
local patterns
|
||
--Размер массива шаблонов поиска
|
||
local sPatterns
|
||
|
||
----------------------------------------------------------------------------------------------------------------
|
||
|
||
--Пересчитать цвета шаблонов
|
||
--Приоритет поиска шаблонов снижается сверху вниз
|
||
local function definePatterns()
|
||
patterns = {
|
||
--Комментарии
|
||
{ ["pattern"] = "%-%-.*", ["color"] = currentColorScheme.comments, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
|
||
--Строки
|
||
{ ["pattern"] = "\"[^\"\"]*\"", ["color"] = currentColorScheme.strings, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
|
||
--Циклы, условия, объявления
|
||
{ ["pattern"] = "while ", ["color"] = currentColorScheme.loops, ["cutFromLeft"] = 0, ["cutFromRight"] = 1 },
|
||
{ ["pattern"] = "do$", ["color"] = currentColorScheme.loops, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
{ ["pattern"] = "do ", ["color"] = currentColorScheme.loops, ["cutFromLeft"] = 0, ["cutFromRight"] = 1 },
|
||
{ ["pattern"] = "end$", ["color"] = currentColorScheme.loops, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
{ ["pattern"] = "end ", ["color"] = currentColorScheme.loops, ["cutFromLeft"] = 0, ["cutFromRight"] = 1 },
|
||
{ ["pattern"] = "for ", ["color"] = currentColorScheme.loops, ["cutFromLeft"] = 0, ["cutFromRight"] = 1 },
|
||
{ ["pattern"] = " in ", ["color"] = currentColorScheme.loops, ["cutFromLeft"] = 0, ["cutFromRight"] = 1 },
|
||
{ ["pattern"] = "repeat ", ["color"] = currentColorScheme.loops, ["cutFromLeft"] = 0, ["cutFromRight"] = 1 },
|
||
{ ["pattern"] = "if ", ["color"] = currentColorScheme.loops, ["cutFromLeft"] = 0, ["cutFromRight"] = 1 },
|
||
{ ["pattern"] = "then", ["color"] = currentColorScheme.loops, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
{ ["pattern"] = "until ", ["color"] = currentColorScheme.loops, ["cutFromLeft"] = 0, ["cutFromRight"] = 1 },
|
||
{ ["pattern"] = "return", ["color"] = currentColorScheme.loops, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
{ ["pattern"] = "local ", ["color"] = currentColorScheme.loops, ["cutFromLeft"] = 0, ["cutFromRight"] = 1 },
|
||
{ ["pattern"] = "function ", ["color"] = currentColorScheme.loops, ["cutFromLeft"] = 0, ["cutFromRight"] = 1 },
|
||
{ ["pattern"] = "else$", ["color"] = currentColorScheme.loops, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
{ ["pattern"] = "else ", ["color"] = currentColorScheme.loops, ["cutFromLeft"] = 0, ["cutFromRight"] = 1 },
|
||
{ ["pattern"] = "elseif ", ["color"] = currentColorScheme.loops, ["cutFromLeft"] = 0, ["cutFromRight"] = 1 },
|
||
|
||
--Состояния переменной
|
||
{ ["pattern"] = "true", ["color"] = currentColorScheme.boolean, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
{ ["pattern"] = "false", ["color"] = currentColorScheme.boolean, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
{ ["pattern"] = "nil", ["color"] = currentColorScheme.boolean, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
|
||
--Функции
|
||
--{ ["pattern"] = "%s([%a%d%_%-%.])*%(", ["color"] = currentColorScheme.functions, ["cutFromLeft"] = 0, ["cutFromRight"] = 1 },
|
||
|
||
--And, or, not, break
|
||
{ ["pattern"] = " and ", ["color"] = currentColorScheme.logic, ["cutFromLeft"] = 0, ["cutFromRight"] = 1 },
|
||
{ ["pattern"] = " or ", ["color"] = currentColorScheme.logic, ["cutFromLeft"] = 0, ["cutFromRight"] = 1 },
|
||
{ ["pattern"] = " not ", ["color"] = currentColorScheme.logic, ["cutFromLeft"] = 0, ["cutFromRight"] = 1 },
|
||
{ ["pattern"] = " break$", ["color"] = currentColorScheme.logic, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
{ ["pattern"] = "^break", ["color"] = currentColorScheme.logic, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
{ ["pattern"] = " break ", ["color"] = currentColorScheme.logic, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
|
||
--Числа
|
||
{ ["pattern"] = "%s(0x)(%w*)", ["color"] = currentColorScheme.numbers, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
{ ["pattern"] = "(%s)([%d%.]*)", ["color"] = currentColorScheme.numbers, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
|
||
--Сравнения и мат. операции
|
||
{ ["pattern"] = "<=", ["color"] = currentColorScheme.compares, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
{ ["pattern"] = ">=", ["color"] = currentColorScheme.compares, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
{ ["pattern"] = "<", ["color"] = currentColorScheme.compares, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
{ ["pattern"] = ">", ["color"] = currentColorScheme.compares, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
{ ["pattern"] = "==", ["color"] = currentColorScheme.compares, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
{ ["pattern"] = "~=", ["color"] = currentColorScheme.compares, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
{ ["pattern"] = "=", ["color"] = currentColorScheme.compares, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
{ ["pattern"] = "%+", ["color"] = currentColorScheme.compares, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
{ ["pattern"] = "%-", ["color"] = currentColorScheme.compares, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
{ ["pattern"] = "%*", ["color"] = currentColorScheme.compares, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
{ ["pattern"] = "%/", ["color"] = currentColorScheme.compares, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
{ ["pattern"] = "%.%.", ["color"] = currentColorScheme.compares, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
{ ["pattern"] = "%#", ["color"] = currentColorScheme.compares, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
{ ["pattern"] = "#^", ["color"] = currentColorScheme.compares, ["cutFromLeft"] = 0, ["cutFromRight"] = 0 },
|
||
}
|
||
|
||
--Ну, и размер массива шаблонов тоже
|
||
sPatterns = #patterns
|
||
end
|
||
|
||
--Костыльная замена обычному string.find()
|
||
--Работает медленнее, но хотя бы поддерживает юникод
|
||
function unicode.find(str, pattern, init, plain)
|
||
-- checkArg(1, str, "string")
|
||
-- checkArg(2, pattern, "string")
|
||
-- checkArg(3, init, "number", "nil")
|
||
if init then
|
||
if init < 0 then
|
||
init = -#unicode.sub(str,init)
|
||
elseif init > 0 then
|
||
init = #unicode.sub(str,1,init-1)+1
|
||
end
|
||
end
|
||
|
||
a, b = string.find(str, pattern, init, plain)
|
||
|
||
if a then
|
||
local ap,bp = str:sub(1,a-1), str:sub(a,b)
|
||
a = unicode.len(ap)+1
|
||
b = a + unicode.len(bp)-1
|
||
return a,b
|
||
else
|
||
return a
|
||
end
|
||
end
|
||
|
||
--Проанализировать строку и создать на ее основе цветовую карту
|
||
function syntax.highlight(text)
|
||
--Массив символов и их цветов
|
||
local massiv = {}
|
||
--Длина текста
|
||
local sText = unicode.len(text)
|
||
--Базовый цвет текста
|
||
local currentColor = currentColorScheme.text
|
||
--Откуда будем искать совпадения шаблонов
|
||
local searchFrom = 1
|
||
--Переменная успешного поиска
|
||
local sucessfullyFound = false
|
||
--Перебираем всю строку посимвольно и присваиваем каждому символу свой цвет
|
||
local symbol = 1
|
||
while symbol <= sText do
|
||
--Обнуляем успех поиска, ибо хуй знает, найдет ли оно что-то
|
||
sucessfullyFound = false
|
||
--Перебираем все шаблоны
|
||
for i = 1, sPatterns do
|
||
--Ищем совпадения
|
||
local starting, ending = unicode.find(text, patterns[i].pattern, searchFrom)
|
||
--Если старт совпадения совпадает с номером символа, то
|
||
if starting and starting == symbol then
|
||
--Ставим цвет для текущего и последующих символов
|
||
currentColor = patterns[i].color
|
||
--Указываем всем символам, соответствующим шаблону, их цвет
|
||
for j = (starting + patterns[i].cutFromLeft), (ending - patterns[i].cutFromRight) do
|
||
massiv[j] = { ["symbol"] = unicode.sub(text, j, j), ["color"] = currentColor }
|
||
end
|
||
--Указываем новую позицию поиска
|
||
searchFrom = ending + 1 - patterns[i].cutFromRight
|
||
--И новую позицию символа
|
||
symbol = searchFrom
|
||
--Ставим true, ибо паттерн был найден
|
||
sucessfullyFound = true
|
||
--Разрываем цикл, ибо нехуй больше искать
|
||
break
|
||
end
|
||
--Обнуляем переменные, ибо я так люблю
|
||
starting, ending = nil, nil
|
||
end
|
||
|
||
--Если ни хера не нашло, то
|
||
if not sucessfullyFound then
|
||
--Ставим обычный цвет текста
|
||
currentColor = currentColorScheme.text
|
||
--Загоняем обычный символ
|
||
massiv[symbol] = { ["symbol"] = unicode.sub(text, symbol, symbol), ["color"] = currentColor }
|
||
--Го некст
|
||
symbol = symbol + 1
|
||
end
|
||
end
|
||
|
||
--И тут тоже
|
||
sText, currentColor, searchFrom = nil, nil, nil
|
||
|
||
--Возвращаем полученный массив
|
||
return massiv
|
||
end
|
||
|
||
--Объявить новую цветовую схему
|
||
function syntax.setColorScheme(colorScheme)
|
||
--Выбранная цветовая схема
|
||
currentColorScheme = colorScheme
|
||
--Пересчитываем шаблоны
|
||
definePatterns()
|
||
end
|
||
|
||
--Нарисовать созданный массив по указанным координатам и обрезать строку до указанной длины.
|
||
function syntax.highlightAndDraw(x, y, limit, text)
|
||
--Чутка левее делаем координату, т.к. цикл начинается с 1
|
||
x = x - 1
|
||
--Получаем подсвеченный массив
|
||
local massiv = syntax.highlight(text)
|
||
--Задаем стартовый цвет
|
||
local currentColor = currentColorScheme.text
|
||
gpu.setForeground(currentColor)
|
||
--Перебираем все элементы полученного массива
|
||
for symbol = 1, limit do
|
||
--Если такой символ в массиве вообще существует, то
|
||
if massiv[symbol] then
|
||
--Легкая оптимизация. Меняет цвет текста только в случае несоответствия текущего цвета и цвета из массива
|
||
if currentColor ~= massiv[symbol].color then currentColor = massiv[symbol].color; gpu.setForeground(massiv[symbol].color) end
|
||
--Рисуем символ на экране
|
||
gpu.set(x + symbol, y, massiv[symbol].symbol)
|
||
--А если не существует, то разорвать цикл и закончить рисование строки
|
||
else
|
||
break
|
||
end
|
||
end
|
||
end
|
||
|
||
--Открыть файл для чтения и отобразить первые строки из него, чтобы чекнуть, как работает подсветка
|
||
function syntax.highlightFileForDebug(pathToFile, colorSchemeName)
|
||
--Устанавливаем цветовую схему
|
||
syntax.setColorScheme(colorSchemes[colorSchemeName] or colorSchemes.midnight)
|
||
--Очищаем экран рекомендуемым цветом
|
||
ecs.prepareToExit(currentColorScheme.recommendedBackground, currentColorScheme.text)
|
||
--Получаем размер экрана
|
||
local xSize, ySize = gpu.getResolution()
|
||
--Открываем файлик
|
||
local file = io.open(pathToFile, "r")
|
||
--Счетчик строк
|
||
local lineCounter = 1
|
||
--Читаем строки
|
||
for line in file:lines() do
|
||
--Подсвечиваем строку и рисуем
|
||
syntax.highlightAndDraw(2, lineCounter, xSize - 2, line)
|
||
--Счетчик в плюс
|
||
lineCounter = lineCounter + 1
|
||
--Разрываем цикл, если кол-во строк превысило высоту экрана
|
||
if lineCounter > ySize then break end
|
||
end
|
||
--Закрываем файл
|
||
file:close()
|
||
end
|
||
|
||
----------------------------------------------------------------------------------------------------------------
|
||
|
||
--Стартовое объявление цветовой схемы при загрузке библиотеки
|
||
syntax.setColorScheme(colorSchemes.midnight)
|
||
|
||
--syntax.highlightFileForDebug("highlightText", midnight)
|
||
|
||
return syntax
|
||
|
||
|
||
|
||
|