You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@daffodil.apache.org by sh...@apache.org on 2023/04/12 13:00:56 UTC
[daffodil-vscode] branch main updated: Provides the ability to re-select values of attributes items - add logic to mitigate missing dfdl prefixes - tweak logic to determine if cursor is between quotes - Fixes incorrect velues triggered by nested elements - fixes the resulting choice when intellisense is trigger between two closing tags on a multi tag line - fix wrong suggestions at end of schema open tag - fix incorrect suggestion at beginning and end of a multi tag line - fix broken attribute suggestions from inside multiline [...]
This is an automated email from the ASF dual-hosted git repository.
shanedell pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/daffodil-vscode.git
The following commit(s) were added to refs/heads/main by this push:
new 57513b4 Provides the ability to re-select values of attributes items - add logic to mitigate missing dfdl prefixes - tweak logic to determine if cursor is between quotes - Fixes incorrect velues triggered by nested elements - fixes the resulting choice when intellisense is trigger between two closing tags on a multi tag line - fix wrong suggestions at end of schema open tag - fix incorrect suggestion at beginning and end of a multi tag line - fix broken attribute suggestions fro [...]
57513b4 is described below
commit 57513b49e8b7ae10e3d4c547495a3f0463bf42d2
Author: rt320 <98...@users.noreply.github.com>
AuthorDate: Wed Apr 5 11:44:05 2023 -0400
Provides the ability to re-select values of attributes items
- add logic to mitigate missing dfdl prefixes
- tweak logic to determine if cursor is between quotes
- Fixes incorrect velues triggered by nested elements
- fixes the resulting choice when intellisense is trigger between
two closing tags on a multi tag line
- fix wrong suggestions at end of schema open tag
- fix incorrect suggestion at beginning and end of a multi tag line
- fix broken attribute suggestions from inside multiline tag
- fixed cursor between alert open and close tags was returning
results for appinfo
- Change logic to for assert and discriminator auto complete
add white space
- Remove prettier-ignore directive from attributeValueItems.ts
Closes #573
Closes #577
Closes #578
---
src/language/dfdl.ts | 2 +
src/language/providers/attributeCompletion.ts | 11 +-
src/language/providers/attributeValueCompletion.ts | 145 ++++++++++
src/language/providers/closeElement.ts | 21 +-
src/language/providers/closeElementSlash.ts | 9 +-
src/language/providers/closeUtils.ts | 44 +++-
src/language/providers/elementCompletion.ts | 45 +++-
.../providers/intellisense/attributeItems.ts | 2 +-
.../providers/intellisense/attributeValueItems.ts | 292 +++++++++++++++++++++
.../providers/intellisense/elementItems.ts | 19 +-
src/language/providers/utils.ts | 162 +++++++++++-
src/tests/suite/language/items.test.ts | 3 +
12 files changed, 723 insertions(+), 32 deletions(-)
diff --git a/src/language/dfdl.ts b/src/language/dfdl.ts
index a60dd6b..6628416 100644
--- a/src/language/dfdl.ts
+++ b/src/language/dfdl.ts
@@ -20,6 +20,7 @@ import * as fs from 'fs'
import { getElementCompletionProvider } from './providers/elementCompletion'
import { getAttributeCompletionProvider } from './providers/attributeCompletion'
import { getCloseElementProvider } from './providers/closeElement'
+import { getAttributeValueCompletionProvider } from './providers/attributeValueCompletion'
import { getCloseElementSlashProvider } from './providers/closeElementSlash'
export function activate(context: vscode.ExtensionContext) {
@@ -34,6 +35,7 @@ export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
getElementCompletionProvider(dfdlFormat),
getAttributeCompletionProvider(),
+ getAttributeValueCompletionProvider(),
getCloseElementProvider(),
getCloseElementSlashProvider()
)
diff --git a/src/language/providers/attributeCompletion.ts b/src/language/providers/attributeCompletion.ts
index d2aed4a..d3ff53b 100644
--- a/src/language/providers/attributeCompletion.ts
+++ b/src/language/providers/attributeCompletion.ts
@@ -25,8 +25,10 @@ import {
getCommonItems,
getXsdNsPrefix,
getItemsOnLineCount,
+ cursorWithinQuotes,
cursorWithinBraces,
dfdlDefaultPrefix,
+ cursorAfterEquals,
} from './utils'
import { attributeCompletion } from './intellisense/attributeItems'
@@ -76,6 +78,8 @@ export function getAttributeCompletionProvider() {
if (
checkBraceOpen(document, position) ||
cursorWithinBraces(document, position) ||
+ cursorWithinQuotes(document, position) ||
+ cursorAfterEquals(document, position) ||
nearestOpenItem.includes('none')
) {
return undefined
@@ -101,7 +105,10 @@ export function getAttributeCompletionProvider() {
)
}
-function getDefinedTypes(document: vscode.TextDocument, nsPrefix: string) {
+export function getDefinedTypes(
+ document: vscode.TextDocument,
+ nsPrefix: string
+) {
let additionalTypes = ''
let lineNum = 0
const lineCount = document.lineCount
@@ -229,7 +236,7 @@ function checkNearestOpenItem(
''
)
case 'discriminator':
- return getCompletionItems(['message'], '', '', nsPrefix, '')
+ return getCompletionItems(['test', 'message'], '', '', nsPrefix, '')
case 'format':
return getCompletionItems(
[
diff --git a/src/language/providers/attributeValueCompletion.ts b/src/language/providers/attributeValueCompletion.ts
new file mode 100644
index 0000000..7f0e3ff
--- /dev/null
+++ b/src/language/providers/attributeValueCompletion.ts
@@ -0,0 +1,145 @@
+/*
+ * 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 { checkBraceOpen, cursorWithinBraces, getXsdNsPrefix } from './utils'
+import { getDefinedTypes } from './attributeCompletion'
+import {
+ attributeValues,
+ noChoiceAttributes,
+} from './intellisense/attributeValueItems'
+
+export function getAttributeValueCompletionProvider() {
+ return vscode.languages.registerCompletionItemProvider(
+ 'dfdl',
+ {
+ async provideCompletionItems(
+ document: vscode.TextDocument,
+ position: vscode.Position
+ ) {
+ if (
+ checkBraceOpen(document, position) ||
+ cursorWithinBraces(document, position)
+ ) {
+ return undefined
+ }
+ const nsPrefix = getXsdNsPrefix(document, position)
+ let additionalItems = getDefinedTypes(document, nsPrefix)
+ let [attributeName, startPos, endPos] = getAttributeDetails(
+ document,
+ position
+ )
+
+ if (attributeName !== 'none') {
+ let replaceValue = ''
+ if (startPos === endPos) {
+ replaceValue = ' '
+ }
+
+ if (attributeName.includes(':')) {
+ attributeName = attributeName.substring(
+ attributeName.indexOf(':') + 1
+ )
+ }
+
+ if (noChoiceAttributes.includes(attributeName)) {
+ return undefined
+ }
+
+ let startPosition = position.with(position.line, startPos)
+ let endPosition = position.with(position.line, endPos + 1)
+
+ let range = new vscode.Range(startPosition, endPosition)
+
+ await vscode.window.activeTextEditor?.edit((editBuilder) => {
+ editBuilder.replace(range, replaceValue)
+ })
+
+ attributeValues(attributeName, startPosition, additionalItems)
+ }
+ return undefined
+ },
+ },
+ ' ' // triggered whenever a newline is typed
+ )
+}
+
+function getAttributeDetails(
+ document: vscode.TextDocument,
+ position: vscode.Position
+): [attributeName: string, valueStartPos: number, valueEndPos: number] {
+ const quoteChar: string[] = ["'", '"']
+ const triggerLine = position.line
+ const triggerPos = position.character
+ let currentLine = triggerLine
+ let currentPos = triggerPos
+ let endPos = -1
+ let currentText = document.lineAt(currentLine).text
+ let textBeforeTrigger = currentText.substring(0, triggerPos)
+ let attributeName = 'none'
+ let attributeStartPos = 0
+
+ while (
+ !currentText.includes("'") &&
+ !currentText.includes('"') &&
+ !currentText.includes('=') &&
+ !currentText.includes('<') &&
+ !currentText.includes('>') &&
+ currentLine > 0 &&
+ currentLine < document.lineCount
+ ) {
+ currentText = document.lineAt(--currentLine).text
+ }
+
+ if (currentLine === 0 || currentLine === document.lineCount) {
+ return ['none', 0, 0]
+ }
+
+ if ((currentPos = textBeforeTrigger.lastIndexOf('=')) !== -1) {
+ if (triggerPos === currentPos + 1) {
+ attributeStartPos = textBeforeTrigger.lastIndexOf(' ') + 1
+ attributeName = textBeforeTrigger.substring(attributeStartPos, currentPos)
+ return [attributeName, currentPos + 1, currentPos + 1]
+ }
+ }
+
+ for (let i = 0; i < quoteChar.length; ++i) {
+ if (currentText.includes(quoteChar[i])) {
+ if (currentLine === triggerLine) {
+ currentPos = textBeforeTrigger.lastIndexOf(quoteChar[i])
+
+ if (
+ currentPos < triggerPos &&
+ textBeforeTrigger.lastIndexOf('=') === currentPos - 1
+ ) {
+ endPos = currentText.indexOf(quoteChar[i], currentPos + 1)
+ attributeStartPos = textBeforeTrigger.lastIndexOf(' ')
+ attributeName = textBeforeTrigger.substring(
+ attributeStartPos + 1,
+ currentPos - 1
+ )
+ }
+ }
+ }
+
+ if (attributeName !== 'none') {
+ break
+ }
+ }
+ return [attributeName, currentPos, endPos]
+}
diff --git a/src/language/providers/closeElement.ts b/src/language/providers/closeElement.ts
index 4450fe5..399c942 100644
--- a/src/language/providers/closeElement.ts
+++ b/src/language/providers/closeElement.ts
@@ -17,7 +17,12 @@
import * as vscode from 'vscode'
import { checkMissingCloseTag } from './closeUtils'
-import { checkBraceOpen, cursorWithinBraces } from './utils'
+import {
+ checkBraceOpen,
+ cursorAfterEquals,
+ cursorWithinBraces,
+ cursorWithinQuotes,
+} from './utils'
import {
getXsdNsPrefix,
insertSnippet,
@@ -35,19 +40,24 @@ export function getCloseElementProvider() {
) {
if (
checkBraceOpen(document, position) ||
- cursorWithinBraces(document, position)
+ cursorWithinBraces(document, position) ||
+ cursorWithinQuotes(document, position) ||
+ cursorAfterEquals(document, position)
) {
return undefined
}
+
let backpos = position.with(position.line, position.character)
let backpos3 = position.with(position.line, position.character)
if (position.character > 0) {
backpos = position.with(position.line, position.character - 1)
}
+
if (position.character > 2) {
backpos3 = position.with(position.line, position.character - 3)
}
+
let nsPrefix = getXsdNsPrefix(document, position)
const origPrefix = nsPrefix
@@ -68,6 +78,7 @@ export function getCloseElementProvider() {
}
let range = new vscode.Range(position, position)
+
if (
(triggerText.endsWith('>') && itemsOnLine < 2) ||
(triggerText.endsWith('>>') && itemsOnLine > 1) ||
@@ -169,6 +180,7 @@ function checkNearestTagNotClosed(
nsPrefix: string
) {
const triggerText = document.lineAt(position.line).text
+
switch (nearestTagNotClosed) {
case 'defineVariable':
case 'setVariable':
@@ -177,9 +189,9 @@ function checkNearestTagNotClosed(
case 'assert':
case 'discriminator':
if (triggerText.endsWith('>')) {
- insertSnippet('</' + nsPrefix + nearestTagNotClosed + '>', backpos)
+ insertSnippet('$1</' + nsPrefix + nearestTagNotClosed + '>', backpos)
} else {
- insertSnippet('></' + nsPrefix + nearestTagNotClosed + '>$0', backpos)
+ insertSnippet('>$1</' + nsPrefix + nearestTagNotClosed + '>$0', backpos)
}
break
default:
@@ -204,6 +216,7 @@ function checkTriggerText(
if (triggerText.includes('<' + nsPrefix + nearestTagNotClosed)) {
let tagPos = triggerText.lastIndexOf('<' + nsPrefix + nearestTagNotClosed)
let tagEndPos = triggerText.indexOf('>', tagPos)
+
if (
tagPos != -1 &&
!triggerText.substring(tagEndPos - 1, 2).includes('/>') &&
diff --git a/src/language/providers/closeElementSlash.ts b/src/language/providers/closeElementSlash.ts
index 49d47bd..3137e30 100644
--- a/src/language/providers/closeElementSlash.ts
+++ b/src/language/providers/closeElementSlash.ts
@@ -24,6 +24,8 @@ import {
getItemPrefix,
getItemsOnLineCount,
cursorWithinBraces,
+ cursorWithinQuotes,
+ cursorAfterEquals,
} from './utils'
export function getCloseElementSlashProvider() {
@@ -48,7 +50,9 @@ export function getCloseElementSlashProvider() {
if (
checkBraceOpen(document, position) ||
- cursorWithinBraces(document, position)
+ cursorWithinBraces(document, position) ||
+ cursorWithinQuotes(document, position) ||
+ cursorAfterEquals(document, position)
) {
return undefined
}
@@ -89,8 +93,10 @@ function checkItemsOnLine(
triggerText: string
) {
nsPrefix = getItemPrefix(nearestTagNotClosed, nsPrefix)
+
if (itemsOnLine == 1 || itemsOnLine == 0) {
insertSnippet('/>$0', backpos)
+
if (
nearestTagNotClosed.includes('defineVariable') ||
nearestTagNotClosed.includes('setVariable')
@@ -111,6 +117,7 @@ function checkItemsOnLine(
) {
let tagPos = triggerText.lastIndexOf('<' + nsPrefix + nearestTagNotClosed)
let tagEndPos = triggerText.indexOf('>', tagPos)
+
if (
tagPos != -1 &&
!triggerText.substring(tagEndPos - 1, 2).includes('/>') &&
diff --git a/src/language/providers/closeUtils.ts b/src/language/providers/closeUtils.ts
index 12d1378..1b931cf 100644
--- a/src/language/providers/closeUtils.ts
+++ b/src/language/providers/closeUtils.ts
@@ -78,6 +78,7 @@ export function cursorInsideCloseTag(
const triggerPos = position.character
const closeTagStart = triggerText.lastIndexOf('</')
const closeTagEnd = triggerText.lastIndexOf('>')
+
if (
triggerPos > closeTagStart &&
triggerPos <= closeTagEnd &&
@@ -126,10 +127,13 @@ export function getCloseTag(
tagOpen = tagClose + 1
}
} else {
+ let nestedTagCount = 0
let endPos = triggerText.indexOf('>', startPos)
+
if (triggerText.includes('?xml version')) {
return [tag, 0, 0]
}
+
if (
(triggerText.includes('</') || triggerText.includes('/>')) &&
triggerText.includes(tag) &&
@@ -162,9 +166,30 @@ export function getCloseTag(
//skipping to closing tag
while (!currentText.includes('</' + nsPrefix + tag)) {
currentText = document.lineAt(++lineNum).text
+
+ //If currentText is multi tag line skip to next line
if (getItemsOnLineCount(currentText) > 1) {
currentText = document.lineAt(++lineNum).text
}
+
+ if (currentText.includes('<' + nsPrefix + tag)) {
+ ++nestedTagCount
+ while (!currentText.includes('>')) {
+ currentText = document.lineAt(++lineNum).text
+ }
+ if (currentText.includes('/>')) {
+ --nestedTagCount
+ }
+ }
+
+ //if currentText is a closing tag
+ if (
+ currentText.includes('</' + nsPrefix + tag) &&
+ nestedTagCount > 0
+ ) {
+ --nestedTagCount
+ currentText = ''
+ }
}
}
@@ -174,10 +199,12 @@ export function getCloseTag(
!currentText.includes('>')
) {
isMultiLineTag = true
+
//skip to the end tag symbol
while (!currentText.includes('>')) {
currentText = document.lineAt(++lineNum).text
}
+
//if the tag isn't self closing, skip to the closing tag
if (!currentText.includes('/>')) {
while (!currentText.includes('</' + nsPrefix + tag)) {
@@ -187,12 +214,14 @@ export function getCloseTag(
}
if (
- currentText.includes('</' + nsPrefix + tag) ||
+ (currentText.includes('</' + nsPrefix + tag) &&
+ nestedTagCount === 0) ||
(currentText.includes('/>') && isMultiLineTag)
) {
if (isMultiLineTag) {
startPos = triggerPos
}
+
//if the cursor is after the closing tag
if (
lineNum == triggerLine &&
@@ -200,14 +229,13 @@ export function getCloseTag(
) {
return ['none', lineNum, startPos]
}
+
return [tag, lineNum, startPos]
}
}
-
++lineNum
}
}
-
return ['none', 0, 0]
}
@@ -257,7 +285,7 @@ export function getItemsForLineGT1(
return items[i]
}
- return undefined
+ return 'none'
}
export function getItemsForLineLT2(
@@ -278,6 +306,7 @@ export function getItemsForLineLT2(
let closeTagArray: number[] = []
nsPrefix = getItemPrefix(items[i], nsPrefix)
+
while (
currentText.indexOf('<' + nsPrefix + items[i]) === -1 &&
currentLine > -1
@@ -296,6 +325,7 @@ export function getItemsForLineLT2(
if (currentText.indexOf('<' + nsPrefix + items[i]) > -1) {
while (lineBefore > -1) {
currentText = document.lineAt(lineBefore).text
+
if (getItemsOnLineCount(currentText) < 2) {
if (currentText.indexOf('<' + nsPrefix + items[i]) > -1) {
openTagArray.push(lineBefore)
@@ -310,7 +340,11 @@ export function getItemsForLineLT2(
}
//if selfclosing remove from the array
- if (testText.indexOf('/>') > -1 || testText.includes('xml version')) {
+ if (
+ testText.indexOf('/>') > -1 ||
+ testText.includes('xml version') ||
+ currentText.indexOf('</' + nsPrefix + items[i]) > -1
+ ) {
openTagArray.splice(openTagArray.length - 1, 1)
}
}
diff --git a/src/language/providers/elementCompletion.ts b/src/language/providers/elementCompletion.ts
index fb28848..f9f836a 100644
--- a/src/language/providers/elementCompletion.ts
+++ b/src/language/providers/elementCompletion.ts
@@ -26,6 +26,8 @@ import {
nearestTag,
getItemsOnLineCount,
cursorWithinBraces,
+ cursorWithinQuotes,
+ cursorAfterEquals,
} from './utils'
import { elementCompletion } from './intellisense/elementItems'
@@ -39,7 +41,9 @@ export function getElementCompletionProvider(dfdlFormatString: string) {
) {
if (
checkBraceOpen(document, position) ||
- cursorWithinBraces(document, position)
+ cursorWithinBraces(document, position) ||
+ cursorWithinQuotes(document, position) ||
+ cursorAfterEquals(document, position)
) {
return undefined
}
@@ -49,9 +53,20 @@ export function getElementCompletionProvider(dfdlFormatString: string) {
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 (checkMissingCloseTag(document, position, nsPrefix) !== 'none') {
+ if (missingCloseTag !== 'none') {
+ return undefined
+ }
+ if (
+ missingCloseTag === 'none' &&
+ itemsOnLine > 1 &&
+ (triggerPos === lastCloseSymbol + 1 || triggerPos === firstOpenSymbol)
+ ) {
return undefined
}
@@ -215,6 +230,10 @@ function checkTagNearestOpen(
'',
nsPrefix
)
+ case 'assert':
+ return getElementCompletionItems(['CDATA', '{}'], '', '', nsPrefix)
+ case 'discriminator':
+ return getElementCompletionItems(['CDATA', '{}'], '', '', nsPrefix)
case 'defineFormat':
return getElementCompletionItems(['format'], '', '', nsPrefix)
case 'schema':
@@ -271,12 +290,23 @@ export function getTagNearestTrigger(
)
if (itemsOnLine > 1) {
- let afterTrigger =
- triggerText.substring(triggerPos).indexOf('<') + triggerPos
- let beforeTrigger = triggerText.substring(0, triggerPos).lastIndexOf('>')
- if (triggerPos === afterTrigger && triggerPos === beforeTrigger + 1)
+ const afterTriggerText = triggerText.substring(triggerPos)
+ const afterTriggerPos = afterTriggerText.indexOf('<') + triggerPos
+ const beforeTriggerText = triggerText.substring(0, triggerPos)
+ const lastOpenTagBeforeTriggerPos = beforeTriggerText.lastIndexOf('<')
+ const beforeTriggerPos = beforeTriggerText.lastIndexOf('>')
+ const beforeTriggerTag = beforeTriggerText.substring(
+ lastOpenTagBeforeTriggerPos
+ )
+
+ if (
+ triggerPos === afterTriggerPos &&
+ triggerPos === beforeTriggerPos + 1 &&
+ !beforeTriggerTag.startsWith('</')
+ ) {
tagNearestTrigger = foundTag
- return tagNearestTrigger
+ return tagNearestTrigger
+ }
}
startLine = foundLine
@@ -295,6 +325,7 @@ export function getTagNearestTrigger(
tagNearestTrigger = foundTag
return tagNearestTrigger
}
+
if (endTag === 'none') {
startLine = foundLine - 1
} else {
diff --git a/src/language/providers/intellisense/attributeItems.ts b/src/language/providers/intellisense/attributeItems.ts
index 6d5f20e..27ee6d1 100644
--- a/src/language/providers/intellisense/attributeItems.ts
+++ b/src/language/providers/intellisense/attributeItems.ts
@@ -236,7 +236,7 @@ export const attributeCompletion = (additionalItems, nsPrefix: string, dfdlPrefi
},
{
item: 'dfdl:leadingSkip',
- snippetString: dfdlPrefix + 'trailingSkip="0$1"$0',
+ snippetString: dfdlPrefix + 'leadingSkip="0$1"$0',
markdownString: 'A non-negative number of bytes or bits to skip before alignment is applied',
},
{
diff --git a/src/language/providers/intellisense/attributeValueItems.ts b/src/language/providers/intellisense/attributeValueItems.ts
new file mode 100644
index 0000000..9b28a18
--- /dev/null
+++ b/src/language/providers/intellisense/attributeValueItems.ts
@@ -0,0 +1,292 @@
+/*
+ * 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 { insertSnippet } from '../utils'
+
+export const noChoiceAttributes = [
+ 'name',
+ 'ref',
+ 'occursCount',
+ 'length',
+ 'prefixLengthType',
+ 'nilValue',
+ 'lengthPattern',
+ 'inputValueCalc',
+ 'outputValueCalc',
+ 'hiddenGroupRef',
+ 'choiceBranchKey',
+ 'textNumberRoundingIncrement',
+ 'separator',
+ 'terminator',
+ 'choiceLength',
+ 'fillByte',
+ 'initiator',
+ 'choiceDispatchKey',
+ 'escapeSchemeRef',
+ 'test',
+ 'testPattern',
+ 'message',
+]
+
+export function attributeValues(
+ attributeName: string,
+ startPos: vscode.Position,
+ additionalTypes: string
+) {
+ switch (attributeName) {
+ case 'minOccurs':
+ insertSnippet('"${1|0,1|}"$0', startPos)
+ break
+ case 'maxOccurs':
+ insertSnippet('"${1|0,1,unbounded|}"$0', startPos)
+ break
+ case 'occursCount':
+ insertSnippet('"$1"$0', startPos)
+ break
+ case 'byteOrder':
+ insertSnippet('"${1|bigEndian,littleEndian|}"$0', startPos)
+ break
+ case 'bitOrder':
+ insertSnippet(
+ '"${1|mostSignificantBitFirst,leastSignificantBitFirst|}"$0',
+ startPos
+ )
+ break
+ case 'occursCountKind':
+ insertSnippet(
+ '"${1|expression,fixed,implicit,parsed,stopValue|}"$0',
+ startPos
+ )
+ break
+ case 'length':
+ insertSnippet('"$1"$0', startPos)
+ break
+ case 'lengthKind':
+ insertSnippet(
+ '"${1|delimited,fixed,explicit,implicit,prefixed,patternendOfParent|}"$0',
+ startPos
+ )
+ break
+ case 'prefixIncludesPrefixLength':
+ insertSnippet('"${1|yes,no|}"$0', startPos)
+ break
+ case 'prefixLengthType':
+ insertSnippet('"$1"$0', startPos)
+ break
+ case 'utf16Width':
+ insertSnippet('"${1|fixed,variable|}"$0', startPos)
+ break
+ case 'encoding':
+ insertSnippet(
+ '"${1|US-ASCII,ASCII,UTF-8,UTF-16,UTF-16BE,UTF-16LE,ISO-8859-1|}"$0',
+ startPos
+ )
+ break
+ case 'encodingErrorPolicy':
+ insertSnippet('"${1|error,replace|}"$0', startPos)
+ break
+ case 'nilKind':
+ insertSnippet(
+ '"${1|literalCharacter,literalValue,logicalValue|}"$0',
+ startPos
+ )
+ break
+ case 'nilValue':
+ insertSnippet('nilValue="$1"$0', startPos)
+ break
+ case 'nilValueDelimiterPolicy':
+ insertSnippet('"${1|initiator,terminator,both,none|}"$0', startPos)
+ break
+ case 'alignment':
+ insertSnippet('"${1|1,2,implicit|}"$0', startPos)
+ break
+ case 'lengthUnits':
+ insertSnippet('"${1|bits,bytes,characters|}"$0', startPos)
+ break
+ case 'lengthPattern':
+ insertSnippet('"$1"$0', startPos)
+ break
+ case 'inputValueCalc':
+ insertSnippet('"{$1}"$0', startPos)
+ break
+ case 'outputValueCalc':
+ insertSnippet('"{$1}"$0', startPos)
+ break
+ case 'alignmentUnits':
+ insertSnippet('"${1|bits,bytes|}"$0', startPos)
+ break
+ case 'outputNewLine':
+ insertSnippet('"${1|%CR;,%LF;,%CR;%LF;,%NEL;,%LS;|}"$0', startPos)
+ break
+ case 'choiceBranchKey':
+ insertSnippet('"$1"$0', startPos)
+ break
+ case 'representation':
+ insertSnippet('"${1|binary,text|}"$0', startPos)
+ break
+ case 'textStringJustification':
+ insertSnippet('"${1|left,right,center|}"$0', startPos)
+ break
+ case 'textStandardZeroRep':
+ insertSnippet('"0"$0', startPos)
+ break
+ case 'textStandardInfinityRep':
+ insertSnippet('"Inf"$0', startPos)
+ break
+ case 'textStandardExponentRep':
+ insertSnippet('"E"$0', startPos)
+ break
+ case 'textStandardNaNRep':
+ insertSnippet('"NaN"$0', startPos)
+ break
+ case 'textNumberPattern':
+ insertSnippet('"#,##0.###;-#,##0.###"$0', startPos)
+ break
+ case 'textNumberRep':
+ insertSnippet('"${1|standard,zoned|}"$0', startPos)
+ break
+ case 'textNumberRoundingMode':
+ insertSnippet(
+ '"${1|roundCeiling,roundFloor,roundDown,roundUp,roundHalfEven,roundHalfDown,roundHalfUp,roundUnnecessary|}"$0',
+ startPos
+ )
+ break
+ case 'textNumberRoundingIncrement':
+ insertSnippet('"0"$0', startPos)
+ break
+ case 'textNumberRounding':
+ insertSnippet('"${1|explicit,pattern|}"$0', startPos)
+ break
+ case 'textNumberCheckPolicy':
+ insertSnippet('"${1|lax,strict|}"$0', startPos)
+ break
+ case 'textOutputMinLength':
+ insertSnippet('"0"$0', startPos)
+ break
+ case 'textStandardGroupingSeparator':
+ insertSnippet('","$0', startPos)
+ break
+ case 'textPadKind':
+ insertSnippet('"${1|none,padChar|}"$0', startPos)
+ break
+ case 'textStandardBase':
+ insertSnippet('"${1|2,8,10,16|}"$0', startPos)
+ break
+ case 'textTrimKind':
+ insertSnippet('"${1|none,padChar|}"$0', startPos)
+ break
+ case 'leadingSkip':
+ insertSnippet('"0$1"$0', startPos)
+ break
+ case 'trailingSkip':
+ insertSnippet('"0$1"$0', startPos)
+ break
+ case 'truncateSpecifiedLengthString':
+ insertSnippet('"${1|no,yes|}"$0', startPos)
+ break
+ case 'sequenceKind':
+ insertSnippet('"${1|ordered,unordered|}"$0', startPos)
+ break
+ case 'separator':
+ insertSnippet('"$1"$0', startPos)
+ break
+ case 'separatorPosition':
+ insertSnippet('"${1|infix,postfix,prefix|}"$0', startPos)
+ break
+ case 'separatorSuppressionPolicy':
+ insertSnippet(
+ '"${1|anyEmpty,never,trailingEmpty,trailingEmptyStrict|}"$0',
+ startPos
+ )
+ break
+ case 'terminator':
+ insertSnippet('"$1"$0', startPos)
+ break
+ case 'textBidi':
+ insertSnippet('"${1|no,yes|}"$0', startPos)
+ break
+ case 'hiddenGroupRef':
+ insertSnippet('"$1"\n$0', startPos)
+ break
+ case 'choiceLengthKind':
+ insertSnippet('"${1|explicit,implicit|}"$0', startPos)
+ break
+ case 'choiceLength':
+ insertSnippet('"$1"$0', startPos)
+ break
+ case 'fillByte':
+ insertSnippet('"$1"$0', startPos)
+ break
+ case 'ignoreCase':
+ insertSnippet('"${1|no,yes|}"$0', startPos)
+ break
+ case 'initiatedContent':
+ insertSnippet('"${1|yes,no|}"$0', startPos)
+ break
+ case 'initiator':
+ insertSnippet('"$1"$0', startPos)
+ break
+ case 'choiceDispatchKey':
+ insertSnippet('"$1"$0', startPos)
+ break
+ case 'binaryNumberRep':
+ insertSnippet('"${1|binary,packed,bcd,ibm4690Packed|}"$0', startPos)
+ break
+ case 'floating':
+ insertSnippet('"${1|no,yes|}"$0', startPos)
+ break
+ case 'binaryFloatRep':
+ insertSnippet('"${1|ieee,ibm390Hex|}"$0', startPos)
+ break
+ case 'calendarPatternKind':
+ insertSnippet('"${1|explicit,implicit|}"$0', startPos)
+ break
+ case 'documentFinalTerminatorCanBeMissing':
+ insertSnippet('"${1|yes,no|}"$0', startPos)
+ break
+ case 'emptyValueDelimiterPolicy':
+ insertSnippet('"${1|initiator,terminator,both,none|}"$0', startPos)
+ break
+ case 'escapeSchemeRef':
+ insertSnippet('"$1"$0', startPos)
+ break
+ case 'testKind':
+ insertSnippet('"${1|expression,pattern|}"$0', startPos)
+ break
+ case 'test':
+ insertSnippet('"{$1}"$0', startPos)
+ break
+ case 'testPattern':
+ insertSnippet('"$1"$0', startPos)
+ break
+ case 'message':
+ insertSnippet('"$1"$0', startPos)
+ break
+ case 'failureType':
+ insertSnippet('"${1|processingError,recoverableError|}"$0', startPos)
+ break
+ case 'type':
+ insertSnippet(
+ '"${1|xs:string,xs:decimal,xs:float,xs:double,xs:integer,xs:nonNegativeInteger,xs:int,xs:unsignedInt,xs:short,xs:unsignedShort,xs:long,xs:unsignedLong,xs:byte,xs:unsignedByte,xs:hexBinary,xs:boolean' +
+ additionalTypes +
+ '|}"$0',
+ startPos
+ )
+ break
+ }
+}
diff --git a/src/language/providers/intellisense/elementItems.ts b/src/language/providers/intellisense/elementItems.ts
index a79f43f..ca82eb6 100644
--- a/src/language/providers/intellisense/elementItems.ts
+++ b/src/language/providers/intellisense/elementItems.ts
@@ -53,12 +53,12 @@ export const elementCompletion = (definedVariables, dfdlFormatString, nsPrefix)
},
{
item: 'dfdl:assert',
- snippetString: '<dfdl:assert>$1\n</dfdl:assert>$0',
+ snippetString: '<dfdl:assert $0',
markdownString: 'Used to assert truths about a DFDL model',
},
{
item: 'dfdl:discriminator',
- snippetString: '<dfdl:discriminator test="{$1}"/>$0',
+ snippetString: '<dfdl:discriminator $0',
markdownString: 'Used during parsing to resolve points or uncertainity, remove ambiguity during speculative parsing, improve diagnostic behavior',
},
{
@@ -160,6 +160,21 @@ export const elementCompletion = (definedVariables, dfdlFormatString, nsPrefix)
snippetString: '<' + nsPrefix + 'maxExclusive value="$1"/>$0',
markdownString: 'Used to check the validity of an element'
},
+ {
+ item: '<[CDATA[]]>',
+ snippetString: '<[CDATA[$1]]>$0',
+ markdownString: ''
+ },
+ {
+ item: '<![CDATA[]]>',
+ snippetString: '<![CDATA[$1]]>$0',
+ markdownString: ''
+ },
+ {
+ item: '{}',
+ snippetString: '{$1}$0',
+ markdownString: ''
+ },
],
}
}
diff --git a/src/language/providers/utils.ts b/src/language/providers/utils.ts
index 6c711e4..b50d4a4 100644
--- a/src/language/providers/utils.ts
+++ b/src/language/providers/utils.ts
@@ -69,10 +69,12 @@ export function lineCount(
let lineNum = position.line
let lineCount = 0
const nsPrefix = getXsdNsPrefix(document, position)
+
while (lineNum !== 0) {
--lineNum
++lineCount
const triggerText = document.lineAt(lineNum).text
+
if (
triggerText.includes('<' + nsPrefix + tag) &&
!triggerText.includes('</' + nsPrefix + tag) &&
@@ -92,6 +94,7 @@ export function nearestOpen(
return 'none'
}
const nsPrefix = getXsdNsPrefix(document, position)
+
for (let i = 0; i < items.length; ++i) {
if (checkTagOpen(document, position, nsPrefix, items[i])) {
return items[i]
@@ -114,6 +117,7 @@ export function nearestTag(
const itemsOnLine = getItemsOnLineCount(document.lineAt(lineNum).text)
let tagPos = triggerText.indexOf('<')
let endPos = triggerText.lastIndexOf('>')
+
if (
itemsOnLine > 1 &&
startPos !== tagPos &&
@@ -122,15 +126,18 @@ export function nearestTag(
) {
let textBeforeTrigger = triggerText.substring(0, startPos)
let prevTagPos = 0
+
while (prevTagPos > -1) {
prevTagPos = textBeforeTrigger.lastIndexOf('<')
let tag = textBeforeTrigger.substring(prevTagPos)
+
if (
!textBeforeTrigger.includes('</') &&
!textBeforeTrigger.includes('/>')
) {
for (let i = 0; i < items.length; ++i) {
nsPrefix = getItemPrefix(items[i], origPrefix)
+
if (tag.includes('<' + nsPrefix + items[i])) {
return [items[i], startLine, prevTagPos]
}
@@ -145,15 +152,28 @@ export function nearestTag(
) {
--lineNum
}
+
while (lineNum > -1 && lineNum < document.lineCount) {
let currentText = document.lineAt(lineNum).text
+
if (getItemsOnLineCount(currentText) < 2) {
- if (!currentText.includes('</') && !currentText.includes('/>')) {
+ if (!currentText.includes('/>')) {
for (let i = 0; i < items.length; ++i) {
nsPrefix = getItemPrefix(items[i], origPrefix)
+
if (
- currentText.includes('<' + nsPrefix + items[i]) ||
- (lineNum === 0 && currentText.includes(items[i]))
+ !currentText.includes('</') &&
+ (currentText.includes('<' + nsPrefix + items[i]) ||
+ (lineNum === 0 && currentText.includes(items[i])))
+ ) {
+ return [items[i], lineNum, startPos]
+ }
+
+ if (
+ currentText.includes('<' + nsPrefix + items[i]) &&
+ currentText.includes('</' + nsPrefix + items[i]) &&
+ position.character > currentText.indexOf('>') &&
+ position.character <= currentText.indexOf('</')
) {
return [items[i], lineNum, startPos]
}
@@ -180,17 +200,21 @@ export function checkTagOpen(
let isMultiLineTag = false
let origTriggerText = triggerText
let origTriggerLine = triggerLine
- while (itemsOnLine < 2 && triggerText.indexOf('<') === -1) {
+ const triggerPos = position.character
+ const textBeforeTrigger = triggerText.substring(0, triggerPos)
+
+ while (itemsOnLine < 2 && !triggerText.trim().startsWith('<')) {
triggerText = document.lineAt(--triggerLine).text
}
+
if (!(triggerText.endsWith('>') && triggerText.includes('<'))) {
isMultiLineTag = true
}
- const triggerPos = position.character
- const textBeforeTrigger = triggerText.substring(0, triggerPos)
+
let tagPos = textBeforeTrigger.lastIndexOf('<' + nsPrefix + tag)
const nextTagPos = triggerText.indexOf('<', tagPos + 1)
let tagEndPos = triggerText.indexOf('>', tagPos)
+
if (tagPos > -1 && itemsOnLine > 1) {
if (
triggerPos > tagPos &&
@@ -206,20 +230,25 @@ export function checkTagOpen(
origTriggerText = document.lineAt(--origTriggerLine).text
}
tagPos = triggerText.indexOf('<' + nsPrefix + tag)
+
if (itemsOnLine < 2 && tagPos > -1) {
if (triggerText !== origTriggerText) {
tagEndPos = origTriggerText.indexOf('>')
}
+
if (
(triggerPos > tagPos &&
triggerPos <= tagEndPos &&
triggerLine === position.line) ||
- (origTriggerLine == position.line && triggerPos <= tagEndPos) ||
+ (origTriggerLine == position.line &&
+ triggerPos <= tagEndPos &&
+ triggerPos > tagPos) ||
position.line < origTriggerLine
) {
return true
}
}
+
if (!isMultiLineTag || tagPos === -1) {
return false
}
@@ -238,6 +267,7 @@ export function checkTagOpen(
export function getItemPrefix(item: string, nsPrefix: string) {
let itemPrefix = nsPrefix
+
if (
item === 'assert' ||
item === 'discriminator' ||
@@ -247,9 +277,11 @@ export function getItemPrefix(item: string, nsPrefix: string) {
) {
itemPrefix = 'dfdl:'
}
+
if (item === 'xml version') {
itemPrefix = '?'
}
+
if (item === 'dfdl:element' || item === 'dfdl:simpleType') {
itemPrefix = ''
}
@@ -269,7 +301,8 @@ export function checkMultiLineTag(
return false
}
let currentLine = position.line
- let currentText = document.lineAt(currentLine).text
+ const origText = document.lineAt(currentLine).text
+ let currentText = origText
//the current line doesn't have the self close symbol
if (!currentText.endsWith('/>')) {
@@ -277,19 +310,24 @@ export function checkMultiLineTag(
--currentLine
currentText = document.lineAt(currentLine).text
}
+
if (
currentText.indexOf('<' + nsPrefix + tag) !== -1 &&
currentText.indexOf('>') === -1 &&
- (currentText.indexOf('<' + nsPrefix + tag) < position.character ||
- currentLine < position.line)
+ currentText.indexOf('<' + nsPrefix + tag) &&
+ currentLine <= position.line &&
+ (origText.indexOf('>') > position.character ||
+ origText.indexOf('>') === -1)
) {
return true
}
}
+
if (currentText.endsWith('/>')) {
let triggerPos = position.character
let tagEndPos = currentText.indexOf('/>')
let triggerLine = position.line
+
if (
(triggerLine === currentLine && triggerPos < tagEndPos) ||
(triggerLine === tagLine && triggerPos > tagPos && tagPos !== -1) ||
@@ -308,10 +346,12 @@ export function getXsdNsPrefix(
) {
let initialLineNum = position.line
let lineNum = 0
+
while (initialLineNum !== 0 && lineNum <= initialLineNum) {
const lineText = document.lineAt(lineNum).text
// returns either empty prefix value or a prefix plus a colon
let text = lineText.match(schemaPrefixRegEx)
+
if (text != null) {
return text[1]
}
@@ -325,10 +365,12 @@ export function getItemsOnLineCount(triggerText: String) {
let itemsOnLine = 0
let nextPos = 0
let result = 0
+
if (triggerText.includes('schema')) {
itemsOnLine = 1
return itemsOnLine
}
+
while (result != -1 && triggerText.includes('<')) {
result = triggerText.indexOf('<', nextPos)
if (result > -1) {
@@ -338,6 +380,7 @@ export function getItemsOnLineCount(triggerText: String) {
break
}
let testForCloseTag = triggerText.substring(nextPos, endPos)
+
if (
!testForCloseTag.includes('</') &&
!testForCloseTag.includes('<!--') &&
@@ -354,6 +397,99 @@ export function getItemsOnLineCount(triggerText: String) {
return itemsOnLine
}
+export function cursorAfterEquals(
+ document: vscode.TextDocument,
+ position: vscode.Position
+) {
+ const triggerText = document.lineAt(position.line).text
+ const triggerPos = position.character
+ const textBeforeTrigger = triggerText.substring(0, triggerPos)
+ let currentPos = -1
+
+ if ((currentPos = textBeforeTrigger.lastIndexOf('=')) === -1) {
+ return false
+ }
+ if (triggerPos === currentPos + 1) {
+ return true
+ }
+ return false
+}
+
+export function cursorWithinQuotes(
+ document: vscode.TextDocument,
+ position: vscode.Position
+) {
+ const quoteChar: string[] = ["'", '"']
+ let startLine = position.line
+
+ for (let i = 0; i < quoteChar.length; ++i) {
+ let currentText = document.lineAt(startLine).text
+
+ if (
+ currentText.includes('<') &&
+ !currentText.includes("'") &&
+ !currentText.includes('"')
+ ) {
+ return false
+ }
+
+ if (currentText.includes(quoteChar[i])) {
+ let textBeforeTrigger = currentText.substring(0, position.character)
+ //let tagStartPos = -1
+ let quoteStartLine = startLine
+ let quoteStartPos = -1
+ let equalStartPos = -1
+
+ while (
+ (equalStartPos = textBeforeTrigger.lastIndexOf('=' + quoteChar[i])) ===
+ -1
+ ) {
+ if (textBeforeTrigger.indexOf('<') !== -1) {
+ break
+ }
+ textBeforeTrigger = document.lineAt(--quoteStartLine).text
+ }
+
+ quoteStartPos = equalStartPos + 1
+ let quoteEndLine = quoteStartLine
+ let quoteEndPos = -1
+
+ if (quoteStartPos > -1) {
+ while (
+ quoteEndLine < document.lineCount &&
+ (quoteEndPos = currentText.indexOf(
+ quoteChar[i],
+ quoteStartPos + 1
+ )) === -1
+ ) {
+ currentText = document.lineAt(++quoteEndLine).text
+ }
+
+ if (
+ quoteEndPos > -1 &&
+ currentText.indexOf('=', quoteStartPos - 1) === quoteStartPos - 1
+ ) {
+ if (
+ (position.line > quoteStartLine && position.line < quoteEndLine) ||
+ (quoteEndLine === quoteStartLine &&
+ position.character > quoteStartPos &&
+ position.character <= quoteEndPos) ||
+ (position.line === quoteStartLine &&
+ position.character > quoteStartPos &&
+ position.line < quoteEndLine) ||
+ (position.line === quoteEndLine &&
+ position.character <= quoteEndPos &&
+ position.line > quoteStartLine)
+ ) {
+ return true
+ }
+ }
+ }
+ }
+ }
+ return false
+}
+
export function cursorWithinBraces(
document: vscode.TextDocument,
position: vscode.Position
@@ -362,6 +498,7 @@ export function cursorWithinBraces(
let currentText = document.lineAt(startLine).text
let braceStartLine = startLine
let braceStartPos = -1
+
while (
braceStartLine > 0 &&
(braceStartPos = currentText.indexOf('{')) === -1
@@ -370,6 +507,7 @@ export function cursorWithinBraces(
}
let braceEndLine = braceStartLine
let braceEndPos = -1
+
if (braceStartPos > -1) {
while (
braceEndLine < document.lineCount &&
@@ -377,6 +515,7 @@ export function cursorWithinBraces(
) {
currentText = document.lineAt(++braceEndLine).text
}
+
if (braceEndPos > -1) {
if (
(position.line > braceStartLine && position.line < braceEndLine) ||
@@ -408,14 +547,17 @@ export function checkBraceOpen(
while (!triggerText.includes('}') && lineNum < document.lineCount) {
triggerText = document.lineAt(++lineNum).text
}
+
if (!triggerText.includes('}')) {
return true
}
}
+
if (triggerText.includes('}')) {
while (!triggerText.includes('{') && lineNum > 0) {
triggerText = document.lineAt(--lineNum).text
}
+
if (!triggerText.includes('{')) {
return true
}
diff --git a/src/tests/suite/language/items.test.ts b/src/tests/suite/language/items.test.ts
index 01b6f7b..02c2926 100644
--- a/src/tests/suite/language/items.test.ts
+++ b/src/tests/suite/language/items.test.ts
@@ -51,6 +51,9 @@ suite('Items Test Suite', () => {
'minExclusive',
'maxInclusive',
'maxExclusive',
+ '<[CDATA[]]>',
+ '<![CDATA[]]>',
+ '{}',
]
const expectedAttributeItems = [
'name',