summaryrefslogtreecommitdiff
path: root/script/provider/markdown.lua
blob: b54785674506c1e69d2c016cc593336ae9df7af2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
local wssymbol = require("core.workspace-symbol")
local guide = require("parser.guide")

---@class markdown
local mt = {}
mt.__index = mt
mt.__name = 'markdown'

mt._splitLine = false

---@async
---Converts `[mySymbol](lua://mySymbol)` into a link that points to the origin of `mySymbol`.
---@param txt string
local function processSymbolReferences(txt)
	local function replacer(linkText, symbol)
		local source ---@type table

		for _, match in ipairs(wssymbol(symbol)) do
			if match.name == symbol then
				source = match.source
				break
			end
		end

		if not source then
			log.warn(string.format("Failed to find source of %q symbol in markdown comment", symbol))
			return
		end

		local row, _ = guide.rowColOf(source.start)
		local uri = string.format("%s#%i", guide.getUri(source), row + 1)

		return string.format("[%s](%s)", linkText, uri)
	end

	return string.gsub(txt, "%[([^]]*)%]%(lua://([^)]+)%)", replacer)
end

function mt:__tostring()
    return self:string()
end

---@param language string
---@param text? string|markdown
function mt:add(language, text)
    if not text then
        return self
    end
    self._cacheResult = nil
    if type(text) == 'table' then
        self[#self+1] = {
            type     = 'markdown',
            markdown = text,
        }
    else
        text = tostring(text)
        self[#self+1] = {
            type     = 'text',
            language = language,
            text     = text,
        }
    end
    return self
end

function mt:splitLine()
    self._cacheResult = nil
    self[#self+1] = {
        type = 'splitline',
    }
    return self
end

function mt:emptyLine()
    self._cacheResult = nil
    self[#self+1] = {
        type = 'emptyline',
    }
    return self
end

---@return string
function mt:string(nl)
    if self._cacheResult then
        return self._cacheResult
    end
    local lines = {}
    local language = 'md'

    local function concat(markdown)
        for _, obj in ipairs(markdown) do
            if obj.type == 'splitline' then
                if language ~= 'md' then
                    lines[#lines+1] = '```'
                    language = 'md'
                end
                if #lines > 0
                and lines[#lines] ~= '---' then
                    lines[#lines+1] = ''
                    lines[#lines+1] = '---'
                end
            elseif obj.type == 'emptyline' then
                if  #lines > 0
                and lines[#lines] ~= '' then
                    if language ~= 'md' then
                        language = 'md'
                        lines[#lines+1] = '```'
                    end
                    lines[#lines+1] = ''
                end
            elseif obj.type == 'markdown' then
                concat(obj.markdown)
            else
                if obj.language ~= language then
                    if language ~= 'md' then
                        lines[#lines+1] = '```'
                    end
                    if #lines > 0 then
                        lines[#lines+1] = ''
                    end
                    if obj.language ~= 'md' then
                        lines[#lines+1] = '```' .. obj.language
                    end
                end
                if obj.language == 'md' and #lines > 0 then
                    local last = lines[#lines]
                    if obj.text:sub(1, 1) == '@'
                    or last:sub(1, 1) == '@' then
                        if lines[#lines] ~= '' then
                            lines[#lines+1] = ''
                        end
                    elseif last == '---' then
                        if lines[#lines] ~= '' then
                            lines[#lines+1] = ''
                        end
                    end
                end
                lines[#lines + 1] = processSymbolReferences(obj.text)
                language = obj.language
            end
        end
    end

    concat(self)
    if language ~= 'md' then
        lines[#lines+1] = '```'
    end
    while true do
        if lines[#lines] == '---'
        or lines[#lines] == '' then
            lines[#lines] = nil
        else
            break
        end
    end

    local result = table.concat(lines, nl or '\n')
    self._cacheResult = result
    return result
end

return function ()
    return setmetatable({}, mt)
end