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

shanedell 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 ca76938    Significant modifications made to intellisense results     
- context logic change to use line and cursor position at trigger     - 
functions developed to:       - determine the tag at the context position       
- determine if the tag is open       - determine if the trigger occured between 
tags       - determine if a close tag is missing
ca76938 is described below

commit ca769382f3e84a24ef5132adb7761d0e7b8defaa
Author: rt320 <[email protected]>
AuthorDate: Mon Feb 20 08:42:31 2023 -0500

      Significant modifications made to intellisense results
        - context logic change to use line and cursor position at trigger
        - functions developed to:
          - determine the tag at the context position
          - determine if the tag is open
          - determine if the trigger occured between tags
          - determine if a close tag is missing
    
        - Additional modifications made to:
          - provide above functionality if line contains multiple tags
          - provide intellisense based on tag at context position
          - simplify the logic to determine context
          - minimize if/else logic
          - fixes close tag completion for schema when close tag is missing
          - fixes tag end symbol missing problem in issue 459
    
        - Also fixes a bug with context when intellisense is trigger at the
        start of the line of a multi-tag line, and also if trigger at the
        end of the line of a multi-tag line
    
        - Incorporates modifications made by Shane Dell
    
        Closes #438
        Closes #459
        Closes #460
---
 src/language/providers/attributeCompletion.ts      | 358 +++++++++----------
 src/language/providers/closeElement.ts             | 223 +++++++-----
 src/language/providers/closeElementSlash.ts        | 129 +++++--
 src/language/providers/closeUtils.ts               | 283 +++++++++++++++
 src/language/providers/elementCompletion.ts        | 256 ++++++++++++--
 .../providers/intellisense/attributeItems.ts       |  10 +-
 .../providers/intellisense/elementItems.ts         |  48 ++-
 src/language/providers/utils.ts                    | 389 ++++++++++-----------
 src/tests/suite/language/items.test.ts             |  38 +-
 9 files changed, 1160 insertions(+), 574 deletions(-)

diff --git a/src/language/providers/attributeCompletion.ts 
b/src/language/providers/attributeCompletion.ts
index 0735174..a2235fd 100644
--- a/src/language/providers/attributeCompletion.ts
+++ b/src/language/providers/attributeCompletion.ts
@@ -21,13 +21,10 @@ import {
   nearestOpen,
   checkBraceOpen,
   lineCount,
-  checkLastItemOpen,
-  checkSequenceOpen,
-  checkElementOpen,
-  checkSimpleTypeOpen,
   createCompletionItem,
   getCommonItems,
   getXsdNsPrefix,
+  getItemsOnLineCount,
 } from './utils'
 
 import { attributeCompletion } from './intellisense/attributeItems'
@@ -66,192 +63,31 @@ export function getAttributeCompletionProvider() {
         const triggerText = document
           .lineAt(position)
           .text.substring(0, position.character)
-        var nearestOpenItem = nearestOpen(document, position)
+        let nearestOpenItem = nearestOpen(document, position)
+        let itemsOnLine = getItemsOnLineCount(triggerText)
         const nsPrefix = getXsdNsPrefix(document, position)
+        let additionalItems = getDefinedTypes(document, nsPrefix)
 
         if (
           !checkBraceOpen(document, position) &&
           !triggerText.includes('assert') &&
           !nearestOpenItem.includes('none')
         ) {
-          if (nearestOpenItem.includes('element')) {
-            var preVal = ''
-            if (!triggerText.includes(nsPrefix + 'element')) {
-              if (lineCount(document, position) === 1) {
-                preVal = '\t'
-              } else {
-                preVal = ''
-              }
-            }
-            var additionalItems = getDefinedTypes(document, nsPrefix)
+          let preVal =
+            !triggerText.includes('<' + nsPrefix + nearestOpenItem) &&
+            lineCount(document, position, nearestOpenItem) === 1 &&
+            itemsOnLine < 2
+              ? '\t'
+              : ''
 
-            if (
-              checkLastItemOpen(document, position) &&
-              (triggerText.includes('<' + nsPrefix + 'element name="') ||
-                triggerText.includes('<' + nsPrefix + 'element ref="') ||
-                checkElementOpen(document, position))
-            ) {
-              return getCompletionItems(
-                [
-                  'dfdl:defineFormat',
-                  'dfdl:defineEscapeScheme',
-                  'type=',
-                  'minOccurs=',
-                  'maxOccurs=',
-                  'dfdl:occursCount=',
-                  'dfdl:byteOrder=',
-                  'dfdl:occursCountKind=',
-                  'dfdl:length=',
-                  'dfdl:lengthKind=',
-                  'dfdl:encoding=',
-                  'dfdl:alignment=',
-                  'dfdl:lengthUnits=',
-                  'dfdl:lengthPattern=',
-                  'dfdl:inputValueCalc=',
-                  'dfdl:outputValueCalc=',
-                  'dfdl:alignmentUnits=',
-                  'dfdl:terminator=',
-                  'dfdl:outputNewLine=',
-                  'dfdl:choiceBranchKey=',
-                  'dfdl:representation',
-                ],
-                preVal,
-                additionalItems,
-                nsPrefix
-              )
-            }
-          }
-
-          if (nearestOpenItem.includes('sequence')) {
-            var preVal = ''
-            if (!triggerText.includes(nsPrefix + 'sequence')) {
-              if (lineCount(document, position) === 1) {
-                preVal = '\t'
-              } else {
-                preVal = ''
-              }
-            }
-
-            if (
-              checkLastItemOpen(document, position) &&
-              (triggerText.includes('<' + nsPrefix + 'sequence') ||
-                checkSequenceOpen(document, position))
-            ) {
-              return getCompletionItems(
-                [
-                  'dfdl:hiddenGroupRef=',
-                  'dfdl:sequenceKind=',
-                  'dfdl:separator=',
-                  'dfdl:separatorPosition=',
-                  'dfdl:separatorSuppressionPolicy',
-                ],
-                preVal,
-                undefined,
-                nsPrefix
-              )
-            }
-          }
-
-          if (triggerText.includes('choice')) {
-            if (!triggerText.includes('>')) {
-              return getCompletionItems(
-                [
-                  'dfdl:choiceLengthKind=',
-                  'dfdl:choiceLength=',
-                  'dfdl:initiatedContent=',
-                  'dfdl:choiceDispatchKey=',
-                  'dfdl:choiceBranchKey=',
-                ],
-                undefined,
-                undefined,
-                nsPrefix
-              )
-            }
-          }
-
-          if (
-            triggerText.includes('simpleType') ||
-            checkSimpleTypeOpen(document, position)
-          ) {
-            if (!triggerText.includes('>')) {
-              return getCompletionItems(
-                [
-                  'dfdl:length=',
-                  'dfdl:lengthKind=',
-                  'dfdl:simpleType',
-                  'dfdl:simpleType',
-                  nsPrefix + 'restriction',
-                ],
-                undefined,
-                undefined,
-                nsPrefix
-              )
-            }
-          }
-
-          if (triggerText.includes('defineVariable')) {
-            var preVal = ''
-            if (!triggerText.includes('dfdl:defineVariable')) {
-              if (lineCount(document, position) === 1) {
-                preVal = '\t'
-              } else {
-                preVal = ''
-              }
-            }
-            var additionalItems = getDefinedTypes(document, nsPrefix)
-
-            var xmlItems = [
-              {
-                item: 'external=',
-                snippetString: preVal + 'external="${1|true,false|}"$0',
-              },
-              {
-                item: 'defaultValue=',
-                snippetString: preVal + 'defaultValue="0$1"$0',
-              },
-            ]
-
-            if (!triggerText.includes('>')) {
-              let compItems: vscode.CompletionItem[] = []
-              xmlItems.forEach((e) => {
-                const completionItem = new vscode.CompletionItem(e.item)
-                completionItem.insertText = new vscode.SnippetString(
-                  e.snippetString
-                )
-
-                compItems.push(completionItem)
-              })
-
-              getCommonItems(['type='], '', additionalItems, nsPrefix).forEach(
-                (ci) => {
-                  compItems.push(ci)
-                }
-              )
-
-              return compItems
-            }
-          }
-
-          if (nearestOpenItem.includes('setVariable')) {
-            var preVal = ''
-            if (!triggerText.includes('dfdl:setVariable')) {
-              if (lineCount(document, position) === 1) {
-                preVal = '\t'
-              } else {
-                preVal = ''
-              }
-            }
-
-            const xmlValue = new vscode.CompletionItem('value=')
-            xmlValue.insertText = new vscode.SnippetString('value="$1"$0')
-            xmlValue.documentation = new vscode.MarkdownString('')
-
-            if (!triggerText.includes('>')) {
-              return [xmlValue]
-            }
-          }
+          return checkNearestOpenItem(
+            nearestOpenItem,
+            triggerText,
+            nsPrefix,
+            preVal,
+            additionalItems
+          )
         }
-        return undefined
       },
     },
     ' ',
