feat: add support for Wiki Links in Markdown syntax

This commit modifies the Markdown syntax in the editor to support Wiki
Links. It adds a new token pattern for [[...]] syntax and updates the
completion provider to handle Wiki Link contexts. Now, when a user types
[[, the editor will suggest Wiki Link completions.

The changes include:
- Adding a new token pattern for [[...]] syntax
- Updating the completion provider to handle Wiki Link contexts

These modifications enhance the editing experience by allowing users to
easily create Wiki Links within their Markdown documents.
This commit is contained in:
purocean
2024-06-14 20:08:19 +08:00
parent afa605c681
commit 2e478f0bbd
3 changed files with 62 additions and 3 deletions
+2
View File
@@ -199,6 +199,7 @@ export default {
{ label: '/ __ Bold', insertText: '__$1__' },
{ label: '/ ~~ Delete', insertText: '~~$1~~' },
{ label: '/ == Mark', insertText: '==$1==' },
{ label: '/ [[]] Wiki Link', insertText: '[[$1]]' },
{ label: '/ ``` Fence', insertText: '```$1\n$2\n```\n' },
{ label: '/ ||| Table', insertText: '| ${1:TH} | ${2:TH} | ${3:TH} |\n| -- | -- | -- |\n| TD | TD | TD |' },
{ label: '/ ||| Small Table', insertText: '| ${1:TH} | ${2:TH} | ${3:TH} |\n| -- | -- | -- |\n| TD | TD | TD |\n{.small}' },
@@ -211,6 +212,7 @@ export default {
ctx.editor.tapMarkdownMonarchLanguage(mdLanguage => {
mdLanguage.tokenizer.root.unshift(
[/==\S.*\S?==/, 'keyword'],
[/\[\[[^[\]]+\]\]/, 'string'],
[/~\S[^~]*\S?~/, 'string'],
[/\^\S[^^]*\S?\^/, 'string'],
)
+29 -3
View File
@@ -9,6 +9,8 @@ import type Token from 'markdown-it/lib/token'
enum CompletionContextKind {
Link, // [...](|)
WikiLink, // [[|]]
ReferenceLink, // [...][|]
LinkDefinition, // []: | // TODO: not implemented
@@ -89,6 +91,7 @@ class CompletionProvider implements Monaco.languages.CompletionItemProvider {
}
case CompletionContextKind.LinkDefinition:
case CompletionContextKind.WikiLink:
case CompletionContextKind.Link: {
const items: Monaco.languages.CompletionItem[] = []
@@ -151,6 +154,9 @@ class CompletionProvider implements Monaco.languages.CompletionItemProvider {
/// [...](...|
private readonly linkStartPattern = /\[([^\]]*?)\]\(\s*([^\s()]*)$/
/// [[...|
private readonly wikiLinkStartPattern = /\[\[\s*([^\s[\]]*)$/
/// [...|
private readonly referenceLinkStartPattern = /\[\s*([^\s[\]]*)$/
@@ -186,6 +192,19 @@ class CompletionProvider implements Monaco.languages.CompletionItemProvider {
}
}
const wikiLinkPrefixMatch = linePrefixText.match(this.wikiLinkStartPattern)
if (wikiLinkPrefixMatch) {
const prefix = wikiLinkPrefixMatch[1]
const suffix = lineSuffixText.match(/^[^\]]*/)
return {
kind: CompletionContextKind.WikiLink,
linkPrefix: prefix,
linkTextStartPosition: position.delta(0, -prefix.length),
linkSuffix: suffix ? suffix[0] : '',
anchorInfo: this.getAnchorContext(prefix),
}
}
const definitionLinkPrefixMatch = linePrefixText.match(this.definitionPattern)
if (definitionLinkPrefixMatch) {
const prefix = definitionLinkPrefixMatch[1]
@@ -230,14 +249,21 @@ class CompletionProvider implements Monaco.languages.CompletionItemProvider {
if (!anchorMatch) {
return undefined
}
let beforeAnchor = anchorMatch[1]
if (anchorMatch[1] && !this.ctx.utils.path.extname(beforeAnchor)) {
beforeAnchor += '.md'
}
return {
beforeAnchor: anchorMatch[1],
beforeAnchor,
anchorPrefix: anchorMatch[2],
}
}
private async * providePathSuggestions (position: Monaco.Position, context: CompletionContext): AsyncIterable<Monaco.languages.CompletionItem> {
const valueBeforeLastSlash = context.linkPrefix.substring(0, context.linkPrefix.lastIndexOf('/') + 1) || '.' // keep the last slash
const valueBeforeLastSlash = context.linkPrefix.substring(0, context.linkPrefix.lastIndexOf('/') + 1) // keep the last slash
const currentFile = this.ctx.store.state.currentFile
if (!currentFile) {
@@ -246,7 +272,7 @@ class CompletionProvider implements Monaco.languages.CompletionItemProvider {
const parentDir = this.ctx.utils.path.resolve(
this.ctx.utils.path.dirname(currentFile.path),
valueBeforeLastSlash
valueBeforeLastSlash || '.'
)
const pathSegmentStart = position.delta(0, valueBeforeLastSlash.length - context.linkPrefix.length)
+31
View File
@@ -73,3 +73,34 @@ markdown.core.ruler.after('normalize', 'after_normalize', state => {
state.env.tokens = state.tokens
return true
})
markdown.linkify.add('[[', {
validate: /^\s*([^[\]]+)\s*\]\]/,
normalize: (match) => {
const parts = match.raw.slice(2, -2).split('|')
const url = parts[0].trim()
// external link
if (/^[a-zA-Z]{1,8}:\/\/.*/.test(url)) {
match.url = url
match.text = parts[1] || url
return
}
const [path, hash] = url.split('#')
const hashStr = hash ? `#${hash}` : ''
const name = parts[1] || (path.split('/').pop() + hashStr)
match.text = name || url
// has extension name
if (/\.[^/]+$/.test(path)) {
match.url = url
} else if (path) {
match.url = `${path}.md${hashStr}`
} else {
match.url = hashStr
match.text = name || hash || url
}
}
})