Updated markdown parser, testing it

This commit is contained in:
Robert Jelic
2025-02-17 09:36:51 +01:00
parent daae1ddb3d
commit cf49f69612
7 changed files with 167 additions and 55 deletions

View File

@@ -75,7 +75,7 @@ function BaseElement:init(props, basalt)
return self return self
end end
--- Post initialization hook --- Post initialization
--- @return table self The BaseElement instance --- @return table self The BaseElement instance
function BaseElement:postInit() function BaseElement:postInit()
if(self._props)then if(self._props)then
@@ -174,6 +174,8 @@ function BaseElement:handleEvent(event, ...)
return false return false
end end
--- Returns the base frame of the element
--- @return table BaseFrame The base frame of the element
function BaseElement:getBaseFrame() function BaseElement:getBaseFrame()
if self.parent then if self.parent then
return self.parent:getBaseFrame() return self.parent:getBaseFrame()
@@ -181,8 +183,26 @@ function BaseElement:getBaseFrame()
return self return self
end end
--- Destroys the element and cleans up all references
--- @usage element:destroy()
function BaseElement:destroy() function BaseElement:destroy()
-- Remove from parent if exists
if self.parent then
self.parent:removeChild(self)
end
for event in pairs(self._registeredEvents) do
self:listenEvent(event, false)
end
self._values.eventCallbacks = {}
self._props = nil
self._values = nil
self.basalt = nil
self.parent = nil
self.__index = nil
setmetatable(self, nil)
end end
--- Requests a render update for this element --- Requests a render update for this element

View File

@@ -464,4 +464,11 @@ function Container:render()
end end
end end
function Container:destroy()
for _, child in ipairs(self._values.children) do
child:destroy()
end
VisualElement.destroy(self)
end
return Container return Container

View File