@@ -260,24 +96,174 @@ export function getAttributeCompletionProvider() {
 }
 
 function getDefinedTypes(document: vscode.TextDocument, nsPrefix: string) {
-  var additionalTypes = ''
-  var lineNum = 0
+  let additionalTypes = ''
+  let lineNum = 0
   const lineCount = document.lineCount
 
   while (lineNum !== lineCount) {
     const triggerText = document
       .lineAt(lineNum)
       .text.substring(0, document.lineAt(lineNum).range.end.character)
+
     if (
       triggerText.includes(nsPrefix + 'simpleType Name=') ||
       triggerText.includes(nsPrefix + 'complexType Name=')
     ) {
-      var startPos = triggerText.indexOf('"', 0)
-      var endPos = triggerText.indexOf('"', startPos + 1)
-      var newType = triggerText.substring(startPos + 1, endPos)
+      let startPos = triggerText.indexOf('"', 0)
+      let endPos = triggerText.indexOf('"', startPos + 1)
+      let newType = triggerText.substring(startPos + 1, endPos)
+
       additionalTypes = String(additionalTypes + ',' + newType)
     }
+
     ++lineNum
   }
+
   return additionalTypes
 }
+
+function checkNearestOpenItem(
+  nearestOpenItem: string,
+  triggerText: string,
+  nsPrefix: string,
+  preVal: string,
+  additionalItems: string
+): vscode.CompletionItem[] | undefined {
+  switch (nearestOpenItem) {
+    case 'element':
+      return getCompletionItems(
+        [
+          'name=',
+          'ref=',
+          'dfdl:defineFormat',
+          'dfdl:defineEscapeScheme',
+          'type=',
+          'minOccurs=',
+          'maxOccurs=',
+          'dfdl:occursCount=',
+          'dfdl:byteOrder=',
+          'dfdl:occursCountKind=',
+          'dfdl:length=',
+          'dfdl:lengthKind=',
+          'dfdl:encoding=',
+          'dfdl:alignment=',
+          'dfdl:lengthUnits=',
+          'dfdl:lengthPattern=',
+          'dfdl:inputValueCalc=',
+          'dfdl:outputValueCalc=',
+          'dfdl:alignmentUnits=',
+          'dfdl:terminator=',
+          'dfdl:outputNewLine=',
+          'dfdl:choiceBranchKey=',
+          'dfdl:representation',
+        ],
+        preVal,
+        additionalItems,
+        nsPrefix
+      )
+    case 'sequence':
+      return getCompletionItems(
+        [
+          'dfdl:hiddenGroupRef=',
+          'dfdl:sequenceKind=',
+          'dfdl:separator=',
+          'dfdl:separatorPosition=',
+          'dfdl:separatorSuppressionPolicy',
+        ],
+        preVal,
+        undefined,
+        nsPrefix
+      )
+    case 'choice':
+      return getCompletionItems(
+        [
+          'dfdl:choiceLengthKind=',
+          'dfdl:choiceLength=',
+          'dfdl:initiatedContent=',
+          'dfdl:choiceDispatchKey=',
+          'dfdl:choiceBranchKey=',
+        ],
+        undefined,
+        undefined,
+        nsPrefix
+      )
+    case 'group':
+      return getCompletionItems(
+        ['ref=', 'name='],
+        undefined,
+        undefined,
+        nsPrefix
+      )
+
+    case 'simpleType':
+      return getCompletionItems(
+        [
+          'dfdl:length=',
+          'dfdl:lengthKind=',
+          'dfdl:simpleType',
+          'dfdl:simpleType',
+          nsPrefix + 'restriction',
+        ],
+        undefined,
+        undefined,
+        nsPrefix
+      )
+    case 'format':
+      return getCompletionItems(
+        [
+          'dfdl:byteOrder=',
+          'dfdl:bitOrder=',
+          'dfdl:encoding=',
+          'dfdl:initiator=',
+          'dfdl:outputNewLine=',
+          'dfdl:separator=',
+          'dfdl:terminator=',
+          nsPrefix + 'restriction',
+        ],
+        undefined,
+        undefined,
+        nsPrefix
+      )
+
+    case 'defineVariable':
+      return getDefineVariableCompletionItems(preVal, additionalItems, 
nsPrefix)
+    case 'setVariable':
+      const xmlValue = new vscode.CompletionItem('value=')
+      xmlValue.insertText = new vscode.SnippetString('value="$1"$0')
+      xmlValue.documentation = new vscode.MarkdownString('')
+      return undefined
+    default:
+      return undefined
+  }
+}
+
+function getDefineVariableCompletionItems(
+  preVal: string,
+  additionalItems: string,
+  nsPrefix: string
+): vscode.CompletionItem[] {
+  let xmlItems = [
+    {
+      item: 'external=',
+      snippetString: preVal + 'external="${1|true,false|}"$0',
+    },
+    {
+      item: 'defaultValue=',
+      snippetString: preVal + 'defaultValue="0$1"$0',
+    },
+  ]
+
+  let compItems: vscode.CompletionItem[] = []
+  xmlItems.forEach((e) => {
+    const completionItem = new vscode.CompletionItem(e.item)
+    completionItem.insertText = new vscode.SnippetString(e.snippetString)
+
+    compItems.push(completionItem)
+  })
+
+  getCommonItems(['type='], '', additionalItems, nsPrefix).forEach((ci) => {
+    compItems.push(ci)
+  })
+
+  return compItems
+}
diff --git a/src/language/providers/closeElement.ts 
b/src/language/providers/closeElement.ts
index 19c8ac0..357e7ba 100644
--- a/src/language/providers/closeElement.ts
+++ b/src/language/providers/closeElement.ts
@@ -16,114 +16,161 @@
  */
 
 import * as vscode from 'vscode'
