Repository: apache/daffodil-vscode
Pull request #1177: Add root element and namespace auto suggestions
Author: login: shanedell
Created: 2025-02-19T16:13:11Z | Merged: 2025-03-27T00:54:28Z

Add root element and namespace auto suggestions

- Create completion providers needed to show root element and namespace suggestions when inside a launch.json file.
- Create methods to parse the schema path from the launch.json and read in the XML.
- Parse the schema XML to get the possible root elements and namespace.
  - Root elements were limited to element's that were direct children of the schema.
- Add fast-xml-parser and strnum to LICENSE file

Closes https://github.com/apache/daffodil-vscode/issues/984


Diff:
diff --git a/build/package/LICENSE b/build/package/LICENSE
index 0693ca968..86ad597f2 100644
--- a/build/package/LICENSE
+++ b/build/package/LICENSE
@@ -569,6 +569,32 @@ conditions of the following licenses.
     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     SOFTWARE.
 
+- 'fast-xml-parser' in extension/dist/ext/extension.js
+  This product bundles the 'fast-xml-parser' from the above files.
+  This package is available under the MIT License:
+
+    MIT License
+
+    Copyright (c) 2017 Amit Kumar Gupta
+
+    Permission is hereby granted, free of charge, to any person obtaining a copy
+    of this software and associated documentation files (the "Software"), to deal
+    in the Software without restriction, including without limitation the rights
+    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+    copies of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be included in all
+    copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+    SOFTWARE.
+
 - 'fsevents' in extension/dist/ext/extension.js
   This product bundles 'fsevents' from the above files.
   This package is available under the MIT License:
@@ -1408,6 +1434,32 @@ conditions of the following licenses.
     
     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
+- 'strnum' in extension/dist/ext/extension.js
+  This product bundles 'strnum' from the above files.
+  This package is available under the MIT LICENSE:
+
+    MIT License
+
+    Copyright (c) 2021 Natural Intelligence
+
+    Permission is hereby granted, free of charge, to any person obtaining a copy
+    of this software and associated documentation files (the "Software"), to deal
+    in the Software without restriction, including without limitation the rights
+    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+    copies of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be included in all
+    copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+    SOFTWARE.
+
 - 'supports-color' in extension/dist/ext/extension.js
 - 'supports-color' in node_modules/@omega-edit/client
   This product bundles 'supports-color' from the above files.
diff --git a/package.json b/package.json
index cdb536777..356a26f7b 100644
--- a/package.json
+++ b/package.json
@@ -80,6 +80,7 @@
     "css-loader": "^6.8.1",
     "css-minimizer-webpack-plugin": "^7.0.0",
     "esbuild": "^0.25.0",
+    "fast-xml-parser": "^4.5.2",
     "glob": "8.1.0",
     "mini-css-extract-plugin": "^2.7.6",
     "mocha": "10.2.0",
diff --git a/src/adapter/activateDaffodilDebug.ts b/src/adapter/activateDaffodilDebug.ts
index b024528c8..92af23ae6 100644
--- a/src/adapter/activateDaffodilDebug.ts
+++ b/src/adapter/activateDaffodilDebug.ts
@@ -14,6 +14,8 @@ import * as dfdlLang from 'language/dfdl'
 import * as dfdlExt from 'language/semantics/dfdlExt'
 import * as dataEditClient from 'dataEditor'
 import * as tdmlEditor from 'tdmlEditor'
