src/language/providers/attributeValueCompletion.ts (164 lines of code) (raw):

/* * 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, getNsPrefix } 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 schemaPosition = new vscode.Position(0, 0) const nsPrefix = getNsPrefix(document, schemaPosition) let additionalItems = getDefinedTypes(document, nsPrefix) let [attributeName, startPos, endPos] = getAttributeDetails( document, position ) if (attributeName !== 'none' && !attributeName.includes('xmlns:')) { 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 space is typed ) } export function getTDMLAttributeValueCompletionProvider() { return vscode.languages.registerCompletionItemProvider( 'dfdl', { async provideCompletionItems( document: vscode.TextDocument, position: vscode.Position ) { if ( checkBraceOpen(document, position) || cursorWithinBraces(document, position) ) { return undefined } const nsPrefix = getNsPrefix(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 space 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 ) { textBeforeTrigger = textBeforeTrigger.substring( 0, textBeforeTrigger.lastIndexOf('=') ) 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] }