import { MarkType , MarkSpec , DOMOutputSpec , Mark as ProseMark } from "prosemirror-model" ; import { InputRule } from "prosemirror-inputrules" ; import { toggleMark , Keymap } from "prosemirror-commands" ; import * as Md from "@common/markdown/markdown-ast" ; import { markActive , markInputRule } from "@common/prosemirror/util/mark-utils" ; import { openPrompt , TextField } from "@common/prompt/prompt" ; import { MarkExtension , MdastMarkMap , MdastMarkMapType , Prose2Mdast_MarkMap_Presets } from "@common/extensions/extension" ; import * as Uni from "unist" ; import * as Mdast from "mdast" ; import { AnyChildren , markMapBasic } from "@common/markdown/mdast2prose" ; import { unistIsStringLiteral } from "@common/markdown/unist-utils" ; export function boldRule ( markType ) { return markInputRule ( /\*\*([^\s](?:.*[^\s])?)\*\*(.)$/ , markType ) ; } compare export class BoldExtension extends MarkExtension < Md . Strong > { get name ( ) { return "strong" as const ; } createMarkSpec ( ) { return { parseDOM : [ { tag : "b" } , { tag : "strong" } , { style : "font-weight" , getAttrs : ( value ) => /^(bold(er)?|[5-9]\d{2,})$/ . test ( value as string ) && null } ] , toDOM ( ) { return [ "strong" ] ; } } ; } createInputRules ( ) { return [ boldRule ( this . markType ) ] ; } createKeymap ( ) { return { "Mod-b" : toggleMark ( this . markType ) , "Mod-B" : toggleMark ( this . markType ) } ; } get mdastNodeType ( ) { return "strong" as const ; } ; createMdastMap ( ) { return MdastMarkMapType . MARK_DEFAULT ; } prose2mdast ( ) { return Prose2Mdast_MarkMap_Presets . MARK_DEFAULT ; } } export function italicRule ( markType ) { return markInputRule ( /(?<!\*)\*(?:[^\s\*](.*[^\s])?)\*([^\*])$/ , markType ) ; } export class ItalicExtension extends MarkExtension < Md . Emphasis > { get name ( ) { return "em" as const ; } createMarkSpec ( ) { return { parseDOM : [ { tag : "i" } , { tag : "em" } , { style : "font-style" , getAttrs : ( value ) => value == "italic" && null } ] , toDOM ( ) { return [ "em" ] ; } } ; } createInputRules ( ) { return [ italicRule ( this . markType ) ] ; } createKeymap ( ) { return { "Mod-i" : toggleMark ( this . markType ) , "Mod-I" : toggleMark ( this . markType ) } ; } get mdastNodeType ( ) { return "emphasis" as const ; } ; createMdastMap ( ) { return MdastMarkMapType . MARK_DEFAULT ; } prose2mdast ( ) { return Prose2Mdast_MarkMap_Presets . MARK_DEFAULT ; } } export class LinkExtension extends MarkExtension < Md . Link > { get name ( ) { return "link" as const ; } createMarkSpec ( ) { return { attrs : { href : { } , title : { default : null } } , inclusive : false , parseDOM : [ { tag : "a[href]" , getAttrs ( dom ) { return { href : ( dom as HTMLElement ) . getAttribute ( "href" ) , title : ( dom as HTMLElement ) . getAttribute ( "title" ) } ; } } ] , toDOM ( node ) { return [ "a" , node . attrs ] ; } } ; } createKeymap ( ) { return { "Ctrl-k" : ( state , dispatch , view ) => { if ( state . selection . empty ) { return false ; } let markType = this . markType ; if ( markActive ( state , markType ) ) { console . log ( "link active" ) ; toggleMark ( markType ) ( state , dispatch ) ; return true ; } openPrompt ( { title : "Create a link" , fields : { href : new TextField ( { label : "Link target" , required : true } ) , title : new TextField ( { label : "Title" } ) } , callback ( attrs ) { if ( ! view ) { return ; } toggleMark ( markType , attrs ) ( view . state , view . dispatch ) ; view . focus ( ) ; } } ) ; return true ; } } ; } get mdastNodeType ( ) { return "link" as const ; } ; createMdastMap ( ) { return { mapType : "mark_custom" , mapMark : markMapBasic ( this . markType , node => ( { href : node . url , title : node . title } ) ) } ; } prose2mdast ( ) { return { create : ( mark , node ) : Md . Link => { let linkAttrs = mark . attrs as { href : string ; title : string | null ; } ; let linkNode = { type : this . mdastNodeType , url : linkAttrs . href , children : [ node ] , ... ( linkAttrs . title ? { title : linkAttrs . title } : { } ) , } ; return ( linkNode as Md . Link ) ; } } ; } } export class CodeExtension extends MarkExtension < Md . InlineCode > { get name ( ) { return "code" as const ; } createMarkSpec ( ) { return { inclusive : false , parseDOM : [ { tag : "code" } ] , toDOM ( ) { return [ "code" ] ; } } ; } createKeymap ( ) { return { "Mod-`" : toggleMark ( this . markType ) } ; } createInputRules ( ) { return [ ] ; } get mdastNodeType ( ) { return "inlineCode" as const ; } ; createMdastMap ( ) { return MdastMarkMapType . MARK_LITERAL ; } prose2mdast ( ) { return Prose2Mdast_MarkMap_Presets . MARK_LITERAL ; } } export function wikilinkRule ( markType ) { return markInputRule ( /\[\[([^\s](?:[^\]]*[^\s])?)\]\](.)$/ , markType ) ; } export class WikilinkExtension extends MarkExtension < Md . Wikilink > { get name ( ) { return "wikilink" as const ; } createMarkSpec ( ) { return { attrs : { title : { default : null } } , inclusive : false , parseDOM : [ { tag : "span.wikilink" } ] , toDOM ( node ) { return [ "span" , Object . assign ( { class : "wikilink" } , node . attrs ) ] ; } } ; } createKeymap ( ) { return { "Mod-[" : toggleMark ( this . markType ) } ; } createInputRules ( ) { return [ wikilinkRule ( this . markType ) ] ; } get mdastNodeType ( ) { return "wikiLink" as const ; } ; createMdastMap ( ) { return MdastMarkMapType . MARK_LITERAL ; } prose2mdast ( ) { return { create : < N extends Uni . Node > ( mark , node ) : Md . Wikilink | N => { if ( ! unistIsStringLiteral ( node ) ) { console . error ( `mark type ${ this . name } can only wrap Literal node ; skipping` ) ; return node ; } let wikiAttrs = mark . attrs as { title : string ; } ; return { type : this . mdastNodeType , value : node . value , data : { alias : node . value , permalink : node . value , exists : true } } ; } } ; } }