feat: improve handling of wiki links and anchors in markdown rendering

This commit is contained in:
purocean
2024-06-19 01:02:30 +08:00
parent 676ad073b8
commit 6893b4061d
4 changed files with 48 additions and 28 deletions
@@ -340,7 +340,8 @@ body.find-in-preview-highlight ::selection {
}
}
a:not([href^="#fn"])[href^="#"]:after {
a:not([href^="#fn"])[href^="#"]:after,
a[is-anchor]:after {
content: '\200D\2002';
padding: 2px;
background-repeat: no-repeat;
@@ -359,7 +360,8 @@ body.find-in-preview-highlight ::selection {
a[href$=".md"],
a[href*=".md:"],
a[href*=".md#"] {
a[href*=".md#"],
a:not([is-anchor])[wiki-link] {
&:after {
content: '\200D\2002';
padding: 1px;
+30 -20
View File
@@ -30,9 +30,9 @@ async function getElement (id: string) {
return _find(id) || _find(id.toUpperCase())
}
async function getFirstMatchPath (repo: string, dir: string, path: string) {
if (path.includes('/')) {
return path
async function getFirstMatchPath (repo: string, dir: string, fileName: string) {
if (fileName.includes('/')) {
return fileName
}
const findInDir = (items: Components.Tree.Node[]): string | null => {
@@ -40,8 +40,8 @@ async function getFirstMatchPath (repo: string, dir: string, path: string) {
const p = normalizeSep(item.path)
if (
item.type === 'file' &&
(p === normalizeSep(join(dir, path)) ||
p === normalizeSep(join(dir, `${path}.md`)))
(p === normalizeSep(join(dir, fileName)) ||
p === normalizeSep(join(dir, `${fileName}.md`)))
) {
return item.path
}
@@ -59,7 +59,7 @@ async function getFirstMatchPath (repo: string, dir: string, path: string) {
const findByName = (items: Components.Tree.Node[]): string | null => {
for (const item of items) {
if (item.type === 'file' && (item.name === path || item.name === `${path}.md`)) {
if (item.type === 'file' && (item.name === fileName || item.name === `${fileName}.md`)) {
return item.path
}
@@ -167,13 +167,17 @@ function handleLink (link: HTMLAnchorElement): boolean {
return { pos, path }
}
const isWikiLink = !!link.getAttribute(DOM_ATTR_NAME.WIKI_LINK)
const _switchDoc = async () => {
let { path, pos } = parsePathPos(normalizeSep(tmp[0]))
const dir = dirname(filePath || '')
// wiki link
if (link.getAttribute(DOM_ATTR_NAME.WIKI_LINK)) {
path = (await getFirstMatchPath(fileRepo, dir, path)) || path
if (isWikiLink) {
path = normalizeSep(path)
path = path.replace(/:/g, '/') // replace all ':' to '/'
path = await getFirstMatchPath(fileRepo, dir, path) || path
path = path.endsWith('.md') ? path : `${path}.md`
}
if (!path.startsWith('/')) { // to absolute path
@@ -184,17 +188,7 @@ function handleLink (link: HTMLAnchorElement): boolean {
return switchDoc(file).then(() => {
pos && setPosition(pos[0], pos[1])
})
}
const path = normalizeSep(tmp[0])
const file: Doc = { path, type: 'file', name: basename(path), repo: fileRepo }
const isMarkdownFile = /(\.md$|\.md#|\.md:)/.test(href)
const supportOpenDirectly = isMarkdownFile || getAllCustomEditors().some(x => x.when?.({ doc: file }))
if (supportOpenDirectly) {
_switchDoc().then(async () => {
}).then(async () => {
const hash = tmp.slice(1).join('#')
// jump anchor
if (hash) {
@@ -212,7 +206,16 @@ function handleLink (link: HTMLAnchorElement): boolean {
}
}
})
}
const path = normalizeSep(tmp[0])
const file: Doc = { path, type: 'file', name: basename(path), repo: fileRepo }
const isMarkdownFile = /(\.md$|\.md#|\.md:)/.test(href)
const supportOpenDirectly = isMarkdownFile || getAllCustomEditors().some(x => x.when?.({ doc: file }))
if (supportOpenDirectly) {
_switchDoc()
return true
} else if (href && href.startsWith('#')) { // for anchor
getElement(href.replace(/^#/, '')).then(el => {
@@ -223,6 +226,9 @@ function handleLink (link: HTMLAnchorElement): boolean {
const { pos } = parsePathPos(href)
pos && setPosition(pos[0], pos[1])
return true
} else if (isWikiLink) {
_switchDoc()
return true
} else {
return false
}
@@ -238,6 +244,10 @@ function convertLink (state: StateCore) {
}
const link = (token: Token) => {
if (token.attrGet(DOM_ATTR_NAME.WIKI_LINK)) {
return
}
const isAnchor = token.tag === 'a'
const attrName = isAnchor ? 'href' : 'src'
const attrVal = decodeURIComponent(token.attrGet(attrName) || '')
+13 -6
View File
@@ -3,7 +3,6 @@ import type StateInline from 'markdown-it/lib/rules_inline/state_inline'
const reMatch = /^([^[#|]*)?(?:#([^#|]*))?(?:\|([^\]]*))?$/
const reExternalLink = /^[a-zA-Z]{1,8}:\/\/.*/
const reExtName = /\.[^/]+$/
const rePos = /(:\d{1,6},?\d{0,6})$/
function wikiLinks (state: StateInline, silent?: boolean) {
@@ -31,6 +30,7 @@ function wikiLinks (state: StateInline, silent?: boolean) {
let url = link + hashStr
let text = label || url
let isAnchor = false
// internal link
if (!reExternalLink.test(link)) {
@@ -44,17 +44,24 @@ function wikiLinks (state: StateInline, silent?: boolean) {
const fileName = link.split('/').pop()
text = label || (fileName ? (fileName + posStr + hashStr) : url)
// no extension name
if (link && !reExtName.test(link)) {
url = `${link}.md${posStr}${hashStr}`
} else if (!link) {
if (!link) {
url = posStr + hashStr
text = label || hash || url
isAnchor = true
}
}
if (!silent) {
state.push('link_open', 'a', 1).attrs = [['href', url], [ctx.args.DOM_ATTR_NAME.WIKI_LINK, 'true']]
const attrs: [string, string][] = [
['href', url],
[ctx.args.DOM_ATTR_NAME.WIKI_LINK, 'true'],
]
if (isAnchor) {
attrs.push([ctx.args.DOM_ATTR_NAME.IS_ANCHOR, 'true'])
}
state.push('link_open', 'a', 1).attrs = attrs
state.push('text', '', 0).content = text
state.push('link_close', 'a', -1)
}
+1
View File
@@ -39,6 +39,7 @@ export const DOM_ATTR_NAME = {
TOKEN_IDX: 'data-token-idx',
DISPLAY_NONE: 'display-none',
WIKI_LINK: 'wiki-link',
IS_ANCHOR: 'is-anchor',
}
export const DOM_CLASS_NAME = {