From 8123959a1cf191b18516fe1332e25688b291eb56 Mon Sep 17 00:00:00 2001 From: Outer Cloud Studio Date: Sun, 21 Aug 2022 13:41:21 -0500 Subject: [PATCH 01/29] Remove Old Validator Files / References --- .../Editors/FunctionValidator/Error.vue | 26 - .../Editors/FunctionValidator/Tab.ts | 404 ---- .../Editors/FunctionValidator/Tab.vue | 294 --- .../Editors/FunctionValidator/Warning.vue | 26 - src/components/Languages/LanguageManager.ts | 3 +- src/components/Languages/Mcfunction.ts | 6 +- .../Languages/Mcfunction/Validation/Error.ts | 7 - .../Languages/Mcfunction/Validation/Token.ts | 8 - .../Mcfunction/Validation/Validator.ts | 1786 ----------------- .../Mcfunction/Validation/Warning.ts | 7 - .../Projects/Project/BedrockProject.ts | 1 - src/locales/en.json | 111 - src/locales/ja.json | 109 - src/locales/nl.json | 111 - src/locales/zh-CN.json | 107 - 15 files changed, 3 insertions(+), 3003 deletions(-) delete mode 100644 src/components/Editors/FunctionValidator/Error.vue delete mode 100644 src/components/Editors/FunctionValidator/Tab.ts delete mode 100644 src/components/Editors/FunctionValidator/Tab.vue delete mode 100644 src/components/Editors/FunctionValidator/Warning.vue delete mode 100644 src/components/Languages/Mcfunction/Validation/Error.ts delete mode 100644 src/components/Languages/Mcfunction/Validation/Token.ts delete mode 100644 src/components/Languages/Mcfunction/Validation/Validator.ts delete mode 100644 src/components/Languages/Mcfunction/Validation/Warning.ts diff --git a/src/components/Editors/FunctionValidator/Error.vue b/src/components/Editors/FunctionValidator/Error.vue deleted file mode 100644 index ec9c89d11..000000000 --- a/src/components/Editors/FunctionValidator/Error.vue +++ /dev/null @@ -1,26 +0,0 @@ - - - - - diff --git a/src/components/Editors/FunctionValidator/Tab.ts b/src/components/Editors/FunctionValidator/Tab.ts deleted file mode 100644 index d7fa5f0f2..000000000 --- a/src/components/Editors/FunctionValidator/Tab.ts +++ /dev/null @@ -1,404 +0,0 @@ -import { FileTab } from '/@/components/TabSystem/FileTab' -import FunctionValidatorTabComponent from './Tab.vue' -import { Tab } from '../../TabSystem/CommonTab' -import { TabSystem } from '../../TabSystem/TabSystem' -import Error from './Error.vue' -import Warning from './Warning.vue' -import Vue from 'vue' -import { FunctionValidator } from '/@/components/Languages/Mcfunction/Validation/Validator' -import { App } from '/@/App' -import { translate } from '../../Locales/Manager' - -export class FunctionValidatorTab extends Tab { - protected fileTab: FileTab | undefined - protected currentLine = 0 - protected content: string = '' - - protected stopped = false - - protected app: App | undefined - - constructor(protected parent: TabSystem, protected tab: FileTab) { - super(parent) - this.fileTab = tab - } - - get name(): string { - return translate('functionValidator.tabName') - } - - isFor(fileHandle: FileSystemFileHandle): Promise { - return Promise.resolve(false) - } - - component = FunctionValidatorTabComponent - - get icon() { - return 'mdi-cog-box' - } - - get iconColor() { - return 'primary' - } - - save() {} - - //Load function content - protected async LoadFileContent() { - if (this.fileTab) { - let file = await this.fileTab.getFile() - this.content = await file?.text() - } - } - - protected translateError(errorName: string) { - return translate('functionValidator.errors.' + errorName) - } - - protected translateWarning(errorName: string) { - return translate('functionValidator.warnings.' + errorName) - } - - protected async UpdateLoadedState() { - let dataLoadingElement = document.getElementById('data-loading') - let loadedDataElement = document.getElementById('loaded-content') - - if (this.app!.languageManager.mcfunction.validator.blockStateData) { - if (dataLoadingElement) { - dataLoadingElement.classList.add('hidden') - } - - if (loadedDataElement) { - loadedDataElement.classList.remove('hidden') - } - - await this.LoadCurrentLine() - } else { - setTimeout(() => { - this.UpdateLoadedState() - }, 500) - } - } - - //Displays data - protected async LoadCurrentLine() { - let lines = this.content.split('\n') - - if (this.currentLine < lines.length) { - let fullCommand = lines[this.currentLine].substring( - 0, - lines[this.currentLine].length - 1 - ) - - if ( - lines[this.currentLine].substring( - lines[this.currentLine].length - 1 - ) != '\r' - ) { - fullCommand = lines[this.currentLine].substring( - 0, - lines[this.currentLine].length - ) - } - - let command = fullCommand.split(' ')[0] - - let lineCounterElement = document.getElementById('line-counter') - - if (lineCounterElement) { - lineCounterElement.textContent = - 'Line: ' + (this.currentLine + 1).toString() - } - - let fullCommmandDisplayElement = document.getElementById( - 'full-command-display' - ) - - if (fullCommmandDisplayElement) { - fullCommmandDisplayElement.innerHTML = - 'Full Command: ' + lines[this.currentLine] - } - - let alertsElement = document.getElementById('alerts') - let docsElement = document.getElementById('docs') - - if (alertsElement && docsElement) { - let alertCount = alertsElement.children.length - - for (let i = 0; i < alertCount; i++) { - alertsElement.children[0].remove() - } - - if ( - lines[this.currentLine] == '\r' || - lines[this.currentLine].length == 0 - ) { - docsElement.textContent = 'No documentation.' - } else { - let data = - await this.app!.languageManager.mcfunction.validator.ValidateCommand( - fullCommand - ) - - let currentErrorLines = [] - - for (let i = 0; i < data[0].length; i++) { - const start = data[0][i].start - const end = data[0][i].end - - currentErrorLines.push([start, end]) - - let translated = '' - - if (typeof data[0][i].value === 'string') { - translated = this.translateError(data[0][i].value) - } else { - for (let j = 0; j < data[0][i].value.length; j++) { - console.log(data[0][i].value[j]) - console.log(data[0][i].value[j].startsWith('$')) - - if (data[0][i].value[j].startsWith('$')) { - translated += - data[0][i].value[j].substring(1) - } else { - translated += this.translateError( - data[0][i].value[j] - ) - } - } - } - - console.log(translated) - - var ComponentClass = Vue.extend(Error) - var instance = new ComponentClass({ - propsData: { - alertText: translated, - }, - }) - - instance.$mount() - alertsElement.appendChild(instance.$el) - } - - let currentWarningLines = [] - - for (let i = 0; i < data[1].length; i++) { - const start = data[1][i].start - const end = data[1][i].end - - currentWarningLines.push([start, end]) - - let translated = '' - - if (typeof data[1][i].value === 'string') { - translated = this.translateWarning(data[1][i].value) - } else { - for (let j = 0; j < data[1][i].value.length; j++) { - if (data[1][i].value[j].startsWith('$')) { - translated += - data[1][i].value[j].substring(1) - } else { - translated += this.translateWarning( - data[1][i].value[j] - ) - } - } - } - - var ComponentClass = Vue.extend(Warning) - var instance = new ComponentClass({ - propsData: { - alertText: translated, - }, - }) - - instance.$mount() - alertsElement.appendChild(instance.$el) - } - - if (fullCommmandDisplayElement) { - if (fullCommmandDisplayElement.innerHTML) { - let writeOffset = 0 - let writeIndex = 0 - - for ( - let i = 0; - i < fullCommmandDisplayElement.innerHTML.length; - i++ - ) { - for ( - let j = 0; - j < currentErrorLines.length; - j++ - ) { - if (currentErrorLines[j][0] == writeIndex) { - fullCommmandDisplayElement.innerHTML = - 'Full Command: ' + - fullCommmandDisplayElement.innerHTML.substring( - 14, - 14 + i - ) + - '' + - fullCommmandDisplayElement.innerHTML.substring( - 14 + i, - fullCommmandDisplayElement - .innerHTML.length - ) - - i += 25 - } - - if (currentErrorLines[j][1] == writeIndex) { - fullCommmandDisplayElement.innerHTML = - 'Full Command: ' + - fullCommmandDisplayElement.innerHTML.substring( - 14, - 14 + i - ) + - '' + - fullCommmandDisplayElement.innerHTML.substring( - 14 + i, - fullCommmandDisplayElement - .innerHTML.length - ) - - i += 7 - } - } - - for ( - let j = 0; - j < currentWarningLines.length; - j++ - ) { - if ( - currentWarningLines[j][0] == writeIndex - ) { - fullCommmandDisplayElement.innerHTML = - 'Full Command: ' + - fullCommmandDisplayElement.innerHTML.substring( - 14, - 14 + i - ) + - '' + - fullCommmandDisplayElement.innerHTML.substring( - 14 + i, - fullCommmandDisplayElement - .innerHTML.length - ) - - i += 27 - } - - if ( - currentWarningLines[j][1] == writeIndex - ) { - fullCommmandDisplayElement.innerHTML = - 'Full Command: ' + - fullCommmandDisplayElement.innerHTML.substring( - 14, - 14 + i - ) + - '' + - fullCommmandDisplayElement.innerHTML.substring( - 14 + i, - fullCommmandDisplayElement - .innerHTML.length - ) - - i += 7 - } - } - - writeIndex++ - } - } - } - - docsElement.textContent = - await this.app!.languageManager.mcfunction.validator.GetDocs( - command - ) - - if (data[0].length > 0) { - return true - } - } - } - } else { - return true - } - - return false - } - - protected SlowStepLine() { - setTimeout(() => { - if (!this.stopped) { - this.currentLine += 1 - this.LoadCurrentLine().then((shouldStop) => { - if (!shouldStop && !this.stopped) { - this.SlowStepLine() - } - - this.stopped = false - }) - } - }, 0) - } - - protected async Play() { - this.stopped = false - - await this.LoadFileContent() - - this.currentLine = 0 - - let shouldStop = false - - shouldStop = await this.LoadCurrentLine() - - if (!shouldStop) { - this.SlowStepLine() - } - } - - protected async StepLine() { - this.stopped = false - - await this.LoadFileContent() - - this.currentLine += 1 - this.LoadCurrentLine() - } - - protected async Restart() { - await this.LoadFileContent() - - this.currentLine = 0 - this.LoadCurrentLine() - - this.stopped = true - } - - async onActivate() { - await super.onActivate() - - this.app = await App.getApp() - - if (!this.app.languageManager.mcfunction.validator.loadedCommandData) { - this.app.languageManager.mcfunction.validator.LoadCommandData() - } - - await this.LoadFileContent() - - await this.app.languageManager.mcfunction.validator.LoadCommandData() - - await this.UpdateLoadedState() - } - - async onDeactivate() { - this.stopped = true - } -} diff --git a/src/components/Editors/FunctionValidator/Tab.vue b/src/components/Editors/FunctionValidator/Tab.vue deleted file mode 100644 index 407e9dea4..000000000 --- a/src/components/Editors/FunctionValidator/Tab.vue +++ /dev/null @@ -1,294 +0,0 @@ - - - - - - - diff --git a/src/components/Editors/FunctionValidator/Warning.vue b/src/components/Editors/FunctionValidator/Warning.vue deleted file mode 100644 index 3f554ccfc..000000000 --- a/src/components/Editors/FunctionValidator/Warning.vue +++ /dev/null @@ -1,26 +0,0 @@ - - - - - diff --git a/src/components/Languages/LanguageManager.ts b/src/components/Languages/LanguageManager.ts index 24ceef43f..b1e0d6d33 100644 --- a/src/components/Languages/LanguageManager.ts +++ b/src/components/Languages/LanguageManager.ts @@ -4,10 +4,9 @@ import { McfunctionLanguage } from './Mcfunction' import { MoLangLanguage } from './MoLang' export class LanguageManager { - public readonly mcfunction = new McfunctionLanguage() - protected otherLanguages = new Set([ new MoLangLanguage(), new LangLanguage(), + new McfunctionLanguage(), ]) } diff --git a/src/components/Languages/Mcfunction.ts b/src/components/Languages/Mcfunction.ts index f8f3ad155..de5af96f2 100644 --- a/src/components/Languages/Mcfunction.ts +++ b/src/components/Languages/Mcfunction.ts @@ -12,7 +12,6 @@ import './Mcfunction/WithinJson' import { tokenProvider } from './Mcfunction/TokenProvider' import type { Project } from '/@/components/Projects/Project/Project' import { isWithinTargetSelector } from './Mcfunction/TargetSelector/isWithin' -import { FunctionValidator } from '/@/components/Languages/Mcfunction/Validation/Validator' import { proxy } from 'comlink' import { useMonaco } from '../../utils/libs/useMonaco' @@ -162,15 +161,14 @@ const loadCommands = async (lang: McfunctionLanguage) => { ) tokenProvider.keywords = commands.map((command) => command) - const targetSelectorArguments = await project.commandData.allSelectorArguments() + const targetSelectorArguments = + await project.commandData.allSelectorArguments() tokenProvider.targetSelectorArguments = targetSelectorArguments lang.updateTokenProvider(tokenProvider) } export class McfunctionLanguage extends Language { - public validator = new FunctionValidator() - constructor() { super({ id: 'mcfunction', diff --git a/src/components/Languages/Mcfunction/Validation/Error.ts b/src/components/Languages/Mcfunction/Validation/Error.ts deleted file mode 100644 index 800283ae5..000000000 --- a/src/components/Languages/Mcfunction/Validation/Error.ts +++ /dev/null @@ -1,7 +0,0 @@ -export class SmartError { - constructor( - public value: string | string[], - public start: number = 0, - public end: number = 0 - ) {} -} diff --git a/src/components/Languages/Mcfunction/Validation/Token.ts b/src/components/Languages/Mcfunction/Validation/Token.ts deleted file mode 100644 index 48bb96d25..000000000 --- a/src/components/Languages/Mcfunction/Validation/Token.ts +++ /dev/null @@ -1,8 +0,0 @@ -export class Token { - constructor( - public value: string, - public type: string, - public start: number = 0, - public end: number = 0 - ) {} -} diff --git a/src/components/Languages/Mcfunction/Validation/Validator.ts b/src/components/Languages/Mcfunction/Validation/Validator.ts deleted file mode 100644 index 11818a14e..000000000 --- a/src/components/Languages/Mcfunction/Validation/Validator.ts +++ /dev/null @@ -1,1786 +0,0 @@ -import { SmartError } from './Error' -import { SmartWarning } from './Warning' -import { RefSchema } from '/@/components/JSONSchema/Schema/Ref' -import { Token } from './Token' -import { BedrockProject } from '/@/components/Projects/Project/BedrockProject' -import { markRaw } from 'vue' -import { SchemaManager } from '/@/components/JSONSchema/Manager' -import { App } from '/@/App' - -export class FunctionValidator { - protected validCommands: Array = [] - protected validSelectorArgs: Array = [] - protected commandData: any | undefined - protected generalCommandData: any | undefined - protected generalSelectorArgsData: any | undefined - - public loadedCommandData = false - - public blockStateData: any | undefined - - constructor() {} - - protected DoesStringArrayMatchArray( - array1: Array, - array2: Array - ) { - if (array1.length != array2.length) { - return false - } - - for (let i = 0; i < array1.length; i++) { - if (array1[i] != array2[i]) { - return false - } - } - - return true - } - - protected LateLoadData() { - setTimeout(() => { - if (!this.blockStateData) { - let blockStateDataLoaded = markRaw( - SchemaManager.request( - 'file:///data/packages/minecraftBedrock/schema/general/vanilla/blockState.json' - ) - ) - - if (blockStateDataLoaded.type) { - this.blockStateData = blockStateDataLoaded - } else { - this.LateLoadData() - } - } - }, 1000) - } - - public async LoadCommandData() { - this.loadedCommandData = true - - const app = await App.getApp() - const project = await app.project - - if (project instanceof BedrockProject) { - this.commandData = project.commandData - } - - this.validCommands = [] - this.validSelectorArgs = [] - - this.generalCommandData = [] - this.generalSelectorArgsData = [] - - let foundTypes: string[] = [] - - if (this.commandData) { - for (let v = 0; v < this.commandData._data.vanilla.length; v++) { - const vanilla = this.commandData._data.vanilla[v] - - for (let i = 0; i < vanilla.commands.length; i++) { - if ( - !this.validCommands.includes( - vanilla.commands[i].commandName - ) - ) { - this.validCommands.push(vanilla.commands[i].commandName) - } - - this.generalCommandData.push(markRaw(vanilla.commands[i])) - } - - if (vanilla.selectorArguments) { - for (let i = 0; i < vanilla.selectorArguments.length; i++) { - if ( - !this.validSelectorArgs.includes( - vanilla.selectorArguments[i].argumentName - ) - ) { - this.validSelectorArgs.push( - vanilla.selectorArguments[i].argumentName - ) - } - - this.generalSelectorArgsData.push( - markRaw(vanilla.selectorArguments[i]) - ) - } - } - } - } else { - console.error('Unable to load commands.json') - } - - let blockStateDataLoaded = markRaw( - SchemaManager.request( - 'file:///data/packages/minecraftBedrock/schema/general/vanilla/blockState.json' - ) - ) - - if (blockStateDataLoaded.type) { - this.blockStateData = blockStateDataLoaded - } else { - this.LateLoadData() - } - } - - public async GetDocs(command: string = '') { - if (this.commandData) { - for (let i = 0; i < this.generalCommandData.length; i++) { - if (this.generalCommandData[i].commandName == command) { - return this.generalCommandData[i].description - } - } - } else { - console.error('Unable to load commands.json') - } - - return 'Unable to get docs for this command!' - } - - protected isInt(n: string) { - return /^-?\d+$/.test(n) - } - - protected isFloat(n: string) { - return /^\d+\.\d+$/.test(n) - } - - specialSymbols = [ - '[', - ']', - '!', - '=', - '@', - '~', - '^', - '!', - ',', - '{', - '}', - '.', - ':', - ] - - whiteSpace = [' ', '\t', '\n', '\r'] - - selectorTargets = ['a', 's', 'p', 'r', 'e'] - - protected ParseDirty(dirtyString: Token) { - let readStart = 0 - let readEnd = dirtyString.value.length - - let foundTokens: Token[] = [] - - let lastUnexpected = -1 - - while (readStart < dirtyString.value.length) { - let found = false - let added = true - let shouldCombine = false - - while (readEnd > readStart && !found) { - let read = dirtyString.value.substring(readStart, readEnd) - - if (this.specialSymbols.includes(read)) { - foundTokens.push( - new Token(read, 'Symbol', readStart, readEnd) - ) - found = true - } else if (this.isInt(read)) { - foundTokens.push( - new Token(read, 'Integer', readStart, readEnd) - ) - found = true - shouldCombine = true - } else if (this.isFloat(read)) { - foundTokens.push( - new Token(read, 'Float', readStart, readEnd) - ) - found = true - } else if (read == 'true' || read == 'false') { - foundTokens.push( - new Token(read, 'Boolean', readStart, readEnd) - ) - found = true - } else if (read == ' ') { - foundTokens.push( - new Token(read, 'Space', readStart, readEnd) - ) - found = true - } - - readEnd-- - } - - if (found) { - if (lastUnexpected != -1) { - if (added) { - if (shouldCombine) { - let whatToAdd = - foundTokens[foundTokens.length - 1].value - - foundTokens.splice( - foundTokens.length - 1, - 1, - new Token( - dirtyString.value.substring( - lastUnexpected, - readStart - ) + whatToAdd, - 'String', - lastUnexpected, - readStart + whatToAdd.length - ) - ) - - if (foundTokens.length - 3 >= 0) { - if ( - foundTokens[foundTokens.length - 3].type == - 'Integer' - ) { - foundTokens.splice( - foundTokens.length - 3, - 2, - new Token( - foundTokens[foundTokens.length - 3] - .value + - foundTokens[ - foundTokens.length - 2 - ].value, - 'String', - foundTokens[ - foundTokens.length - 3 - ].start, - foundTokens[ - foundTokens.length - 2 - ].end - ) - ) - } - } - } else { - foundTokens.splice( - foundTokens.length - 1, - 0, - new Token( - dirtyString.value.substring( - lastUnexpected, - readStart - ), - 'String', - lastUnexpected, - readStart - ) - ) - - if (foundTokens.length - 3 >= 0) { - if ( - foundTokens[foundTokens.length - 3].type == - 'Integer' - ) { - foundTokens.splice( - foundTokens.length - 3, - 2, - new Token( - foundTokens[foundTokens.length - 3] - .value + - foundTokens[ - foundTokens.length - 2 - ].value, - 'String', - foundTokens[ - foundTokens.length - 3 - ].start, - foundTokens[ - foundTokens.length - 2 - ].end - ) - ) - } - } - } - } else { - foundTokens.push( - new Token( - dirtyString.value.substring( - lastUnexpected, - readEnd - ), - 'String', - lastUnexpected, - readStart - ) - ) - - if (foundTokens.length - 3 >= 0) { - if ( - foundTokens[foundTokens.length - 3].type == - 'Integer' - ) { - foundTokens.splice( - foundTokens.length - 3, - 2, - new Token( - foundTokens[foundTokens.length - 3] - .value + - foundTokens[foundTokens.length - 2] - .value, - 'String', - foundTokens[ - foundTokens.length - 3 - ].start, - foundTokens[foundTokens.length - 2].end - ) - ) - } - } - } - - lastUnexpected = -1 - } - } else { - if (lastUnexpected == -1) { - lastUnexpected = readStart - } - } - - readStart = readEnd + 1 - readEnd = dirtyString.value.length - } - - if (lastUnexpected != -1) { - foundTokens.push( - new Token( - dirtyString.value.substring(lastUnexpected, readEnd), - 'String', - lastUnexpected, - readEnd - ) - ) - - if (foundTokens.length - 3 >= 0) { - if (foundTokens[foundTokens.length - 3].type == 'Integer') { - foundTokens.splice( - foundTokens.length - 3, - 2, - new Token( - foundTokens[foundTokens.length - 3].value + - foundTokens[foundTokens.length - 2].value, - 'String', - foundTokens[foundTokens.length - 3].start, - foundTokens[foundTokens.length - 2].end - ) - ) - } - } - - lastUnexpected = -1 - } - - return foundTokens - } - - protected MatchTypes(currentType: string, targetType: string) { - if (currentType == targetType) { - return true - } - - switch (targetType) { - case 'string': - if (currentType == 'String') { - return true - } - break - case 'number': - if (currentType == 'Integer') { - return true - } - break - case 'selector': - if ( - currentType == 'Selector' || - currentType == 'String' || - currentType == 'Long String' || - currentType == 'Complex Selector' - ) { - return true - } - break - case 'coordinate': - if (currentType == 'Integer' || currentType == 'Position') { - return true - } - break - case 'boolean': - if (currentType == 'Boolean') { - return true - } - break - case 'jsonData': - if (currentType == 'JSON') { - return true - } - break - case 'scoreData': - if (currentType == 'Score Data') { - return true - } - break - case 'Score': - if (currentType == 'Range' || currentType == 'Integer') { - return true - } - break - case 'blockState': - if (currentType == 'Block State') { - return true - } - break - } - - return false - } - - protected ValidateSelector( - tokens: Token[], - start: number, - end: number - ): any { - let errors: SmartError[] = [] - let warnings: SmartError[] = [] - - if (tokens.length == 0) { - errors.push(new SmartError('selectors.emptyComplex', start, end)) - - return [errors, warnings] - } - - let confirmedAtributes: string[] = [] - - for (let i = 0; i < tokens.length / 4; i++) { - const offset = i * 4 - - if (tokens[offset].type != 'String') { - errors.push( - new SmartError( - 'selectors.expectedStringAsAttribute', - tokens[offset].start, - tokens[offset].end - ) - ) - - return [errors, warnings] - } - - let targetAtribute = tokens[offset].value - let found = false - let argData = null - - for (let j = 0; j < this.generalSelectorArgsData.length; j++) { - const selectorArgument = this.generalSelectorArgsData[j] - - if (selectorArgument.argumentName == targetAtribute) { - argData = selectorArgument - found = true - break - } - } - - if (!found) { - errors.push( - new SmartError( - [ - 'selectors.invalidSelectorAttribute.part1', - '$' + targetAtribute, - 'selectors.invalidSelectorAttribute.part2', - ], - tokens[offset].start, - tokens[offset].end - ) - ) - - return [errors, warnings] - } - - if (1 + offset >= tokens.length) { - errors.push( - new SmartError( - 'common.expectedEquals', - tokens[offset].start, - tokens[offset].end - ) - ) - - return [errors, warnings] - } - - if ( - !(tokens[1 + offset].value == '=' && tokens[1].type == 'Symbol') - ) { - errors.push( - new SmartError( - 'common.expectedEquals', - tokens[offset + 1].start, - tokens[offset + 1].end - ) - ) - - return [errors, warnings] - } - - if (2 + offset >= tokens.length) { - errors.push( - new SmartError( - 'common.expectedValue', - tokens[offset + 1].start, - tokens[offset + 1].end - ) - ) - - return [errors, warnings] - } - - let negated = false - let value = tokens[2 + offset] - - if ( - tokens[2 + offset].value == '!' && - tokens[2 + offset].type == 'Symbol' - ) { - value = tokens[3 + offset] - negated = true - } - - if (argData.additionalData) { - if (!argData.additionalData.supportsNegation && negated) { - errors.push( - new SmartError( - [ - 'selectors.unsupportedNegation.part1', - '$' + targetAtribute, - 'selectors.unsupportedNegation.part2', - ], - tokens[2 + offset].start, - value.end - ) - ) - - return [errors, warnings] - } - - if ( - argData.additionalData.multipleInstancesAllowed == - 'never' && - confirmedAtributes.includes(targetAtribute) - ) { - errors.push( - new SmartError( - [ - 'selectors.multipleInstancesNever.part1', - '$' + targetAtribute, - 'selectors.multipleInstancesNever.part2', - ], - tokens[offset].start, - value.end - ) - ) - - return [errors, warnings] - } - - if ( - argData.additionalData.multipleInstancesAllowed == - 'whenNegated' && - !negated && - confirmedAtributes.includes(targetAtribute) - ) { - errors.push( - new SmartError( - [ - 'selectors.multipleInstancesNegated.part1', - '$' + targetAtribute, - 'selectors.multipleInstancesNegated.part1', - ], - tokens[offset].start, - value.end - ) - ) - - return [errors, warnings] - } - - let targetType = argData.type - - if (targetAtribute == 'name') { - targetType = 'selector' - } - - if (!this.MatchTypes(value.type, targetType)) { - errors.push( - new SmartError( - [ - 'common.expectedType.part1', - '$' + targetType, - 'common.expectedType.part2', - '$' + value.type, - 'common.expectedType.part3', - ], - value.start, - value.end - ) - ) - - return [errors, warnings] - } - - if (argData.additionalData.values) { - if (!argData.additionalData.values.includes(value.value)) { - errors.push( - new SmartError( - [ - 'selectors.valueNotValid.part1', - '$' + value.value, - 'selectors.valueNotValid.part2', - ], - value.start, - value.end - ) - ) - - return [errors, warnings] - } - } - - if (argData.additionalData.schemaReference) { - let referencePath = argData.additionalData.schemaReference - - let refSchema = new RefSchema( - referencePath, - '$ref', - referencePath - ) - - let schemaReference = refSchema.getCompletionItems({}) - - let foundSchema = false - - for (let j = 0; j < schemaReference.length; j++) { - if (schemaReference[j].value == value.value) { - foundSchema = true - } - } - - if (!foundSchema) { - //Warning maybe from wrong addon - if (targetAtribute == 'family') { - warnings.push( - new SmartWarning( - [ - 'schema.familyNotFound.part1', - '$' + value.value, - 'schema.familyNotFound.part2', - ], - value.start, - value.end - ) - ) - } else if (targetAtribute == 'type') { - warnings.push( - new SmartWarning( - [ - 'schema.typeNotFound.part1', - '$' + value.value, - 'schema.typeNotFound.part2', - ], - value.start, - value.end - ) - ) - } else if (targetAtribute == 'tag') { - warnings.push( - new SmartWarning( - [ - 'schema.tagNotFound.part1', - '$' + value.value, - 'schema.tagNotFound.part2', - ], - value.start, - value.end - ) - ) - } else { - warnings.push( - new SmartWarning( - [ - 'schema.schemaValueNotFound.part1', - '$' + value.value, - 'schema.schemaValueNotFound.part2', - ], - value.start, - value.end - ) - ) - } - } - } - } - - let possibleCommaPos = 3 - - if (negated) { - possibleCommaPos = 4 - } - - if (possibleCommaPos + offset < tokens.length) { - if ( - !( - tokens[possibleCommaPos + offset].value == ',' && - tokens[possibleCommaPos + offset].type == 'Symbol' - ) - ) { - errors.push( - new SmartError( - 'common.expectedComma', - tokens[possibleCommaPos + offset].start, - tokens[possibleCommaPos + offset].end - ) - ) - return [errors, warnings] - } - } - } - - return [errors, warnings] - } - - protected ValidateJSON(tokens: Token[]) { - let errors: string[] = [] - let warnings: SmartError[] = [] - - let reconstructedJSON = '{' - - for (let i = 0; i < tokens.length; i++) { - if (tokens[i].type == 'Long String') { - reconstructedJSON += '"' + tokens[i].value + '"' - } else { - reconstructedJSON += tokens[i].value - } - } - - reconstructedJSON += '}' - - try { - JSON.parse(reconstructedJSON) - } catch (e) { - errors.push('jsonNotValid') - } - - return [errors, warnings] - } - - protected ValidateScoreData( - tokens: Token[], - start: number, - end: number - ): any { - let errors: SmartError[] = [] - let warnings: SmartError[] = [] - - if (tokens.length == 0) { - //Unexpected empty score data - errors.push(new SmartError('scoreData.empty', start, end)) - return [errors, warnings] - } - - let confirmedValues: string[] = [] - - for (let i = 0; i < tokens.length / 4; i++) { - const offset = i * 4 - - let targetValue = tokens[offset].value - - if (tokens[offset].type != 'String') { - errors.push( - new SmartError( - 'scoreData.expectedStringAsAttribute', - tokens[offset].start, - tokens[offset].end - ) - ) - - return [errors, warnings] - } - - if (1 + offset >= tokens.length) { - errors.push( - new SmartError( - 'common.expectedEquals', - tokens[offset].start, - tokens[offset].end - ) - ) - - return [errors, warnings] - } - - if ( - !(tokens[1 + offset].value == '=' && tokens[1].type == 'Symbol') - ) { - errors.push( - new SmartError( - 'common.expectedEquals', - tokens[offset + 1].start, - tokens[offset + 1].end - ) - ) - - return [errors, warnings] - } - - if (2 + offset >= tokens.length) { - errors.push( - new SmartError( - 'common.expectedValue', - tokens[offset + 1].start, - tokens[offset + 1].end - ) - ) - - return [errors, warnings] - } - - let value = tokens[2 + offset] - - if (!this.MatchTypes(value.type, 'Score')) { - errors.push( - new SmartError( - [ - 'scoreData.invalidType.part1', - '$' + value.type, - 'scoreData.invalidType.part2', - ], - tokens[offset + 2].start, - tokens[offset + 2].end - ) - ) - - return [errors, warnings] - } - - if (confirmedValues.includes(value.value)) { - errors.push( - new SmartError( - 'scoreData.repeat', - tokens[offset + 2].start, - tokens[offset + 2].end - ) - ) - - return [errors, warnings] - } - - if (3 + offset < tokens.length) { - if ( - !( - tokens[3 + offset].value == ',' && - tokens[3 + offset].type == 'Symbol' - ) - ) { - errors.push( - new SmartError( - 'common.expectedComa', - tokens[offset + 3].start, - tokens[offset + 3].end - ) - ) - - return [errors, warnings] - } - } - - confirmedValues.push(targetValue) - } - - return [errors, warnings] - } - - protected ValidateBlockState(tokens: Token[]) { - let errors: string[] = [] - let warnings: SmartError[] = [] - - if (tokens.length == 0) { - //Empty Block States Are Supported! - return [errors, warnings] - } - - let confirmedValues: string[] = [] - - for (let i = 0; i < tokens.length / 4; i++) { - const offset = i * 4 - - let targetValue = tokens[0 + offset].value - - if (tokens[0 + offset].type != 'Long String') { - errors.push('blockStateExpectedLongStringAsValue') - return [errors, warnings] - } - - if (1 + offset >= tokens.length) { - //Error expected ':' - errors.push('expectedColonButNothing') - return [errors, warnings] - } - - if ( - !(tokens[1 + offset].value == ':' && tokens[1].type == 'Symbol') - ) { - //Error expected ':' - errors.push('expectedColon') - return [errors, warnings] - } - - if (2 + offset >= tokens.length) { - //Error expected value - errors.push('expectedValueButNothing') - return [errors, warnings] - } - - let value = tokens[2 + offset] - - if (this.blockStateData) { - let targetData - - let properties = Object.getOwnPropertyNames( - this.blockStateData.properties - ) - - for (let j = 0; j < properties.length; j++) { - if (properties[j] == targetValue) { - targetData = this.blockStateData.properties[ - properties[j] - ] - - targetData['name'] = properties[j] - break - } - } - - let targetValues = targetData.enum - - for (let j = 0; j < targetValues.length; j++) { - targetValues[j] = targetValues[j].toString() - } - - if (!targetValues.includes(value.value)) { - errors.push( - 'invalidBlockStateValue.part1' + - value.value + - 'invalidBlockStateValue.part2' - ) - return [errors, warnings] - } - } - - if (confirmedValues.includes(value.value)) { - errors.push('repeatOfBlockState') - return [errors, warnings] - } - - if (3 + offset < tokens.length) { - if ( - tokens[3 + offset].value == ',' && - tokens[3 + offset].type == 'Symbol' - ) { - //Error expected ',' - errors.push('expectedComa') - return [errors, warnings] - } - } - - confirmedValues.push(targetValue) - } - - return [errors, warnings] - } - - public ValidateCommand( - command: string | null, - commandTokens: Token[] | null = null - ) { - let errors: SmartError[] = [] - let warnings: any[] = [] - - if (!this.blockStateData) { - warnings.push(new SmartWarning('data.missingData', 0, 0)) - } - - //Seperate into strings by quotes for parsing - - let splitStrings - - let inString = false - - let tokens: Token[] = [] - - let baseCommand: Token - - if (!commandTokens) { - if (command?.startsWith('/')) command = command.substring(1) - - splitStrings = command!.split('"') - - let lastChange = -1 - - for (let i = 0; i < splitStrings.length; i++) { - if (!inString) { - let tokenStart = 0 - - if (tokens.length > 0) { - tokenStart = tokens[tokens.length - 1].end + 1 - } - - tokens.push( - new Token( - splitStrings[i], - 'Dirty', - tokenStart, - tokenStart + splitStrings[i].length - ) - ) - } else { - let tokenStart = 0 - - if (tokens.length > 0) { - tokenStart = tokens[tokens.length - 1].end + 1 - } - - tokens.push( - new Token( - splitStrings[i], - 'Long String', - tokenStart, - tokenStart + splitStrings[i].length - ) - ) - } - - inString = !inString - lastChange = i - } - - if (!inString) { - errors.push( - new SmartError( - 'common.unclosedString', - tokens[lastChange].start, - tokens[tokens.length - 1].end - ) - ) - return [errors, warnings] - } - - //Tokenize strings for validation - - for (let i = 0; i < tokens.length; i++) { - if (tokens[i].type == 'Dirty') { - let parsedTokens: Token[] = this.ParseDirty(tokens[i]) - - for (let j = 0; j < parsedTokens.length; j++) { - tokens.splice(i + j + 1, 0, parsedTokens[j]) - } - - tokens.splice(i, 1) - - i += parsedTokens.length - 1 - } - } - - if (tokens.length > 0) { - if (tokens[0].type == 'Space') { - errors.push( - new SmartError( - 'common.spaceAtStart', - tokens[lastChange].start, - tokens[lastChange].end - ) - ) - - return [errors, warnings] - } - - if (tokens.length == 0) { - errors.push(new SmartError('commands.empty')) - - return [errors, warnings] - } - - baseCommand = tokens[0] - - //Test for basic command - baseCommand = tokens.shift()! - - if (!this.validCommands.includes(baseCommand.value)) { - errors.push( - new SmartError( - [ - 'command.invalid.part1', - '$' + baseCommand.value, - 'command.invalid.part2', - ], - baseCommand.start, - baseCommand.end - ) - ) - - return [errors, warnings] - } - - //Construct Identifiers - for (let i = 0; i < tokens.length; i++) { - const token = tokens[i] - - if (token.type == 'Symbol' && token.value == ':') { - if (i + 1 >= tokens.length) { - errors.push( - new SmartError( - 'common.exptectedColon', - token.start, - token.end - ) - ) - - return [errors, warnings] - } - - if (i - 1 < 0) { - errors.push( - new SmartError( - 'identifiers.missingNamespace', - token.start, - token.end - ) - ) - - return [errors, warnings] - } - - if (tokens[i - 1].type == 'Space') { - errors.push( - new SmartError( - 'identifiers.missingNamespace', - token.start, - token.end - ) - ) - - return [errors, warnings] - } - - let firstValue = tokens[i - 1] - let secondValue = tokens[i + 1] - - if ( - firstValue.type == 'String' && - secondValue.type == 'String' - ) { - tokens[i - 1] = new Token( - firstValue.value + ':' + secondValue.value, - 'String' - ) - - tokens.splice(i, 2) - } - } - } - - //Remove Spaces - for (let i = 0; i < tokens.length; i++) { - const token = tokens[i] - - if (token.type == 'Space') { - tokens.splice(i, 1) - i-- - } - } - - //Construct Positions - for (let i = 0; i < tokens.length; i++) { - const token = tokens[i] - - if ( - token.type == 'Symbol' && - (token.value == '~' || token.value == '^') - ) { - if (i + 1 < tokens.length) { - let numberTarget = tokens[i + 1] - - if (numberTarget.type == 'Integer') { - tokens[i] = new Token( - token.value + numberTarget.value, - 'Position' - ) - - tokens.splice(i + 1, 1) - } else { - tokens[i] = new Token(token.value, 'Position') - } - } - - tokens[i] = new Token(token.value, 'Position') - } - } - - //Construct json - let inJSON = false - let JSONToReconstruct: Token[] = [] - - for (let i = 0; i < tokens.length; i++) { - const token = tokens[i] - - if (token.type == 'Symbol' && token.value == '{') { - if (inJSON) { - errors.push( - new SmartError( - 'common.unexpectedOpenBracket', - token.start, - token.end - ) - ) - - return [errors, warnings] - } else { - inJSON = true - } - } else if (token.type == 'Symbol' && token.value == '}') { - if (inJSON) { - inJSON = false - - let result = this.ValidateJSON(JSONToReconstruct) - - if (result[0].length == 0) { - let startingPoint = - i - JSONToReconstruct.length - 1 - - tokens.splice( - startingPoint, - JSONToReconstruct.length + 2, - (tokens[startingPoint] = new Token( - tokens[startingPoint].value, - 'JSON' - )) - ) - } - - JSONToReconstruct = [] - } else { - errors.push( - new SmartError( - 'common.unexpectedClosedCurlyBracket', - token.start, - token.end - ) - ) - return [errors, warnings] - } - } else { - if (inJSON) { - JSONToReconstruct.push(token) - } - } - } - - //Construct ranges - for (let i = 0; i < tokens.length; i++) { - const token = tokens[i] - - if (token.type == 'Symbol' && token.value == '.') { - if (i + -1 < 0) { - errors.push( - new SmartError( - 'ranges.missingFirstNumber', - token.start, - token.end - ) - ) - - return [errors, warnings] - } - - if (i + 1 >= tokens.length) { - errors.push( - new SmartError( - 'ranges.missingDot', - tokens[i - 1].start, - token.end - ) - ) - - return [errors, warnings] - } - - if (i + 2 >= tokens.length) { - errors.push( - new SmartError( - 'ranges.missingSecondNumber', - tokens[i - 1].start, - tokens[i + 1].end - ) - ) - - return [errors, warnings] - } - - let firstNum = tokens[i - 1] - let secondNum = tokens[i + 2] - let dot = tokens[i + 1] - - if (firstNum.type != 'Integer') { - errors.push( - new SmartError( - 'ranges.missingFirstNumber', - token.start, - token.end - ) - ) - - return [errors, warnings] - } - - if (!(dot.value == '.' && dot.type == 'Symbol')) { - errors.push( - new SmartError( - 'ranges.missingDot', - tokens[i - 1].start, - token.end - ) - ) - - return [errors, warnings] - } - - if (secondNum.type != 'Integer') { - errors.push( - new SmartError( - 'ranges.missingSecondNumber', - tokens[i - 1].start, - tokens[i + 1].end - ) - ) - - return [errors, warnings] - } - - tokens[i - 1] = new Token( - firstNum.value + ' ' + secondNum.value, - 'Range' - ) - - tokens.splice(i, 3) - } - } - - //Construct Score Data - let inScoreData = false - let scoreDataToReconstruct: Token[] = [] - - let startBracketPos = 0 - let endBracketPos = 0 - - for (let i = 0; i < tokens.length; i++) { - const token = tokens[i] - - if (token.type == 'Symbol' && token.value == '{') { - if (inScoreData) { - errors.push( - new SmartError( - 'common.unexpectedOpenCurlyBracket', - token.start, - token.end - ) - ) - - return [errors, warnings] - } else { - inScoreData = true - - startBracketPos = token.start - } - } else if (token.type == 'Symbol' && token.value == '}') { - if (inScoreData) { - inScoreData = false - - endBracketPos = token.end - - let result = this.ValidateScoreData( - scoreDataToReconstruct, - startBracketPos, - endBracketPos - ) - - errors = errors.concat(result[0]) - warnings = warnings.concat(result[1]) - - if (errors.length > 0) { - return [errors, warnings] - } - - let startingPoint = - i - scoreDataToReconstruct.length - 1 - - tokens.splice( - startingPoint, - scoreDataToReconstruct.length + 2, - (tokens[startingPoint] = new Token( - tokens[startingPoint].value, - 'Score Data' - )) - ) - - scoreDataToReconstruct = [] - } else { - errors.push( - new SmartError( - 'common.unexpectedClosedCurlyBracket', - token.start, - token.end - ) - ) - - return [errors, warnings] - } - } else { - if (inScoreData) { - scoreDataToReconstruct.push(token) - } - } - } - - //Construct Basic Selectors - for (let i = 0; i < tokens.length; i++) { - const token = tokens[i] - - if (token.type == 'Symbol' && token.value == '@') { - if (i + 1 >= tokens.length) { - errors.push( - new SmartError( - 'selectors.expectedLetterAfterAt', - token.start, - token.end - ) - ) - - return [errors, warnings] - } - - let selectorTarget = tokens[i + 1] - - if (selectorTarget.type != 'String') { - errors.push( - new SmartError( - 'selectors.expectedLetterAfterAt', - token.start, - token.end - ) - ) - - return [errors, warnings] - } - - if ( - !this.selectorTargets.includes(selectorTarget.value) - ) { - errors.push( - new SmartError( - [ - 'selectors.invalid.part1', - '$' + selectorTarget.value, - 'iselectors.invalid.part2', - ], - token.start, - tokens[i + 1].end - ) - ) - - return [errors, warnings] - } - - tokens[i] = new Token( - '@' + selectorTarget.value, - 'Selector' - ) - - tokens.splice(i + 1, 1) - } - } - - //Construct Block States - let inBlockState = false - let blockStateToReconstruct: Token[] = [] - - for (let i = 0; i < tokens.length; i++) { - const token = tokens[i] - - if (token.type == 'Symbol' && token.value == '[') { - if (inBlockState) { - errors.push( - new SmartError( - 'common.unexpectedOpenSquareBracket', - token.start, - token.end - ) - ) - - return [errors, warnings] - } else { - inBlockState = true - } - } else if (token.type == 'Symbol' && token.value == ']') { - if (inBlockState) { - inBlockState = false - - let result = this.ValidateBlockState( - blockStateToReconstruct - ) - - warnings = warnings.concat(result[1]) - - if (result[0].length == 0) { - let startingPoint = - i - blockStateToReconstruct.length - 1 - - tokens.splice( - startingPoint, - blockStateToReconstruct.length + 2, - (tokens[startingPoint] = new Token( - tokens[startingPoint].value, - 'Block State' - )) - ) - } - - blockStateToReconstruct = [] - } else { - errors.push( - new SmartError( - 'common.unexpectedClosedSquareBracket', - token.start, - token.end - ) - ) - - return [errors, warnings] - } - } else { - if (inBlockState) { - blockStateToReconstruct.push(token) - } - } - } - - //Construct Complex Selectors - let inSelector = false - let selectorToReconstruct: Token[] = [] - - startBracketPos = 0 - endBracketPos = 0 - - for (let i = 0; i < tokens.length; i++) { - const token = tokens[i] - - if (token.type == 'Symbol' && token.value == '[') { - if (inSelector) { - errors.push( - new SmartError( - 'common.unexpectedOpenSquareBracket', - token.start, - token.end - ) - ) - - return [errors, warnings] - } else { - if (i - 1 < 0) { - errors.push( - new SmartError( - 'identifiers.selectorNotBeforeOpenSquareBracket', - token.start, - token.end - ) - ) - - return [errors, warnings] - } - - if (tokens[i - 1].type != 'Selector') { - errors.push( - new SmartError( - 'identifiers.selectorNotBeforeOpenSquareBracket', - token.start, - token.end - ) - ) - - return [errors, warnings] - } - - inSelector = true - - startBracketPos = token.start - } - } else if (token.type == 'Symbol' && token.value == ']') { - if (inSelector) { - inSelector = false - endBracketPos = token.end - - let result = this.ValidateSelector( - selectorToReconstruct, - startBracketPos, - endBracketPos - ) - - errors = errors.concat(result[0]) - warnings = warnings.concat(result[1]) - - if (errors.length > 0) { - return [errors, warnings] - } - - let startingPoint = - i - selectorToReconstruct.length - 2 - - tokens.splice( - startingPoint, - selectorToReconstruct.length + 3, - (tokens[startingPoint] = new Token( - tokens[startingPoint].value, - 'Complex Selector' - )) - ) - - selectorToReconstruct = [] - } else { - errors.push( - new SmartError( - 'common.unexpectedClosedSquareBracket', - token.start, - token.end - ) - ) - - return [errors, warnings] - } - } else { - if (inSelector) { - selectorToReconstruct.push(token) - } - } - } - } - } else { - tokens = commandTokens! - - baseCommand = tokens.shift()! - } - - //Validate Command Argument Types - let possibleCommandVariations = [] - - for (let i = 0; i < this.generalCommandData.length; i++) { - if (this.generalCommandData[i].commandName == baseCommand!.value) { - possibleCommandVariations.push( - this.generalCommandData[i].arguments - ) - } - } - - let lastValidVariations: Token[] = Array.from(possibleCommandVariations) - - for (let i = 0; i < tokens.length; i++) { - const arg = tokens[i] - - for (let j = 0; j < possibleCommandVariations.length; j++) { - const variation = possibleCommandVariations[j] - - if (variation.length <= i) { - if (variation[variation.length - 1].type != 'command') { - possibleCommandVariations.splice(j, 1) - j-- - continue - } - } else { - if (variation[i].type == 'command') { - let commandSlicedTokens = tokens.slice(i, tokens.length) - - let result = this.ValidateCommand( - null, - commandSlicedTokens - ) - - if (result[0].length > 0) { - possibleCommandVariations.splice(j, 1) - j-- - } - } else { - if (!this.MatchTypes(arg.type, variation[i].type)) { - possibleCommandVariations.splice(j, 1) - j-- - } - } - } - } - - if (possibleCommandVariations.length == 0) { - errors.push( - new SmartError( - [ - 'arguments.noneValid.part1', - '$' + (i + 1), - 'arguments.noneValid.part2', - '$' + arg.type, - 'arguments.noneValid.part3', - ], - arg.start, - arg.end - ) - ) - - return [errors, warnings] - } - - lastValidVariations = Array.from(possibleCommandVariations) - } - - //Check for missing and optional parameters - for (let j = 0; j < possibleCommandVariations.length; j++) { - const variation = possibleCommandVariations[j] - - if (tokens.length < variation.length) { - if (!variation[tokens.length].isOptional) { - possibleCommandVariations.splice(j, 1) - j-- - } - - continue - } - } - - if (possibleCommandVariations.length == 0) { - errors.push( - new SmartError( - [ - 'arguments.noneValidEnd.part1', - '$' + tokens.length, - 'arguments.noneValidEnd.part2', - ], - tokens[tokens.length - 1].start, - tokens[tokens.length - 1].end - ) - ) - - return [errors, warnings] - } - - return [errors, warnings] - } -} diff --git a/src/components/Languages/Mcfunction/Validation/Warning.ts b/src/components/Languages/Mcfunction/Validation/Warning.ts deleted file mode 100644 index 7a55076f8..000000000 --- a/src/components/Languages/Mcfunction/Validation/Warning.ts +++ /dev/null @@ -1,7 +0,0 @@ -export class SmartWarning { - constructor( - public value: string | string[], - public start: number, - public end: number - ) {} -} diff --git a/src/components/Projects/Project/BedrockProject.ts b/src/components/Projects/Project/BedrockProject.ts index c22047143..39e936637 100644 --- a/src/components/Projects/Project/BedrockProject.ts +++ b/src/components/Projects/Project/BedrockProject.ts @@ -5,7 +5,6 @@ import { createFromGeometry } from '/@/components/Editors/EntityModel/create/fro import { createFromClientEntity } from '/@/components/Editors/EntityModel/create/fromClientEntity' import { createFromEntity } from '/@/components/Editors/EntityModel/create/fromEntity' import { ParticlePreviewTab } from '/@/components/Editors/ParticlePreview/ParticlePreview' -// import { FunctionValidatorTab } from '../../Editors/FunctionValidator/Tab' import { BlockModelTab } from '/@/components/Editors/BlockModel/Tab' import { CommandData } from '/@/components/Languages/Mcfunction/Data' // import { WorldTab } from '/@/components/BedrockWorlds/Render/Tab' diff --git a/src/locales/en.json b/src/locales/en.json index f14703746..ede18764a 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1096,116 +1096,5 @@ "forceValue": "Force Value", "edit": "Edit" } - }, - "functionValidator": { - "actionName": "Validate Function", - "tabName": "Function Validator", - "errors": { - "common": { - "expectedEquals": "Expected equals!", - "expectedValue": "Expected value!", - "expectedType": { - "part1": "Expected type '", - "part2": "' but got type of '", - "part3": "'!" - }, - "expectedComma": "Expected comma!", - "unclosedString": "Unclosed string!", - "spaceAtStart": "Spaces at the start are not supported!", - "expectedColon": "Expected colon!", - "unexpectedOpenCurlyBracket": "Unexpected open curly bracket!", - "unexpectedCloseCurlyBracket": "Unexpected close curly bracket!", - "unexpectedOpenSquareBracket": "Unexpected open square bracket!", - "unexpectedCloseSquareBracket": "Unexpected close square bracket!" - }, - "command": { - "empty": "Empty commands are not supported!", - "invalid": { - "part1": "Command: '", - "part2": "' is not a valid command!" - } - }, - "identifiers": { - "missingNamespace": "Missing namespace in identifier!" - }, - "ranges": { - "missingFirstNumber": "Missing first number in range!", - "missingDot": "Missing dot in range!", - "missingSecondNumber": "Missing second number in range!" - }, - "selectors": { - "emptyComplex": "Unexpected empty complex selector!", - "expectedStringAsAttribute": "Selector attribute must be a string!", - "invalidSelectorAttribute": { - "part1": "'", - "part2": "' is not a valid selector attribute!" - }, - "unsupportedNegation": { - "part1": "Selector attribute: '", - "part2": "' does not support negation!" - }, - "multipleInstancesNever": { - "part1": "Selector attribute: '", - "part2": "' does not support multiple instances!" - }, - "multipleInstancesNegated": { - "part1": "Selector attribute: '", - "part2": "' only supports multiple instances when negated!" - }, - "valueNotValid": { - "part1": "Selector attribute does not support value '", - "part2": "'!" - }, - "expectedLetterAfterAt": "Expected letter after '@'!", - "invalid": { - "part1": "Selector: '", - "part2": "' is not a valid selector!" - }, - "selectorNotBeforeOpenSquareBracket": "Expected a selector before '['!" - }, - "scoreData": { - "empty": "Unexpected empty score data!", - "expectedStringAsAttribute": "Score data attribute must be a string!", - "invalidType": { - "part1": "This score data value type: '", - "part2": "' is not supported!" - }, - "repeat": "Score data value must not repeat!" - }, - "arguments": { - "noneValid": { - "part1": "No valid command variations found! Argument ", - "part2": " may be invalid! It is of type '", - "part3": "' but that type is not supported in the current variation tree." - }, - "noneValidEnd": { - "part1": "No valid command variations found! You may be missing some arguments or argument ", - "part2": " may be invalid!" - } - } - }, - "warnings": { - "schema": { - "familyNotFound": { - "part1": "Could not find family '", - "part2": "'. This could either be a mistake or the family is from another addon." - }, - "typeNotFound": { - "part1": "Could not find type '", - "part2": "'. This could either be a mistake or the type is from another addon." - }, - "tagNotFound": { - "part1": "Could not find tag '", - "part2": "'. This could either be a mistake or the tag is from another addon." - }, - "schemaValueNotFound": { - "part1": "Could not find schema value '", - "part2": "'. This could either be a mistake or the schema value is from another addon." - } - }, - "data": { - "missingData": "Some data was not correctly loaded!" - } - } } } diff --git a/src/locales/ja.json b/src/locales/ja.json index 893cae1fa..3428fc255 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -1006,114 +1006,5 @@ "forceValue": "必須", "edit": "編集" } - }, - "functionValidator": { - "actionName": "Function を検証", - "tabName": "関数 バリデーター", - "errors": { - "common": { - "expectedEquals": "= がありません", - "expectedValue": "値がありません", - "expectedType": { - "part1": "セレクタの型は '", - "part2": "' ではありません '", - "part3": "' にしてください" - }, - "expectedComma": ", がありません", - "unclosedString": "文字列が閉じられていません", - "spaceAtStart": "先頭の空白文字はサポートされていません", - "expectedColon": ": がありません", - "unexpectedOpenCurlyBracket": "予期せぬ { があります", - "unexpectedCloseCurlyBracket": "予期せぬ } があります", - "unexpectedOpenSquareBracket": "予期せぬ [ があります", - "unexpectedCloseSquareBracket": "予期せぬ ] があります" - }, - "command": { - "empty": "空のコマンドはサポートされていません", - "invalid": { - "part1": "コマンド '", - "part2": "' は有効なコマンドではありません" - } - }, - "identifiers": { "missingNamespace": "ID に名前空間がありません" }, - "ranges": { - "missingFirstNumber": "整数部が正しくありません", - "missingDot": "予期しない少数点の位置です", - "missingSecondNumber": "少数部が正しくありません" - }, - "selectors": { - "emptyComplex": "セレクター属性が空です", - "expectedStringAsAttribute": "セレクター属性は文字列である必要があります", - "invalidSelectorAttribute": { - "part1": "'", - "part2": "' は有効なセレクター属性ではありません" - }, - "unsupportedNegation": { - "part1": "セレクター属性: '", - "part2": "' は否定をサポートしていません" - }, - "multipleInstancesNever": { - "part1": "セレクター属性: '", - "part2": "' は複数のインスタンスをサポートしていません" - }, - "multipleInstancesNegated": { - "part1": "セレクター属性: '", - "part2": "' は否定された場合のみ複数のインスタンスをサポートします" - }, - "valueNotValid": { - "part1": "セレクター属性は '", - "part2": "' をサポートしていません" - }, - "expectedLetterAfterAt": "@ の後に文字がありません", - "invalid": { - "part1": "セレクター: '", - "part2": "' は有効なセレクタではありません" - }, - "selectorNotBeforeOpenSquareBracket": "予期しない [ があります" - }, - "scoreData": { - "empty": "スコアデータが空です", - "expectedStringAsAttribute": "スコアデータは文字列でなければなりません", - "invalidType": { - "part1": "スコアデータでは '", - "part2": "' 型はサポートされていません" - }, - "repeat": "スコアデータ値は繰り返し使えません" - }, - "arguments": { - "noneValid": { - "part1": "有効なコマンド形式ではありません。 第", - "part2": "引数が無効です。 現在'", - "part3": "' 型はサポートされていません。" - }, - "noneValidEnd": { - "part1": "有効なコマンド形式ではありません。引数が不足しているか、第", - "part2": "引数が無効です。" - } - } - }, - "warnings": { - "schema": { - "familyNotFound": { - "part1": "family='", - "part2": "'が見つかりませんでした。これは何かの間違いか、他のアドオンのタグである可能性があります。" - }, - "typeNotFound": { - "part1": "type='", - "part2": "'が見つかりませんでした。これは何かの間違いか、他のアドオンのタグである可能性があります。" - }, - "tagNotFound": { - "part1": "tag='", - "part2": "'が見つかりませんでした。これは何かの間違いか、他のアドオンのタグである可能性があります。" - }, - "schemaValueNotFound": { - "part1": "value='", - "part2": "'が見つかりませんでした。これは何かの間違いか、他のアドオンのタグである可能性があります。" - } - }, - "data": { - "missingData": "一部のデータが正しく読み込まれませんでした" - } - } } } diff --git a/src/locales/nl.json b/src/locales/nl.json index d9564be65..790fead76 100644 --- a/src/locales/nl.json +++ b/src/locales/nl.json @@ -1020,116 +1020,5 @@ "forceValue": "Forceer Waarde", "edit": "Bewerk" } - }, - "functionValidator": { - "actionName": "Functie Valideren", - "tabName": "Functievalidator", - "errors": { - "common": { - "expectedEquals": "Verwachte gelijk aan!", - "expectedValue": "Verwachte waarde!", - "expectedType": { - "part1": "Verwachte type '", - "part2": "' maar kreeg een type van '", - "part3": "'!" - }, - "expectedComma": "Verwachte komma!", - "unclosedString": "Ongesloten string!", - "spaceAtStart": "Spaties aan het begin worden niet ondersteund!", - "expectedColon": "Verwachte dubbele punt!", - "unexpectedOpenCurlyBracket": "Onverwachte open accolade!", - "unexpectedCloseCurlyBracket": "Onverwachte gesloten accolade!", - "unexpectedOpenSquareBracket": "Onverwachte open vierkante haak!", - "unexpectedCloseSquareBracket": "Onverwachte gesloten vierkant haak!" - }, - "command": { - "empty": "Lege commando's worden niet ondersteund!", - "invalid": { - "part1": "Commando: '", - "part2": "' is geen geldig commando!" - } - }, - "identifiers": { - "missingNamespace": "Ontbrekende namespace in identifier!" - }, - "ranges": { - "missingFirstNumber": "Ontbrekend eerste nummer in bereik!", - "missingDot": "Ontbrekende punt in bereik!", - "missingSecondNumber": "Ontbrekend tweede nummer in bereik!" - }, - "selectors": { - "emptyComplex": "Onverwachte lege complexe selector!", - "expectedStringAsAttribute": "Selector attribuut moet een string zijn!", - "invalidSelectorAttribute": { - "part1": "'", - "part2": "' is geen geldig selector attribuut!" - }, - "unsupportedNegation": { - "part1": "Selector attribuut: '", - "part2": "' ondersteunt geen negatie!" - }, - "multipleInstancesNever": { - "part1": "Selector attribuut: '", - "part2": "' ondersteunt niet meerdere instanties!" - }, - "multipleInstancesNegated": { - "part1": "Selector attribuut: '", - "part2": "' ondersteunt alleen meerdere instanties wanneer deze worden genegeerd!" - }, - "valueNotValid": { - "part1": "Selector attribuut ondersteunt waarde '", - "part2": "' niet!" - }, - "expectedLetterAfterAt": "Verwachte letter na '@'!", - "invalid": { - "part1": "Selector: '", - "part2": "' is geen geldige selector!" - }, - "selectorNotBeforeOpenSquareBracket": "Verwachtte een selector vóór '['!" - }, - "scoreData": { - "empty": "Onverwachte lege score gegevens!", - "expectedStringAsAttribute": "Score gegevens attribuut moet een string zijn!", - "invalidType": { - "part1": "Dit type score gegevenswaarde: '", - "part2": "' wordt niet ondersteund!" - }, - "repeat": "Score gegevenswaarde mag niet herhalen!" - }, - "arguments": { - "noneValid": { - "part1": "Geen geldige commandovariaties gevonden! Argument ", - "part2": " kan ongeldig zijn! Het is van het type '", - "part3": "' maar dat type wordt niet ondersteund in de huidige variatieboom." - }, - "noneValidEnd": { - "part1": "Geen geldige commandovariaties gevonden! Mogelijk mist u enkele argumenten of argumenten ", - "part2": " kunnen ongeldig zijn!" - } - } - }, - "warnings": { - "schema": { - "familyNotFound": { - "part1": "Kon familie '", - "part2": "' niet vinden. Dit kan een vergissing zijn of de familie is van een andere add-on." - }, - "typeNotFound": { - "part1": "Kon type '", - "part2": "' niet vinden. Dit kan een vergissing zijn of het type is van een andere add-on." - }, - "tagNotFound": { - "part1": "Kon tag '", - "part2": "' niet vinden. Dit kan een vergissing zijn of de tag is van een andere add-on." - }, - "schemaValueNotFound": { - "part1": "Kon schemawaarde '", - "part2": "' niet vinden. Dit kan een vergissing zijn of de schemawaarde is van een andere add-on." - } - }, - "data": { - "missingData": "Sommige gegevens zijn niet correct geladen!" - } - } } } diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json index ecc86d979..720e8334d 100644 --- a/src/locales/zh-CN.json +++ b/src/locales/zh-CN.json @@ -894,112 +894,5 @@ "forceValue": "强制值", "edit": "编辑" } - }, - "functionValidator": { - "actionName": "验证函数", - "tabName": "函数验证器", - "errors": { - "common": { - "expectedEquals": "预期应出现一个等号!", - "expectedValue": "预期应出现一个值!", - "expectedType": { - "part1": "预期应出现类型‘", - "part2": "’,但是却得到了类型‘", - "part3": "’!" - }, - "expectedComma": "预期应出现一个半角逗号!", - "unclosedString": "未闭合的字符串!", - "spaceAtStart": "不支持开头出现空格!", - "expectedColon": "预期应出现一个半角冒号!", - "unexpectedOpenCurlyBracket": "出现了意外的左花括号!", - "unexpectedCloseCurlyBracket": "出现了意外的右花括号!", - "unexpectedOpenSquareBracket": "出现了意外的左方括号!", - "unexpectedCloseSquareBracket": "出现了意外的右方括号!" - }, - "command": { - "empty": "不支持命令为空!", - "invalid": { - "part1": "命令:‘", - "part2": "’不是一个有效的命令!" - } - }, - "identifiers": { "missingNamespace": "标识符中缺少命名空间!" }, - "ranges": { - "missingFirstNumber": "范围中缺少的第一个数字!", - "missingDot": "范围中点的数目不足!", - "missingSecondNumber": "范围中缺少的第二个数字!" - }, - "selectors": { - "emptyComplex": "出现了意外的空的复杂选择器!", - "expectedStringAsAttribute": "选择器特性必须是一个字符串!", - "invalidSelectorAttribute": { - "part1": "‘", - "part2": "’不是一个有效的选择器特性!" - }, - "unsupportedNegation": { - "part1": "选择器特性:‘", - "part2": "’不支持否定!" - }, - "multipleInstancesNever": { - "part1": "选择器特性:‘", - "part2": "’不支持同时出现多个实例!" - }, - "multipleInstancesNegated": { - "part1": "选择器特性:‘", - "part2": "’当被否定时只支持同时出现多个实例的情形!" - }, - "valueNotValid": { - "part1": "选择器特性不支持值‘", - "part2": "’!" - }, - "expectedLetterAfterAt": "预期在‘@’之后应出现字母!", - "invalid": { - "part1": "选择器‘", - "part2": "’不是一个有效的选择器!" - }, - "selectorNotBeforeOpenSquareBracket": "预期在‘[’之前应出现一个选择器!" - }, - "scoreData": { - "empty": "出现了意外的空的分数数据!", - "expectedStringAsAttribute": "分数数据特性必须是一个字符串!", - "invalidType": { - "part1": "不支持该分数数据的值的类型:", - "part2": "’!" - }, - "repeat": "分数数据的值不能重复!" - }, - "arguments": { - "noneValid": { - "part1": "找不到有效的命令变体!参数‘", - "part2": "’可能无效!它的类型是‘", - "part3": "’,但当前的变体树不支持该类型。" - }, - "noneValidEnd": { - "part1": "找不到有效的命令变体!你可能遗漏了一些参数或参数‘", - "part2": "’可能无效!" - } - } - }, - "warnings": { - "schema": { - "familyNotFound": { - "part1": "找不到族‘", - "part2": "’。这可能是一个错误,或者该族来自另一个附加包。" - }, - "typeNotFound": { - "part1": "找不到类型‘", - "part2": "’。这可能是一个错误,或者该类型来自另一个附加包。" - }, - "tagNotFound": { - "part1": "找不到标签‘", - "part2": "’。这可能是一个错误,或者该标签来自另一个附加包。" - }, - "schemaValueNotFound": { - "part1": "找不到模式值‘", - "part2": "’。这可能是一个错误,或者该模式值来自另一个附加包。" - } - }, - "data": { "missingData": "某些数据未正确加载!" } - } } } From 20f9bb81c7fb36d313d8cc710d643671320840e1 Mon Sep 17 00:00:00 2001 From: Outer Cloud Studio Date: Sun, 21 Aug 2022 22:19:59 -0500 Subject: [PATCH 02/29] First Working Validation Validates: - Command Name - Argument Types - Argument Count --- src/components/Languages/Mcfunction.ts | 33 ++++- src/components/Languages/Mcfunction/Data.ts | 2 +- .../Languages/Mcfunction/Validator.ts | 115 ++++++++++++++++++ 3 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 src/components/Languages/Mcfunction/Validator.ts diff --git a/src/components/Languages/Mcfunction.ts b/src/components/Languages/Mcfunction.ts index de5af96f2..53071b18f 100644 --- a/src/components/Languages/Mcfunction.ts +++ b/src/components/Languages/Mcfunction.ts @@ -14,6 +14,7 @@ import type { Project } from '/@/components/Projects/Project/Project' import { isWithinTargetSelector } from './Mcfunction/TargetSelector/isWithin' import { proxy } from 'comlink' import { useMonaco } from '../../utils/libs/useMonaco' +import { CommandValidator } from './Mcfunction/Validator' export const config: languages.LanguageConfiguration = { wordPattern: /[aA-zZ]+/, @@ -169,6 +170,8 @@ const loadCommands = async (lang: McfunctionLanguage) => { } export class McfunctionLanguage extends Language { + protected validator: CommandValidator | undefined + constructor() { super({ id: 'mcfunction', @@ -214,6 +217,11 @@ export class McfunctionLanguage extends Language { }), disposable ) + + const project = app.project + if (!(project instanceof BedrockProject)) return + + this.validator = new CommandValidator(project.commandData) }) } @@ -226,5 +234,28 @@ export class McfunctionLanguage extends Language { return true } - validate() {} + async validate(model: editor.IModel) { + if (this.validator == undefined) return + + const { editor, MarkerSeverity } = await useMonaco() + + try { + await this.validator.parse(model.getValue()) + + editor.setModelMarkers(model, this.id, []) + } catch (err: any) { + console.log(err) + + editor.setModelMarkers(model, this.id, [ + { + startColumn: err.start + 1, + endColumn: err.end + 1, + startLineNumber: err.line + 1, + endLineNumber: err.line + 1, + message: err.message, + severity: MarkerSeverity.Error, + }, + ]) + } + } } diff --git a/src/components/Languages/Mcfunction/Data.ts b/src/components/Languages/Mcfunction/Data.ts index c69d9463a..14f3491b7 100644 --- a/src/components/Languages/Mcfunction/Data.ts +++ b/src/components/Languages/Mcfunction/Data.ts @@ -436,7 +436,7 @@ export class CommandData extends Signal { /** * Given an argument type, test whether a string matches the type */ - protected async isArgumentType( + async isArgumentType( testStr: string, commandArgument: ICommandArgument, commandName?: string diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts new file mode 100644 index 000000000..377c0f618 --- /dev/null +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -0,0 +1,115 @@ +import { + tokenizeCommand, + tokenizeTargetSelector, + castType, +} from 'bridge-common-utils' +import { CommandData } from './Data' + +export class CommandValidator { + protected commandData: CommandData + + constructor(commandData: CommandData) { + this.commandData = commandData + } + + getType(word: string) { + const typedWord = castType(word) + const type = typeof typedWord + + if (type == 'number') return 'number' + + if (type == 'boolean') return 'boolean' + + return type + } + + async parse(content: string) { + console.log('Validating!') + + //Split content into lines + const lines = content.split('\n') + + for (let i = 0; i < lines.length; i++) { + const line = lines[i] + + let { tokens } = tokenizeCommand(line) + + const commandName = tokens[0] + + //If first word is emtpy then this is an empty line + if (commandName.word == '') continue + + if ( + !(await this.commandData.allCommands()).includes( + commandName.word + ) + ) + throw { + message: 'languages.mcfunction.parse.error.unkown_command', + line: i, + start: commandName.startColumn, + end: commandName.endColumn, + } + + let definitions = await this.commandData.getCommandDefinitions( + commandName.word, + false + ) + + //Remove empty tokens as to not confuse the argument checker + tokens = tokens.filter((token) => token.word != '') + + //If there are two many arguments than any definition throw an error + if ( + definitions.find( + (definition) => + definition.arguments.length >= tokens.length - 1 + ) == undefined + ) + throw { + message: + 'languages.mcfunction.parse.error.too_may_arguments', + line: i, + start: tokens[0].startColumn, + end: tokens[tokens.length - 1].endColumn, + } + + //Check for a valid command definition + for (let k = 1; k < tokens.length; k++) { + const argument = tokens[k] + + for (let j = 0; j < definitions.length; j++) { + if (definitions[j].arguments.length < k) { + definitions.splice(j, 1) + + j-- + + continue + } + + const targetArgument = definitions[j].arguments[k - 1] + const argumentType = await this.commandData.isArgumentType( + argument.word, + targetArgument, + commandName.word + ) + + if (argumentType != 'full') { + definitions.splice(j, 1) + + j-- + } + } + + if (definitions.length == 0) + throw { + message: + 'languages.mcfunction.parse.error.invalid_argument', + line: i, + start: argument.startColumn, + end: argument.endColumn, + } + } + } + } +} From 38995b2ea4d40d0db1051235d4403d1728f1a35f Mon Sep 17 00:00:00 2001 From: solvedDev Date: Mon, 22 Aug 2022 15:32:58 +0200 Subject: [PATCH 03/29] upd: use diagnostics array instead of throwing error --- src/components/Languages/Mcfunction.ts | 25 ++----- .../Languages/Mcfunction/Validator.ts | 69 +++++++++++-------- 2 files changed, 46 insertions(+), 48 deletions(-) diff --git a/src/components/Languages/Mcfunction.ts b/src/components/Languages/Mcfunction.ts index 53071b18f..2a0dd36ef 100644 --- a/src/components/Languages/Mcfunction.ts +++ b/src/components/Languages/Mcfunction.ts @@ -237,25 +237,10 @@ export class McfunctionLanguage extends Language { async validate(model: editor.IModel) { if (this.validator == undefined) return - const { editor, MarkerSeverity } = await useMonaco() - - try { - await this.validator.parse(model.getValue()) - - editor.setModelMarkers(model, this.id, []) - } catch (err: any) { - console.log(err) - - editor.setModelMarkers(model, this.id, [ - { - startColumn: err.start + 1, - endColumn: err.end + 1, - startLineNumber: err.line + 1, - endLineNumber: err.line + 1, - message: err.message, - severity: MarkerSeverity.Error, - }, - ]) - } + const { editor } = await useMonaco() + + const diagnostics = await this.validator.parse(model.getValue()) + + editor.setModelMarkers(model, this.id, diagnostics) } } diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index 377c0f618..08a088421 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -4,6 +4,8 @@ import { castType, } from 'bridge-common-utils' import { CommandData } from './Data' +import type { editor } from 'monaco-editor' +import { useMonaco } from '/@/utils/libs/useMonaco' export class CommandValidator { protected commandData: CommandData @@ -26,8 +28,10 @@ export class CommandValidator { async parse(content: string) { console.log('Validating!') - //Split content into lines + // Split content into lines const lines = content.split('\n') + const diagnostics: editor.IMarkerData[] = [] + const { MarkerSeverity } = await useMonaco() for (let i = 0; i < lines.length; i++) { const line = lines[i] @@ -36,45 +40,50 @@ export class CommandValidator { const commandName = tokens[0] - //If first word is emtpy then this is an empty line + // If first word is emtpy then this is an empty line if (commandName.word == '') continue if ( !(await this.commandData.allCommands()).includes( commandName.word ) - ) - throw { - message: 'languages.mcfunction.parse.error.unkown_command', - line: i, - start: commandName.startColumn, - end: commandName.endColumn, - } + ) { + diagnostics.push({ + severity: MarkerSeverity.Error, + message: `Command ${commandName.word} not found`, + startLineNumber: i + 1, + startColumn: commandName.startColumn + 1, + endLineNumber: i + 1, + endColumn: commandName.endColumn + 1, + }) + } let definitions = await this.commandData.getCommandDefinitions( commandName.word, false ) - //Remove empty tokens as to not confuse the argument checker + // Remove empty tokens as to not confuse the argument checker tokens = tokens.filter((token) => token.word != '') - //If there are two many arguments than any definition throw an error + // If there are too many arguments for all available definitions, throw an error if ( definitions.find( (definition) => definition.arguments.length >= tokens.length - 1 ) == undefined - ) - throw { - message: - 'languages.mcfunction.parse.error.too_may_arguments', - line: i, - start: tokens[0].startColumn, - end: tokens[tokens.length - 1].endColumn, - } + ) { + diagnostics.push({ + severity: MarkerSeverity.Error, + message: `Too many arguments for command ${commandName.word}`, + startLineNumber: i + 1, + startColumn: commandName.startColumn + 1, + endLineNumber: i + 1, + endColumn: tokens.at(-1)?.endColumn ?? 1, + }) + } - //Check for a valid command definition + // Check for a valid command definition for (let k = 1; k < tokens.length; k++) { const argument = tokens[k] @@ -101,15 +110,19 @@ export class CommandValidator { } } - if (definitions.length == 0) - throw { - message: - 'languages.mcfunction.parse.error.invalid_argument', - line: i, - start: argument.startColumn, - end: argument.endColumn, - } + if (definitions.length == 0) { + diagnostics.push({ + severity: MarkerSeverity.Error, + message: `Argument ${argument.word} not valid here`, + startLineNumber: i + 1, + startColumn: argument.startColumn + 1, + endLineNumber: i + 1, + endColumn: argument.endColumn + 1, + }) + } } } + + return diagnostics } } From 8fdd1ffe1dcd21733a93f53bbd1b35c6d3030244 Mon Sep 17 00:00:00 2001 From: solvedDev Date: Mon, 22 Aug 2022 15:33:25 +0200 Subject: [PATCH 04/29] fix: skip comment lines --- src/components/Languages/Mcfunction/Validator.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index 08a088421..032d8d9da 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -35,6 +35,7 @@ export class CommandValidator { for (let i = 0; i < lines.length; i++) { const line = lines[i] + if (line[0] == '#') continue let { tokens } = tokenizeCommand(line) From 70ba25f26e0b73556d8e1fe0613bbaa5581c8070 Mon Sep 17 00:00:00 2001 From: solvedDev Date: Mon, 22 Aug 2022 15:41:22 +0200 Subject: [PATCH 05/29] fix: don't continue validating line if it's not a valid command name --- src/components/Languages/Mcfunction/Validator.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index 032d8d9da..18446dc36 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -51,12 +51,15 @@ export class CommandValidator { ) { diagnostics.push({ severity: MarkerSeverity.Error, - message: `Command ${commandName.word} not found`, + message: `Command "${commandName.word}" does not exist`, startLineNumber: i + 1, startColumn: commandName.startColumn + 1, endLineNumber: i + 1, endColumn: commandName.endColumn + 1, }) + + // The command is not valid; it makes no sense to continue validating this line + continue } let definitions = await this.commandData.getCommandDefinitions( @@ -76,7 +79,7 @@ export class CommandValidator { ) { diagnostics.push({ severity: MarkerSeverity.Error, - message: `Too many arguments for command ${commandName.word}`, + message: `Too many arguments for command "${commandName.word}"`, startLineNumber: i + 1, startColumn: commandName.startColumn + 1, endLineNumber: i + 1, @@ -114,7 +117,7 @@ export class CommandValidator { if (definitions.length == 0) { diagnostics.push({ severity: MarkerSeverity.Error, - message: `Argument ${argument.word} not valid here`, + message: `Argument "${argument.word}" is not valid here`, startLineNumber: i + 1, startColumn: argument.startColumn + 1, endLineNumber: i + 1, From 6a15a92afe584633665742ce6c1acb98b36f439c Mon Sep 17 00:00:00 2001 From: Outer Cloud Studio Date: Mon, 22 Aug 2022 23:16:09 -0500 Subject: [PATCH 06/29] Begin evaluating subcommands --- src/components/Languages/Mcfunction/Data.ts | 3 +- .../Languages/Mcfunction/Validator.ts | 149 +++++++++++++++++- 2 files changed, 143 insertions(+), 9 deletions(-) diff --git a/src/components/Languages/Mcfunction/Data.ts b/src/components/Languages/Mcfunction/Data.ts index 14f3491b7..8af3f478b 100644 --- a/src/components/Languages/Mcfunction/Data.ts +++ b/src/components/Languages/Mcfunction/Data.ts @@ -152,7 +152,8 @@ export class CommandData extends Signal { (selectorArgument: unknown) => selectorArgument !== undefined ) } - protected async getSubcommands(commandName: string): Promise { + + async getSubcommands(commandName: string): Promise { const schemas = await this.getSchema() return schemas diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index 18446dc36..d6889d538 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -3,7 +3,7 @@ import { tokenizeTargetSelector, castType, } from 'bridge-common-utils' -import { CommandData } from './Data' +import { CommandData, ICommandArgument } from './Data' import type { editor } from 'monaco-editor' import { useMonaco } from '/@/utils/libs/useMonaco' @@ -14,15 +14,82 @@ export class CommandValidator { this.commandData = commandData } - getType(word: string) { - const typedWord = castType(word) - const type = typeof typedWord + async parseSubcommand( + baseCommandName: string, + leftTokens: { + startColumn: number + endColumn: number + word: string + }[] + ) { + console.log('Subcommand:') + console.log(leftTokens) - if (type == 'number') return 'number' + const subcommandName = leftTokens[0] - if (type == 'boolean') return 'boolean' + let subcommandDefinitions = ( + await this.commandData.getSubcommands(baseCommandName) + ).filter((definition) => definition.commandName == subcommandName.word) - return type + if (subcommandDefinitions.length == 0) + return { + passed: false, + } + + let passedSubcommandDefinition = undefined + + for (const definition of subcommandDefinitions) { + console.log('Validating Subcommand Definition') + console.log(definition) + + let failed = false + + for (let j = 0; j < definition.arguments.length; j++) { + const argument = leftTokens[j + 1] + const targetArgument = definition.arguments[j] + + console.log(argument) + console.log(targetArgument) + + const argumentType = await this.commandData.isArgumentType( + argument.word, + targetArgument + ) + + if (argumentType != 'full') { + console.warn( + `Subcommand: Check against ${argument.word} and ${targetArgument.type} failed!` + ) + + failed = true + + break + } + + // TODO: Check extra conditions + } + + if (!failed) { + console.log(`Subcommand: definition passed!`) + passedSubcommandDefinition = definition + } + } + + if (passedSubcommandDefinition == undefined) { + console.warn('Subcommand: no definition passed!') + + return { + passed: false, + } + } else { + console.log('Subcommand: Fully passed!') + + return { + passed: true, + argumentsConsumedCount: + passedSubcommandDefinition.arguments.length, + } + } } async parse(content: string) { @@ -67,14 +134,22 @@ export class CommandValidator { false ) + console.log(JSON.parse(JSON.stringify(definitions))) + // Remove empty tokens as to not confuse the argument checker tokens = tokens.filter((token) => token.word != '') - // If there are too many arguments for all available definitions, throw an error + // If there are too many arguments for all available definitions, throw an error but only if there is not definition that includes subcommands if ( definitions.find( (definition) => definition.arguments.length >= tokens.length - 1 + ) == undefined && + definitions.find( + (definition) => + definition.arguments.find( + (argument) => argument.type == 'subcommand' + ) != undefined ) == undefined ) { diagnostics.push({ @@ -101,16 +176,72 @@ export class CommandValidator { } const targetArgument = definitions[j].arguments[k - 1] + + if (targetArgument.type == 'subcommand') { + // TODO: Implement Subcommands + + const result = await this.parseSubcommand( + commandName.word, + tokens.slice(k, tokens.length) + ) + + console.log('Subcommand result!') + console.log(result) + + if (result.passed) { + continue + } else { + // Fail because subcommand doesn't match any definitions + console.warn( + `Check for subcommand ${argument.word} failed!` + ) + + definitions.splice(j, 1) + + j-- + + continue + } + } + const argumentType = await this.commandData.isArgumentType( argument.word, targetArgument, commandName.word ) + // Fail if type does not match if (argumentType != 'full') { + console.warn( + `Check against ${argument.word} and ${targetArgument.type} failed!` + ) + + definitions.splice(j, 1) + + j-- + + continue + } + + // Fail if there are additional values that are not met + if ( + targetArgument.additionalData != undefined && + targetArgument.additionalData.values != undefined && + !targetArgument.additionalData.values.includes( + argument.word + ) + ) { + console.warn( + `Invalid value ${argument.word} of ${JSON.stringify( + targetArgument.additionalData.values + )}!` + ) + definitions.splice(j, 1) j-- + + continue } } @@ -123,6 +254,8 @@ export class CommandValidator { endLineNumber: i + 1, endColumn: argument.endColumn + 1, }) + + break } } } From bc40fd2ab62c172b72b4f0187be05844a644fdd2 Mon Sep 17 00:00:00 2001 From: Outer Cloud Studio Date: Mon, 22 Aug 2022 23:31:36 -0500 Subject: [PATCH 07/29] Change definition validation to be token index independent --- .../Languages/Mcfunction/Validator.ts | 88 ++++++++----------- 1 file changed, 38 insertions(+), 50 deletions(-) diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index d6889d538..9920a136f 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -129,6 +129,9 @@ export class CommandValidator { continue } + // Remove empty tokens as to not confuse the argument checker + tokens = tokens.filter((token) => token.word != '') + let definitions = await this.commandData.getCommandDefinitions( commandName.word, false @@ -136,51 +139,32 @@ export class CommandValidator { console.log(JSON.parse(JSON.stringify(definitions))) - // Remove empty tokens as to not confuse the argument checker - tokens = tokens.filter((token) => token.word != '') - - // If there are too many arguments for all available definitions, throw an error but only if there is not definition that includes subcommands - if ( - definitions.find( - (definition) => - definition.arguments.length >= tokens.length - 1 - ) == undefined && - definitions.find( - (definition) => - definition.arguments.find( - (argument) => argument.type == 'subcommand' - ) != undefined - ) == undefined - ) { - diagnostics.push({ - severity: MarkerSeverity.Error, - message: `Too many arguments for command "${commandName.word}"`, - startLineNumber: i + 1, - startColumn: commandName.startColumn + 1, - endLineNumber: i + 1, - endColumn: tokens.at(-1)?.endColumn ?? 1, - }) - } - - // Check for a valid command definition - for (let k = 1; k < tokens.length; k++) { - const argument = tokens[k] + // We only need to record the error of the most farthest in token because that is the most likely variation the user was attempting to type + let lastTokenError = 0 - for (let j = 0; j < definitions.length; j++) { - if (definitions[j].arguments.length < k) { + // Loop over every definition and test for validness + for (let j = 0; j < definitions.length; j++) { + // Loop over every token that is not the command name + let targetArgumentIndex = 0 + for (let k = 1; k < tokens.length; k++) { + if (definitions[j].arguments.length < targetArgumentIndex) { definitions.splice(j, 1) j-- - continue + if (lastTokenError < k) lastTokenError = k + + break } - const targetArgument = definitions[j].arguments[k - 1] + const argument = tokens[k] + + const targetArgument = + definitions[j].arguments[targetArgumentIndex] if (targetArgument.type == 'subcommand') { // TODO: Implement Subcommands - - const result = await this.parseSubcommand( + /*const result = await this.parseSubcommand( commandName.word, tokens.slice(k, tokens.length) ) @@ -201,7 +185,7 @@ export class CommandValidator { j-- continue - } + }*/ } const argumentType = await this.commandData.isArgumentType( @@ -220,7 +204,9 @@ export class CommandValidator { j-- - continue + if (lastTokenError < k) lastTokenError = k + + break } // Fail if there are additional values that are not met @@ -241,23 +227,25 @@ export class CommandValidator { j-- - continue - } - } + if (lastTokenError < k) lastTokenError = k - if (definitions.length == 0) { - diagnostics.push({ - severity: MarkerSeverity.Error, - message: `Argument "${argument.word}" is not valid here`, - startLineNumber: i + 1, - startColumn: argument.startColumn + 1, - endLineNumber: i + 1, - endColumn: argument.endColumn + 1, - }) + break + } - break + targetArgumentIndex++ } } + + if (definitions.length == 0) { + diagnostics.push({ + severity: MarkerSeverity.Error, + message: `Argument "${tokens[lastTokenError].word}" is not valid here`, + startLineNumber: i + 1, + startColumn: tokens[lastTokenError].startColumn + 1, + endLineNumber: i + 1, + endColumn: tokens[lastTokenError].endColumn + 1, + }) + } } return diagnostics From 4e71bb7068de149018787f08715318118cfcffc9 Mon Sep 17 00:00:00 2001 From: Outer Cloud Studio Date: Mon, 22 Aug 2022 23:52:55 -0500 Subject: [PATCH 08/29] Validating Subcommands --- .../Languages/Mcfunction/Validator.ts | 69 ++++++++++++++++++- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index 9920a136f..27980ee4d 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -44,6 +44,15 @@ export class CommandValidator { let failed = false + // Fail if there is not enought tokens to satisfy the definition + console.log('Length check?') + console.log(leftTokens.length) + console.log(definition.arguments.length) + + if (leftTokens.length - 1 < definition.arguments.length) { + continue + } + for (let j = 0; j < definition.arguments.length; j++) { const argument = leftTokens[j + 1] const targetArgument = definition.arguments[j] @@ -56,6 +65,7 @@ export class CommandValidator { targetArgument ) + // Fail if type does not match if (argumentType != 'full') { console.warn( `Subcommand: Check against ${argument.word} and ${targetArgument.type} failed!` @@ -132,6 +142,20 @@ export class CommandValidator { // Remove empty tokens as to not confuse the argument checker tokens = tokens.filter((token) => token.word != '') + if (tokens.length < 2) { + diagnostics.push({ + severity: MarkerSeverity.Error, + message: `Command "${commandName.word}" needs parameters`, + startLineNumber: i + 1, + startColumn: commandName.startColumn + 1, + endLineNumber: i + 1, + endColumn: commandName.endColumn + 1, + }) + + // The command is not valid; it makes no sense to continue validating this line + continue + } + let definitions = await this.commandData.getCommandDefinitions( commandName.word, false @@ -144,16 +168,24 @@ export class CommandValidator { // Loop over every definition and test for validness for (let j = 0; j < definitions.length; j++) { + let failed = false + // Loop over every token that is not the command name let targetArgumentIndex = 0 for (let k = 1; k < tokens.length; k++) { if (definitions[j].arguments.length < targetArgumentIndex) { + console.warn( + 'Failed cause less arguments than target argument index' + ) + definitions.splice(j, 1) j-- if (lastTokenError < k) lastTokenError = k + failed = true + break } @@ -162,9 +194,13 @@ export class CommandValidator { const targetArgument = definitions[j].arguments[targetArgumentIndex] + console.log( + `Checking ${argument.word} against ${targetArgument.type}` + ) + if (targetArgument.type == 'subcommand') { // TODO: Implement Subcommands - /*const result = await this.parseSubcommand( + const result = await this.parseSubcommand( commandName.word, tokens.slice(k, tokens.length) ) @@ -173,6 +209,10 @@ export class CommandValidator { console.log(result) if (result.passed) { + k += result.argumentsConsumedCount! + + targetArgumentIndex++ + continue } else { // Fail because subcommand doesn't match any definitions @@ -184,8 +224,12 @@ export class CommandValidator { j-- - continue - }*/ + if (lastTokenError < k) lastTokenError = k + + failed = true + + break + } } const argumentType = await this.commandData.isArgumentType( @@ -206,6 +250,8 @@ export class CommandValidator { if (lastTokenError < k) lastTokenError = k + failed = true + break } @@ -229,11 +275,28 @@ export class CommandValidator { if (lastTokenError < k) lastTokenError = k + failed = true + break } targetArgumentIndex++ } + + if (failed) continue + + if (targetArgumentIndex < definitions[j].arguments.length) { + console.warn( + 'Failed because not argument is less than definition argument count' + ) + + definitions.splice(j, 1) + + j-- + + if (lastTokenError < tokens.length - 1) + lastTokenError = tokens.length - 1 + } } if (definitions.length == 0) { From 507225a220f29ea92503afa0e79f61305999840c Mon Sep 17 00:00:00 2001 From: Outer Cloud Studio Date: Tue, 23 Aug 2022 00:07:39 -0500 Subject: [PATCH 09/29] Bug Fixes + Handle Subcommands --- .../Languages/Mcfunction/Validator.ts | 43 +++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index 27980ee4d..af846fe7e 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -49,7 +49,7 @@ export class CommandValidator { console.log(leftTokens.length) console.log(definition.arguments.length) - if (leftTokens.length - 1 < definition.arguments.length) { + if (leftTokens.length - 1 <= definition.arguments.length) { continue } @@ -93,6 +93,7 @@ export class CommandValidator { } } else { console.log('Subcommand: Fully passed!') + console.log(passedSubcommandDefinition) return { passed: true, @@ -173,7 +174,9 @@ export class CommandValidator { // Loop over every token that is not the command name let targetArgumentIndex = 0 for (let k = 1; k < tokens.length; k++) { - if (definitions[j].arguments.length < targetArgumentIndex) { + if ( + definitions[j].arguments.length <= targetArgumentIndex + ) { console.warn( 'Failed cause less arguments than target argument index' ) @@ -194,12 +197,17 @@ export class CommandValidator { const targetArgument = definitions[j].arguments[targetArgumentIndex] + console.log(definitions[j].arguments.length) + console.log(targetArgumentIndex) + console.log( + JSON.parse(JSON.stringify(definitions[j].arguments)) + ) + console.log(targetArgument) console.log( `Checking ${argument.word} against ${targetArgument.type}` ) if (targetArgument.type == 'subcommand') { - // TODO: Implement Subcommands const result = await this.parseSubcommand( commandName.word, tokens.slice(k, tokens.length) @@ -211,6 +219,35 @@ export class CommandValidator { if (result.passed) { k += result.argumentsConsumedCount! + if (targetArgument.allowMultiple) { + let nextResult: { + passed: boolean + argumentsConsumedCount?: number + } = { + passed: true, + argumentsConsumedCount: 0, + } + + while (nextResult.passed) { + nextResult = await this.parseSubcommand( + commandName.word, + tokens.slice(k + 1, tokens.length) + ) + + if (nextResult.passed) { + const origK = k + + k += + nextResult.argumentsConsumedCount! + + 1 + + console.log( + `Extra subcommand pushed k to ${k} from ${origK} or ${tokens[k].word} from ${tokens[origK].word}` + ) + } + } + } + targetArgumentIndex++ continue From 561514aee4c58d34d7967c60785fad5a3ed9c2e5 Mon Sep 17 00:00:00 2001 From: Outer Cloud Studio Date: Tue, 23 Aug 2022 23:52:37 -0500 Subject: [PATCH 10/29] Remove most debug logs, More comments --- .../Languages/Mcfunction/Validator.ts | 61 +++---------------- 1 file changed, 7 insertions(+), 54 deletions(-) diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index af846fe7e..edec8d965 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -22,9 +22,6 @@ export class CommandValidator { word: string }[] ) { - console.log('Subcommand:') - console.log(leftTokens) - const subcommandName = leftTokens[0] let subcommandDefinitions = ( @@ -38,28 +35,20 @@ export class CommandValidator { let passedSubcommandDefinition = undefined + // Loop over every subcommand definition to check for a matching one for (const definition of subcommandDefinitions) { - console.log('Validating Subcommand Definition') - console.log(definition) - let failed = false // Fail if there is not enought tokens to satisfy the definition - console.log('Length check?') - console.log(leftTokens.length) - console.log(definition.arguments.length) - if (leftTokens.length - 1 <= definition.arguments.length) { continue } + // Loop over every argument for (let j = 0; j < definition.arguments.length; j++) { const argument = leftTokens[j + 1] const targetArgument = definition.arguments[j] - console.log(argument) - console.log(targetArgument) - const argumentType = await this.commandData.isArgumentType( argument.word, targetArgument @@ -80,21 +69,15 @@ export class CommandValidator { } if (!failed) { - console.log(`Subcommand: definition passed!`) passedSubcommandDefinition = definition } } if (passedSubcommandDefinition == undefined) { - console.warn('Subcommand: no definition passed!') - return { passed: false, } } else { - console.log('Subcommand: Fully passed!') - console.log(passedSubcommandDefinition) - return { passed: true, argumentsConsumedCount: @@ -174,13 +157,10 @@ export class CommandValidator { // Loop over every token that is not the command name let targetArgumentIndex = 0 for (let k = 1; k < tokens.length; k++) { + // Fail if there are not enough arguments in definition if ( definitions[j].arguments.length <= targetArgumentIndex ) { - console.warn( - 'Failed cause less arguments than target argument index' - ) - definitions.splice(j, 1) j-- @@ -197,28 +177,17 @@ export class CommandValidator { const targetArgument = definitions[j].arguments[targetArgumentIndex] - console.log(definitions[j].arguments.length) - console.log(targetArgumentIndex) - console.log( - JSON.parse(JSON.stringify(definitions[j].arguments)) - ) - console.log(targetArgument) - console.log( - `Checking ${argument.word} against ${targetArgument.type}` - ) - if (targetArgument.type == 'subcommand') { const result = await this.parseSubcommand( commandName.word, tokens.slice(k, tokens.length) ) - console.log('Subcommand result!') - console.log(result) - if (result.passed) { + // Skip over tokens consumed in the subcommand validation k += result.argumentsConsumedCount! + // If there allows multiple subcommands keep going untill a subcommand fails if (targetArgument.allowMultiple) { let nextResult: { passed: boolean @@ -253,10 +222,6 @@ export class CommandValidator { continue } else { // Fail because subcommand doesn't match any definitions - console.warn( - `Check for subcommand ${argument.word} failed!` - ) - definitions.splice(j, 1) j-- @@ -277,10 +242,6 @@ export class CommandValidator { // Fail if type does not match if (argumentType != 'full') { - console.warn( - `Check against ${argument.word} and ${targetArgument.type} failed!` - ) - definitions.splice(j, 1) j-- @@ -300,12 +261,6 @@ export class CommandValidator { argument.word ) ) { - console.warn( - `Invalid value ${argument.word} of ${JSON.stringify( - targetArgument.additionalData.values - )}!` - ) - definitions.splice(j, 1) j-- @@ -320,13 +275,11 @@ export class CommandValidator { targetArgumentIndex++ } + // Skip if already failed in case this leaves an undefined reference if (failed) continue + // Fail if there are not enough tokens to satisfy definition if (targetArgumentIndex < definitions[j].arguments.length) { - console.warn( - 'Failed because not argument is less than definition argument count' - ) - definitions.splice(j, 1) j-- From 40648dc2718c7516967c9c570656d2bcacb2cff3 Mon Sep 17 00:00:00 2001 From: Outer Cloud Studio Date: Wed, 24 Aug 2022 20:37:47 -0500 Subject: [PATCH 11/29] Handle recursive command validation --- .../Languages/Mcfunction/Validator.ts | 347 ++++++++++-------- 1 file changed, 185 insertions(+), 162 deletions(-) diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index edec8d965..2d35f5bcb 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -14,7 +14,7 @@ export class CommandValidator { this.commandData = commandData } - async parseSubcommand( + protected async parseSubcommand( baseCommandName: string, leftTokens: { startColumn: number @@ -56,10 +56,6 @@ export class CommandValidator { // Fail if type does not match if (argumentType != 'full') { - console.warn( - `Subcommand: Check against ${argument.word} and ${targetArgument.type} failed!` - ) - failed = true break @@ -86,162 +82,132 @@ export class CommandValidator { } } - async parse(content: string) { - console.log('Validating!') - - // Split content into lines - const lines = content.split('\n') - const diagnostics: editor.IMarkerData[] = [] + protected async parseCommand( + line: string | undefined, + tokens: any[], + offset: number + ): Promise { const { MarkerSeverity } = await useMonaco() + let diagnostics: editor.IMarkerData[] = [] - for (let i = 0; i < lines.length; i++) { - const line = lines[i] - if (line[0] == '#') continue + if (line != undefined) tokens = tokenizeCommand(line).tokens - let { tokens } = tokenizeCommand(line) + const commandName = tokens[0] - const commandName = tokens[0] + // If first word is emtpy then this is an empty line + if (commandName.word == '') return diagnostics - // If first word is emtpy then this is an empty line - if (commandName.word == '') continue + if ( + !(await this.commandData.allCommands()).includes(commandName.word) + ) { + diagnostics.push({ + severity: MarkerSeverity.Error, + message: `Command "${commandName.word}" does not exist`, + startLineNumber: -1, + startColumn: commandName.startColumn + 1, + endLineNumber: -1, + endColumn: commandName.endColumn + 1, + }) - if ( - !(await this.commandData.allCommands()).includes( - commandName.word - ) - ) { - diagnostics.push({ - severity: MarkerSeverity.Error, - message: `Command "${commandName.word}" does not exist`, - startLineNumber: i + 1, - startColumn: commandName.startColumn + 1, - endLineNumber: i + 1, - endColumn: commandName.endColumn + 1, - }) - - // The command is not valid; it makes no sense to continue validating this line - continue - } + // The command is not valid; it makes no sense to continue validating this line + return diagnostics + } - // Remove empty tokens as to not confuse the argument checker - tokens = tokens.filter((token) => token.word != '') + console.log(`Validating command ${commandName.word}!`) - if (tokens.length < 2) { - diagnostics.push({ - severity: MarkerSeverity.Error, - message: `Command "${commandName.word}" needs parameters`, - startLineNumber: i + 1, - startColumn: commandName.startColumn + 1, - endLineNumber: i + 1, - endColumn: commandName.endColumn + 1, - }) + // Remove empty tokens as to not confuse the argument checker + tokens = tokens.filter((token) => token.word != '') - // The command is not valid; it makes no sense to continue validating this line - continue - } + if (tokens.length < 2) { + diagnostics.push({ + severity: MarkerSeverity.Error, + message: `Command "${commandName.word}" needs parameters`, + startLineNumber: -1, + startColumn: commandName.startColumn + 1, + endLineNumber: -1, + endColumn: commandName.endColumn + 1, + }) - let definitions = await this.commandData.getCommandDefinitions( - commandName.word, - false - ) + // The command is not valid; it makes no sense to continue validating this line + return diagnostics + } - console.log(JSON.parse(JSON.stringify(definitions))) + let definitions = await this.commandData.getCommandDefinitions( + commandName.word, + false + ) - // We only need to record the error of the most farthest in token because that is the most likely variation the user was attempting to type - let lastTokenError = 0 + // We only need to record the error of the most farthest in token because that is the most likely variation the user was attempting to type + let lastTokenError = 0 - // Loop over every definition and test for validness - for (let j = 0; j < definitions.length; j++) { - let failed = false + // Loop over every definition and test for validness + for (let j = 0; j < definitions.length; j++) { + let failed = false - // Loop over every token that is not the command name - let targetArgumentIndex = 0 - for (let k = 1; k < tokens.length; k++) { - // Fail if there are not enough arguments in definition - if ( - definitions[j].arguments.length <= targetArgumentIndex - ) { - definitions.splice(j, 1) + // Loop over every token that is not the command name + let targetArgumentIndex = 0 + for (let k = 1; k < tokens.length; k++) { + // Fail if there are not enough arguments in definition + if (definitions[j].arguments.length <= targetArgumentIndex) { + definitions.splice(j, 1) - j-- + j-- - if (lastTokenError < k) lastTokenError = k + if (lastTokenError < k) lastTokenError = k - failed = true + failed = true - break - } + break + } - const argument = tokens[k] - - const targetArgument = - definitions[j].arguments[targetArgumentIndex] - - if (targetArgument.type == 'subcommand') { - const result = await this.parseSubcommand( - commandName.word, - tokens.slice(k, tokens.length) - ) - - if (result.passed) { - // Skip over tokens consumed in the subcommand validation - k += result.argumentsConsumedCount! - - // If there allows multiple subcommands keep going untill a subcommand fails - if (targetArgument.allowMultiple) { - let nextResult: { - passed: boolean - argumentsConsumedCount?: number - } = { - passed: true, - argumentsConsumedCount: 0, - } + const argument = tokens[k] - while (nextResult.passed) { - nextResult = await this.parseSubcommand( - commandName.word, - tokens.slice(k + 1, tokens.length) - ) + const targetArgument = + definitions[j].arguments[targetArgumentIndex] - if (nextResult.passed) { - const origK = k + console.log( + `Validating agument of type ${targetArgument.type} onto ${argument.word}!` + ) - k += - nextResult.argumentsConsumedCount! + - 1 + if (targetArgument.type == 'subcommand') { + const result = await this.parseSubcommand( + commandName.word, + tokens.slice(k, tokens.length) + ) - console.log( - `Extra subcommand pushed k to ${k} from ${origK} or ${tokens[k].word} from ${tokens[origK].word}` - ) - } - } + if (result.passed) { + // Skip over tokens consumed in the subcommand validation + k += result.argumentsConsumedCount! + + // If there allows multiple subcommands keep going untill a subcommand fails + if (targetArgument.allowMultiple) { + let nextResult: { + passed: boolean + argumentsConsumedCount?: number + } = { + passed: true, + argumentsConsumedCount: 0, } - targetArgumentIndex++ - - continue - } else { - // Fail because subcommand doesn't match any definitions - definitions.splice(j, 1) + while (nextResult.passed) { + nextResult = await this.parseSubcommand( + commandName.word, + tokens.slice(k + 1, tokens.length) + ) - j-- + if (nextResult.passed) { + const origK = k - if (lastTokenError < k) lastTokenError = k - - failed = true - - break + k += nextResult.argumentsConsumedCount! + 1 + } + } } - } - const argumentType = await this.commandData.isArgumentType( - argument.word, - targetArgument, - commandName.word - ) + targetArgumentIndex++ - // Fail if type does not match - if (argumentType != 'full') { + continue + } else { + // Fail because subcommand doesn't match any definitions definitions.splice(j, 1) j-- @@ -252,52 +218,109 @@ export class CommandValidator { break } + } - // Fail if there are additional values that are not met - if ( - targetArgument.additionalData != undefined && - targetArgument.additionalData.values != undefined && - !targetArgument.additionalData.values.includes( - argument.word - ) - ) { - definitions.splice(j, 1) + if (targetArgument.type == 'command') { + const leftTokens = tokens.slice(k, tokens.length) - j-- + const result = await this.parseCommand( + undefined, + leftTokens, + offset + targetArgumentIndex + ) - if (lastTokenError < k) lastTokenError = k + diagnostics = diagnostics.concat(result) - failed = true + return diagnostics + } - break - } + const argumentType = await this.commandData.isArgumentType( + argument.word, + targetArgument, + commandName.word + ) - targetArgumentIndex++ - } + // Fail if type does not match + if (argumentType != 'full') { + definitions.splice(j, 1) + + j-- + + if (lastTokenError < k) lastTokenError = k + + failed = true - // Skip if already failed in case this leaves an undefined reference - if (failed) continue + break + } - // Fail if there are not enough tokens to satisfy definition - if (targetArgumentIndex < definitions[j].arguments.length) { + // Fail if there are additional values that are not met + if ( + targetArgument.additionalData != undefined && + targetArgument.additionalData.values != undefined && + !targetArgument.additionalData.values.includes( + argument.word + ) + ) { definitions.splice(j, 1) j-- - if (lastTokenError < tokens.length - 1) - lastTokenError = tokens.length - 1 + if (lastTokenError < k) lastTokenError = k + + failed = true + + break } + + targetArgumentIndex++ } - if (definitions.length == 0) { - diagnostics.push({ - severity: MarkerSeverity.Error, - message: `Argument "${tokens[lastTokenError].word}" is not valid here`, - startLineNumber: i + 1, - startColumn: tokens[lastTokenError].startColumn + 1, - endLineNumber: i + 1, - endColumn: tokens[lastTokenError].endColumn + 1, - }) + // Skip if already failed in case this leaves an undefined reference + if (failed) continue + + // Fail if there are not enough tokens to satisfy definition + if (targetArgumentIndex < definitions[j].arguments.length) { + definitions.splice(j, 1) + + j-- + + if (lastTokenError < tokens.length - 1) + lastTokenError = tokens.length - 1 + } + } + + if (definitions.length == 0) { + diagnostics.push({ + severity: MarkerSeverity.Error, + message: `Argument "${tokens[lastTokenError].word}" is not valid here`, + startLineNumber: -1, + startColumn: tokens[lastTokenError].startColumn + 1, + endLineNumber: -1, + endColumn: tokens[lastTokenError].endColumn + 1, + }) + } + + return diagnostics + } + + async parse(content: string) { + console.log('Validating!') + + // Split content into lines + const lines = content.split('\n') + const diagnostics: editor.IMarkerData[] = [] + + for (let i = 0; i < lines.length; i++) { + const line = lines[i] + if (line[0] == '#') continue + + const results = await this.parseCommand(line, [], 0) + + for (const diagnostic of results) { + diagnostic.startLineNumber = i + 1 + diagnostic.endLineNumber = i + 1 + + diagnostics.push(diagnostic) } } From 6f860e94219d7a790b8aa76165e28a64ce697ec8 Mon Sep 17 00:00:00 2001 From: Outer Cloud Studio Date: Wed, 24 Aug 2022 20:44:21 -0500 Subject: [PATCH 12/29] Subcommand argument validation supports additional values --- .../Languages/Mcfunction/Validator.ts | 41 +++++++++++++------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index 2d35f5bcb..1e3e1e1d1 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -61,7 +61,19 @@ export class CommandValidator { break } - // TODO: Check extra conditions + // Fail if there are additional values that are not met + if (targetArgument.additionalData != undefined) { + if ( + targetArgument.additionalData.values != undefined && + !targetArgument.additionalData.values.includes( + argument.word + ) + ) { + failed = true + + break + } + } } if (!failed) { @@ -220,6 +232,8 @@ export class CommandValidator { } } + // If need to validate a command we just validate all the other tokens and returns because we won't + // need to check any more tokens as they will be consumed within the new command if (targetArgument.type == 'command') { const leftTokens = tokens.slice(k, tokens.length) @@ -254,22 +268,23 @@ export class CommandValidator { } // Fail if there are additional values that are not met - if ( - targetArgument.additionalData != undefined && - targetArgument.additionalData.values != undefined && - !targetArgument.additionalData.values.includes( - argument.word - ) - ) { - definitions.splice(j, 1) + if (targetArgument.additionalData != undefined) { + if ( + targetArgument.additionalData.values != undefined && + !targetArgument.additionalData.values.includes( + argument.word + ) + ) { + definitions.splice(j, 1) - j-- + j-- - if (lastTokenError < k) lastTokenError = k + if (lastTokenError < k) lastTokenError = k - failed = true + failed = true - break + break + } } targetArgumentIndex++ From ed079a7011fc80538a9e589a5cec694db75ca065 Mon Sep 17 00:00:00 2001 From: Outer Cloud Studio Date: Wed, 24 Aug 2022 21:49:39 -0500 Subject: [PATCH 13/29] Warning Support + Schema Reference Support --- .../Languages/Mcfunction/Validator.ts | 111 +++++++++++++++++- 1 file changed, 106 insertions(+), 5 deletions(-) diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index 1e3e1e1d1..f4a398a67 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -6,6 +6,7 @@ import { import { CommandData, ICommandArgument } from './Data' import type { editor } from 'monaco-editor' import { useMonaco } from '/@/utils/libs/useMonaco' +import { RefSchema } from '/@/components/JSONSchema/Schema/Ref' export class CommandValidator { protected commandData: CommandData @@ -21,7 +22,13 @@ export class CommandValidator { endColumn: number word: string }[] - ) { + ): Promise<{ + passed: boolean + argumentsConsumedCount?: number | undefined + warnings: editor.IMarkerData[] + }> { + const { MarkerSeverity } = await useMonaco() + const subcommandName = leftTokens[0] let subcommandDefinitions = ( @@ -31,12 +38,17 @@ export class CommandValidator { if (subcommandDefinitions.length == 0) return { passed: false, + warnings: [], } let passedSubcommandDefinition = undefined + let warnings: editor.IMarkerData[] = [] + // Loop over every subcommand definition to check for a matching one for (const definition of subcommandDefinitions) { + let definitionWarnings = [] + let failed = false // Fail if there is not enought tokens to satisfy the definition @@ -61,8 +73,8 @@ export class CommandValidator { break } - // Fail if there are additional values that are not met if (targetArgument.additionalData != undefined) { + // Fail if there are additional values that are not met if ( targetArgument.additionalData.values != undefined && !targetArgument.additionalData.values.includes( @@ -73,23 +85,62 @@ export class CommandValidator { break } + + // Warn if unkown schema value + if ( + targetArgument.additionalData.schemaReference != + undefined + ) { + const referencePath = + targetArgument.additionalData.schemaReference + + const schemaReference = new RefSchema( + referencePath, + '$ref', + referencePath + ).getCompletionItems({}) + + if ( + schemaReference.find( + (reference) => reference.value == argument.word + ) == undefined + ) { + definitionWarnings.push({ + severity: MarkerSeverity.Warning, + message: `Unkown schema value "${argument.word}"`, + startLineNumber: -1, + startColumn: argument.startColumn + 1, + endLineNumber: -1, + endColumn: argument.endColumn + 1, + }) + } + } } } - if (!failed) { + // Only add definition if it is longer since it's the most likely correct one + if ( + !failed && + (passedSubcommandDefinition == undefined || + passedSubcommandDefinition.arguments.length < + definition.arguments.length) + ) { passedSubcommandDefinition = definition + warnings = definitionWarnings } } if (passedSubcommandDefinition == undefined) { return { passed: false, + warnings: [], } } else { return { passed: true, argumentsConsumedCount: passedSubcommandDefinition.arguments.length, + warnings, } } } @@ -100,6 +151,7 @@ export class CommandValidator { offset: number ): Promise { const { MarkerSeverity } = await useMonaco() + let diagnostics: editor.IMarkerData[] = [] if (line != undefined) tokens = tokenizeCommand(line).tokens @@ -156,6 +208,8 @@ export class CommandValidator { for (let j = 0; j < definitions.length; j++) { let failed = false + let warnings: editor.IMarkerData[] = [] + // Loop over every token that is not the command name let targetArgumentIndex = 0 for (let k = 1; k < tokens.length; k++) { @@ -187,6 +241,8 @@ export class CommandValidator { tokens.slice(k, tokens.length) ) + warnings = warnings.concat(result.warnings) + if (result.passed) { // Skip over tokens consumed in the subcommand validation k += result.argumentsConsumedCount! @@ -196,9 +252,11 @@ export class CommandValidator { let nextResult: { passed: boolean argumentsConsumedCount?: number + warnings: editor.IMarkerData[] } = { passed: true, argumentsConsumedCount: 0, + warnings: [], } while (nextResult.passed) { @@ -208,7 +266,9 @@ export class CommandValidator { ) if (nextResult.passed) { - const origK = k + warnings = warnings.concat( + nextResult.warnings + ) k += nextResult.argumentsConsumedCount! + 1 } @@ -245,6 +305,10 @@ export class CommandValidator { diagnostics = diagnostics.concat(result) + for (const warning of warnings) { + diagnostics.push(warning) + } + return diagnostics } @@ -267,8 +331,8 @@ export class CommandValidator { break } - // Fail if there are additional values that are not met if (targetArgument.additionalData != undefined) { + // Fail if there are additional values that are not met if ( targetArgument.additionalData.values != undefined && !targetArgument.additionalData.values.includes( @@ -285,6 +349,36 @@ export class CommandValidator { break } + + // Warn if unkown schema value + if ( + targetArgument.additionalData.schemaReference != + undefined + ) { + const referencePath = + targetArgument.additionalData.schemaReference + + const schemaReference = new RefSchema( + referencePath, + '$ref', + referencePath + ).getCompletionItems({}) + + if ( + schemaReference.find( + (reference) => reference.value == argument.word + ) == undefined + ) { + warnings.push({ + severity: MarkerSeverity.Warning, + message: `Unkown schema value "${argument.word}"`, + startLineNumber: -1, + startColumn: argument.startColumn + 1, + endLineNumber: -1, + endColumn: argument.endColumn + 1, + }) + } + } } targetArgumentIndex++ @@ -301,6 +395,13 @@ export class CommandValidator { if (lastTokenError < tokens.length - 1) lastTokenError = tokens.length - 1 + + // Continue to not add warnings to the diagnostics + continue + } + + for (const warning of warnings) { + diagnostics.push(warning) } } From c29896f872af782aaa8e1b1591df300ab1cbf3b3 Mon Sep 17 00:00:00 2001 From: Outer Cloud Studio Date: Wed, 24 Aug 2022 22:53:16 -0500 Subject: [PATCH 14/29] Change how diagnostics are stored Diagnostics and warnings are stored per definition until the definition passes. This allows warnings in one command to still appear if one of its command arguments has an error. --- .../Languages/Mcfunction/Validator.ts | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index f4a398a67..8977f1f4d 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -153,6 +153,7 @@ export class CommandValidator { const { MarkerSeverity } = await useMonaco() let diagnostics: editor.IMarkerData[] = [] + let warnings: editor.IMarkerData[] = [] if (line != undefined) tokens = tokenizeCommand(line).tokens @@ -204,11 +205,16 @@ export class CommandValidator { // We only need to record the error of the most farthest in token because that is the most likely variation the user was attempting to type let lastTokenError = 0 + let longestPassLength = -1 + // Loop over every definition and test for validness for (let j = 0; j < definitions.length; j++) { + console.log(`---- New Definition ---- ${commandName.word}`) + let failed = false - let warnings: editor.IMarkerData[] = [] + let definitionWarnings: editor.IMarkerData[] = [] + let definitionDiagnostics: editor.IMarkerData[] = [] // Loop over every token that is not the command name let targetArgumentIndex = 0 @@ -241,7 +247,9 @@ export class CommandValidator { tokens.slice(k, tokens.length) ) - warnings = warnings.concat(result.warnings) + definitionWarnings = definitionWarnings.concat( + result.warnings + ) if (result.passed) { // Skip over tokens consumed in the subcommand validation @@ -266,9 +274,10 @@ export class CommandValidator { ) if (nextResult.passed) { - warnings = warnings.concat( - nextResult.warnings - ) + definitionWarnings = + definitionWarnings.concat( + nextResult.warnings + ) k += nextResult.argumentsConsumedCount! + 1 } @@ -303,13 +312,11 @@ export class CommandValidator { offset + targetArgumentIndex ) - diagnostics = diagnostics.concat(result) + definitionDiagnostics = definitionDiagnostics.concat(result) - for (const warning of warnings) { - diagnostics.push(warning) - } + targetArgumentIndex++ - return diagnostics + break } const argumentType = await this.commandData.isArgumentType( @@ -369,7 +376,7 @@ export class CommandValidator { (reference) => reference.value == argument.word ) == undefined ) { - warnings.push({ + definitionWarnings.push({ severity: MarkerSeverity.Warning, message: `Unkown schema value "${argument.word}"`, startLineNumber: -1, @@ -400,9 +407,12 @@ export class CommandValidator { continue } - for (const warning of warnings) { - diagnostics.push(warning) - } + if (targetArgumentIndex < longestPassLength) break + + longestPassLength = targetArgumentIndex + + diagnostics = definitionDiagnostics + warnings = definitionWarnings } if (definitions.length == 0) { @@ -414,9 +424,12 @@ export class CommandValidator { endLineNumber: -1, endColumn: tokens[lastTokenError].endColumn + 1, }) + + // Return here since we don't want warnings added + return diagnostics } - return diagnostics + return diagnostics.concat(warnings) } async parse(content: string) { From a3639f62150ff38c6fb8fae544a4d425e2fd02ca Mon Sep 17 00:00:00 2001 From: Outer Cloud Studio Date: Wed, 24 Aug 2022 23:11:52 -0500 Subject: [PATCH 15/29] Optional arguments --- src/components/Languages/Mcfunction/Data.ts | 1 + .../Languages/Mcfunction/Validator.ts | 35 +++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/components/Languages/Mcfunction/Data.ts b/src/components/Languages/Mcfunction/Data.ts index 8af3f478b..b0e063c91 100644 --- a/src/components/Languages/Mcfunction/Data.ts +++ b/src/components/Languages/Mcfunction/Data.ts @@ -57,6 +57,7 @@ export interface ICommandArgument { schemaReference?: string values?: string[] } + isOptional: boolean } export interface ICompletionItem { diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index 8977f1f4d..ed4a5e9d5 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -211,6 +211,19 @@ export class CommandValidator { for (let j = 0; j < definitions.length; j++) { console.log(`---- New Definition ---- ${commandName.word}`) + let requiredArgurmentsCount = 0 + + for ( + requiredArgurmentsCount = 0; + requiredArgurmentsCount < definitions[j].arguments.length; + requiredArgurmentsCount++ + ) { + if ( + definitions[j].arguments[requiredArgurmentsCount].isOptional + ) + break + } + let failed = false let definitionWarnings: editor.IMarkerData[] = [] @@ -289,6 +302,12 @@ export class CommandValidator { continue } else { // Fail because subcommand doesn't match any definitions + if (targetArgument.isOptional) { + targetArgumentIndex++ + + break + } + definitions.splice(j, 1) j-- @@ -301,7 +320,7 @@ export class CommandValidator { } } - // If need to validate a command we just validate all the other tokens and returns because we won't + // If we need to validate a command we just validate all the other tokens and returns because we won't // need to check any more tokens as they will be consumed within the new command if (targetArgument.type == 'command') { const leftTokens = tokens.slice(k, tokens.length) @@ -327,6 +346,12 @@ export class CommandValidator { // Fail if type does not match if (argumentType != 'full') { + if (targetArgument.isOptional) { + targetArgumentIndex++ + + break + } + definitions.splice(j, 1) j-- @@ -346,6 +371,12 @@ export class CommandValidator { argument.word ) ) { + if (targetArgument.isOptional) { + targetArgumentIndex++ + + break + } + definitions.splice(j, 1) j-- @@ -395,7 +426,7 @@ export class CommandValidator { if (failed) continue // Fail if there are not enough tokens to satisfy definition - if (targetArgumentIndex < definitions[j].arguments.length) { + if (targetArgumentIndex < requiredArgurmentsCount) { definitions.splice(j, 1) j-- From b0bb122c9c9a4e14867b62bdc46b9005c15c4c5e Mon Sep 17 00:00:00 2001 From: Outer Cloud Studio Date: Thu, 25 Aug 2022 21:14:21 -0500 Subject: [PATCH 16/29] Fix option values --- .../Languages/Mcfunction/Validator.ts | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index ed4a5e9d5..7a6f3bcb4 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -302,12 +302,6 @@ export class CommandValidator { continue } else { // Fail because subcommand doesn't match any definitions - if (targetArgument.isOptional) { - targetArgumentIndex++ - - break - } - definitions.splice(j, 1) j-- @@ -346,12 +340,6 @@ export class CommandValidator { // Fail if type does not match if (argumentType != 'full') { - if (targetArgument.isOptional) { - targetArgumentIndex++ - - break - } - definitions.splice(j, 1) j-- @@ -371,12 +359,6 @@ export class CommandValidator { argument.word ) ) { - if (targetArgument.isOptional) { - targetArgumentIndex++ - - break - } - definitions.splice(j, 1) j-- From 207f1c16b86e53fd3c235d46486bba5939592565 Mon Sep 17 00:00:00 2001 From: Outer Cloud Studio Date: Thu, 25 Aug 2022 21:26:13 -0500 Subject: [PATCH 17/29] Translation Key Support + En Translations --- .../Languages/Mcfunction/Validator.ts | 46 +++++++++++++++++-- src/locales/en.json | 20 ++++++++ 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index 7a6f3bcb4..0f88fecb3 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -7,6 +7,7 @@ import { CommandData, ICommandArgument } from './Data' import type { editor } from 'monaco-editor' import { useMonaco } from '/@/utils/libs/useMonaco' import { RefSchema } from '/@/components/JSONSchema/Schema/Ref' +import { LocaleManager } from '/@/components/Locales/Manager' export class CommandValidator { protected commandData: CommandData @@ -107,7 +108,14 @@ export class CommandValidator { ) { definitionWarnings.push({ severity: MarkerSeverity.Warning, - message: `Unkown schema value "${argument.word}"`, + message: + LocaleManager.translate( + 'validation.mcfunction.unkown_schema.part1' + ) + + argument.word + + LocaleManager.translate( + 'validation.mcfunction.unkown_schema.part2' + ), startLineNumber: -1, startColumn: argument.startColumn + 1, endLineNumber: -1, @@ -167,7 +175,14 @@ export class CommandValidator { ) { diagnostics.push({ severity: MarkerSeverity.Error, - message: `Command "${commandName.word}" does not exist`, + message: + LocaleManager.translate( + 'validation.mcfunction.unknown_command.part1' + ) + + commandName.word + + LocaleManager.translate( + 'validation.mcfunction.unknown_command.part2' + ), startLineNumber: -1, startColumn: commandName.startColumn + 1, endLineNumber: -1, @@ -186,7 +201,14 @@ export class CommandValidator { if (tokens.length < 2) { diagnostics.push({ severity: MarkerSeverity.Error, - message: `Command "${commandName.word}" needs parameters`, + message: + LocaleManager.translate( + 'validation.mcfunction.missing_parameters.part1' + ) + + commandName.word + + LocaleManager.translate( + 'validation.mcfunction.missing_parameters.part2' + ), startLineNumber: -1, startColumn: commandName.startColumn + 1, endLineNumber: -1, @@ -391,7 +413,14 @@ export class CommandValidator { ) { definitionWarnings.push({ severity: MarkerSeverity.Warning, - message: `Unkown schema value "${argument.word}"`, + message: + LocaleManager.translate( + 'validation.mcfunction.unkown_schema.part1' + ) + + argument.word + + LocaleManager.translate( + 'validation.mcfunction.unkown_schema.part2' + ), startLineNumber: -1, startColumn: argument.startColumn + 1, endLineNumber: -1, @@ -431,7 +460,14 @@ export class CommandValidator { if (definitions.length == 0) { diagnostics.push({ severity: MarkerSeverity.Error, - message: `Argument "${tokens[lastTokenError].word}" is not valid here`, + message: + LocaleManager.translate( + 'validation.mcfunction.invalid_argument.part1' + ) + + tokens[lastTokenError].word + + LocaleManager.translate( + 'validation.mcfunction.invalid_argument.part2' + ), startLineNumber: -1, startColumn: tokens[lastTokenError].startColumn + 1, endLineNumber: -1, diff --git a/src/locales/en.json b/src/locales/en.json index ede18764a..feabd18ee 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1096,5 +1096,25 @@ "forceValue": "Force Value", "edit": "Edit" } + }, + "validation": { + "mcfunction": { + "invalid_argument": { + "part1": "Command \"", + "part2": "\" needs parameters" + }, + "unkown_command": { + "part1": "Command \"", + "part2": "\" does not exist" + }, + "missing_parameters": { + "part1": "Command \"", + "part2": "\" needs parameters" + }, + "unknown_schema": { + "part1": "Unkown schema value \"", + "part2": "\"" + } + } } } From 3a8c22adcc9b7d53d8c32c39007eba422424de00 Mon Sep 17 00:00:00 2001 From: Outer Cloud Studio Date: Thu, 25 Aug 2022 21:27:48 -0500 Subject: [PATCH 18/29] Fix invalid argument translation --- src/locales/en.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index feabd18ee..7c584081d 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1100,8 +1100,8 @@ "validation": { "mcfunction": { "invalid_argument": { - "part1": "Command \"", - "part2": "\" needs parameters" + "part1": "Argument \"", + "part2": "\" is not valid here" }, "unkown_command": { "part1": "Command \"", From ba9253a0d6bfc2612a04ef48cc7a7d48e532a5da Mon Sep 17 00:00:00 2001 From: Outer Cloud Studio Date: Thu, 25 Aug 2022 23:50:10 -0500 Subject: [PATCH 19/29] Validate Selectors --- .../Languages/Mcfunction/Validator.ts | 362 +++++++++++++++++- 1 file changed, 357 insertions(+), 5 deletions(-) diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index 0f88fecb3..3d8e52025 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -153,6 +153,323 @@ export class CommandValidator { } } + protected async parseSelector(selectorToken: { + startColumn: number + endColumn: number + word: string + }): Promise<{ + passed: boolean + diagnostic?: editor.IMarkerData + warnings: editor.IMarkerData[] + }> { + console.warn(`Parsing Selector ${selectorToken.word}`) + + const { MarkerSeverity } = await useMonaco() + + let warnings: editor.IMarkerData[] = [] + + let baseSelector = selectorToken.word.substring(0, 2) + + // Check for base selector, we later check @i to be @initiator + if (!['@a', '@p', '@r', '@e', '@s', '@i'].includes(baseSelector)) + return { + passed: false, + diagnostic: { + severity: MarkerSeverity.Error, + message: `Invalid selector base "${baseSelector}"`, + startLineNumber: -1, + startColumn: selectorToken.startColumn + 1, + endLineNumber: -1, + endColumn: selectorToken.endColumn + 1, + }, + warnings: [], + } + + if ( + baseSelector == '@i' && + selectorToken.word.substring(0, '@initiator'.length) != '@initiator' + ) + return { + passed: false, + diagnostic: { + severity: MarkerSeverity.Error, + message: `Invalid selector base "${baseSelector}"`, + startLineNumber: -1, + startColumn: selectorToken.startColumn + 1, + endLineNumber: -1, + endColumn: selectorToken.endColumn + 1, + }, + warnings: [], + } + + // If the selector is merely the base we can just pass + if (baseSelector == selectorToken.word) + return { + passed: true, + warnings: [], + } + + if (selectorToken.word[baseSelector.length] != '[') + return { + passed: false, + diagnostic: { + severity: MarkerSeverity.Error, + message: `Unexpected symbol "${ + selectorToken.word[baseSelector.length] + }". Expected "["`, + startLineNumber: -1, + startColumn: selectorToken.startColumn + 1, + endLineNumber: -1, + endColumn: selectorToken.endColumn + 1, + }, + warnings: [], + } + + if (!selectorToken.word.endsWith(']')) + return { + passed: false, + diagnostic: { + severity: MarkerSeverity.Error, + message: `Unexpected symbol "${ + selectorToken.word[selectorToken.word.length - 1] + }". Expected "]"`, + startLineNumber: -1, + startColumn: selectorToken.startColumn + 1, + endLineNumber: -1, + endColumn: selectorToken.endColumn + 1, + }, + warnings: [], + } + + let selectorArguments = selectorToken.word + .substring(baseSelector.length + 1, selectorToken.word.length - 1) + .split(',') + + // Check for weird comma syntax ex: ,, + if (selectorArguments.find((argument) => argument == '') != undefined) + return { + passed: false, + diagnostic: { + severity: MarkerSeverity.Error, + message: `Unexpected symbol ",". Expected a selector argument`, + startLineNumber: -1, + startColumn: selectorToken.startColumn + 1, + endLineNumber: -1, + endColumn: selectorToken.endColumn + 1, + }, + warnings: [], + } + + const selectorArgumentsSchema = + await this.commandData.getSelectorArgumentsSchema() + + // Store argument names that can't be used multiple times or where not negated when they need to be + let canNotUseNames = [] + + for (const argument of selectorArguments) { + console.log(`Validating command argument ${argument}`) + + // Fail if there is for somereason multiple = + if (argument.split('=').length - 1 != 1) + return { + passed: false, + diagnostic: { + severity: MarkerSeverity.Error, + message: `Expected 1 symbol "=". Got ${ + argument.split('=').length - 1 + }`, + startLineNumber: -1, + startColumn: selectorToken.startColumn + 1, + endLineNumber: -1, + endColumn: selectorToken.endColumn + 1, + }, + warnings: [], + } + + let argumentName = argument.split('=')[0] + let argumentValue = argument.split('=')[1] + + const argumentSchema = selectorArgumentsSchema.find( + (schema) => schema.argumentName == argumentName + ) + + if (argumentSchema == undefined) + return { + passed: false, + diagnostic: { + severity: MarkerSeverity.Error, + message: `Invalid selector argument "${argumentName}"`, + startLineNumber: -1, + startColumn: selectorToken.startColumn + 1, + endLineNumber: -1, + endColumn: selectorToken.endColumn + 1, + }, + warnings: [], + } + + const negated = argumentValue.startsWith('!') + const canNotUse = canNotUseNames.includes(argumentName) + + // Fail if negated and shouldn't be + if ( + negated && + (argumentSchema.additionalData == undefined || + !argumentSchema.additionalData.supportsNegation) + ) + return { + passed: false, + diagnostic: { + severity: MarkerSeverity.Error, + message: `Argument "${argumentName}" does not support negation`, + startLineNumber: -1, + startColumn: selectorToken.startColumn + 1, + endLineNumber: -1, + endColumn: selectorToken.endColumn + 1, + }, + warnings: [], + } + + // Check if this type should not be used again + if (canNotUse) { + if (argumentSchema.additionalData == undefined) + return { + passed: false, + diagnostic: { + severity: MarkerSeverity.Error, + message: `Argument "${argumentName}" does not support multiple instances`, + startLineNumber: -1, + startColumn: selectorToken.startColumn + 1, + endLineNumber: -1, + endColumn: selectorToken.endColumn + 1, + }, + warnings: [], + } + + if ( + argumentSchema.additionalData.multipleInstancesAllowed == + 'whenNegated' + ) { + return { + passed: false, + diagnostic: { + severity: MarkerSeverity.Error, + message: `Argument "${argumentName}" does not support multiple instances when not all negated`, + startLineNumber: -1, + startColumn: selectorToken.startColumn + 1, + endLineNumber: -1, + endColumn: selectorToken.endColumn + 1, + }, + warnings: [], + } + } + + return { + passed: false, + diagnostic: { + severity: MarkerSeverity.Error, + message: `Argument "${argumentName}" does not support multiple instances`, + startLineNumber: -1, + startColumn: selectorToken.startColumn + 1, + endLineNumber: -1, + endColumn: selectorToken.endColumn + 1, + }, + warnings: [], + } + } + + const argumentType = await this.commandData.isArgumentType( + argumentValue, + argumentSchema + ) + + // Fail if type does not match, NOTE: Should check scoredata in future when implemented + if (argumentType != 'full') { + return { + passed: false, + diagnostic: { + severity: MarkerSeverity.Error, + message: `Invalid selector argument value "${argumentValue}" for argument "${argumentName}"`, + startLineNumber: -1, + startColumn: selectorToken.startColumn + 1, + endLineNumber: -1, + endColumn: selectorToken.endColumn + 1, + }, + warnings: [], + } + } + + if (argumentSchema.additionalData != undefined) { + // Fail if there are additional values that are not met + if ( + argumentSchema.additionalData.values != undefined && + !argumentSchema.additionalData.values.includes( + argumentValue + ) + ) { + return { + passed: false, + diagnostic: { + severity: MarkerSeverity.Error, + message: `Invalid selector argument value "${argumentValue}" for argument "${argumentName}"`, + startLineNumber: -1, + startColumn: selectorToken.startColumn + 1, + endLineNumber: -1, + endColumn: selectorToken.endColumn + 1, + }, + warnings: [], + } + } + + // Warn if unkown schema value + if ( + argumentSchema.additionalData.schemaReference != undefined + ) { + const referencePath = + argumentSchema.additionalData.schemaReference + + const schemaReference = new RefSchema( + referencePath, + '$ref', + referencePath + ).getCompletionItems({}) + + if ( + schemaReference.find( + (reference) => reference.value == argumentValue + ) == undefined + ) { + warnings.push({ + severity: MarkerSeverity.Warning, + message: `Unkown schema value "${argumentValue}" for argument "${argumentName}"`, + startLineNumber: -1, + startColumn: selectorToken.startColumn + 1, + endLineNumber: -1, + endColumn: selectorToken.endColumn + 1, + }) + } + } + } + + if ( + argumentSchema.additionalData == undefined || + argumentSchema.additionalData.multipleInstancesAllowed == + undefined || + argumentSchema.additionalData.multipleInstancesAllowed == + 'never' || + (argumentSchema.additionalData.multipleInstancesAllowed == + 'whenNegated' && + !negated) + ) { + canNotUseNames.push(argumentName) + } + } + + return { + passed: true, + warnings, + } + } + protected async parseCommand( line: string | undefined, tokens: any[], @@ -247,6 +564,7 @@ export class CommandValidator { } let failed = false + let failedLongest = false let definitionWarnings: editor.IMarkerData[] = [] let definitionDiagnostics: editor.IMarkerData[] = [] @@ -260,7 +578,11 @@ export class CommandValidator { j-- - if (lastTokenError < k) lastTokenError = k + if (lastTokenError < k) { + failedLongest = true + + lastTokenError = k + } failed = true @@ -328,7 +650,11 @@ export class CommandValidator { j-- - if (lastTokenError < k) lastTokenError = k + if (lastTokenError < k) { + failedLongest = true + + lastTokenError = k + } failed = true @@ -366,13 +692,31 @@ export class CommandValidator { j-- - if (lastTokenError < k) lastTokenError = k + if (lastTokenError < k) { + failedLongest = true + + lastTokenError = k + } failed = true break } + // Validate selector but don't completely fail if selector fail so rest of command can validate as well + if (targetArgument.type == 'selector') { + const result = await this.parseSelector(argument) + + console.log(result) + + if (result.diagnostic != undefined) + definitionDiagnostics.push(result.diagnostic) + + definitionWarnings = definitionWarnings.concat( + result.warnings + ) + } + if (targetArgument.additionalData != undefined) { // Fail if there are additional values that are not met if ( @@ -385,7 +729,11 @@ export class CommandValidator { j-- - if (lastTokenError < k) lastTokenError = k + if (lastTokenError < k) { + failedLongest = true + + lastTokenError = k + } failed = true @@ -434,7 +782,11 @@ export class CommandValidator { } // Skip if already failed in case this leaves an undefined reference - if (failed) continue + if (failed) { + if (failedLongest) diagnostics = definitionDiagnostics + + continue + } // Fail if there are not enough tokens to satisfy definition if (targetArgumentIndex < requiredArgurmentsCount) { From 89f901dc7591e1d26ba6c90a34a02099f8d4002d Mon Sep 17 00:00:00 2001 From: Outer Cloud Studio Date: Fri, 26 Aug 2022 00:04:47 -0500 Subject: [PATCH 20/29] Restructure Json Data Needs further testing to see if this functions properly --- .../Languages/Mcfunction/Validator.ts | 48 ++++++++++++++++--- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index 3d8e52025..eefff3ffa 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -162,8 +162,6 @@ export class CommandValidator { diagnostic?: editor.IMarkerData warnings: editor.IMarkerData[] }> { - console.warn(`Parsing Selector ${selectorToken.word}`) - const { MarkerSeverity } = await useMonaco() let warnings: editor.IMarkerData[] = [] @@ -267,8 +265,6 @@ export class CommandValidator { let canNotUseNames = [] for (const argument of selectorArguments) { - console.log(`Validating command argument ${argument}`) - // Fail if there is for somereason multiple = if (argument.split('=').length - 1 != 1) return { @@ -482,6 +478,48 @@ export class CommandValidator { if (line != undefined) tokens = tokenizeCommand(line).tokens + console.warn('Tokens!') + console.log(JSON.parse(JSON.stringify(tokens))) + + // Reconstruct JSON because tokenizer doesn't handle this well + for (let i = 0; i < tokens.length; i++) { + if (tokens[i - 1] != undefined) { + // if we get a case where tokens are like "property", :"value" then we combine them + if ( + tokens[i].word.startsWith(':') && + tokens[i - 1].word[tokens[i - 1].word.length - 1] == '"' + ) { + tokens.splice(i - 1, 2, { + startColumn: tokens[i - 1].startColumn, + endColumn: tokens[i].endColumn, + word: tokens[i - 1].word + tokens[i].word, + }) + + i-- + + continue + } + + if ( + tokens[i].word == '}' && + tokens[i - 1].word.startsWith('{') + ) { + tokens.splice(i - 1, 2, { + startColumn: tokens[i - 1].startColumn, + endColumn: tokens[i].endColumn, + word: tokens[i - 1].word + tokens[i].word, + }) + + i-- + + continue + } + } + } + + console.warn('Tokens Restructured!') + console.log(JSON.parse(JSON.stringify(tokens))) + const commandName = tokens[0] // If first word is emtpy then this is an empty line @@ -707,8 +745,6 @@ export class CommandValidator { if (targetArgument.type == 'selector') { const result = await this.parseSelector(argument) - console.log(result) - if (result.diagnostic != undefined) definitionDiagnostics.push(result.diagnostic) From 10b6f8e331213b52edb497091c83db2374e0b8f7 Mon Sep 17 00:00:00 2001 From: Outer Cloud Studio Date: Sat, 27 Aug 2022 18:35:00 -0500 Subject: [PATCH 21/29] Score Data --- src/components/Languages/Mcfunction/Data.ts | 1 + .../Languages/Mcfunction/Validator.ts | 52 ++++++++++++++++--- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/components/Languages/Mcfunction/Data.ts b/src/components/Languages/Mcfunction/Data.ts index b0e063c91..c4175d0c9 100644 --- a/src/components/Languages/Mcfunction/Data.ts +++ b/src/components/Languages/Mcfunction/Data.ts @@ -43,6 +43,7 @@ export type TArgumentType = | 'command' | 'scoreData' | 'subcommand' + | 'integerRange' | `$${string}` /** diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index eefff3ffa..c5f7494ae 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -153,6 +153,38 @@ export class CommandValidator { } } + protected async parseScoreData(token: string): Promise { + if (!token.startsWith('{')) return false + + if (!token.endsWith('}')) return false + + let pieces = token.substring(1, token.length - 1).split(',') + + // Check for weird comma syntax ex: ,, + if (pieces.find((argument) => argument == '') != undefined) return false + + for (const piece of pieces) { + const scoreName = piece.split('=')[0] + const scoreValue = piece.split('=').slice(1).join('=') + + if (scoreValue == undefined) return false + + let argumentType = await this.commandData.isArgumentType( + scoreValue, + { + argumentName: 'scoreData', + description: 'scoreDataParser', + type: 'integerRange', + isOptional: false, + } + ) + + if (argumentType != 'full') return false + } + + return true + } + protected async parseSelector(selectorToken: { startColumn: number endColumn: number @@ -265,15 +297,13 @@ export class CommandValidator { let canNotUseNames = [] for (const argument of selectorArguments) { - // Fail if there is for somereason multiple = - if (argument.split('=').length - 1 != 1) + // Fail if there is for somereason no = + if (argument.split('=').length - 1 < 1) return { passed: false, diagnostic: { severity: MarkerSeverity.Error, - message: `Expected 1 symbol "=". Got ${ - argument.split('=').length - 1 - }`, + message: `Expected symbol "="`, startLineNumber: -1, startColumn: selectorToken.startColumn + 1, endLineNumber: -1, @@ -283,7 +313,7 @@ export class CommandValidator { } let argumentName = argument.split('=')[0] - let argumentValue = argument.split('=')[1] + let argumentValue = argument.split('=').slice(1).join('=') const argumentSchema = selectorArgumentsSchema.find( (schema) => schema.argumentName == argumentName @@ -373,11 +403,18 @@ export class CommandValidator { } } - const argumentType = await this.commandData.isArgumentType( + let argumentType = await this.commandData.isArgumentType( argumentValue, argumentSchema ) + // We need to parse scoreData on its own because the normal argument type checker seems to not work on it + if ( + argumentSchema.type == 'scoreData' && + (await this.parseScoreData(argumentValue)) + ) + argumentType = 'full' + // Fail if type does not match, NOTE: Should check scoredata in future when implemented if (argumentType != 'full') { return { @@ -500,6 +537,7 @@ export class CommandValidator { continue } + // add the beginning and ending of a json data together if ( tokens[i].word == '}' && tokens[i - 1].word.startsWith('{') From 449ef39e3a03a0299de32072e114861aad71d7b9 Mon Sep 17 00:00:00 2001 From: Outer Cloud Studio Date: Sat, 27 Aug 2022 19:08:49 -0500 Subject: [PATCH 22/29] Block State --- .../Languages/Mcfunction/Validator.ts | 94 ++++++++++++++++++- 1 file changed, 89 insertions(+), 5 deletions(-) diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index c5f7494ae..aeba7a88f 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -62,11 +62,18 @@ export class CommandValidator { const argument = leftTokens[j + 1] const targetArgument = definition.arguments[j] - const argumentType = await this.commandData.isArgumentType( + let argumentType = await this.commandData.isArgumentType( argument.word, targetArgument ) + if ( + targetArgument.type == 'blockState' && + argumentType == 'full' && + !(await this.parseBlockState(argument.word)) + ) + argumentType = 'none' + // Fail if type does not match if (argumentType != 'full') { failed = true @@ -185,6 +192,56 @@ export class CommandValidator { return true } + protected async parseBlockState(token: string): Promise { + console.warn('BlockState!') + + if (!token.startsWith('[')) return false + + if (!token.endsWith(']')) return false + + const pieces = token.substring(1, token.length - 1).split(',') + + // Check for weird comma syntax ex: ,, + if (pieces.find((argument) => argument == '') != undefined) return false + + for (const piece of pieces) { + const scoreName = piece.split(':')[0] + const scoreValue = piece.split(':').slice(1).join(':') + + if (scoreValue == undefined) return false + + const isString = + (await this.commandData.isArgumentType(scoreValue, { + argumentName: 'scoreData', + description: 'scoreDataParser', + type: 'string', + isOptional: false, + })) == 'full' && + (/([a-zA-Z])/.test(scoreValue) || scoreValue == '""') && + ((scoreValue.startsWith('"') && scoreValue.endsWith('"')) || + (!scoreValue.startsWith('"') && + !scoreValue.endsWith('"'))) && + (scoreValue.split('"').length - 1 == 2 || + scoreValue.split('"').length - 1 == 0) + + const isNumber = + (await this.commandData.isArgumentType(scoreValue, { + argumentName: 'scoreData', + description: 'scoreDataParser', + type: 'number', + isOptional: false, + })) == 'full' + + console.warn(scoreValue) + console.log(isString) + console.log(isNumber) + + if (!isString && !isNumber) return false + } + + return true + } + protected async parseSelector(selectorToken: { startColumn: number endColumn: number @@ -537,10 +594,28 @@ export class CommandValidator { continue } - // add the beginning and ending of a json data together + // if we get a case where tokens are like ["state":"a","state":"b" then we combine them + if ( + tokens[i].word.startsWith(',') && + tokens[i - 1].word[tokens[i - 1].word.length - 1] == '"' + ) { + tokens.splice(i - 1, 2, { + startColumn: tokens[i - 1].startColumn, + endColumn: tokens[i].endColumn, + word: tokens[i - 1].word + tokens[i].word, + }) + + i-- + + continue + } + + // add the beginning and ending of a json data or scoreData together if ( - tokens[i].word == '}' && - tokens[i - 1].word.startsWith('{') + (tokens[i].word == '}' && + tokens[i - 1].word.startsWith('{')) || + (tokens[i].word == ']' && + tokens[i - 1].word.startsWith('[')) ) { tokens.splice(i - 1, 2, { startColumn: tokens[i - 1].startColumn, @@ -756,12 +831,21 @@ export class CommandValidator { break } - const argumentType = await this.commandData.isArgumentType( + let argumentType = await this.commandData.isArgumentType( argument.word, targetArgument, commandName.word ) + console.log(argumentType) + + if ( + targetArgument.type == 'blockState' && + argumentType == 'full' && + !(await this.parseBlockState(argument.word)) + ) + argumentType = 'none' + // Fail if type does not match if (argumentType != 'full') { definitions.splice(j, 1) From 9d49dedd2cf75b4d8936fb33e3430f6082cfc3f1 Mon Sep 17 00:00:00 2001 From: Outer Cloud Studio Date: Sat, 27 Aug 2022 19:13:43 -0500 Subject: [PATCH 23/29] Remove Debug Logs --- .../Languages/Mcfunction/Validator.ts | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index aeba7a88f..af5720b1a 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -193,8 +193,6 @@ export class CommandValidator { } protected async parseBlockState(token: string): Promise { - console.warn('BlockState!') - if (!token.startsWith('[')) return false if (!token.endsWith(']')) return false @@ -231,11 +229,6 @@ export class CommandValidator { type: 'number', isOptional: false, })) == 'full' - - console.warn(scoreValue) - console.log(isString) - console.log(isNumber) - if (!isString && !isNumber) return false } @@ -572,9 +565,6 @@ export class CommandValidator { if (line != undefined) tokens = tokenizeCommand(line).tokens - console.warn('Tokens!') - console.log(JSON.parse(JSON.stringify(tokens))) - // Reconstruct JSON because tokenizer doesn't handle this well for (let i = 0; i < tokens.length; i++) { if (tokens[i - 1] != undefined) { @@ -630,9 +620,6 @@ export class CommandValidator { } } - console.warn('Tokens Restructured!') - console.log(JSON.parse(JSON.stringify(tokens))) - const commandName = tokens[0] // If first word is emtpy then this is an empty line @@ -661,8 +648,6 @@ export class CommandValidator { return diagnostics } - console.log(`Validating command ${commandName.word}!`) - // Remove empty tokens as to not confuse the argument checker tokens = tokens.filter((token) => token.word != '') @@ -699,8 +684,6 @@ export class CommandValidator { // Loop over every definition and test for validness for (let j = 0; j < definitions.length; j++) { - console.log(`---- New Definition ---- ${commandName.word}`) - let requiredArgurmentsCount = 0 for ( @@ -745,10 +728,6 @@ export class CommandValidator { const targetArgument = definitions[j].arguments[targetArgumentIndex] - console.log( - `Validating agument of type ${targetArgument.type} onto ${argument.word}!` - ) - if (targetArgument.type == 'subcommand') { const result = await this.parseSubcommand( commandName.word, @@ -837,8 +816,6 @@ export class CommandValidator { commandName.word ) - console.log(argumentType) - if ( targetArgument.type == 'blockState' && argumentType == 'full' && @@ -992,8 +969,6 @@ export class CommandValidator { } async parse(content: string) { - console.log('Validating!') - // Split content into lines const lines = content.split('\n') const diagnostics: editor.IMarkerData[] = [] From d03217dd7a03fe680add41eca0df3d135e688c67 Mon Sep 17 00:00:00 2001 From: Joelant05 <64587014+Joelant05@users.noreply.github.com> Date: Sun, 28 Aug 2022 17:42:36 +0100 Subject: [PATCH 24/29] upd: translate all function validation messages --- .../Languages/Mcfunction/Validator.ts | 154 +++++++++++------- src/components/Locales/Manager.ts | 11 ++ src/locales/en.json | 46 ++++-- 3 files changed, 140 insertions(+), 71 deletions(-) diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index af5720b1a..84ab2b9f8 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -7,7 +7,10 @@ import { CommandData, ICommandArgument } from './Data' import type { editor } from 'monaco-editor' import { useMonaco } from '/@/utils/libs/useMonaco' import { RefSchema } from '/@/components/JSONSchema/Schema/Ref' -import { LocaleManager } from '/@/components/Locales/Manager' +import { + translateWithInsertions as twi, + translate as t, +} from '/@/components/Locales/Manager' export class CommandValidator { protected commandData: CommandData @@ -115,14 +118,10 @@ export class CommandValidator { ) { definitionWarnings.push({ severity: MarkerSeverity.Warning, - message: - LocaleManager.translate( - 'validation.mcfunction.unkown_schema.part1' - ) + - argument.word + - LocaleManager.translate( - 'validation.mcfunction.unkown_schema.part2' - ), + message: twi( + 'validation.mcfunction.unknownSchema.name', + [`"${argument.word}"`] + ), startLineNumber: -1, startColumn: argument.startColumn + 1, endLineNumber: -1, @@ -256,7 +255,10 @@ export class CommandValidator { passed: false, diagnostic: { severity: MarkerSeverity.Error, - message: `Invalid selector base "${baseSelector}"`, + message: twi( + 'validation.mcfunction.invalidSelectorBase.name', + [`"${baseSelector}"`] + ), startLineNumber: -1, startColumn: selectorToken.startColumn + 1, endLineNumber: -1, @@ -273,7 +275,10 @@ export class CommandValidator { passed: false, diagnostic: { severity: MarkerSeverity.Error, - message: `Invalid selector base "${baseSelector}"`, + message: twi( + 'validation.mcfunction.invalidSelectorBase.name', + [`"${baseSelector}"`] + ), startLineNumber: -1, startColumn: selectorToken.startColumn + 1, endLineNumber: -1, @@ -294,9 +299,10 @@ export class CommandValidator { passed: false, diagnostic: { severity: MarkerSeverity.Error, - message: `Unexpected symbol "${ - selectorToken.word[baseSelector.length] - }". Expected "["`, + message: twi( + 'validation.mcfunction.unexpectedSymbol.name', + [`"${selectorToken.word[baseSelector.length]}"`, '"["'] + ), startLineNumber: -1, startColumn: selectorToken.startColumn + 1, endLineNumber: -1, @@ -310,9 +316,10 @@ export class CommandValidator { passed: false, diagnostic: { severity: MarkerSeverity.Error, - message: `Unexpected symbol "${ - selectorToken.word[selectorToken.word.length - 1] - }". Expected "]"`, + message: twi( + 'validation.mcfunction.unexpectedSymbol.name', + [`"${selectorToken.word[baseSelector.length]}"`, '"]"'] + ), startLineNumber: -1, startColumn: selectorToken.startColumn + 1, endLineNumber: -1, @@ -331,7 +338,15 @@ export class CommandValidator { passed: false, diagnostic: { severity: MarkerSeverity.Error, - message: `Unexpected symbol ",". Expected a selector argument`, + message: twi( + 'validation.mcfunction.unexpectedSymbol.name', + [ + `"${selectorToken.word[baseSelector.length]}"`, + `"${t( + 'validation.mcfunction.tokens.selectorArgument' + )}"`, + ] + ), startLineNumber: -1, startColumn: selectorToken.startColumn + 1, endLineNumber: -1, @@ -353,7 +368,10 @@ export class CommandValidator { passed: false, diagnostic: { severity: MarkerSeverity.Error, - message: `Expected symbol "="`, + message: twi( + 'validation.mcfunction.unexpectedSymbol.name', + [`"${argument}"`, `"="`] + ), startLineNumber: -1, startColumn: selectorToken.startColumn + 1, endLineNumber: -1, @@ -374,7 +392,10 @@ export class CommandValidator { passed: false, diagnostic: { severity: MarkerSeverity.Error, - message: `Invalid selector argument "${argumentName}"`, + message: twi( + 'validation.mcfunction.invalidSelectorArgument.name', + [`"${argumentName}"`] + ), startLineNumber: -1, startColumn: selectorToken.startColumn + 1, endLineNumber: -1, @@ -396,7 +417,13 @@ export class CommandValidator { passed: false, diagnostic: { severity: MarkerSeverity.Error, - message: `Argument "${argumentName}" does not support negation`, + message: twi( + 'validation.mcfunction.argumentNoSupport.name', + [ + `"${argumentName}"`, + t('validation.mcfunction.conditions.negation'), + ] + ), startLineNumber: -1, startColumn: selectorToken.startColumn + 1, endLineNumber: -1, @@ -412,7 +439,15 @@ export class CommandValidator { passed: false, diagnostic: { severity: MarkerSeverity.Error, - message: `Argument "${argumentName}" does not support multiple instances`, + message: twi( + 'validation.mcfunction.argumentNoSupport.name', + [ + `"${argumentName}"`, + t( + 'validation.mcfunction.conditions.multipleInstances' + ), + ] + ), startLineNumber: -1, startColumn: selectorToken.startColumn + 1, endLineNumber: -1, @@ -429,7 +464,10 @@ export class CommandValidator { passed: false, diagnostic: { severity: MarkerSeverity.Error, - message: `Argument "${argumentName}" does not support multiple instances when not all negated`, + message: twi( + 'validation.mcfunction.argumentNoSupport.bothConditions', + [`"${argumentName}"`] + ), startLineNumber: -1, startColumn: selectorToken.startColumn + 1, endLineNumber: -1, @@ -443,7 +481,15 @@ export class CommandValidator { passed: false, diagnostic: { severity: MarkerSeverity.Error, - message: `Argument "${argumentName}" does not support multiple instances`, + message: twi( + 'validation.mcfunction.argumentNoSupport.name', + [ + `"${argumentName}"`, + t( + 'validation.mcfunction.conditions.multipleInstances' + ), + ] + ), startLineNumber: -1, startColumn: selectorToken.startColumn + 1, endLineNumber: -1, @@ -471,7 +517,10 @@ export class CommandValidator { passed: false, diagnostic: { severity: MarkerSeverity.Error, - message: `Invalid selector argument value "${argumentValue}" for argument "${argumentName}"`, + message: twi( + 'validation.mcfunction.invalidSelectorArgumentValue.name', + [`"${argumentValue}"`, `"${argumentName}"`] + ), startLineNumber: -1, startColumn: selectorToken.startColumn + 1, endLineNumber: -1, @@ -493,7 +542,10 @@ export class CommandValidator { passed: false, diagnostic: { severity: MarkerSeverity.Error, - message: `Invalid selector argument value "${argumentValue}" for argument "${argumentName}"`, + message: twi( + 'validation.mcfunction.invalidSelectorArgumentValue.name', + [`"${argumentValue}"`, `"${argumentName}"`] + ), startLineNumber: -1, startColumn: selectorToken.startColumn + 1, endLineNumber: -1, @@ -523,7 +575,10 @@ export class CommandValidator { ) { warnings.push({ severity: MarkerSeverity.Warning, - message: `Unkown schema value "${argumentValue}" for argument "${argumentName}"`, + message: twi( + 'validation.mcfunction.unknownSchemaInArgument.name', + [`"${argumentValue}"`, `"${argumentName}"`] + ), startLineNumber: -1, startColumn: selectorToken.startColumn + 1, endLineNumber: -1, @@ -630,14 +685,9 @@ export class CommandValidator { ) { diagnostics.push({ severity: MarkerSeverity.Error, - message: - LocaleManager.translate( - 'validation.mcfunction.unknown_command.part1' - ) + - commandName.word + - LocaleManager.translate( - 'validation.mcfunction.unknown_command.part2' - ), + message: twi('validation.mcfunction.unknownCommand.name', [ + `"${commandName.word}"`, + ]), startLineNumber: -1, startColumn: commandName.startColumn + 1, endLineNumber: -1, @@ -654,14 +704,9 @@ export class CommandValidator { if (tokens.length < 2) { diagnostics.push({ severity: MarkerSeverity.Error, - message: - LocaleManager.translate( - 'validation.mcfunction.missing_parameters.part1' - ) + - commandName.word + - LocaleManager.translate( - 'validation.mcfunction.missing_parameters.part2' - ), + message: twi('validation.mcfunction.missingArguments.name', [ + `"${commandName.word}"`, + ]), startLineNumber: -1, startColumn: commandName.startColumn + 1, endLineNumber: -1, @@ -896,14 +941,10 @@ export class CommandValidator { ) { definitionWarnings.push({ severity: MarkerSeverity.Warning, - message: - LocaleManager.translate( - 'validation.mcfunction.unkown_schema.part1' - ) + - argument.word + - LocaleManager.translate( - 'validation.mcfunction.unkown_schema.part2' - ), + message: twi( + 'validation.mcfunction.unknownSchema.name', + [`"${commandName.word}"`] + ), startLineNumber: -1, startColumn: argument.startColumn + 1, endLineNumber: -1, @@ -947,14 +988,9 @@ export class CommandValidator { if (definitions.length == 0) { diagnostics.push({ severity: MarkerSeverity.Error, - message: - LocaleManager.translate( - 'validation.mcfunction.invalid_argument.part1' - ) + - tokens[lastTokenError].word + - LocaleManager.translate( - 'validation.mcfunction.invalid_argument.part2' - ), + message: twi('validation.mcfunction.invalidArgument.name', [ + `"${tokens[lastTokenError].word}"`, + ]), startLineNumber: -1, startColumn: tokens[lastTokenError].startColumn + 1, endLineNumber: -1, diff --git a/src/components/Locales/Manager.ts b/src/components/Locales/Manager.ts index a2ff2c334..9691c1a88 100644 --- a/src/components/Locales/Manager.ts +++ b/src/components/Locales/Manager.ts @@ -105,6 +105,17 @@ export function translate(key?: string) { return LocaleManager.translate(key) } +export function translateWithInsertions(key?: string, insert?: string[]) { + let translation = LocaleManager.translate(key) + if (!insert) return translation + + for (let i = 0; i <= insert.length; i++) { + translation = translation?.replace(`{{$${i + 1}}}`, insert[i]) + } + + return translation +} + function clone(obj: any) { if (typeof window.structuredClone === 'function') return window.structuredClone(obj) diff --git a/src/locales/en.json b/src/locales/en.json index 7c584081d..740390951 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1099,21 +1099,43 @@ }, "validation": { "mcfunction": { - "invalid_argument": { - "part1": "Argument \"", - "part2": "\" is not valid here" + "invalidArgument": { + "name": "Argument {{$1}} is not valid here" }, - "unkown_command": { - "part1": "Command \"", - "part2": "\" does not exist" + "unknownCommand": { + "name": "Command {{$1}} does not exist" }, - "missing_parameters": { - "part1": "Command \"", - "part2": "\" needs parameters" + "missingArguments": { + "name": "Command {{$1}} requires more arguments" }, - "unknown_schema": { - "part1": "Unkown schema value \"", - "part2": "\"" + "unknownSchema": { + "name": "Unknown schema value {{$1}}" + }, + "unknownSchemaInArgument": { + "name": "Unknown schema value {{$1}} for argument {{$2}}" + }, + "invalidSelectorBase": { + "name": "Invalid target selector base {{$1}}" + }, + "unexpectedSymbol": { + "name": "Unexpected symbol {{$1}}. Expected {{$2}}" + }, + "invalidSelectorArgument": { + "name": "Invalid selector argument {{$1}}" + }, + "argumentNoSupport": { + "name": "Argument {{$1}} doesn not support {{$2}}", + "bothConditions": "Argument {{$1}} does not support multiple instances when not all negated" + }, + "invalidSelectorArgumentValue": { + "name": "Invalid selector argument value {{$1}} for argument {{$2}}" + }, + "conditions": { + "negation": "negation", + "multipleInstances": "multiple instances" + }, + "tokens": { + "selectorArgument": "selector argument" } } } From 18a126cdbbf42b63f9053d83a8c1c3a50448a4b8 Mon Sep 17 00:00:00 2001 From: Joelant05 <64587014+Joelant05@users.noreply.github.com> Date: Sun, 28 Aug 2022 21:27:20 +0100 Subject: [PATCH 25/29] upd: tweaks to code style --- .../Languages/Mcfunction/Validator.ts | 83 +++++++++---------- 1 file changed, 37 insertions(+), 46 deletions(-) diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index 84ab2b9f8..ff67fe25e 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -28,7 +28,7 @@ export class CommandValidator { }[] ): Promise<{ passed: boolean - argumentsConsumedCount?: number | undefined + argumentsConsumedCount?: number warnings: editor.IMarkerData[] }> { const { MarkerSeverity } = await useMonaco() @@ -45,7 +45,7 @@ export class CommandValidator { warnings: [], } - let passedSubcommandDefinition = undefined + let passedSubcommandDefinition let warnings: editor.IMarkerData[] = [] @@ -84,10 +84,10 @@ export class CommandValidator { break } - if (targetArgument.additionalData != undefined) { + if (targetArgument.additionalData) { // Fail if there are additional values that are not met if ( - targetArgument.additionalData.values != undefined && + targetArgument.additionalData.values && !targetArgument.additionalData.values.includes( argument.word ) @@ -97,11 +97,8 @@ export class CommandValidator { break } - // Warn if unkown schema value - if ( - targetArgument.additionalData.schemaReference != - undefined - ) { + // Warn if unknown schema value + if (targetArgument.additionalData.schemaReference) { const referencePath = targetArgument.additionalData.schemaReference @@ -112,9 +109,9 @@ export class CommandValidator { ).getCompletionItems({}) if ( - schemaReference.find( + !schemaReference.find( (reference) => reference.value == argument.word - ) == undefined + ) ) { definitionWarnings.push({ severity: MarkerSeverity.Warning, @@ -135,7 +132,7 @@ export class CommandValidator { // Only add definition if it is longer since it's the most likely correct one if ( !failed && - (passedSubcommandDefinition == undefined || + (!passedSubcommandDefinition || passedSubcommandDefinition.arguments.length < definition.arguments.length) ) { @@ -144,7 +141,7 @@ export class CommandValidator { } } - if (passedSubcommandDefinition == undefined) { + if (!passedSubcommandDefinition) { return { passed: false, warnings: [], @@ -167,13 +164,13 @@ export class CommandValidator { let pieces = token.substring(1, token.length - 1).split(',') // Check for weird comma syntax ex: ,, - if (pieces.find((argument) => argument == '') != undefined) return false + if (pieces.find((argument) => argument == '')) return false for (const piece of pieces) { const scoreName = piece.split('=')[0] const scoreValue = piece.split('=').slice(1).join('=') - if (scoreValue == undefined) return false + if (!scoreValue) return false let argumentType = await this.commandData.isArgumentType( scoreValue, @@ -199,13 +196,13 @@ export class CommandValidator { const pieces = token.substring(1, token.length - 1).split(',') // Check for weird comma syntax ex: ,, - if (pieces.find((argument) => argument == '') != undefined) return false + if (pieces.find((argument) => argument == '')) return false for (const piece of pieces) { const scoreName = piece.split(':')[0] const scoreValue = piece.split(':').slice(1).join(':') - if (scoreValue == undefined) return false + if (!scoreValue) return false const isString = (await this.commandData.isArgumentType(scoreValue, { @@ -333,7 +330,7 @@ export class CommandValidator { .split(',') // Check for weird comma syntax ex: ,, - if (selectorArguments.find((argument) => argument == '') != undefined) + if (selectorArguments.find((argument) => argument == '')) return { passed: false, diagnostic: { @@ -387,7 +384,7 @@ export class CommandValidator { (schema) => schema.argumentName == argumentName ) - if (argumentSchema == undefined) + if (!argumentSchema) return { passed: false, diagnostic: { @@ -410,7 +407,7 @@ export class CommandValidator { // Fail if negated and shouldn't be if ( negated && - (argumentSchema.additionalData == undefined || + (!argumentSchema.additionalData || !argumentSchema.additionalData.supportsNegation) ) return { @@ -434,7 +431,7 @@ export class CommandValidator { // Check if this type should not be used again if (canNotUse) { - if (argumentSchema.additionalData == undefined) + if (!argumentSchema.additionalData) return { passed: false, diagnostic: { @@ -530,10 +527,10 @@ export class CommandValidator { } } - if (argumentSchema.additionalData != undefined) { + if (argumentSchema.additionalData) { // Fail if there are additional values that are not met if ( - argumentSchema.additionalData.values != undefined && + argumentSchema.additionalData.values && !argumentSchema.additionalData.values.includes( argumentValue ) @@ -555,10 +552,8 @@ export class CommandValidator { } } - // Warn if unkown schema value - if ( - argumentSchema.additionalData.schemaReference != undefined - ) { + // Warn if unknown schema value + if (argumentSchema.additionalData.schemaReference) { const referencePath = argumentSchema.additionalData.schemaReference @@ -569,9 +564,9 @@ export class CommandValidator { ).getCompletionItems({}) if ( - schemaReference.find( + !schemaReference.find( (reference) => reference.value == argumentValue - ) == undefined + ) ) { warnings.push({ severity: MarkerSeverity.Warning, @@ -589,9 +584,8 @@ export class CommandValidator { } if ( - argumentSchema.additionalData == undefined || - argumentSchema.additionalData.multipleInstancesAllowed == - undefined || + !argumentSchema.additionalData || + !argumentSchema.additionalData.multipleInstancesAllowed || argumentSchema.additionalData.multipleInstancesAllowed == 'never' || (argumentSchema.additionalData.multipleInstancesAllowed == @@ -618,11 +612,11 @@ export class CommandValidator { let diagnostics: editor.IMarkerData[] = [] let warnings: editor.IMarkerData[] = [] - if (line != undefined) tokens = tokenizeCommand(line).tokens + if (line) tokens = tokenizeCommand(line).tokens // Reconstruct JSON because tokenizer doesn't handle this well for (let i = 0; i < tokens.length; i++) { - if (tokens[i - 1] != undefined) { + if (tokens[i - 1]) { // if we get a case where tokens are like "property", :"value" then we combine them if ( tokens[i].word.startsWith(':') && @@ -677,8 +671,8 @@ export class CommandValidator { const commandName = tokens[0] - // If first word is emtpy then this is an empty line - if (commandName.word == '') return diagnostics + // If first word is empty then this is an empty line + if (!commandName || commandName.word == '') return diagnostics if ( !(await this.commandData.allCommands()).includes(commandName.word) @@ -889,7 +883,7 @@ export class CommandValidator { if (targetArgument.type == 'selector') { const result = await this.parseSelector(argument) - if (result.diagnostic != undefined) + if (result.diagnostic) definitionDiagnostics.push(result.diagnostic) definitionWarnings = definitionWarnings.concat( @@ -897,10 +891,10 @@ export class CommandValidator { ) } - if (targetArgument.additionalData != undefined) { + if (targetArgument.additionalData) { // Fail if there are additional values that are not met if ( - targetArgument.additionalData.values != undefined && + targetArgument.additionalData.values && !targetArgument.additionalData.values.includes( argument.word ) @@ -920,11 +914,8 @@ export class CommandValidator { break } - // Warn if unkown schema value - if ( - targetArgument.additionalData.schemaReference != - undefined - ) { + // Warn if unknown schema value + if (targetArgument.additionalData.schemaReference) { const referencePath = targetArgument.additionalData.schemaReference @@ -935,9 +926,9 @@ export class CommandValidator { ).getCompletionItems({}) if ( - schemaReference.find( + !schemaReference.find( (reference) => reference.value == argument.word - ) == undefined + ) ) { definitionWarnings.push({ severity: MarkerSeverity.Warning, From 652b2998c3c11a940fd58f711a8fb893dc7d3c12 Mon Sep 17 00:00:00 2001 From: Outer Cloud Studio Date: Mon, 29 Aug 2022 20:19:08 -0500 Subject: [PATCH 26/29] Fix: negated values failing in schema --- src/components/Languages/Mcfunction/Validator.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index af5720b1a..26d875cce 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -405,6 +405,10 @@ export class CommandValidator { warnings: [], } + // Remove ! at the beginning + if (negated) + argumentValue = argumentValue.substring(1, argumentValue.length) + // Check if this type should not be used again if (canNotUse) { if (argumentSchema.additionalData == undefined) From 7bf5f27e1d6c2f20fbd59cee21b7725928bd41ed Mon Sep 17 00:00:00 2001 From: Outer Cloud Studio Date: Mon, 29 Aug 2022 20:42:40 -0500 Subject: [PATCH 27/29] Fix: Clarify not enough arguments error, Incorrect schema warning value --- .../Languages/Mcfunction/Validator.ts | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index 6fb17e4b1..5c87ead64 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -722,6 +722,7 @@ export class CommandValidator { // We only need to record the error of the most farthest in token because that is the most likely variation the user was attempting to type let lastTokenError = 0 + let lastTokenErrorReason = '' let longestPassLength = -1 @@ -759,6 +760,11 @@ export class CommandValidator { failedLongest = true lastTokenError = k + + lastTokenErrorReason = twi( + 'validation.mcfunction.invalidArgument.name', + [`"${tokens[k].word}"`] + ) } failed = true @@ -827,6 +833,11 @@ export class CommandValidator { failedLongest = true lastTokenError = k + + lastTokenErrorReason = twi( + 'validation.mcfunction.invalidArgument.name', + [`"${tokens[k].word}"`] + ) } failed = true @@ -876,6 +887,11 @@ export class CommandValidator { failedLongest = true lastTokenError = k + + lastTokenErrorReason = twi( + 'validation.mcfunction.invalidArgument.name', + [`"${tokens[k].word}"`] + ) } failed = true @@ -911,6 +927,11 @@ export class CommandValidator { failedLongest = true lastTokenError = k + + lastTokenErrorReason = twi( + 'validation.mcfunction.invalidArgument.name', + [`"${tokens[k].word}"`] + ) } failed = true @@ -938,7 +959,7 @@ export class CommandValidator { severity: MarkerSeverity.Warning, message: twi( 'validation.mcfunction.unknownSchema.name', - [`"${commandName.word}"`] + [`"${tokens[k].word}"`] ), startLineNumber: -1, startColumn: argument.startColumn + 1, @@ -965,9 +986,15 @@ export class CommandValidator { j-- - if (lastTokenError < tokens.length - 1) + if (lastTokenError < tokens.length - 1) { lastTokenError = tokens.length - 1 + lastTokenErrorReason = twi( + 'validation.mcfunction.missingArguments.name', + [`"${commandName.word}"`] + ) + } + // Continue to not add warnings to the diagnostics continue } @@ -983,9 +1010,7 @@ export class CommandValidator { if (definitions.length == 0) { diagnostics.push({ severity: MarkerSeverity.Error, - message: twi('validation.mcfunction.invalidArgument.name', [ - `"${tokens[lastTokenError].word}"`, - ]), + message: lastTokenErrorReason, startLineNumber: -1, startColumn: tokens[lastTokenError].startColumn + 1, endLineNumber: -1, From 5bab97d64e81bbef61577de8c79b81837e8348b3 Mon Sep 17 00:00:00 2001 From: Outer Cloud Studio Date: Mon, 29 Aug 2022 20:49:28 -0500 Subject: [PATCH 28/29] Fix: Invalid say argument error --- src/components/Languages/Mcfunction/Validator.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index 5c87ead64..c04c1c5a5 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -715,6 +715,9 @@ export class CommandValidator { return diagnostics } + // If this is a say command we ignore validating arguments since they are all strings + if (commandName.word == 'say') return diagnostics.concat(warnings) + let definitions = await this.commandData.getCommandDefinitions( commandName.word, false From ff9fb3aab799f0d5ce7325698a350fffd9957e12 Mon Sep 17 00:00:00 2001 From: Outer Cloud Studio Date: Tue, 30 Aug 2022 09:03:15 -0500 Subject: [PATCH 29/29] Fix: @initator --- .../Languages/Mcfunction/Validator.ts | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/components/Languages/Mcfunction/Validator.ts b/src/components/Languages/Mcfunction/Validator.ts index c04c1c5a5..9d5c83fce 100644 --- a/src/components/Languages/Mcfunction/Validator.ts +++ b/src/components/Languages/Mcfunction/Validator.ts @@ -264,26 +264,31 @@ export class CommandValidator { warnings: [], } - if ( - baseSelector == '@i' && - selectorToken.word.substring(0, '@initiator'.length) != '@initiator' - ) - return { - passed: false, - diagnostic: { - severity: MarkerSeverity.Error, - message: twi( - 'validation.mcfunction.invalidSelectorBase.name', - [`"${baseSelector}"`] - ), - startLineNumber: -1, - startColumn: selectorToken.startColumn + 1, - endLineNumber: -1, - endColumn: selectorToken.endColumn + 1, - }, - warnings: [], + if (baseSelector == '@i') { + if ( + selectorToken.word.substring(0, '@initiator'.length) != + '@initiator' + ) { + return { + passed: false, + diagnostic: { + severity: MarkerSeverity.Error, + message: twi( + 'validation.mcfunction.invalidSelectorBase.name', + [`"${baseSelector}"`] + ), + startLineNumber: -1, + startColumn: selectorToken.startColumn + 1, + endLineNumber: -1, + endColumn: selectorToken.endColumn + 1, + }, + warnings: [], + } } + baseSelector = '@initiator' + } + // If the selector is merely the base we can just pass if (baseSelector == selectorToken.word) return {