Diffdown is a real-time collaborative Markdown editor/previewer built on the AT Protocol diffdown.com
at 9a6ba7fadf312bda2dc714899aa76bcb8f00d5aa 87 lines 3.3 kB view raw
1import {ScriptText, StyleText, TextareaText, 2 Element, TagName, Attribute, AttributeName, OpenTag, CloseTag, 3 AttributeValue, UnquotedAttributeValue} from "./parser.terms.js" 4import {parseMixed} from "@lezer/common" 5 6function getAttrs(openTag, input) { 7 let attrs = Object.create(null) 8 for (let att of openTag.getChildren(Attribute)) { 9 let name = att.getChild(AttributeName), value = att.getChild(AttributeValue) || att.getChild(UnquotedAttributeValue) 10 if (name) attrs[input.read(name.from, name.to)] = 11 !value ? "" : value.type.id == AttributeValue ? input.read(value.from + 1, value.to - 1) : input.read(value.from, value.to) 12 } 13 return attrs 14} 15 16function findTagName(openTag, input) { 17 let tagNameNode = openTag.getChild(TagName) 18 return tagNameNode ? input.read(tagNameNode.from, tagNameNode.to) : " " 19} 20 21function maybeNest(node, input, tags) { 22 let attrs 23 for (let tag of tags) { 24 if (!tag.attrs || tag.attrs(attrs || (attrs = getAttrs(node.node.parent.firstChild, input)))) 25 return {parser: tag.parser, bracketed: true} 26 } 27 return null 28} 29 30// tags?: { 31// tag: string, 32// attrs?: ({[attr: string]: string}) => boolean, 33// parser: Parser 34// }[] 35// attributes?: { 36// name: string, 37// tagName?: string, 38// parser: Parser 39// }[] 40 41export function configureNesting(tags = [], attributes = []) { 42 let script = [], style = [], textarea = [], other = [] 43 for (let tag of tags) { 44 let array = tag.tag == "script" ? script : tag.tag == "style" ? style : tag.tag == "textarea" ? textarea : other 45 array.push(tag) 46 } 47 let attrs = attributes.length ? Object.create(null) : null 48 for (let attr of attributes) (attrs[attr.name] || (attrs[attr.name] = [])).push(attr) 49 50 return parseMixed((node, input) => { 51 let id = node.type.id 52 if (id == ScriptText) return maybeNest(node, input, script) 53 if (id == StyleText) return maybeNest(node, input, style) 54 if (id == TextareaText) return maybeNest(node, input, textarea) 55 56 if (id == Element && other.length) { 57 let n = node.node, open = n.firstChild, tagName = open && findTagName(open, input), attrs 58 if (tagName) for (let tag of other) { 59 if (tag.tag == tagName && (!tag.attrs || tag.attrs(attrs || (attrs = getAttrs(open, input))))) { 60 let close = n.lastChild 61 let to = close.type.id == CloseTag ? close.from : n.to 62 if (to > open.to) 63 return {parser: tag.parser, overlay: [{from: open.to, to}]} 64 } 65 } 66 } 67 68 if (attrs && id == Attribute) { 69 let n = node.node, nameNode 70 if (nameNode = n.firstChild) { 71 let matches = attrs[input.read(nameNode.from, nameNode.to)] 72 if (matches) for (let attr of matches) { 73 if (attr.tagName && attr.tagName != findTagName(n.parent, input)) continue 74 let value = n.lastChild 75 if (value.type.id == AttributeValue) { 76 let from = value.from + 1 77 let last = value.lastChild, to = value.to - (last && last.isError ? 0 : 1) 78 if (to > from) return {parser: attr.parser, overlay: [{from, to}], bracketed: true} 79 } else if (value.type.id == UnquotedAttributeValue) { 80 return {parser: attr.parser, overlay: [{from: value.from, to: value.to}]} 81 } 82 } 83 } 84 } 85 return null 86 }) 87}