Skip to content

Commit

Permalink
fix(injected): better handling of formatting indented code blocks (#606)
Browse files Browse the repository at this point in the history
  • Loading branch information
stevearc committed Jan 3, 2025
1 parent 0fbb850 commit 28f7fc7
Show file tree
Hide file tree
Showing 12 changed files with 140 additions and 25 deletions.
77 changes: 70 additions & 7 deletions lua/conform/formatters/injected.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,21 @@ local function in_range(range, start_lnum, end_lnum)
return not range or (start_lnum <= range["end"][1] and range["start"][1] <= end_lnum)
end

---@param language? string
local function prefix_pattern(language)
-- Handle markdown code blocks that are inside blockquotes
-- > ```lua
-- > local x = 1
-- > ```
return language == "markdown" and "^>?%s*" or "^%s*"
end

---@param lines string[]
---@param language? string The language of the buffer
---@return string?
local function get_indent(lines, language)
local indent = nil
-- Handle markdown code blocks that are inside blockquotes
-- > ```lua
-- > local x = 1
-- > ```
local pattern = language == "markdown" and "^>?%s*" or "^%s*"
local pattern = prefix_pattern(language)
for _, line in ipairs(lines) do
if line ~= "" then
local whitespace = line:match(pattern)
Expand Down Expand Up @@ -93,7 +98,53 @@ local function restore_surrounding(lines, surrounding)
end
end

---@class LangRange
---Merge adjacent ranges that have the same language and share a prefix
---@param regions LangRange[]
---@param bufnr integer
---@param buf_lang? string
---@return LangRange[]
local function merge_ranges_with_prefix(regions, bufnr, buf_lang)
local ret = {}

local last_range = nil
local accum = {}

local function append_accum()
if #accum == 0 then
return
end
local lines = vim.api.nvim_buf_get_lines(bufnr, accum[1][2] - 1, accum[#accum][4], true)
local prefix = get_indent(lines, buf_lang)
if prefix then
local new_range = {
accum[1][1],
accum[1][2],
accum[1][3],
accum[#accum][4],
accum[#accum][5],
}
table.insert(ret, new_range)
else
vim.list_extend(ret, accum)
end
accum = {}
end

for _, range in ipairs(regions) do
if not last_range or range[1] ~= last_range[1] or range[2] ~= last_range[4] then
-- This is a new region entirely; new language, or not contiguous
append_accum()
accum = {}
end
table.insert(accum, range)
last_range = range
end
append_accum()

return ret
end

---@class (exact) LangRange
---@field [1] string language
---@field [2] integer start lnum
---@field [3] integer start col
Expand Down Expand Up @@ -212,6 +263,8 @@ return {
end
end

regions = merge_ranges_with_prefix(regions, ctx.buf, buf_lang)

if ctx.range then
regions = vim.tbl_filter(function(region)
return in_range(ctx.range, region[2], region[4])
Expand Down Expand Up @@ -299,7 +352,17 @@ return {
local input_lines = util.tbl_slice(lines, start_lnum, end_lnum)
input_lines[#input_lines] = input_lines[#input_lines]:sub(1, end_col)
if start_col > 0 then
input_lines[1] = input_lines[1]:sub(start_col + 1)
local prefix = input_lines[1]:sub(0, start_col)
if prefix:match(prefix_pattern(buf_lang)) ~= "" then
-- The first line in the range doesn't start at col 0, but the text on that line before
-- it is just indentation nothing semantic.
-- Update the range to include the indentation so that remove_surrounding() below can
-- consider it as part of the indentation for the entire block.
start_col = 0
region[3] = 0
else
input_lines[1] = input_lines[1]:sub(start_col + 1)
end
end
local ft_formatters = assert(get_formatters(lang))
---@type string[]
Expand Down
1 change: 1 addition & 0 deletions tests/injected/block_quote.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ text

> ```lua
> local foo = 'bar'
> local bar = 2
> ```
3 changes: 2 additions & 1 deletion tests/injected/block_quote.md.formatted
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
text

> ```lua
> |local foo = 'bar'|
> >local foo = 'bar'
> local bar = 2<
> ```
1 change: 1 addition & 0 deletions tests/injected/combined_injections.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ local foo = 'bar'

```lua
local foo = 'bar'
local bar = 3
```
5 changes: 3 additions & 2 deletions tests/injected/combined_injections.md.formatted
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ text
<!-- comment -->

```lua
|local foo = 'bar'|
>local foo = 'bar'<
```


<!-- comment -->

```lua
|local foo = 'bar'|
>local foo = 'bar'
local bar = 3<
```
14 changes: 14 additions & 0 deletions tests/injected/inline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,17 @@ bar.innerHTML = `
baz.innerHTML = `
<div> world </div>
`;

baz.innerHTML = `<div>
world
</div>`;

baz.innerHTML = `<div>
world
</div>
`;

baz.innerHTML = `<div>
world
</div>
`;
20 changes: 17 additions & 3 deletions tests/injected/inline.ts.formatted
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
foo.innerHTML = `|<div> hello </div>|`;
foo.innerHTML = `><div> hello </div><`;

bar.innerHTML = `
|<div> world </div>|
><div> world </div><
`;

baz.innerHTML = `
|<div> world </div>|
><div> world </div><
`;

baz.innerHTML = `><div>
world
</div><`;

baz.innerHTML = `><div>
world
</div><
`;

baz.innerHTML = `><div>
world
</div><
`;
8 changes: 8 additions & 0 deletions tests/injected/list_item.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
1. item

```lua
local function foo()

print("hello world")
end
```
8 changes: 8 additions & 0 deletions tests/injected/list_item.md.formatted
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
1. item

```lua
>local function foo()

print("hello world")
end<
```
1 change: 1 addition & 0 deletions tests/injected/simple.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ text

```lua
local foo = "bar"
local bar = 2
```
3 changes: 2 additions & 1 deletion tests/injected/simple.md.formatted
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
text

```lua
|local foo = "bar"|
>local foo = "bar"
local bar = 2<
```
24 changes: 13 additions & 11 deletions tests/injected_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,22 @@ describe("injected formatter", function()
lua = { "test_mark" },
html = { "test_mark" },
}
-- A test formatter that bookends lines with "|" so we can check what was passed in
-- A test formatter that bookends lines with "><" so we can check what was passed in
conform.formatters.test_mark = {
format = function(self, ctx, lines, callback)
local ret = {}
for i, line in ipairs(lines) do
if i == 1 and line == "" then
-- Simulate formatters removing starting newline
elseif i == #lines and line == "" then
-- Simulate formatters removing trailing newline
else
table.insert(ret, "|" .. line .. "|")
end
lines = vim.deepcopy(lines)
-- Simulate formatters removing starting newline
while #lines > 0 and lines[1] == "" do
table.remove(lines, 1)
end
callback(nil, ret)
-- Simulate formatters removing trailing newline
while #lines > 0 and lines[#lines] == "" do
table.remove(lines)
end

lines[1] = ">" .. lines[1]
lines[#lines] = lines[#lines] .. "<"
callback(nil, lines)
end,
}
end)
Expand Down

0 comments on commit 28f7fc7

Please sign in to comment.