diff --git a/Composer/packages/client/src/pages/language-generation/code-editor.tsx b/Composer/packages/client/src/pages/language-generation/code-editor.tsx index 857a358fda..860b1a2d11 100644 --- a/Composer/packages/client/src/pages/language-generation/code-editor.tsx +++ b/Composer/packages/client/src/pages/language-generation/code-editor.tsx @@ -52,7 +52,8 @@ const CodeEditor: React.FC = (props) => { setContent(value); }, [file, templateId, projectId]); - const currentDiagnostics = inlineMode && template ? filterTemplateDiagnostics(diagnostics, template) : diagnostics; + const currentDiagnostics = + inlineMode && file && template ? filterTemplateDiagnostics(file, template.name) : diagnostics; const editorDidMount: EditorDidMount = (_getValue, lgEditor) => { setLgEditor(lgEditor); diff --git a/Composer/packages/client/src/pages/language-understanding/code-editor.tsx b/Composer/packages/client/src/pages/language-understanding/code-editor.tsx index cd988add9a..dba9b620d1 100644 --- a/Composer/packages/client/src/pages/language-understanding/code-editor.tsx +++ b/Composer/packages/client/src/pages/language-understanding/code-editor.tsx @@ -7,7 +7,7 @@ import { LuEditor, EditorDidMount } from '@bfc/code-editor'; import get from 'lodash/get'; import debounce from 'lodash/debounce'; import isEmpty from 'lodash/isEmpty'; -import { filterTemplateDiagnostics } from '@bfc/indexers'; +import { filterSectionDiagnostics } from '@bfc/indexers'; import { RouteComponentProps } from '@reach/router'; import querystring from 'query-string'; import { CodeEditorSettings } from '@bfc/shared'; @@ -51,7 +51,7 @@ const CodeEditor: React.FC = (props) => { setContent(value); }, [file, sectionId, projectId]); - const currentDiagnostics = inlineMode && intent ? filterTemplateDiagnostics(diagnostics, intent) : diagnostics; + const currentDiagnostics = inlineMode && file && sectionId ? filterSectionDiagnostics(file, sectionId) : diagnostics; const editorDidMount: EditorDidMount = (_getValue, luEditor) => { setLuEditor(luEditor); diff --git a/Composer/packages/lib/code-editor/src/LgEditor.tsx b/Composer/packages/lib/code-editor/src/LgEditor.tsx index cb9d284e2c..c23fcd0d1a 100644 --- a/Composer/packages/lib/code-editor/src/LgEditor.tsx +++ b/Composer/packages/lib/code-editor/src/LgEditor.tsx @@ -4,7 +4,7 @@ import React, { useState, useEffect } from 'react'; import { listen, MessageConnection } from 'vscode-ws-jsonrpc'; import get from 'lodash/get'; -import { MonacoServices } from 'monaco-languageclient'; +import { MonacoServices, MonacoLanguageClient } from 'monaco-languageclient'; import { EditorDidMount } from '@monaco-editor/react'; import { registerLGLanguage } from './languages'; @@ -34,6 +34,7 @@ const defaultLGServer = { declare global { interface Window { monacoServiceInstance: MonacoServices; + monacoLGEditorInstance: MonacoLanguageClient; } } @@ -63,17 +64,23 @@ export function LgEditor(props: LGLSPEditorProps) { } const uri = get(editor.getModel(), 'uri._formatted', ''); - const url = createUrl(lgServer); - const webSocket: WebSocket = createWebSocket(url); - listen({ - webSocket, - onConnection: (connection: MessageConnection) => { - const languageClient = createLanguageClient('LG Language Client', ['botbuilderlg'], connection); - SendRequestWithRetry(languageClient, 'initializeDocuments', { lgOption, uri }); - const disposable = languageClient.start(); - connection.onClose(() => disposable.dispose()); - }, - }); + + if (!window.monacoLGEditorInstance) { + const url = createUrl(lgServer); + const webSocket: WebSocket = createWebSocket(url); + listen({ + webSocket, + onConnection: (connection: MessageConnection) => { + const languageClient = createLanguageClient('LG Language Client', ['botbuilderlg'], connection); + SendRequestWithRetry(languageClient, 'initializeDocuments', { lgOption, uri }); + const disposable = languageClient.start(); + connection.onClose(() => disposable.dispose()); + window.monacoLGEditorInstance = languageClient; + }, + }); + } else { + SendRequestWithRetry(window.monacoLGEditorInstance, 'initializeDocuments', { lgOption, uri }); + } }, [editor]); const onInit: OnInit = (monaco) => { diff --git a/Composer/packages/lib/code-editor/src/LuEditor.tsx b/Composer/packages/lib/code-editor/src/LuEditor.tsx index 6b67877f6c..82899c2a0f 100644 --- a/Composer/packages/lib/code-editor/src/LuEditor.tsx +++ b/Composer/packages/lib/code-editor/src/LuEditor.tsx @@ -4,7 +4,7 @@ import React, { useRef, useState, useEffect } from 'react'; import { listen, MessageConnection } from 'vscode-ws-jsonrpc'; import get from 'lodash/get'; -import { MonacoServices } from 'monaco-languageclient'; +import { MonacoServices, MonacoLanguageClient } from 'monaco-languageclient'; import { EditorDidMount, Monaco } from '@monaco-editor/react'; import { registerLULanguage } from './languages'; @@ -31,6 +31,7 @@ const defaultLUServer = { declare global { interface Window { monacoServiceInstance: MonacoServices; + monacoLUEditorInstance: MonacoLanguageClient; } } @@ -87,37 +88,53 @@ const LuEditor: React.FC = (props) => { } const uri = get(editor.getModel(), 'uri._formatted', ''); - const url = createUrl(luServer); - const webSocket: WebSocket = createWebSocket(url); - listen({ - webSocket, - onConnection: (connection: MessageConnection) => { - const languageClient = createLanguageClient('LU Language Client', ['lu'], connection); - - const m = monacoRef.current; - if (m) { - // this is the correct way to combine keycodes in Monaco - // eslint-disable-next-line no-bitwise - editor.addCommand(m.KeyMod.Shift | m.KeyCode.Enter, () => { - const position = editor.getPosition(); - SendRequestWithRetry(languageClient, 'labelingExperienceRequest', { uri, position }); - }); - } - - SendRequestWithRetry(languageClient, 'initializeDocuments', { luOption, uri }); - - languageClient.onReady().then(() => - languageClient.onNotification('addUnlabelUtterance', (result) => { - const edits = result.edits.map((e) => { - return convertEdit(e); + + if (!window.monacoLUEditorInstance) { + const url = createUrl(luServer); + const webSocket: WebSocket = createWebSocket(url); + listen({ + webSocket, + onConnection: (connection: MessageConnection) => { + const languageClient = createLanguageClient('LU Language Client', ['lu'], connection); + + const m = monacoRef.current; + if (m) { + // this is the correct way to combine keycodes in Monaco + // eslint-disable-next-line no-bitwise + editor.addCommand(m.KeyMod.Shift | m.KeyCode.Enter, () => { + const position = editor.getPosition(); + SendRequestWithRetry(languageClient, 'labelingExperienceRequest', { uri, position }); }); - editor.executeEdits(uri, edits); - }) - ); - const disposable = languageClient.start(); - connection.onClose(() => disposable.dispose()); - }, - }); + } + + SendRequestWithRetry(languageClient, 'initializeDocuments', { luOption, uri }); + + languageClient.onReady().then(() => + languageClient.onNotification('addUnlabelUtterance', (result) => { + const edits = result.edits.map((e) => { + return convertEdit(e); + }); + editor.executeEdits(uri, edits); + }) + ); + const disposable = languageClient.start(); + connection.onClose(() => disposable.dispose()); + window.monacoLUEditorInstance = languageClient; + }, + }); + } else { + const m = monacoRef.current; + const languageClient = window.monacoLUEditorInstance; + if (m) { + // this is the correct way to combine keycodes in Monaco + // eslint-disable-next-line no-bitwise + editor.addCommand(m.KeyMod.Shift | m.KeyCode.Enter, () => { + const position = editor.getPosition(); + SendRequestWithRetry(languageClient, 'labelingExperienceRequest', { uri, position }); + }); + } + SendRequestWithRetry(languageClient, 'initializeDocuments', { luOption, uri }); + } }, [editor]); const onInit: OnInit = (monaco) => { registerLULanguage(monaco); diff --git a/Composer/packages/lib/indexers/__tests__/lgIndexer.test.ts b/Composer/packages/lib/indexers/__tests__/lgIndexer.test.ts index f47e0267a7..b35d376a9e 100644 --- a/Composer/packages/lib/indexers/__tests__/lgIndexer.test.ts +++ b/Composer/packages/lib/indexers/__tests__/lgIndexer.test.ts @@ -16,6 +16,9 @@ describe('parse', () => { -What's up bro`; const { templates }: any = parse(content); expect(templates.length).toEqual(2); + expect(templates[0].range.startLineNumber).toEqual(1); + expect(templates[0].range.endLineNumber).toEqual(3); + expect(templates[0].parameters).toEqual([]); expect(templates[0].name).toEqual('Exit'); expect(templates[1].name).toEqual('Greeting'); }); diff --git a/Composer/packages/lib/indexers/__tests__/lgUtil.test.ts b/Composer/packages/lib/indexers/__tests__/lgUtil.test.ts index be1a99763b..662b31dae3 100644 --- a/Composer/packages/lib/indexers/__tests__/lgUtil.test.ts +++ b/Composer/packages/lib/indexers/__tests__/lgUtil.test.ts @@ -3,7 +3,7 @@ import { Templates } from 'botbuilder-lg'; -import { updateTemplate, addTemplate, removeTemplate, extractOptionByKey } from '../src/utils/lgUtil'; +import { updateTemplate, addTemplate, checkTemplate, removeTemplate, extractOptionByKey } from '../src/utils/lgUtil'; describe('update lg template', () => { it('should update lg template', () => { @@ -23,7 +23,7 @@ describe('update lg template', () => { it('should update lg template with error', () => { const content = `# Exit --Thanks for using todo bot.\${ +-Thanks for using todo bot.\${ # Greeting -What's up bro`; @@ -56,8 +56,8 @@ describe('add lg template', () => { }); }); -describe('add lg template', () => { - it('should add lg template', () => { +describe('remove lg template', () => { + it('should remove lg template', () => { const content = `# Exit -Thanks for using todo bot. @@ -70,6 +70,28 @@ describe('add lg template', () => { }); }); +describe('check lg template', () => { + it('check a valid template', () => { + const template = { + name: 'Greeting', + body: '-hi', + parameters: [], + }; + const diags = checkTemplate(template); + expect(diags).toHaveLength(0); + }); + + it('check an invalid template', () => { + const template = { + name: 'Greeting', + body: 'hi ${ ', + parameters: [], + }; + const diags = checkTemplate(template); + expect(diags).toHaveLength(1); + }); +}); + describe('extract option by key', () => { it('should extract optin', () => { const options = ['@strict = false', '@Namespace = foo', '@Exports = bar, cool']; diff --git a/Composer/packages/lib/indexers/src/lgIndexer.ts b/Composer/packages/lib/indexers/src/lgIndexer.ts index 5b2776bf0d..1047a44617 100644 --- a/Composer/packages/lib/indexers/src/lgIndexer.ts +++ b/Composer/packages/lib/indexers/src/lgIndexer.ts @@ -28,8 +28,8 @@ function parse(content: string, id = '', importResolver: ImportResolverDelegate body: t.body, parameters: t.parameters, range: { - startLineNumber: get(t, 'parseTree.start.line', 0), - endLineNumber: get(t, 'parseTree.stop.line', 0), + startLineNumber: get(t, 'sourceRange.parseTree.start.line', 0), + endLineNumber: get(t, 'sourceRange.parseTree.stop.line', 0), }, }; }); diff --git a/Composer/packages/lib/indexers/src/utils/diagnosticUtil.ts b/Composer/packages/lib/indexers/src/utils/diagnosticUtil.ts index f2dea09220..8d4751b247 100644 --- a/Composer/packages/lib/indexers/src/utils/diagnosticUtil.ts +++ b/Composer/packages/lib/indexers/src/utils/diagnosticUtil.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { LuIntentSection, Diagnostic, DiagnosticSeverity, Range, Position, CodeRange } from '@bfc/shared'; +import { Diagnostic, DiagnosticSeverity, Range, Position, CodeRange, LgFile, LuFile } from '@bfc/shared'; export function createSingleMessage(d: Diagnostic): string { let msg = `${d.message}\n`; @@ -47,7 +47,10 @@ export function isDiagnosticWithInRange(diagnostic: Diagnostic, range: CodeRange return diagnostic.range.start.line >= range.startLineNumber && diagnostic.range.end.line <= range.endLineNumber; } -export function filterTemplateDiagnostics(diagnostics: Diagnostic[], { range }: { range?: CodeRange }): Diagnostic[] { +export function filterTemplateDiagnostics(file: LgFile, name: string): Diagnostic[] { + const { diagnostics, templates } = file; + const range = templates.find((t) => t.name === name)?.range; + if (!range) return []; const filteredDiags = diagnostics.filter((d) => { return d.range && isDiagnosticWithInRange(d, range); @@ -65,8 +68,10 @@ export function filterTemplateDiagnostics(diagnostics: Diagnostic[], { range }: }); } -export function filterSectionDiagnostics(diagnostics: Diagnostic[], section: LuIntentSection): Diagnostic[] { - const { range } = section; +export function filterSectionDiagnostics(file: LuFile, name: string): Diagnostic[] { + const { diagnostics, intents } = file; + const range = intents.find((t) => t.Name === name)?.range; + if (!range) return diagnostics; const filteredDiags = diagnostics.filter((d) => { return isDiagnosticWithInRange(d, range); diff --git a/Composer/packages/lib/indexers/src/utils/lgUtil.ts b/Composer/packages/lib/indexers/src/utils/lgUtil.ts index dcbb92d025..15ced599d8 100644 --- a/Composer/packages/lib/indexers/src/utils/lgUtil.ts +++ b/Composer/packages/lib/indexers/src/utils/lgUtil.ts @@ -7,7 +7,7 @@ * */ -import { Templates } from 'botbuilder-lg'; +import { Templates, Diagnostic as LGDiagnostic } from 'botbuilder-lg'; import { LgTemplate } from '@bfc/shared'; export interface Template { @@ -32,14 +32,22 @@ export function increaseNameUtilNotExist(templates: LgTemplate[], name: string): export function updateTemplate( content: string, templateName: string, - { name, parameters = [], body }: LgTemplate + { name, parameters, body }: { name?: string; parameters?: string[]; body?: string } ): string { const resource = Templates.parseText(content); + const originTemplate = resource.toArray().find((t) => t.name === templateName); // add if not exist - if (resource.toArray().findIndex((t) => t.name === templateName) === -1) { - return resource.addTemplate(name, parameters, body).toString(); + if (!originTemplate) { + return resource.addTemplate(templateName, parameters || [], body || '').toString(); } else { - return resource.updateTemplate(templateName, name, parameters, body).toString(); + return resource + .updateTemplate( + templateName, + name || originTemplate.name, + parameters || originTemplate.parameters, + body || originTemplate.body + ) + .toString(); } } @@ -132,6 +140,11 @@ export function checkSingleLgTemplate(template: LgTemplate) { } } +export function checkTemplate(template: LgTemplate): LGDiagnostic[] { + const text = textFromTemplate(template); + return Templates.parseText(text, '').diagnostics; +} + export function extractOptionByKey(nameOfKey: string, options: string[]): string { let result = ''; for (const option of options) { diff --git a/Composer/packages/tools/language-servers/language-generation/__tests__/unitTest/util.test.ts b/Composer/packages/tools/language-servers/language-generation/__tests__/unitTest/util.test.ts deleted file mode 100644 index 2601bce4cf..0000000000 --- a/Composer/packages/tools/language-servers/language-generation/__tests__/unitTest/util.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import { textFromTemplate, checkTemplate, updateTemplate } from '../../src/utils'; - -describe('LG LSP server util function test', () => { - it('text from template', () => { - const template = { - name: 'Greeting', - body: '-hi', - }; - const templateText = textFromTemplate(template); - expect(templateText).toContain('# Greeting'); - }); - - it('check a valid template', () => { - const template = { - name: 'Greeting', - body: '-hi', - }; - const diags = checkTemplate(template); - expect(diags).toHaveLength(0); - }); - - it('check an invalid template', () => { - const template = { - name: 'Greeting', - body: 'hi ${ ', - }; - const diags = checkTemplate(template); - expect(diags).toHaveLength(1); - }); - - it('update template', () => { - const content = ` -# Greeting -- hello - `; - const newContent = updateTemplate(content, 'Greeting', '- hi'); - expect(newContent).toContain('- hi'); - }); -}); 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 9f9ccad03f..02353834b3 100644 --- a/Composer/packages/tools/language-servers/language-generation/src/LGServer.ts +++ b/Composer/packages/tools/language-servers/language-generation/src/LGServer.ts @@ -17,13 +17,13 @@ import { TextDocumentPositionParams, DocumentOnTypeFormattingParams } from 'vsco import get from 'lodash/get'; import { filterTemplateDiagnostics, isValid } from '@bfc/indexers'; import { MemoryResolver, ResolverResource, LgTemplate } from '@bfc/shared'; +import * as lgUtil from '@bfc/indexers/lib/utils/lgUtil'; import { LgParser } from './lgParser'; import { buildInfunctionsMap } from './builtinFunctionsMap'; import { getRangeAtPosition, LGDocument, - checkTemplate, convertDiagnostics, generageDiagnostic, LGOption, @@ -148,16 +148,6 @@ export class LGServer { if (!this.getLgResources) { diagnostics.push('[Error lgOption] getLgResources is required but not exist.'); - } else { - const { fileId, templateId } = lgOption; - const lgFile = await this.getLGDocument(document)?.index(); - if (!lgFile) { - diagnostics.push(`[Error lgOption] File ${fileId}.lg do not exist`); - } else if (templateId) { - const { templates } = lgFile; - const template = templates.find(({ name }) => name === templateId); - if (!template) diagnostics.push(`Template ${fileId}.lg#${templateId} do not exist`); - } } this.connection.console.log(diagnostics.join('\n')); this.sendDiagnostics( @@ -170,17 +160,17 @@ export class LGServer { const { uri } = document; const { fileId, templateId, projectId } = lgOption || {}; const index = async () => { - let content: string = document.getText(); + let content = this.documents.get(uri)?.getText() || ''; // if inline mode, composite local with server resolved file. if (this.getLgResources && fileId && templateId) { - try { - const resources = this.getLgResources(projectId); - content = resources.find((item) => item.id === fileId)?.content || content; - } catch (error) { - // ignore if file not exist + const resources = this.getLgResources(projectId); + const lastContent = resources.find((item) => item.id === fileId)?.content; + + if (lastContent) { + const body = content; + content = lgUtil.updateTemplate(lastContent, templateId, { body }); } } - const id = fileId || uri; let templates: LgTemplate[] = []; let diagnostics: any[] = []; @@ -192,7 +182,7 @@ export class LGServer { diagnostics.push(generageDiagnostic(error.message, DiagnosticSeverity.Error, document)); } - return { templates, diagnostics }; + return { id, content, templates, diagnostics }; }; const lgDocument: LGDocument = { uri, @@ -720,26 +710,27 @@ export class LGServer { return; } - const { templates, diagnostics } = lgFile; - // if inline editor, concat new content for validate if (fileId && templateId) { - const templateDiags = checkTemplate({ - name: templateId, - parameters: [], - body: text, - }); + const templateDiags = lgUtil + .checkTemplate({ + name: templateId, + parameters: [], + body: text, + }) + .filter((diagnostic) => { + // ignore non-exist references in template body. + return diagnostic.message.includes('does not have an evaluator') === false; + }); // error in template. if (isValid(templateDiags) === false) { const lspDiagnostics = convertDiagnostics(templateDiags, document, 1); this.sendDiagnostics(document, lspDiagnostics); return; } - const template = templates.find(({ name }) => name === templateId); - if (!template) return; // filter diagnostics belong to this template. - const lgDiagnostics = filterTemplateDiagnostics(diagnostics, template); + const lgDiagnostics = filterTemplateDiagnostics(lgFile, templateId); const lspDiagnostics = convertDiagnostics(lgDiagnostics, document, 1); this.sendDiagnostics(document, lspDiagnostics); return; diff --git a/Composer/packages/tools/language-servers/language-generation/src/lgParser.ts b/Composer/packages/tools/language-servers/language-generation/src/lgParser.ts index af3ff08d00..3d8286bc5f 100644 --- a/Composer/packages/tools/language-servers/language-generation/src/lgParser.ts +++ b/Composer/packages/tools/language-servers/language-generation/src/lgParser.ts @@ -4,10 +4,10 @@ import { fork, ChildProcess } from 'child_process'; import path from 'path'; -import { Templates, Diagnostic } from 'botbuilder-lg'; import { importResolverGenerator } from '@bfc/shared'; import { ResolverResource } from '@bfc/shared'; import uniqueId from 'lodash/uniqueId'; +import { lgIndexer } from '@bfc/indexers'; const isTest = process.env?.NODE_ENV === 'test'; export interface WorkerMsg { @@ -16,28 +16,11 @@ export interface WorkerMsg { payload?: any; } -function createDiagnostic(diagnostic: Diagnostic) { - const { code, range, severity, source, message } = diagnostic; - const { start, end } = range; - return { - code, - range: { - start: { line: start.line, character: start.character }, - end: { line: end.line, character: end.character }, - }, - severity, - source, - message, - }; -} - class LgParserWithoutWorker { public async parseText(content: string, id: string, resources: ResolverResource[]) { - const resolver = importResolverGenerator(resources, '.lg'); - const { allTemplates, allDiagnostics } = Templates.parseText(content, id, resolver); - const templates = allTemplates.map((item) => ({ name: item.name, parameters: item.parameters, body: item.body })); - const diagnostics = allDiagnostics.map((item) => createDiagnostic(item)); - return { templates, diagnostics }; + const lgImportResolver = importResolverGenerator(resources, '.lg'); + const { templates, diagnostics } = lgIndexer.parse(content, id, lgImportResolver); + return { id, content, templates, diagnostics }; } } diff --git a/Composer/packages/tools/language-servers/language-generation/src/lgWorker.ts b/Composer/packages/tools/language-servers/language-generation/src/lgWorker.ts index 4cf26af4e2..ad7c11a61d 100644 --- a/Composer/packages/tools/language-servers/language-generation/src/lgWorker.ts +++ b/Composer/packages/tools/language-servers/language-generation/src/lgWorker.ts @@ -1,36 +1,18 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { Templates, Diagnostic } from 'botbuilder-lg'; import { importResolverGenerator } from '@bfc/shared'; +import { lgIndexer } from '@bfc/indexers'; import { WorkerMsg } from './lgParser'; -function createDiagnostic(diagnostic: Diagnostic) { - const { code, range, severity, source, message } = diagnostic; - const { start, end } = range; - return { - code, - range: { - start: { line: start.line, character: start.character }, - end: { line: end.line, character: end.character }, - }, - severity, - source, - message, - }; -} - process.on('message', (message: WorkerMsg) => { const { content, id, resources } = message.payload; - let templates: any[] = []; - let diagnostics: any[] = []; + try { - const resolver = importResolverGenerator(resources, '.lg'); - const { allTemplates, allDiagnostics } = Templates.parseText(content, id, resolver); - templates = allTemplates.map((item) => ({ name: item.name, parameters: item.parameters, body: item.body })); - diagnostics = allDiagnostics.map((item) => createDiagnostic(item)); - process.send?.({ id: message.id, payload: { templates, diagnostics } }); + const lgImportResolver = importResolverGenerator(resources, '.lg'); + const { templates, diagnostics } = lgIndexer.parse(content, id, lgImportResolver); + process.send?.({ id: message.id, payload: { id, content, templates, diagnostics } }); } catch (error) { process.send?.({ id: message.id, error }); } 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 74abc839b7..5faed76d55 100644 --- a/Composer/packages/tools/language-servers/language-generation/src/utils.ts +++ b/Composer/packages/tools/language-servers/language-generation/src/utils.ts @@ -2,8 +2,8 @@ // Licensed under the MIT License. import { TextDocument, Range, Position, DiagnosticSeverity, Diagnostic } from 'vscode-languageserver-types'; -import { DiagnosticSeverity as LGDiagnosticSeverity, Diagnostic as LGDiagnostic, Templates } from 'botbuilder-lg'; -import { LgTemplate, Diagnostic as BFDiagnostic, LgFile, LgParsed } from '@bfc/shared'; +import { DiagnosticSeverity as LGDiagnosticSeverity } from 'botbuilder-lg'; +import { Diagnostic as BFDiagnostic, LgFile } from '@bfc/shared'; import { offsetRange } from '@bfc/indexers'; // state should map to tokenizer state @@ -36,12 +36,7 @@ export interface LGDocument { projectId?: string; fileId?: string; templateId?: string; - index: () => Promise; -} - -export interface LGParsedResource { - templates: LgTemplate[]; - diagnostics: Diagnostic[]; + index: () => Promise; } export type LGFileResolver = (id: string) => LgFile | undefined; @@ -103,38 +98,6 @@ export function convertDiagnostics(lgDiags: BFDiagnostic[] = [], document: TextD return diagnostics; } -export function textFromTemplate(template: Template): string { - const { name, parameters = [], body } = template; - const textBuilder: string[] = []; - if (name && body) { - textBuilder.push(`# ${name.trim()}`); - if (parameters.length) { - textBuilder.push(`(${parameters.join(', ')})`); - } - textBuilder.push(`\n${template.body.trim()}`); - } - return textBuilder.join(''); -} - -export function checkTemplate(template: Template): LGDiagnostic[] { - const text = textFromTemplate(template); - return Templates.parseText(text, '').diagnostics.filter((diagnostic) => { - // ignore non-exist references in template body. - return diagnostic.message.includes('does not have an evaluator') === false; - }); -} - -export function updateTemplate(content: string, name: string, body: string): string { - const lgFile = Templates.parseText(content); - const template = lgFile.toArray().find((t) => t.name === name); - // add if not exist - if (!template) { - return lgFile.addTemplate(name, [], body).toString(); - } else { - return lgFile.updateTemplate(name, name, template.parameters, body).toString(); - } -} - export const cardTypes = [ 'Typing', 'Suggestions', diff --git a/Composer/packages/ui-plugins/lg/src/LgField.tsx b/Composer/packages/ui-plugins/lg/src/LgField.tsx index 93e06deb77..7d81c691ce 100644 --- a/Composer/packages/ui-plugins/lg/src/LgField.tsx +++ b/Composer/packages/ui-plugins/lg/src/LgField.tsx @@ -64,7 +64,7 @@ const LgField: React.FC> = (props) => { body: getInitialTemplate(name, value), }; - const diagnostics = lgFile ? filterTemplateDiagnostics(lgFile.diagnostics, template) : []; + const diagnostics = lgFile ? filterTemplateDiagnostics(lgFile, template.name) : []; const lgOption = { projectId, diff --git a/Composer/packages/ui-plugins/luis/src/LuisIntentEditor.tsx b/Composer/packages/ui-plugins/luis/src/LuisIntentEditor.tsx index a0b2e0a851..752e5d9176 100644 --- a/Composer/packages/ui-plugins/luis/src/LuisIntentEditor.tsx +++ b/Composer/packages/ui-plugins/luis/src/LuisIntentEditor.tsx @@ -45,7 +45,7 @@ const LuisIntentEditor: React.FC> = (props) => { shellApi.updateUserSettings({ codeEditor: settings }); }; - const diagnostics = luFile ? filterSectionDiagnostics(luFile.diagnostics, luIntent) : []; + const diagnostics = luFile ? filterSectionDiagnostics(luFile, luIntent.Name) : []; return (