From 83fcee49038842f3d7f60ebce5058d6e3fccbc92 Mon Sep 17 00:00:00 2001 From: cukmekerb Date: Mon, 5 Jul 2021 21:50:35 -0700 Subject: minimap plugin no longer crashes lite-xl and now renders tab indentation --- plugins/minimap.lua | 511 ++++++++++++++++++++++++++-------------------------- 1 file changed, 256 insertions(+), 255 deletions(-) (limited to 'plugins/minimap.lua') diff --git a/plugins/minimap.lua b/plugins/minimap.lua index f58a609..ecae5eb 100644 --- a/plugins/minimap.lua +++ b/plugins/minimap.lua @@ -1,107 +1,112 @@ --- mod-version:1 -- lite-xl 1.16 --- Pulled from git@github.com:andsve/lite-plugins.git - -local command = require "core.command" -local common = require "core.common" -local config = require "core.config" -local style = require "core.style" -local DocView = require "core.docview" +-- mod-version:1 +local command = require "core.command" +local common = require "core.common" +local config = require "core.config" +local style = require "core.style" +local DocView = require "core.docview" -- General plugin settings -config.minimap_enabled = true -config.minimap_width = 100 -config.minimap_instant_scroll = false +config.minimap_enabled = true +config.minimap_width = 100 +config.minimap_instant_scroll = false config.minimap_syntax_highlight = true -config.minimap_scale = 1 -config.minimap_draw_background = true +config.minimap_scale = 1 + +-- how many spaces one tab is equivalent to +config.minimap_tab_width = 4 + +config.minimap_draw_background = true -- Configure size for rendering each char in the minimap -local char_height = 1 * SCALE * config.minimap_scale +local char_height = 1 * SCALE * config.minimap_scale local char_spacing = 0.8 * SCALE * config.minimap_scale local line_spacing = 2 * SCALE * config.minimap_scale -- Overloaded since the default implementation adds a extra x3 size of hotspot for the mouse to hit the scrollbar. local prev_scrollbar_overlaps_point = DocView.scrollbar_overlaps_point DocView.scrollbar_overlaps_point = function(self, x, y) - if not config.minimap_enabled then return prev_scrollbar_overlaps_point(self, x, y) end + if not config.minimap_enabled then + return prev_scrollbar_overlaps_point(self, x, y) + end - local sx, sy, sw, sh = self:get_scrollbar_rect() - return x >= sx and x < sx + sw and y >= sy and y < sy + sh + local sx, sy, sw, sh = self:get_scrollbar_rect() + return x >= sx and x < sx + sw and y >= sy and y < sy + sh end -- Helper function to determine if current file is too large to be shown fully inside the minimap area. local function is_file_too_large(self) - local line_count = #self.doc.lines - local _, _, _, sh = self:get_scrollbar_rect() + local line_count = #self.doc.lines + local _, _, _, sh = self:get_scrollbar_rect() - -- check if line count is too large to fit inside the minimap area - local max_minmap_lines = math.floor(sh / line_spacing) - return line_count > 1 and line_count > max_minmap_lines + -- check if line count is too large to fit inside the minimap area + local max_minmap_lines = math.floor(sh / line_spacing) + return line_count > 1 and line_count > max_minmap_lines end -- Overloaded with an extra check if the user clicked inside the minimap to automatically scroll to that line. local prev_on_mouse_pressed = DocView.on_mouse_pressed DocView.on_mouse_pressed = function(self, button, x, y, clicks) - if not config.minimap_enabled then return prev_on_mouse_pressed(self, button, x, y, clicks) end - - -- check if user clicked in the minimap area and jump directly to that line - -- unless they are actually trying to perform a drag - local minimap_hit = self:scrollbar_overlaps_point(x, y) - if minimap_hit then - local line_count = #self.doc.lines - local minimap_height = line_count * line_spacing - - -- check if line count is too large to fit inside the minimap area - local is_too_large = is_file_too_large(self) - if is_too_large then - local _, _, _, sh = self:get_scrollbar_rect() - minimap_height = sh - end - - -- calc which line to jump to - local dy = y - self.position.y - local jump_to_line = math.floor((dy / minimap_height) * line_count) + 1 - - local _, cy, _, cy2 = self:get_content_bounds() - local lh = self:get_line_height() - local visible_lines_count = math.max(1, (cy2 - cy) / lh) - local visible_lines_start = math.max(1, math.floor(cy / lh)) - - -- calc if user hit the currently visible area - local hit_visible_area = true - if is_too_large then - - local visible_height = visible_lines_count * line_spacing - local scroll_pos = (visible_lines_start-1) / (line_count - visible_lines_count - 1) - scroll_pos = math.min(1.0, scroll_pos) -- 0..1 - local visible_y = self.position.y + scroll_pos * (minimap_height - visible_height) - - local t = (line_count - visible_lines_start) / visible_lines_count - if t <= 1 then - visible_y = visible_y + visible_height * (1.0 - t) - end - - if y < visible_y or y > visible_y + visible_height then - hit_visible_area = false - end - else - - -- If the click is on the currently visible line numbers, - -- ignore it since then they probably want to initiate a drag instead. - if jump_to_line < visible_lines_start or jump_to_line > visible_lines_start + visible_lines_count then - hit_visible_area = false - end - end - - -- if user didn't click on the visible area (ie not dragging), scroll accordingly - if not hit_visible_area then - self:scroll_to_line(jump_to_line, false, config.minimap_instant_scroll) - return - end - - end - - return prev_on_mouse_pressed(self, button, x, y, clicks) + if not config.minimap_enabled then + return prev_on_mouse_pressed(self, button, x, y, clicks) + end + + -- check if user clicked in the minimap area and jump directly to that line + -- unless they are actually trying to perform a drag + local minimap_hit = self:scrollbar_overlaps_point(x, y) + if minimap_hit then + local line_count = #self.doc.lines + local minimap_height = line_count * line_spacing + + -- check if line count is too large to fit inside the minimap area + local is_too_large = is_file_too_large(self) + if is_too_large then + local _, _, _, sh = self:get_scrollbar_rect() + minimap_height = sh + end + + -- calc which line to jump to + local dy = y - self.position.y + local jump_to_line = math.floor((dy / minimap_height) * line_count) + 1 + + local _, cy, _, cy2 = self:get_content_bounds() + local lh = self:get_line_height() + local visible_lines_count = math.max(1, (cy2 - cy) / lh) + local visible_lines_start = math.max(1, math.floor(cy / lh)) + + -- calc if user hit the currently visible area + local hit_visible_area = true + if is_too_large then + + local visible_height = visible_lines_count * line_spacing + local scroll_pos = (visible_lines_start - 1) / + (line_count - visible_lines_count - 1) + scroll_pos = math.min(1.0, scroll_pos) -- 0..1 + local visible_y = self.position.y + scroll_pos * + (minimap_height - visible_height) + + local t = (line_count - visible_lines_start) / visible_lines_count + if t <= 1 then visible_y = visible_y + visible_height * (1.0 - t) end + + if y < visible_y or y > visible_y + visible_height then + hit_visible_area = false + end + else + + -- If the click is on the currently visible line numbers, + -- ignore it since then they probably want to initiate a drag instead. + if jump_to_line < visible_lines_start or jump_to_line > visible_lines_start + + visible_lines_count then hit_visible_area = false end + end + + -- if user didn't click on the visible area (ie not dragging), scroll accordingly + if not hit_visible_area then + self:scroll_to_line(jump_to_line, false, config.minimap_instant_scroll) + return + end + + end + + return prev_on_mouse_pressed(self, button, x, y, clicks) end -- Overloaded with pretty much the same logic as original DocView implementation, @@ -109,190 +114,186 @@ end -- since the "scrollbar" essentially represents the lines visible in the content view. local prev_on_mouse_moved = DocView.on_mouse_moved DocView.on_mouse_moved = function(self, x, y, dx, dy) - if not config.minimap_enabled then return prev_on_mouse_moved(self, x, y, dx, dy) end - - if self.dragging_scrollbar then - local line_count = #self.doc.lines - local lh = self:get_line_height() - local delta = lh / line_spacing * dy - - if is_file_too_large(self) then - local _, sy, _, sh = self:get_scrollbar_rect() - delta = (line_count * lh) / sh * dy - end - - self.scroll.to.y = self.scroll.to.y + delta - end - - -- we need to "hide" that the scrollbar is dragging so that View doesnt does its own scrolling logic - local t = self.dragging_scrollbar - self.dragging_scrollbar = false - local r = prev_on_mouse_moved(self, x, y, dx, dy) - self.dragging_scrollbar = t - return r + if not config.minimap_enabled then + return prev_on_mouse_moved(self, x, y, dx, dy) + end + + if self.dragging_scrollbar then + local line_count = #self.doc.lines + local lh = self:get_line_height() + local delta = lh / line_spacing * dy + + if is_file_too_large(self) then + local _, sy, _, sh = self:get_scrollbar_rect() + delta = (line_count * lh) / sh * dy + end + + self.scroll.to.y = self.scroll.to.y + delta + end + + -- we need to "hide" that the scrollbar is dragging so that View doesnt does its own scrolling logic + local t = self.dragging_scrollbar + self.dragging_scrollbar = false + local r = prev_on_mouse_moved(self, x, y, dx, dy) + self.dragging_scrollbar = t + return r end -- Overloaded since we want the mouse to interact with the full size of the minimap area, -- not juse the scrollbar. local prev_get_scrollbar_rect = DocView.get_scrollbar_rect -DocView.get_scrollbar_rect = function (self) - if not config.minimap_enabled then return prev_get_scrollbar_rect(self) end - - return - self.position.x + self.size.x - config.minimap_width * SCALE, - self.position.y, - config.minimap_width * SCALE, - self.size.y +DocView.get_scrollbar_rect = function(self) + if not config.minimap_enabled then return prev_get_scrollbar_rect(self) end + + return self.position.x + self.size.x - config.minimap_width * SCALE, + self.position.y, config.minimap_width * SCALE, self.size.y end -- Overloaded so we can render the minimap in the "scrollbar area". local prev_draw_scrollbar = DocView.draw_scrollbar -DocView.draw_scrollbar = function (self) - if not config.minimap_enabled then return prev_draw_scrollbar(self) end - - local x, y, w, h = self:get_scrollbar_rect() - - local highlight = self.hovered_scrollbar or self.dragging_scrollbar - local visual_color = highlight and style.scrollbar2 or style.scrollbar - - local _, cy, _, cy2 = self:get_content_bounds() - local lh = self:get_line_height() - local visible_lines_count = math.max(1, (cy2 - cy) / lh) - local visible_lines_start = math.max(1, math.floor(cy / lh)) - local scroller_height = visible_lines_count * line_spacing - local line_count = #self.doc.lines - - local visible_y = self.position.y + (visible_lines_start-1) * line_spacing - - - -- check if file is too large to fit inside the minimap area - local max_minmap_lines = math.floor(h / line_spacing) - local minimap_start_line = 1 - if is_file_too_large(self) then - - local scroll_pos = (visible_lines_start-1) / (line_count - visible_lines_count - 1) - scroll_pos = math.min(1.0, scroll_pos) -- 0..1, procent of visual area scrolled - - local scroll_pos_pixels = scroll_pos * (h - scroller_height) - visible_y = self.position.y + scroll_pos_pixels - - -- offset visible area if user is scrolling past end - local t = (line_count - visible_lines_start) / visible_lines_count - if t <= 1 then - visible_y = visible_y + scroller_height * (1.0 - t) - end - - minimap_start_line = visible_lines_start - math.floor(scroll_pos_pixels / line_spacing) - minimap_start_line = math.max(1, math.min(minimap_start_line, line_count - max_minmap_lines)) - end - - if config.minimap_draw_background then - renderer.draw_rect(x, y, w, h, style.minimap_background or style.background) - end - -- draw visual rect - renderer.draw_rect(x, visible_y, w, scroller_height, visual_color) - - -- time to draw the actual code, setup some local vars that are used in both highlighted and plain renderind. - local line_y = y - - -- when not using syntax highlighted rendering, just use the normal color but dim it 50%. - local color = style.syntax["normal"] - color = { color[1],color[2],color[3],color[4] * 0.5 } - - -- we try to "batch" characters so that they can be rendered as just one rectangle instead of one for each. - local batch_width = 0 - local batch_start = x - local minimap_cutoff_x = x + config.minimap_width * SCALE - - -- render lines with syntax highlighting - if config.minimap_syntax_highlight then - - -- keep track of the highlight type, since this needs to break batches as well - local batch_syntax_type = nil - - local function flush_batch(type) - if batch_width > 0 then - -- fetch and dim colors - color = style.syntax[batch_syntax_type] - color = { color[1], color[2], color[3], color[4] * 0.5 } - renderer.draw_rect(batch_start, line_y, batch_width, char_height, color) - end - batch_syntax_type = type - batch_start = batch_start + batch_width - batch_width = 0 - end - - -- per line - local endidx = minimap_start_line + max_minmap_lines - endidx = math.min(endidx, line_count) - for idx=minimap_start_line,endidx do - batch_syntax_type = nil - batch_start = x - batch_width = 0 - - -- per token - for _, type, text in self.doc.highlighter:each_token(idx) do - -- flush prev batch - if not batch_syntax_type then batch_syntax_type = type end - if batch_syntax_type ~= type then - flush_batch(type) - end - - -- per character - for char in common.utf8_chars(text) do - if char == " " or char == "\n" then - flush_batch(type) - batch_start = batch_start + char_spacing - elseif batch_start + batch_width > minimap_cutoff_x then - flush_batch(type) - break - else - batch_width = batch_width + char_spacing - end - - end - end - flush_batch(nil) - line_y = line_y + line_spacing - end - - else -- render lines without syntax highlighting - - local function flush_batch() - if batch_width > 0 then - renderer.draw_rect(batch_start, line_y, batch_width, char_height, color) - end - batch_start = batch_start + batch_width - batch_width = 0 - end - - for idx=1,line_count-1 do - batch_start = x - batch_width = 0 - - for char in common.utf8_chars(self.doc.lines[idx]) do - if char == " " or char == "\n" then - flush_batch() - batch_start = batch_start + char_spacing - elseif batch_start + batch_width > minimap_cutoff_x then - flush_batch() - else - batch_width = batch_width + char_spacing - end - end - flush_batch() - line_y = line_y + line_spacing - end - - end +DocView.draw_scrollbar = function(self) + if not config.minimap_enabled then return prev_draw_scrollbar(self) end + + local x, y, w, h = self:get_scrollbar_rect() + + local highlight = self.hovered_scrollbar or self.dragging_scrollbar + local visual_color = highlight and style.scrollbar2 or style.scrollbar + + local _, cy, _, cy2 = self:get_content_bounds() + local lh = self:get_line_height() + local visible_lines_count = math.max(1, (cy2 - cy) / lh) + local visible_lines_start = math.max(1, math.floor(cy / lh)) + local scroller_height = visible_lines_count * line_spacing + local line_count = #self.doc.lines + + local visible_y = self.position.y + (visible_lines_start - 1) * line_spacing + + -- check if file is too large to fit inside the minimap area + local max_minmap_lines = math.floor(h / line_spacing) + local minimap_start_line = 1 + if is_file_too_large(self) then + + local scroll_pos = (visible_lines_start - 1) / + (line_count - visible_lines_count - 1) + scroll_pos = math.min(1.0, scroll_pos) -- 0..1, procent of visual area scrolled + + local scroll_pos_pixels = scroll_pos * (h - scroller_height) + visible_y = self.position.y + scroll_pos_pixels + + -- offset visible area if user is scrolling past end + local t = (line_count - visible_lines_start) / visible_lines_count + if t <= 1 then visible_y = visible_y + scroller_height * (1.0 - t) end + + minimap_start_line = visible_lines_start - + math.floor(scroll_pos_pixels / line_spacing) + minimap_start_line = math.max(1, math.min(minimap_start_line, + line_count - max_minmap_lines)) + end + + if config.minimap_draw_background then + renderer.draw_rect(x, y, w, h, style.minimap_background or style.background) + end + -- draw visual rect + renderer.draw_rect(x, visible_y, w, scroller_height, visual_color) + + -- time to draw the actual code, setup some local vars that are used in both highlighted and plain renderind. + local line_y = y + + -- when not using syntax highlighted rendering, just use the normal color but dim it 50%. + local color = style.syntax["normal"] + color = {color[1], color[2], color[3], color[4] * 0.5} + + -- we try to "batch" characters so that they can be rendered as just one rectangle instead of one for each. + local batch_width = 0 + local batch_start = x + local minimap_cutoff_x = x + config.minimap_width * SCALE + local batch_syntax_type = nil + local function flush_batch(type) + local old_color = color + color = style.syntax[batch_syntax_type] + if config.minimap_syntax_highlight and color ~= nil then + -- fetch and dim colors + color = {color[1], color[2], color[3], color[4] * 0.5} + else + color = old_color + end + if batch_width > 0 then + renderer.draw_rect(batch_start, line_y, batch_width, char_height, color) + end + batch_syntax_type = type + batch_start = batch_start + batch_width + batch_width = 0 + end + + -- render lines with syntax highlighting + if config.minimap_syntax_highlight then + + -- keep track of the highlight type, since this needs to break batches as well + batch_syntax_type = nil + + -- per line + local endidx = minimap_start_line + max_minmap_lines + endidx = math.min(endidx, line_count) + for idx = minimap_start_line, endidx do + batch_syntax_type = nil + batch_start = x + batch_width = 0 + + -- per token + for _, type, text in self.doc.highlighter:each_token(idx) do + -- flush prev batch + if not batch_syntax_type then batch_syntax_type = type end + if batch_syntax_type ~= type then flush_batch(type) end + + -- per character + for char in common.utf8_chars(text) do + if char == " " or char == "\n" then + flush_batch(type) + batch_start = batch_start + char_spacing + elseif char == " " then + flush_batch(type) + batch_start = batch_start + (char_spacing * config.minimap_tab_width) + elseif batch_start + batch_width > minimap_cutoff_x then + flush_batch(type) + break + else + batch_width = batch_width + char_spacing + end + + end + end + flush_batch(nil) + line_y = line_y + line_spacing + end + + else -- render lines without syntax highlighting + for idx = 1, line_count - 1 do + batch_start = x + batch_width = 0 + + for char in common.utf8_chars(self.doc.lines[idx]) do + if char == " " or char == "\n" then + flush_batch() + batch_start = batch_start + char_spacing + elseif batch_start + batch_width > minimap_cutoff_x then + flush_batch() + else + batch_width = batch_width + char_spacing + end + end + flush_batch() + line_y = line_y + line_spacing + end + + end end command.add(nil, { - ["minimap:toggle-visibility"] = function() - config.minimap_enabled = not config.minimap_enabled - end, - ["minimap:toggle-syntax-highlighting"] = function() - config.minimap_syntax_highlight = not config.minimap_syntax_highlight - end, + ["minimap:toggle-visibility"] = function() + config.minimap_enabled = not config.minimap_enabled + end, + ["minimap:toggle-syntax-highlighting"] = function() + config.minimap_syntax_highlight = not config.minimap_syntax_highlight + end }) -- cgit v1.2.3