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 c262b6b  Fixes problems with space and slash truncating  attribute 
values without default values when typng a space or slash in the quoted text 
Fixes a problem where typing a slash almost anywhere inserts '/>' a self 
closing tag Prevents hover popups for non-attribute items Fixes intellisense 
suggesting incorrect attributes for elements that spread across multiple lines
c262b6b is described below

commit c262b6b71d0401fc71dd2ded5b0d48443555ea8e
Author: rthomas320 <[email protected]>
AuthorDate: Thu Sep 19 07:39:30 2024 -0400

    Fixes problems with space and slash truncating  attribute values
    without default values when typng a space or slash in the quoted
    text
    Fixes a problem where typing a slash almost anywhere inserts '/>'
    a self closing tag
    Prevents hover popups for non-attribute items
    Fixes intellisense suggesting incorrect attributes for elements
    that spread across multiple lines
    
    closes #1065
---
 src/language/providers/attributeCompletion.ts      |  20 ++++
 src/language/providers/attributeValueCompletion.ts |  12 +-
 src/language/providers/closeElement.ts             |  19 +++-
 src/language/providers/closeElementSlash.ts        |  42 +++----
 src/language/providers/closeUtils.ts               |   1 +
 src/language/providers/elementCompletion.ts        | 125 ++++++++++++---------
 .../providers/intellisense/attributeHoverItems.ts  |   4 +
 .../providers/intellisense/attributeItems.ts       |  10 ++
 .../providers/intellisense/attributeValueItems.ts  |   1 +
 .../providers/intellisense/elementItems.ts         |   4 +-
 src/language/providers/utils.ts                    |  86 ++++++++++----
 src/tests/suite/language/items.test.ts             |   2 +
 12 files changed, 214 insertions(+), 112 deletions(-)

diff --git a/src/language/providers/attributeCompletion.ts 
b/src/language/providers/attributeCompletion.ts
index 3d51644..8369327 100644
--- a/src/language/providers/attributeCompletion.ts
+++ b/src/language/providers/attributeCompletion.ts
@@ -326,6 +326,26 @@ function checkNearestOpenItem(
         spacingChar,
         afterChar
       )
