Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Auto-Suggestion for fields and values for structure LG #2681

Merged
merged 7 commits into from
Apr 18, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Composer/packages/lib/code-editor/src/BaseEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const defaultOptions = {
folding: false,
renderLineHighlight: 'none',
formatOnType: true,
fixedOverflowWidgets: true,
};

const styles = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
updateTemplate,
cardTypes,
cardPropDict,
cardPropPossibleValueType,
} from './utils';

// define init methods call from client
Expand Down Expand Up @@ -344,22 +345,62 @@ export class LGServer {
const position = params.position;
const range = Range.create(0, 0, position.line, position.character);
const lines = document.getText(range).split('\n');
let lastLine = '';
const keyValueRegex = /.+=.+/;
let cardName = '';
for (const line of lines) {
if (line.trim().startsWith('#')) {
state.push(TEMPLATENAME);
} else if (line.trim().startsWith('-')) {
state.push(TEMPLATEBODY);
} else if ((state[state.length - 1] === TEMPLATENAME || templateId) && line.trim().startsWith('[')) {
state.push(STRUCTURELG);
lastLine = line;
} else if (state[state.length - 1] === STRUCTURELG && line.trim() === '') {
return lastLine.trim().substring(1);
cardName = line.trim().substring(1);
} else if (state[state.length - 1] === STRUCTURELG && (line.trim() === '' || keyValueRegex.test(line))) {
state.push(STRUCTURELG);
} else {
state.push(ROOT);
}
}

if (state[state.length - 1] === STRUCTURELG) {
return cardName;
}

return undefined;
}

private findLastStructureLGProps(
params: TextDocumentPositionParams,
templateId: string | undefined
): string[] | undefined {
const state: LGCursorState[] = [];
const document = this.documents.get(params.textDocument.uri);
if (!document) return;
const position = params.position;
const range = Range.create(0, 0, position.line, position.character);
const lines = document.getText(range).split('\n');
const keyValueRegex = /.+=.+/;
let props: string[] = [];
for (const line of lines) {
if (line.trim().startsWith('#')) {
state.push(TEMPLATENAME);
} else if (line.trim().startsWith('-')) {
state.push(TEMPLATEBODY);
} else if ((state[state.length - 1] === TEMPLATENAME || templateId) && line.trim().startsWith('[')) {
state.push(STRUCTURELG);
props = [];
} else if (state[state.length - 1] === STRUCTURELG && (line.trim() === '' || keyValueRegex.test(line))) {
state.push(STRUCTURELG);
props.push(line.split('=')[0].trim());
} else {
state.push(ROOT);
}
}

if (state[state.length - 1] === STRUCTURELG) {
return props;
}

return undefined;
}

