syntax highlighting

This commit is contained in:
2024-12-23 19:54:52 +01:00
parent d8a7da82fe
commit 3d0d721896
3 changed files with 203 additions and 119 deletions

View File

@@ -3,8 +3,11 @@ local M = {}
local config = require("gitea.config")
local api = require("gitea.api")
local auth = require("gitea.auth")
local highlights = require("gitea.highlights") -- we still require highlights
-- We'll keep detect_owner_repo() from before
------------------------------------------------------------------------------
-- DETECT OWNER/REPO
------------------------------------------------------------------------------
local function detect_owner_repo()
local cmd = "git config remote.origin.url"
local url = vim.fn.systemlist(cmd)[1]
@@ -29,39 +32,69 @@ local function detect_owner_repo()
return nil, nil
end
--------------------------------------------------------------------------------
--- Data Structures to track what's in the ephemeral buffer
--------------------------------------------------------------------------------
-- We'll store some metadata for each "segment" in the buffer:
-- - The issue's title line range
-- - The issue's body line range
-- - Each existing comment (with start/end line range)
-- - Possibly space for a new comment
--
-- On save (:w), we compare what's changed and do the appropriate API calls.
--------------------------------------------------------------------------------
------------------------------------------------------------------------------
-- BUFFER METADATA
------------------------------------------------------------------------------
local function create_issue_buffer_metadata(issue)
-- track lines for the title, the body, each comment
local meta = {
return {
issue_number = issue.number,
-- will fill in fields once we place them in the buffer
title_start = nil,
title_end = nil,
body_start = nil,
body_end = nil,
comments = {}, -- array of { id, start, end }
new_comment_start = nil, -- where user can type a new comment
title_start = nil,
title_end = nil,
body_start = nil,
body_end = nil,
comments = {}, -- each = { id, start_line, end_line }
new_comment_start = nil,
}
return meta
end
--------------------------------------------------------------------------------
--- RENDER ISSUE INTO BUFFER
--------------------------------------------------------------------------------
------------------------------------------------------------------------------
-- APPLY HIGHLIGHTS
------------------------------------------------------------------------------
local function apply_issue_highlights(bufnr, meta)
-- The first line in the file is line 0 in Luas API, so we subtract 1
-- from meta.* to convert to 0-based indexing.
------------------------------------------------------------------------
-- Lines #0 & #1 hold meta info (“# Gitea Issue #...”, “# STATE: ...”)
------------------------------------------------------------------------
vim.api.nvim_buf_add_highlight(bufnr, 0, "GiteaIssueMeta", 0, 0, -1)
vim.api.nvim_buf_add_highlight(bufnr, 0, "GiteaIssueMeta", 1, 0, -1)
------------------------------------------------------------------------
-- Title
------------------------------------------------------------------------
local title_line_0 = meta.title_start - 1
vim.api.nvim_buf_add_highlight(bufnr, 0, "GiteaIssueTitle", title_line_0, 0, -1)
------------------------------------------------------------------------
-- Body lines
------------------------------------------------------------------------
-- meta.body_start .. meta.body_end (inclusive, 1-based)
for line = meta.body_start, meta.body_end do
local line_0 = line - 1
vim.api.nvim_buf_add_highlight(bufnr, 0, "GiteaCommentBody", line_0, 0, -1)
end
------------------------------------------------------------------------
-- Comment headings & bodies
------------------------------------------------------------------------
for _, c in ipairs(meta.comments) do
-- The heading line has “COMMENT #123 by user”
local heading_line_0 = c.start_line - 1
vim.api.nvim_buf_add_highlight(bufnr, 0, "GiteaCommentHeading", heading_line_0, 0, -1)
-- The actual comment text is lines [heading_line_0+1 .. c.end_line-1]
for text_line = (c.start_line + 1), c.end_line do
local text_line_0 = text_line - 1
vim.api.nvim_buf_add_highlight(bufnr, 0, "GiteaCommentBody", text_line_0, 0, -1)
end
end
end
------------------------------------------------------------------------------
-- RENDER ISSUE INTO BUFFER
------------------------------------------------------------------------------
local function render_issue_into_buf(bufnr, issue, comments)
-- We assume the buffer is empty and ephemeral.
local lines = {}
table.insert(lines, string.format("# Gitea Issue #%d: %s", issue.number, issue.title))
table.insert(lines, "# STATE: " .. (issue.state or "unknown"))
@@ -69,18 +102,13 @@ local function render_issue_into_buf(bufnr, issue, comments)
table.insert(lines, "# On save (:w), changes to the title/body will be updated; changes to comment lines will be updated, and new text at the bottom becomes a new comment.")
table.insert(lines, "")
-- store line index references for metadata
local meta = create_issue_buffer_metadata(issue)
-- place the "title line"
meta.title_start = #lines+1
meta.title_start = #lines + 1
table.insert(lines, issue.title or "")
meta.title_end = #lines
-- a blank line
table.insert(lines, "")
-- place the "body lines"
meta.body_start = #lines+1
meta.body_start = #lines + 1
if issue.body then
local body_lines = vim.split(issue.body, "\n", true)
if #body_lines == 0 then
@@ -95,19 +123,16 @@ local function render_issue_into_buf(bufnr, issue, comments)
end
meta.body_end = #lines
-- spacing
table.insert(lines, "")
table.insert(lines, "# --- Comments ---")
local line_count = #lines
local comment_meta = {}
for _, c in ipairs(comments) do
table.insert(lines, "")
local start_line = #lines+1
-- We'll place a small header: e.g. "Comment (ID=12345) by username"
local author = c.user and c.user.login or c.user and c.user.username or c.user or "?"
local start_line = #lines + 1
local author = c.user and (c.user.login or c.user.username) or "?"
table.insert(lines, string.format("COMMENT #%d by %s", c.id, author))
-- Then the actual comment body lines
local c_body_lines = vim.split(c.body or "", "\n", true)
if #c_body_lines == 0 then
table.insert(lines, "")
@@ -120,101 +145,73 @@ local function render_issue_into_buf(bufnr, issue, comments)
table.insert(comment_meta, {
id = c.id,
start_line = start_line,
end_line = end_line,
end_line = end_line,
})
end
-- place a blank line for user to add a new comment
table.insert(lines, "")
table.insert(lines, "# --- Add a new comment below this line. If you leave it blank, no new comment is created. ---")
local new_comment_start = #lines+1
table.insert(lines, "") -- an empty line to start from
meta.new_comment_start = #lines + 1
table.insert(lines, "")
-- Save comment meta
meta.comments = comment_meta
-- Put all lines into the buffer
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
-- store metadata
local b_meta = {
issue_number = issue.number,
title_start = meta.title_start,
title_end = meta.title_end,
body_start = meta.body_start,
body_end = meta.body_end,
comments = comment_meta,
new_comment_start = new_comment_start,
}
return b_meta
-- Now apply highlight groups
apply_issue_highlights(bufnr, meta)
return meta
end
--------------------------------------------------------------------------------
--- Parse buffer changes and do API updates on save
--------------------------------------------------------------------------------
------------------------------------------------------------------------------
-- ON SAVE (BufWriteCmd)
------------------------------------------------------------------------------
local function on_issue_buf_write(bufnr, metadata, owner, repo)
-- read all buffer lines
local new_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
----------------- Title update check -----------------
local new_title = table.concat(vim.list_slice(new_lines, metadata.title_start, metadata.title_end), "\n")
-- remove trailing newlines just in case
new_title = new_title:gsub("%s+$", "")
-- we do a get_issue call to see the current title
local current_issue_data = api.get_issue(owner, repo, metadata.issue_number)
if not current_issue_data then
vim.notify("[gitea.nvim] Could not re-fetch the issue to verify updates", vim.log.levels.ERROR)
return
end
local changed_title = false
if (current_issue_data.title or "") ~= new_title then
changed_title = true
end
----------------- Body update check ------------------
local changed_title = (current_issue_data.title or "") ~= new_title
local new_body_lines = vim.list_slice(new_lines, metadata.body_start, metadata.body_end)
local new_body = table.concat(new_body_lines, "\n")
local changed_body = false
if (current_issue_data.body or "") ~= new_body then
changed_body = true
end
local changed_body = (current_issue_data.body or "") ~= new_body
----------------- Comments update check -------------
local changed_comments = {}
for _, cmeta in ipairs(metadata.comments) do
local c_lines = vim.list_slice(new_lines, cmeta.start_line, cmeta.end_line)
-- The first line is "COMMENT #<id> by <author>"
-- The rest is the actual body
-- e.g. `COMMENT #112 by alice`
local first_line = c_lines[1] or ""
-- first line => "COMMENT #<id> by <author>"
local c_body = {}
for i=2,#c_lines do
for i = 2, #c_lines do
table.insert(c_body, c_lines[i])
end
local new_comment_body = table.concat(c_body, "\n")
-- We fetch the current comment from the server to check if changed
-- But Gitea's issue comment API does not necessarily let us get a single comment by ID easily.
-- Instead, we do "issue_data, with comments" or store old body in memory.
-- For simplicity, let's store the old body in an extmark or in a table. But let's just do a naive approach:
-- We'll assume if the line is changed, we want to patch it. We have no big reference of old body, so let's do it.
table.insert(changed_comments, {
id = cmeta.id,
id = cmeta.id,
new_body = new_comment_body
})
end
----------------- New comment check ------------------
local new_comment_body_lines = {}
for i=metadata.new_comment_start, #new_lines do
for i = metadata.new_comment_start, #new_lines do
table.insert(new_comment_body_lines, new_lines[i])
end
local new_comment_body = table.concat(new_comment_body_lines, "\n"):gsub("^%s+", ""):gsub("%s+$", "")
local create_new_comment = (#new_comment_body > 0)
-------------- Perform the updates --------------
-- Edit issue if title or body changed
if changed_title or changed_body then
local updated, st = api.edit_issue(owner, repo, metadata.issue_number, {
title = changed_title and new_title or current_issue_data.title,
body = changed_body and new_body or current_issue_data.body,
body = changed_body and new_body or current_issue_data.body,
})
if updated then
vim.notify("[gitea.nvim] Issue updated!")
@@ -223,9 +220,7 @@ local function on_issue_buf_write(bufnr, metadata, owner, repo)
end
end
-- for each changed comment, attempt to patch
-- We do not do a diff, so we always do a "PATCH" if we suspect changes
-- => If the user changed nothing, Gitea might respond 200 anyway, no big deal
-- Edit each comment
for _, cupd in ipairs(changed_comments) do
local updated_comment, st = api.edit_issue_comment(owner, repo, metadata.issue_number, cupd.id, cupd.new_body)
if not updated_comment then
@@ -235,6 +230,7 @@ local function on_issue_buf_write(bufnr, metadata, owner, repo)
end
end
-- Possibly add a new comment
if create_new_comment then
local created, st = api.comment_issue(owner, repo, metadata.issue_number, new_comment_body)
if created then
@@ -245,9 +241,9 @@ local function on_issue_buf_write(bufnr, metadata, owner, repo)
end
end
--------------------------------------------------------------------------------
--- Open a single ephemeral buffer for the entire Issue with comments
--------------------------------------------------------------------------------
------------------------------------------------------------------------------
-- OPEN ISSUE BUFFER
------------------------------------------------------------------------------
local function open_full_issue_buffer(owner, repo, number)
local issue_data, status = api.get_issue(owner, repo, number)
if not issue_data then
@@ -255,9 +251,6 @@ local function open_full_issue_buffer(owner, repo, number)
return
end
-- get all existing comments from the Gitea API
-- for Gitea, we can do: GET /repos/{owner}/{repo}/issues/{index}/comments
-- The plugin's `api` might have a function for that, or we can add it quickly
local all_comments, cstatus = api.get_issue_comments(owner, repo, number)
if not all_comments then
vim.notify(string.format("[gitea.nvim] Error retrieving issue #%d comments (HTTP %s).", number, cstatus or "?"), vim.log.levels.ERROR)
@@ -266,12 +259,18 @@ local function open_full_issue_buffer(owner, repo, number)
local buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_option(buf, "buftype", "acwrite")
vim.api.nvim_buf_set_option(buf, "filetype", "markdown")
-- Use our new "gitea" filetype, so after/syntax/gitea.vim will load
vim.api.nvim_buf_set_option(buf, "filetype", "gitea")
vim.api.nvim_buf_set_name(buf, string.format("gitea_issue_full_%d", number))
-- Load highlight definitions (e.g. GiteaIssueTitle, GiteaCommentBody, etc.)
highlights.setup()
local metadata = render_issue_into_buf(buf, issue_data, all_comments)
-- We create an autocmd on BufWriteCmd to parse changes and do updates
-- Auto command to handle saving
vim.api.nvim_create_autocmd("BufWriteCmd", {
buffer = buf,
callback = function()
@@ -282,9 +281,9 @@ local function open_full_issue_buffer(owner, repo, number)
vim.api.nvim_set_current_buf(buf)
end
--------------------------------------------------------------------------------
--- SUBCOMMANDS
--------------------------------------------------------------------------------
------------------------------------------------------------------------------
-- SUBCOMMANDS
------------------------------------------------------------------------------
local subcommands = {}
subcommands.issue = {
@@ -294,8 +293,7 @@ subcommands.issue = {
vim.notify("[gitea.nvim] Could not detect owner/repo.", vim.log.levels.ERROR)
return
end
-- use the same telescope approach as before:
local has_telescope, tele = pcall(require, "telescope")
local has_telescope, _ = pcall(require, "telescope")
if not has_telescope then
vim.notify("[gitea.nvim] telescope.nvim is not installed", vim.log.levels.ERROR)
return
@@ -333,7 +331,6 @@ subcommands.issue = {
if not selection or not selection.value then
return
end
-- Instead of just editing, we now open the full timeline buffer
open_full_issue_buffer(owner, repo, selection.value.number)
end
map("i", "<CR>", select_issue)
@@ -344,7 +341,6 @@ subcommands.issue = {
end,
show = function(args)
-- Alternative: show a single issue by number
local number = tonumber(args[1])
if not number then
vim.notify("[gitea.nvim] Usage: :Gitea issue show <number>", vim.log.levels.ERROR)
@@ -357,19 +353,15 @@ subcommands.issue = {
end
open_full_issue_buffer(owner, repo, number)
end,
-- The rest of your commands can remain or be removed as needed
-- but presumably we can just use "show" for everything
-- or keep an existing minimal create command, etc.
}
subcommands.pr = {
-- We'll leave your existing PR commands as is or remove for brevity
-- your other PR-related commands, if any
}
--------------------------------------------------------------------------------
--- REGISTER
--------------------------------------------------------------------------------
------------------------------------------------------------------------------
-- REGISTER COMMAND
------------------------------------------------------------------------------
function M.register()
vim.api.nvim_create_user_command("Gitea", function(cmd_opts)
auth.ensure_token()
@@ -433,4 +425,4 @@ function M.register()
})
end
return M
return M

58
lua/gitea/highlights.lua Normal file
View File

@@ -0,0 +1,58 @@
local M = {}
-- A small Dracula-like palette, adapt or extend if needed
local palette = {
bg = "#282A36",
fg = "#F8F8F2",
selection = "#44475A",
comment = "#6272A4",
cyan = "#8BE9FD",
green = "#50FA7B",
orange = "#FFB86C",
pink = "#FF79C6",
purple = "#BD93F9",
red = "#FF5555",
yellow = "#F1FA8C",
}
function M.setup()
-- Title of an issue or PR
vim.api.nvim_set_hl(0, "GiteaIssueTitle", {
fg = palette.pink,
bold = true,
})
-- Meta lines, e.g. status lines or extra info
vim.api.nvim_set_hl(0, "GiteaIssueMeta", {
fg = palette.comment,
italic = true,
})
-- Heading for each comment block
vim.api.nvim_set_hl(0, "GiteaCommentHeading", {
fg = palette.orange,
bold = true,
})
-- The actual comment body text
vim.api.nvim_set_hl(0, "GiteaCommentBody", {
fg = palette.fg,
bg = nil, -- or palette.bg if you want a background
})
-- Something for a user mention
vim.api.nvim_set_hl(0, "GiteaUser", {
fg = palette.cyan,
bold = true,
})
-- If you'd like comment lines in a faint color
vim.api.nvim_set_hl(0, "GiteaInlineComment", {
fg = palette.comment,
italic = true,
})
-- ...Add or tweak any other highlight groups you need...
end
return M