From 41198fdaf38f52b346c424876579d61117b6d38f Mon Sep 17 00:00:00 2001 From: Shuai Wang Date: Thu, 16 Apr 2020 19:01:55 +0800 Subject: [PATCH 1/4] init --- .../language-generation/src/LGServer.ts | 119 ++++++++++-------- .../language-generation/src/utils.ts | 14 +++ 2 files changed, 80 insertions(+), 53 deletions(-) diff --git a/Composer/packages/tools/language-servers/language-generation/src/LGServer.ts b/Composer/packages/tools/language-servers/language-generation/src/LGServer.ts index cedb22bfd7..e3e7b01b4f 100644 --- a/Composer/packages/tools/language-servers/language-generation/src/LGServer.ts +++ b/Composer/packages/tools/language-servers/language-generation/src/LGServer.ts @@ -35,6 +35,7 @@ import { updateTemplate, cardTypes, cardPropDict, + cardPropPossibleValueType, } from './utils'; // define init methods call from client @@ -344,7 +345,8 @@ 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); @@ -352,9 +354,9 @@ export class LGServer { 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))) { + return cardName; } else { state.push(ROOT); } @@ -504,6 +506,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); } @@ -552,59 +555,68 @@ export class LGServer { } const cardType = this.matchCardTypeState(params, templateId); + console.log('cardType:' + cardType); + const cardNameRegex = /^\s*\[[\w]+/; + const lastLine = lines[lines.length - 2]; + console.log('last line:' + lastLine); + const paddingIndent = cardNameRegex.test(lastLine) ? '\t' : ''; + console.log('flag:' + cardNameRegex.test(lastLine)); + console.log('paddingIndent' + paddingIndent.length); if (cardType && cardTypes.includes(cardType)) { - let item: CompletionItem | undefined = undefined; + const items: CompletionItem[] = []; 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}`, - }; - } 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}`, - }; - } 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[cardType].forEach(u => { + const item = { + label: `${u}: ${cardPropPossibleValueType[u]}`, + kind: CompletionItemKind.Keyword, + insertText: `${paddingIndent}${u} = ${cardPropPossibleValueType[u]}`, + documentation: `Suggested propertiy ${u} in ${cardType}`, + }; + items.push(item); + }); } - - if (item) { + //else if (cardType === 'Suggestions') { + // let insertStr = ''; + // insertStr = cardPropDict[cardType].map(u => `\t${u} = `).join('\r\n'); + // const 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'); + // const item = { + // label: `Properties for ${cardType}`, + // kind: CompletionItemKind.Keyword, + // insertText: insertStr, + // documentation: `Suggested properties for Card: ${cardType}`, + // }; + // } else if (cardType.endsWith('Card')) { + // let insertStr = ''; + // insertStr = cardPropDict.Cards.map(u => `\t${u} = `).join('\r\n'); + // const item = { + // label: `Properties:${insertStr}`, + // kind: CompletionItemKind.Keyword, + // insertText: insertStr, + // documentation: `Suggested properties for Card: ${cardType}`, + // }; + // } else { + // let insertStr = ''; + // insertStr = cardPropDict.Others.map(u => `\t${u} = `).join('\r\n'); + // const item = { + // label: `Properties for ${cardType}`, + // kind: CompletionItemKind.Keyword, + // insertText: insertStr, + // documentation: `Suggested properties for Card: ${cardType}`, + // }; + // } + + if (items) { return Promise.resolve({ isIncomplete: true, - items: [item], + items: items, }); } } @@ -647,7 +659,8 @@ export class LGServer { } } - return Promise.resolve(edits); + return Promise.resolve(null); + //return Promise.resolve(edits); } private matchStructureLG(lines: string[]): boolean { diff --git a/Composer/packages/tools/language-servers/language-generation/src/utils.ts b/Composer/packages/tools/language-servers/language-generation/src/utils.ts index 5f88ecd386..3316a3ff96 100644 --- a/Composer/packages/tools/language-servers/language-generation/src/utils.ts +++ b/Composer/packages/tools/language-servers/language-generation/src/utils.ts @@ -153,6 +153,20 @@ export const cardTypes = [ 'Activity', ]; +export const cardPropPossibleValueType = { + title: 'An Example Card', + type: 'Action Type', + value: 'Some Value', + SuggestionActions: 'Text | ${CardAction("action")}', + subtitle: 'An Example Subtitle', + text: 'Some text', + image: 'https://example.com/demo.jpg', + buttons: 'Text | ${CardAction("Show Cards")}', + contenttype: 'adaptivecard', + content: '${json(fromFile("../../card.json"))}', + name: 'An Example Name', +}; + export const cardPropDict = { CardAction: ['title', 'type', 'value'], Suggestions: ['SuggestionActions'], From 14ccfa019c2200b54a12f04c64ba67dbf61e4630 Mon Sep 17 00:00:00 2001 From: Shuai Wang Date: Thu, 16 Apr 2020 21:38:29 +0800 Subject: [PATCH 2/4] improvement --- .../language-generation/src/LGServer.ts | 168 ++++++++++++------ .../language-generation/src/utils.ts | 4 +- 2 files changed, 114 insertions(+), 58 deletions(-) diff --git a/Composer/packages/tools/language-servers/language-generation/src/LGServer.ts b/Composer/packages/tools/language-servers/language-generation/src/LGServer.ts index e3e7b01b4f..7cd112f766 100644 --- a/Composer/packages/tools/language-servers/language-generation/src/LGServer.ts +++ b/Composer/packages/tools/language-servers/language-generation/src/LGServer.ts @@ -356,12 +356,51 @@ export class LGServer { state.push(STRUCTURELG); cardName = line.trim().substring(1); } else if (state[state.length - 1] === STRUCTURELG && (line.trim() === '' || keyValueRegex.test(line))) { - return cardName; + 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; } @@ -555,65 +594,52 @@ export class LGServer { } const cardType = this.matchCardTypeState(params, templateId); - console.log('cardType:' + cardType); + const propsList = this.findLastStructureLGProps(params, templateId); const cardNameRegex = /^\s*\[[\w]+/; const lastLine = lines[lines.length - 2]; - console.log('last line:' + lastLine); const paddingIndent = cardNameRegex.test(lastLine) ? '\t' : ''; - console.log('flag:' + cardNameRegex.test(lastLine)); - console.log('paddingIndent' + paddingIndent.length); + const normalCardTypes = ['CardAction', 'Suggestions', 'Attachment']; if (cardType && cardTypes.includes(cardType)) { const items: CompletionItem[] = []; - if (cardType === 'CardAction') { + if (normalCardTypes.includes(cardType)) { cardPropDict[cardType].forEach(u => { - const item = { - label: `${u}: ${cardPropPossibleValueType[u]}`, - kind: CompletionItemKind.Keyword, - insertText: `${paddingIndent}${u} = ${cardPropPossibleValueType[u]}`, - documentation: `Suggested propertiy ${u} in ${cardType}`, - }; - items.push(item); + if (!propsList?.includes(u)) { + const item = { + label: `${u}: ${cardPropPossibleValueType[u]}`, + kind: CompletionItemKind.Keyword, + insertText: `${paddingIndent}${u} = ${cardPropPossibleValueType[u]}`, + documentation: `Suggested propertiy ${u} in ${cardType}`, + }; + items.push(item); + } + }); + } else if (cardType.endsWith('Card')) { + cardPropDict.Cards.forEach(u => { + if (!propsList?.includes(u)) { + const item = { + label: `${u}: ${cardPropPossibleValueType[u]}`, + kind: CompletionItemKind.Keyword, + insertText: `${paddingIndent}${u} = ${cardPropPossibleValueType[u]}`, + documentation: `Suggested propertiy ${u} in ${cardType}`, + }; + items.push(item); + } + }); + } else { + cardPropDict.Others.forEach(u => { + if (!propsList?.includes(u)) { + const item = { + label: `${u}: ${cardPropPossibleValueType[u]}`, + kind: CompletionItemKind.Keyword, + insertText: `${paddingIndent}${u} = ${cardPropPossibleValueType[u]}`, + documentation: `Suggested propertiy ${u} in ${cardType}`, + }; + items.push(item); + } }); } - //else if (cardType === 'Suggestions') { - // let insertStr = ''; - // insertStr = cardPropDict[cardType].map(u => `\t${u} = `).join('\r\n'); - // const 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'); - // const item = { - // label: `Properties for ${cardType}`, - // kind: CompletionItemKind.Keyword, - // insertText: insertStr, - // documentation: `Suggested properties for Card: ${cardType}`, - // }; - // } else if (cardType.endsWith('Card')) { - // let insertStr = ''; - // insertStr = cardPropDict.Cards.map(u => `\t${u} = `).join('\r\n'); - // const item = { - // label: `Properties:${insertStr}`, - // kind: CompletionItemKind.Keyword, - // insertText: insertStr, - // documentation: `Suggested properties for Card: ${cardType}`, - // }; - // } else { - // let insertStr = ''; - // insertStr = cardPropDict.Others.map(u => `\t${u} = `).join('\r\n'); - // const item = { - // label: `Properties for ${cardType}`, - // kind: CompletionItemKind.Keyword, - // insertText: insertStr, - // documentation: `Suggested properties for Card: ${cardType}`, - // }; - // } - - if (items) { + + if (items.length > 0) { return Promise.resolve({ isIncomplete: true, items: items, @@ -651,16 +677,36 @@ 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); } } - return Promise.resolve(null); - //return Promise.resolve(edits); + return Promise.resolve(edits); } private matchStructureLG(lines: string[]): boolean { @@ -684,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( diff --git a/Composer/packages/tools/language-servers/language-generation/src/utils.ts b/Composer/packages/tools/language-servers/language-generation/src/utils.ts index 3316a3ff96..b9a79c6965 100644 --- a/Composer/packages/tools/language-servers/language-generation/src/utils.ts +++ b/Composer/packages/tools/language-servers/language-generation/src/utils.ts @@ -157,11 +157,11 @@ export const cardPropPossibleValueType = { title: 'An Example Card', type: 'Action Type', value: 'Some Value', - SuggestionActions: 'Text | ${CardAction("action")}', + SuggestionActions: 'Text | ${Some_CardAction}', subtitle: 'An Example Subtitle', text: 'Some text', image: 'https://example.com/demo.jpg', - buttons: 'Text | ${CardAction("Show Cards")}', + buttons: 'Text | ${Some_CardAction}', contenttype: 'adaptivecard', content: '${json(fromFile("../../card.json"))}', name: 'An Example Name', From b67c07a46772ec20af4793330c3a01432a7d4813 Mon Sep 17 00:00:00 2001 From: Shuai Wang Date: Fri, 17 Apr 2020 10:59:18 +0800 Subject: [PATCH 3/4] change suggestion item kind --- .../language-servers/language-generation/src/LGServer.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Composer/packages/tools/language-servers/language-generation/src/LGServer.ts b/Composer/packages/tools/language-servers/language-generation/src/LGServer.ts index 7cd112f766..fc12cca6dd 100644 --- a/Composer/packages/tools/language-servers/language-generation/src/LGServer.ts +++ b/Composer/packages/tools/language-servers/language-generation/src/LGServer.ts @@ -606,7 +606,7 @@ export class LGServer { if (!propsList?.includes(u)) { const item = { label: `${u}: ${cardPropPossibleValueType[u]}`, - kind: CompletionItemKind.Keyword, + kind: CompletionItemKind.Snippet, insertText: `${paddingIndent}${u} = ${cardPropPossibleValueType[u]}`, documentation: `Suggested propertiy ${u} in ${cardType}`, }; @@ -618,7 +618,7 @@ export class LGServer { if (!propsList?.includes(u)) { const item = { label: `${u}: ${cardPropPossibleValueType[u]}`, - kind: CompletionItemKind.Keyword, + kind: CompletionItemKind.Snippet, insertText: `${paddingIndent}${u} = ${cardPropPossibleValueType[u]}`, documentation: `Suggested propertiy ${u} in ${cardType}`, }; @@ -630,7 +630,7 @@ export class LGServer { if (!propsList?.includes(u)) { const item = { label: `${u}: ${cardPropPossibleValueType[u]}`, - kind: CompletionItemKind.Keyword, + kind: CompletionItemKind.Snippet, insertText: `${paddingIndent}${u} = ${cardPropPossibleValueType[u]}`, documentation: `Suggested propertiy ${u} in ${cardType}`, }; From 6a099b214a107ece6a25ab9ded12e2f5f0ab708c Mon Sep 17 00:00:00 2001 From: Andy Brown Date: Fri, 17 Apr 2020 10:11:00 -0700 Subject: [PATCH 4/4] add fixedOverflowWidgets to base editor options --- Composer/packages/lib/code-editor/src/BaseEditor.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/Composer/packages/lib/code-editor/src/BaseEditor.tsx b/Composer/packages/lib/code-editor/src/BaseEditor.tsx index 85fcb7c1aa..4ec24df036 100644 --- a/Composer/packages/lib/code-editor/src/BaseEditor.tsx +++ b/Composer/packages/lib/code-editor/src/BaseEditor.tsx @@ -31,6 +31,7 @@ const defaultOptions = { folding: false, renderLineHighlight: 'none', formatOnType: true, + fixedOverflowWidgets: true, }; const styles = {