Skip to content

Commit

Permalink
Merge pull request #238 from noirbizarre/fix/code-blocks
Browse files Browse the repository at this point in the history
fix(code-blocks): Fixes multiple language parsing cases (tilde, spaces before/after, directives...)
  • Loading branch information
OXY2DEV authored Jan 8, 2025
2 parents 72cd342 + 534dfc8 commit 6e9f184
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 41 deletions.
34 changes: 10 additions & 24 deletions lua/markview/extras/editor.lua
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ editor.configuraton = {
local start, col, stop, _ = TSNode:range();
local lines = vim.api.nvim_buf_get_lines(buffer, start, stop, false);

local ft = lines[1]:match("```(%S*)") or "lua";
local _, info = languages.get_fence(lines[1])
local ft = languages.info(info);
local _l = {};

table.remove(lines, 1);
Expand All @@ -54,15 +55,16 @@ editor.configuraton = {
table.insert(_l, line:sub(col, #line))
end

return ft:gsub("[%{%}]", ""), _l, start + 1, stop - 1;
return ft, _l, start + 1, stop - 1;
end
},
appliers = {
["fenced_code_block"] = function (buffer, TSNode, lines)
local start, _, _, _ = TSNode:range();
local delimiter = vim.api.nvim_buf_get_lines(buffer, start, start + 1, false)[1];

local before = delimiter:match("^(.-)```");
local fence = languages.get_fence(delimiter)
local before = delimiter:match("^(.-)" .. fence);

for l, line in ipairs(lines) do
lines[l] = (before or "") .. line
Expand All @@ -89,23 +91,6 @@ editor.configuraton = {
end
}

--- Gets the filetype from an info string
---@param delim string
---@return string
local get_ft = function (delim)
local ft = "";

if delim:match("^```%{%{(.-)%}%}") then
ft = languages.get_ft(delim:match("^```%{%{(.-)%}%}"));
elseif delim:match("^```%{(.-)%}") then
ft = languages.get_ft(delim:match("^```%{(.-)%}"));
elseif delim:match("^```(%S+)") then
ft = languages.get_ft(delim:match("^```(%S+)"));
end

return ft;
end

--- Creates a new buffer when not available.
--- Otherwise, returns the current editor buffer.
---@return integer
Expand Down Expand Up @@ -400,10 +385,11 @@ editor.create = function ()
end

start_delim = tostring(input);
vim.bo[editor.buffer].filetype = get_ft(start_delim);
local icon, hl = languages.get_icon(get_ft(start_delim));

ft = get_ft(start_delim);
local fence, info = languages.get_fence(start_delim);
ft = languages.info(info);
end_delim = fence or end_delim;
vim.bo[editor.buffer].filetype = ft
local icon, hl = languages.get_icon(ft);

hl = hl .. "Fg";

Expand Down
50 changes: 50 additions & 0 deletions lua/markview/languages.lua
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,28 @@ languages.patterns = {
["html"] = "HTML"
};

--- Known language info string patterns
---@type string[]
languages.info_patterns = {
-- {{ lang }} params
"%{%{([^%}]*)%}%}%s*(.*)$",
-- Myst code blocks (code, code-block, code-cell) with language
-- https://mystmd.org/guide/code#code-blocks
"%{code%S*%}%s*(%S+)$",
-- Other {}-wrapped directive with unknown processing
"%{([^%}]*)%}%s*(.*)$",
-- Language string and additional info
-- https://spec.commonmark.org/0.31.2/#example-143
"(%S-)%s+(.*)$",
-- Language string without additional info or no language
-- https://spec.commonmark.org/0.31.2/#example-143
"(%S*)%s*$",
}

--- Known code-block fences
---@type string[]
languages.fences = {"`", "~"}

--- Gets the language name from a string
---@param name string
---@return string
Expand Down Expand Up @@ -434,4 +456,32 @@ languages.get_icon = function (ft)
return languages.icons[ft] or languages.icons.default, hl, sign;
end

--- Extract fenced code block header
---@param line string
---@return string|nil fence the matched fence string
---@return string info the matched info string
languages.get_fence = function(line)
for _, char in pairs(languages.fences) do
--- Match any supported fence, optionnaly indented or quoted
local fence, info = line:match("^>*%s*(" .. string.rep(char, 3) .. "+)%s*(.-)%s*$");
if fence ~= nil then
return fence, info;
end
end
return nil, ""
end

--- Extract language and parameters from an infostring
---@param info string the info string to parse
---@return string language the extracted language
---@return string|nil # Some optional extra data
languages.info = function (info)
for _, pattern in pairs(languages.info_patterns) do
local lang, extra = info:match(pattern);
if lang then
return lang, extra;
end
end
end

return languages;
26 changes: 9 additions & 17 deletions lua/markview/parser.lua
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ parser.filter_lines = function (buffer, from, to)

local code_block_indent = 0;
local desc_indent = 0;
local current_fence = "";

local start = 0;

Expand Down Expand Up @@ -181,11 +182,14 @@ parser.filter_lines = function (buffer, from, to)
parent_marker = line:match("^%s*(%d+[%)%.])");
end

if line:match("(```)") and withinCodeBlock ~= true then
local fence = lang.get_fence(line)
if fence and withinCodeBlock ~= true then
withinCodeBlock = true;
current_fence = fence;
code_block_indent = spaces_before;
elseif line:match("(```)") and withinCodeBlock == true then
elseif withinCodeBlock == true and line:match(current_fence) then
withinCodeBlock = false;
current_fence = "";
elseif withinCodeBlock == true then
spaces_before = code_block_indent;
goto withinElement;
Expand All @@ -203,7 +207,7 @@ parser.filter_lines = function (buffer, from, to)
then
spaces_before = math.max(0, spaces_before - vim.fn.strchars((parent_marker or "") .. " "));

if line:match("(```)") then
if fence then
code_block_indent = spaces_before;
elseif insideDescription == true then
align_spaces[l] = 2;
Expand Down Expand Up @@ -381,20 +385,8 @@ parser.md = function (buffer, TStree, from, to)

local block_start = vim.api.nvim_buf_get_lines(buffer, row_start, row_start + 1, false)[1];

local language_string, additional_info = "", nil;

if block_start:match("%s*```%{%{([^%}]*)%}%}") then
language_string = block_start:match("%s*```%{%{([^%}]*)%}%}");
additional_info = block_start:match("%s*```%{%{[^%}]*%}%}%s*(.*)$");
elseif block_start:match("%s*```%{([^%}]*)%}") then
language_string = block_start:match("%s*```%{([^%}]*)%}");
additional_info = block_start:match("%s*```%{[^%}]*%}%s*(.*)$");
elseif block_start:match("%s*```(%S*)$") then
language_string = block_start:match("%s*```(%S*)$");
elseif block_start:match("%s*```(%S*)%s*") then
language_string = block_start:match("%s*```(%S*)%s");
additional_info = block_start:match("%s*```%S*%s+(.*)$");
end
local _, info = lang.get_fence(block_start);
local language_string, additional_info = lang.info(info)

local code_lines = vim.api.nvim_buf_get_lines(buffer, row_start + 1, row_end - 1, false);

Expand Down

0 comments on commit 6e9f184

Please sign in to comment.