Merge pull request #8 from github/joshmgross/empty-mapping-nodes
Handle completion edge cases with empty mapping nodes
This commit is contained in:
@@ -145,4 +145,28 @@ jobs:
|
|||||||
expect(result.length).toEqual(1);
|
expect(result.length).toEqual(1);
|
||||||
expect(result[0].label).toEqual("my-custom-label");
|
expect(result[0].label).toEqual("my-custom-label");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("does not show parent mapping sibling keys", async () => {
|
||||||
|
const input = `on: push
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
container: |
|
||||||
|
runs-on: ubuntu-latest`;
|
||||||
|
const result = await complete(...getPositionFromCursor(input));
|
||||||
|
expect(result).not.toBeUndefined();
|
||||||
|
expect(result.length).toEqual(6);
|
||||||
|
// Should not contain other top-level job keys like `if` and `runs-on`
|
||||||
|
expect(result.map(x => x.label)).not.toContain("if");
|
||||||
|
expect(result.map(x => x.label)).not.toContain("runs-on");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows mapping keys within a new map ", async () => {
|
||||||
|
const input = `on: push
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
concurrency: |`;
|
||||||
|
const result = await complete(...getPositionFromCursor(input));
|
||||||
|
expect(result).not.toBeUndefined();
|
||||||
|
expect(result.map(x => x.label).sort()).toEqual(["cancel-in-progress", "group"]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -71,14 +71,13 @@ export async function complete(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const values = await getValues(token, parent, newPos, textDocument.uri, valueProviderConfig);
|
const values = await getValues(token, parent, textDocument.uri, valueProviderConfig);
|
||||||
return values.map(value => CompletionItem.create(value.label));
|
return values.map(value => CompletionItem.create(value.label));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getValues(
|
async function getValues(
|
||||||
token: TemplateToken | null,
|
token: TemplateToken | null,
|
||||||
parent: TemplateToken | null,
|
parent: TemplateToken | null,
|
||||||
position: Position,
|
|
||||||
workflowUri: string,
|
workflowUri: string,
|
||||||
valueProviderConfig: ValueProviderConfig | undefined
|
valueProviderConfig: ValueProviderConfig | undefined
|
||||||
): Promise<Value[]> {
|
): Promise<Value[]> {
|
||||||
@@ -86,13 +85,6 @@ async function getValues(
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token?.templateTokenType === TokenType.Null) {
|
|
||||||
// Ensure there's a space after the parent key
|
|
||||||
if (parent.range && position.character + 1 === parent.range.end[1]) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const existingValues = getExistingValues(token, parent);
|
const existingValues = getExistingValues(token, parent);
|
||||||
|
|
||||||
let customValues: Value[] | undefined = undefined;
|
let customValues: Value[] | undefined = undefined;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import {isString} from "@github/actions-workflow-parser";
|
||||||
import {TemplateToken} from "@github/actions-workflow-parser/templates/tokens/index";
|
import {TemplateToken} from "@github/actions-workflow-parser/templates/tokens/index";
|
||||||
import {MappingToken} from "@github/actions-workflow-parser/templates/tokens/mapping-token";
|
import {MappingToken} from "@github/actions-workflow-parser/templates/tokens/mapping-token";
|
||||||
import {SequenceToken} from "@github/actions-workflow-parser/templates/tokens/sequence-token";
|
import {SequenceToken} from "@github/actions-workflow-parser/templates/tokens/sequence-token";
|
||||||
@@ -54,13 +55,24 @@ export function findToken(pos: Position, root?: TemplateToken): TokenResult {
|
|||||||
for (let i = 0; i < mappingToken.count; i++) {
|
for (let i = 0; i < mappingToken.count; i++) {
|
||||||
const {key, value} = mappingToken.get(i);
|
const {key, value} = mappingToken.get(i);
|
||||||
|
|
||||||
// Null tokens don't have a position, we can only use the line information
|
if (onSameLine(pos, key, value)) {
|
||||||
if (nullNodeOnLine(pos, key, value)) {
|
if (posInToken(pos, key) && key.range!.end[1] + 1 === value.range!.start[1]) {
|
||||||
return {
|
// There's no space between the key and value, there's nothing valid to complete here
|
||||||
token: value,
|
return {
|
||||||
keyToken: null,
|
token: null,
|
||||||
parent: key
|
keyToken: null,
|
||||||
};
|
parent: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty nodes positions won't always match the cursor, so check if we're on the same line
|
||||||
|
if (emptyNode(value)) {
|
||||||
|
return {
|
||||||
|
token: value,
|
||||||
|
keyToken: null,
|
||||||
|
parent: key
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.push({
|
s.push({
|
||||||
@@ -122,11 +134,7 @@ function posInToken(pos: Position, token: TemplateToken): boolean {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function nullNodeOnLine(pos: Position, key: TemplateToken, value: TemplateToken): boolean {
|
function onSameLine(pos: Position, key: TemplateToken, value: TemplateToken): boolean {
|
||||||
if (value.templateTokenType !== TokenType.Null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!value.range) {
|
if (!value.range) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -136,7 +144,7 @@ function nullNodeOnLine(pos: Position, key: TemplateToken, value: TemplateToken)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (value.range.start[0] !== value.range.end[0]) {
|
if (value.range.start[0] !== value.range.end[0]) {
|
||||||
// Token occupies multiple lines, can't be a null node
|
// Token occupies multiple lines, can't be an empty node
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,3 +156,19 @@ function nullNodeOnLine(pos: Position, key: TemplateToken, value: TemplateToken)
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function emptyNode(token: TemplateToken | null): boolean {
|
||||||
|
if (!token) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token.templateTokenType === TokenType.Null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isString(token)) {
|
||||||
|
return token.value === "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user