]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: loc for elements
authorEvan You <yyx990803@gmail.com>
Tue, 14 Nov 2023 10:03:00 +0000 (18:03 +0800)
committerEvan You <yyx990803@gmail.com>
Sat, 25 Nov 2023 08:18:29 +0000 (16:18 +0800)
packages/compiler-core/src/parser/Tokenizer.ts
packages/compiler-core/src/parser/index.ts

index 8c2269b1e446f9f67d2342afe559d6dea3a1973e..475bb29ee28b3bee20b41e54ab736ba9cefa26e2 100644 (file)
@@ -29,7 +29,7 @@ import {
 } from 'entities/lib/decode.js'
 import { Position } from '../ast'
 
-const enum CharCodes {
+export const enum CharCodes {
   Tab = 0x9, // "\t"
   NewLine = 0xa, // "\n"
   FormFeed = 0xc, // "\f"
@@ -216,9 +216,9 @@ export default class Tokenizer {
       }
     }
     return {
-      offset: index,
+      column,
       line,
-      column
+      offset: index
     }
   }
 
index 88c0b5576d592a19a37f1c736bc0cb89bb4ff18f..7d00d11d88fe5c2b60564db26b4478e9fd838910 100644 (file)
@@ -1,5 +1,7 @@
 import { fromCodePoint } from 'entities/lib/decode.js'
 import {
+  AttributeNode,
+  DirectiveNode,
   ElementNode,
   ElementTypes,
   NodeTypes,
@@ -8,72 +10,7 @@ import {
   createRoot
 } from '../ast'
 import { ParserOptions } from '../options'
-import Tokenizer from './Tokenizer'
-import { hasOwn } from '@vue/shared'
-
-const formTags = new Set([
-  'input',
-  'option',
-  'optgroup',
-  'select',
-  'button',
-  'datalist',
-  'textarea'
-])
-const pTag = new Set(['p'])
-const tableSectionTags = new Set(['thead', 'tbody'])
-const ddtTags = new Set(['dd', 'dt'])
-const rtpTags = new Set(['rt', 'rp'])
-
-const openImpliesClose = new Map<string, Set<string>>([
-  ['tr', new Set(['tr', 'th', 'td'])],
-  ['th', new Set(['th'])],
-  ['td', new Set(['thead', 'th', 'td'])],
-  ['body', new Set(['head', 'link', 'script'])],
-  ['li', new Set(['li'])],
-  ['p', pTag],
-  ['h1', pTag],
-  ['h2', pTag],
-  ['h3', pTag],
-  ['h4', pTag],
-  ['h5', pTag],
-  ['h6', pTag],
-  ['select', formTags],
-  ['input', formTags],
-  ['output', formTags],
-  ['button', formTags],
-  ['datalist', formTags],
-  ['textarea', formTags],
-  ['option', new Set(['option'])],
-  ['optgroup', new Set(['optgroup', 'option'])],
-  ['dd', ddtTags],
-  ['dt', ddtTags],
-  ['address', pTag],
-  ['article', pTag],
-  ['aside', pTag],
-  ['blockquote', pTag],
-  ['details', pTag],
-  ['div', pTag],
-  ['dl', pTag],
-  ['fieldset', pTag],
-  ['figcaption', pTag],
-  ['figure', pTag],
-  ['footer', pTag],
-  ['form', pTag],
-  ['header', pTag],
-  ['hr', pTag],
-  ['main', pTag],
-  ['nav', pTag],
-  ['ol', pTag],
-  ['pre', pTag],
-  ['section', pTag],
-  ['table', pTag],
-  ['ul', pTag],
-  ['rt', rtpTags],
-  ['rp', rtpTags],
-  ['tbody', tableSectionTags],
-  ['tfoot', tableSectionTags]
-])
+import Tokenizer, { CharCodes } from './Tokenizer'
 
 const voidElements = new Set([
   'area',
@@ -113,21 +50,16 @@ const htmlIntegrationElements = new Set([
 
 let currentOptions: ParserOptions = {}
 let currentRoot: RootNode = createRoot([])
-let elementStack: ElementNode[] = []
 
 // parser state
 let htmlMode = false
 let currentInput = ''
-let openTagStart = 0
-let tagname = ''
-let attribname = ''
-let attribvalue = ''
-let attribs: Record<string, string> | null = null
-let startIndex = 0
-let endIndex = 0
+let currentElement: ElementNode | null = null
+let currentProp: AttributeNode | DirectiveNode | null = null
+let currentAttrValue = ''
 let inPre = 0
 // let inVPre = 0
-const stack: string[] = []
+const stack: ElementNode[] = []
 const foreignContext: boolean[] = [false]
 
 const tokenizer = new Tokenizer(
@@ -143,17 +75,14 @@ const tokenizer = new Tokenizer(
     },
 
     onopentagname(start, end) {
-      emitOpenTag(getSlice(start, (endIndex = end)))
+      emitOpenTag(getSlice(start, end), start)
     },
 
     onopentagend(end) {
-      endIndex = end
-      endOpenTag(false)
-      startIndex = end + 1
+      endOpenTag()
     },
 
     onclosetag(start, end) {
-      endIndex = end
       const name = getSlice(start, end)
 
       if (
@@ -164,16 +93,15 @@ const tokenizer = new Tokenizer(
       }
 
       if (!voidElements.has(name)) {
-        const pos = stack.indexOf(name)
+        const pos = stack.findIndex(e => e.tag === name)
         if (pos !== -1) {
           for (let index = 0; index <= pos; index++) {
-            stack.shift()
-            onCloseTag()
+            onCloseTag(stack.shift()!, end)
           }
         } else if (htmlMode && name === 'p') {
           // Implicit open before close
-          emitOpenTag('p')
-          closeCurrentTag(true)
+          emitOpenTag('p', start)
+          closeCurrentTag(end)
         }
       } else if (htmlMode && name === 'br') {
         // TODO
@@ -181,53 +109,64 @@ const tokenizer = new Tokenizer(
         // this.cbs.onopentag?.('br', {}, true)
         // this.cbs.onclosetag?.('br', false)
       }
-
-      // Set `startIndex` for next node
-      startIndex = end + 1
     },
 
     onselfclosingtag(end) {
-      endIndex = end
-      closeCurrentTag(false)
-      startIndex = end + 1
+      closeCurrentTag(end)
     },
 
     onattribname(start, end) {
-      attribname = getSlice((startIndex = start), end)
+      // TODO directives
+      currentProp = {
+        type: NodeTypes.ATTRIBUTE,
+        name: getSlice(start, end),
+        value: undefined,
+        loc: {
+          start: tokenizer.getPositionForIndex(start),
+          // @ts-expect-error to be attached on attribute end
+          end: undefined,
+          source: ''
+        }
+      }
     },
     onattribdata(start, end) {
-      attribvalue += getSlice(start, end)
+      currentAttrValue += getSlice(start, end)
     },
     onattribentity(codepoint) {
-      attribvalue += fromCodePoint(codepoint)
+      currentAttrValue += fromCodePoint(codepoint)
     },
     onattribend(_quote, end) {
-      endIndex = end
-      if (attribs && !hasOwn(attribs, attribname)) {
-        // TODO gen attributes AST nodes
-        attribs[attribname] = attribvalue
+      if (currentElement) {
+        if (currentProp!.type === NodeTypes.ATTRIBUTE) {
+          // assign value
+          currentProp!.value = {
+            type: NodeTypes.TEXT,
+            content: currentAttrValue,
+            // @ts-expect-error TODO
+            loc: {}
+          }
+        } else {
+          // TODO
+        }
+        currentProp!.loc.end = tokenizer.getPositionForIndex(end)
+        currentElement.props.push(currentProp!)
       }
-      attribvalue = ''
+      currentAttrValue = ''
     },
 
     oncomment(start, end, offset) {
-      endIndex = end
       // TODO oncomment
-      startIndex = end + 1
     },
 
     onend() {
-      // Set the end index for all remaining tags
-      endIndex = startIndex
+      const end = currentInput.length
       for (let index = 0; index < stack.length; index++) {
-        onCloseTag()
+        onCloseTag(stack[index], end)
       }
     },
 
     oncdata(start, end, offset) {
-      endIndex = end
       // TODO throw error
-      startIndex = end + 1
     }
   }
 )
@@ -236,18 +175,31 @@ function getSlice(start: number, end: number) {
   return currentInput.slice(start, end)
 }
 
-function emitOpenTag(name: string) {
-  openTagStart = startIndex
-  tagname = name
-  const impliesClose = htmlMode && openImpliesClose.get(name)
-  if (impliesClose) {
-    while (stack.length > 0 && impliesClose.has(stack[0])) {
-      stack.shift()
-      onCloseTag()
-    }
+function emitOpenTag(name: string, start: number) {
+  currentElement = {
+    type: NodeTypes.ELEMENT,
+    tag: name,
+    // TODO refine namespace
+    ns: 0,
+    // TODO refine tag type
+    tagType: ElementTypes.ELEMENT,
+    props: [],
+    children: [],
+    loc: {
+      start: tokenizer.getPositionForIndex(start - 1),
+      // @ts-expect-error to be attached on tag close
+      end: undefined,
+      source: ''
+    },
+    codegenNode: undefined
   }
+}
+
+function endOpenTag() {
+  addNode(currentElement!)
+  const name = currentElement!.tag
   if (!voidElements.has(name)) {
-    stack.unshift(name)
+    stack.unshift(currentElement!)
     if (htmlMode) {
       if (foreignContextElements.has(name)) {
         foreignContext.unshift(true)
@@ -256,28 +208,15 @@ function emitOpenTag(name: string) {
       }
     }
   }
-  attribs = {}
-}
-
-function closeCurrentTag(isOpenImplied: boolean) {
-  const name = tagname
-  endOpenTag(isOpenImplied)
-  if (stack[0] === name) {
-    onCloseTag()
-    stack.shift()
-  }
+  currentElement = null
 }
 
-function endOpenTag(isImplied: boolean) {
-  startIndex = openTagStart
-  if (attribs) {
-    onOpenTag(tagname)
-    attribs = null
-  }
-  if (voidElements.has(tagname)) {
-    onCloseTag()
+function closeCurrentTag(end: number) {
+  const name = currentElement!.tag
+  endOpenTag()
+  if (stack[0].tag === name) {
+    onCloseTag(stack.shift()!, end)
   }
-  tagname = ''
 }
 
 function onText(content: string, start: number, end: number) {
@@ -300,32 +239,13 @@ function onText(content: string, start: number, end: number) {
   }
 }
 
-function onOpenTag(tag: string) {
-  const el: ElementNode = {
-    type: NodeTypes.ELEMENT,
-    tag,
-    // TODO namespace
-    ns: 0,
-    // TODO refine tag type
-    tagType: ElementTypes.ELEMENT,
-    // TODO props
-    props: [],
-    children: [],
-    loc: {
-      // @ts-expect-error TODO
-      start: {},
-      // @ts-expect-error TODO
-      end: { offset: endIndex },
-      source: ''
-    },
-    codegenNode: undefined
+function onCloseTag(el: ElementNode, end: number) {
+  // attach end position
+  let offset = 0
+  while (currentInput.charCodeAt(end + offset) !== CharCodes.Gt) {
+    offset++
   }
-  addNode(el)
-  elementStack.push(el)
-}
-
-function onCloseTag() {
-  const el = elementStack.pop()!
+  el.loc.end = tokenizer.getPositionForIndex(end + offset + 1)
   // whitepsace management
   el.children = condenseWhitespace(el.children)
 }
@@ -394,19 +314,15 @@ function addNode(node: TemplateChildNode) {
 }
 
 function getParent() {
-  return elementStack[elementStack.length - 1] || currentRoot
+  return stack[0] || currentRoot
 }
 
 function reset() {
   tokenizer.reset()
-  tagname = ''
-  attribname = ''
-  attribvalue = ''
-  attribs = null
-  startIndex = 0
-  endIndex = 0
+  currentElement = null
+  currentProp = null
+  currentAttrValue = ''
   stack.length = 0
-  elementStack.length = 0
   foreignContext.length = 1
   foreignContext[0] = false
 }