diff --git a/debian/changelog b/debian/changelog index d1829ff..e7553df 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,15 @@ +lite-xl (2.1.8-5) stonking; urgency=medium + + * Vendor gitpanel 0.3.0 (own plugin, lite-xl-gitpanel @ 0731a63): a git + panel integrated into the treeview pane - uncommitted changes with + diff stats, staging, commits and history - toggled from a git-branch + button in the treeview toolbar or the status bar branch indicator. + History is pinned to the bottom half of the panel; clicking a commit + opens its patch with syntax highlighting via the already bundled + language_diff. + + -- Valentin Haudiquet Wed, 17 Sep 2026 01:55:00 +0200 + lite-xl (2.1.8-4) stonking; urgency=medium * Bundle a curated set of community plugins (vendored, pinned in diff --git a/debian/extra/README.source b/debian/extra/README.source index d88cd2c..d3db312 100644 --- a/debian/extra/README.source +++ b/debian/extra/README.source @@ -4,6 +4,11 @@ Vendored from upstream, pinned by commit: plugins/: gitstatus, gitopen, fontconfig, open_ext, select_colorscheme, smoothcaret, nerdicons, language_* (all 104; none overlap the ones bundled in the core data/ directory) +- lite-xl-gitpanel (own plugin, source tree lite-xl-gitpanel) @ 0731a63 (main, 2026-09) + plugins/: gitpanel -- git panel integrated into the treeview pane + (changes, staging, commits, history) with a toolbar toggle button; + uses the bundled language_diff for commit diff highlighting + - lite-xl-lsp https://github.com/lite-xl/lite-xl-lsp @ d1432ae (master, 2026-09) vendored whole as plugins/lsp/ (screenshots removed) - lite-xl-widgets https://github.com/lite-xl/lite-xl-widgets @ dcf1c8c diff --git a/debian/extra/plugins/gitpanel.lua b/debian/extra/plugins/gitpanel.lua new file mode 100644 index 0000000..0d22054 --- /dev/null +++ b/debian/extra/plugins/gitpanel.lua @@ -0,0 +1,678 @@ +-- mod-version:3 +-- version: 0.3.0 +-- Git panel for Lite XL: uncommitted changes, staging, commits, history. +-- Self-contained, needs only the `git` CLI in PATH. +-- Toggle with Ctrl+Shift+G, the git button in the treeview toolbar or the +-- branch name in the status bar. When the treeview plugin is present the +-- panel replaces the folder list in the left pane; otherwise it opens as +-- its own pane. +-- In the panel: click a file to open it, click a commit to view its patch. +-- Keys (panel focused): s stage · u unstage · a stage all · c commit · r refresh + +local core = require "core" +local common = require "core.common" +local config = require "core.config" +local style = require "core.style" +local keymap = require "core.keymap" +local command = require "core.command" +local syntax = require "core.syntax" +local View = require "core.view" +local Doc = require "core.doc" +local StatusView = require "core.statusview" + +config.plugins.gitpanel = common.merge({ + history_length = 40, +}, config.plugins.gitpanel) + + +-------------------------------------------------------------------------------- +-- Async git helpers (run inside core.add_thread coroutines) +-------------------------------------------------------------------------------- + +-- Run a command, reading incrementally so large outputs (git show) cannot +-- deadlock on the pipe buffer. Must be called from a coroutine. +local function exec(args) + local proc = process.start(args) + if not proc then return "" end + local chunks = {} + while proc:running() do + local chunk = proc:read_stdout(65536) + if chunk and #chunk > 0 then + chunks[#chunks + 1] = chunk + else + coroutine.yield(0.05) + end + end + while true do + local chunk = proc:read_stdout(65536) + if chunk and #chunk > 0 then chunks[#chunks + 1] = chunk else break end + end + return table.concat(chunks) +end + +local state = { branch = nil, changes = {}, history = {}, clean = false, loading = false } + +local function parse_numstat(out, map) + for line in (out .. "\n"):gmatch("([^\n]*)\n") do + local ins, del, path = line:match("^(%d+)%s+(%d+)%s+(.+)$") + if path then + path = path:match("^.- ->%s*(.+)$") or path -- renames: keep new path + map[path] = { n = tonumber(ins) or 0, d = tonumber(del) or 0 } + end + end +end + +local function refresh(callback) + if state.loading then return end + state.loading = true + core.add_thread(function() + local branch, changes, history + -- pcall so a failed git run (spawn error, ...) can never leave + -- state.loading stuck and silently kill every later refresh + local ok, err = pcall(function() + branch, changes, history = nil, {}, {} + if system.get_file_info(core.project_dir .. PATHSEP .. ".git") then + local work, staged = {}, {} + parse_numstat(exec { "git", "diff", "--numstat" }, work) + parse_numstat(exec { "git", "diff", "--cached", "--numstat" }, staged) + + local status = exec { "git", "status", "--porcelain=v1", "-b" } + local first = true + for line in (status .. "\n"):gmatch("([^\n]*)\n") do + if first and line:match("^## ") then + branch = line:sub(4):match("^(.-)%.%.%.") or line:sub(4):match("^(%S+)") + first = false + elseif line ~= "" then + local xy, path = line:match("^(..) (.+)$") + if xy then + path = path:match("^(.-)%s+->%s+(.+)$") or path + local x, y = xy:sub(1, 1), xy:sub(2, 2) + local nums = (x ~= " ") and staged[path] or work[path] + changes[#changes + 1] = { + xy = xy, + st = xy == "??" and "??" or (x ~= " " and x or y), + path = path, + staged = x ~= " " and x ~= "?", + untracked = xy == "??", + plus = nums and nums.n or nil, + minus = nums and nums.d or nil, + } + end + end + end + + local log = exec { "git", "log", "-n", tostring(config.plugins.gitpanel.history_length), + "--pretty=format:%h %s" } + for line in (log .. "\n"):gmatch("([^\n]*)\n") do + local hash, subject = line:match("^(%S+) (.*)$") + if hash then history[#history + 1] = { hash = hash, subject = subject } end + end + end + end) + state.loading = false + if ok then + state.branch, state.changes, state.history = branch, changes, history + state.clean = branch ~= nil and #changes == 0 + else + core.error("git-panel: refresh failed: %s", tostring(err)) + end + if callback and ok then callback() end + end) +end + + +-------------------------------------------------------------------------------- +-- Panel view +-------------------------------------------------------------------------------- + +local function row_height() + return style.font:get_height() + common.round(4 * SCALE) +end + +local function status_color(st) + if st == "M" or st == "T" or st == "R" or st == "C" then + return style.gitstatus_modification or style.warn + end + if st == "A" or st == "??" then return style.gitstatus_addition or style.good end + if st == "D" or st == "U" then return style.gitstatus_deletion or style.error end + return style.text +end + +local GitPanelView = View:extend() + +local show_commit -- forward declaration: called from on_mouse_pressed below + +-- Set once the treeview plugin is available (it loads after us); when nil +-- the panel falls back to opening as its own split pane. +local treeview = nil + +function GitPanelView:new() + GitPanelView.super.new(self) + self.hover = nil -- { section = "changes"|"history", idx } + self.mouse = { x = 0, y = 0 } + self.changes_offset = 0 + self.history_offset = 0 + self.changes_rows = {} + self.history_rows = {} + self.changes_visible = 1 + self.history_visible = 1 + self.mid_y = 0 +end + +function GitPanelView:get_name() + return "Git" +end + +-- While embedded in the treeview's pane, the pane width follows our size.x +-- (the node is locked along x). Animate toward the treeview's target so +-- divider drags resize us, and the folder view inherits the final width. +function GitPanelView:set_target_size(axis, value) + if axis == "x" and treeview then + treeview:set_target_size(axis, value) + return true + end +end + +function GitPanelView:update() + if treeview then + self:move_towards(self.size, "x", treeview.target_size) + end + GitPanelView.super.update(self) +end + +-- The panel is split in two independently scrolling sections: the changes list +-- on the top half and the history pinned to the bottom half. +function GitPanelView:build_changes_rows() + local rows = {} + for _, e in ipairs(state.changes) do rows[#rows + 1] = { type = "file", entry = e } end + if #rows == 0 then + rows[#rows + 1] = { + type = "empty", + label = state.loading and "refreshing..." + or (state.branch and "working tree clean" or "not a git repository"), + } + end + return rows +end + +function GitPanelView:build_history_rows() + local rows = {} + for _, c in ipairs(state.history) do rows[#rows + 1] = { type = "commit", entry = c } end + if #rows == 0 then + rows[#rows + 1] = { + type = "empty", + label = state.loading and "refreshing..." or "no commits yet", + } + end + return rows +end + +local function draw_row_hover(x, y, w, rh, hovered) + if hovered then + local c = { table.unpack(style.treeview_hover_bg or style.line_highlight) } + c[4] = 120 + renderer.draw_rect(x, y, w, rh, c) + end +end + +local function draw_file_row(x, y, w, rh, e, pad) + common.draw_text(style.font, status_color(e.st), e.st, nil, x + pad, y, 0, rh) + local tx = x + pad + style.font:get_width(e.st) + common.round(6 * SCALE) + common.draw_text(style.font, style.treeview_text or style.text, e.path, nil, tx, y, 0, rh) + if e.plus or e.minus then + local label = string.format("+%d -%d", e.plus or 0, e.minus or 0) + common.draw_text(style.font, style.dim, label, nil, + x + w - style.font:get_width(label) - pad, y, 0, rh) + end +end + +local function draw_commit_row(x, y, rh, e, pad) + common.draw_text(style.font, style.dim, e.hash, nil, x + pad, y, 0, rh) + local tx = x + pad + style.font:get_width("0000000") + common.round(6 * SCALE) + common.draw_text(style.font, style.treeview_text or style.text, e.subject, nil, tx, y, 0, rh) +end + +function GitPanelView:draw() + self:draw_background(style.background2) + local x, y = self.position.x, self.position.y + local w, h = self.size.x, self.size.y + local rh = row_height() + local pad = common.round(5 * SCALE) + local font_h = style.font:get_height() + + -- history is pinned to the bottom half; snap the divider to a row boundary + local footer_h = font_h + common.round(4 * SCALE) + local half_rows = math.max(1, math.floor((h - footer_h) / 2 / rh)) + local mid = y + half_rows * rh + self.mid_y = mid + + self.changes_rows = self:build_changes_rows() + self.history_rows = self:build_history_rows() + self.changes_visible = math.max(1, half_rows - 2) -- branch + section headers + self.history_visible = math.max(1, half_rows - 1) -- section header + self.changes_offset = common.clamp(self.changes_offset, + 0, math.max(0, #self.changes_rows - self.changes_visible)) + self.history_offset = common.clamp(self.history_offset, + 0, math.max(0, #self.history_rows - self.history_visible)) + + if state.branch then + common.draw_text(style.font, style.accent, "branch " .. state.branch, nil, x + pad, y, 0, rh) + else + common.draw_text(style.font, style.dim, + state.loading and "loading..." or "not a git repository", nil, x + pad, y, 0, rh) + end + + common.draw_text(style.font, style.accent, + string.format("changes (%d)", #state.changes), nil, x + pad, y + rh, 0, rh) + local cy = y + 2 * rh + core.push_clip_rect(x, cy, w, mid - cy) + for i = self.changes_offset + 1, + math.min(#self.changes_rows, self.changes_offset + self.changes_visible) do + local row = self.changes_rows[i] + local ry = cy + (i - 1 - self.changes_offset) * rh + draw_row_hover(x, ry, w, rh, + self.hover and self.hover.section == "changes" and self.hover.idx == i) + if row.type == "file" then + draw_file_row(x, ry, w, rh, row.entry, pad) + else + common.draw_text(style.font, style.dim, row.label, nil, x + pad, ry, 0, rh) + end + end + core.pop_clip_rect() + + renderer.draw_rect(x, mid, w, SCALE, style.divider or style.dim) + + common.draw_text(style.font, style.accent, + string.format("history (%d)", #state.history), nil, x + pad, mid, 0, rh) + local hy = mid + rh + core.push_clip_rect(x, hy, w, y + h - footer_h - hy) + for i = self.history_offset + 1, + math.min(#self.history_rows, self.history_offset + self.history_visible) do + local row = self.history_rows[i] + local ry = hy + (i - 1 - self.history_offset) * rh + draw_row_hover(x, ry, w, rh, + self.hover and self.hover.section == "history" and self.hover.idx == i) + if row.type == "commit" then + draw_commit_row(x, ry, rh, row.entry, pad) + else + common.draw_text(style.font, style.dim, row.label, nil, x + pad, ry, 0, rh) + end + end + core.pop_clip_rect() + + local hint = "s stage - u unstage - a all - c commit - r refresh" + common.draw_text(style.font, style.dim, hint, nil, x + pad, + y + h - font_h - common.round(2 * SCALE), 0, font_h) +end + +function GitPanelView:on_mouse_moved(px, py, ...) + GitPanelView.super.on_mouse_moved(self, px, py, ...) + self.mouse.x, self.mouse.y = px, py + self.hover = nil + local rh = row_height() + local top_start = self.position.y + 2 * rh + local hist_start = self.mid_y + rh + if py >= top_start and py < self.mid_y then + local idx = math.floor((py - top_start) / rh) + self.changes_offset + 1 + if self.changes_rows[idx] then self.hover = { section = "changes", idx = idx } end + elseif py >= hist_start then + local idx = math.floor((py - hist_start) / rh) + self.history_offset + 1 + if self.history_rows[idx] then self.hover = { section = "history", idx = idx } end + end +end + +function GitPanelView:on_mouse_pressed(button, px, py_, clicks) + GitPanelView.super.on_mouse_pressed(self, button, px, py_, clicks) + local hov = self.hover + if hov then + local row = hov.section == "changes" + and self.changes_rows[hov.idx] or self.history_rows[hov.idx] + if row and row.type == "file" and row.entry.st ~= "D" then + -- deleted files can't be opened + core.root_view:open_doc(core.open_doc(core.project_dir .. PATHSEP .. row.entry.path)) + elseif row and row.type == "commit" then + show_commit(row.entry.hash, row.entry.subject) + end + end + return true +end + +function GitPanelView:on_mouse_left() + GitPanelView.super.on_mouse_left(self) + self.hover = nil +end + +function GitPanelView:on_mouse_wheel(dy) + GitPanelView.super.on_mouse_wheel(self, dy) + local in_history = self.mouse.y >= self.mid_y + local rows_n = in_history and #self.history_rows or #self.changes_rows + local visible = in_history and self.history_visible or self.changes_visible + local offset = in_history and self.history_offset or self.changes_offset + offset = common.clamp(offset - dy * 3, 0, math.max(0, rows_n - visible)) + if in_history then self.history_offset = offset else self.changes_offset = offset end +end + + +-------------------------------------------------------------------------------- +-- Actions +-------------------------------------------------------------------------------- + +local panel = nil -- the open GitPanelView instance, if any + +local function open_scratch(name, text, syn) + local doc = Doc() + doc:reset() + local lines = {} + for line in (text .. "\n"):gmatch("([^\n]*)\n") do lines[#lines + 1] = line .. "\n" end + if #lines == 0 then lines[1] = "\n" end + doc.lines = lines + doc:set_filename(name, nil) + doc:reset_syntax() + if syn then doc.syntax = syn end -- else it stays whatever reset_syntax picked + core.root_view:open_doc(doc) +end + +-- Commit diffs are highlighted with the official language_diff plugin when it +-- is installed (it registers its syntax under the name "Diff"); otherwise this +-- fallback covers `git show` output with the same colors. +style.syntax.diff_add = style.syntax.diff_add or { common.color "#72b886" } +style.syntax.diff_del = style.syntax.diff_del or { common.color "#f36161" } + +local diff_syntax_fallback = syntax.add({ + name = "Git Diff (gitpanel)", + patterns = { + { pattern = "^commit %x+", type = "function" }, + { pattern = "^[%a%-]+: ", type = "keyword" }, + { pattern = "^diff %-%-git .*", type = "function" }, + { pattern = "^index .*", type = "comment" }, + { pattern = "^new .*", type = "comment" }, + { pattern = "^deleted .*", type = "comment" }, + { pattern = "^similarity index .*", type = "comment" }, + { pattern = "^rename .*", type = "comment" }, + { pattern = "^@@ .-@@", type = "number" }, + { pattern = "^%-%-%- .*", type = "keyword" }, + { pattern = "^%+%+%+ .*", type = "keyword" }, + { pattern = "^%-.*", type = "diff_del" }, + { pattern = "^%+.*", type = "diff_add" }, + { pattern = "^ %d+ files? changed.*", type = "function" }, + }, + symbols = {}, +}) + +local function get_diff_syntax() + for _, s in ipairs(syntax.items) do + if s.name == "Diff" then return s end + end + return diff_syntax_fallback +end + +function show_commit(hash, subject) + core.add_thread(function() + open_scratch("[git] " .. hash .. " " .. (subject or ""), + exec { "git", "show", "--no-color", hash }, get_diff_syntax()) + end) +end +local function hovered_file_entry() + if not (panel and panel.hover and panel.hover.section == "changes") then return nil end + local row = panel.changes_rows[panel.hover.idx] + return row and row.type == "file" and row.entry or nil +end + +local function run_on_entry(args_for) + local e = hovered_file_entry() + if not e or e.untracked and args_for == "unstage" then return end + local args + if args_for == "stage" then + args = { "git", "add", "--", e.path } + else + args = { "git", "reset", "-q", "HEAD", "--", e.path } + end + core.add_thread(function() + exec(args) + refresh() + end) +end + +local function panel_active() + return panel ~= nil and core.active_view == panel +end + + +-------------------------------------------------------------------------------- +-- Treeview integration: the panel replaces the folder list in the left pane, +-- toggled from the toolbar's git button. The treeview plugin loads after us, +-- so require it lazily. The treeview's leaf node is locked (no tab bar is +-- drawn there), which makes it a clean container to swap views in. +-------------------------------------------------------------------------------- + +local function treeview_ready() + if treeview then return true end + if config.plugins.treeview == false then return false end + local ok, tv = pcall(require, "plugins.treeview") + if ok and tv and tv.node then + treeview = tv + return true + end + return false +end + +local function treeview_leaf() + return treeview.node:get_node_for_view(treeview) +end + +local function pane_active() + if not (treeview and panel) then return false end + local leaf = treeview_leaf() + return leaf ~= nil and leaf.active_view == panel +end + +local function pane_attach() + if not panel then panel = GitPanelView() end + local leaf = treeview_leaf() + if not leaf:get_view_idx(panel) then + panel.size.x = treeview.size.x -- else the locked pane would collapse on us + local locked = leaf.locked + leaf.locked = nil -- add_view() refuses locked nodes + leaf:add_view(panel) -- makes the panel the node's active view and focuses it + leaf.locked = locked + else + leaf:set_active_view(panel) + end + refresh() +end + +local function pane_detach() + local leaf = treeview_leaf() + local focused = core.active_view == panel + leaf.active_view = treeview -- route the pane back without stealing focus + leaf:remove_view(core.root_view.root_node, panel) + panel:on_mouse_left() + if focused then core.set_active_view(treeview) end +end + +local function open_panel() + if panel and core.root_view:get_node_for_view(panel) then + refresh() + core.set_active_view(panel) + return + end + panel = GitPanelView() + refresh() + local node = core.root_view:get_active_node() + node:split("left", panel) + core.set_active_view(panel) +end + +local function close_panel() + if not panel then return end + local node = core.root_view:get_node_for_view(panel) + if node then node:close_view(core.root_view.node, panel) end + panel = nil +end + +local function toggle_panel() + if treeview_ready() then + if pane_active() then pane_detach() else pane_attach() end + else + if panel_active() then close_panel() else open_panel() end + end +end + +command.add(nil, { + ["git-panel:toggle"] = function() toggle_panel() end, +}) + +command.add(panel_active, { + ["git-panel:stage"] = function() + if hovered_file_entry() then run_on_entry("stage") end + end, + ["git-panel:unstage"] = function() + if hovered_file_entry() then run_on_entry("unstage") end + end, + ["git-panel:stage-all"] = function() + core.add_thread(function() + exec { "git", "add", "-A" } + refresh() + end) + end, + ["git-panel:refresh"] = function() refresh() end, + ["git-panel:commit"] = function() + local staged = 0 + for _, e in ipairs(state.changes) do if e.staged then staged = staged + 1 end end + if staged == 0 then + core.error("git-panel: nothing staged, press s on changed files first") + return + end + core.command_view:enter("Commit message", function(text) + text = text:match("^%s*(.-)%s*$") + if text == "" then + core.error("git-panel: empty commit message") + return + end + core.add_thread(function() + exec { "git", "commit", "-m", text } + refresh(function() + local left = 0 + for _, e in ipairs(state.changes) do if e.staged then left = left + 1 end end + if left == 0 then + core.log("git-panel: committed: " .. text) + else + core.error("git-panel: commit failed, staged files remain") + end + end) + end) + end) + end, +}) + +keymap.add { + ["ctrl+shift+g"] = "git-panel:toggle", +} + +keymap.add { + ["s"] = "git-panel:stage", + ["u"] = "git-panel:unstage", + ["a"] = "git-panel:stage-all", + ["c"] = "git-panel:commit", + ["r"] = "git-panel:refresh", +} + + +-------------------------------------------------------------------------------- +-- Toolbar button: a git-branch icon next to the treeview toolbar buttons, +-- highlighted while the panel replaces the folder list. treeview loads after +-- us (plugins load alphabetically), so wait for it in a thread. +-------------------------------------------------------------------------------- + +-- VS Code-style git-branch icon (vscode-codicons, MIT), rasterized offline to +-- opaque rect runs on a 34x34 grid; soft = partial-coverage edge cells +local GIT_ICON_GRID = 34 +local GIT_ICON = { + {8,0,3,1},{6,1,7,1},{5,2,3,1},{11,2,3,1},{5,3,2,1},{12,3,2,1},{5,4,1,1}, + {13,4,2,3},{4,5,2,1},{5,6,1,1},{5,7,2,1},{12,7,2,1},{21,7,6,1},{6,8,2,1}, + {11,8,3,1},{20,8,3,1},{26,8,2,1},{7,9,6,1},{20,9,2,1},{27,9,2,1},{9,10,2,10}, + {19,10,2,3},{28,10,1,1},{28,11,2,1},{28,12,1,1},{20,13,2,1},{27,13,2,1}, + {20,14,3,1},{26,14,3,1},{21,15,7,1},{23,16,3,1},{23,17,2,1},{22,18,3,1}, + {12,19,12,1},{9,20,14,1},{9,21,3,1},{9,22,2,2},{7,24,6,1},{6,25,2,1}, + {11,25,3,1},{5,26,2,1},{12,26,2,1},{5,27,1,1},{13,27,2,3},{4,28,2,1}, + {5,29,1,1},{5,30,2,1},{12,30,2,1},{5,31,3,1},{11,31,3,1},{6,32,7,1},{8,33,3,1} +} +local GIT_ICON_SOFT = { + {7,0,5,1},{5,1,9,1},{5,2,4,1},{10,2,4,1},{4,3,3,4},{12,3,3,5},{22,6,5,1}, + {4,7,4,1},{21,7,7,1},{5,8,9,1},{20,8,9,1},{6,9,7,1},{19,9,3,5},{26,9,4,1}, + {7,10,5,1},{27,10,3,4},{8,11,3,8},{20,14,4,1},{25,14,4,1},{20,15,9,1}, + {22,16,5,1},{23,17,3,1},{21,18,4,1},{8,19,16,1},{8,20,15,1},{8,21,13,1}, + {8,22,4,1},{7,23,5,1},{6,24,7,1},{5,25,9,1},{4,26,4,1},{12,26,3,5}, + {4,27,3,4},{5,31,4,1},{10,31,4,1},{5,32,9,1},{7,33,5,1} +} + +local function draw_git_icon(x, y, h, color) + local s = h / GIT_ICON_GRID + local soft = { table.unpack(color) } + soft[4] = (soft[4] or 255) * 0.5 + local eps = s / 6 -- hide seams between adjacent runs + for _, r in ipairs(GIT_ICON_SOFT) do + renderer.draw_rect(x + r[1] * s, y + r[2] * s, r[3] * s + eps, r[4] * s + eps, soft) + end + for _, r in ipairs(GIT_ICON) do + renderer.draw_rect(x + r[1] * s, y + r[2] * s, r[3] * s + eps, r[4] * s + eps, color) + end +end + +core.add_thread(function() + for _ = 1, 20 do + if treeview_ready() then break end + coroutine.yield(1) + end + if not treeview then return end + local toolbar = treeview.toolbar + if not toolbar then return end -- toolbarview disabled: keybinding still works + -- blank symbol: the icon itself is painted by hand below + local git_button = { symbol = " ", command = "git-panel:toggle" } + table.insert(toolbar.toolbar_commands, git_button) + -- widen the pane if the default is too narrow for the extra button + treeview:set_target_size("x", math.max(treeview.target_size or 0, toolbar:get_min_width())) + -- dim by default, lit on hover, accent while the panel is showing + local toolbar_draw = toolbar.draw + function toolbar.draw(self) + toolbar_draw(self) + for item, x, y, w, h in self:each_item() do + if item == git_button then + local color = pane_active() and style.accent + or (self.hovered_item == item and style.text or style.dim) + draw_git_icon(x + (w - h) / 2, y, h, color) + end + end + end +end) + + +-------------------------------------------------------------------------------- +-- Status bar "git button": make gitstatus's branch item open the panel. +-- gitstatus loads after us, so poll until its item exists. +-------------------------------------------------------------------------------- + +core.add_thread(function() + for _ = 1, 20 do + local ok, item = pcall(core.status_view.get_item, core.status_view, "status:git") + if ok and item then + item.command = "git-panel:toggle" + item.tooltip = "git: click to open panel" + return + end + coroutine.yield(1) + end + -- gitstatus absent: add our own fallback branch button + core.status_view:add_item({ + name = "git-panel:branch", + alignment = StatusView.Item.RIGHT, + position = -2, + get_item = function() + if not state.branch then return {} end + return { style.accent, "branch: ", style.text, state.branch } + end, + on_click = function() command.perform("git-panel:toggle") end, + tooltip = "git: click to open panel", + separator = core.status_view.separator2, + }) +end) diff --git a/debian/install b/debian/install index 58ae498..2b36999 100644 --- a/debian/install +++ b/debian/install @@ -6,6 +6,7 @@ debian/extra/libraries/font_symbols_nerdfont_mono_regular.lua usr/share/lite-xl/ debian/extra/fonts/SymbolsNerdFontMono-Regular.ttf usr/share/lite-xl/libraries/font_symbols_nerdfont_mono_regular/ debian/extra/plugins/fontconfig.lua usr/share/lite-xl/plugins/ debian/extra/plugins/gitopen.lua usr/share/lite-xl/plugins/ +debian/extra/plugins/gitpanel.lua usr/share/lite-xl/plugins/ debian/extra/plugins/gitstatus.lua usr/share/lite-xl/plugins/ debian/extra/plugins/language_R.lua usr/share/lite-xl/plugins/ debian/extra/plugins/language_angelscript.lua usr/share/lite-xl/plugins/