diff --git a/script/core/hover/description.lua b/script/core/hover/description.lua
index afeb1bb963e2fe18cbadf7dbcc1d548a018ae9f5..9ec5cd726b5fb3ede57c9e21cce75e0135e4e2bf 100644
--- a/script/core/hover/description.lua
+++ b/script/core/hover/description.lua
@@ -246,34 +246,23 @@ local function getFunctionComment(source)
         end
     end
 
-    local comments = {}
-    local isComment = true
+    local md = markdown()
     for _, doc in ipairs(docGroup) do
         if doc.type == 'doc.comment' then
-            if not isComment then
-                comments[#comments+1] = '\n'
-            end
-            isComment = true
             if doc.comment.text:sub(1, 1) == '-' then
-                comments[#comments+1] = doc.comment.text:sub(2)
+                md:add('md', doc.comment.text:sub(2))
             else
-                comments[#comments+1] = doc.comment.text
+                md:add('md', doc.comment.text)
             end
-            comments[#comments+1] = '\n'
         elseif doc.type == 'doc.param' then
             if doc.comment then
-                isComment = false
-                comments[#comments+1] = '\n'
-                comments[#comments+1] = ('@*param* `%s` — %s'):format(
+                md:add('md', ('@*param* `%s` — %s'):format(
                     doc.param[1],
                     doc.comment.text
-                )
-                comments[#comments+1] = '\n'
+                ))
             end
         elseif doc.type == 'doc.return' then
             if hasReturnComment then
-                isComment = false
-                comments[#comments+1] = '\n'
                 local name = {}
                 for _, rtn in ipairs(doc.returns) do
                     if rtn.name then
@@ -282,37 +271,24 @@ local function getFunctionComment(source)
                 end
                 if doc.comment then
                     if #name == 0 then
-                        comments[#comments+1] = ('@*return* — %s'):format(doc.comment.text)
+                        md:add('md', ('@*return* — %s'):format(doc.comment.text))
                     else
-                        comments[#comments+1] = ('@*return* `%s` — %s'):format(table.concat(name, ','), doc.comment.text)
+                        md:add('md', ('@*return* `%s` — %s'):format(table.concat(name, ','), doc.comment.text))
                     end
                 else
                     if #name == 0 then
-                        comments[#comments+1] = '@*return*'
+                        md:add('md', '@*return*')
                     else
-                        comments[#comments+1] = ('@*return* `%s`'):format(table.concat(name, ','))
+                        md:add('md', ('@*return* `%s`'):format(table.concat(name, ',')))
                     end
                 end
-                comments[#comments+1] = '\n'
             end
         elseif doc.type == 'doc.overload' then
-            comments[#comments+1] = '---'
+            md:splitLine()
         end
     end
-    if comments[1] == '\n' then
-        table.remove(comments, 1)
-    end
-    if comments[#comments] == '\n' then
-        table.remove(comments)
-    end
-    comments = table.concat(comments)
 
     local enums = getBindEnums(source, docGroup)
-    if comments == '' and not enums then
-        return
-    end
-    local md = markdown()
-    md:add('md', comments)
     md:add('lua', enums)
     return md
 end
diff --git a/script/provider/markdown.lua b/script/provider/markdown.lua
index 7b94157e3177227fc796e44880ba3f548b80506b..140267d705905520b955621597eea4203054e6b0 100644
--- a/script/provider/markdown.lua
+++ b/script/provider/markdown.lua
@@ -58,9 +58,7 @@ function mt:string()
             elseif obj.type == 'markdown' then
                 concat(obj.markdown)
             else
-                if obj.language == language then
-                    lines[#lines+1] = obj.text
-                else
+                if obj.language ~= language then
                     if language ~= 'md' then
                         lines[#lines+1] = '```'
                     end
@@ -70,9 +68,18 @@ function mt:string()
                     if obj.language ~= 'md' then
                         lines[#lines+1] = '```' .. obj.language
                     end
-                    lines[#lines+1] = obj.text
-                    language = obj.language
                 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
+                    end
+                end
+                lines[#lines+1] = obj.text
+                language = obj.language
             end
         end
     end
diff --git a/test/crossfile/hover.lua b/test/crossfile/hover.lua
index e673bc5f4c69ec3d6874db46c893407b37c6b1af..04f7cc02ee95423f5d334bce1560146dadc7e6dd 100644
--- a/test/crossfile/hover.lua
+++ b/test/crossfile/hover.lua
@@ -534,7 +534,9 @@ hover = {
     name = 'f',
     description = [[
 @*param* `arg3` — comment3
+
 ---
+
 @*param* `arg1` — comment1
 
 @*param* `arg2` — comment2]]
@@ -592,7 +594,7 @@ function f()
   -> boolean
   2. string]],
     name = 'f',
-    description = nil
+    description = ''
 }}
 
 TEST {{ path = 'a.lua', content = '', }, {