diff --git a/languageservice/src/complete.test.ts b/languageservice/src/complete.test.ts index b6c7436..84abd19 100644 --- a/languageservice/src/complete.test.ts +++ b/languageservice/src/complete.test.ts @@ -320,6 +320,19 @@ on: expect(result).toHaveLength(0); }); + it("null strings still give suggestions", async () => { + const input = ` +on: push +jobs: + one: + runs-on: ubuntu-latest + |: + - uses: actions/checkout@v2 +`; + const result = await complete(...getPositionFromCursor(input)); + expect(result).toHaveLength(16); + }); + it("well known mapping keys have descriptions", async () => { const input = ` o| diff --git a/languageservice/src/utils/expression-detection.ts b/languageservice/src/utils/expression-detection.ts index 2242d8a..4406c78 100644 --- a/languageservice/src/utils/expression-detection.ts +++ b/languageservice/src/utils/expression-detection.ts @@ -7,6 +7,6 @@ import {TemplateToken} from "@github/actions-workflow-parser/templates/tokens/in export function isPotentiallyExpression(token: TemplateToken): boolean { const isAlwaysExpression = token.definition?.definitionType === DefinitionType.String && (token.definition as StringDefinition).isExpression; - const containsExpression = isString(token) && token.value.indexOf(OPEN_EXPRESSION) >= 0; + const containsExpression = isString(token) && token.value != null && token.value.indexOf(OPEN_EXPRESSION) >= 0; return isAlwaysExpression || containsExpression; } diff --git a/workflow-parser/src/templates/template-reader.ts b/workflow-parser/src/templates/template-reader.ts index 7d5bce0..02df3d7 100644 --- a/workflow-parser/src/templates/template-reader.ts +++ b/workflow-parser/src/templates/template-reader.ts @@ -203,14 +203,15 @@ class TemplateReader { ); // Duplicate - const upperKey = nextKey.value.toUpperCase(); - if (upperKeys[upperKey]) { - this._context.error(nextKey, `'${nextKey.value}' is already defined`); - this.skipValue(); - continue; + if (nextKey.value) { + const upperKey = nextKey.value.toUpperCase(); + if (upperKeys[upperKey]) { + this._context.error(nextKey, `'${nextKey.value}' is already defined`); + this.skipValue(); + continue; + } + upperKeys[upperKey] = true; } - upperKeys[upperKey] = true; - // Well known const nextPropertyDef = this._schema.matchPropertyAndFilter(mappingDefinitions, nextKey.value); if (nextPropertyDef) { @@ -339,13 +340,15 @@ class TemplateReader { ); // Duplicate - const upperKey = nextKey.value.toUpperCase(); - if (upperKeys[upperKey]) { - this._context.error(nextKey, `'${nextKey.value}' is already defined`); - this.skipValue(); - continue; + if (nextKey.value) { + const upperKey = nextKey.value.toUpperCase(); + if (upperKeys[upperKey]) { + this._context.error(nextKey, `'${nextKey.value}' is already defined`); + this.skipValue(); + continue; + } + upperKeys[upperKey] = true; } - upperKeys[upperKey] = true; // Validate this.validate(nextKey, keyDefinition); @@ -443,7 +446,7 @@ class TemplateReader { private parseScalar(token: LiteralToken, definitionInfo: DefinitionInfo): ScalarToken { // Not a string - if (!isString(token)) { + if (!isString(token) || !token.value) { return token; }