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