Expand Down Expand Up @@ -504,6 +545,7 @@ export class LGServer {
const lgDoc = this.getLGDocument(document);
const lgFile = lgDoc?.index();
const templateId = lgDoc?.templateId;
const lines = document.getText(range).split('\n');
if (!lgFile) {
return Promise.resolve(null);
}
Expand Down Expand Up @@ -552,59 +594,55 @@ export class LGServer {
}

const cardType = this.matchCardTypeState(params, templateId);
const propsList = this.findLastStructureLGProps(params, templateId);
const cardNameRegex = /^\s*\[[\w]+/;
const lastLine = lines[lines.length - 2];
const paddingIndent = cardNameRegex.test(lastLine) ? '\t' : '';
const normalCardTypes = ['CardAction', 'Suggestions', 'Attachment'];
if (cardType && cardTypes.includes(cardType)) {
let item: CompletionItem | undefined = undefined;
if (cardType === 'CardAction') {
let insertStr = '';
insertStr = cardPropDict[cardType].map(u => `\t${u} = `).join('\r\n');
item = {
label: `Properties for ${cardType}`,
kind: CompletionItemKind.Keyword,
insertText: insertStr,
documentation: `Suggested properties for Card: ${cardType}`,
};
} else if (cardType === 'Suggestions') {
let insertStr = '';
insertStr = cardPropDict[cardType].map(u => `\t${u} = `).join('\r\n');
item = {
label: `Properties for ${cardType}`,
kind: CompletionItemKind.Keyword,
insertText: insertStr,
documentation: `Suggested properties for Card: ${cardType}`,
};
} else if (cardType === 'Attachment') {
let insertStr = '';
insertStr = cardPropDict[cardType].map(u => `\t${u} = `).join('\r\n');
item = {
label: `Properties for ${cardType}`,
kind: CompletionItemKind.Keyword,
insertText: insertStr,
documentation: `Suggested properties for Card: ${cardType}`,
};
const items: CompletionItem[] = [];
if (normalCardTypes.includes(cardType)) {
cardPropDict[cardType].forEach(u => {
if (!propsList?.includes(u)) {
const item = {
label: `${u}: ${cardPropPossibleValueType[u]}`,
kind: CompletionItemKind.Snippet,
insertText: `${paddingIndent}${u} = ${cardPropPossibleValueType[u]}`,
documentation: `Suggested propertiy ${u} in ${cardType}`,
};
items.push(item);
}
});
} else if (cardType.endsWith('Card')) {
let insertStr = '';
insertStr = cardPropDict.Cards.map(u => `\t${u} = `).join('\r\n');
item = {
label: `Properties for ${cardType}`,
kind: CompletionItemKind.Keyword,
insertText: insertStr,
documentation: `Suggested properties for Card: ${cardType}`,
};
cardPropDict.Cards.forEach(u => {
if (!propsList?.includes(u)) {
const item = {
label: `${u}: ${cardPropPossibleValueType[u]}`,
kind: CompletionItemKind.Snippet,
insertText: `${paddingIndent}${u} = ${cardPropPossibleValueType[u]}`,
documentation: `Suggested propertiy ${u} in ${cardType}`,
};
items.push(item);
}
});
} else {
let insertStr = '';
insertStr = cardPropDict.Others.map(u => `\t${u} = `).join('\r\n');
item = {
label: `Properties for ${cardType}`,
kind: CompletionItemKind.Keyword,
insertText: insertStr,
documentation: `Suggested properties for Card: ${cardType}`,
};
cardPropDict.Others.forEach(u => {
if (!propsList?.includes(u)) {
const item = {
label: `${u}: ${cardPropPossibleValueType[u]}`,
kind: CompletionItemKind.Snippet,
insertText: `${paddingIndent}${u} = ${cardPropPossibleValueType[u]}`,
documentation: `Suggested propertiy ${u} in ${cardType}`,
};
items.push(item);
}
});
}

if (item) {
if (items.length > 0) {
return Promise.resolve({
isIncomplete: true,
items: [item],
items: items,
});
}
}
Expand Down Expand Up @@ -639,8 +677,29 @@ export class LGServer {
const range = Range.create(0, 0, position.line, position.character);
const lines = document.getText(range).split('\n');
const isInStructureLGMode = this.matchStructureLG(lines);
if (key === '\n' && isInStructureLGMode) {
const deleteRange = Range.create(position.line, 0, position.line, 4);
const lgDoc = this.getLGDocument(document);
const templateId = lgDoc?.templateId;
const cardType = this.matchCardTypeState(params, templateId);
const propsList = this.findLastStructureLGProps(params, templateId);
const normalCardTypes = ['CardAction', 'Suggestions', 'Attachment'];
let expectedPropsList: string[] = [];
let matchedAllProps = false;
if (cardType) {
if (normalCardTypes.includes(cardType)) {
expectedPropsList = cardPropDict[cardType];
} else if (cardType.endsWith('Card')) {
expectedPropsList = cardPropDict.Cards;
} else {
expectedPropsList = cardPropDict.Others;
}
}

if (expectedPropsList.length > 0 && propsList !== undefined && this.isSubList(expectedPropsList, propsList)) {
matchedAllProps = true;
}
if (key === '\n' && isInStructureLGMode && matchedAllProps) {
const length = templateId ? 4 : 2;
const deleteRange = Range.create(position.line, 0, position.line, length);
const deleteItem: TextEdit = TextEdit.del(deleteRange);
if (document.getText(deleteRange).trim() === '') {
edits.push(deleteItem);
Expand Down Expand Up @@ -671,6 +730,16 @@ export class LGServer {
return true;
}

private isSubList(list1: string[], list2: string[]): boolean {
for (const item of list1) {
if (!list2.includes(item)) {
return false;
}
}

return true;
}

protected validate(document: TextDocument): void {
this.cleanPendingValidation(document);
this.pendingValidationRequests.set(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,20 @@ export const cardTypes = [
'Activity',
];

export const cardPropPossibleValueType = {
title: 'An Example Card',
type: 'Action Type',
value: 'Some Value',
SuggestionActions: 'Text | ${Some_CardAction}',
subtitle: 'An Example Subtitle',
text: 'Some text',
image: 'https://example.com/demo.jpg',
buttons: 'Text | ${Some_CardAction}',
contenttype: 'adaptivecard',
content: '${json(fromFile("../../card.json"))}',
name: 'An Example Name',
};

a-b-r-o-w-n marked this conversation as resolved.
Show resolved Hide resolved
export const cardPropDict = {
CardAction: ['title', 'type', 'value'],
Suggestions: ['SuggestionActions'],
Expand Down