Skip to content

Commit 1bb656b

Browse files
committed
feat(nvim): handle prittier ignoring alerts/callouts
1 parent fb8f417 commit 1bb656b

File tree

2 files changed

+167
-1
lines changed

2 files changed

+167
-1
lines changed

home/private_dot_config/nvim/lua/plugins/lsp.lua

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ return {
2727
hcl = { "terragrunt_hclfmt" },
2828
json = { "prettierd", "prettier", stop_after_first = true },
2929
justfile = { "just" },
30-
markdown = { "prettierd", "markdownlint", "markdown-toc" },
30+
markdown = { "protect_gh_alerts", "prettierd", "markdownlint", "markdown-toc" },
3131
["markdown.mdx"] = { "prettierd", "prettier", stop_after_first = true },
3232
lua = { "stylua" },
3333
packer = { "packer_fmt" },
@@ -41,6 +41,19 @@ return {
4141
zsh = { "beautysh" },
4242
},
4343
formatters = {
44+
-- Protect GitHub-style alert blocks from Prettier reformatting
45+
-- https://github.com/prettier/prettier/issues/15479
46+
protect_gh_alerts = {
47+
meta = {
48+
url = "https://github.com/prettier/prettier/issues/15479",
49+
description = "Wrap GitHub-style alert blocks in prettier-ignore comments",
50+
},
51+
command = "lua",
52+
stdin = true,
53+
args = function()
54+
return { vim.fn.stdpath("config") .. "/lua/util/protect_gh_alerts.lua" }
55+
end,
56+
},
4457
beautysh = {
4558
prepend_args = { "--indent-size", "2" },
4659
},
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#!/usr/bin/env lua
2+
-- protect_gh_alerts.lua
3+
--
4+
-- Protect GitHub-style alert blocks from Prettier reformatting by wrapping
5+
-- them in <!-- prettier-ignore-start --> / <!-- prettier-ignore-end --> comments.
6+
--
7+
-- This script is designed to be used as a stdin/stdout formatter with conform.nvim.
8+
--
9+
-- GitHub Alert Syntax:
10+
-- > [!NOTE]
11+
-- > Content here
12+
--
13+
-- Supported alert types: NOTE, TIP, IMPORTANT, WARNING, CAUTION
14+
--
15+
-- Reference: https://github.com/prettier/prettier/issues/15479
16+
17+
-- Configuration
18+
local MAX_LINES = 10000
19+
local ALERT_TYPES = { "NOTE", "TIP", "IMPORTANT", "WARNING", "CAUTION" }
20+
21+
-- Read all input
22+
local content = io.read("*all")
23+
if not content or content == "" then
24+
return
25+
end
26+
27+
local lines = {}
28+
for line in content:gmatch("([^\n]*)\n?") do
29+
table.insert(lines, line)
30+
end
31+
32+
-- Remove trailing empty string from gmatch if present
33+
if #lines > 0 and lines[#lines] == "" then
34+
table.remove(lines)
35+
end
36+
37+
-- Performance check
38+
if #lines > MAX_LINES then
39+
io.stderr:write(
40+
string.format("[protect_gh_alerts] Skipping large file (%d lines > %d threshold)\n", #lines, MAX_LINES)
41+
)
42+
io.write(content)
43+
return
44+
end
45+
46+
-- Helper: check if line is alert start
47+
local function is_alert_start(line)
48+
for _, alert_type in ipairs(ALERT_TYPES) do
49+
if line:match("^%s*>%s*%[!" .. alert_type .. "%]") then
50+
return true, alert_type
51+
end
52+
end
53+
return false, nil
54+
end
55+
56+
-- Helper: check if already protected (look back up to 2 lines for prettier-ignore-start or prettier-ignore)
57+
local function is_protected(output)
58+
local check_count = math.min(2, #output)
59+
for offset = 0, check_count - 1 do
60+
local idx = #output - offset
61+
if idx > 0 then
62+
local prev_line = output[idx]
63+
-- Match both prettier-ignore-start and prettier-ignore (for backwards compatibility)
64+
if
65+
prev_line:match("prettier%-ignore%-start")
66+
or prev_line:match("prettier%-ignore[^%-]")
67+
or prev_line:match("prettier%-ignore$")
68+
then
69+
return true
70+
end
71+
-- Stop at non-empty, non-whitespace line that isn't a blank line
72+
if prev_line:match("%S") and not prev_line:match("^%s*$") then
73+
break
74+
end
75+
end
76+
end
77+
return false
78+
end
79+
80+
-- Process lines
81+
local output = {}
82+
local i = 1
83+
local protected_count = 0
84+
85+
while i <= #lines do
86+
local line = lines[i]
87+
local is_alert, alert_type = is_alert_start(line)
88+
89+
if is_alert then
90+
-- Check if already protected
91+
if is_protected(output) then
92+
-- Already protected, just copy lines
93+
table.insert(output, line)
94+
i = i + 1
95+
96+
-- Copy remaining alert lines
97+
while i <= #lines and lines[i]:match("^%s*>") do
98+
table.insert(output, lines[i])
99+
i = i + 1
100+
end
101+
else
102+
-- Protect this alert block
103+
local indent = line:match("^(%s*)") or ""
104+
105+
-- Insert ignore-start comment
106+
table.insert(output, indent .. "<!-- prettier-ignore-start -->")
107+
108+
-- Copy alert start
109+
table.insert(output, line)
110+
local alert_start_line = i
111+
i = i + 1
112+
113+
-- Copy all consecutive blockquote lines (including empty >)
114+
local last_alert_line = alert_start_line
115+
while i <= #lines and lines[i]:match("^%s*>") do
116+
table.insert(output, lines[i])
117+
last_alert_line = i
118+
i = i + 1
119+
end
120+
121+
-- Insert end comment after the alert block
122+
table.insert(output, indent .. "<!-- prettier-ignore-end -->")
123+
124+
-- Log protection
125+
protected_count = protected_count + 1
126+
io.stderr:write(
127+
string.format(
128+
"[protect_gh_alerts] Protected %s alert at line %d-%d\n",
129+
alert_type,
130+
alert_start_line,
131+
last_alert_line
132+
)
133+
)
134+
end
135+
else
136+
-- Regular line, just copy
137+
table.insert(output, line)
138+
i = i + 1
139+
end
140+
end
141+
142+
-- Write output
143+
io.write(table.concat(output, "\n"))
144+
145+
-- Ensure file ends with newline
146+
if #output > 0 then
147+
io.write("\n")
148+
end
149+
150+
-- Summary log
151+
if protected_count > 0 then
152+
io.stderr:write(string.format("[protect_gh_alerts] Protected %d alert block(s)\n", protected_count))
153+
end

0 commit comments

Comments
 (0)