diff --git a/src/language/semantics/dfdlExt.ts b/src/language/semantics/dfdlExt.ts index 18c54968a..e9b62c0d5 100644 --- a/src/language/semantics/dfdlExt.ts +++ b/src/language/semantics/dfdlExt.ts @@ -3,19 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { EOL } from 'os' import * as vscode from 'vscode' -import { - XPathLexer, - ExitCondition, - LexPosition, - Token, - BaseToken, -} from './xpLexer' -import { - XslLexer, - GlobalInstructionData, - GlobalInstructionType, -} from './xslLexer' +import { XPathLexer, ExitCondition, LexPosition, Token } from './xpLexer' +import { XslLexer } from './xslLexer' import { XsltTokenDiagnostics } from './xsltTokenDiagnostics' import { DocumentChangeHandler } from './documentChangeHandler' @@ -53,40 +44,13 @@ export class XPathSemanticTokensProvider this.collection = collection } - private static globalInstructionData: GlobalInstructionData[] = [] - - public static getGlobalInstructionData() { - return XPathSemanticTokensProvider.globalInstructionData - } - - public static setVariableNames = (names: string[]) => { - const data: GlobalInstructionData[] = [] - - names.forEach((name) => { - const token: BaseToken = { - line: 1, - startCharacter: 0, - length: 1, - value: name, - tokenType: 0, - } - const variableInstruction: GlobalInstructionData = { - type: GlobalInstructionType.Variable, - name: name, - token: token, - idNumber: 0, - } - data.push(variableInstruction) - }) - XPathSemanticTokensProvider.globalInstructionData = data - } - async provideDocumentSemanticTokens( document: vscode.TextDocument, token: vscode.CancellationToken ): Promise { this.xpLexer.documentTokens = [] - let variables: string[] = this.findAllVariables(document.getText()) + let [variables, namespaces]: [string[], string[]] = + this.findAllVariablesandNamespaces(document.getText()) let tokens: Token[] = [] const tokenPositions = this.findAllXPath(document.getText()) @@ -118,7 +82,10 @@ export class XPathSemanticTokensProvider // This was moved to inside the loop. If it isn't, the sections of XPath will be treated // as a single XPath section instead of multiples - setTimeout(() => this.reportProblems(tmpTokens, document, variables), 0) + setTimeout( + () => this.reportProblems(tmpTokens, document, variables, namespaces), + 0 + ) // Reset the xpLexer. If this is not done, existing tokens will not be flushed // and will be re-added to the tokens list. This might not affect the operation, but it does @@ -201,14 +168,27 @@ export class XPathSemanticTokensProvider } // Find the names of all variables in the file - private findAllVariables(document: String | undefined): string[] { + private findAllVariablesandNamespaces( + document: String | undefined + ): [string[], string[]] { if (document === undefined) { - return [] + return [[], []] } - const lines = document.split('\n') + const lines = document.split(EOL) + const variableRegex = /(dfdl:defineVariable.*name=\")(.*?)\"/ - const variables: string[] = [] + // These are built-in predefined variables. Double check if we still need to hardcode these + // once we have the ability to create a DFDL model outside of a Parse operation. + const variables: string[] = [ + 'dfdl:encoding', + 'dfdl:byteOrder', + 'dfdl:binaryFloatRep', + 'dfdl:outputNewLine', + ] + + const namespaceRegex = /xmlns:(.*?)=\"/g + const namespaces: string[] = [] // Capture and return a list of variable names for (let i = 0; i < lines.length; i++) { @@ -217,24 +197,39 @@ export class XPathSemanticTokensProvider if (variableMatch) { variables.push(variableMatch[2]) } + + // If a DFDL schema has multiple namespaces defined on the same line, we need to make sure we are globally + // matching for the namespace pattern so that we don't miss any + let namespaceMatch: RegExpExecArray | null = null + + // Continue matching until we don't find any more namespaces on the same line + do { + namespaceMatch = namespaceRegex.exec(lines[i]) + + if (namespaceMatch) { + namespaces.push(namespaceMatch[1]) + } + } while (namespaceMatch) } - return variables + return [variables, namespaces] } // This function will produce the error/warning list for vscode private reportProblems( allTokens: Token[], document: vscode.TextDocument, - variables: string[] + variables: string[], + namespaces: string[] ) { let diagnostics = XsltTokenDiagnostics.calculateDiagnostics( document, allTokens, DocumentChangeHandler.lastXMLDocumentGlobalData, - XPathSemanticTokensProvider.globalInstructionData, [], - variables + [], + variables, + namespaces ) diagnostics.forEach((diag) => { this.diagnosticList.push(diag) diff --git a/src/language/semantics/functionData.ts b/src/language/semantics/functionData.ts index 211154cab..3626b9734 100644 --- a/src/language/semantics/functionData.ts +++ b/src/language/semantics/functionData.ts @@ -47,6 +47,7 @@ export class FunctionData { 'collection#1', 'compare#2', 'compare#3', + 'concat#2', 'concat#3', 'contains#2', 'contains#3', diff --git a/src/language/semantics/xsltTokenDiagnostics.ts b/src/language/semantics/xsltTokenDiagnostics.ts index 954cb4906..b879eee02 100644 --- a/src/language/semantics/xsltTokenDiagnostics.ts +++ b/src/language/semantics/xsltTokenDiagnostics.ts @@ -311,7 +311,8 @@ export class XsltTokenDiagnostics { globalInstructionData: GlobalInstructionData[], importedInstructionData: GlobalInstructionData[], symbols: vscode.DocumentSymbol[], - globalVariables: string[] + globalVariables: string[], + xmlNamespaces: string[] ): vscode.Diagnostic[] => { let inScopeVariablesList: VariableData[] = [] let xpathVariableCurrentlyBeingDefined: boolean @@ -330,7 +331,7 @@ export class XsltTokenDiagnostics { let topLevelSymbols: vscode.DocumentSymbol[] = symbols let tagIdentifierName: string = '' let lastTokenIndex = allTokens.length - 1 - let inheritedPrefixes: string[] = [] + let inheritedPrefixes: string[] = xmlNamespaces let globalVariableData: VariableData[] = [] let checkedGlobalVarNames: string[] = [] let checkedGlobalFnNames: string[] = [] @@ -567,7 +568,8 @@ export class XsltTokenDiagnostics { inScopeXPathVariablesList, xpathStack, inScopeVariablesList, - elementStack + elementStack, + xmlNamespaces ) if (unResolvedToken !== null) { unresolvedXsltVariableReferences.push(unResolvedToken) @@ -1624,7 +1626,8 @@ export class XsltTokenDiagnostics { inScopeXPathVariablesList: VariableData[], xpathStack: XPathData[], inScopeVariablesList: VariableData[], - elementStack: ElementData[] + elementStack: ElementData[], + xmlNamespaces: string[] ): BaseToken | null { let fullVarName = XsltTokenDiagnostics.getTextForToken( token.line, @@ -1666,6 +1669,13 @@ export class XsltTokenDiagnostics { importedResolved = globalVarName !== varName && importedVariables.indexOf(varName) > -1 } + // Parse the namespaces from the DFDL document that contains the XPath segments. If the variable is + // defined in an external namespace that we are importing, don't flag it. This is a temporary solution + // that we can remove once we have the ability to create a DFDL model outside of a Parse operation. + if (!resolved && !importedResolved && varName.includes(':')) { + let splits = varName.split(':') + resolved = xmlNamespaces.includes(splits[0]) ? token : undefined + } if (!resolved && !importedResolved) { result = token }