diff --git a/Composer/packages/client/package.json b/Composer/packages/client/package.json index 35dacf7ba4..efe6a0bfd9 100644 --- a/Composer/packages/client/package.json +++ b/Composer/packages/client/package.json @@ -27,7 +27,7 @@ "babel-plugin-named-asset-import": "^0.3.1", "babel-preset-react-app": "^7.0.1", "bfj": "6.1.1", - "botbuilder-lg": "https://botbuilder.myget.org/F/botbuilder-declarative/npm/botbuilder-lg/-/4.7.0-preview0.tgz", + "botbuilder-lg": "https://botbuilder.myget.org/F/botbuilder-declarative/npm/botbuilder-lg/-/4.7.0-preview2.tgz", "case-sensitive-paths-webpack-plugin": "2.2.0", "codemirror": "^5.45.0", "css-loader": "1.0.0", diff --git a/Composer/packages/client/src/ShellApi.ts b/Composer/packages/client/src/ShellApi.ts index 9b7a8d59ec..51300e84fc 100644 --- a/Composer/packages/client/src/ShellApi.ts +++ b/Composer/packages/client/src/ShellApi.ts @@ -1,13 +1,12 @@ +/*eslint @typescript-eslint/no-use-before-define: ["error", { "functions": false }]*/ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import React, { useEffect, useContext, useMemo, useState } from 'react'; +import React, { useEffect, useContext, useMemo } from 'react'; import { ShellData } from '@bfc/shared'; import isEqual from 'lodash.isequal'; import get from 'lodash.get'; -import { parseLgTemplate, checkLgContent, updateTemplateInContent } from '../src/store/action/lg'; - import { isExpression } from './utils'; import * as lgUtil from './utils/lgUtil'; import { StoreContext } from './store'; @@ -245,7 +244,7 @@ export const ShellApi: React.FC = () => { if (!file) throw new Error(`lg file ${id} not found`); if (!templateName) throw new Error(`templateName is missing or empty`); - parseLgTemplate(template); + lgUtil.parseLgTemplate(template); await updateLgTemplate({ file, @@ -253,8 +252,8 @@ export const ShellApi: React.FC = () => { template, }); - const content = updateTemplateInContent({ content: file.content, templateName, template }); - return checkLgContent(content); + const content = lgUtil.updateTemplate(file.content, templateName, template); + return lgUtil.checkLgContent(content); } function removeLgTemplateHandler({ id, templateName }, event) { diff --git a/Composer/packages/client/src/pages/language-generation/index.js b/Composer/packages/client/src/pages/language-generation/index.js index b469aae795..e99f890045 100644 --- a/Composer/packages/client/src/pages/language-generation/index.js +++ b/Composer/packages/client/src/pages/language-generation/index.js @@ -165,7 +165,7 @@ export const LGPage = props => { onText={formatMessage('Edit mode')} offText={formatMessage('Edit mode')} checked={editMode} - disabled={(!isRoot && editMode === false) || fileValid === false} + disabled={(!isRoot && editMode === false) || (codeRange === null && fileValid === false)} onChange={onToggleEditMode} /> diff --git a/Composer/packages/client/src/store/action/lg.ts b/Composer/packages/client/src/store/action/lg.ts index b1ccd58acc..dd4857715a 100644 --- a/Composer/packages/client/src/store/action/lg.ts +++ b/Composer/packages/client/src/store/action/lg.ts @@ -7,74 +7,6 @@ import { ActionCreator } from '../types'; import { ActionTypes } from './../../constants'; import httpClient from './../../utils/httpUtil'; -export function textFromTemplates(templates) { - let text = ''; - - templates.forEach(template => { - if (template.Name && (template.Body !== null && template.Body !== undefined)) { - text += `# ${template.Name.trim()}`; - if (template.Parameters && template.Parameters.length > 0) { - text += '(' + template.Parameters.join(', ') + ')'; - } - text += '\n'; - text += `${template.Body.trim()}`; - } - }); - - return text; -} - -// update & remove template & create if not exist -export function updateTemplateInContent({ content, templateName, template }) { - const oldTemplates = lgUtil.parse(content); - if (Array.isArray(oldTemplates) === false) throw new Error('origin lg file is not valid'); - - const originalTemplate = oldTemplates.find(x => x.Name === templateName); - let newContent = content.replace(/\s+$/, ''); - - if (originalTemplate === undefined) { - newContent = `${content}${content ? '\n\n' : ''}${textFromTemplates([template])}\n`; - } else { - const startLineNumber = originalTemplate.ParseTree._start.line; - const endLineNumber = originalTemplate.ParseTree._stop && originalTemplate.ParseTree._stop.line; - - const lines = content.split('\n'); - const contentBefore = lines.slice(0, startLineNumber - 1).join('\n'); - const contentAfter = lines.slice(endLineNumber || lines.length).join('\n'); - const newTemplateContent = textFromTemplates([template]); - - newContent = [contentBefore, newTemplateContent, contentAfter].join('\n'); - } - - return newContent; -} - -/** - * - * @param {Name: string, ?Parameters: string[], Body: string} template - */ -export function parseLgTemplate(template) { - const content = textFromTemplates([template]); - - if (lgUtil.parse(content).length !== 1) { - throw new Error('Not a single template'); - } -} - -/** - * - * @param string, content - */ -export function checkLgContent(content) { - // check lg content, make up error message - - const diagnostics = lgUtil.check(content); - if (lgUtil.isValid(diagnostics) === false) { - const errorMsg = lgUtil.combineMessage(diagnostics); - throw new Error(errorMsg); - } -} - export const updateLgFile: ActionCreator = async ({ dispatch }, { id, content }) => { try { const response = await httpClient.put(`/projects/opened/lgFiles/${id}`, { id, content }); @@ -124,28 +56,16 @@ export const removeLgFile: ActionCreator = async ({ dispatch }, { id }) => { }; export const updateLgTemplate: ActionCreator = async (store, { file, templateName, template }) => { - parseLgTemplate(template); - const newContent = updateTemplateInContent({ content: file.content, templateName, template }); + const newContent = lgUtil.updateTemplate(file.content, templateName, template); return await updateLgFile(store, { id: file.id, content: newContent }); }; -export const createLgTemplate: ActionCreator = async (store, { file, template, position }) => { - parseLgTemplate(template); - - let content = file.content; - if (position === 0) { - content = textFromTemplates([template]) + '\n\n' + content; - } else { - content = content.replace(/\s+$/, '') + '\n\n' + textFromTemplates([template]) + '\n'; - } - - checkLgContent(content); - return await updateLgFile(store, { id: file.id, content }); +export const createLgTemplate: ActionCreator = async (store, { file, template }) => { + const newContent = lgUtil.addTemplate(file.content, template); + return await updateLgFile(store, { id: file.id, content: newContent }); }; export const removeLgTemplate: ActionCreator = async (store, { file, templateName }) => { - const newContent = updateTemplateInContent({ content: file.content, templateName, template: {} }); - checkLgContent(newContent); - + const newContent = lgUtil.removeTemplate(file.content, templateName); return await updateLgFile(store, { id: file.id, content: newContent }); }; diff --git a/Composer/packages/client/src/utils/lgUtil.ts b/Composer/packages/client/src/utils/lgUtil.ts index 062c76763d..8e812b6f93 100644 --- a/Composer/packages/client/src/utils/lgUtil.ts +++ b/Composer/packages/client/src/utils/lgUtil.ts @@ -6,14 +6,19 @@ import { get } from 'lodash'; const lgStaticChecker = new StaticChecker(); -const lgImportResolver = new ImportResolver(); +const lgImportResolver = ImportResolver.fileResolver; + +interface Template { + Name: string; + Parameters: string[]; + Body: string; +} export function isValid(diagnostics: Diagnostic[]): boolean { return diagnostics.every(d => d.Severity !== DiagnosticSeverity.Error); } export function check(content: string, id = ''): Diagnostic[] { - // @ts-ignore return lgStaticChecker.checkText(content, id, lgImportResolver); } @@ -31,3 +36,60 @@ export function combineMessage(diagnostics: Diagnostic[]): string { return msg; }, ''); } + +export function checkLgContent(content: string) { + // check lg content, make up error message + const diagnostics = check(content); + if (isValid(diagnostics) === false) { + const errorMsg = combineMessage(diagnostics); + throw new Error(errorMsg); + } +} + +export function updateTemplate(content: string, templateName: string, { Name, Parameters, Body }: Template): string { + const resource = LGParser.parse(content); + // add if not exist + if (resource.Templates.findIndex(t => t.Name === templateName) === -1) { + return resource.addTemplate(Name, Parameters, Body).toString(); + } else { + return resource.updateTemplate(templateName, Name, Parameters, Body).toString(); + } +} + +export function addTemplate(content: string, { Name, Parameters, Body }: Template): string { + const resource = LGParser.parse(content); + return resource.addTemplate(Name, Parameters, Body).toString(); +} + +export function removeTemplate(content: string, templateName: string): string { + const resource = LGParser.parse(content); + return resource.deleteTemplate(templateName).toString(); +} + +export function textFromTemplates(templates) { + let text = ''; + + templates.forEach(template => { + if (template.Name && (template.Body !== null && template.Body !== undefined)) { + text += `# ${template.Name.trim()}`; + if (template.Parameters && template.Parameters.length > 0) { + text += '(' + template.Parameters.join(', ') + ')'; + } + text += '\n'; + text += `${template.Body.trim()}`; + } + }); + + return text; +} +/** + * + * @param {Name: string, ?Parameters: string[], Body: string} template + */ +export function parseLgTemplate(template) { + const content = textFromTemplates([template]); + + if (parse(content).length !== 1) { + throw new Error('Not a single template'); + } +} diff --git a/Composer/packages/lib/indexers/package.json b/Composer/packages/lib/indexers/package.json index 126e817288..0601597182 100644 --- a/Composer/packages/lib/indexers/package.json +++ b/Composer/packages/lib/indexers/package.json @@ -28,7 +28,7 @@ }, "dependencies": { "botbuilder-expression-parser": "^4.5.11", - "botbuilder-lg": "https://botbuilder.myget.org/F/botbuilder-declarative/npm/botbuilder-lg/-/4.7.0-preview0.tgz", + "botbuilder-lg": "https://botbuilder.myget.org/F/botbuilder-declarative/npm/botbuilder-lg/-/4.7.0-preview2.tgz", "lodash.get": "^4.4.2", "lodash.has": "^4.5.2", "lodash.uniq": "^4.5.0", diff --git a/Composer/yarn.lock b/Composer/yarn.lock index 583babce3c..09b683dfe8 100644 --- a/Composer/yarn.lock +++ b/Composer/yarn.lock @@ -3964,9 +3964,18 @@ botbuilder-expression-parser@^4.5.9: xmldom "^0.1.27" xpath "0.0.27" -"botbuilder-lg@https://botbuilder.myget.org/F/botbuilder-declarative/npm/botbuilder-lg/-/4.7.0-preview0.tgz": - version "4.7.0-preview0" - resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/botbuilder-lg/-/4.7.0-preview0.tgz#46ac6e53e44ba64ae56971ab8404c3f2f9ab239d" +"botbuilder-lg@https://botbuilder.myget.org/F/botbuilder-declarative/npm/botbuilder-lg/-/4.7.0-preview2.tgz": + version "4.7.0-preview2" + resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/botbuilder-lg/-/4.7.0-preview2.tgz#2a3dd7d7991e0727fb88630501d2fb14d475537b" + dependencies: + antlr4ts "0.5.0-alpha.1" + botframework-expressions "https://botbuilder.myget.org/F/botbuilder-declarative/npm/botframework-expressions/-/4.7.0-preview0.tgz" + lodash "^4.17.11" + uuid "^3.3.3" + +"botbuilder-lg@https://botbuilder.myget.org/F/botbuilder-declarative/npm/botbuilder-lg/-/4.7.0-preview2.tgz": + version "4.7.0-preview2" + resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/botbuilder-lg/-/4.7.0-preview2.tgz#2a3dd7d7991e0727fb88630501d2fb14d475537b" dependencies: antlr4ts "0.5.0-alpha.1" botframework-expressions "https://botbuilder.myget.org/F/botbuilder-declarative/npm/botframework-expressions/-/4.7.0-preview0.tgz"