From 03f0dcd82417115ae4409cabdd32eabd71fb8a0f Mon Sep 17 00:00:00 2001 From: purocean Date: Tue, 18 Jun 2024 00:48:10 +0800 Subject: [PATCH] feat: enhance Wiki Link syntax to support line and column positions --- help/FEATURES.md | 2 +- help/FEATURES_ZH-CN.md | 2 +- .../components/DefaultPreviewerRender.ce.vue | 1 + src/renderer/plugins/markdown-link.ts | 39 ++++++++++++++++--- src/renderer/plugins/markdown-wiki-links.ts | 18 ++++++--- 5 files changed, 50 insertions(+), 12 deletions(-) diff --git a/help/FEATURES.md b/help/FEATURES.md index 03c78b9d..bb4c0f96 100644 --- a/help/FEATURES.md +++ b/help/FEATURES.md @@ -69,7 +69,7 @@ Type '/' in the editor to get prompts *[HTML]: Hyper Text Markup Language *[W3C]: World Wide Web Consortium The HTML specification is maintained by the W3C. -+ Wiki Link: Supports using `[[filename#anchor|display text]]` syntax to link documents, such as [[README#Highlights|Highlights]] ++ Wiki Link: Supports using `[[filename#anchor|display text]]` or `[[filename:line,column|display text]]` syntax to link documents, such as [[README#Highlights|Features]] [[README:3,4]] ## Github Alerts diff --git a/help/FEATURES_ZH-CN.md b/help/FEATURES_ZH-CN.md index 7b3e7190..2f8e1eee 100644 --- a/help/FEATURES_ZH-CN.md +++ b/help/FEATURES_ZH-CN.md @@ -70,7 +70,7 @@ define: *[HTML]: Hyper Text Markup Language *[W3C]: World Wide Web Consortium The HTML specification is maintained by the W3C. -+ Wiki 链接:支持使用 `[[文件名#锚点|显示文本]]` 语法来链接文档,如 [[README#Highlights|特色功能]] ++ Wiki 链接:支持使用 `[[文件名#锚点|显示文本]]` 或 `[[文件名:行,列|显示文本]]` 语法来链接文档,如 [[README#Highlights|特色功能]] [[README:3,4]] ## Github Alerts diff --git a/src/renderer/components/DefaultPreviewerRender.ce.vue b/src/renderer/components/DefaultPreviewerRender.ce.vue index 29b806ea..9c0d180d 100644 --- a/src/renderer/components/DefaultPreviewerRender.ce.vue +++ b/src/renderer/components/DefaultPreviewerRender.ce.vue @@ -358,6 +358,7 @@ body.find-in-preview-highlight ::selection { } a[href$=".md"], + a[href*=".md:"], a[href*=".md#"] { &:after { content: '\200D\2002'; diff --git a/src/renderer/plugins/markdown-link.ts b/src/renderer/plugins/markdown-link.ts index 1aea53de..168ca373 100644 --- a/src/renderer/plugins/markdown-link.ts +++ b/src/renderer/plugins/markdown-link.ts @@ -1,6 +1,6 @@ import StateCore from 'markdown-it/lib/rules_core/state_core' import Token from 'markdown-it/lib/token' -import { Plugin } from '@fe/context' +import ctx, { Plugin } from '@fe/context' import store from '@fe/support/store' import { removeQuery, sleep } from '@fe/utils' import { isElectron, isWindows } from '@fe/support/env' @@ -142,10 +142,33 @@ function handleLink (link: HTMLAnchorElement): boolean { } const tmp = decodeURI(href).split('#') + const rePos = /:([0-9]+),?([0-9]+)?$/ + + const setPosition = (line: number, column: number) => { + ctx.view.disableSyncScrollAwhile(() => { + if (ctx.editor.isDefault()) { + ctx.editor.highlightLine(line, true, 1000) + ctx.editor.getEditor().setPosition({ lineNumber: line, column }) + ctx.editor.getEditor().focus() + } + + ctx.view.highlightLine(line, true, 1000) + }) + } + + const parsePathPos = (path: string): {pos: [number, number] | null, path: string} => { + const match = path.match(rePos) + let pos: [number, number] | null = null + if (match) { + path = path.replace(rePos, '') + pos = [parseInt(match[1]), match[2] ? parseInt(match[2]) : 1] + } + + return { pos, path } + } const _switchDoc = async () => { - let path = normalizeSep(tmp[0]) - + let { path, pos } = parsePathPos(normalizeSep(tmp[0])) const dir = dirname(filePath || '') // wiki link @@ -159,13 +182,15 @@ function handleLink (link: HTMLAnchorElement): boolean { const file: Doc = { path, type: 'file', name: basename(path), repo: fileRepo } - return switchDoc(file) + 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#)/.test(href) + const isMarkdownFile = /(\.md$|\.md#|\.md:)/.test(href) const supportOpenDirectly = isMarkdownFile || getAllCustomEditors().some(x => x.when?.({ doc: file })) if (supportOpenDirectly) { @@ -194,6 +219,10 @@ function handleLink (link: HTMLAnchorElement): boolean { el && scrollIntoView(el) }) return true + } else if (href && href.startsWith(':') && rePos.test(href)) { // for pos + const { pos } = parsePathPos(href) + pos && setPosition(pos[0], pos[1]) + return true } else { return false } diff --git a/src/renderer/plugins/markdown-wiki-links.ts b/src/renderer/plugins/markdown-wiki-links.ts index 203bc416..3990e485 100644 --- a/src/renderer/plugins/markdown-wiki-links.ts +++ b/src/renderer/plugins/markdown-wiki-links.ts @@ -1,9 +1,10 @@ import ctx, { Plugin } from '@fe/context' import type StateInline from 'markdown-it/lib/rules_inline/state_inline' -const reMatch = /^\s*([^[#|]*)?(?:#([^|]*))?(?:\|([^\]]*))?\s*$/ +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) { // check [[ @@ -22,7 +23,7 @@ function wikiLinks (state: StateInline, silent?: boolean) { return false } - const link = (parts[1] || '').trim() + let link = (parts[1] || '').trim() const hash = parts[2] || '' const label = parts[3] || '' @@ -33,14 +34,21 @@ function wikiLinks (state: StateInline, silent?: boolean) { // internal link if (!reExternalLink.test(link)) { + let posStr = '' + const posMatch = link.match(rePos) + if (posMatch) { + link = link.replace(rePos, '') + posStr = posMatch[1] + } + const fileName = link.split('/').pop() - text = label || (fileName ? fileName + hashStr : url) + text = label || (fileName ? (fileName + posStr + hashStr) : url) // no extension name if (link && !reExtName.test(link)) { - url = `${link}.md${hashStr}` + url = `${link}.md${posStr}${hashStr}` } else if (!link) { - url = hashStr + url = posStr + hashStr text = label || hash || url } }