+import * as rootCompletion from 'rootCompletion'
+
 import {
   CancellationToken,
   DebugConfiguration,
@@ -553,6 +555,7 @@ export function activateDaffodilDebug(
   dataEditClient.activate(context)
   launchWizard.activate(context)
   tdmlEditor.activate(context)
+  rootCompletion.activate(context)
 }
 
 class DaffodilConfigurationProvider
diff --git a/src/rootCompletion/index.ts b/src/rootCompletion/index.ts
new file mode 100644
index 000000000..705aff439
--- /dev/null
+++ b/src/rootCompletion/index.ts
@@ -0,0 +1,26 @@
+/*
+ * 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 { getCompletionProviders } from './utils'
+
+export function activate(context: vscode.ExtensionContext) {
+  context.subscriptions.push(
+    ...getCompletionProviders('rootName'),
+    ...getCompletionProviders('rootNamespace')
+  )
+}
diff --git a/src/rootCompletion/utils.ts b/src/rootCompletion/utils.ts
new file mode 100644
index 000000000..bb18fd035
--- /dev/null
+++ b/src/rootCompletion/utils.ts
@@ -0,0 +1,192 @@
+/*
+ * 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 fs from 'fs'
+import * as vscode from 'vscode'
+import { XMLParser } from 'fast-xml-parser'
+
+// Method to create simple completion item given provided value and position
+export function getSimpleCompletionItem(
+  document: vscode.TextDocument,
+  position: vscode.Position,
+  value: string
+): vscode.CompletionItem {
+  let item = new vscode.CompletionItem(
+    `${value}`,
+    vscode.CompletionItemKind.Value
+  )
+  const text = item.label.toString()
+
+  const before =
+    position.character > 0
+      ? document.getText(new vscode.Range(position.translate(0, -1), position))
+      : ''
+  const after = document.getText(
+    new vscode.Range(position, position.translate(0, 1))
+  )
+
+  // Check if already wrapped in quotes
+  const alreadyWrapped = before === '"' && after === '"'
+
+  // If not wrapped in quotes add double quotes to before and after text
+  item.insertText = !alreadyWrapped ? `"${text}"` : text
+
+  item.range = new vscode.Range(position, position)
+  return item
+}
+
+/* Method to get previous lines based on i.
+ *
+ * The reason for going 4 lines back is because the schema object only has
+ * 3 sub properties. So, this mean the schema object should technically
+ * not be anymore than 3 or 4 lines back from where the path attribute is set.
+ */
+function getPrevLines(
+  document: vscode.TextDocument,
+  i: number
+): vscode.TextLine[] {
+  let prevLines: vscode.TextLine[] = []
+
+  for (var j = 0; j < 4; j++) {
+    if (i > j) {
+      prevLines.push(document.lineAt(i - (j + 1)))
+    }
+  }
+
+  return prevLines
+}
+
+/* Method to get the schema path from the configuration file being edited.
+ *
+ * The reason for going 4 lines back is because the schema object only has
+ * 3 sub properties. So, this mean the schema object should technically
+ * not be anymore than 3 or 4 lines back from where the path attribute is set.
+ */
+export function getSchemaPathFromLaunchJSON(
+  document: vscode.TextDocument,
+  position: vscode.Position
+): string {
+  let schemaPath = ''
+
+  for (var i = position.line - 4; i < position.line; i++) {
+    if (i <= 0) continue
+
+    let currLine = document.lineAt(i)
+    if (!currLine.text.includes('path')) continue
+
+    const prevLines = getPrevLines(document, i)
+
+    let isUnderSchema =
+      prevLines
+        .map((prevLines) => prevLines.text.includes('schema'))
+        .filter((b) => b == true).length > 0
+
+    if (isUnderSchema) {
+      const regex = /(["'])path\1\s*:\s*(["'])(.*?)\2/
+      const match = currLine.text.match(regex)
+
+      schemaPath = match ? JSON.parse(`{${match[0]}}`).path : ''
+    }
+  }
+
+  return schemaPath.includes('${workspaceFolder}') &&
+    vscode.workspace.workspaceFolders
+    ? schemaPath.replace(
+        '${workspaceFolder}',
+        vscode.workspace.workspaceFolders[0].uri.fsPath
+      )
+    : schemaPath
+}
+
+// Method to get the possible items
+export function getPossibleItems(
+  document: vscode.TextDocument,
+  position: vscode.Position,
+  findRootNamespace: boolean
+): string[] {
+  const schemaPath = getSchemaPathFromLaunchJSON(document, position)
+
+  const parser = new XMLParser({
+    ignoreAttributes: false,
+    removeNSPrefix: true,
+    isArray: (name) => name == 'element',
+  })
+
+  const fileData = schemaPath === '' ? '' : fs.readFileSync(schemaPath)
+  if (fileData === '') return []
+
+  const obj = parser.parse(fileData)
+
+  if (obj && typeof obj === 'object') {
+    return findRootNamespace
+      ? [obj['schema']['@_targetNamespace']]
+      : obj['schema']['element'].map((e) => e['@_name'])
+  }
+
+  return []
+}
+
+export function checkForItems(
+  document: vscode.TextDocument,
+  position: vscode.Position,
+  includeText: string
+): vscode.CompletionItem[] {
+  if (
+    !document.fileName.includes('launch.json') ||
+    !document.lineAt(position).text.includes(`"${includeText}"`)
+  )
+    return []
+
+  const possibleItems: string[] = getPossibleItems(
+    document,
+    position,
+    includeText.includes('rootNamespace')
+  )
+
+  return possibleItems.map((prn) =>
+    getSimpleCompletionItem(document, position, prn)
+  )
+}
+
+export function getCompletionProviders(includeText: string) {
+  return [
+    vscode.languages.registerCompletionItemProvider(
+      { language: 'jsonc' },
+      {
+        provideCompletionItems(
+          document: vscode.TextDocument,
+          position: vscode.Position
+        ) {
+          return checkForItems(document, position, includeText)
+        },
+      },
+      '"' // trigger
+    ),
+    vscode.languages.registerCompletionItemProvider(
+      { language: 'json' },
+      {
+        provideCompletionItems(
+          document: vscode.TextDocument,
+          position: vscode.Position
+        ) {
+          return checkForItems(document, position, includeText)
+        },
+      },
+      '"' // trigger
+    ),
+  ]
+}
diff --git a/webpack/ext-dev.webpack.config.js b/webpack/ext-dev.webpack.config.js
index be9da7abc..6e1cff57f 100644
--- a/webpack/ext-dev.webpack.config.js
+++ b/webpack/ext-dev.webpack.config.js
@@ -23,6 +23,7 @@ const localModuleAliases = {
   language: localModulePath('language'),
   launchWizard: localModulePath('launchWizard/launchWizard'),
   infoset: localModulePath('infoset'),
+  rootCompletion: localModulePath('rootCompletion'),
 }
 
 const packageData = jsoncParse(
diff --git a/webpack/ext-package.webpack.config.js b/webpack/ext-package.webpack.config.js
index 12498f15e..8662775d0 100644
--- a/webpack/ext-package.webpack.config.js
+++ b/webpack/ext-package.webpack.config.js
@@ -38,6 +38,7 @@ const localModuleAliases = {
   language: localModulePath('language'),
   launchWizard: localModulePath('launchWizard/launchWizard'),
   infoset: localModulePath('infoset'),
+  rootCompletion: localModulePath('rootCompletion'),
 }
 
 const packageData = jsoncParse(
diff --git a/yarn.lock b/yarn.lock
index 1f1681d12..b845cc7fd 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2087,6 +2087,13 @@ fast-redact@^3.1.1:
   resolved "https://registry.npmjs.org/fast-redact/-/fast-redact-3.3.0.tgz"
   integrity sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ==
 
+fast-xml-parser@^4.5.2:
+  version "4.5.2"
+  resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.5.2.tgz#f535f77b1396b859498203a2fd1994c4230a0ff9"
+  integrity sha512-xmnYV9o0StIz/0ArdzmWTxn9oDy0lH8Z80/8X/TD2EUQKXY4DHxoT9mYBqgGIG17DgddCJtH1M6DriMbalNsAA==
+  dependencies:
+    strnum "^1.0.5"
+
 fastest-levenshtein@^1.0.12:
   version "1.0.16"
   resolved "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz"
@@ -4280,6 +4287,11 @@ strip-json-comments@~2.0.1:
   resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz"
   integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==
 
+strnum@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db"
+  integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==
+
 stylehacks@^7.0.4:
   version "7.0.4"
   resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-7.0.4.tgz#9c21f7374f4bccc0082412b859b3c89d77d3277c"


Reviews:
user: login: stevedlawrence
body: +1, suggestions that might make things less fragile and potentially more clear
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-04T18:59:58Z
user: login: shanedell
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-04T19:32:57Z
user: login: stevedlawrence
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-04T19:36:11Z
user: login: shanedell
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-05T18:38:38Z
user: login: stevedlawrence
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-05T19:15:46Z
user: login: shanedell
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-05T19:17:04Z
user: login: shanedell
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-05T19:18:55Z
user: login: shanedell
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-05T19:27:47Z
user: login: stevedlawrence
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-05T19:41:48Z
user: login: rthomas320
body: Tested on WIndows 10 works as described.
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T15:58:23Z
user: login: hdalsania
body: @shanedell  Everything is working as expected, rootname rootnamespace were suggested in launch.json files based on the configured schema. The only issue I see is that when you select rootname or rootnamespace from the suggestion, they are not embedded in quotes automatically as shown below, 

![image](https://github.com/user-attachments/assets/fcf6508a-5503-4d25-beb6-0429bd1840f0)

state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-14T20:45:01Z
user: login: hdalsania
body: +1 
rootName and rootNameSpace suggestion now embedded within quotes.

state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-23T17:20:08Z
user: login: scholarsmate
body: +1
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T20:50:06Z

Review comments:
diff_hunk: @@ -0,0 +1,106 @@
+/*
+ * 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 fs from 'fs'
+import * as vscode from 'vscode'
+import { queryXML } from 'xmlUtils'
+
+// Method to create simple completion item given provided value and position
+export function getSimpleCompletionItem(
+  value: string,
+  position: vscode.Position
+): vscode.CompletionItem {
+  let item = new vscode.CompletionItem(
+    `${value}`,
+    vscode.CompletionItemKind.Value
+  )
+  item.range = new vscode.Range(position, position)
+  return item
+}
+
+// Method to get previous lines based on i.
+function getPrevLines(
+  document: vscode.TextDocument,
+  i: number
+): vscode.TextLine[] {
+  let prevLines: vscode.TextLine[] = []
+
+  for (var j = 0; j < 4; j++) {
+    if (i > j) {
+      prevLines.push(document.lineAt(i - (j + 1)))
+    }
+  }
+
+  return prevLines
+}
+
+// Method to get the schema path from the configuration file being edited
+export function getSchemaPathFromLaunchJSON(
path: src/rootCompletion/utils.ts
user: login: stevedlawrence
body: Is there a reason this function is implemented the way it is? It looks like it looks at the previous four lines for "path", then if it finds that it looks at the previous for lines of that for "schema", and then does some str replace to get the path?

This seems fragile and the 4 constant seems a bit arbitrary. For example, what if the `path` or `schema` is more than four behind, or is after the current line? Why not not scan the entire json file? These are relatively small files so I imagine it woudl be pretty fast. Or alternatively, what about loading the json into a variable and accessing the `schema/path` members directly? I guess that breaks if the json isn't valid, but is that a concern? Or is this just how completion things are supposed to be written and are only supposed to look at immediate lines for context?
created_at: 2025-03-04T17:18:57Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,68 @@
+/*
+ * 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 { getSimpleCompletionItem, getPossibleItems } from './utils'
+
+export function checkForRootNameItems(
+  document: vscode.TextDocument,
+  position: vscode.Position
+): vscode.CompletionItem[] {
+  if (
+    !document.fileName.includes('launch.json') ||
+    !document.lineAt(position).text.includes('"rootName"')
+  )
+    return []
+
+  const possibleRootNames: string[] = getPossibleItems(
+    document,
+    position,
+    'xs:element',
path: src/rootCompletion/rootName.ts
user: login: stevedlawrence
body: Looking for elements with the xs prefix is potential fragile. We sometimes see "xsd" as an alternative prefix, and more and more schemas are written so the "xs:" prefix isn't needed at all (e.g. `<element name="foo">...`. 

I would suggest either passing in a namespace instead of a prefix and then look for elements that have a prefix that maps to that namespace, or maybe more simply just ignore namespaces/prefixes and assume the schema document uses correct namespaces/prefixes. It looks like the way fast-xml-parser it makes dealing with namespaces/prefixes diffciult, I would suggest doing the latter and setting the fast-xml-parser option `removeNSPrefix` to true and not worry about them.
created_at: 2025-03-04T18:02:45Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,70 @@
+/*
+ * 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 { getSimpleCompletionItem, getPossibleItems } from './utils'
+
+export function checkForRootNamespaceItems(
+  document: vscode.TextDocument,
+  position: vscode.Position
+): vscode.CompletionItem[] {
+  if (
+    !document.fileName.includes('launch.json') ||
+    !document.lineAt(position).text.includes('"rootNamespace"')
+  )
+    return []
+
+  const possibleRootNamespaces = getPossibleItems(
+    document,
+    position,
+    'xs:schema',
path: src/rootCompletion/rootNamespace.ts
user: login: stevedlawrence
body: Same comment here as a above, `xs` isn't necessarily the right prefix.
created_at: 2025-03-04T18:04:57Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,106 @@
+/*
+ * 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 fs from 'fs'
+import * as vscode from 'vscode'
+import { queryXML } from 'xmlUtils'
+
+// Method to create simple completion item given provided value and position
+export function getSimpleCompletionItem(
+  value: string,
+  position: vscode.Position
+): vscode.CompletionItem {
+  let item = new vscode.CompletionItem(
+    `${value}`,
+    vscode.CompletionItemKind.Value
+  )
+  item.range = new vscode.Range(position, position)
+  return item
+}
+
+// Method to get previous lines based on i.
+function getPrevLines(
+  document: vscode.TextDocument,
+  i: number
+): vscode.TextLine[] {
+  let prevLines: vscode.TextLine[] = []
+
+  for (var j = 0; j < 4; j++) {
+    if (i > j) {
+      prevLines.push(document.lineAt(i - (j + 1)))
+    }
+  }
+
+  return prevLines
+}
+
+// Method to get the schema path from the configuration file being edited
+export function getSchemaPathFromLaunchJSON(
+  document: vscode.TextDocument,
+  position: vscode.Position
+): string {
+  let schemaPath = ''
+
+  for (var i = position.line - 4; i < position.line; i++) {
+    if (i <= 0) continue
+
+    let currLine = document.lineAt(i)
+    if (!currLine.text.includes('path')) continue
+
+    const prevLines = getPrevLines(document, i)
+
+    let isUnderSchema =
+      prevLines
+        .map((prevLines) => prevLines.text.includes('schema'))
+        .filter((b) => b == true).length > 0
+
+    if (isUnderSchema) {
+      schemaPath = currLine.text
+        .replaceAll(' ', '')
+        .replaceAll('"', '')
+        .replaceAll(',', '')
+        .replace('path:', '')
path: src/rootCompletion/utils.ts
user: login: stevedlawrence
body: This also feels a bit fragile. For example, if a path has a space in it the space will just be removed. Or if there are multiple values on the same line separated by a comma, things will just get concatenated together?

What about using regex to extract the value? Something like this maybe:
```regex
(["'])path\1\s*:\s*(["'])(.*?)\2
```
Capture group 3 will be the value of path. It's a bit ugly, but avoids possible edge cases.
created_at: 2025-03-04T18:21:24Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,69 @@
+/*
+ * 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 { XMLParser } from 'fast-xml-parser'
+
+function getAttributeValues(
path: src/xmlUtils/index.ts
user: login: stevedlawrence
body: This function feels like it's adding generality but at the cost of added complexity. I'm wondering if that complexity is needed, and if readability woudl go up if replaced. For example, if queryXml instead just called parser.parse() and returned the resulting object, then the callers look like this (assuming you set the option to remove namespace prefixes):

```js
let rootNamespace = obj["schema"]["@_targetNamespace"]
```

```js
let rootElems = obj["schema"]["element"].map((e) => e["@_name"]))
```

To me those lines feel more clear about how and what fields they are accessing. You might also need to add a try/catch around those lines handle if those keys don't exist, but that's doesn't feel too bad, and it makes the behavior more clear when they don't exist.

Note that if you add this option to the `XMLParser`, you can make it so the "element" key is always an array and you don't have to do isArray checks:
```js
isArray: (name, jpath, isLeafNode, isAttribute) => name == "element"
```


created_at: 2025-03-04T18:55:13Z
author_association: MEMBER
diff_hunk: @@ -2087,6 +2087,13 @@ fast-redact@^3.1.1:
   resolved "https://registry.npmjs.org/fast-redact/-/fast-redact-3.3.0.tgz"
   integrity sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ==
 
+fast-xml-parser@^4.5.2:
path: yarn.lock
user: login: stevedlawrence
body: This is MIT licensed and requires an update to the LICENSE files. ASF cares heavily about licenses compatibility and proper attribution. Please make sure to stay on top of this.
created_at: 2025-03-04T18:59:30Z
author_association: MEMBER
diff_hunk: @@ -4280,6 +4287,11 @@ strip-json-comments@~2.0.1:
   resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz"
   integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==
 
+strnum@^1.0.5:
path: yarn.lock
user: login: stevedlawrence
body: Same here, this is MIT and needs to be in the LICENSE file.
created_at: 2025-03-04T18:59:43Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,106 @@
+/*
+ * 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 fs from 'fs'
+import * as vscode from 'vscode'
+import { queryXML } from 'xmlUtils'
+
+// Method to create simple completion item given provided value and position
+export function getSimpleCompletionItem(
+  value: string,
+  position: vscode.Position
+): vscode.CompletionItem {
+  let item = new vscode.CompletionItem(
+    `${value}`,
+    vscode.CompletionItemKind.Value
+  )
+  item.range = new vscode.Range(position, position)
+  return item
+}
+
+// Method to get previous lines based on i.
+function getPrevLines(
+  document: vscode.TextDocument,
+  i: number
+): vscode.TextLine[] {
+  let prevLines: vscode.TextLine[] = []
+
+  for (var j = 0; j < 4; j++) {
+    if (i > j) {
+      prevLines.push(document.lineAt(i - (j + 1)))
+    }
+  }
+
+  return prevLines
+}
+
+// Method to get the schema path from the configuration file being edited
+export function getSchemaPathFromLaunchJSON(
path: src/rootCompletion/utils.ts
user: login: shanedell
body: @stevedlawrence So, for reading the whole JSON file that causes an issue where you have multiple different configurations, causing the possibility of grabbing the incorrect schema from a different config the users isn't actually trying to use. To do the processing to make sure we grab the correct config might slow down the auto-suggestions and like you mentioned could cause an issue if the json isn't valid. Since if it's not valid we probably won't be able to access any of the data.

The reason for only doing 4 lines back is because the `schema` object only has 3 attributes which are `path`, `rootName` and `rootNamespace`. This was to ensure we grab the schema's path since the object `infosetOutput` also has a attribute of `path`. But the reason for 4 was because schema should never be further than 3 back, when you are trying to set the `rootName` or `rootNamespace` as those have to be inside of the schema object. Just added an extra line as a buffer.

It's possible that we could just go line by line back from the entered line if it contains `rootName` or `rootNamespace` till we hit `schema`. Then from there grab the next 3 lines after `schema`, as those should be `rootName`, `rootNamespace` and `path`. However, if the key doesn't match one of those 3 we ignore the line and if path isn't found return nothing. Does this sound like a better alternative? 
created_at: 2025-03-04T19:32:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,106 @@
+/*
+ * 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 fs from 'fs'
+import * as vscode from 'vscode'
+import { queryXML } from 'xmlUtils'
+
+// Method to create simple completion item given provided value and position
+export function getSimpleCompletionItem(
+  value: string,
+  position: vscode.Position
+): vscode.CompletionItem {
+  let item = new vscode.CompletionItem(
+    `${value}`,
+    vscode.CompletionItemKind.Value
+  )
+  item.range = new vscode.Range(position, position)
+  return item
+}
+
+// Method to get previous lines based on i.
+function getPrevLines(
+  document: vscode.TextDocument,
+  i: number
+): vscode.TextLine[] {
+  let prevLines: vscode.TextLine[] = []
+
+  for (var j = 0; j < 4; j++) {
+    if (i > j) {
+      prevLines.push(document.lineAt(i - (j + 1)))
+    }
+  }
+
+  return prevLines
+}
+
+// Method to get the schema path from the configuration file being edited
+export function getSchemaPathFromLaunchJSON(
path: src/rootCompletion/utils.ts
user: login: stevedlawrence
body: I have no strong preference, but it might be helpful to add comments that explain the chosen behavior, especially if there are magic numbers.
created_at: 2025-03-04T19:36:11Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,69 @@
+/*
+ * 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 { XMLParser } from 'fast-xml-parser'
+
+function getAttributeValues(
path: src/xmlUtils/index.ts
user: login: shanedell
body: @stevedlawrence So I did need some testing, and I still needed to use isArray method because when testing with at least PCAP, there was only one element directly under it. So, if I added a second one then  `obj['schema']['element'].map((e) => e['@_name'])` worked fine. So I still needed to do something like:

```typescript
Array.isArray(obj['schema']['element'])
  ? obj['schema']['element'].map((e) => e['@_name'])
  : [obj['schema']['element']['@_name']]
```
created_at: 2025-03-05T18:38:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,69 @@
+/*
+ * 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 { XMLParser } from 'fast-xml-parser'
+
+function getAttributeValues(
path: src/xmlUtils/index.ts
user: login: stevedlawrence
body: Did you add the `isArray` option mentioned in my above comment to the XMLParser options (or something similar)? That should tell it to treat all `element`s as arrays, even if they only show up once lik in PCAP. 
created_at: 2025-03-05T19:15:46Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,69 @@
+/*
+ * 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 { XMLParser } from 'fast-xml-parser'
+
+function getAttributeValues(
path: src/xmlUtils/index.ts
user: login: shanedell
body: Oh I must I have misread that my apologies, I will do that
created_at: 2025-03-05T19:17:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,69 @@
+/*
+ * 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 { XMLParser } from 'fast-xml-parser'
+
+function getAttributeValues(
path: src/xmlUtils/index.ts
user: login: shanedell
body: That did work, thank you
created_at: 2025-03-05T19:18:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,106 @@
+/*
+ * 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 fs from 'fs'
+import * as vscode from 'vscode'
+import { queryXML } from 'xmlUtils'
+
+// Method to create simple completion item given provided value and position
+export function getSimpleCompletionItem(
+  value: string,
+  position: vscode.Position
+): vscode.CompletionItem {
+  let item = new vscode.CompletionItem(
+    `${value}`,
+    vscode.CompletionItemKind.Value
+  )
+  item.range = new vscode.Range(position, position)
+  return item
+}
+
+// Method to get previous lines based on i.
+function getPrevLines(
+  document: vscode.TextDocument,
+  i: number
+): vscode.TextLine[] {
+  let prevLines: vscode.TextLine[] = []
+
+  for (var j = 0; j < 4; j++) {
+    if (i > j) {
+      prevLines.push(document.lineAt(i - (j + 1)))
+    }
+  }
+
+  return prevLines
+}
+
+// Method to get the schema path from the configuration file being edited
+export function getSchemaPathFromLaunchJSON(
path: src/rootCompletion/utils.ts
user: login: shanedell
body: @stevedlawrence I addressed most of the comments, I am still going to work on adding the new dependencies to the LICENSE files. I also didn't full change how this is done but added comments on why 4 is being used, if that resolves this comment then we can close it or I can work on a different approach to getting the schema path.
created_at: 2025-03-05T19:27:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,106 @@
+/*
+ * 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 fs from 'fs'
+import * as vscode from 'vscode'
+import { queryXML } from 'xmlUtils'
+
+// Method to create simple completion item given provided value and position
+export function getSimpleCompletionItem(
+  value: string,
+  position: vscode.Position
+): vscode.CompletionItem {
+  let item = new vscode.CompletionItem(
+    `${value}`,
+    vscode.CompletionItemKind.Value
+  )
+  item.range = new vscode.Range(position, position)
+  return item
+}
+
+// Method to get previous lines based on i.
+function getPrevLines(
+  document: vscode.TextDocument,
+  i: number
+): vscode.TextLine[] {
+  let prevLines: vscode.TextLine[] = []
+
+  for (var j = 0; j < 4; j++) {
+    if (i > j) {
+      prevLines.push(document.lineAt(i - (j + 1)))
+    }
+  }
+
+  return prevLines
+}
+
+// Method to get the schema path from the configuration file being edited
+export function getSchemaPathFromLaunchJSON(
path: src/rootCompletion/utils.ts
user: login: stevedlawrence
body: The comment you've addresses my concerns. Thanks!
created_at: 2025-03-05T19:41:48Z
author_association: MEMBER

PR comments:
user: login: shanedell
created_at: 2025-02-19T16:13:54Z
body: This PR is for creating the auto suggestions in the launch.json, which should give the user the correct rootName and rootNamespace options based on the schema name they have defined. This PR can be updated to also on the Scala Debugger side, check if the rootName and rootNamespace are set, if not relay a message saying they are not set and list the options available based on the schema. Is this something we want on the debugger side or the auto suggestions enough?
author_association: CONTRIBUTOR
user: login: hdalsania
created_at: 2025-03-04T16:38:57Z
body: @stevedlawrence  Do you have bandwidth to review this and provide your feedback?
author_association: COLLABORATOR
user: login: shanedell
created_at: 2025-03-19T16:03:20Z
body: @hdalsania @rthomas320 Please give this another look, double quotes should automatically be added, unless you used `""` and selected the completion item while in between the quotes.
author_association: CONTRIBUTOR