-import { getXsdNsPrefix, insertSnippet, nearestOpen } from './utils'
+import { checkMissingCloseTag } from './closeUtils'
+import {
+  getXsdNsPrefix,
+  insertSnippet,
+  getItemsOnLineCount,
+  getItemPrefix,
+} from './utils'
 
 export function getCloseElementProvider() {
   return vscode.languages.registerCompletionItemProvider(
     'dfdl',
     {
-      provideCompletionItems(
+      async provideCompletionItems(
         document: vscode.TextDocument,
         position: vscode.Position
       ) {
-        var backpos = position.with(position.line, position.character - 1)
-        const nsPrefix = getXsdNsPrefix(document, position)
-        const nearestOpenItem = nearestOpen(document, position)
+        let backpos = position.with(position.line, position.character)
+        let backpos3 = position.with(position.line, position.character)
+
+        if (position.character > 0) {
+          backpos = position.with(position.line, position.character - 1)
+        }
+        if (position.character > 2) {
+          backpos3 = position.with(position.line, position.character - 3)
+        }
+        let nsPrefix = getXsdNsPrefix(document, position)
+        const origPrefix = nsPrefix
+
+        const nearestTagNotClosed = checkMissingCloseTag(
+          document,
+          position,
+          nsPrefix
+        )
+        nsPrefix = getItemPrefix(nearestTagNotClosed, origPrefix)
         const triggerText = document
           .lineAt(position)
           .text.substring(0, position.character)
 
+        let itemsOnLine = getItemsOnLineCount(triggerText)
+
+        if (nearestTagNotClosed.includes('none')) {
+          return undefined
+        }
+
         if (
-          !triggerText.includes('</') &&
-          triggerText.endsWith('>') &&
-          (triggerText.includes('<' + nsPrefix + 'element') ||
-            nearestOpenItem.includes('element') ||
-            triggerText.includes('<' + nsPrefix + 'group') ||
-            nearestOpenItem.includes('group') ||
-            triggerText.includes('<' + nsPrefix + 'sequence') ||
-            nearestOpenItem.includes('sequence') ||
-            triggerText.includes('<' + nsPrefix + 'simpleType') ||
-            nearestOpenItem.includes('simpleType') ||
-            triggerText.includes('<' + nsPrefix + 'choice') ||
-            nearestOpenItem.includes('choice') ||
-            triggerText.includes('dfdl:defineVariable') ||
-            nearestOpenItem.includes('Variable'))
+          (triggerText.endsWith('>') && itemsOnLine < 2) ||
+          (triggerText.endsWith('>>') && itemsOnLine > 1) ||
+          (triggerText.endsWith('.=>') && itemsOnLine === 0)
         ) {
-          var range = new vscode.Range(backpos, position)
-          vscode.window.activeTextEditor?.edit((editBuilder) => {
+          let range = new vscode.Range(backpos, position)
+
+          await vscode.window.activeTextEditor?.edit((editBuilder) => {
             editBuilder.replace(range, '')
           })
-          if (
-            triggerText.endsWith('>') &&
-            (triggerText.includes('<' + nsPrefix + 'element ref') ||
-              triggerText.includes('<' + nsPrefix + 'group ref'))
-          ) {
-            insertSnippet(' />\n$0', backpos)
-          } else if (
-            triggerText.endsWith('>') &&
-            (triggerText.includes('<' + nsPrefix + 'element') ||
-              nearestOpenItem.includes('element'))
-          ) {
-            insertSnippet('>\n\t$0\n</' + nsPrefix + 'element>', backpos)
-          } else if (
-            triggerText.endsWith('>') &&
-            (triggerText.includes('<' + nsPrefix + 'group') ||
-              nearestOpenItem.includes('group'))
-          ) {
-            insertSnippet('>\n\t$0\n</' + nsPrefix + 'group>', backpos)
-          } else if (
-            (triggerText.endsWith('>') &&
-              triggerText.includes('<' + nsPrefix + 'sequence')) ||
-            nearestOpenItem.includes('sequence')
-          ) {
-            insertSnippet('>\n\t$0\n</' + nsPrefix + 'sequence>', backpos)
-          } else if (
-            (triggerText.endsWith('>') &&
-              triggerText.includes('<' + nsPrefix + 'choice')) ||
-            nearestOpenItem.includes('choice')
-          ) {
-            insertSnippet('>\n\t$0\n</' + nsPrefix + 'choice>', backpos)
-          } else if (
-            (triggerText.endsWith('>') &&
-              triggerText.includes('<' + nsPrefix + 'simpleType')) ||
-            nearestOpenItem.includes('simpleType')
-          ) {
-            insertSnippet('>\n\t$0\n</' + nsPrefix + 'simpleType>', backpos)
-          } else if (
-            (triggerText.endsWith('>') &&
-              triggerText.includes('dfdl:defineVariable')) ||
-            nearestOpenItem.includes('defineVariable')
-          ) {
-            var startPos = document.lineAt(position).text.indexOf('<', 0)
-            var range = new vscode.Range(backpos, position)
-            vscode.window.activeTextEditor?.edit((editBuilder) => {
-              editBuilder.replace(range, '')
-            })
-            insertSnippet('>\n</dfdl:defineVariable>\n', backpos)
-            var backpos2 = position.with(position.line + 2, startPos - 2)
-            insertSnippet('</<' + nsPrefix + 'appinfo>\n', backpos2)
-            var backpos3 = position.with(position.line + 3, startPos - 4)
-            insertSnippet('</<' + nsPrefix + 'annotation>$0', backpos3)
-          } else if (
-            (triggerText.endsWith('>') &&
-              triggerText.includes('dfdl:setVariable')) ||
-            nearestOpenItem.includes('setVariable')
-          ) {
-            var startPos = document.lineAt(position).text.indexOf('<', 0)
-            var range = new vscode.Range(backpos, position)
-            vscode.window.activeTextEditor?.edit((editBuilder) => {
-              editBuilder.replace(range, '')
-            })
-            insertSnippet('>\n</dfdl:setVariable>\n', backpos)
-            var backpos2 = position.with(position.line + 2, startPos - 2)
-            insertSnippet('</' + nsPrefix + 'appinfo>\n', backpos2)
-            var backpos3 = position.with(position.line + 3, startPos - 4)
-            insertSnippet('</' + nsPrefix + 'annotation>$0', backpos3)
-          }
+
+          checkItemsOnLine(
+            document,
+            position,
+            range,
+            itemsOnLine,
+            triggerText,
+            nsPrefix,
+            nearestTagNotClosed,
+            backpos,
+            backpos3
+          )
         }
+
         return undefined
       },
     },
     '>' // triggered whenever a '>' is typed
   )
 }
+
+function checkItemsOnLine(
+  document: vscode.TextDocument,
+  position: vscode.Position,
+  range: vscode.Range,
+  itemsOnLine: number,
+  triggerText: string,
+  nsPrefix: string,
+  nearestTagNotClosed: string,
+  backpos: vscode.Position,
+  backpos3: vscode.Position
+) {
+  if (itemsOnLine == 0 && !triggerText.includes('</')) {
+    insertSnippet('</' + nsPrefix + nearestTagNotClosed + '>$0', backpos3)
+  }
+
+  if (itemsOnLine === 1 && !triggerText.includes('</')) {
+    checkNearestTagNotClosed(
+      document,
+      position,
+      range,
+      nearestTagNotClosed,
+      backpos,
+      nsPrefix
+    )
+  }
+
+  if (itemsOnLine > 1) {
+    checkTriggerText(triggerText, nsPrefix, backpos, nearestTagNotClosed)
+  }
+}
+
+function checkNearestTagNotClosed(
+  document: vscode.TextDocument,
+  position: vscode.Position,
+  range: vscode.Range,
+  nearestTagNotClosed: string,
+  backpos: vscode.Position,
+  nsPrefix: string
+) {
+  if (
+    nearestTagNotClosed.includes('defineVariable') ||
+    nearestTagNotClosed.includes('setVariable')
+  ) {
+    let startPos = document.lineAt(position).text.indexOf('<', 0)
+
+    insertSnippet('>\n</dfdl:' + nearestTagNotClosed + '>', backpos)
+
+    let backpos2 = position.with(position.line + 2, startPos - 2)
+    insertSnippet('</' + nsPrefix + 'appinfo>\n', backpos2)
+
+    let backpos3 = position.with(position.line + 3, startPos - 4)
+    insertSnippet('</' + nsPrefix + 'annotation>$0', backpos3)
+  }
+
+  if (!nearestTagNotClosed.includes('Variable')) {
+    insertSnippet('>\n</' + nsPrefix + nearestTagNotClosed + '>', backpos)
+  }
+}
+
+function checkTriggerText(
+  triggerText: string,
+  nsPrefix: string,
+  backpos: vscode.Position,
+  nearestTagNotClosed: string
+) {
+  if (triggerText.includes('<' + nsPrefix + nearestTagNotClosed)) {
+    let tagPos = triggerText.lastIndexOf('<' + nsPrefix + nearestTagNotClosed)
+    let tagEndPos = triggerText.indexOf('>', tagPos)
+    if (
+      tagPos != -1 &&
+      !triggerText.substring(tagEndPos - 1, 2).includes('/>') &&
+      !triggerText
+        .substring(tagEndPos)
+        .includes('</' + nsPrefix + nearestTagNotClosed)
+    ) {
+      if (triggerText.endsWith('>>')) {
+        insertSnippet('</' + nsPrefix + nearestTagNotClosed + '>$0', backpos)
+      } else {
+        insertSnippet('></' + nsPrefix + nearestTagNotClosed + '>$0', backpos)
+      }
+    }
+  }
+}
diff --git a/src/language/providers/closeElementSlash.ts 
b/src/language/providers/closeElementSlash.ts
index 721f7f0..d417900 100644
--- a/src/language/providers/closeElementSlash.ts
+++ b/src/language/providers/closeElementSlash.ts
@@ -16,66 +16,125 @@
  */
 
 import * as vscode from 'vscode'
+import { checkMissingCloseTag } from './closeUtils'
 import {
   insertSnippet,
-  nearestOpen,
   checkBraceOpen,
   getXsdNsPrefix,
+  getItemPrefix,
+  getItemsOnLineCount,
 } from './utils'
 
 export function getCloseElementSlashProvider() {
   return vscode.languages.registerCompletionItemProvider(
     'dfdl',
     {
-      provideCompletionItems(
+      async provideCompletionItems(
         document: vscode.TextDocument,
         position: vscode.Position
       ) {
-        var backpos = position.with(position.line, position.character - 1)
+        let backpos = position.with(position.line, position.character - 1)
         const nsPrefix = getXsdNsPrefix(document, position)
         const triggerText = document
           .lineAt(position)
           .text.substring(0, position.character)
-        const nearestOpenItem = nearestOpen(document, position)
+        let nearestTagNotClosed = checkMissingCloseTag(
+          document,
+          position,
+          nsPrefix
+        )
+        const itemsOnLine = getItemsOnLineCount(triggerText)
+
         if (checkBraceOpen(document, position)) {
           return undefined
         }
-        if (
-          triggerText.endsWith('/') &&
-          (triggerText.includes('<' + nsPrefix + 'element') ||
-            nearestOpenItem.includes('element') ||
-            triggerText.includes('<' + nsPrefix + 'group') ||
-            nearestOpenItem.includes('group') ||
-            triggerText.includes('<' + nsPrefix + 'sequence') ||
-            nearestOpenItem.includes('sequence'))
-        ) {
-          var range = new vscode.Range(backpos, position)
-          vscode.window.activeTextEditor?.edit((editBuilder) => {
-            editBuilder.replace(range, '')
-          })
-          insertSnippet(' />$0', backpos)
-        }
-        if (
-          triggerText.endsWith('/') &&
-          (triggerText.includes('dfdl:defineVariable') ||
-            triggerText.includes('dfdl:setVariable') ||
-            nearestOpenItem.includes('defineVariable') ||
-            nearestOpenItem.includes('setVariable'))
-        ) {
-          var startPos = document.lineAt(position).text.indexOf('<', 0)
-          var range = new vscode.Range(backpos, position)
-          vscode.window.activeTextEditor?.edit((editBuilder) => {
+
+        if (triggerText.endsWith('/')) {
+          let range = new vscode.Range(backpos, position)
+
+          await vscode.window.activeTextEditor?.edit((editBuilder) => {
             editBuilder.replace(range, '')
           })
-          insertSnippet('/>\n', backpos)
-          var backpos2 = position.with(position.line + 1, startPos - 2)
-          insertSnippet('</<' + nsPrefix + 'appinfo>\n', backpos2)
-          var backpos3 = position.with(position.line + 2, startPos - 4)
-          insertSnippet('</<' + nsPrefix + 'annotation>$0', backpos3)
+
+          checkItemsOnLine(
+            document,
+            position,
+            itemsOnLine,
+            nearestTagNotClosed,
+            backpos,
+            nsPrefix,
+            triggerText
+          )
         }
+
         return undefined
       },
     },
-    '/' // triggered whenever a '/' is typed
+    '/'
+    // triggered whenever a '/' is typed
   )
 }
+
+function checkItemsOnLine(
+  document: vscode.TextDocument,
+  position: vscode.Position,
+  itemsOnLine: number,
+  nearestTagNotClosed: string,
+  backpos: vscode.Position,
+  nsPrefix: string,
+  triggerText: string
+) {
+  nsPrefix = getItemPrefix(nearestTagNotClosed, nsPrefix)
+  if (itemsOnLine == 1 || itemsOnLine == 0) {
+    if (itemsOnLine == 1) {
+      insertSnippet('/>$0', backpos)
+    }
+    if (itemsOnLine == 0) {
+      const backpos3 = position.with(position.line, position.character - 3)
+      insertSnippet('</' + nsPrefix + nearestTagNotClosed + '>$0', backpos3)
+    }
+
+    if (
+      nearestTagNotClosed.includes('defineVariable') ||
+      nearestTagNotClosed.includes('setVariable')
+    ) {
+      let startPos = document.lineAt(position).text.indexOf('<', 0)
+      let range = new vscode.Range(backpos, position)
+      vscode.window.activeTextEditor?.edit((editBuilder) => {
+        editBuilder.replace(range, '')
+      })
+
+      insertSnippet('/>\n', backpos)
+
+      let backpos2 = position.with(position.line + 1, startPos - 2)
+      insertSnippet('</' + nsPrefix + 'appinfo>\n', backpos2)
+
+      let backpos3 = position.with(position.line + 2, startPos - 4)
+      insertSnippet('</' + nsPrefix + 'annotation>$0', backpos3)
+    }
+  }
+
+  if (itemsOnLine > 1) {
+    if (
+      triggerText.endsWith('/') &&
+      triggerText.includes('<' + nsPrefix + nearestTagNotClosed)
+    ) {
+      let tagPos = triggerText.lastIndexOf('<' + nsPrefix + 
nearestTagNotClosed)
+      let tagEndPos = triggerText.indexOf('>', tagPos)
+      if (
+        tagPos != -1 &&
+        !triggerText.substring(tagEndPos - 1, 2).includes('/>') &&
+        triggerText
+          .substring(backpos.character - 1, backpos.character)
+          .includes('>') &&
+        !triggerText
+          .substring(tagEndPos)
+          .includes('</' + nsPrefix + nearestTagNotClosed)
+      ) {
+        insertSnippet('</' + nsPrefix + nearestTagNotClosed + '>$0', backpos)
+      } else {
+        insertSnippet('/>$0', backpos)
+      }
+    }
+  }
+}
diff --git a/src/language/providers/closeUtils.ts 
b/src/language/providers/closeUtils.ts
new file mode 100644
index 0000000..9bb75bb
--- /dev/null
+++ b/src/language/providers/closeUtils.ts
@@ -0,0 +1,283 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import * as vscode from 'vscode'
+import { getItemsOnLineCount, getItemPrefix, getItems } from './utils'
+
+export function checkMissingCloseTag(
+  document: vscode.TextDocument,
+  position: vscode.Position,
+  nsPrefix: string
+) {
+  const triggerLine = position.line
+  const triggerPos = position.character
+  const triggerText = document.lineAt(triggerLine).text
+  const itemsOnLine = getItemsOnLineCount(triggerText)
+  const origPrefix = nsPrefix
+
+  const items = getItems()
+  for (let i = 0; i < items.length; ++i) {
+    const textBeforeTrigger = triggerText.substring(0, triggerPos)
+
+    nsPrefix = getItemPrefix(items[i], origPrefix)
+
+    if (itemsOnLine > 1) {
+      if (textBeforeTrigger.lastIndexOf('<' + nsPrefix + items[i]) > -1) {
+        let gt1res = getItemsForLineGT1(
+          triggerText,
+          triggerPos,
+          nsPrefix,
+          items,
+          i
+        )
+
+        if (gt1res != undefined) {
+          return gt1res
+        }
+      }
+    }
+
+    if (itemsOnLine < 2) {
+      let lt2res = getItemsForLineLT2(
+        document,
+        triggerText,
+        triggerLine,
+        nsPrefix,
+        items,
+        i
+      )
+
+      if (lt2res != undefined) {
+        return lt2res
+      }
+    }
+  }
+
+  return 'none'
+}
+
+export function getCloseTag(
+  document: vscode.TextDocument,
+  position: vscode.Position,
+  nsPrefix: string,
+  tag: string,
+  startLine: number,
+  startPos: number
+): [string, number, number] {
+  let lineNum = startLine
+  let tagOpen = startPos
+  const triggerLine = position.line
+  const triggerPos = position.character
+  const triggerText = document.lineAt(startLine).text
+  const itemsOnLine = getItemsOnLineCount(document.lineAt(lineNum).text)
+  let endPos = triggerText.lastIndexOf('>')
+  nsPrefix = getItemPrefix(tag, nsPrefix)
+
+  if (itemsOnLine > 1 && startPos < endPos) {
+    while (tagOpen > -1 && tagOpen <= triggerPos) {
+      tagOpen = triggerText.indexOf('<', tagOpen)
+      let tagClose = triggerText.indexOf('>', tagOpen)
+      let tagPart = triggerText.substring(tagOpen, tagClose)
+
+      if (
+        tagPart.includes(tag) &&
+        (tagPart.includes('</') || tagPart.includes('/>'))
+      ) {
+        return [tag, startLine, tagOpen]
+      }
+
+      tagOpen = tagClose + 1
+    }
+  } else {
+    let endPos = triggerText.indexOf('>', startPos)
+
+    if (
+      (triggerText.includes('</') || triggerText.includes('/>')) &&
+      triggerText.includes(tag) &&
+      endPos > -1 &&
+      itemsOnLine < 2
+    ) {
+      return [tag, startLine, startPos]
+    }
+
+    while (lineNum > -1 && lineNum < document.lineCount) {
+      let currentText = document.lineAt(lineNum).text
+      startPos = currentText.indexOf('<')
+
+      if (getItemsOnLineCount(currentText) < 2) {
+        //skip lines until the close tag for this item
+        if (
+          currentText.includes('<' + nsPrefix + tag) &&
+          (currentText.includes('>') || tag === 'schema') &&
+          !currentText.includes('/>')
+        ) {
+          //skipping to closing tag
+          while (!currentText.includes('</' + nsPrefix + tag)) {
+            currentText = document.lineAt(++lineNum).text
+            if (getItemsOnLineCount(currentText) > 1) {
+              currentText = document.lineAt(++lineNum).text
+            }
+          }
+        }
+
+        if (currentText.includes('</' + nsPrefix + tag)) {
+          //if the cursor is after the closing tag
+          if (
+            lineNum == triggerLine &&
+            currentText.indexOf('>', triggerPos) === -1
+          ) {
+            return ['none', lineNum, startPos]
+          }
+          return [tag, lineNum, startPos]
+        }
+      }
+
+      ++lineNum
+    }
+  }
+
+  return ['none', 0, 0]
+}
+
+export function getItemsForLineGT1(
+  triggerText: string,
+  triggerPos: number,
+  nsPrefix: string,
+  items: string[],
+  i: number
+) {
+  let openTagArray: number[] = []
+  let closeTagArray: number[] = []
+  let [nextCloseCharPos, nextOpenTagPos] = [0, 0, 0]
+
+  while (
+    (nextOpenTagPos = triggerText.indexOf(
+      '<' + nsPrefix + items[i],
+      nextOpenTagPos
+    )) > -1
+  ) {
+    openTagArray.push(nextOpenTagPos)
+
+    if ((nextCloseCharPos = triggerText.indexOf('>', nextOpenTagPos)) > -1) {
+      //if tag is self closing remove it from the openTagArray
+      if (
+        triggerText.substring(nextCloseCharPos - 1, nextCloseCharPos + 1) ===
+        '/>'
+      ) {
+        openTagArray.splice(-1, 1)
+      }
+
+      nextOpenTagPos = nextOpenTagPos + 1
+    }
+  }
+
+  while (
+    (nextCloseCharPos = triggerText.indexOf(
+      '</' + nsPrefix + items[i],
+      nextCloseCharPos
+    )) > -1
+  ) {
+    closeTagArray.push(nextCloseCharPos)
+    nextCloseCharPos = nextCloseCharPos + 1
+  }
+
+  if (openTagArray.length > closeTagArray.length) {
+    return items[i]
+  }
+
+  return undefined
+}
+
+export function getItemsForLineLT2(
+  document: vscode.TextDocument,
+  triggerText: string,
+  triggerLine: number,
+  nsPrefix: string,
+  items: string[],
+  i: number
+) {
+  let [currentText, currentLine] = [triggerText, triggerLine]
+  let [lineBefore, lineAfter] = [triggerLine, triggerLine]
+  let openTagArray: number[] = []
+  let closeTagArray: number[] = []
+
+  while (
+    currentText.indexOf('<' + nsPrefix + items[i]) === -1 &&
+    currentLine > -1
+  ) {
+    --currentLine
+
+    if (currentLine > -1) {
+      currentText = document.lineAt(currentLine).text
+    }
+
+    if (getItemsOnLineCount(currentText) > 1) {
+      --currentLine
+    }
+  }
+
+  if (currentText.indexOf('<' + nsPrefix + items[i]) > -1) {
+    while (lineBefore > -1) {
+      currentText = document.lineAt(lineBefore).text
+      if (getItemsOnLineCount(currentText) < 2) {
+        if (currentText.indexOf('<' + nsPrefix + items[i]) > -1) {
+          openTagArray.push(lineBefore)
+
+          //if selfclosing remove from the array
+          if (currentText.indexOf('/>') > -1) {
+            openTagArray.splice(openTagArray.length - 1, 1)
+          }
+        }
+
+        if (currentText.indexOf('</' + nsPrefix + items[i]) > -1) {
+          closeTagArray.push(lineBefore)
+        }
+      }
+
+      --lineBefore
+    }
+
+    ++lineAfter
+
+    while (lineAfter < document.lineCount) {
+      currentText = document.lineAt(lineAfter).text
+
+      if (getItemsOnLineCount(currentText) < 2) {
+        if (currentText.indexOf('<' + nsPrefix + items[i]) > -1) {
+          openTagArray.push(lineAfter)
+
+          //if selfclosing remove from the array
+          if (currentText.indexOf('/>') > -1) {
+            openTagArray.splice(openTagArray.length - 1, 1)
+          }
+        }
+
+        if (currentText.indexOf('</' + nsPrefix + items[i]) > -1) {
+          closeTagArray.push(lineAfter)
+        }
+      }
+
+      ++lineAfter
+    }
+
+    if (openTagArray.length > closeTagArray.length) {
+      return items[i]
+    }
+  }
+
+  return undefined
+}
diff --git a/src/language/providers/elementCompletion.ts 
b/src/language/providers/elementCompletion.ts
index cba6ee6..d1db5cc 100644
--- a/src/language/providers/elementCompletion.ts
+++ b/src/language/providers/elementCompletion.ts
@@ -16,9 +16,17 @@
  */
 
 import * as vscode from 'vscode'
-import { checkBraceOpen, getXsdNsPrefix } from './utils'
+import { getCloseTag } from './closeUtils'
+import {
+  checkBraceOpen,
+  getXsdNsPrefix,
+  nearestOpen,
+  createCompletionItem,
+  getCommonItems,
+  nearestTag,
+  getItemsOnLineCount,
+} from './utils'
 import { elementCompletion } from './intellisense/elementItems'
-import { createCompletionItem } from './utils'
 
 export function getElementCompletionProvider(dfdlFormatString: string) {
   return vscode.languages.registerCompletionItemProvider('dfdl', {
@@ -32,57 +40,233 @@ export function 
getElementCompletionProvider(dfdlFormatString: string) {
         console.log('in elementCompletionProvider - brace is showing open')
         return undefined
       }
-      var nsPrefix = getXsdNsPrefix(document, position)
-      var definedVariables = getDefinedVariables(document)
 
-      // a completion item that inserts its text as snippet,
-      // the `insertText`-property is a `SnippetString` which will be
-      // honored by the editor.
-      let compItems: vscode.CompletionItem[] = []
+      let nsPrefix = getXsdNsPrefix(document, position)
+      let [triggerLine, triggerPos] = [position.line, position.character]
+      let triggerText = document.lineAt(triggerLine).text
+      let itemsOnLine = getItemsOnLineCount(triggerText)
+      let nearestOpenItem = nearestOpen(document, position)
 
-      elementCompletion(
-        definedVariables,
-        dfdlFormatString,
-        nsPrefix
-      ).items.forEach((e) => {
-        const line = document
-          .lineAt(position)
-          .text.substring(0, position.character)
-
-        if (line.includes('<') && e.snippetString.startsWith('<')) {
-          e.snippetString = e.snippetString.substring(1, 
e.snippetString.length)
-        }
+      if (nearestOpenItem.includes('none')) {
+        let definedVariables = getDefinedVariables(document)
 
-        compItems.push(createCompletionItem(e, '', nsPrefix))
-      })
+        let tagNearestTrigger = getTagNearestTrigger(
+          document,
+          position,
+          triggerText,
+          triggerLine,
+          triggerPos,
+          itemsOnLine,
+          nsPrefix
+        )
 
-      return compItems
+        return checkTagNearestOpen(
+          document,
+          position,
+          tagNearestTrigger,
+          definedVariables,
+          nsPrefix
+        )
+      }
     },
   })
 }
 
+function getElementCompletionItems(
+  itemsToUse: string[],
+  preVal: string = '',
+  definedVariables: string = '',
+  nsPrefix: string
+) {
+  let compItems: vscode.CompletionItem[] = getCommonItems(
+    itemsToUse,
+    preVal,
+    definedVariables,
+    nsPrefix
+  )
+  let dfdlFormatString: string = ''
+
+  elementCompletion(definedVariables, dfdlFormatString, 
nsPrefix).items.forEach(
+    (e) => {
+      for (let i = 0; i < itemsToUse.length; ++i) {
+        if (e.item.includes(itemsToUse[i])) {
+          const completionItem = createCompletionItem(e, preVal, nsPrefix)
+          compItems.push(completionItem)
+        }
+      }
+    }
+  )
+
+  return compItems
+}
+
 function getDefinedVariables(document: vscode.TextDocument) {
-  var additionalTypes = ''
-  var lineNum = 0
-  var itemCnt = 0
+  let additionalTypes = ''
+  let lineNum = 0
+  let itemCnt = 0
   const lineCount = document.lineCount
+
   while (lineNum !== lineCount) {
     const triggerText = document
       .lineAt(lineNum)
       .text.substring(0, document.lineAt(lineNum).range.end.character)
+
     if (triggerText.includes('dfdl:defineVariable name=')) {
-      var startPos = triggerText.indexOf('"', 0)
-      var endPos = triggerText.indexOf('"', startPos + 1)
-      var newType = triggerText.substring(startPos + 1, endPos)
-      if (itemCnt === 0) {
-        additionalTypes = newType
-        ++itemCnt
-      } else {
-        additionalTypes = String(additionalTypes + ',' + newType)
-        ++itemCnt
-      }
+      let startPos = triggerText.indexOf('"', 0)
+      let endPos = triggerText.indexOf('"', startPos + 1)
+      let newType = triggerText.substring(startPos + 1, endPos)
+
+      additionalTypes =
+        itemCnt === 0 ? newType : String(additionalTypes + ',' + newType)
+      ++itemCnt
     }
+
     ++lineNum
   }
+
   return additionalTypes
 }
+
+function checkTagNearestOpen(
+  document: vscode.TextDocument,
+  position: vscode.Position,
+  tagNearestTrigger: string,
+  definedVariables: string,
+  nsPrefix: string
+) {
+  switch (tagNearestTrigger) {
+    case 'element':
+      return getElementCompletionItems(
+        ['complexType', 'simpleType', 'annotation'],
+        '',
+        definedVariables,
+        nsPrefix
+      )
+    case 'sequence':
+      return getElementCompletionItems(
+        ['element', 'sequence', 'choice', 'annotation', 'appinfo'],
+        '',
+        '',
+        nsPrefix
+      )
+    case 'choice':
+      return getElementCompletionItems(
+        ['element', 'group ref', 'annotation'],
+        '',
+        '',
+        nsPrefix
+      )
+    case 'group':
+      return getElementCompletionItems(
+        ['sequence', 'annotation'],
+        '',
+        '',
+        nsPrefix
+      )
+    case 'complexType':
+      return getElementCompletionItems(['sequence'], '', '', nsPrefix)
+    case 'simpleType':
+      return getElementCompletionItems(
+        ['annotation', 'restriction'],
+        '',
+        '',
+        nsPrefix
+      )
+    case 'annotation':
+      return getElementCompletionItems(['appinfo'], '', '', nsPrefix)
+    case 'appinfo':
+      return getElementCompletionItems(
+        [
+          'assert',
+          'discriminator',
+          'defineFormat',
+          'format',
+          'defineVariable',
+          'setVariable',
+        ],
+        '',
+        '',
+        nsPrefix
+      )
+    case 'defineFormat':
+      return getElementCompletionItems(['format'], '', '', nsPrefix)
+    case 'schema':
+      return getElementCompletionItems(
+        [
+          'sequence',
+          'element',
+          'group',
+          'complexType',
+          'simpleType',
+          'annotation',
+        ],
+        '',
+        '',
+        nsPrefix
+      )
+    default:
+      return undefined
+  }
+}
+
+export function getTagNearestTrigger(
+  document: vscode.TextDocument,
+  position: vscode.Position,
+  triggerText: string,
+  triggerLine: number,
+  triggerPos: number,
+  itemsOnLine: number,
+  nsPrefix: string
+): string {
+  let [startLine, startPos] = [triggerLine, triggerPos]
+  let tagNearestTrigger = 'none'
+
+  if (
+    itemsOnLine > 1 &&
+    (triggerPos === triggerText.indexOf('<') ||
+      triggerPos === triggerText.lastIndexOf('>') + 1)
+  ) {
+    return tagNearestTrigger
+  }
+  while (true) {
+    let [foundTag, foundLine, foundPos] = nearestTag(
+      document,
+      position,
+      nsPrefix,
+      startLine,
+      startPos
+    )
+
+    startLine = foundLine
+
+    let [endTag, endTagLine, endTagPos] = getCloseTag(
+      document,
+      position,
+      nsPrefix,
+      foundTag,
+      foundLine,
+      foundPos
+    )
+
+    if (itemsOnLine > 1 && foundLine === triggerLine) {
+      if (foundTag === endTag && endTagPos >= triggerPos) {
+        tagNearestTrigger = foundTag
+        return tagNearestTrigger
+      }
+      if (endTag === 'none') {
+        startLine = foundLine - 1
+      } else {
+        startPos = foundPos - 1
+      }
+    }
+
+    if (itemsOnLine < 2) {
+      if (foundTag === endTag && endTagLine >= triggerLine) {
+        tagNearestTrigger = foundTag
+        return tagNearestTrigger
+      }
+
+      startLine = foundLine - 1
+    }
+  }
+}
diff --git a/src/language/providers/intellisense/attributeItems.ts 
b/src/language/providers/intellisense/attributeItems.ts
index fa74a88..2c64c64 100644
--- a/src/language/providers/intellisense/attributeItems.ts
+++ b/src/language/providers/intellisense/attributeItems.ts
@@ -20,14 +20,12 @@ export const attributeCompletion = (additionalItems, 
nsPrefix: string) => {
   return {
     items: [
       {
-        item: 'dfdl:defineFormat',
-        snippetString: '<dfdl:defineFormat name="$1" 
>\n\t$2\n</dfdl:defineFormat>\n$0',
-        markdownString: 'dfdl format name and configuration',
+        item: 'name=',
+        snippetString: 'name="$1"$0',
       },
       {
-        item: 'dfdl:defineEscapeScheme',
-        snippetString: '<dfdl:defineEscapeScheme name=$1 
>\n\t$0,/dfdl:defineEscapeScheme>\n',
-        markdownString: 'dfdl escape character definition',
+        item: 'ref=',
+        snippetString: 'ref="$1"$0',
       },
       {
         item: 'minOccurs=',
diff --git a/src/language/providers/intellisense/elementItems.ts 
b/src/language/providers/intellisense/elementItems.ts
index 1a88c9f..63af325 100644
--- a/src/language/providers/intellisense/elementItems.ts
+++ b/src/language/providers/intellisense/elementItems.ts
@@ -25,7 +25,7 @@ export const elementCompletion = (definedVariables, 
dfdlFormatString, nsPrefix)
       },
       {
         item: nsPrefix + 'schema',
-        snippetString: '<' + nsPrefix + '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"\n\t\txmlns:math="www.w3.org/2005/xpath-functions/math";
 elementFormDefault="qualified">\n$0\n</' + nsPrefix + 'schema>',
+        snippetString: '<' + nsPrefix + '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"\n\t\t
 elementFormDefault="unqualified">\n$0\n</' + nsPrefix + 'schema>',
       },
       {
         item: nsPrefix + 'element name',
@@ -48,12 +48,12 @@ export const elementCompletion = (definedVariables, 
dfdlFormatString, nsPrefix)
       },
       {
         item: 'dfdl:assert',
-        snippetString: '<' + nsPrefix + 'annotation>\n\t<' + nsPrefix + 
'appinfo 
source="http://www.ogf.org/dfdl/";>\n\t\t<dfdl:assert>"<$1>"</dfdl:assert>\n\t</'
 + nsPrefix + 'appinfo>\n</' + nsPrefix + 'annotation>$0',
+        snippetString: '<dfdl:assert>"<$1>"</dfdl:assert>$0',
         markdownString: 'dfdl assertion test',
       },
       {
-        item: 'dfdL:discriminator',
-        snippetString: '<' + nsPrefix + 'annotation>\n\t<' + nsPrefix + 
'appinfo source="http://www.ogf.org/dfdl/";>\n\t\t<dfdl:discriminator 
test="{$1}"/>\n\t</' + nsPrefix + 'appinfo>\n</' + nsPrefix + 'annotation>$0',
+        item: 'dfdl:discriminator',
+        snippetString: '<dfdl:discriminator test="{$1}"/>$0',
         markdownString: 'dfdl discriminator test',
       },
       {
@@ -62,15 +62,15 @@ export const elementCompletion = (definedVariables, 
dfdlFormatString, nsPrefix)
       },
       {
         item: 'dfdl:format',
-        snippetString: dfdlFormatString,
+        snippetString: '<dfdl:format $0/>',
       },
       {
         item: nsPrefix + 'annotation',
-        snippetString: '<' + nsPrefix + 'annotation>\n\t<' + nsPrefix + 
'appinfo source="http://www.ogf.org/dfdl/";>\n\t\t$0\n\t</' + nsPrefix + 
'appinfo>\n</' + nsPrefix + 'annotation>',
+        snippetString: '<' + nsPrefix + 'annotation>\n\t$0\n\</' + nsPrefix + 
'annotation>',
       },
       {
         item: nsPrefix + 'appinfo',
-        snippetString: '<' + nsPrefix + 'annotation>\n\t<' + nsPrefix + 
'appinfo source="http://www.ogf.org/dfdl/";>\n\t\t$0\n\t</' + nsPrefix + 
'appinfo>\n</' + nsPrefix + 'annotation>',
+        snippetString: '<' + nsPrefix + 'appinfo 
source="http://www.ogf.org/dfdl/";>\n\t$0\n</' + nsPrefix + 'appinfo>',
       },
       {
         item: nsPrefix + 'complexType',
@@ -102,12 +102,42 @@ export const elementCompletion = (definedVariables, 
dfdlFormatString, nsPrefix)
       },
       {
         item: 'dfdl:defineVariable',
-        snippetString: '<' + nsPrefix + 'annotation>\n\t<' + nsPrefix + 
'appinfo source="http://www.ogf.org/dfdl/";>\n\t\t<dfdl:defineVariable 
name="$1"$0',
+        snippetString: '\t<dfdl:defineVariable name="$1"$0',
       },
       {
         item: 'dfdl:setVariable',
-        snippetString: '<' + nsPrefix + 'annotation>\n\t<' + nsPrefix + 
'appinfo source="http://www.ogf.org/dfdl/";>\n\t\t<dfdl:setVariable ref="${1|' + 
definedVariables + '|}"$0',
+        snippetString: '\t<dfdl:setVariable ref="${1|' + definedVariables + 
'"|}, value="$2"$0',
+      },
+      {
+        item: 'dfdl:defineFormat',
+        snippetString: '<dfdl:defineFormat name="$1" 
>\n\t$2\n</dfdl:defineFormat>$0',
+        markdownString: 'dfdl format name and configuration',
+      },
+      {
+        item: 'dfdl:defineEscapeScheme',
+        snippetString: '<dfdl:defineEscapeScheme name=$1 
>\n\t$0,/dfdl:defineEscapeScheme>',
+        markdownString: 'dfdl escape character definition',
+      },
+      /*TODO not sure how to make dfdl:element work without braking xs:element 
+      {
+        item: 'dfdl:element',
+        snippetString: '\t<dfdl:defineVariable name="$1"$0',
+      },
+      //these items have been separated into individual tags... not sure if I 
should keep this code around
+      */
+      /*{
+        item: nsPrefix + 'annotation+appinfo',
+        snippetString: '<' + nsPrefix + 'annotation>\n\t<' + nsPrefix + 
'appinfo source="http://www.ogf.org/dfdl/";>\n\t\t$0\n\t</' + nsPrefix + 
'appinfo>\n</' + nsPrefix + 'annotation>',
       },
+      item: 'dfdl:assert+appinfo+annotation',
+        snippetString: '<' + nsPrefix + 'annotation>\n\t<' + nsPrefix + 
'appinfo 
source="http://www.ogf.org/dfdl/";>\n\t\t<dfdl:assert>"<$1>"</dfdl:assert>\n\t</'
 + nsPrefix + 'appinfo>\n</' + nsPrefix + 'annotation>$0',
+        markdownString: 'dfdl assertion test',
+      },
+      {
+        item: 'dfdl:discriminator+appinfo+annotation',
+        snippetString: '<' + nsPrefix + 'annotation>\n\t<' + nsPrefix + 
'appinfo source="http://www.ogf.org/dfdl/";>\n\t\t<dfdl:discriminator 
test="{$1}"/>\n\t</' + nsPrefix + 'appinfo>\n</' + nsPrefix + 'annotation>$0',
+        markdownString: 'dfdl discriminator test',
+      },*/
     ],
   }
 }
diff --git a/src/language/providers/utils.ts b/src/language/providers/utils.ts
index 51ede84..f795ae2 100644
--- a/src/language/providers/utils.ts
+++ b/src/language/providers/utils.ts
@@ -20,6 +20,29 @@ import { commonCompletion } from './intellisense/commonItems'
 
 const schemaPrefixRegEx = new RegExp('</?(|[^ ]+:)schema')
 
+//List of high level dfdl element items
+const items = [
+  'element',
+  'sequence',
+  'choice',
+  'group',
+  'simpleType',
+  'complexType',
+  'annotation',
+  'appinfo',
+  'assert',
+  'discriminator',
+  'defineFormat',
+  'format',
+  'defineVariable',
+  'setVariable',
+  'schema',
+]
+
+export function getItems() {
+  return items
+}
+
 // default namespace in the event that a namespace was not found
 const defaultXsdNsPrefix = 'xs'
 
@@ -31,43 +54,21 @@ export function insertSnippet(snippetString: string, 
backpos: vscode.Position) {
   )
 }
 
-//Checks if the line at the current position is the last opened tag
-export function checkLastItemOpen(
-  document: vscode.TextDocument,
-  position: vscode.Position
-) {
-  var lineNum = position.line
-  const triggerText = document.lineAt(lineNum).text
-  while (triggerText.length === 0) {
-    --lineNum
-  }
-  const previousLine = document.lineAt(lineNum).text
-  return !(
-    previousLine.includes('</') ||
-    previousLine.includes('/>') ||
-    ((triggerText.includes('element') ||
-      triggerText.includes('sequence') ||
-      triggerText.includes('choice') ||
-      triggerText.includes('group') ||
-      triggerText.includes('Variable')) &&
-      (triggerText.includes('</') || triggerText.includes('/>')))
-  )
-}
-
 export function lineCount(
   document: vscode.TextDocument,
-  position: vscode.Position
+  position: vscode.Position,
+  tag: string
 ) {
-  var lineNum = position.line
-  var lineCount = 0
+  let lineNum = position.line
+  let lineCount = 0
   const nsPrefix = getXsdNsPrefix(document, position)
   while (lineNum !== 0) {
     --lineNum
     ++lineCount
     const triggerText = document.lineAt(lineNum).text
     if (
-      triggerText.includes('<' + nsPrefix + 'element') &&
-      !triggerText.includes('</' + nsPrefix + 'element') &&
+      triggerText.includes('<' + nsPrefix + tag) &&
+      !triggerText.includes('</' + nsPrefix + tag) &&
       !triggerText.includes('/>')
     ) {
       return lineCount
@@ -80,204 +81,164 @@ export function nearestOpen(
   document: vscode.TextDocument,
   position: vscode.Position
 ) {
-  var lineNum = position.line
   const nsPrefix = getXsdNsPrefix(document, position)
-  while (lineNum !== -1) {
-    const triggerText = document.lineAt(lineNum).text
-    if (triggerText.includes('element') && !triggerText.includes('/>')) {
-      if (checkElementOpen(document, position)) {
-        return 'element'
-      }
-    } else if (
-      triggerText.includes('sequence') &&
-      !triggerText.includes('/>')
-    ) {
-      if (checkSequenceOpen(document, position)) {
-        return 'sequence'
-      }
-    } else if (triggerText.includes('choice') && !triggerText.includes('/>')) {
-      if (checkChoiceOpen(document, position)) {
-        return 'choice'
-      }
-    } else if (triggerText.includes('group')) {
-      if (
-        triggerText.includes('<' + nsPrefix + 'group') &&
-        !triggerText.includes('</' + nsPrefix + 'group') &&
-        !triggerText.includes('/>') &&
-        !triggerText.includes('/')
-      ) {
-        return 'group'
-      }
-    } else if (
-      triggerText.includes('simpleType') &&
-      !triggerText.includes('/>')
-    ) {
-      if (checkSimpleTypeOpen(document, position)) {
-        return 'simpleType'
-      }
-    } else if (
-      triggerText.includes('defineVariable') &&
-      !triggerText.includes('/>')
-    ) {
-      if (checkDefineVariableOpen(document, position)) {
-        return 'defineVariable'
-      }
-    } else if (
-      triggerText.includes('setVariable') &&
-      !triggerText.includes('/>')
-    ) {
-      if (checkSetVariableOpen(document, position)) {
-        return 'setVariable'
-      }
-    } else if (triggerText.includes('/>')) {
-      return 'none'
+  for (let i = 0; i < items.length; ++i) {
+    if (checkTagOpen(document, position, nsPrefix, items[i])) {
+      return items[i]
     }
-    --lineNum
   }
   return 'none'
 }
 
-export function checkElementOpen(
+export function nearestTag(
   document: vscode.TextDocument,
-  position: vscode.Position
-) {
-  const nsPrefix = getXsdNsPrefix(document, position)
-  var lineNum = position.line
-  while (lineNum !== -1) {
-    const triggerText = document.lineAt(lineNum).text
-    if (
-      triggerText.includes('<' + nsPrefix + 'element') &&
-      (triggerText.includes('>') ||
-        triggerText.includes('</' + nsPrefix + 'element') ||
-        triggerText.includes('/>'))
-    ) {
-      return false
-    }
-    if (triggerText.includes('</' + nsPrefix + 'element')) {
-      return false
+  position: vscode.Position,
+  nsPrefix: string,
+  startLine: number,
+  startPos: number
+): [string, number, number] {
+  const triggerLine = position.line
+  const origPrefix = nsPrefix
+  let lineNum = startLine
+  const triggerText = document.lineAt(triggerLine).text
+  const itemsOnLine = getItemsOnLineCount(document.lineAt(lineNum).text)
+  let tagPos = triggerText.indexOf('<')
+  let endPos = triggerText.lastIndexOf('>')
+  if (
+    itemsOnLine > 1 &&
+    startPos !== tagPos &&
+    startPos < endPos &&
+    endPos != startPos
+  ) {
+    let textBeforeTrigger = triggerText.substring(0, startPos)
+    let prevTagPos = 0
+    while (prevTagPos > -1) {
+      prevTagPos = textBeforeTrigger.lastIndexOf('<')
+      let tag = textBeforeTrigger.substring(prevTagPos)
+      if (
+        !textBeforeTrigger.includes('</') &&
+        !textBeforeTrigger.includes('/>')
+      ) {
+        for (let i = 0; i < items.length; ++i) {
+          nsPrefix = getItemPrefix(items[i], origPrefix)
+          if (tag.includes('<' + nsPrefix + items[i])) {
+            return [items[i], startLine, prevTagPos]
+          }
+        }
+      }
+      textBeforeTrigger = textBeforeTrigger.substring(0, prevTagPos)
     }
+  } else {
     if (
-      triggerText.includes('<' + nsPrefix + 'element') &&
-      !triggerText.includes('</' + nsPrefix + 'element') &&
-      !triggerText.includes('/>') &&
-      !triggerText.includes('>')
+      (startLine === triggerLine && tagPos === startPos) ||
+      triggerText.trim() === ''
     ) {
-      return true
+      --lineNum
+    }
+    while (lineNum > -1 && lineNum < document.lineCount) {
+      let currentText = document.lineAt(lineNum).text
+      if (getItemsOnLineCount(currentText) < 2) {
+        if (!currentText.includes('</') && !currentText.includes('/>')) {
+          for (let i = 0; i < items.length; ++i) {
+            nsPrefix = getItemPrefix(items[i], origPrefix)
+            if (currentText.includes('<' + nsPrefix + items[i])) {
+              return [items[i], lineNum, startPos]
+            }
+          }
+        }
+      }
+      --lineNum
     }
-    --lineNum
   }
-  return false
+  return ['none', 0, 0]
 }
 
-export function checkSequenceOpen(
+export function checkTagOpen(
   document: vscode.TextDocument,
-  position: vscode.Position
+  position: vscode.Position,
+  nsPrefix: string,
+  tag: string
 ) {
-  const nsPrefix = getXsdNsPrefix(document, position)
-  var lineNum = position.line
-  while (lineNum !== 0) {
-    const triggerText = document.lineAt(lineNum).text
-    if (
-      (triggerText.includes('<' + nsPrefix + 'sequence') &&
-        (triggerText.includes('</' + nsPrefix + 'sequence') ||
-          triggerText.includes('/>'))) ||
-      triggerText.includes('</' + nsPrefix + 'sequence>')
-    ) {
-      return false
-    }
-    if (
-      triggerText.includes('<' + nsPrefix + 'sequence') &&
-      !triggerText.includes('</' + nsPrefix + 'sequence') &&
-      !triggerText.includes('/>')
-    ) {
-      return true
-    }
-    --lineNum
+  nsPrefix = getItemPrefix(tag, nsPrefix)
+
+  let triggerLine = position.line
+  let triggerText = document.lineAt(triggerLine).text
+  let itemsOnLine = getItemsOnLineCount(triggerText)
+  const origTriggerText = triggerText
+  while (itemsOnLine < 2 && triggerText.indexOf('<') === -1) {
+    triggerText = document.lineAt(--triggerLine).text
   }
-  return false
-}
 
-export function checkChoiceOpen(
-  document: vscode.TextDocument,
-  position: vscode.Position
-) {
-  const nsPrefix = getXsdNsPrefix(document, position)
-  var lineNum = position.line
-  while (lineNum !== 0) {
-    const triggerText = document.lineAt(lineNum).text
+  const triggerPos = position.character
+  const textBeforeTrigger = triggerText.substring(0, triggerPos)
+  const tagPos = textBeforeTrigger.lastIndexOf('<' + nsPrefix + tag)
+  const nextTagPos = triggerText.indexOf('<', tagPos + 1)
+  let tagEndPos = triggerText.indexOf('>', tagPos)
+  if (tagPos > -1 && itemsOnLine > 1) {
     if (
-      (triggerText.includes('<' + nsPrefix + 'choice') &&
-        (triggerText.includes('</' + nsPrefix + 'choice') ||
-          triggerText.includes('/>'))) ||
-      triggerText.includes('</' + nsPrefix + 'choice>')
-    ) {
-      return false
-    }
-    if (
-      triggerText.includes('<' + nsPrefix + 'choice') &&
-      !triggerText.includes('</' + nsPrefix + 'choice') &&
-      !triggerText.includes('/>')
+      triggerPos > tagPos &&
+      ((triggerPos <= tagEndPos &&
+        (nextTagPos > tagEndPos || nextTagPos === -1)) ||
+        tagEndPos === -1)
     ) {
       return true
     }
-    --lineNum
   }
-  return false
-}
-export function checkSimpleTypeOpen(
-  document: vscode.TextDocument,
-  position: vscode.Position
-) {
-  const nsPrefix = getXsdNsPrefix(document, position)
-  var lineNum = position.line
-  while (lineNum !== 0) {
-    const triggerText = document.lineAt(lineNum).text
-    if (
-      triggerText.includes('<' + nsPrefix + 'simpleType') &&
-      !triggerText.includes('</' + nsPrefix + 'simpleType') &&
-      !triggerText.includes('/>')
-    ) {
+  if (tagPos > -1 && itemsOnLine < 2) {
+    if (triggerText !== origTriggerText) {
+      tagEndPos = origTriggerText.indexOf('>')
+    }
+    if (triggerPos > tagPos && (triggerPos <= tagEndPos || tagEndPos === -1)) {
       return true
     }
-    --lineNum
   }
-  return false
+  //if this tag is part of a multi line set of annotations return true
+  //else this tag is not open return false
+  return checkMultiLineAnnotations(
+    document,
+    position,
+    itemsOnLine,
+    nsPrefix,
+    tag
+  )
 }
 
-export function checkDefineVariableOpen(
-  document: vscode.TextDocument,
-  position: vscode.Position
-) {
-  var lineNum = position.line
-  while (lineNum !== 0) {
-    const triggerText = document.lineAt(lineNum).text
-    if (
-      triggerText.includes('<dfdl:defineVariable') &&
-      !triggerText.includes('</dfdl:defineVariable') &&
-      !triggerText.includes('/>')
-    ) {
-      return true
-    }
-    --lineNum
+export function getItemPrefix(item: string, nsPrefix: string) {
+  let itemPrefix = nsPrefix
+  if (
+    item === 'assert' ||
+    item === 'discriminator' ||
+    item === 'defineFormat' ||
+    item === 'format' ||
+    item.includes('Variable')
+  ) {
+    itemPrefix = 'dfdl:'
   }
-  return false
+  return itemPrefix
 }
 
-export function checkSetVariableOpen(
+export function checkMultiLineAnnotations(
   document: vscode.TextDocument,
-  position: vscode.Position
+  position: vscode.Position,
+  itemsOnLine: number,
+  nsPrefix: string,
+  tag: string
 ) {
-  var lineNum = position.line
-  while (lineNum !== 0) {
-    const triggerText = document.lineAt(lineNum).text
-    if (
-      triggerText.includes('<dfdl:setVariable') &&
-      !triggerText.includes('</dfdl:setVariable') &&
-      !triggerText.includes('/>')
-    ) {
-      return true
-    }
-    --lineNum
+  if (itemsOnLine > 1 || tag === 'schema') {
+    return false
+  }
+  let currentLine = position.line
+  let currentText = document.lineAt(currentLine).text
+
+  while (currentText.trim() === '' || !currentText.includes('<')) {
+    --currentLine
+    currentText = document.lineAt(currentLine).text
+  }
+  if (
+    currentText.indexOf('<' + nsPrefix + tag) !== -1 &&
+    currentText.indexOf('>') === -1
+  ) {
+    return true
   }
   return false
 }
@@ -287,8 +248,8 @@ export function getXsdNsPrefix(
   document: vscode.TextDocument,
   position: vscode.Position
 ) {
-  var initialLineNum = position.line
-  var lineNum = 0
+  let initialLineNum = position.line
+  let lineNum = 0
   while (initialLineNum !== 0 && lineNum <= initialLineNum) {
     const lineText = document.lineAt(lineNum).text
     // returns either empty prefix value or a prefix plus a colon
@@ -302,20 +263,54 @@ export function getXsdNsPrefix(
   return defaultXsdNsPrefix + ':'
 }
 
+export function getItemsOnLineCount(triggerText: String) {
+  let itemsOnLine = 0
+  let nextPos = 0
+  let result = 0
+  if (triggerText.includes('schema')) {
+    itemsOnLine = 1
+    return itemsOnLine
+  }
+  while (result != -1 && triggerText.includes('<')) {
+    result = triggerText.indexOf('<', nextPos)
+    if (result > -1) {
+      let endPos = triggerText.indexOf('>', nextPos)
+      if (endPos === -1) {
+        ++itemsOnLine
+        break
+      }
+      let testForCloseTag = triggerText.substring(nextPos, endPos)
+      if (
+        !testForCloseTag.includes('</') &&
+        !testForCloseTag.includes('<!--') &&
+        !testForCloseTag.includes('-->')
+      ) {
+        ++itemsOnLine
+      }
+      result = nextPos
+      nextPos = endPos + 1
+    }
+  }
+  return itemsOnLine
+}
+
 export function checkBraceOpen(
   document: vscode.TextDocument,
   position: vscode.Position
 ) {
-  var lineNum = position.line
+  let lineNum = position.line
 
   while (lineNum !== 0) {
     const triggerText = document.lineAt(lineNum).text
     //.text.substring(0, document.lineAt(lineNum).range.end.character)
 
+    if (!triggerText.includes('{')) {
+      return false
+    }
     if (
       triggerText.includes('"{') &&
       triggerText.includes('}"') &&
-      triggerText.includes('..') &&
+      (triggerText.includes('..') || triggerText.includes('.')) &&
       !triggerText.includes('}"/') &&
       !triggerText.includes('>')
     ) {
diff --git a/src/tests/suite/language/items.test.ts 
b/src/tests/suite/language/items.test.ts
index 8310ba5..746c973 100644
--- a/src/tests/suite/language/items.test.ts
+++ b/src/tests/suite/language/items.test.ts
@@ -23,30 +23,34 @@ import { elementCompletion } from 
'../../../language/providers/intellisense/elem
 suite('Items Test Suite', () => {
   const expectedElementItems = [
     'xml version',
-    'xs:schema',
-    'xs:element name',
-    'xs:element ref',
-    'xs:group name',
-    'xs:group ref',
+    'schema',
+    'element name',
+    'element ref',
+    'group name',
+    'group ref',
     'dfdl:assert',
-    'dfdL:discriminator',
+    'dfdl:discriminator',
     'dfdl:hiddenGroupRef',
     'dfdl:format',
-    'xs:annotation',
-    'xs:appinfo',
-    'xs:complexType',
-    'xs:complexType name=',
-    'xs:simpleType',
-    'xs:simpleType name=',
-    'xs:sequence',
-    'xs:choice',
+    'annotation',
+    'appinfo',
+    'complexType',
+    'complexType name=',
+    'simpleType',
+    'simpleType name=',
+    'sequence',
+    'choice',
     'dfdl:defineVariable',
     'dfdl:setVariable',
+    'dfdl:defineFormat',
+    'dfdl:defineEscapeScheme',
   ]
   const expectedAttributeItems = [
     'dfdl:defineFormat',
     'dfdl:defineEscapeScheme',
     'type=',
+    'name=',
+    'ref=',
     'minOccurs=',
     'maxOccurs=',
     'dfdl:occursCount=',
@@ -75,7 +79,7 @@ suite('Items Test Suite', () => {
     'dfdl:initiatedContent=',
     'dfdl:choiceDispatchKey=',
     'dfdl:simpleType',
-    'xs:restriction',
+    'restriction',
   ]
 
   test('all commonItems available', async () => {
@@ -85,13 +89,13 @@ suite('Items Test Suite', () => {
   })
 
   test('all elementItems available', async () => {
-    elementCompletion('', '', 'xs:').items.forEach((item) => {
+    elementCompletion('', '', '').items.forEach((item) => {
       assert.strictEqual(expectedElementItems.includes(item.item), true)
     })
   })
 
   test('all attributeItems available', async () => {
-    attributeCompletion('', 'xs:').items.forEach((item) => {
+    attributeCompletion('', '').items.forEach((item) => {
       assert.strictEqual(expectedAttributeItems.includes(item.item), true)
     })
   })

Reply via email to