This is an automated email from the ASF dual-hosted git repository.

rthomas320 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/daffodil-vscode.git


The following commit(s) were added to refs/heads/main by this push:
     new 9be74dd  handle name and ref attributes consistently modified 
suggestions so that newVaribleInstance is not available for group element with 
an name attribute added missing ref attribute to xml tags that require it bug 
fix for intellisense deleting attributes when an attribute value ends in an 
equal sign fixed an intellisense logic flaw that presented element suggestions 
instead of attribute suggestion in some instances
9be74dd is described below

commit 9be74dd9f5042536579d9444295b31eeec783c87
Author: rthomas320 <[email protected]>
AuthorDate: Tue Jul 15 10:24:57 2025 -0400

    handle name and ref attributes consistently
    modified suggestions so that newVaribleInstance is not available for
    group element with an name attribute
    added missing ref attribute to xml tags that require it
    bug fix for intellisense deleting attributes when an attribute value
    ends in an equal sign
    fixed an intellisense logic flaw that presented element suggestions
    instead of attribute suggestion in some instances
    
    closes #1082
    closes #1306
    closes #1307
    closes #1318
    closes #1326
---
 src/language/providers/attributeCompletion.ts      |  17 +-
 src/language/providers/attributeValueCompletion.ts |  17 +-
 src/language/providers/closeElementSlash.ts        |  14 +-
 src/language/providers/closeUtils.ts               |   8 +
 src/language/providers/elementCompletion.ts        |  62 ++++--
 .../providers/intellisense/elementItems.ts         |  19 +-
 src/language/providers/utils.ts                    | 228 ++++++++++++++-------
 src/tests/DfdlIntellisenseTestingChecklist.md      |   2 +-
 src/tests/suite/language/items.test.ts             |   7 +-
 9 files changed, 268 insertions(+), 106 deletions(-)

