Rewrote text rendering to be slightly better
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 16 Apr 2020 21:33:27 +0000 (16:33 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 16 Apr 2020 21:33:27 +0000 (16:33 -0500)
conf.lua
src/ui/components.lua
src/ui/text.lua
src/utils.lua
src/wasm/decompile.lua
src/wasm/text.lua

index 8b1368951cab9b60092aba606ac43c48fcb27b25..752af6d28097aa6f3ef0da73c0856e55295982bf 100644 (file)
--- a/conf.lua
+++ b/conf.lua
@@ -50,5 +50,5 @@ end
 return {
        COLOR_SCHEMES = COLOR_SCHEMES;
 
-       COLOR_SCHEME = COLOR_SCHEMES.DARK;
+       COLOR_SCHEME = COLOR_SCHEMES.LIGHT;
 }
index 74588c96829af7eedddc9f9f8329f227e4f577d6..97940a4f1d14eb8f504efed8dd16ce3486cdbbeb 100644 (file)
@@ -116,9 +116,10 @@ local function_block = { extends = "drag_rect" }
 function function_block:init(wasm_function)
        self.func = wasm_function
 
-       local header_text, body_text = wasm_text.generate_text_format(self.func, globals.wasm_module, COLORS)
+       local header_text, body_text, code_text = wasm_text.generate_text_format(self.func, globals.wasm_module, COLORS)
        self.header_text = header_text
        self.body_text = body_text
+       self.code_text = code_text
 
        self.scroll = 0
        self.body_visible = true
@@ -226,11 +227,15 @@ function function_block:predraw()
                love.graphics.setColor(COLORS.dark_background)
                love.graphics.rectangle("fill", 2, 2, self.rect.w - 4, 18)
 
-               local x, y = ui_text.render_text(2, 2, self.header_text)
+               local x, y = ui_text.render_lines(2, 2, self.header_text)
 
                if self.body_visible and self.body_text then
                        love.graphics.intersectScissor(2, y - 4, self.rect.w - 4, self.rect.h - 22)
-                       ui_text.render_text(2, y - self.scroll, self.body_text)
+                       x, y = ui_text.render_lines(2, y - self.scroll, self.body_text)
+                       x, y = ui_text.render_lines(2, y, self.code_text, {
+                               fg = COLORS.code,
+                               bg = COLORS.dark_background
+                       })
                end
 
                love.graphics.pop()
index 7a490fbd0f55e55d3eb9386e7b9263d3fdd212c1..53ca9790eb29191082c80e8a07caa0c5dda46740 100644 (file)
@@ -1,11 +1,66 @@
 import { }
 
-FONTS = {}
-
 Text = {}
 
+function Text.render_lines(x, y, lines, line_number)
+       local font = love.graphics.getFont()
+
+       local origx = x
+       local font_height = font:getHeight()
+       local tab_width = font:getWidth "    "
+       local indent = 0
+       local had_text = false
+
+       local line_num = 1
+
+       for _, line in ipairs(lines) do
+               x = origx
+               if line_number then
+                       love.graphics.setColor(line_number.bg)
+                       love.graphics.rectangle("fill", x, y, tab_width, font_height + 2)
+                       love.graphics.setColor(line_number.fg)
+                       love.graphics.print(("%4d"):format(line_num), x, y)
+                       x = x + tab_width + 10
+                       line_num = line_num + 1
+               end
+
+               if line.indent then indent = indent + line.indent end
+               x = x + tab_width * indent
+
+
+               had_text = false
+               for _, text_obj in ipairs(line) do
+                       local text = text_obj[1]
+                       -- TODO: This isn't great but it works for now
+                       if not text then goto continue end
+                       had_text = true
+
+                       local width = font:getWidth(text)
+
+                       if text_obj.bg then
+                               love.graphics.setColor(text_obj.bg)
+                               love.graphics.rectangle("fill", x, y, width, font_height + 2);
+                       end
+
+                       if text_obj.fg then love.graphics.setColor(text_obj.fg) end
+
+                       love.graphics.print(text, x, y)
+
+                       x = x + width
+                       ::continue::
+               end
+
+               if had_text then
+                       y = y + font_height + 2
+               end
+       end
+
+       return x, y
+end
+
+
 -- TODO: Rewrite because this is bad
-function Text.render_text(x, y, text)
+function Text.render_text_old(x, y, text)
        local curr_font = love.graphics.getFont()
 
        local origx = x
@@ -57,6 +112,12 @@ function Text.render_text(x, y, text)
        return x, y
 end
 
+-- For printing formatted text to the console
+function Text.print_text(text)
+       error "Unimplemented"
+end
+
+FONTS = {}
 function Text.register_font(name, size)
        local fonts = FONTS[name] or {}
        fonts[size] = love.graphics.newFont(("res/%s.ttf"):format(name), size)
index 0889f6fbf733352e2ffe0486f9f33a7820f815a1..176b891e1f87c101340f3727dbffe74b171defcc 100644 (file)
@@ -71,6 +71,12 @@ function revipairs(t)
        return ripairs_it, t, max
 end
 
+function table.append(orig, new)
+       for _, e in ipairs(new) do
+               table.insert(orig, e)
+       end
+end
+
 class "Rectangle" {
        init = function(self, x, y, w, h)
                self.x = x
index e4bf5e04a0653ffe912b47844e7b65dc40fba61c..40a48b110c35fd23b780f00ab3892dc0ef35d97d 100644 (file)
@@ -116,12 +116,12 @@ end
 
 function expr_local_set(stack, instr, func)
        local varname = func.locals[instr.x + 1].name
-       return varname .. " = " .. stack:pop()
+       return varname .. " <- " .. stack:pop()
 end
 
 function expr_local_tee(stack, instr, func)
        local varname = func.locals[instr.x + 1].name
-       stack:push(varname .. " = " .. stack:pop())
+       stack:push(varname .. " <- " .. stack:pop())
 end
 
 function expr_global_get(stack, instr)
@@ -129,7 +129,7 @@ function expr_global_get(stack, instr)
 end
 
 function expr_global_set(stack, instr)
-       return "global" .. instr.x .. " = " .. stack:pop()
+       return "global" .. instr.x .. " <- " .. stack:pop()
 end
 
 function expr_load(stack, instr)
index 9abbbfb8d9c22639b8567424916d44f60bb545a8..881144a7ec6a83eec4880e8fdc69682cbab91f8a 100644 (file)
@@ -8,42 +8,45 @@ import {
 -- (but I wouldn't paralleize it right now anyway)
 local line = 1
 
-function build_type(text, type_, color)
+function build_type(type_, color)
+       local text = ""
        for _, t in ipairs(type_.param_types) do
-               table.insert(text, { text = t .. " ", color = color })
+               text = text .. t .. " "
        end
 
        if #type_.param_types == 0 then
-               table.insert(text, { text = "void ", color = color })
+               text = text .. "void "
        end
 
-       table.insert(text, { text = "-> " })
+       text = text .. "-> "
 
        for _, t in ipairs(type_.result_types) do
-               table.insert(text, { text = t .. " " })
+               text = text .. t .. " "
        end
 
        if #type_.result_types == 0 then
-               table.insert(text, { text = "void" })
+               text = text .. "void"
        end
+
+       return { text; fg = color }
 end
 
-function build_header(func, mod, colors)
+-- Ctx -> List TextObj
+function build_header(ctx)
        local text = {}
 
-       if mod.start ~= nil and mod.start.contents.func == func.funcidx then
-               table.insert(text, { text = "*START* ", color = colors.primary_text })
+       if ctx.mod.start ~= nil and ctx.mod.start.contents.func == ctx.func.funcidx then
+               table.insert(text, { "*START* ", fg = ctx.colors.primary_text })
        end
-       table.insert(text, { text = "func[" .. func.funcidx .. "] ", color = colors.code })
-       table.insert(text, { text = func.name, color = colors.keyword })
-       table.insert(text, { text = ": ", color = colors.code })
-
-       build_type(text, func.type_, colors.code)
-       table.insert(text, { newline = true })
+       table.insert(text, { "func[" .. ctx.func.funcidx .. "] ", fg = ctx.colors.code })
+       table.insert(text, { ctx.func.name, fg = ctx.colors.keyword })
+       table.insert(text, { ": ", fg = ctx.colors.code })
 
+       table.insert(text, build_type(ctx.func.type_, ctx.colors.code))
        return text
 end
 
+-- This will be moved into ui/text
 function add_line_number(textlist, colors, newline)
        if newline == nil then newline = true end
 
@@ -59,116 +62,148 @@ function add_line_number(textlist, colors, newline)
        line = line + 1
 end
 
-function instr_to_text(textlist, instr, func, mod, colors)
+-- Ctx -> Instr -> List List TextObj
+function instr_to_text(ctx, instr)
+       local lines = {}
+
        if instr[1] == "block" then
-               table.insert(textlist, { change_indent = 1 })
-               for _, ins in ipairs(instr.instrs) do
-                       instr_to_text(textlist, ins, func, mod, colors)
+               table.insert(lines, { indent = 1 })
+               for i, ins in ipairs(instr.instrs) do
+                       local res = instr_to_text(ctx, ins)
+                       table.append(lines, res)
                end
-               table.insert(textlist, { change_indent = -1 })
-               add_line_number(textlist, colors)
-               table.insert(textlist, { text = "label ", color = colors.jumppoint })
-               table.insert(textlist, { text = instr.label, color = colors.value })
+               table.insert(lines, {
+                       indent = -1;
+                       { "label ", fg = ctx.colors.jumppoint };
+                       { instr.label, fg = ctx.colors.value };
+               })
 
        elseif instr[1] == "loop" then
-               add_line_number(textlist, colors)
-               table.insert(textlist, { text = "loop ", color = colors.jumppoint })
-               table.insert(textlist, { text = instr.label, color = colors.value, change_indent = 1 })
-
-               for _, ins in ipairs(instr.instrs) do
-                       instr_to_text(textlist, ins, func, mod, colors)
+               table.insert(lines, {
+                       { "loop ", fg = ctx.colors.jumppoint };
+                       { instr.label, fg = ctx.colors.value };
+               })
+               table.insert(lines, { indent = 1 })
+               for i, ins in ipairs(instr.instrs) do
+                       local res = instr_to_text(ctx, ins)
+                       table.append(lines, res)
                end
-               table.insert(textlist, { change_indent = -1 })
+               table.insert(lines, { indent = -1 })
 
        elseif instr[1] == "if" then
-               add_line_number(textlist, colors)
-               table.insert(textlist, { text = "if", color = colors.jumppoint, change_indent = 1 })
-               for _, ins in ipairs(instr.instrs) do
-                       instr_to_text(textlist, ins, func, mod, colors)
+               table.insert(lines, {
+                       { "if ", fg = ctx.colors.jumppoint };
+                       { instr.label, fg = ctx.colors.value };
+               })
+               table.insert(lines, { indent = 1 })
+               for i, ins in ipairs(instr.instrs) do
+                       local res = instr_to_text(ctx, ins)
+                       table.append(lines, res)
                end
-               table.insert(textlist, { change_indent = -1 })
+               table.insert(lines, { indent = -1 })
 
                if #instr.else_instrs > 0 then
-                       add_line_number(textlist, colors)
-                       table.insert(textlist, { text = "else", color = colors.jumppoint, change_indent = 1 })
-                       for _, ins in ipairs(instr.else_instrs) do
-                               instr_to_text(textlist, ins, func, mod, colors)
+                       table.insert(lines, { "else", fg = ctx.colors.jumppoint })
+                       table.insert(lines, { indent = 1 })
+                       for i, ins in ipairs(instr.instrs) do
+                               local res = instr_to_text(ctx, ins)
+                               table.append(lines, res)
                        end
-                       table.insert(textlist, { change_indent = -1 })
+                       table.insert(lines, { indent = -1 })
                end
 
        elseif instr[1] == "call" then
-               add_line_number(textlist, colors)
-               table.insert(textlist, { text = instr[1] .. " ", color = colors.keyword })
-               table.insert(textlist, { text = mod.funcs[instr.x].name, color = colors.value })
+               table.insert(lines, {
+                       { "call ", fg = ctx.colors.keyword },
+                       { ctx.mod.funcs[instr.x].name, fg = ctx.colors.value }
+               })
 
        elseif instr[1] == "call_indirect" then
-               add_line_number(textlist, colors)
-               table.insert(textlist, { text = ("%s [%d] "):format(instr[1], instr.table), color = colors.keyword })
-               build_type(textlist, mod.types.contents[instr.x + 1], colors.code)
+               table.insert(lines, {
+                       { ("%s [%d] "):format(instr[1], instr.table), fg = ctx.colors.keyword },
+                       build_type(ctx.mod.types.contents[instr.x + 1], ctx.colors.code)
+               })
 
        elseif instr[1]:match("local") then
-               add_line_number(textlist, colors)
-               table.insert(textlist, { text = instr[1] .. " ", color = colors.keyword })
-               table.insert(textlist, { text = func.locals[instr.x + 1].name, color = colors.value })
+               table.insert(lines, {
+                       { instr[1] .. " ", fg = ctx.colors.keyword },
+                       { ctx.func.locals[instr.x + 1].name, fg = ctx.colors.value }
+               })
 
        else
-               add_line_number(textlist, colors)
-               table.insert(textlist, { text = instr[1] .. " ", color = colors.keyword })
+               local line = {}
+               table.insert(line, { instr[1] .. " ", fg = ctx.colors.keyword })
                if instr.x then
                        if type(instr.x) == "table" then
                                if instr.x[1] == "memarg" then
-                                       table.insert(textlist, {
-                                               text = ("align=0x%02x offset=0x%04x"):format(instr.x.align, instr.x.offset);
-                                               color = colors.value
+                                       table.insert(line, {
+                                               ("align=0x%02x offset=0x%04x"):format(instr.x.align, instr.x.offset);
+                                               fg = ctx.colors.value
                                        })
                                end
 
                                if instr.x[1] == "labelidx" then
                                        if instr.x.block then
-                                               table.insert(textlist, { text = instr.x.block.label .. " ", color = colors.value })
+                                               table.insert(line, { instr.x.block.label .. " ", fg = ctx.colors.value })
                                        end
-                                       table.insert(textlist, { text = ("[0x%02x]"):format(instr.x.labelidx), color = colors.code })
+                                       table.insert(line, { ("[0x%02x]"):format(instr.x.labelidx), fg = ctx.colors.code })
                                end
                        else
-                               table.insert(textlist, { text = tostring(instr.x), color = colors.value })
+                               table.insert(line, { tostring(instr.x), fg = ctx.colors.value })
                        end
                end
+
+               lines = { line }
        end
 
-       return line
+       return lines
 end
 
-function build_body(func, mod, colors)
-       if func.imported then
-               return { { text = "Imported function", colors = colors.code } }
+
+function build_body(ctx)
+       if ctx.func.imported then
+               return { { "Imported function", fg = ctx.colors.code } }
        end
 
-       local text = {}
-       table.insert(text, { text = " Locals:", color = colors.keyword, newline = true, change_indent = 2 })
-       for _, loc in ipairs(func.locals) do
-               table.insert(text, { text = loc.type_ .. " " .. loc.name, color = colors.code , newline = true })
+       local lines = {}
+       table.insert(lines, {
+               { " Locals:", fg = ctx.colors.keyword }
+       })
+       table.insert(lines, { indent = 1 })
+       for _, loc in ipairs(ctx.func.locals) do
+               table.insert(lines, {
+                       { loc.type_ .. " " .. loc.name, fg = ctx.colors.code }
+               })
        end
 
-       if #func.locals == 0 then
-               table.insert(text, { text = "no locals", color = colors.code, newline = true })
+       if #ctx.func.locals == 0 then
+               table.insert(lines, {
+                       { "no locals", fg = ctx.colors.code }
+               })
        end
 
-       table.insert(text, { newline = true, change_indent = -2 })
-       table.insert(text, { text = " Body:", color = colors.keyword })
+       table.insert(lines, { indent = -1 })
+       table.insert(lines, {
+               { " Body:", fg = ctx.colors.keyword }
+       })
 
-       for _, instr in ipairs(func.body) do
-               instr_to_text(text, instr, func, mod, colors)
+       local code_lines = {}
+       for _, instr in ipairs(ctx.func.body) do
+               table.append(code_lines, instr_to_text(ctx, instr))
        end
 
-       return text
+       return lines, code_lines
 end
 
 function generate_text_format(func, mod, colors, line_start)
-       line = line_start or 1
-       local heading = build_header(func, mod, colors)
-       local body = build_body(func, mod, colors)
-       return heading, body
+       local ctx = {
+               func = func;
+               mod = mod;
+               colors = colors;
+       }
+       local heading = build_header(ctx)
+       local body_preface, body = build_body(ctx)
+       return { heading }, body_preface, body
 end
 
 return module {
@@ -176,3 +211,84 @@ return module {
        build_header = build_header;
        build_body = build_body;
 }
+
+
+-- function instr_to_text_old(textlist, instr, func, mod, colors)
+--     if instr[1] == "block" then
+--             table.insert(textlist, { change_indent = 1 })
+--             for _, ins in ipairs(instr.instrs) do
+--                     instr_to_text(textlist, ins, func, mod, colors)
+--             end
+--             table.insert(textlist, { change_indent = -1 })
+--             add_line_number(textlist, colors)
+--             table.insert(textlist, { text = "label ", color = colors.jumppoint })
+--             table.insert(textlist, { text = instr.label, color = colors.value })
+--
+--     elseif instr[1] == "loop" then
+--             add_line_number(textlist, colors)
+--             table.insert(textlist, { text = "loop ", color = colors.jumppoint })
+--             table.insert(textlist, { text = instr.label, color = colors.value, change_indent = 1 })
+--
+--             for _, ins in ipairs(instr.instrs) do
+--                     instr_to_text(textlist, ins, func, mod, colors)
+--             end
+--             table.insert(textlist, { change_indent = -1 })
+--
+--     elseif instr[1] == "if" then
+--             add_line_number(textlist, colors)
+--             table.insert(textlist, { text = "if", color = colors.jumppoint, change_indent = 1 })
+--             for _, ins in ipairs(instr.instrs) do
+--                     instr_to_text(textlist, ins, func, mod, colors)
+--             end
+--             table.insert(textlist, { change_indent = -1 })
+--
+--             if #instr.else_instrs > 0 then
+--                     add_line_number(textlist, colors)
+--                     table.insert(textlist, { text = "else", color = colors.jumppoint, change_indent = 1 })
+--                     for _, ins in ipairs(instr.else_instrs) do
+--                             instr_to_text(textlist, ins, func, mod, colors)
+--                     end
+--                     table.insert(textlist, { change_indent = -1 })
+--             end
+--
+--     elseif instr[1] == "call" then
+--             add_line_number(textlist, colors)
+--             table.insert(textlist, { text = instr[1] .. " ", color = colors.keyword })
+--             table.insert(textlist, { text = mod.funcs[instr.x].name, color = colors.value })
+--
+--     elseif instr[1] == "call_indirect" then
+--             add_line_number(textlist, colors)
+--             table.insert(textlist, { text = ("%s [%d] "):format(instr[1], instr.table), color = colors.keyword })
+--             build_type(textlist, mod.types.contents[instr.x + 1], colors.code)
+--
+--     elseif instr[1]:match("local") then
+--             add_line_number(textlist, colors)
+--             table.insert(textlist, { text = instr[1] .. " ", color = colors.keyword })
+--             table.insert(textlist, { text = func.locals[instr.x + 1].name, color = colors.value })
+--
+--     else
+--             add_line_number(textlist, colors)
+--             table.insert(textlist, { text = instr[1] .. " ", color = colors.keyword })
+--             if instr.x then
+--                     if type(instr.x) == "table" then
+--                             if instr.x[1] == "memarg" then
+--                                     table.insert(textlist, {
+--                                             text = ("align=0x%02x offset=0x%04x"):format(instr.x.align, instr.x.offset);
+--                                             color = colors.value
+--                                     })
+--                             end
+--
+--                             if instr.x[1] == "labelidx" then
+--                                     if instr.x.block then
+--                                             table.insert(textlist, { text = instr.x.block.label .. " ", color = colors.value })
+--                                     end
+--                                     table.insert(textlist, { text = ("[0x%02x]"):format(instr.x.labelidx), color = colors.code })
+--                             end
+--                     else
+--                             table.insert(textlist, { text = tostring(instr.x), color = colors.value })
+--                     end
+--             end
+--     end
+--
+--     return line
+-- end