+    case 'include':
+      return getCompletionItems(
+        ['schemaLocation'],
+        '',
+        '',
+        nsPrefix,
+        '',
+        spacingChar,
+        afterChar
+      )
+    case 'import':
+      return getCompletionItems(
+        ['schemaLocation', 'namespace'],
+        '',
+        '',
+        nsPrefix,
+        '',
+        spacingChar,
+        afterChar
+      )
     case 'assert':
       return getCompletionItems(
         ['testKind', 'test', 'testPattern', 'message', 'failureType'],
diff --git a/src/language/providers/attributeValueCompletion.ts 
b/src/language/providers/attributeValueCompletion.ts
index 741f5d5..c59c984 100644
--- a/src/language/providers/attributeValueCompletion.ts
+++ b/src/language/providers/attributeValueCompletion.ts
@@ -45,25 +45,19 @@ export function getAttributeValueCompletionProvider() {
           position
         )
 
-        if (attributeName !== 'none') {
+        if (attributeName !== 'none' && !attributeName.includes('xmlns:')) {
           let replaceValue = ''
           if (startPos === endPos) {
             replaceValue = ' '
           }
 
-          if (
-            attributeName.includes(':') &&
-            !attributeName.includes('xmlns:')
-          ) {
+          if (attributeName.includes(':')) {
             attributeName = attributeName.substring(
               attributeName.indexOf(':') + 1
             )
           }
 
-          if (
-            noChoiceAttributes.includes(attributeName) ||
-            attributeName.includes('xmlns:')
-          ) {
+          if (noChoiceAttributes.includes(attributeName)) {
             return undefined
           }
 
diff --git a/src/language/providers/closeElement.ts 
b/src/language/providers/closeElement.ts
index ffd1438..42d0b6f 100644
--- a/src/language/providers/closeElement.ts
+++ b/src/language/providers/closeElement.ts
@@ -27,6 +27,7 @@ import {
   getXsdNsPrefix,
   insertSnippet,
   isInXPath,
+  isNotTriggerChar,
   getItemsOnLineCount,
   getItemPrefix,
 } from './utils'
@@ -39,12 +40,14 @@ export function getCloseElementProvider() {
         document: vscode.TextDocument,
         position: vscode.Position
       ) {
+        let triggerChar = '>'
         if (
           checkBraceOpen(document, position) ||
           cursorWithinBraces(document, position) ||
           cursorWithinQuotes(document, position) ||
           cursorAfterEquals(document, position) ||
-          isInXPath(document, position)
+          isInXPath(document, position) ||
+          isNotTriggerChar(document, position, triggerChar)
         ) {
           return undefined
         }
@@ -184,7 +187,7 @@ export function getTDMLCloseElementProvider() {
             backpos3
           )
         }
-        return undefined
+        //return undefined
       },
     },
     '>' // triggered whenever a '>' is typed
@@ -202,7 +205,11 @@ function checkItemsOnLine(
   backpos: vscode.Position,
   backpos3: vscode.Position
 ) {
-  if (itemsOnLine == 0 && !triggerText.includes('</')) {
+  if (
+    itemsOnLine == 0 &&
+    !triggerText.includes('</') &&
+    !triggerText.includes('/>')
+  ) {
     if (triggerText.trim() === '>') {
       insertSnippet('</' + nsPrefix + nearestTagNotClosed + '>', backpos)
     } else {
@@ -237,7 +244,11 @@ function checkItemsOnLine(
     }
   }
 
-  if (itemsOnLine === 1 && !triggerText.includes('</')) {
+  if (
+    itemsOnLine === 1 &&
+    !triggerText.includes('</') &&
+    !triggerText.includes('/>')
+  ) {
     checkNearestTagNotClosed(
       document,
       position,
diff --git a/src/language/providers/closeElementSlash.ts 
b/src/language/providers/closeElementSlash.ts
index 7021c86..cc06f03 100644
--- a/src/language/providers/closeElementSlash.ts
+++ b/src/language/providers/closeElementSlash.ts
@@ -21,6 +21,7 @@ import {
   insertSnippet,
   checkBraceOpen,
   isInXPath,
+  isNotTriggerChar,
   getXsdNsPrefix,
   getItemPrefix,
   getItemsOnLineCount,
@@ -47,28 +48,28 @@ export function getCloseElementSlashProvider() {
           position,
           nsPrefix
         )
-        if (nearestTagNotClosed === 'none') {
-          return undefined
-        }
         const itemsOnLine = getItemsOnLineCount(triggerText)
-
+        const triggerChar = '/'
         if (
           checkBraceOpen(document, position) ||
           cursorWithinBraces(document, position) ||
           cursorWithinQuotes(document, position) ||
           cursorAfterEquals(document, position) ||
-          isInXPath(document, position)
+          isInXPath(document, position) ||
+          isNotTriggerChar(document, position, triggerChar)
         ) {
           return undefined
         }
 
-        if (triggerText.endsWith('/')) {
+        if (!(nearestTagNotClosed == 'none')) {
           let range = new vscode.Range(backpos, position)
 
           await vscode.window.activeTextEditor?.edit((editBuilder) => {
             editBuilder.replace(range, '')
           })
+        }
 
+        if (triggerText.endsWith('/')) {
           checkItemsOnLine(
             document,
             position,
@@ -80,7 +81,7 @@ export function getCloseElementSlashProvider() {
           )
         }
 
-        return undefined
+        //return undefined
       },
     },
     '/'
@@ -119,12 +120,6 @@ export function getTDMLCloseElementSlashProvider() {
         }
 
         if (triggerText.endsWith('/')) {
-          let range = new vscode.Range(backpos, position)
-
-          await vscode.window.activeTextEditor?.edit((editBuilder) => {
-            editBuilder.replace(range, '')
-          })
-
           checkItemsOnLine(
             document,
             position,
@@ -155,19 +150,22 @@ function checkItemsOnLine(
 ) {
   nsPrefix = getItemPrefix(nearestTagNotClosed, nsPrefix)
 
-  if (itemsOnLine == 1 || itemsOnLine == 0) {
-    insertSnippet('/>$0', backpos)
+  if (
+    !(nearestTagNotClosed == 'none') &&
+    (itemsOnLine == 1 || itemsOnLine == 0)
+  ) {
+    // let range = new vscode.Range(backpos, position)
 
+    // await vscode.window.activeTextEditor?.edit((editBuilder) => {
+    //   editBuilder.replace(range, '')
+    // })
     if (
       nearestTagNotClosed.includes('defineVariable') ||
       nearestTagNotClosed.includes('setVariable')
     ) {
-      let range = new vscode.Range(backpos, position)
-      vscode.window.activeTextEditor?.edit((editBuilder) => {
-        editBuilder.replace(range, '')
-      })
-
       insertSnippet('/>\n', backpos)
+    } else {
+      insertSnippet('/>$0', backpos)
     }
   }
 
@@ -178,6 +176,10 @@ function checkItemsOnLine(
     ) {
       let tagPos = triggerText.lastIndexOf('<' + nsPrefix + 
nearestTagNotClosed)
       let tagEndPos = triggerText.indexOf('>', tagPos)
+      // let range = new vscode.Range(backpos, position)
+      // await vscode.window.activeTextEditor?.edit((editBuilder) => {
+      //   editBuilder.replace(range, '')
+      // })
 
       if (
         tagPos != -1 &&
diff --git a/src/language/providers/closeUtils.ts 
b/src/language/providers/closeUtils.ts
index 1b931cf..65f238b 100644
--- a/src/language/providers/closeUtils.ts
+++ b/src/language/providers/closeUtils.ts
@@ -336,6 +336,7 @@ export function getItemsForLineLT2(
             testLine = lineBefore
             while (!testText.includes('>')) {
               testText = document.lineAt(++testLine).text
+              if (testText.indexOf('<') > -1) [openTagArray.push(testLine)]
             }
           }
 
diff --git a/src/language/providers/elementCompletion.ts 
b/src/language/providers/elementCompletion.ts
index f4eed6f..63669eb 100644
--- a/src/language/providers/elementCompletion.ts
+++ b/src/language/providers/elementCompletion.ts
@@ -21,6 +21,7 @@ import {
   checkBraceOpen,
   getXsdNsPrefix,
   isInXPath,
+  isTagEndTrigger,
   nearestOpen,
   createCompletionItem,
   getCommonItems,
@@ -33,68 +34,74 @@ import {
 import { elementCompletion } from './intellisense/elementItems'
 
 export function getElementCompletionProvider(dfdlFormatString: string) {
-  return vscode.languages.registerCompletionItemProvider('dfdl', {
-    provideCompletionItems(
-      document: vscode.TextDocument,
-      position: vscode.Position,
-      token: vscode.CancellationToken,
-      context: vscode.CompletionContext
-    ) {
-      if (
-        checkBraceOpen(document, position) ||
-        cursorWithinBraces(document, position) ||
-        cursorWithinQuotes(document, position) ||
-        cursorAfterEquals(document, position) ||
-        isInXPath(document, position)
+  return vscode.languages.registerCompletionItemProvider(
+    'dfdl',
+    {
+      provideCompletionItems(
+        document: vscode.TextDocument,
+        position: vscode.Position,
+        token: vscode.CancellationToken,
+        context: vscode.CompletionContext
       ) {
-        return undefined
-      }
-
-      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)
-      let lastCloseSymbol = triggerText.lastIndexOf('>')
-      let firstOpenSymbol = triggerText.indexOf('<')
-
-      let missingCloseTag = checkMissingCloseTag(document, position, nsPrefix)
-
-      if (nearestOpenItem.includes('none')) {
-        if (missingCloseTag !== 'none') {
-          return undefined
-        }
         if (
-          missingCloseTag === 'none' &&
-          itemsOnLine > 1 &&
-          (triggerPos === lastCloseSymbol + 1 || triggerPos === 
firstOpenSymbol)
+          !checkBraceOpen(document, position) &&
+          !cursorWithinBraces(document, position) &&
+          !cursorWithinQuotes(document, position) &&
+          !cursorAfterEquals(document, position) &&
+          !isInXPath(document, position) &&
+          !isTagEndTrigger(document, position)
         ) {
-          return undefined
-        }
-
-        let definedVariables = getDefinedVariables(document)
+          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)
+          let lastCloseSymbol = triggerText.lastIndexOf('>')
+          let firstOpenSymbol = triggerText.indexOf('<')
+
+          let missingCloseTag = checkMissingCloseTag(
+            document,
+            position,
+            nsPrefix
+          )
 
-        let [tagNearestTrigger, tagPosition] = getTagNearestTrigger(
-          document,
-          position,
-          triggerText,
-          triggerLine,
-          triggerPos,
-          itemsOnLine,
-          nsPrefix
-        )
+          if (!nearestOpenItem.includes('none') && missingCloseTag == 'none') {
+            return undefined
+          }
+          if (
+            missingCloseTag === 'none' &&
+            itemsOnLine > 1 &&
+            (triggerPos === lastCloseSymbol + 1 ||
+              triggerPos === firstOpenSymbol)
+          ) {
+            return undefined
+          }
+
+          let definedVariables = getDefinedVariables(document)
+
+          let [tagNearestTrigger, tagPosition] = getTagNearestTrigger(
+            document,
+            position,
+            triggerText,
+            triggerLine,
+            triggerPos,
+            itemsOnLine,
+            nsPrefix
+          )
 
-        return nearestOpenTagChildElements(
-          document,
-          position,
-          tagNearestTrigger,
-          tagPosition,
-          definedVariables,
-          nsPrefix
-        )
-      }
+          return nearestOpenTagChildElements(
+            document,
+            position,
+            tagNearestTrigger,
+            tagPosition,
+            definedVariables,
+            nsPrefix
+          )
+        }
+      },
     },
-  })
+    '\n'
+  )
 }
 
 export function getTDMLElementCompletionProvider(tdmlFormatString: string) {
@@ -386,6 +393,10 @@ function nearestOpenTagChildElements(
       return getElementCompletionItems(['dfdl:escapeScheme'], '', '', nsPrefix)
     case 'format':
       return getElementCompletionItems(['dfdl:property'], '', '', nsPrefix)
+    case 'include':
+      return getElementCompletionItems([''], '', '', nsPrefix)
+    case 'import':
+      return getElementCompletionItems([''], '', '', nsPrefix)
     case 'schema':
       return getElementCompletionItems(
         [
@@ -396,6 +407,8 @@ function nearestOpenTagChildElements(
           'complexType',
           'simpleType',
           'annotation',
+          'include',
+          'import',
         ],
         '',
         '',
diff --git a/src/language/providers/intellisense/attributeHoverItems.ts 
b/src/language/providers/intellisense/attributeHoverItems.ts
index 4395c67..baf4dfd 100644
--- a/src/language/providers/intellisense/attributeHoverItems.ts
+++ b/src/language/providers/intellisense/attributeHoverItems.ts
@@ -225,6 +225,10 @@ export function attributeHoverValues(attributeName: 
string): string {
       return 'Defines text for use in an error message'
     case 'failureType':
       return 'Specifies the type of failure that occurs when the dfdl:assert 
is unsuccessful'
+    case 'schemaLocation':
+      return 'Specifies the location of the schema'
+    case 'namespace':
+      return 'User defined identifier for the namespace defined by 
schemaLocation value'
     default:
       return 'No definition available'
   }
diff --git a/src/language/providers/intellisense/attributeItems.ts 
b/src/language/providers/intellisense/attributeItems.ts
index b999a41..be54936 100644
--- a/src/language/providers/intellisense/attributeItems.ts
+++ b/src/language/providers/intellisense/attributeItems.ts
@@ -545,6 +545,16 @@ export const attributeCompletion = (
         snippetString: spacingChar + 
'failureType="${1|processingError,recoverableError|}"$0' + afterChar,
         markdownString: 'Specifies the type of failure that occurs when the 
dfdl:assert is unsuccessful',
       },
+      {
+        item: 'schemaLocation',
+        snippetString: spacingChar + 'schemaLocation="$1"$0' + afterChar,
+        markdownString: 'Specifies the location of the schema'
+      },
+      {
+        item: 'namespace',
+        snippetString: spacingChar + 'namespace="$1"$0' + afterChar,
+        markdownString: 'User defined identifier for the imported 
schemaLocation'
+      }
     ],
   }
 }
diff --git a/src/language/providers/intellisense/attributeValueItems.ts 
b/src/language/providers/intellisense/attributeValueItems.ts
index 8e757f2..30ca943 100644
--- a/src/language/providers/intellisense/attributeValueItems.ts
+++ b/src/language/providers/intellisense/attributeValueItems.ts
@@ -65,6 +65,7 @@ export const noChoiceAttributes = [
   'source',
   'schemaLocation',
   'targetNamespace',
+  'namespace',
 ]
 
 export function attributeValues(
diff --git a/src/language/providers/intellisense/elementItems.ts 
b/src/language/providers/intellisense/elementItems.ts
index cf13b6e..3eb281f 100644
--- a/src/language/providers/intellisense/elementItems.ts
+++ b/src/language/providers/intellisense/elementItems.ts
@@ -207,7 +207,7 @@ export const elementCompletion = (definedVariables, 
nsPrefix) => {
         markdownString: 'Used to restrict a datatype to a finite set of values'
       },
       {
-        item: 'include',
+        item: nsPrefix + 'include',
         snippetString: '<' + nsPrefix + 'include "$1"/>$0',
         markdownString: 'Used to add all the components of an included schema'
       },
@@ -216,7 +216,7 @@ export const elementCompletion = (definedVariables, 
nsPrefix) => {
         snippetString: '<' + nsPrefix + 
'documentation>\n\t$1\n</documentation>$0'
       },
       {
-        item: 'import',
+        item: nsPrefix + 'import',
         snippetString: '<' + nsPrefix + 'import "$1"/>$0',
         markdownString: 'Used to add all the components of an included schema'
       },
diff --git a/src/language/providers/utils.ts b/src/language/providers/utils.ts
index f083d61..88118d9 100644
--- a/src/language/providers/utils.ts
+++ b/src/language/providers/utils.ts
@@ -45,6 +45,8 @@ const items = [
   'restriction',
   'schema',
   'xml version',
+  'include',
+  'import',
 ]
 
 export function getItems() {
@@ -202,24 +204,16 @@ export function checkTagOpen(
   let triggerText = document.lineAt(triggerLine).text
   let itemsOnLine = getItemsOnLineCount(triggerText)
   let isMultiLineTag = false
-  let origTriggerText = triggerText
   let origTriggerLine = triggerLine
+  let compareText = triggerText
+  let compareLine = triggerLine
   const triggerPos = position.character
   const textBeforeTrigger = triggerText.substring(0, triggerPos)
 
-  while (
-    itemsOnLine < 2 &&
-    !triggerText.trim().startsWith('<') &&
-    !triggerText.includes('/>') &&
-    !triggerText.includes('<')
-  ) {
+  while (itemsOnLine < 2 && !triggerText.trim().startsWith('<')) {
     triggerText = document.lineAt(--triggerLine).text
   }
 
-  if (triggerText.includes('/>') || triggerText.includes('</')) {
-    return false
-  }
-
   if (!(triggerText.endsWith('>') && triggerText.includes('<'))) {
     isMultiLineTag = true
   }
@@ -239,21 +233,21 @@ export function checkTagOpen(
     }
   }
 
-  while (origTriggerText.trim() === '') {
-    origTriggerText = document.lineAt(--origTriggerLine).text
+  while (compareText.trim() === '') {
+    compareText = document.lineAt(--compareLine).text
   }
   tagPos = triggerText.indexOf('<' + nsPrefix + tag)
 
   if (itemsOnLine < 2 && tagPos > -1) {
-    if (triggerText !== origTriggerText) {
-      tagEndPos = origTriggerText.indexOf('>')
+    if (triggerText !== compareText) {
+      tagEndPos = compareText.indexOf('>')
     }
 
     if (
       (triggerPos > tagPos &&
         triggerPos <= tagEndPos &&
         triggerLine === position.line) ||
-      (origTriggerLine == position.line &&
+      (compareLine == position.line &&
         triggerPos <= tagEndPos &&
         triggerPos > tagPos) ||
       position.line < origTriggerLine
@@ -315,21 +309,27 @@ export function checkMultiLineTag(
     return false
   }
   let currentLine = position.line
+  let openTagLine = position.line
+  let closeTagLine = position.line
   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('<')) {
-      --currentLine
-      currentText = document.lineAt(currentLine).text
+      --openTagLine
+      currentText = document.lineAt(openTagLine).text
+      if (currentText.includes('/>')) {
+        closeTagLine = openTagLine
+      }
     }
 
     if (
       currentText.indexOf('<' + nsPrefix + tag) !== -1 &&
       currentText.indexOf('>') === -1 &&
       currentText.indexOf('<' + nsPrefix + tag) &&
-      currentLine <= position.line &&
+      openTagLine <= position.line &&
+      closeTagLine >= position.line &&
       (origText.indexOf('>') > position.character ||
         origText.indexOf('>') === -1)
     ) {
@@ -380,7 +380,10 @@ export function getItemsOnLineCount(triggerText: String) {
   let nextPos = 0
   let result = 0
 
-  if (triggerText.includes('schema')) {
+  if (
+    triggerText.includes('schema') &&
+    !triggerText.includes('schemaLocation')
+  ) {
     itemsOnLine = 1
     return itemsOnLine
   }
@@ -419,6 +422,40 @@ export function isInXPath(
   return isXPath(position)
 }
 
+export function isNotTriggerChar(
+  document: vscode.TextDocument,
+  position: vscode.Position,
+  triggerChar: string
+) {
+  const triggerText = document.lineAt(position.line).text
+  const triggerPos = position.character
+  const trigChar = triggerText.substring(triggerPos - 1, triggerPos)
+  if (trigChar != triggerChar) {
+    return true
+  } else {
+    return false
+  }
+}
+
+export function isTagEndTrigger(
+  document: vscode.TextDocument,
+  position: vscode.Position
+) {
+  const triggerText = document.lineAt(position.line).text
+  const triggerPos = position.character
+  const trigChar = triggerText.substring(triggerPos - 1, triggerPos)
+  if (
+    trigChar == '/' ||
+    (trigChar == '>' &&
+      !triggerText.includes('/>') &&
+      !triggerText.includes('</'))
+  ) {
+    return true
+  } else {
+    return false
+  }
+}
+
 export function cursorAfterEquals(
   document: vscode.TextDocument,
   position: vscode.Position
@@ -457,7 +494,14 @@ export function cursorWithinQuotes(
 
     if (currentText.includes(quoteChar[i])) {
       let textBeforeTrigger = currentText.substring(0, position.character)
-      //let tagStartPos = -1
+
+      if (
+        currentText.indexOf('=' + quoteChar[i]) > position.character &&
+        textBeforeTrigger.trim() == ''
+      ) {
+        return false
+      }
+
       let quoteStartLine = startLine
       let quoteStartPos = -1
       let equalStartPos = -1
diff --git a/src/tests/suite/language/items.test.ts 
b/src/tests/suite/language/items.test.ts
index c771404..d4053bb 100644
--- a/src/tests/suite/language/items.test.ts
+++ b/src/tests/suite/language/items.test.ts
@@ -172,6 +172,8 @@ suite('Items Test Suite', () => {
     'testPattern',
     'message',
     'failureType',
+    'schemaLocation',
+    'namespace',
   ]
 
   test('all commonItems available', async () => {

Reply via email to