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

jeremyyao 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 3ec9f1b  Document IntelliSense functionallity for 
CloseElementSlashProvider in closeElementSlashProvider.ts and closeUtils.ts.
3ec9f1b is described below

commit 3ec9f1baa13a93213cb4df8bea1a8d0506ca7d18
Author: Jeremy Yao <[email protected]>
AuthorDate: Mon Jan 19 03:33:28 2026 -0500

    Document IntelliSense functionallity for CloseElementSlashProvider in 
closeElementSlashProvider.ts and closeUtils.ts.
    
    Closes #1492
---
 src/language/intellisense-development.md    | 115 ++++++++++++++++++++++
 src/language/providers/closeElementSlash.ts | 142 ++++++++++++++++++++++------
 src/language/providers/closeUtils.ts        | 138 +++++++++++++++++++++++----
 3 files changed, 348 insertions(+), 47 deletions(-)

diff --git a/src/language/intellisense-development.md 
b/src/language/intellisense-development.md
index 730317d..6ca382c 100644
--- a/src/language/intellisense-development.md
+++ b/src/language/intellisense-development.md
@@ -41,6 +41,8 @@ This document contains an overview of how Intellisense works, 
as well as a gener
           - [attributeCompletion.ts](#attributecompletionts)
           - [attributeHover.ts](#attributehoverts)
           - [attributeValueCompletion.ts](#attributevaluecompletionts)
+          - [closeElementSlash.ts](#closeelementslashts)
+          - [closeUtils.ts](#closeutilsts)
         - [Intellisense Data Files (intellisense 
subdirectory)](#intellisense-data-files-intellisense-subdirectory)
           - [attributeItems.ts](#attributeitemsts)
           - [attributeValueItems.ts](#attributevalueitemsts)
@@ -196,6 +198,119 @@ Hover tooltips can be found under 
`attributeHoverValues()` in `attributeHoverIte
 
 **Trigger:** Space (` `)
 
+###### closeElementSlash.ts
+
+**Purpose:** Completion provider that handles auto-closing XML/DFDL elements 
when the user types a forward slash '/' character, determining whether to 
insert self-closing tags (`/>`) or full closing tags (`</tag>`).
+
+**Key Functionality:**
+
+- Provides intelligent slash-based auto-completion for both DFDL and TDML files
+- Determines whether to insert self-closing (`/>$0`) or full closing tags 
(`</ns:tag>$0`) based on context
+- Prevents inappropriate completion inside XPath expressions, quoted strings, 
braces, or after equals signs
+- Handles complex multi-tag lines by analyzing tag positions and existing 
closing tags
+- Special case handling for variable-related tags (`defineVariable`, 
`setVariable`) with added newlines for readability
+- Direct document manipulation using snippet insertion rather than traditional 
completion suggestions
+
+**Key Functions:**
+
+- `getCloseElementSlashProvider()`: Main DFDL completion provider registration
+- `getTDMLCloseElementSlashProvider()`: TDML-specific completion provider 
registration
+- `checkItemsOnLine()`: Core logic determining what to insert based on tag 
count, namespace, and context
+
+**Trigger:** Forward slash (`/`)
+
+**Architecture Notes:**
+
+- Uses **guard clauses** for early returns in inappropriate contexts
+- Implements **dual strategy**: simpler TDML provider vs. more robust DFDL 
provider
+- Follows VS Code provider pattern but uses **direct edits** (`insertSnippet`) 
instead of completion lists
+- Separation of concerns: context validation in providers, insertion logic in 
`checkItemsOnLine()`
+
+**Dependencies:**
+
+- `closeUtils.checkMissingCloseTag()`: Determines if a tag needs closing
+- `utils.insertSnippet()`: Performs the actual text insertion
+- Multiple context validators: `checkBraceOpen()`, `cursorWithinBraces()`, 
`cursorWithinQuotes()`, `cursorAfterEquals()`, `isInXPath()`, 
`isNotTriggerChar()`
+- Namespace utilities: `getNsPrefix()`, `getItemPrefix()`, 
`getItemsOnLineCount()`
+
+**Flow:**
+
+1. User types '/' at cursor position
+2. Provider validates context (not in XPath, quotes, braces, etc.)
+3. `checkMissingCloseTag()` scans document to find nearest unclosed tag
+4. If tag needs closing, removes the trigger '/' to prevent duplicates
+5. `checkItemsOnLine()` decides: self-closing tag, full closing tag, or nothing
+6. Inserts appropriate snippet at the correct position
+
+###### closeUtils.ts
+
+**Purpose:** Core utility module providing tag state analysis functions for 
the auto-completion system. Determines whether XML/DFDL tags are properly 
closed and identifies unclosed tags that need completion.
+
+**Key Functionality:**
+
+- **Tag State Analysis**: `checkMissingCloseTag()` is the primary function 
that scans documents to find opened-but-unclosed tags
+- **Dual Strategy Implementation**: Uses different algorithms for single-tag 
lines vs. multi-tag lines:
+  - `getItemsForLineGT1()`: Array-based position tracking for complex 
multi-tag scenarios
+  - `getItemsForLineLT2()`: Bidirectional document scanning for simpler 
single-tag situations
+- **Nested Tag Awareness**: Properly tracks nesting levels to handle complex 
XML structures with same-named nested elements
+- **Multi-line Tag Support**: Extensive logic for tags spanning multiple lines 
(common in DFDL with many attributes)
+- **Namespace Prefix Management**: Dynamically adjusts namespace prefixes 
based on tag types and document context
+- **Closing Tag Location**: `getCloseTag()` finds exact positions of closing 
tags for document structure analysis
+- **Cursor Context Validation**: `cursorInsideCloseTag()` prevents 
interference when user is manually typing closing tags
+
+**Key Functions:**
+
+- `checkMissingCloseTag()`: Main entry point - returns name of unclosed tag or 
'none'
+- `checkItemsForLineGT1()`: Multi-tag line analysis using opening/closing 
position arrays
+- `checkItemsForLineLT2()`: Single-tag line analysis with forward/backward 
scanning
+- `getCloseTag()`: Locates closing tag positions with nested tag tracking
+- `cursorInsideCloseTag()`: Checks if cursor is positioned inside a closing tag
+- `getItemsForLineGT1()`: Complex multi-tag line parser
+- `getItemsForLineLT2()`: Simple single-tag line parser
+
+**Algorithm Details:**
+
+**`getItemsForLineGT1()` (Multi-tag lines):**
+
+1. Builds arrays of all opening and closing tag positions on the line
+2. Filters out self-closing tags from the opening array
+3. Compares array lengths - more opens than closes = unclosed tag
+4. Efficient for complex scenarios but requires careful position tracking
+
+**`getItemsForLineLT2()` (Single-tag lines):**
+
+1. Scans backwards to find the opening tag
+2. Scans both directions (backwards and forwards) to collect all 
opening/closing tags
+3. Handles multi-line tags by traversing until finding closing `>`
+4. Removes self-closing tags from tracking
+5. Compares open/close counts across the entire document context
+
+**`getCloseTag()` (Closing tag locator):**
+
+1. Tracks `nestedTagCount` for proper nested element handling
+2. Skips comment blocks (`<!-- -->`) to avoid false positives
+3. Handles multi-line tags by aggregating text across lines
+4. Returns precise line and character position of closing tags
+
+**Dependencies:**
+
+- `utils.getItemsOnLineCount()`: Counts XML items on a line
+- `utils.getItemPrefix()`: Determines correct namespace prefix for tags
+- `utils.getItems()`: Gets list of all DFDL/XSD tag names
+
+**Integration:**
+
+- Called exclusively by `closeElementSlash.ts` providers
+- Provides the "brains" of the auto-completion system
+- Returns tag names that drive snippet insertion decisions
+
+**Performance Considerations:**
+
+- Early returns when tag is found
+- Avoids full document parsing by scanning only relevant sections
+- Skips complex multi-tag lines in single-tag mode
+- Uses efficient string search methods (`indexOf`, `lastIndexOf`)
+
 ##### Intellisense Data Files (intellisense subdirectory)
 
 ###### attributeItems.ts
diff --git a/src/language/providers/closeElementSlash.ts 
b/src/language/providers/closeElementSlash.ts
index c2fff16..3f1a30e 100644
--- a/src/language/providers/closeElementSlash.ts
+++ b/src/language/providers/closeElementSlash.ts
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 import * as vscode from 'vscode'
 import { checkMissingCloseTag } from './closeUtils'
 import {
@@ -29,56 +28,91 @@ import {
   cursorWithinQuotes,
   cursorAfterEquals,
 } from './utils'
-
+/**
+ * Creates and returns a completion item provider for the 'dfdl' language
+ * that triggers when the user types a forward slash '/' to close XML elements.
+ * This handles DFDL schema files (Data Format Description Language).
+ *
+ * @returns vscode.CompletionItemProvider - A provider that listens for '/' 
triggers
+ */
 export function getCloseElementSlashProvider() {
   return vscode.languages.registerCompletionItemProvider(
     'dfdl',
     {
+      /**
+       * Provides completion items when '/' is typed in a DFDL document.
+       * Determines whether to insert a self-closing tag "/>" or a full 
closing tag.
+       *
+       * @param document - The active text document
+       * @param position - The current cursor position where '/' was typed
+       * @returns Promise<void> | undefined - Returns undefined or no result; 
edits are applied directly
+       */
       async provideCompletionItems(
         document: vscode.TextDocument,
         position: vscode.Position
       ) {
+        // Create position one character back from cursor to capture the 
trigger point
         let backpos = new vscode.Position(position.line, position.character - 
1)
+
+        // Get the namespace prefix (e.g., 'xs:', 'dfdl:', or empty string)
         let nsPrefix = getNsPrefix(document, position)
+
+        // Get the full line text where the trigger occurred
         let triggerText = document.lineAt(position.line).text
+
+        // Find the position of the last opening tag with namespace prefix
         let tagPos = triggerText.lastIndexOf('<' + nsPrefix + ':')
+
+        // This line appears to be incomplete/unnecessary - likely a 
copy-paste error
+        // It searches for comma+prefix but doesn't store or use the result
         triggerText.lastIndexOf(',' + nsPrefix + ':')
+
+        // If no tag with the detected namespace is found, try default dfdl 
namespace
         if (tagPos < 0) {
           tagPos = triggerText.lastIndexOf('<dfdl:')
           if (tagPos > 0) {
             nsPrefix = 'dfdl:'
           }
         }
-
+        // Re-extract text from cursor position start to current position
         triggerText = document
           .lineAt(position)
           .text.substring(0, position.character)
+
+        // Check if there's an unclosed tag in the document up to this point
         let nearestTagNotClosed = checkMissingCloseTag(
           document,
           position,
           nsPrefix
         )
+
+        // Count how many XML items exist on the current line
         const itemsOnLine = getItemsOnLineCount(triggerText)
+
         const triggerChar = '/'
+
+        // **GUARD CLAUSES**: Return early if any of these conditions are true
+        // These prevent auto-completion in contexts where it would be 
inappropriate
         if (
-          checkBraceOpen(document, position) ||
-          cursorWithinBraces(document, position) ||
-          cursorWithinQuotes(document, position) ||
-          cursorAfterEquals(document, position) ||
-          isInXPath(document, position) ||
-          isNotTriggerChar(document, position, triggerChar)
+          checkBraceOpen(document, position) || // Inside unclosed curly 
braces {}
+          cursorWithinBraces(document, position) || // Cursor positioned 
within braces
+          cursorWithinQuotes(document, position) || // Inside quoted attribute 
values
+          cursorAfterEquals(document, position) || // Immediately after an 
equals sign
+          isInXPath(document, position) || // Within an XPath expression
+          isNotTriggerChar(document, position, triggerChar) // Verify '/' is 
actually the trigger char
         ) {
-          return undefined
+          return undefined // Don't provide completion
         }
-
+        // If there's a tag that needs closing, remove the '/' that was just 
typed
+        // This prevents duplicate slashes when inserting the proper closing 
syntax
         if (!(nearestTagNotClosed == 'none')) {
           let range = new vscode.Range(backpos, position)
-
+          // Perform the edit to remove the trigger character
           await vscode.window.activeTextEditor?.edit((editBuilder) => {
             editBuilder.replace(range, '')
           })
         }
-
+        // Main logic: If the line ends with '/', handle the completion
         if (triggerText.endsWith('/')) {
           checkItemsOnLine(
             document,
@@ -90,45 +124,69 @@ export function getCloseElementSlashProvider() {
             triggerText
           )
         }
-
+        // Return undefined as completions are handled via snippet insertion, 
not suggestions
         //return undefined
       },
     },
-    '/'
+    '/' // Trigger character for this completion provider
     // triggered whenever a '/' is typed
   )
 }
-
+/**
+ * Creates and returns a completion item provider for the 'tdml' language
+ * that triggers when the user types a forward slash '/' to close XML elements.
+ * This handles TDML (Test Data Markup Language) files.
+ *
+ * @returns vscode.CompletionItemProvider - A provider that listens for '/' 
triggers
+ */
 export function getTDMLCloseElementSlashProvider() {
   return vscode.languages.registerCompletionItemProvider(
     'tdml',
     {
+      /**
+       * Provides completion items when '/' is typed in a TDML document.
+       * Similar to DFDL provider but with slightly different guard conditions.
+       *
+       * @param document - The active text document
+       * @param position - The current cursor position where '/' was typed
+       * @returns undefined - Edits are applied directly, no completion list 
shown
+       */
       async provideCompletionItems(
         document: vscode.TextDocument,
         position: vscode.Position
       ) {
+        // Position one character back from cursor
         let backpos = position.with(position.line, position.character - 1)
+
+        // Get namespace prefix (TDML likely uses different namespaces than 
DFDL)
         const nsPrefix = getNsPrefix(document, position)
+
+        // Get text from line start to cursor
         const triggerText = document
           .lineAt(position)
           .text.substring(0, position.character)
+
+        // Check for unclosed tags
         let nearestTagNotClosed = checkMissingCloseTag(
           document,
           position,
           nsPrefix
         )
-        const itemsOnLine = getItemsOnLineCount(triggerText)
 
+        // Count items on current line
+        const itemsOnLine = getItemsOnLineCount(triggerText)
+        // **GUARD CLAUSES**: Similar to DFDL provider but without trigger 
char check
+        // TDML provider has slightly less restrictive conditions
         if (
-          checkBraceOpen(document, position) ||
-          cursorWithinBraces(document, position) ||
-          cursorWithinQuotes(document, position) ||
-          cursorAfterEquals(document, position) ||
-          isInXPath(document, position)
+          checkBraceOpen(document, position) || // Inside unclosed braces
+          cursorWithinBraces(document, position) || // Within braces region
+          cursorWithinQuotes(document, position) || // Inside quotes
+          cursorAfterEquals(document, position) || // After equals sign
+          isInXPath(document, position) // In XPath context
         ) {
-          return undefined
+          return undefined // Don't provide completion
         }
-
+        // Handle the slash completion if line ends with '/'
         if (triggerText.endsWith('/')) {
           checkItemsOnLine(
             document,
@@ -140,15 +198,26 @@ export function getTDMLCloseElementSlashProvider() {
             triggerText
           )
         }
-
+        // No completion list returned - edits applied directly
         return undefined
       },
     },
-    '/'
+    '/' // Trigger character
     // triggered whenever a '/' is typed
   )
 }
-
+/**
+ * Determines the appropriate closing syntax to insert when '/' is typed.
+ * Decides between self-closing "/>" and full closing tags based on context.
+ *
+ * @param document - The active text document
+ * @param position - Current cursor position
+ * @param itemsOnLine - Number of XML items/tags on the current line
+ * @param nearestTagNotClosed - The nearest tag that hasn't been closed yet
+ * @param backpos - Position just before the '/' character
+ * @param nsPrefix - Namespace prefix (e.g., 'xs:', 'dfdl:')
+ * @param triggerText - Text from line start to cursor position
+ */
 function checkItemsOnLine(
   document: vscode.TextDocument,
   position: vscode.Position,
@@ -158,30 +227,41 @@ function checkItemsOnLine(
   nsPrefix: string,
   triggerText: string
 ) {
+  // Adjust namespace prefix based on the tag type (some tags always use dfdl:)
   nsPrefix = getItemPrefix(nearestTagNotClosed, nsPrefix)
-
+  // **CASE 1**: Single tag on line (or zero) AND there's a tag that needs 
closing
+  // This typically means we're at the end of a tag like <element| (cursor at 
|)
   if (
     !(nearestTagNotClosed == 'none') &&
     (itemsOnLine == 1 || itemsOnLine == 0)
   ) {
+    // Special handling for variable-related tags: add newline after 
self-closing
+    // This follows DFDL best practices for readability
     if (
       nearestTagNotClosed.includes('defineVariable') ||
       nearestTagNotClosed.includes('setVariable')
     ) {
       insertSnippet('/>\n', backpos)
     } else {
+      // Standard self-closing tag with cursor positioned after
       insertSnippet('/>$0', backpos)
     }
   }
-
+  // **CASE 2**: Multiple items on the same line
+  // This is more complex - need to determine if we're inside a tag or need 
full close
   if (itemsOnLine > 1) {
     if (
       triggerText.endsWith('/') &&
       triggerText.includes('<' + nsPrefix + nearestTagNotClosed)
     ) {
+      // Find where this specific tag starts on the line
       let tagPos = triggerText.lastIndexOf('<' + nsPrefix + 
nearestTagNotClosed)
       let tagEndPos = triggerText.indexOf('>', tagPos)
-
+      // **CONDITIONS FOR FULL CLOSING TAG**:
+      // 1. Tag exists on line, AND
+      // 2. Tag is NOT already self-closing (no "/>"), AND
+      // 3. Cursor is positioned AFTER the opening ">", AND
+      // 4. No closing tag exists yet for this element
       if (
         tagPos != -1 &&
         !triggerText.substring(tagEndPos - 1, 2).includes('/>') &&
@@ -192,8 +272,10 @@ function checkItemsOnLine(
           .substring(tagEndPos)
           .includes('</' + nsPrefix + nearestTagNotClosed)
       ) {
+        // Insert full closing tag (e.g., </xs:element>)
         insertSnippet('</' + nsPrefix + nearestTagNotClosed + '>$0', backpos)
       } else {
+        // Default to self-closing tag
         insertSnippet('/>$0', backpos)
       }
     }
diff --git a/src/language/providers/closeUtils.ts 
b/src/language/providers/closeUtils.ts
index 297e1c3..a715ecd 100644
--- a/src/language/providers/closeUtils.ts
+++ b/src/language/providers/closeUtils.ts
@@ -18,24 +18,43 @@
 import * as vscode from 'vscode'
 import { getItemsOnLineCount, getItemPrefix, getItems } from './utils'
 
+/**
+ * Main function to determine if there's an unclosed XML/DFDL tag at the 
cursor position.
+ * This is the core logic that drives the auto-completion behavior in 
closeElementSlash.ts.
+ * It scans the document to find the nearest tag that has been opened but not 
closed.
+ *
+ * @param document - The active text document
+ * @param position - The current cursor position
+ * @param nsPrefix - The XML namespace prefix (e.g., 'xs:', 'dfdl:', or empty 
string)
+ * @returns string - The name of the unclosed tag, or 'none' if all tags are 
closed
+ */
 export function checkMissingCloseTag(
   document: vscode.TextDocument,
   position: vscode.Position,
   nsPrefix: string
 ) {
+  // Extract context about the current line
   const triggerLine = position.line
   const triggerPos = position.character
   const triggerText = document.lineAt(triggerLine).text
   const itemsOnLine = getItemsOnLineCount(triggerText)
-  const origPrefix = nsPrefix
+  const origPrefix = nsPrefix // Preserve original prefix for reference
 
+  // Get the list of all DFDL/XML items/tags defined in the extension
   const items = getItems()
+
+  // Iterate through all possible tag types to find any unclosed instances
   for (let i = 0; i < items.length; ++i) {
+    // Get text before cursor to analyze incomplete tags
     const textBeforeTrigger = triggerText.substring(0, triggerPos)
 
+    // Adjust namespace prefix based on tag type (some tags force 'dfdl:' 
prefix)
     nsPrefix = getItemPrefix(items[i], origPrefix)
+
+    // Find the last occurrence of this specific tag before the cursor
     let tagPos = triggerText.lastIndexOf('<' + nsPrefix + items[i])
 
+    // Fallback: if not found with current prefix, try 'dfdl:' prefix
     if (tagPos < 0) {
       tagPos = triggerText.lastIndexOf('<dfdl:' + items[i])
       if (tagPos > 0) {
@@ -43,8 +62,12 @@ export function checkMissingCloseTag(
       }
     }
 
+    // **BRANCH 1**: Multiple items on the current line
+    // This is a complex case requiring careful parsing to determine which tag 
is active
     if (itemsOnLine > 1) {
+      // Check if this tag appears before the cursor position
       if (textBeforeTrigger.lastIndexOf('<' + nsPrefix + items[i]) > -1) {
+        // Use specialized logic for multi-tag lines
         let gt1res = getItemsForLineGT1(
           triggerText,
           triggerPos,
@@ -53,12 +76,15 @@ export function checkMissingCloseTag(
           i
         )
 
+        // If an unclosed tag was found, return it immediately
         if (gt1res != 'none') {
           return gt1res
         }
       }
     }
 
+    // **BRANCH 2**: Single item (or zero) on the current line
+    // This is simpler - scan upwards through the document
     if (itemsOnLine < 2) {
       let lt2res = getItemsForLineLT2(
         document,
@@ -69,24 +95,37 @@ export function checkMissingCloseTag(
         i
       )
 
+      // If an unclosed tag was found, return it immediately
       if (lt2res != 'none') {
         return lt2res
       }
     }
   }
 
+  // No unclosed tags found
   return 'none'
 }
 
+/**
+ * Checks if the cursor is currently positioned inside a closing tag.
+ * Used to prevent auto-completion from interfering when manually typing 
closing tags.
+ *
+ * @param document - The active text document
+ * @param position - The current cursor position
+ * @returns boolean - True if cursor is inside a closing tag (between </ and >)
+ */
 export function cursorInsideCloseTag(
   document: vscode.TextDocument,
   position: vscode.Position
 ) {
   const triggerText = document.lineAt(position.line).text
   const triggerPos = position.character
+
+  // Find positions of closing tag markers
   const closeTagStart = triggerText.lastIndexOf('</')
   const closeTagEnd = triggerText.lastIndexOf('>')
 
+  // Cursor is inside closing tag if it's after </ but before or at >
   if (
     triggerPos > closeTagStart &&
     triggerPos <= closeTagEnd &&
@@ -97,6 +136,18 @@ export function cursorInsideCloseTag(
   return false
 }
 
+/**
+ * Locates the closing tag for a given tag name and position.
+ * Used to find where a tag is closed to understand document structure.
+ *
+ * @param document - The active text document
+ * @param position - The current cursor position
+ * @param nsPrefix - The namespace prefix for the tag
+ * @param tag - The tag name to find the closing tag for
+ * @param startLine - Starting line number for the search
+ * @param startPos - Starting character position for the search
+ * @returns [string, number, number] - Tuple: [tagName, lineNumber, position] 
of closing tag, or ['none', 0, 0] if not found
+ */
 export function getCloseTag(
   document: vscode.TextDocument,
   position: vscode.Position,
@@ -112,19 +163,26 @@ export function getCloseTag(
   const triggerText = document.lineAt(startLine).text
   const itemsOnLine = getItemsOnLineCount(document.lineAt(lineNum).text)
   let endPos = triggerText.lastIndexOf('>')
+
+  // Adjust prefix based on tag type
   nsPrefix = getItemPrefix(tag, nsPrefix)
 
+  // If cursor is inside a closing tag, return 'none' to avoid interfering
   if (itemsOnLine === 1) {
     if (cursorInsideCloseTag(document, position))
       return ['none', lineNum, startPos]
   }
 
+  // **CASE: Multiple items on line and cursor is inside a tag**
+  // Need to parse through the line to find which tag is being closed
   if (itemsOnLine > 1 && startPos < endPos) {
+    // Iterate through tags on the line
     while (tagOpen > -1 && tagOpen <= triggerPos) {
       tagOpen = triggerText.indexOf('<', tagOpen)
       let tagClose = triggerText.indexOf('>', tagOpen)
       let tagPart = triggerText.substring(tagOpen, tagClose)
 
+      // Check if this segment is the closing tag we're looking for
       if (
         tagPart.includes(tag) &&
         (tagPart.includes('</') || tagPart.includes('/>'))
@@ -135,13 +193,16 @@ export function getCloseTag(
       tagOpen = tagClose + 1
     }
   } else {
-    let nestedTagCount = 0
+    // **CASE: Single item per line - scan through document**
+    let nestedTagCount = 0 // Track nesting level for proper tag matching
     let endPos = triggerText.indexOf('>', startPos)
 
+    // Special case: XML declaration tag
     if (triggerText.includes('?xml version')) {
       return [tag, 0, 0]
     }
 
+    // If tag is already closed on this line, return it
     if (
       (triggerText.includes('</') || triggerText.includes('/>')) &&
       triggerText.includes(tag) &&
@@ -151,11 +212,12 @@ export function getCloseTag(
       return [tag, startLine, startPos]
     }
 
+    // **MAIN LOOP**: Scan forward through document to find closing tag
     while (lineNum > -1 && lineNum < document.lineCount) {
       let currentText = document.lineAt(lineNum).text
       let isMultiLineTag = false
 
-      //skip any comment lines
+      // Skip comment blocks to avoid parsing tag-like content in comments
       if (currentText.includes('<!--')) {
         while (!currentText.includes('-->')) {
           currentText = document.lineAt(++lineNum).text
@@ -165,32 +227,34 @@ export function getCloseTag(
 
       startPos = currentText.indexOf('<')
 
+      // Only process lines with single tags to avoid complexity
       if (getItemsOnLineCount(currentText) < 2) {
-        //skip lines until the close tag for this item
+        // Skip lines until we find the close tag for this item
         if (
           currentText.includes('<' + nsPrefix + tag) &&
           currentText.endsWith('>')
         ) {
-          //skipping to closing tag
+          // Scan forward to find the matching closing tag
           while (!currentText.includes('</' + nsPrefix + tag)) {
             currentText = document.lineAt(++lineNum).text
 
-            //If currentText is multi tag line skip to next line
+            // Skip multi-tag lines to maintain context
             if (getItemsOnLineCount(currentText) > 1) {
               currentText = document.lineAt(++lineNum).text
             }
 
+            // Handle nested tags of the same type
             if (currentText.includes('<' + nsPrefix + tag)) {
               ++nestedTagCount
               while (!currentText.includes('>')) {
                 currentText = document.lineAt(++lineNum).text
               }
               if (currentText.includes('/>')) {
-                --nestedTagCount
+                --nestedTagCount // Self-closing tags don't affect nesting
               }
             }
 
-            //if currentText is a closing tag
+            // If we find a closing tag but it's for a nested instance, skip it
             if (
               currentText.includes('</' + nsPrefix + tag) &&
               nestedTagCount > 0
@@ -201,19 +265,19 @@ export function getCloseTag(
           }
         }
 
-        //if end tag symbol is on a different line
+        // **MULTI-LINE TAG HANDLING**: Tag spans multiple lines
         if (
           currentText.includes('<' + nsPrefix + tag) &&
           !currentText.includes('>')
         ) {
           isMultiLineTag = true
 
-          //skip to the end tag symbol
+          // Skip to the end of the opening tag
           while (!currentText.includes('>')) {
             currentText = document.lineAt(++lineNum).text
           }
 
-          //if the tag isn't self closing, skip to the closing tag
+          // If not self-closing, skip to the closing tag
           if (!currentText.includes('/>')) {
             while (!currentText.includes('</' + nsPrefix + tag)) {
               currentText = document.lineAt(++lineNum).text
@@ -221,6 +285,7 @@ export function getCloseTag(
           }
         }
 
+        // **FOUND CLOSING TAG**: Return its location
         if (
           (currentText.includes('</' + nsPrefix + tag) &&
             nestedTagCount === 0) ||
@@ -230,7 +295,7 @@ export function getCloseTag(
             startPos = triggerPos
           }
 
-          //if the cursor is after the closing tag
+          // If cursor is after the closing tag, return 'none'
           if (
             lineNum == triggerLine &&
             currentText.indexOf('>', triggerPos) === -1
@@ -247,6 +312,17 @@ export function getCloseTag(
   return ['none', 0, 0]
 }
 
+/**
+ * Handles the complex case of multiple items/tags on a single line.
+ * Uses array tracking to determine which tags are opened vs. closed.
+ *
+ * @param triggerText - The full text of the current line
+ * @param triggerPos - The cursor position within the line
+ * @param nsPrefix - The namespace prefix
+ * @param items - Array of all possible tag names
+ * @param i - Index of the current item being checked
+ * @returns string - The unclosed tag name, or 'none'
+ */
 export function getItemsForLineGT1(
   triggerText: string,
   triggerPos: number,
@@ -254,10 +330,12 @@ export function getItemsForLineGT1(
   items: string[],
   i: number
 ) {
+  // Track positions of opening and closing tags
   let openTagArray: number[] = []
   let closeTagArray: number[] = []
   let [nextCloseCharPos, nextOpenTagPos] = [0, 0]
 
+  // **FIND ALL OPENING TAGS**: Build array of all opening tag positions
   while (
     (nextOpenTagPos = triggerText.indexOf(
       '<' + nsPrefix + items[i],
@@ -266,8 +344,8 @@ export function getItemsForLineGT1(
   ) {
     openTagArray.push(nextOpenTagPos)
 
+    // Check if self-closing and remove from tracking if so
     if ((nextCloseCharPos = triggerText.indexOf('>', nextOpenTagPos)) > -1) {
-      //if tag is self closing remove it from the openTagArray
       if (
         triggerText.substring(nextCloseCharPos - 1, nextCloseCharPos + 1) ===
         '/>'
@@ -279,6 +357,7 @@ export function getItemsForLineGT1(
     }
   }
 
+  // **FIND ALL CLOSING TAGS**: Build array of closing tag positions
   while (
     (nextCloseCharPos = triggerText.indexOf(
       '</' + nsPrefix + items[i],
@@ -289,6 +368,7 @@ export function getItemsForLineGT1(
     nextCloseCharPos = nextCloseCharPos + 1
   }
 
+  // **DETERMINE STATE**: If more opens than closes, this tag is unclosed
   if (openTagArray.length > closeTagArray.length) {
     return items[i]
   }
@@ -296,6 +376,18 @@ export function getItemsForLineGT1(
   return 'none'
 }
 
+/**
+ * Handles the simpler case of single items per line or scanning across lines.
+ * Looks both backwards and forwards through the document to find matching 
tags.
+ *
+ * @param document - The active text document
+ * @param triggerText - Text of the current line
+ * @param triggerLine - Current line number
+ * @param nsPrefix - Namespace prefix
+ * @param items - Array of all possible tag names
+ * @param i - Current item index
+ * @returns string - The unclosed tag name, or 'none'
+ */
 export function getItemsForLineLT2(
   document: vscode.TextDocument,
   triggerText: string,
@@ -304,6 +396,7 @@ export function getItemsForLineLT2(
   items: string[],
   i: number
 ) {
+  // Initialize tracking variables
   let [currentText, currentLine] = [triggerText, triggerLine]
   let [lineBefore, lineAfter, testLine] = [
     triggerLine,
@@ -313,8 +406,10 @@ export function getItemsForLineLT2(
   let openTagArray: number[] = []
   let closeTagArray: number[] = []
 
+  // Adjust prefix based on tag type
   nsPrefix = getItemPrefix(items[i], nsPrefix)
 
+  // **FIND OPENING TAG**: Scan backwards until we find the opening tag
   while (
     currentText.indexOf('<' + nsPrefix + items[i]) === -1 &&
     currentLine > -1
@@ -325,20 +420,24 @@ export function getItemsForLineLT2(
       currentText = document.lineAt(currentLine).text
     }
 
+    // Skip multi-item lines to avoid confusion
     if (getItemsOnLineCount(currentText) > 1) {
       --currentLine
     }
   }
 
+  // If we found an opening tag, scan the document to check if it's closed
   if (currentText.indexOf('<' + nsPrefix + items[i]) > -1) {
+    // **SCAN BACKWARDS**: Collect all opening/closing tags before current 
position
     while (lineBefore > -1) {
       currentText = document.lineAt(lineBefore).text
 
       if (getItemsOnLineCount(currentText) < 2) {
+        // Found opening tag
         if (currentText.indexOf('<' + nsPrefix + items[i]) > -1) {
           openTagArray.push(lineBefore)
 
-          //if multi line tag
+          // Handle multi-line tags
           let testText = currentText
           if (!testText.includes('>')) {
             testLine = lineBefore
@@ -349,7 +448,7 @@ export function getItemsForLineLT2(
             }
           }
 
-          //if selfclosing remove from the array
+          // Remove from tracking if self-closing or already closed
           if (
             testText.indexOf('/>') > -1 ||
             testText.includes('xml version') ||
@@ -359,6 +458,7 @@ export function getItemsForLineLT2(
           }
         }
 
+        // Found closing tag
         if (currentText.indexOf('</' + nsPrefix + items[i]) > -1) {
           closeTagArray.push(lineBefore)
         }
@@ -367,26 +467,29 @@ export function getItemsForLineLT2(
       --lineBefore
     }
 
+    // **SCAN FORWARDS**: Collect tags after current position
     ++lineAfter
 
     while (lineAfter < document.lineCount) {
       currentText = document.lineAt(lineAfter).text
 
       if (getItemsOnLineCount(currentText) < 2) {
+        // Found opening tag
         if (currentText.indexOf('<' + nsPrefix + items[i]) > -1) {
           openTagArray.push(lineAfter)
 
-          //if multi line tag
+          // Handle multi-line opening tags
           while (!currentText.includes('>')) {
             currentText = document.lineAt(++lineAfter).text
           }
 
-          //if selfclosing remove from the array
+          // Remove if self-closing
           if (currentText.indexOf('/>') > -1) {
             openTagArray.splice(openTagArray.length - 1, 1)
           }
         }
 
+        // Found closing tag
         if (currentText.indexOf('</' + nsPrefix + items[i]) > -1) {
           closeTagArray.push(lineAfter)
         }
@@ -395,6 +498,7 @@ export function getItemsForLineLT2(
       ++lineAfter
     }
 
+    // **DETERMINE STATE**: More openings than closings = tag is unclosed
     if (openTagArray.length > closeTagArray.length) {
       return items[i]
     }


Reply via email to