@@ -47,7 +47,7 @@ end
function Table:sortData(columnIndex) function Table:sortData(columnIndex)
local data = self.get("data") local data = self.get("data")
local direction = self.get("sortDirection") local direction = self.get("sortDirection")
table.sort(data, function(a, b) table.sort(data, function(a, b)
if direction == "asc" then if direction == "asc" then
return a[columnIndex] < b[columnIndex] return a[columnIndex] < b[columnIndex]
@@ -55,7 +55,7 @@ function Table:sortData(columnIndex)
return a[columnIndex] > b[columnIndex] return a[columnIndex] > b[columnIndex]
end end
end) end)
self.set("data", data) self.set("data", data)
return self return self
end end
@@ -65,7 +65,6 @@ function Table:mouse_click(button, x, y)
local relX, relY = self:getRelativePosition(x, y) local relX, relY = self:getRelativePosition(x, y)
-- Header-Click für Sorting
if relY == 1 then if relY == 1 then
local currentX = 1 local currentX = 1
for i, col in ipairs(self.get("columns")) do for i, col in ipairs(self.get("columns")) do
@@ -83,7 +82,6 @@ function Table:mouse_click(button, x, y)
end end
end end
-- Row-Selection (berücksichtigt Scroll-Offset)
if relY > 1 then if relY > 1 then
local rowIndex = relY - 2 + self.get("scrollOffset") local rowIndex = relY - 2 + self.get("scrollOffset")
if rowIndex >= 0 and rowIndex < #self.get("data") then if rowIndex >= 0 and rowIndex < #self.get("data") then
@@ -98,7 +96,7 @@ function Table:mouse_scroll(direction, x, y)
local data = self.get("data") local data = self.get("data")
local height = self.get("height") local height = self.get("height")
local visibleRows = height - 2 local visibleRows = height - 2
local maxScroll = math.max(0, #data - visibleRows + 1) -- +1 korrigiert den Scroll-Bereich local maxScroll = math.max(0, #data - visibleRows + 1)
local newOffset = math.min(maxScroll, math.max(0, self.get("scrollOffset") + direction)) local newOffset = math.min(maxScroll, math.max(0, self.get("scrollOffset") + direction))
self.set("scrollOffset", newOffset) self.set("scrollOffset", newOffset)
@@ -125,14 +123,12 @@ function Table:render()
currentX = currentX + col.width currentX = currentX + col.width
end end
-- Angepasste Berechnung der sichtbaren Zeilen local visibleRows = height - 2
local visibleRows = height - 2 -- Verfügbare Zeilen (minus Header)
for y = 2, height do for y = 2, height do
local rowIndex = y - 2 + scrollOffset local rowIndex = y - 2 + scrollOffset
local rowData = data[rowIndex + 1] local rowData = data[rowIndex + 1]
-- Zeile nur rendern wenn es auch Daten dafür gibt if rowData and (rowIndex + 1) <= #data then
if rowData and (rowIndex + 1) <= #data then -- Korrigierte Bedingung
currentX = 1 currentX = 1
local bg = (rowIndex + 1) == selected and self.get("selectedColor") or self.get("background") local bg = (rowIndex + 1) == selected and self.get("selectedColor") or self.get("background")
@@ -145,33 +141,28 @@ function Table:render()
currentX = currentX + col.width currentX = currentX + col.width
end end
else else
-- Leere Zeile füllen
self:blit(1, y, string.rep(" ", self.get("width")), self:blit(1, y, string.rep(" ", self.get("width")),
string.rep(tHex[self.get("foreground")], self.get("width")), string.rep(tHex[self.get("foreground")], self.get("width")),
string.rep(tHex[self.get("background")], self.get("width"))) string.rep(tHex[self.get("background")], self.get("width")))
end end
end end
-- Scrollbar Berechnung überarbeitet
if #data > height - 2 then if #data > height - 2 then
local scrollbarHeight = height - 2 local scrollbarHeight = height - 2
local thumbSize = math.max(1, math.floor(scrollbarHeight * (height - 2) / #data)) local thumbSize = math.max(1, math.floor(scrollbarHeight * (height - 2) / #data))
-- Thumb Position korrigiert local maxScroll = #data - (height - 2) + 1
local maxScroll = #data - (height - 2) + 1 -- +1 für korrekte End-Position
local scrollPercent = scrollOffset / maxScroll local scrollPercent = scrollOffset / maxScroll
local thumbPos = 2 + math.floor(scrollPercent * (scrollbarHeight - thumbSize)) local thumbPos = 2 + math.floor(scrollPercent * (scrollbarHeight - thumbSize))
if scrollOffset >= maxScroll then if scrollOffset >= maxScroll then
thumbPos = height - thumbSize -- Exakt am Ende thumbPos = height - thumbSize
end end
-- Scrollbar Background
for y = 2, height do for y = 2, height do
self:blit(self.get("width"), y, "\127", tHex[colors.gray], tHex[colors.gray]) self:blit(self.get("width"), y, "\127", tHex[colors.gray], tHex[colors.gray])
end end
-- Thumb zeichnen
for y = thumbPos, math.min(height, thumbPos + thumbSize - 1) do for y = thumbPos, math.min(height, thumbPos + thumbSize - 1) do
self:blit(self.get("width"), y, "\127", tHex[colors.white], tHex[colors.white]) self:blit(self.get("width"), y, "\127", tHex[colors.white], tHex[colors.white])
end end

View File

@@ -2,8 +2,6 @@ local elementManager = require("elementManager")
local BaseElement = elementManager.getElement("BaseElement") local BaseElement = elementManager.getElement("BaseElement")
local tHex = require("libraries/colorHex") local tHex = require("libraries/colorHex")
---@alias color number
---@class VisualElement : BaseElement ---@class VisualElement : BaseElement
local VisualElement = setmetatable({}, BaseElement) local VisualElement = setmetatable({}, BaseElement)
VisualElement.__index = VisualElement VisualElement.__index = VisualElement
@@ -62,13 +60,24 @@ VisualElement.defineProperty(VisualElement, "visible", {default = true, type = "
return value return value
end}) end})
---@combinedProperty position x y ---@combinedProperty position {x y} Position of the element
VisualElement.combineProperties(VisualElement, "position", "x", "y") VisualElement.combineProperties(VisualElement, "position", "x", "y")
---@combinedProperty size width height ---@combinedProperty size {width height} Size of the element
VisualElement.combineProperties(VisualElement, "size", "width", "height") VisualElement.combineProperties(VisualElement, "size", "width", "height")
---@combinedProperty color foreground background ---@combinedProperty color {foreground background} Color of the element
VisualElement.combineProperties(VisualElement, "color", "foreground", "background") VisualElement.combineProperties(VisualElement, "color", "foreground", "background")
---@event onMouseClick {button number, x number, y number} Fired when the element is clicked
---@event onMouseUp {button number, x number, y number} Fired when the mouse is released
---@event onMouseRelease {button number, x number, y number} Fired when the mouse is released
---@event onMouseDrag {button number, x number, y number} Fired when the mouse is dragged
---@event onFocus {-} Fired when the element is focused
---@event onBlur {-} Fired when the element is blurred
---@event onKey {key number, code number, isRepeat boolean} Fired when a key is pressed
---@event onKeyUp {key number, code number} Fired when a key is released
---@event onChar {char string} Fired when a key is pressed
VisualElement.listenTo(VisualElement, "focus") VisualElement.listenTo(VisualElement, "focus")
VisualElement.listenTo(VisualElement, "blur") VisualElement.listenTo(VisualElement, "blur")
@@ -90,14 +99,7 @@ function VisualElement:init(props, basalt)
self.set("type", "VisualElement") self.set("type", "VisualElement")
end end
--- Draws a text character/fg/bg at the specified position with a certain size, used in the rendering system ---@protected
--- @param x number The x position to draw
--- @param y number The y position to draw
--- @param width number The width of the element
--- @param height number The height of the element
--- @param text string The text char to draw
--- @param fg color The foreground color
--- @param bg color The background color
function VisualElement:multiBlit(x, y, width, height, text, fg, bg) function VisualElement:multiBlit(x, y, width, height, text, fg, bg)
x = x + self.get("x") - 1 x = x + self.get("x") - 1
y = y + self.get("y") - 1 y = y + self.get("y") - 1
@@ -260,7 +262,6 @@ function VisualElement:setCursor(x, y, blink)
end end
--- Renders the element --- Renders the element
--- @usage element:render()
function VisualElement:render() function VisualElement:render()
if(not self.get("backgroundEnabled"))then if(not self.get("backgroundEnabled"))then
return return

View File

@@ -1,3 +1,8 @@
---@class Log
---@field _logs table
---@field _enabled boolean
---@field _logToFile boolean
---@field _logFile string
local Log = {} local Log = {}
Log._logs = {} Log._logs = {}
Log._enabled = true Log._enabled = true
@@ -6,7 +11,6 @@ Log._logFile = "basalt.log"
fs.delete(Log._logFile) fs.delete(Log._logFile)
-- Log levels
Log.LEVEL = { Log.LEVEL = {
DEBUG = 1, DEBUG = 1,
INFO = 2, INFO = 2,
@@ -28,10 +32,12 @@ local levelColors = {
[Log.LEVEL.ERROR] = colors.red [Log.LEVEL.ERROR] = colors.red
} }
--- Sets if the logger should log to a file.
function Log.setLogToFile(enable) function Log.setLogToFile(enable)
Log._logToFile = enable Log._logToFile = enable
end end
--- sets if the logger should log
function Log.setEnabled(enable) function Log.setEnabled(enable)
Log._enabled = enable Log._enabled = enable
end end
@@ -51,7 +57,6 @@ local function log(level, ...)
local timeStr = os.date("%H:%M:%S") local timeStr = os.date("%H:%M:%S")
-- Get caller info (skip log function and Log.debug/info/etc functions)
local info = debug.getinfo(3, "Sl") local info = debug.getinfo(3, "Sl")
local source = info.source:match("@?(.*)") local source = info.source:match("@?(.*)")
local line = info.currentline local line = info.currentline
@@ -69,9 +74,7 @@ local function log(level, ...)
local fullMessage = string.format("%s %s%s %s", timeStr, levelStr, levelMsg, message) local fullMessage = string.format("%s %s%s %s", timeStr, levelStr, levelMsg, message)
-- File output
writeToFile(fullMessage) writeToFile(fullMessage)
-- Store in memory
table.insert(Log._logs, { table.insert(Log._logs, {
time = timeStr, time = timeStr,
level = level, level = level,
@@ -79,9 +82,17 @@ local function log(level, ...)
}) })
end end
--- Sends a debug message to the logger.
--- @vararg string The message to log
function Log.debug(...) log(Log.LEVEL.DEBUG, ...) end function Log.debug(...) log(Log.LEVEL.DEBUG, ...) end
--- Sends an info message to the logger.
--- @vararg string The message to log
function Log.info(...) log(Log.LEVEL.INFO, ...) end function Log.info(...) log(Log.LEVEL.INFO, ...) end
--- Sends a warning message to the logger.
--- @vararg string The message to log
function Log.warn(...) log(Log.LEVEL.WARN, ...) end function Log.warn(...) log(Log.LEVEL.WARN, ...) end
--- Sends an error message to the logger.
--- @vararg string The message to log
function Log.error(...) log(Log.LEVEL.ERROR, ...) end function Log.error(...) log(Log.LEVEL.ERROR, ...) end
Log.info("Logger initialized") Log.info("Logger initialized")

View File

@@ -8,7 +8,14 @@ local propertySystem = require("propertySystem")
--- Before you can access Basalt, you need to add the following code on top of your file: --- Before you can access Basalt, you need to add the following code on top of your file:
--- @usage local basalt = require("basalt") --- @usage local basalt = require("basalt")
--- What this code does is it loads basalt into the project, and you can access it by using the variable defined as "basalt". --- What this code does is it loads basalt into the project, and you can access it by using the variable defined as "basalt".
-- @module Basalt
--- @class Basalt
--- @field traceback boolean Whether to show a traceback on errors
--- @field _events table A table of events and their callbacks
--- @field _schedule function[] A table of scheduled functions
--- @field _plugins table A table of plugins
--- @field LOGGER Log The logger instance
--- @field path string The path to the Basalt library
local basalt = {} local basalt = {}
basalt.traceback = true basalt.traceback = true
basalt._events = {} basalt._events = {}
@@ -79,7 +86,8 @@ function basalt.create(type, properties, lazyLoading, parent)
end end
end end
--- Creates and returns a new frame --- Creates and returns a new BaseFrame
--- @shortDescription Creates a new BaseFrame
--- @return table BaseFrame The created frame instance --- @return table BaseFrame The created frame instance
--- @usage local mainFrame = basalt.createFrame() --- @usage local mainFrame = basalt.createFrame()
function basalt.createFrame() function basalt.createFrame()
@@ -90,6 +98,7 @@ function basalt.createFrame()
end end
--- Returns the element manager instance --- Returns the element manager instance
--- @shortDescription Returns the element manager
--- @return table ElementManager The element manager --- @return table ElementManager The element manager
--- @usage local manager = basalt.getElementManager() --- @usage local manager = basalt.getElementManager()
function basalt.getElementManager() function basalt.getElementManager()
@@ -97,6 +106,7 @@ function basalt.getElementManager()
end end
--- Gets or creates the main frame --- Gets or creates the main frame
--- @shortDescription Gets or creates the main frame
--- @return BaseFrame table The main frame instance --- @return BaseFrame table The main frame instance
--- @usage local frame = basalt.getMainFrame() --- @usage local frame = basalt.getMainFrame()
function basalt.getMainFrame() function basalt.getMainFrame()
@@ -107,6 +117,7 @@ function basalt.getMainFrame()
end end
--- Sets the active frame --- Sets the active frame
--- @shortDescription Sets the active frame
--- @param frame table The frame to set as active --- @param frame table The frame to set as active
--- @usage basalt.setActiveFrame(myFrame) --- @usage basalt.setActiveFrame(myFrame)
function basalt.setActiveFrame(frame) function basalt.setActiveFrame(frame)
@@ -114,6 +125,7 @@ function basalt.setActiveFrame(frame)
end end
--- Schedules a function to be updated --- Schedules a function to be updated
--- @shortDescription Schedules a function to be updated
--- @function scheduleUpdate --- @function scheduleUpdate
--- @param func function The function to schedule --- @param func function The function to schedule
--- @return number Id The schedule ID --- @return number Id The schedule ID
@@ -124,6 +136,7 @@ function basalt.scheduleUpdate(func)
end end
--- Removes a scheduled update --- Removes a scheduled update
--- @shortDescription Removes a scheduled update
--- @function removeSchedule --- @function removeSchedule
--- @param id number The schedule ID to remove --- @param id number The schedule ID to remove
--- @usage basalt.removeSchedule(scheduleId) --- @usage basalt.removeSchedule(scheduleId)
@@ -131,7 +144,7 @@ function basalt.removeSchedule(id)
basalt._schedule[id] = nil basalt._schedule[id] = nil
end end
--- @local Internal event handler ---@private
local function updateEvent(event, ...) local function updateEvent(event, ...)
if(event=="terminate")then basalt.stop() end if(event=="terminate")then basalt.stop() end
if lazyElementsEventHandler(event, ...) then return end if lazyElementsEventHandler(event, ...) then return end
@@ -149,7 +162,7 @@ local function updateEvent(event, ...)
end end
end end
--- @local Internal render function ---@private
local function renderFrames() local function renderFrames()
if(mainFrame)then if(mainFrame)then
mainFrame:render() mainFrame:render()
@@ -157,6 +170,7 @@ local function renderFrames()
end end
--- Updates all scheduled functions --- Updates all scheduled functions
--- @shortDescription Updates all scheduled functions
--- @usage basalt.update() --- @usage basalt.update()
function basalt.update() function basalt.update()
for k,v in pairs(basalt._schedule) do for k,v in pairs(basalt._schedule) do
@@ -167,6 +181,7 @@ function basalt.update()
end end
--- Stops the Basalt runtime --- Stops the Basalt runtime
--- @shortDescription Stops the Basalt runtime
--- @usage basalt.stop() --- @usage basalt.stop()
function basalt.stop() function basalt.stop()
term.clear() term.clear()
@@ -175,6 +190,7 @@ function basalt.stop()
end end
--- Starts the Basalt runtime --- Starts the Basalt runtime
--- @shortDescription Starts the Basalt runtime
--- @param isActive? boolean Whether to start active (default: true) --- @param isActive? boolean Whether to start active (default: true)
--- @usage basalt.run() --- @usage basalt.run()
--- @usage basalt.run(false) --- @usage basalt.run(false)
@@ -197,6 +213,10 @@ function basalt.run(isActive)
end end
end end
--- Returns a Plugin API
--- @shortDescription Returns a Plugin API
--- @param name string The name of the plugin
--- @return table Plugin The plugin API
function basalt.getAPI(name) function basalt.getAPI(name)
return elementManager.getAPI(name) return elementManager.getAPI(name)
end end

View File

@@ -11,7 +11,12 @@ local commentTypes = {
"function", "function",
"local", "local",
"shortDescription", "shortDescription",
"property" "property",
"combinedProperty",
"event",
"private",
"protected",
"field"
} }
local function extractComment(line) local function extractComment(line)
@@ -60,8 +65,11 @@ end
function markdown.parse(content) function markdown.parse(content)
local blocks = {} local blocks = {}
local properties = {} local properties = {}
local combinedProperties = {}
local events = {} local events = {}
local fields = {}
local currentBlock = {type = "comment", desc = {}} local currentBlock = {type = "comment", desc = {}}
local skipNextFunction = false
for line in content:gsub("\r\n", "\n"):gmatch("([^\n]*)\n?") do for line in content:gsub("\r\n", "\n"):gmatch("([^\n]*)\n?") do
if line:match("^%s*$") or line == "" then if line:match("^%s*$") or line == "" then
@@ -76,6 +84,8 @@ function markdown.parse(content)
if(commentType == "desc") then if(commentType == "desc") then
currentBlock.usageIsActive = false currentBlock.usageIsActive = false
table.insert(currentBlock.desc, value) table.insert(currentBlock.desc, value)
elseif(commentType == "private")or(commentType == "protected")then
skipNextFunction = true
else else
if(commentType == "module")then if(commentType == "module")then
currentBlock.usageIsActive = false currentBlock.usageIsActive = false
@@ -100,6 +110,11 @@ function markdown.parse(content)
elseif(commentType == "property")then elseif(commentType == "property")then
currentBlock = {type = "comment", desc = {}} currentBlock = {type = "comment", desc = {}}
table.insert(properties, value) table.insert(properties, value)
elseif(commentType == "combinedProperty")then
currentBlock = {type = "comment", desc = {}}
table.insert(combinedProperties, value)
elseif(commentType == "field")then
table.insert(fields, value)
elseif(commentType == "event")then elseif(commentType == "event")then
currentBlock = {type = "comment", desc = {}} currentBlock = {type = "comment", desc = {}}
table.insert(events, value) table.insert(events, value)
@@ -113,10 +128,14 @@ function markdown.parse(content)
end end
else else
local funcName = getFunctionName(line) if(skipNextFunction)then
if funcName then skipNextFunction = false
currentBlock.func = funcName else
currentBlock.type = "function" local funcName = getFunctionName(line)
if funcName then
currentBlock.func = funcName
currentBlock.type = "function"
end
end end
end end
end end
@@ -141,7 +160,7 @@ function markdown.parse(content)
return a.func < b.func return a.func < b.func
end) end)
markdown.blocks = {properties = properties, events = events} markdown.blocks = {combinedProperties = combinedProperties, properties = properties, events = events, fields = fields}
for _, block in ipairs(otherBlocks) do for _, block in ipairs(otherBlocks) do
table.insert(markdown.blocks, block) table.insert(markdown.blocks, block)
end end
@@ -192,6 +211,27 @@ local function markdownFunction(block)
return output return output
end end
local function markdownFields()
if(#markdown.blocks.fields<=0)then
return ""
end
local output = "\n## Fields\n\n|Field|Type|Description|\n|---|---|---|\n"
for _, block in pairs(markdown.blocks.fields) do
local name, rest = block:match("([%w_]+)%s+(.+)")
if name and rest then
local fieldType, desc = rest:match("([^%s].-)%s+([^%s].*)")
if fieldType and desc then
output = output .. string.format("|%s|`%s`|%s|\n",
name,
fieldType,
desc or ""
)
end
end
end
return output
end
local function markdownProperties() local function markdownProperties()
if(#markdown.blocks.properties<=0)then if(#markdown.blocks.properties<=0)then
return "" return ""
@@ -204,18 +244,36 @@ local function markdownProperties()
return output return output
end end
local function markdownCombinedProperties()
if(markdown.blocks.combinedProperties==nil)then
return ""
end
if(#markdown.blocks.combinedProperties<=0)then
return ""
end
local output = "\n## Combined Properties\n\n|Name|Properties|Description|\n|---|---|---|\n"
for _, block in pairs(markdown.blocks.combinedProperties) do
local name, paramType, defaultValue, desc = block:match("([^%s]+)%s+([^%s]+)%s+([^%s]+)%s+(.*)")
output = output .. string.format("|%s|%s|%s|%s\n", name, paramType, defaultValue, desc)
end
return output
end
local function markdownEvents() local function markdownEvents()
if(#markdown.blocks.events<=0)then if(#markdown.blocks.events<=0)then
return "" return ""
end end
local output = "\n## Events\n\n" local output = "\n## Events\n\n|Event|Parameters|Description|\n|---|---|---|\n"
for _, block in pairs(markdown.blocks) do
if block.type == "module" or block.type == "class" then for _, event in pairs(markdown.blocks.events) do
if(block.event~=nil)then local name, params, desc = event:match("([%w_]+)%s+{([^}]+)}%s*(.*)")
for _, line in pairs(block.event) do if name and params then
output = output .. "* " .. line .. "\n" local formattedParams = params:gsub("%s*,%s*", ", ")
end output = output .. string.format("|%s|`%s`|%s|\n",
end name,
formattedParams,
desc or ""
)
end end
end end
return output return output
@@ -263,7 +321,9 @@ local function markdownModule(block)
output = output .. line .. "\n" output = output .. line .. "\n"
end end
output = output .. markdownFields()
output = output .. markdownProperties() output = output .. markdownProperties()
output = output .. markdownCombinedProperties()
output = output .. markdownEvents() output = output .. markdownEvents()
output = output .. markdownModuleOrClassFunctions(block) output = output .. markdownModuleOrClassFunctions(block)
output = output .. "\n" output = output .. "\n"
@@ -288,7 +348,9 @@ local function markdownClass(block)
output = output .. line .. "\n" output = output .. line .. "\n"
end end
output = output .. markdownFields()
output = output .. markdownProperties() output = output .. markdownProperties()
output = output .. markdownCombinedProperties()
output = output .. markdownEvents() output = output .. markdownEvents()
output = output .. markdownModuleOrClassFunctions(block) output = output .. markdownModuleOrClassFunctions(block)
output = output .. "\n" output = output .. "\n"