diff --git a/src/language/providers/attributeCompletion.ts 
b/src/language/providers/attributeCompletion.ts
index 806e102..7184bb2 100644
--- a/src/language/providers/attributeCompletion.ts
+++ b/src/language/providers/attributeCompletion.ts
@@ -74,6 +74,7 @@ function getCompletionItems(
  * @param document
  * @returns
  */
+
 function getPotentialAttributeText(
   position: vscode.Position,
   document: vscode.TextDocument
@@ -83,6 +84,11 @@ function getPotentialAttributeText(
   let upperLineBound: number = position.line
 
   // Determining the lowerbound strategy: Traverse backwards line-by-line 
until we encounter an opening character (<)
+
+  //handle edge case if there's an element closing on the same line or if 
there is a closing tag after the cursor on the same line
+  if (lowerLineBound > 0) {
+    lowerLineBound--
+  }
   while (
     lowerLineBound > 0 && // Make sure we aren't going to negative line indexes
     document.lineAt(lowerLineBound).text.indexOf('<') == -1 // continue going 
up the document if there is no <
@@ -164,7 +170,7 @@ function prunedDuplicateAttributes(
 
   // Traverse forward character by character to find > or <
   while (
-    indexUpperBound < relevantDocText.length - 1 &&
+    indexUpperBound < relevantDocText.length &&
     !(
       relevantDocText[indexUpperBound] == '<' ||
       relevantDocText[indexUpperBound] == '>'
@@ -177,7 +183,7 @@ function prunedDuplicateAttributes(
   // Force it to be closed if the current xml element isn't closed it
   const fullXMLElementText =
     relevantDocText[indexUpperBound - 1] != '>'
-      ? `${relevantDocText.substring(indexLowerBound, indexUpperBound - 1)}>`
+      ? `${relevantDocText.substring(indexLowerBound, indexUpperBound)}>`
       : relevantDocText.substring(indexLowerBound, indexUpperBound)
 
   // Obtain attributes for the currentl XML element after attempting to parse 
the whole thing as an XML element
@@ -223,6 +229,7 @@ export function getAttributeCompletionProvider() {
         let nearestOpenItem = xmlItem.itemName
         let itemsOnLine = getItemsOnLineCount(triggerText)
         const nsPrefix = xmlItem.itemNS
+        const attributeNames = xmlItem.itemAttributes
         let additionalItems = getDefinedTypes(
           document,
           getSchemaNsPrefix(document)
@@ -245,8 +252,10 @@ export function getAttributeCompletionProvider() {
           itemsOnLine < 2
             ? '\t'
             : ''
+
         const fullAttrCompletionList = checkNearestOpenItem(
           nearestOpenItem,
+          attributeNames,
           triggerText,
           nsPrefix,
           preVal,
@@ -286,6 +295,7 @@ export function getTDMLAttributeCompletionProvider() {
         let nearestOpenItem = xmlItem.itemName
         let itemsOnLine = getItemsOnLineCount(triggerText)
         const nsPrefix = xmlItem.itemNS
+        const attributeNames = xmlItem.itemAttributes
         let additionalItems = getDefinedTypes(document, nsPrefix)
 
         if (isInXPath(document, position)) return undefined
@@ -308,6 +318,7 @@ export function getTDMLAttributeCompletionProvider() {
 
         return checkNearestOpenItem(
           nearestOpenItem,
+          attributeNames,
           triggerText,
           nsPrefix,
           preVal,
@@ -354,6 +365,7 @@ export function getDefinedTypes(
 
 function checkNearestOpenItem(
   nearestOpenItem: string,
+  attributeNames: string[],
   triggerText: string,
   nsPrefix: string,
   preVal: string,
@@ -524,6 +536,7 @@ function checkNearestOpenItem(
     case 'format':
       return getCompletionItems(
         [
+          'ref',
           'dfdl:byteOrder',
           'dfdl:bitOrder',
           'dfdl:binaryNumberRep',
diff --git a/src/language/providers/attributeValueCompletion.ts 
b/src/language/providers/attributeValueCompletion.ts
index 076e003..4250b00 100644
--- a/src/language/providers/attributeValueCompletion.ts
+++ b/src/language/providers/attributeValueCompletion.ts
@@ -17,7 +17,12 @@
 
 import * as vscode from 'vscode'
 
-import { checkBraceOpen, cursorWithinBraces, getNsPrefix } from './utils'
+import {
+  checkBraceOpen,
+  cursorWithinBraces,
+  cursorWithinQuotes,
+  getNsPrefix,
+} from './utils'
 import { getDefinedTypes } from './attributeCompletion'
 import {
   attributeValues,
@@ -34,7 +39,8 @@ export function getAttributeValueCompletionProvider() {
       ) {
         if (
           checkBraceOpen(document, position) ||
-          cursorWithinBraces(document, position)
+          cursorWithinBraces(document, position) ||
+          !cursorWithinQuotes(document, position)
         ) {
           return undefined
         }
@@ -189,10 +195,9 @@ function getAttributeDetails(
           )
           endPos = currentText.indexOf(quoteChar[i], currentPos + 1)
           attributeStartPos = textBeforeTrigger.lastIndexOf(' ')
-          attributeName = textBeforeTrigger.substring(
-            attributeStartPos + 1,
-            currentPos - 1
-          )
+          attributeName = textBeforeTrigger
+            .substring(attributeStartPos + 1, currentPos - 1)
+            .trim()
         }
       }
     }
diff --git a/src/language/providers/closeElementSlash.ts 
b/src/language/providers/closeElementSlash.ts
index ff18b0b..c2fff16 100644
--- a/src/language/providers/closeElementSlash.ts
+++ b/src/language/providers/closeElementSlash.ts
@@ -39,8 +39,18 @@ export function getCloseElementSlashProvider() {
         position: vscode.Position
       ) {
         let backpos = new vscode.Position(position.line, position.character - 
1)
-        const nsPrefix = getNsPrefix(document, position)
-        const triggerText = document
+        let nsPrefix = getNsPrefix(document, position)
+        let triggerText = document.lineAt(position.line).text
+        let tagPos = triggerText.lastIndexOf('<' + nsPrefix + ':')
+        triggerText.lastIndexOf(',' + nsPrefix + ':')
+        if (tagPos < 0) {
+          tagPos = triggerText.lastIndexOf('<dfdl:')
+          if (tagPos > 0) {
+            nsPrefix = 'dfdl:'
+          }
+        }
+
+        triggerText = document
           .lineAt(position)
           .text.substring(0, position.character)
         let nearestTagNotClosed = checkMissingCloseTag(
diff --git a/src/language/providers/closeUtils.ts 
b/src/language/providers/closeUtils.ts
index 3b6d285..297e1c3 100644
--- a/src/language/providers/closeUtils.ts
+++ b/src/language/providers/closeUtils.ts
@@ -34,6 +34,14 @@ export function checkMissingCloseTag(
     const textBeforeTrigger = triggerText.substring(0, triggerPos)
 
     nsPrefix = getItemPrefix(items[i], origPrefix)
+    let tagPos = triggerText.lastIndexOf('<' + nsPrefix + items[i])
+
+    if (tagPos < 0) {
+      tagPos = triggerText.lastIndexOf('<dfdl:' + items[i])
+      if (tagPos > 0) {
+        nsPrefix = 'dfdl:'
+      }
+    }
 
     if (itemsOnLine > 1) {
       if (textBeforeTrigger.lastIndexOf('<' + nsPrefix + items[i]) > -1) {
diff --git a/src/language/providers/elementCompletion.ts 
b/src/language/providers/elementCompletion.ts
index 3f07999..77178da 100644
--- a/src/language/providers/elementCompletion.ts
+++ b/src/language/providers/elementCompletion.ts
@@ -27,6 +27,7 @@ import {
   createCompletionItem,
   getCommonItems,
   nearestTag,
+  getAttributeNames,
   getItemsOnLineCount,
   cursorWithinBraces,
   cursorWithinQuotes,
@@ -312,7 +313,17 @@ function nearestOpenTagChildElements(
         iCount < 2
           ? new vscode.Position(tagPosition.line - 1, tagPosition.character)
           : tagPosition
-      let pElement = getAnnotationParent(document, newPosition, nsPrefix)
+      let [pElement, pPosition] = getAnnotationParent(
+        document,
+        newPosition,
+        nsPrefix
+      )
+      let attributeNames: string[] = getAttributeNames(
+        document,
+        pPosition,
+        nsPrefix,
+        pElement
+      )
       switch (pElement) {
         case 'schema':
           return getElementCompletionItems(
@@ -332,7 +343,6 @@ function nearestOpenTagChildElements(
               'dfdl:assert',
               'dfdl:discriminator',
               'dfdl:element',
-              'dfdl:setVariable',
               'dfdl:property',
             ],
             '',
@@ -344,6 +354,7 @@ function nearestOpenTagChildElements(
             [
               'dfdl:assert',
               'dfdl:discriminator',
+              'dfdl:setVariable',
               'dfdl:newVariableInstance',
               'dfdl:sequence',
             ],
@@ -365,18 +376,32 @@ function nearestOpenTagChildElements(
             nsPrefix
           )
         case 'group':
-          return getElementCompletionItems(
-            [
-              'dfdl:assert',
-              'dfdl:group',
-              'dfdl:discriminator',
-              'dfdl:newVariableInstance',
-              'dfdl:setVariable',
-            ],
-            '',
-            '',
-            nsPrefix
-          )
+          if (attributeNames.includes('ref')) {
+            return getElementCompletionItems(
+              [
+                'dfdl:assert',
+                'dfdl:group',
+                'dfdl:discriminator',
+                'dfdl:newVariableInstance',
+                'dfdl:setVariable',
+              ],
+              '',
+              '',
+              nsPrefix
+            )
+          } else {
+            return getElementCompletionItems(
+              [
+                'dfdl:assert',
+                'dfdl:group',
+                'dfdl:discriminator',
+                'dfdl:setVariable',
+              ],
+              '',
+              '',
+              nsPrefix
+            )
+          }
         case 'simpleType':
           return getElementCompletionItems(
             [
@@ -419,6 +444,7 @@ function nearestOpenTagChildElements(
           'annotation',
           'include',
           'import',
+          'defineVariable',
         ],
         '',
         '',
@@ -437,10 +463,11 @@ export function getAnnotationParent(
   document: vscode.TextDocument,
   tagPosition: vscode.Position,
   nsPrefix: string
-): string {
+): [string, vscode.Position] {
   let pElementText = document.lineAt(tagPosition.line).text
   let iCount = getItemsOnLineCount(pElementText)
   let pElement = ''
+  let pPosition = tagPosition
   let [nElement, newPosition] = getTagNearestTrigger(
     document,
     tagPosition,
@@ -460,7 +487,7 @@ export function getAnnotationParent(
       )
     }
     pElementText = document.lineAt(newPosition.line).text
-    let [nElement] = getTagNearestTrigger(
+    let [nElement, nPosition] = getTagNearestTrigger(
       document,
       newPosition,
       pElementText,
@@ -470,8 +497,9 @@ export function getAnnotationParent(
       nsPrefix
     )
     pElement = nElement
+    pPosition = nPosition
   }
-  return pElement
+  return [pElement, pPosition]
 }
 
 export function getTagNearestTrigger(
diff --git a/src/language/providers/intellisense/elementItems.ts 
b/src/language/providers/intellisense/elementItems.ts
index e3517c6..d364b9b 100644
--- a/src/language/providers/intellisense/elementItems.ts
+++ b/src/language/providers/intellisense/elementItems.ts
@@ -27,6 +27,11 @@ export const elementCompletion = (definedVariables, 
nsPrefix) => {
         item: nsPrefix + 'schema',
         snippetString: '<${1|\0,xs:,xsd:|}$2' + 'schema 
xmlns:xs="http://www.w3.org/2001/xmlSchema"\n\t\txmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/"\n\t\txmlns:daf="urn:ogf:dfdl:2013:imp:daffodil.apache.org:2018:ext"\n\t\txmlns:fn="http:/www.w3.org/2005/xpath-functions"\nelementFormDefault="unqualified"$0',
       },
+      {
+        item: nsPrefix + 'element',
+        snippetString: '<' + nsPrefix + 'element $1"$0',
+        markdownString: 'Defines an xs element',
+      },
       {
         item: nsPrefix + 'element name',
         snippetString: '<' + nsPrefix + 'element name="$1"$0',
@@ -37,6 +42,11 @@ export const elementCompletion = (definedVariables, 
nsPrefix) => {
         snippetString: '<' + nsPrefix + 'element ref="$1"$0',
         markdownString: 'Defines a reference to a declared element',
       },
+      {
+        item: nsPrefix + 'group',
+        snippetString: '<' + nsPrefix + 'group "$1">\n\t$0\n</' + nsPrefix + 
'group>',
+        markdownString: 'Defines a named model group to be reused later',
+      },
       {
         item: nsPrefix + 'group name',
         snippetString: '<' + nsPrefix + 'group name = "$1">\n\t$0\n</' + 
nsPrefix + 'group>',
@@ -76,7 +86,7 @@ export const elementCompletion = (definedVariables, nsPrefix) 
=> {
         markdownString: 'Defines a complex type definition',
       },
       {
-        item: nsPrefix + 'complexType name=',
+        item: nsPrefix + 'complexType name',
         snippetString: '<' + nsPrefix + 'complexType name="$1">\n\t$0\n</' + 
nsPrefix + 'complexType>',
         markdownString: 'Defines a complex type definition',
       },
@@ -86,7 +96,7 @@ export const elementCompletion = (definedVariables, nsPrefix) 
=> {
         markdownString: 'Defines a simple type definition',
       },
       {
-        item: nsPrefix + 'simpleType name=',
+        item: nsPrefix + 'simpleType name',
         snippetString: '<' + nsPrefix + 'simpleType name="$1"$0',
         markdownString: 'Defines simple type definition',
       },
@@ -107,6 +117,11 @@ export const elementCompletion = (definedVariables, 
nsPrefix) => {
       },
       {
         item: 'dfdl:defineVariable',
+        snippetString: '<dfdl:defineVariable "$1"$0',
+        markdownString: 'Defines the name, type, and optionally default value 
for the variable.',
+      },
+      {
+        item: 'dfdl:defineVariable name',
         snippetString: '<dfdl:defineVariable name="$1"$0',
         markdownString: 'Defines the name, type, and optionally default value 
for the variable.',
       },
diff --git a/src/language/providers/utils.ts b/src/language/providers/utils.ts
index 087e608..10e19fd 100644
--- a/src/language/providers/utils.ts
+++ b/src/language/providers/utils.ts
@@ -18,6 +18,7 @@
 import * as vscode from 'vscode'
 import { commonCompletion } from './intellisense/commonItems'
 import { isXPath } from '../semantics/dfdlExt'
+import { xml2js } from 'xml-js'
 
 const schemaPrefixRegEx = new RegExp('</?(|[^ ]+:)schema')
 
@@ -53,6 +54,7 @@ const items = [
 export class XmlItem {
   private _itemName: string = 'none'
   private _itemNS: string = 'none'
+  private _itemAttributes: string[] = []
 
   public get itemName() {
     return this._itemName
@@ -69,6 +71,14 @@ export class XmlItem {
   public set itemNS(nameSpace: string) {
     this._itemNS = nameSpace
   }
+
+  public set itemAttributes(attributeNames: string[]) {
+    this._itemAttributes = attributeNames
+  }
+
+  public get itemAttributes() {
+    return this._itemAttributes
+  }
 }
 
 export function getItems() {
@@ -131,7 +141,7 @@ export function nearestOpen(
   const nsPrefix = getNsPrefix(document, position)
 
   for (let i = 0; i < items.length; ++i) {
-    let [isTagOpen, isDfdlNs] = checkTagOpen(
+    let [isTagOpen, isDfdlNs, attributeNames] = checkTagOpen(
       document,
       position,
       nsPrefix,
@@ -144,6 +154,7 @@ export function nearestOpen(
         xmlItem.itemNS = nsPrefix
       }
       xmlItem.itemName = items[i]
+      xmlItem.itemAttributes = attributeNames
       return xmlItem
     }
   }
@@ -236,7 +247,7 @@ export function checkTagOpen(
   position: vscode.Position,
   nsPrefix: string,
   tag: string
-): [boolean, boolean] {
+): [boolean, boolean, string[]] {
   let triggerLine = position.line
   let triggerText = document.lineAt(triggerLine).text
   let itemsOnLine = getItemsOnLineCount(triggerText)
@@ -245,71 +256,82 @@ export function checkTagOpen(
   let origTriggerLine = triggerLine
   let compareText = triggerText
   let compareLine = triggerLine
+  let attributeNames: string[] = []
   const triggerPos = position.character
-  const textBeforeTrigger = triggerText.substring(0, triggerPos)
 
   while (itemsOnLine < 2 && !triggerText.trim().startsWith('<')) {
     triggerText = document.lineAt(--triggerLine).text
   }
 
-  if (!(triggerText.endsWith('>') && triggerText.includes('<'))) {
-    isMultiLineTag = true
-  }
+  let tagPos = triggerText.lastIndexOf('<' + nsPrefix + tag)
 
-  let tagPos = textBeforeTrigger.lastIndexOf('<' + nsPrefix + tag)
   if (tagPos < 0) {
-    tagPos = textBeforeTrigger.lastIndexOf('<dfdl:' + tag)
+    tagPos = triggerText.lastIndexOf('<dfdl:' + tag)
     if (tagPos > 0) {
       isDfdlPrefix = true
       nsPrefix = 'dfdl:'
     }
   }
-  const nextTagPos = triggerText.indexOf('<', tagPos + 1)
-  let tagEndPos = triggerText.indexOf('>', tagPos)
 
-  if (tagPos > -1 && itemsOnLine > 1) {
-    if (
-      triggerPos > tagPos &&
-      ((triggerPos <= tagEndPos &&
-        (nextTagPos > tagEndPos || nextTagPos === -1)) ||
-        tagEndPos === -1)
-    ) {
-      return [true, isDfdlPrefix]
-    }
+  if (
+    !(
+      triggerText.trim().startsWith('<' + nsPrefix + tag) &&
+      triggerText.endsWith('>')
+    )
+  ) {
+    isMultiLineTag = true
   }
 
-  while (compareText.trim() === '') {
-    compareText = document.lineAt(--compareLine).text
-  }
-  tagPos = triggerText.indexOf('<' + nsPrefix + tag)
-  if (tagPos < 0) {
-    tagPos = textBeforeTrigger.lastIndexOf('<dfdl:' + tag)
-    if (tagPos > 0) {
-      isDfdlPrefix = true
-      nsPrefix = 'dfdl:'
+  if (!isMultiLineTag) {
+    const nextTagPos = triggerText.indexOf('<', tagPos + 1)
+    let tagEndPos = triggerText.indexOf('>', tagPos)
+
+    if (tagPos > -1 && itemsOnLine > 1) {
+      if (
+        triggerPos > tagPos &&
+        ((triggerPos <= tagEndPos &&
+          (nextTagPos > tagEndPos || nextTagPos === -1)) ||
+          tagEndPos === -1)
+      ) {
+        attributeNames = getAttributeNames(document, position, nsPrefix, tag)
+        return [true, isDfdlPrefix, attributeNames]
+      }
     }
-  }
 
-  if (itemsOnLine < 2 && tagPos > -1) {
-    if (triggerText !== compareText) {
-      tagEndPos = compareText.indexOf('>')
+    while (compareText.trim() === '') {
+      compareText = document.lineAt(--compareLine).text
+    }
+    tagPos = triggerText.indexOf('<' + nsPrefix + tag)
+    if (tagPos < 0) {
+      tagPos = triggerText.lastIndexOf('<dfdl:' + tag)
+      if (tagPos > 0) {
+        isDfdlPrefix = true
+        nsPrefix = 'dfdl:'
+      }
     }
 
-    if (
-      (triggerPos > tagPos &&
-        triggerPos <= tagEndPos &&
-        triggerLine === position.line) ||
-      (compareLine == position.line &&
-        triggerPos <= tagEndPos &&
-        triggerPos > tagPos) ||
-      position.line < origTriggerLine
-    ) {
-      return [true, isDfdlPrefix]
+    if (itemsOnLine < 2 && tagPos > -1) {
+      if (triggerText !== compareText) {
+        tagEndPos = compareText.indexOf('>')
+      }
+
+      if (
+        (triggerPos > tagPos &&
+          triggerPos <= tagEndPos &&
+          triggerLine === position.line) ||
+        (compareLine == position.line &&
+          triggerPos <= tagEndPos &&
+          triggerPos > tagPos) ||
+        position.line < origTriggerLine
+      ) {
+        attributeNames = getAttributeNames(document, position, nsPrefix, tag)
+        return [true, isDfdlPrefix, attributeNames]
+      }
     }
   }
 
   if (!isMultiLineTag || tagPos === -1) {
-    return [false, isDfdlPrefix]
+    return [false, isDfdlPrefix, attributeNames]
   }
   //if this tag is part of a multi line set of annotations return true
   //else this tag is not open return false
@@ -362,12 +384,13 @@ export function checkMultiLineTag(
   tagLine: number,
   tag: string,
   isDfdlTag = false
-): [boolean, boolean] {
+): [boolean, boolean, string[]] {
   if (nsPrefix === 'dfdl:') {
     isDfdlTag = true
   }
+  let attributeNames: string[] = []
   if (itemsOnLine > 1) {
-    return [false, isDfdlTag]
+    return [false, isDfdlTag, attributeNames]
   }
   let currentLine = position.line
   let openTagLine = position.line
@@ -375,43 +398,42 @@ export function checkMultiLineTag(
   const origText = document.lineAt(currentLine).text
   let currentText = origText
 
-  //the current line doesn't have the self close symbol
-  if (!currentText.endsWith('/>')) {
-    while (currentText.trim() === '' || !currentText.includes('<')) {
-      --openTagLine
-      currentText = document.lineAt(openTagLine).text
-      if (currentText.includes('/>')) {
-        closeTagLine = openTagLine
-      }
-    }
+  //Get the opening tag
+  while (
+    (currentText.trim() === '' ||
+      !currentText.includes('<' + nsPrefix + tag)) &&
+    openTagLine > 0
+  ) {
+    --openTagLine
+    currentText = document.lineAt(openTagLine).text
+  }
 
-    if (
-      currentText.indexOf('<' + nsPrefix + tag) !== -1 &&
-      currentText.indexOf('>') === -1 &&
-      currentText.indexOf('<' + nsPrefix + tag) &&
-      openTagLine <= position.line &&
-      closeTagLine >= position.line &&
-      (origText.indexOf('>') > position.character ||
-        origText.indexOf('>') === -1)
+  if (currentText.includes('<' + nsPrefix + tag)) {
+    let multiLineText = currentText.trim()
+    let closeText = document.lineAt(openTagLine).text
+
+    closeTagLine = openTagLine
+
+    //Get closing tag
+    while (
+      (closeText.trim() === '' || !closeText.includes('>')) &&
+      closeTagLine < document.lineCount
     ) {
-      return [true, isDfdlTag]
+      ++closeTagLine
+      closeText = document.lineAt(closeTagLine).text
+      multiLineText += ' ' + closeText.trim()
     }
-  }
-
-  if (currentText.endsWith('/>')) {
-    let triggerPos = position.character
-    let tagEndPos = currentText.indexOf('/>')
-    let triggerLine = position.line
+    currentText = multiLineText
 
     if (
-      (triggerLine === currentLine && triggerPos < tagEndPos) ||
-      (triggerLine === tagLine && triggerPos > tagPos && tagPos !== -1) ||
-      triggerLine < currentLine
+      currentText.includes('<' + nsPrefix + tag) &&
+      currentText.includes('>')
     ) {
-      return [true, isDfdlTag]
+      attributeNames = getAttributeNames(document, position, nsPrefix, tag)
+      return [true, isDfdlTag, attributeNames]
     }
   }
-  return [false, isDfdlTag]
+  return [false, isDfdlTag, attributeNames]
 }
 
 //returns an empty value or a prefix plus a colon
@@ -437,6 +459,64 @@ export function getNsPrefix(
   return defaultXsdNsPrefix
 }
 
+export function getAttributeNames(
+  document: vscode.TextDocument,
+  position: vscode.Position,
+  nsPrefix: string,
+  tag: string
+): string[] {
+  let currentLine = position.line
+  let openTagLine = position.line
+  let closeTagLine = position.line
+  let currentText = document.lineAt(currentLine).text
+  let closeText = currentText
+
+  //if mulit-line tag
+  if (
+    !(
+      currentText.trim().startsWith('<' + nsPrefix + tag) &&
+      currentText.endsWith('>')
+    )
+  ) {
+    //Get the opening tag
+    while (
+      (currentText.trim() === '' ||
+        !currentText.includes('<' + nsPrefix + tag)) &&
+      openTagLine > -1
+    ) {
+      --openTagLine
+      currentText = document.lineAt(openTagLine).text
+    }
+
+    let multiLineText = currentText.trim()
+
+    closeTagLine = openTagLine
+
+    //Get closing tag
+    closeText = document.lineAt(openTagLine).text
+    while (
+      (closeText.trim() === '' || !closeText.includes('>')) &&
+      closeTagLine < document.lineCount
+    ) {
+      ++closeTagLine
+      closeText = document.lineAt(closeTagLine).text
+      multiLineText += ' ' + closeText.trim()
+    }
+    currentText = multiLineText
+  }
+
+  let attributeNames: string[] = []
+  const xmljs = xml2js(currentText, {})
+  const attributes = xmljs.elements?.[0].attributes
+  if (attributes) {
+    const attributeSet: Set<string> = new Set(Object.keys(attributes))
+    attributeNames = [...attributeSet]
+    return attributeNames
+  }
+
+  return attributeNames
+}
+
 export function getItemsOnLineCount(triggerText: String) {
   let itemsOnLine = 0
   let nextPos = 0
diff --git a/src/tests/DfdlIntellisenseTestingChecklist.md 
b/src/tests/DfdlIntellisenseTestingChecklist.md
index 17445f3..4b4ebdf 100644
--- a/src/tests/DfdlIntellisenseTestingChecklist.md
+++ b/src/tests/DfdlIntellisenseTestingChecklist.md
@@ -147,7 +147,7 @@ Select alignment from the dropdown. select 1 as the value 
for alignment.
 
 Place the cursor at line 33 column 1. Type CTRL+space.
 
-- [ ] Verify the dropdown contains dfdl:defineEscapeScheme, dfdl:defineFormat, 
dfdl:defineVariable,and dfdl:format.
+- [ ] Verify the dropdown contains dfdl:defineEscapeScheme, dfdl:defineFormat, 
,and dfdl:format.
 
 Place the cursor at line 52 column 56 at the end of the line. Backspace over 
the last two characters "/>". The cursor should be directly after 
encoding="ascii". Type a / (slash '/').
 
diff --git a/src/tests/suite/language/items.test.ts 
b/src/tests/suite/language/items.test.ts
index d4053bb..e959568 100644
--- a/src/tests/suite/language/items.test.ts
+++ b/src/tests/suite/language/items.test.ts
@@ -24,8 +24,10 @@ suite('Items Test Suite', () => {
   const expectedElementItems = [
     'xml version',
     'schema',
+    'element',
     'element name',
     'element ref',
+    'group',
     'group name',
     'group ref',
     'dfdl:assert',
@@ -34,13 +36,14 @@ suite('Items Test Suite', () => {
     'annotation',
     'appinfo',
     'complexType',
-    'complexType name=',
+    'complexType name',
     'simpleType',
-    'simpleType name=',
+    'simpleType name',
     'sequence',
     'choice',
     'dfdl:newVariableInstance',
     'dfdl:defineVariable',
+    'dfdl:defineVariable name',
     'dfdl:setVariable',
     'dfdl:defineFormat',
     'dfdl:defineEscapeScheme',

Reply via email to