diff --git a/.vscode/launch.json b/.vscode/launch.json
index 669e566005..4b0516a1ef 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -9,7 +9,23 @@
"webRoot": "${workspaceFolder}"
},
{
- "name": "LSP Server",
+ "name": "LU LSP Server",
+ "type": "node",
+ "request": "launch",
+ "args": [
+ "${workspaceFolder}/Composer/packages/tools/language-servers/language-understanding/demo/src/server.ts"
+ ],
+ "runtimeArgs": [
+ "--nolazy",
+ "-r",
+ "${workspaceFolder}/Composer/node_modules/ts-node/register"
+ ],
+ "sourceMaps": true,
+ "cwd": "${workspaceFolder}/Composer/packages/tools/language-servers/language-understanding/demo/src",
+ "protocol": "inspector",
+ },
+ {
+ "name": "LG LSP Server",
"type": "node",
"request": "launch",
"args": [
diff --git a/Composer/packages/client/setupProxy.js b/Composer/packages/client/setupProxy.js
index a75cd21360..522cc762f7 100644
--- a/Composer/packages/client/setupProxy.js
+++ b/Composer/packages/client/setupProxy.js
@@ -4,7 +4,7 @@ const paths = require('./config/paths');
const proxySetting = require(paths.appPackageJson).proxy;
module.exports = function(app) {
- const wsProxy = proxy('/lg-language-server', {
+ const wsProxy = proxy(['/lg-language-server', '/lu-language-server'], {
target: proxySetting.replace('/^http/', 'ws'),
changeOrigin: true,
ws: true,
diff --git a/Composer/packages/client/src/pages/language-understanding/code-editor.js b/Composer/packages/client/src/pages/language-understanding/code-editor.js
index 04ccd189dc..a51ba5b456 100644
--- a/Composer/packages/client/src/pages/language-understanding/code-editor.js
+++ b/Composer/packages/client/src/pages/language-understanding/code-editor.js
@@ -43,6 +43,14 @@ export default function CodeEditor(props) {
minimap: 'on',
lineDecorationsWidth: undefined,
lineNumbersMinChars: false,
+ glyphMargin: true,
+ autoClosingBrackets: 'always',
+ wordBasedSuggestions: false,
+ autoIndent: true,
+ formatOnType: true,
+ lightbulb: {
+ enabled: true,
+ },
}}
errorMsg={errorMsg}
value={content}
diff --git a/Composer/packages/lib/code-editor/demo/src/index.tsx b/Composer/packages/lib/code-editor/demo/src/index.tsx
index 2be749e1f3..db9db561d6 100644
--- a/Composer/packages/lib/code-editor/demo/src/index.tsx
+++ b/Composer/packages/lib/code-editor/demo/src/index.tsx
@@ -4,9 +4,9 @@
import React from 'react';
import ReactDOM from 'react-dom';
-import App from './lgEditor';
+// import App from './lgEditor';
// import App from './jsonEditor';
-// import App from './luEditor';
+import App from './luEditor';
// import App from './lgJsonEditor';
// import App from './inlineEditor';
// import App from './multiEditors';
diff --git a/Composer/packages/lib/code-editor/demo/src/luEditor.tsx b/Composer/packages/lib/code-editor/demo/src/luEditor.tsx
index 4b333bc10b..6dedb51fac 100644
--- a/Composer/packages/lib/code-editor/demo/src/luEditor.tsx
+++ b/Composer/packages/lib/code-editor/demo/src/luEditor.tsx
@@ -224,6 +224,10 @@ export default function App() {
const props = {
value,
onChange,
+ languageServer: {
+ port: 5003,
+ path: '/lu-language-server',
+ },
};
return ;
}
diff --git a/Composer/packages/lib/code-editor/src/LuEditor.tsx b/Composer/packages/lib/code-editor/src/LuEditor.tsx
index a31b60a7ea..c3967415a6 100644
--- a/Composer/packages/lib/code-editor/src/LuEditor.tsx
+++ b/Composer/packages/lib/code-editor/src/LuEditor.tsx
@@ -2,12 +2,139 @@
// Licensed under the MIT License.
import React from 'react';
+import { listen, MessageConnection } from 'vscode-ws-jsonrpc';
+import * as monacoEditor from '@bfcomposer/monaco-editor/esm/vs/editor/editor.api';
+import * as monacoCore from 'monaco-editor-core';
+import get from 'lodash/get';
+import { MonacoServices, MonacoLanguageClient } from 'monaco-languageclient';
+import { registerLULanguage } from './languages';
+import { createUrl, createWebSocket, createLanguageClient } from './utils/lspUtil';
import { RichEditor, RichEditorProps } from './RichEditor';
-const LU_HELP = 'https://aka.ms/lu-file-format';
-const placeholder = `> See ${LU_HELP} to learn about supported LU concepts.`;
+const LU_HELP = 'https://github.com/microsoft/botframework-cli/blob/master/packages/lu/docs/lu-file-format.md';
+const placeholder = `> To learn more about the LU file format, read the documentation at
+> ${LU_HELP}`;
-export function LuEditor(props: RichEditorProps) {
- return ;
+export interface LUOption {
+ inline: boolean;
+ content: string;
+ template?: {
+ name: string;
+ parameters?: string[];
+ body: string;
+ };
+}
+
+export interface LULSPEditorProps extends RichEditorProps {
+ luOption?: LUOption;
+ languageServer?:
+ | {
+ host?: string;
+ hostname?: string;
+ port?: number | string;
+ path: string;
+ }
+ | string;
+}
+
+const defaultLUServer = {
+ path: '/lu-language-server',
+};
+declare global {
+ interface Window {
+ monacoServiceInstance: MonacoServices;
+ monacoLUEditorInstance: MonacoLanguageClient;
+ }
+}
+
+type ServerEdit = {
+ range: { start: { line: number; character: number }; end: { line: number; character: number } };
+ newText: string;
+};
+
+/*
+convert the edits results from the server to an exectable object in manoco editor
+*/
+function convertEdit(serverEdit: ServerEdit) {
+ return {
+ range: {
+ startLineNumber: serverEdit.range.start.line,
+ startColumn: serverEdit.range.start.character,
+ endLineNumber: serverEdit.range.end.line,
+ endColumn: serverEdit.range.end.character,
+ },
+ text: serverEdit.newText,
+ forceMoveMarkers: true,
+ };
+}
+
+export function LuEditor(props: LULSPEditorProps) {
+ const options = {
+ quickSuggestions: true,
+ wordBasedSuggestions: false,
+ formatOnType: true,
+ };
+
+ const { languageServer, ...restProps } = props;
+ const luServer = languageServer || defaultLUServer;
+
+ const editorWillMount = (monaco: typeof monacoEditor) => {
+ registerLULanguage(monaco);
+ if (typeof props.editorWillMount === 'function') {
+ return props.editorWillMount(monaco);
+ }
+ };
+ const editorDidMount = (editor: monacoEditor.editor.IStandaloneCodeEditor, monaco: typeof monacoEditor) => {
+ if (!window.monacoServiceInstance) {
+ window.monacoServiceInstance = MonacoServices.install(editor as monacoCore.editor.IStandaloneCodeEditor | any);
+ }
+
+ if (!window.monacoLUEditorInstance) {
+ 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);
+ if (!window.monacoLUEditorInstance) {
+ window.monacoLUEditorInstance = languageClient;
+ }
+
+ editor.addCommand(monaco.KeyMod.Shift | monaco.KeyCode.Enter, function() {
+ const position = editor.getPosition();
+ languageClient.sendRequest('labelingExperienceRequest', { uri, position });
+ });
+ 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());
+ },
+ });
+ }
+
+ if (typeof props.editorDidMount === 'function') {
+ return props.editorDidMount(editor, monaco);
+ }
+ };
+
+ return (
+
+ );
}
diff --git a/Composer/packages/lib/code-editor/src/languages/index.ts b/Composer/packages/lib/code-editor/src/languages/index.ts
index 7014f7824e..eff516de15 100644
--- a/Composer/packages/lib/code-editor/src/languages/index.ts
+++ b/Composer/packages/lib/code-editor/src/languages/index.ts
@@ -2,3 +2,4 @@
// Licensed under the MIT License.
export * from './lg';
+export * from './lu';
diff --git a/Composer/packages/lib/code-editor/src/languages/lu.ts b/Composer/packages/lib/code-editor/src/languages/lu.ts
new file mode 100644
index 0000000000..9102017824
--- /dev/null
+++ b/Composer/packages/lib/code-editor/src/languages/lu.ts
@@ -0,0 +1,77 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+import * as monacoEditor from '@bfcomposer/monaco-editor/esm/vs/editor/editor.api';
+
+export function registerLULanguage(monaco: typeof monacoEditor) {
+ monaco.languages.setMonarchTokensProvider('lu', {
+ tokenizer: {
+ root: [
+ [/^\s*#/, { token: 'intent', next: '@intent' }],
+ [/^\s*@/, { token: 'entity-identifier', goBack: 1, next: '@entityMode' }],
+ [/^\s*>\s*[\s\S]*$/, { token: 'comments' }],
+ ],
+
+ intent: [
+ [/^\s*#/, { token: 'intent', next: '@intent' }],
+ [/^\s*-/, { token: 'utterrance-indentifier', next: '@utterrance' }],
+ [/^\s*>\s*[\s\S]*$/, { token: 'comments' }],
+ [/^\s*@/, { token: 'entity-identifier', goBack: 1, next: '@entityMode' }],
+ [/.*$/, 'intent'],
+ ],
+ utterrance: [
+ [/^\s*#/, { token: 'intent', next: '@intent' }],
+ [/^\s*>\s*[\s\S]*$/, { token: 'comments' }],
+ [/^\s*-/, { token: 'utterrance-indentifier', next: 'utterrance' }],
+ [/^\s*@/, { token: 'entity-identifier', goBack: 1, next: '@entityMode' }],
+ [/({)(\s*[\w.@:\s]*\s*)(=)(\s*[\w.]*\s*)(})/, ['lb', 'pattern', 'equal', 'entity-name', 'rb']],
+ [/({\s*@)(\s*[\w.]*\s*)(})/, ['lb', 'entity-name', 'rb']],
+ // eslint-disable-next-line security/detect-unsafe-regex
+ [/\s*\[[\w\s.]+\]\(.{1,2}\/[\w.*]+(#[\w.?]+)?\)/, 'import-desc'],
+ [/./, 'utterance-other'],
+ ],
+ entityMode: [
+ [/^\s*#/, { token: 'intent', next: '@intent' }],
+ [/^\s*>\s*[\s\S]*$/, { token: 'comments' }],
+ [/^\s*-/, { token: 'utterrance-indentifier', next: 'utterrance' }],
+ [
+ /(@\s*)(ml|prebuilt|regex|list|composite|patternany|phraselist)(\s*\w*)/,
+ ['intent-indentifier', 'entity-type', 'entity-name'],
+ ],
+ [/(@\s*)(\s*\w*)/, ['intent-indentifier', 'entity-name']],
+ [/\s*(hasRoles|useFeature)\s*/, 'keywords'],
+ [/.*$/, 'entity-other', '@pop'],
+ ],
+ },
+ });
+
+ monaco.languages.register({
+ id: 'lu',
+ extensions: ['.lu'],
+ aliases: ['LU', 'language-understanding'],
+ mimetypes: ['application/lu'],
+ });
+
+ monaco.languages.setLanguageConfiguration('lu', {
+ autoClosingPairs: [
+ { open: '{', close: '}' },
+ { open: '[', close: ']' },
+ { open: '(', close: ')' },
+ ],
+ });
+
+ monaco.editor.defineTheme('lu', {
+ base: 'vs',
+ inherit: false,
+ colors: {},
+ rules: [
+ { token: 'intent', foreground: '0000FF' },
+ { token: 'pattern', foreground: '00B7C3' },
+ { token: 'entity-name', foreground: '038387' },
+ { token: 'comments', foreground: '7A7A7A' },
+ { token: 'import-desc', foreground: '00A32B' },
+ { token: 'entity-type', foreground: 'DF2C2C' },
+ { token: 'keywords', foreground: '0078D7' },
+ ],
+ });
+}
diff --git a/Composer/packages/server/package.json b/Composer/packages/server/package.json
index 1265a41e56..3920015655 100644
--- a/Composer/packages/server/package.json
+++ b/Composer/packages/server/package.json
@@ -55,6 +55,7 @@
"@azure/ms-rest-js": "^1.8.7",
"@bfc/indexers": "*",
"@bfc/lg-languageserver": "*",
+ "@bfc/lu-languageserver": "*",
"@bfc/shared": "*",
"@bfcomposer/lubuild": "1.1.2-preview",
"archiver": "^3.0.0",
diff --git a/Composer/packages/server/src/server.ts b/Composer/packages/server/src/server.ts
index 4a514bbc22..07492ac3c3 100644
--- a/Composer/packages/server/src/server.ts
+++ b/Composer/packages/server/src/server.ts
@@ -13,6 +13,7 @@ import * as ws from 'ws';
import * as rpc from 'vscode-ws-jsonrpc';
import { IConnection, createConnection } from 'vscode-languageserver';
import { LGServer } from '@bfc/lg-languageserver';
+import { LUServer } from '@bfc/lu-languageserver';
import { BotProjectService } from './services/project';
import { getAuthProvider } from './router/auth';
@@ -124,6 +125,14 @@ function launchLanguageServer(socket: rpc.IWebSocket) {
server.start();
}
+function launchLuLanguageServer(socket: rpc.IWebSocket) {
+ const reader = new rpc.WebSocketMessageReader(socket);
+ const writer = new rpc.WebSocketMessageWriter(socket);
+ const connection: IConnection = createConnection(reader, writer);
+ const server = new LUServer(connection);
+ server.start();
+}
+
attachLSPServer(wss, server, '/lg-language-server', webSocket => {
// launch language server when the web socket is opened
if (webSocket.readyState === webSocket.OPEN) {
@@ -134,3 +143,14 @@ attachLSPServer(wss, server, '/lg-language-server', webSocket => {
});
}
});
+
+attachLSPServer(wss, server, '/lu-language-server', webSocket => {
+ // launch language server when the web socket is opened
+ if (webSocket.readyState === webSocket.OPEN) {
+ launchLuLanguageServer(webSocket);
+ } else {
+ webSocket.on('open', () => {
+ launchLuLanguageServer(webSocket);
+ });
+ }
+});
diff --git a/Composer/packages/tools/language-servers/language-generation/package.json b/Composer/packages/tools/language-servers/language-generation/package.json
index b05fb13a4d..3d8e889244 100644
--- a/Composer/packages/tools/language-servers/language-generation/package.json
+++ b/Composer/packages/tools/language-servers/language-generation/package.json
@@ -17,6 +17,7 @@
"dependencies": {
"@bfc/indexers": "*",
"botbuilder-lg": "4.7.0-preview.93464",
+ "botframework-expressions": "4.7.0-preview.93464",
"request-light": "^0.2.2",
"vscode-languageserver": "^5.3.0-next"
},
diff --git a/Composer/packages/tools/language-servers/language-understanding/.gitignore b/Composer/packages/tools/language-servers/language-understanding/.gitignore
new file mode 100644
index 0000000000..0437368d03
--- /dev/null
+++ b/Composer/packages/tools/language-servers/language-understanding/.gitignore
@@ -0,0 +1,3 @@
+/lib
+/demo/lib
+/demo/dist
\ No newline at end of file
diff --git a/Composer/packages/tools/language-servers/language-understanding/README.md b/Composer/packages/tools/language-servers/language-understanding/README.md
new file mode 100644
index 0000000000..2088fcec58
--- /dev/null
+++ b/Composer/packages/tools/language-servers/language-understanding/README.md
@@ -0,0 +1,27 @@
+# Language-understanding language server demo
+
+
+
+## goto language-understanding directory, install packages, run
+```
+npm install
+```
+
+## How to start the demo
+
+#### 1. under language-understanding directory, run
+```
+yarn start
+```
+
+### 2. go to code-editor directory
+
+start luEditor demo, connect :5003/lu-language-server
+
+## Features
+
+### 1.Auto-suggestions for entity types and defined entities
+
+### 2.Syntax and semantic diagonostics
+
+### Auto-copletion for ml entities and list entities
diff --git a/Composer/packages/tools/language-servers/language-understanding/__tests__/app.test.ts b/Composer/packages/tools/language-servers/language-understanding/__tests__/app.test.ts
new file mode 100644
index 0000000000..09cb074566
--- /dev/null
+++ b/Composer/packages/tools/language-servers/language-understanding/__tests__/app.test.ts
@@ -0,0 +1,94 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+import fs from 'fs';
+
+import WebSocket from 'ws';
+
+import { startServer } from './helpers/server';
+
+const luFile = fs.readFileSync(`${__dirname}/mocks/greeting.lu`, 'utf-8');
+const initializeParams = fs.readFileSync(`${__dirname}/mocks/initialize-params.json`, 'utf-8');
+
+const ws = new WebSocket('ws://localhost:50003/lu-language-server');
+
+type messageResolver = (data) => void;
+
+const subscribers: messageResolver[] = [];
+
+ws.on('message', data => {
+ const subscriber = subscribers.shift();
+ if (subscriber) {
+ subscriber(data);
+ }
+});
+
+function send(data, resolvers?: messageResolver[]): Promise {
+ ws.send(data);
+ return new Promise(resolve => {
+ if (typeof resolvers === 'undefined') {
+ subscribers.push(data => {
+ resolve(data);
+ });
+ return;
+ }
+ if (resolvers.length === 0) {
+ resolve();
+ return;
+ }
+ let count = 0;
+ const readers = resolvers.map(resolver => {
+ return data => {
+ count++;
+ resolver(JSON.parse(data));
+ if (resolvers.length === count) {
+ resolve(JSON.parse(data));
+ }
+ };
+ });
+ subscribers.push(...readers);
+ });
+}
+
+function jsonEscape(str) {
+ return str
+ .replace(/\n/g, '\\n')
+ .replace(/\r/g, '\\r')
+ .replace(/\t/g, '\\t');
+}
+
+const content = jsonEscape(luFile);
+
+describe('lu lsp server', () => {
+ const server = startServer();
+ beforeAll(async () => {
+ await new Promise(resolve => {
+ ws.on('open', () => {
+ resolve();
+ });
+ });
+ });
+
+ afterAll(async done => {
+ ws.close();
+ server.close();
+ done();
+ });
+ it('websocket should connect server', async () => {
+ await send(`{ "jsonrpc":"2.0","id":0,"method":"initialize","params": ${initializeParams} }`, [
+ response => {
+ expect(response.method).toEqual('window/logMessage');
+ expect(response.params.type).toEqual(4);
+ },
+ response => {
+ expect(response.id).toEqual(0);
+ },
+ ]);
+ // client initialized
+ await send(`{"jsonrpc":"2.0","method":"initialized","params":{}}`, []);
+ await send(
+ `{"jsonrpc":"2.0","method":"textDocument/didOpen","params": {"textDocument":{"uri":"inmemory://model/1","languageId":"lu","version":2,"text": "${content}" }}}`,
+ []
+ );
+ });
+});
diff --git a/Composer/packages/tools/language-servers/language-understanding/__tests__/helpers/server.ts b/Composer/packages/tools/language-servers/language-understanding/__tests__/helpers/server.ts
new file mode 100644
index 0000000000..1f2f8aad8f
--- /dev/null
+++ b/Composer/packages/tools/language-servers/language-understanding/__tests__/helpers/server.ts
@@ -0,0 +1,77 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+import * as http from 'http';
+import * as url from 'url';
+import * as net from 'net';
+
+import * as ws from 'ws';
+import * as rpc from 'vscode-ws-jsonrpc';
+import { IConnection, createConnection } from 'vscode-languageserver';
+import express from 'express';
+
+import { LUServer } from '../../src';
+
+function createSocketHandler(webSocket: any): rpc.IWebSocket {
+ const socket: rpc.IWebSocket = {
+ send: content =>
+ webSocket.send(content, error => {
+ if (error) {
+ throw error;
+ }
+ }),
+ onMessage: cb => webSocket.on('message', cb),
+ onError: cb => webSocket.on('error', cb),
+ onClose: cb => webSocket.on('close', cb),
+ dispose: () => webSocket.close(),
+ };
+ return socket;
+}
+
+function attachLSPServer(wss: ws.Server, server: http.Server, path: string, handler: (webSocket) => void) {
+ server.on('upgrade', (request: http.IncomingMessage, socket: net.Socket, head: Buffer) => {
+ const pathname = request.url ? url.parse(request.url).pathname : undefined;
+ if (pathname === path) {
+ wss.handleUpgrade(request, socket, head, webSocket => {
+ const socketHandler = createSocketHandler(webSocket);
+ handler(socketHandler);
+ });
+ }
+ });
+}
+
+function launchLanguageServer(socket: rpc.IWebSocket) {
+ const reader = new rpc.WebSocketMessageReader(socket);
+ const writer = new rpc.WebSocketMessageWriter(socket);
+ const connection: IConnection = createConnection(reader, writer);
+ return new LUServer(connection);
+}
+
+export function startServer() {
+ // create the express application
+ const app = express();
+ // server the static content, i.e. index.html
+ app.use(express.static(__dirname));
+ // start the server
+ const server = app.listen(50003);
+
+ const wss: ws.Server = new ws.Server({
+ noServer: true,
+ perMessageDeflate: false,
+ });
+ attachLSPServer(wss, server, '/lu-language-server', webSocket => {
+ // const socketHandler = createSocketHandler(webSocket);
+
+ // launch language server when the web socket is opened
+ if (webSocket.readyState === webSocket.OPEN) {
+ const server = launchLanguageServer(webSocket);
+ server.start();
+ } else {
+ webSocket.on('open', () => {
+ const server = launchLanguageServer(webSocket);
+ server.start();
+ });
+ }
+ });
+ return server;
+}
diff --git a/Composer/packages/tools/language-servers/language-understanding/__tests__/mocks/greeting.lu b/Composer/packages/tools/language-servers/language-understanding/__tests__/mocks/greeting.lu
new file mode 100644
index 0000000000..5ce8120767
--- /dev/null
+++ b/Composer/packages/tools/language-servers/language-understanding/__tests__/mocks/greeting.lu
@@ -0,0 +1,7 @@
+> Greeting intent and related utterances
+# Greeting
+- hi
+- hello
+- hiya
+- how are you?
+- how do you do?
diff --git a/Composer/packages/tools/language-servers/language-understanding/__tests__/mocks/initialize-params.json b/Composer/packages/tools/language-servers/language-understanding/__tests__/mocks/initialize-params.json
new file mode 100644
index 0000000000..c9cdfd678b
--- /dev/null
+++ b/Composer/packages/tools/language-servers/language-understanding/__tests__/mocks/initialize-params.json
@@ -0,0 +1,238 @@
+{
+ "rootPath": null,
+ "rootUri": null,
+ "capabilities": {
+ "workspace": {
+ "applyEdit": true,
+ "workspaceEdit": {
+ "documentChanges": true,
+ "resourceOperations": [
+ "create",
+ "rename",
+ "delete"
+ ],
+ "failureHandling": "textOnlyTransactional"
+ },
+ "didChangeConfiguration": {
+ "dynamicRegistration": true
+ },
+ "didChangeWatchedFiles": {
+ "dynamicRegistration": true
+ },
+ "symbol": {
+ "dynamicRegistration": true,
+ "symbolKind": {
+ "valueSet": [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15,
+ 16,
+ 17,
+ 18,
+ 19,
+ 20,
+ 21,
+ 22,
+ 23,
+ 24,
+ 25,
+ 26
+ ]
+ }
+ },
+ "executeCommand": {
+ "dynamicRegistration": true
+ },
+ "configuration": true,
+ "workspaceFolders": true
+ },
+ "textDocument": {
+ "publishDiagnostics": {
+ "relatedInformation": true,
+ "tagSupport": true
+ },
+ "synchronization": {
+ "dynamicRegistration": true,
+ "willSave": true,
+ "willSaveWaitUntil": true,
+ "didSave": true
+ },
+ "completion": {
+ "dynamicRegistration": true,
+ "contextSupport": true,
+ "completionItem": {
+ "snippetSupport": true,
+ "commitCharactersSupport": true,
+ "documentationFormat": [
+ "markdown",
+ "plaintext"
+ ],
+ "deprecatedSupport": true,
+ "preselectSupport": true
+ },
+ "completionItemKind": {
+ "valueSet": [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15,
+ 16,
+ 17,
+ 18,
+ 19,
+ 20,
+ 21,
+ 22,
+ 23,
+ 24,
+ 25
+ ]
+ }
+ },
+ "hover": {
+ "dynamicRegistration": true,
+ "contentFormat": [
+ "markdown",
+ "plaintext"
+ ]
+ },
+ "signatureHelp": {
+ "dynamicRegistration": true,
+ "signatureInformation": {
+ "documentationFormat": [
+ "markdown",
+ "plaintext"
+ ],
+ "parameterInformation": {
+ "labelOffsetSupport": true
+ }
+ }
+ },
+ "definition": {
+ "dynamicRegistration": true,
+ "linkSupport": true
+ },
+ "references": {
+ "dynamicRegistration": true
+ },
+ "documentHighlight": {
+ "dynamicRegistration": true
+ },
+ "documentSymbol": {
+ "dynamicRegistration": true,
+ "symbolKind": {
+ "valueSet": [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15,
+ 16,
+ 17,
+ 18,
+ 19,
+ 20,
+ 21,
+ 22,
+ 23,
+ 24,
+ 25,
+ 26
+ ]
+ },
+ "hierarchicalDocumentSymbolSupport": true
+ },
+ "codeAction": {
+ "dynamicRegistration": true,
+ "codeActionLiteralSupport": {
+ "codeActionKind": {
+ "valueSet": [
+ "",
+ "quickfix",
+ "refactor",
+ "refactor.extract",
+ "refactor.inline",
+ "refactor.rewrite",
+ "source",
+ "source.organizeImports"
+ ]
+ }
+ }
+ },
+ "codeLens": {
+ "dynamicRegistration": true
+ },
+ "formatting": {
+ "dynamicRegistration": true
+ },
+ "rangeFormatting": {
+ "dynamicRegistration": true
+ },
+ "onTypeFormatting": {
+ "dynamicRegistration": true
+ },
+ "rename": {
+ "dynamicRegistration": true,
+ "prepareSupport": true
+ },
+ "documentLink": {
+ "dynamicRegistration": true
+ },
+ "typeDefinition": {
+ "dynamicRegistration": true,
+ "linkSupport": true
+ },
+ "implementation": {
+ "dynamicRegistration": true,
+ "linkSupport": true
+ },
+ "colorProvider": {
+ "dynamicRegistration": true
+ },
+ "foldingRange": {
+ "dynamicRegistration": true,
+ "rangeLimit": 5000,
+ "lineFoldingOnly": true
+ },
+ "declaration": {
+ "dynamicRegistration": true,
+ "linkSupport": true
+ }
+ }
+ },
+ "trace": "off",
+ "workspaceFolders": null
+}
diff --git a/Composer/packages/tools/language-servers/language-understanding/__tests__/unitTest/serverUtil.test.ts b/Composer/packages/tools/language-servers/language-understanding/__tests__/unitTest/serverUtil.test.ts
new file mode 100644
index 0000000000..77e367d057
--- /dev/null
+++ b/Composer/packages/tools/language-servers/language-understanding/__tests__/unitTest/serverUtil.test.ts
@@ -0,0 +1,227 @@
+/* eslint-disable @typescript-eslint/no-var-requires */
+/* eslint-disable @typescript-eslint/camelcase */
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+const assert = require('assert');
+
+const util = require('../../lib/matchingPattern');
+
+const text = `@ml location hasRoles homeaddress
+@ composite city1
+@ regex zipcode = /[0-9]{5}/
+@ ml mail usesFeature address
+#checktemperature
+-it is sunny and the temperature is
+- the temperature is 35
+- address is {address=[beijing,100080]}
+`;
+
+const luisObject = {
+ intents: [],
+ entities: [{ name: 'address', roles: ['role1'] }],
+ composites: [{ name: 'geoInfo', children: [], roles: [] }],
+ closedLists: [{ name: 'city', subLists: [], roles: ['role2'] }],
+ regex_entities: [
+ {
+ name: 'zipcode',
+ regexPattern: '[0-9]{5}',
+ roles: [],
+ },
+ ],
+ model_features: [],
+ regex_features: [],
+ utterances: [],
+ patterns: [],
+ patternAnyEntities: [],
+ prebuiltEntities: [],
+};
+
+describe('LU LSP Server Function Unit Tests', function() {
+ it('Test Get ML Entities', function() {
+ const result = util.getMLEntities(text);
+ assert.deepStrictEqual(result, ['location', 'mail']);
+ });
+
+ it('Test Get Composites Entities', function() {
+ const result = util.getCompositesEntities(luisObject);
+ assert.deepStrictEqual(result, ['geoInfo']);
+ });
+
+ it('Test Get RegExp Entities', function() {
+ const result = util.getRegexEntities(luisObject);
+ assert.deepStrictEqual(result, ['zipcode']);
+ });
+
+ it('Test Get All Parsed Entities', function() {
+ const result = util.getSuggestionEntities(luisObject, util.suggestionAllEntityTypes);
+ assert.deepStrictEqual(result, ['address', 'zipcode', 'city', 'geoInfo']);
+ });
+
+ it('Test Get All Parsed Roles', function() {
+ const result = util.getSuggestionRoles(luisObject, util.suggestionAllEntityTypes);
+ assert.deepStrictEqual(result, ['role1', 'role2']);
+ });
+
+ it('Test Entity Can UsesFeature', function() {
+ let lineContent = '@ geoInfo usesFeature ';
+ let result = util.matchedEntityCanUsesFeature(lineContent, text, luisObject);
+ assert.equal(result, true);
+
+ lineContent = '@ composites newAddress2 usesFeature ';
+ result = util.matchedEntityCanUsesFeature(lineContent, text, luisObject);
+ assert.equal(result, true);
+
+ lineContent = '@ ml newAddress usesFeature ';
+ result = util.matchedEntityCanUsesFeature(lineContent, text, luisObject);
+ assert.equal(result, true);
+
+ lineContent = '@ zipcode usesFeature ';
+ result = util.matchedEntityCanUsesFeature(lineContent, text, luisObject);
+ assert.equal(result, false);
+ });
+
+ it('Test Intent Can UsesFeature', function() {
+ let text = '@ intent mockIntent usesFeature ';
+ let result = util.matchIntentUsesFeatures(text);
+ assert.equal(result, true);
+
+ text = '@ intent mockIntent useFeature ';
+ result = util.matchIntentUsesFeatures(text);
+ assert.equal(result, false);
+ });
+
+ it('Test Intent In a Entity Definiton', function() {
+ let text = '@ intent mockIntent';
+ let result = util.matchIntentInEntityDef(text);
+ assert.equal(result, true);
+
+ text = '@ intents mockIntent ';
+ result = util.matchIntentInEntityDef(text);
+ assert.equal(result, false);
+ });
+
+ it('Test Entity Definition', function() {
+ let text = '@ ';
+ let result = util.isEntityType(text);
+ assert.equal(result, true);
+
+ text = '@ ml ';
+ result = util.isEntityType(text);
+ assert.equal(result, false);
+ });
+
+ it('Test Prebuilt Entity Definition', function() {
+ let text = '@ prebuilt ';
+ let result = util.isPrebuiltEntity(text);
+ assert.equal(result, true);
+
+ text = '@ prebuilt ml1 ';
+ result = util.isPrebuiltEntity(text);
+ assert.equal(result, false);
+ });
+
+ it('Test RegExp Entity Definition', function() {
+ let text = '@ regex zipcode =';
+ let result = util.isRegexEntity(text);
+ assert.equal(result, true);
+
+ text = '@ ml ml1 ';
+ result = util.isRegexEntity(text);
+ assert.equal(result, false);
+ });
+
+ it('Test Seperated Line Entity', function() {
+ let text = '@ zipcode =';
+ let result = util.isSeperatedEntityDef(text);
+ assert.equal(result, true);
+
+ text = '@ ml ml1 ';
+ result = util.isSeperatedEntityDef(text);
+ assert.equal(result, false);
+ });
+
+ it('Test Entity Name ', function() {
+ let text = '@ ml location';
+ let result = util.isEntityName(text);
+ assert.equal(result, true);
+
+ text = '@ location';
+ result = util.isEntityName(text);
+ assert.equal(result, true);
+
+ text = '@ intent ';
+ result = util.isEntityName(text);
+ assert.equal(result, false);
+ });
+
+ it('Test Composite Entity ', function() {
+ let text = '@ composite location = [';
+ let result = util.isCompositeEntity(text);
+ assert.equal(result, true);
+
+ text = '@ composite location = []';
+ result = util.isCompositeEntity(text);
+ assert.equal(result, true);
+
+ text = '@ intent ';
+ result = util.isCompositeEntity(text);
+ assert.equal(result, false);
+ });
+
+ it('Test Entering Pattern ', function() {
+ let text = '- The weather in Seattle is { ';
+ let result = util.matchedEnterPattern(text);
+ assert.equal(result, true);
+
+ text = '- The weather in Seattle is { }';
+ result = util.matchedEnterPattern(text);
+ assert.equal(result, true);
+
+ text = '- The weather in Seattle is ';
+ result = util.matchedEnterPattern(text);
+ assert.equal(result, false);
+ });
+
+ it('Test Entering Roles ', function() {
+ let text = '- The weather in Seattle is {morning: ';
+ let result = util.matchedRolesPattern(text);
+ assert.equal(result, true);
+
+ text = '- The weather in Seattle is {morning: }';
+ result = util.matchedRolesPattern(text);
+ assert.equal(result, true);
+
+ text = '- The weather in Seattle is { ';
+ result = util.matchedRolesPattern(text);
+ assert.equal(result, false);
+ });
+
+ it('Test Entering Entity ', function() {
+ let text = '- The weather in Seattle is {@ ';
+ let result = util.matchedEntityPattern(text);
+ assert.equal(result, true);
+
+ text = '- The weather in Seattle is {@}';
+ result = util.matchedEntityPattern(text);
+ assert.equal(result, true);
+
+ text = '- The weather in Seattle is { ';
+ result = util.matchedEntityPattern(text);
+ assert.equal(result, false);
+ });
+
+ it('Test Remove Labeled Utterance', function() {
+ let text = '- this is a {type = Audio} message from {device = PC}';
+ let result = util.removeLabelsInUtterance(text);
+ assert.equal(result, '- this is a Audio message from PC');
+
+ text = '- this is a {@ type = Audio} message from {@device = PC}';
+ result = util.removeLabelsInUtterance(text);
+ assert.equal(result, '- this is a Audio message from PC');
+
+ text = '- this is a { type: role =Audio} message from {@device = PC}';
+ result = util.removeLabelsInUtterance(text);
+ assert.equal(result, '- this is a Audio message from PC');
+ });
+});
diff --git a/Composer/packages/tools/language-servers/language-understanding/demo/.eslintrc.js b/Composer/packages/tools/language-servers/language-understanding/demo/.eslintrc.js
new file mode 100644
index 0000000000..328b328945
--- /dev/null
+++ b/Composer/packages/tools/language-servers/language-understanding/demo/.eslintrc.js
@@ -0,0 +1,7 @@
+module.exports = {
+ extends: ['../../../../../.eslintrc.react.js'],
+ parserOptions: {
+ project: './tsconfig.json',
+ tsconfigRootDir: __dirname,
+ },
+};
diff --git a/Composer/packages/tools/language-servers/language-understanding/demo/src/attach.ts b/Composer/packages/tools/language-servers/language-understanding/demo/src/attach.ts
new file mode 100644
index 0000000000..82e3ca58c8
--- /dev/null
+++ b/Composer/packages/tools/language-servers/language-understanding/demo/src/attach.ts
@@ -0,0 +1,31 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+import * as http from 'http';
+import * as url from 'url';
+import * as net from 'net';
+
+import * as ws from 'ws';
+import * as rpc from 'vscode-ws-jsonrpc';
+
+export function createSocketHandler(webSocket: any): rpc.IWebSocket {
+ const socket: rpc.IWebSocket = {
+ send: content => webSocket.send(content),
+ onMessage: cb => webSocket.on('message', cb),
+ onError: cb => webSocket.on('error', cb),
+ onClose: cb => webSocket.on('close', cb),
+ dispose: () => webSocket.close(),
+ };
+ return socket;
+}
+
+export function attachLSPServer(wss: ws.Server, server: http.Server, path: string, handler: (webSocket) => void) {
+ server.on('upgrade', (request: http.IncomingMessage, socket: net.Socket, head: Buffer) => {
+ const pathname = request.url ? url.parse(request.url).pathname : undefined;
+ if (pathname === path) {
+ wss.handleUpgrade(request, socket, head, webSocket => {
+ handler(createSocketHandler(webSocket));
+ });
+ }
+ });
+}
diff --git a/Composer/packages/tools/language-servers/language-understanding/demo/src/server.ts b/Composer/packages/tools/language-servers/language-understanding/demo/src/server.ts
new file mode 100644
index 0000000000..64392d5b54
--- /dev/null
+++ b/Composer/packages/tools/language-servers/language-understanding/demo/src/server.ts
@@ -0,0 +1,44 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+// import { attachLSPServer } from '../../src/attachWSToHTTPServer';
+import * as ws from 'ws';
+import * as rpc from 'vscode-ws-jsonrpc';
+import { IConnection, createConnection } from 'vscode-languageserver';
+
+import { LUServer } from '../../src';
+
+import { attachLSPServer } from './attach';
+
+const express = require('express');
+
+// create the express application
+const app = express();
+// server the static content, i.e. index.html
+app.use(express.static(__dirname));
+// start the server
+const server = app.listen(5003);
+
+const wss: ws.Server = new ws.Server({
+ noServer: true,
+ perMessageDeflate: false,
+});
+
+function launchLanguageServer(socket: rpc.IWebSocket) {
+ const reader = new rpc.WebSocketMessageReader(socket);
+ const writer = new rpc.WebSocketMessageWriter(socket);
+ const connection: IConnection = createConnection(reader, writer);
+ const server = new LUServer(connection);
+ server.start();
+ return server;
+}
+
+attachLSPServer(wss, server, '/lu-language-server', webSocket => {
+ if (webSocket.readyState === webSocket.OPEN) {
+ launchLanguageServer(webSocket);
+ } else {
+ webSocket.on('open', () => {
+ launchLanguageServer(webSocket);
+ });
+ }
+});
diff --git a/Composer/packages/tools/language-servers/language-understanding/demo/tsconfig.json b/Composer/packages/tools/language-servers/language-understanding/demo/tsconfig.json
new file mode 100644
index 0000000000..715831d5b4
--- /dev/null
+++ b/Composer/packages/tools/language-servers/language-understanding/demo/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "extends": "../tsconfig.json",
+ "include": [
+ "./src/**/*",
+ "./__tests__/**/*"
+ ],
+ "exclude": [
+ "node_modules"
+ ],
+}
\ No newline at end of file
diff --git a/Composer/packages/tools/language-servers/language-understanding/jest.config.js b/Composer/packages/tools/language-servers/language-understanding/jest.config.js
new file mode 100644
index 0000000000..20a62c0671
--- /dev/null
+++ b/Composer/packages/tools/language-servers/language-understanding/jest.config.js
@@ -0,0 +1,18 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+const path = require('path');
+
+module.exports = {
+ preset: 'ts-jest/presets/js-with-babel',
+ testPathIgnorePatterns: ['/node_modules/', '/helpers/', '/mocks/'],
+ watchPathIgnorePatterns: ['/__tests__/mocks'],
+ globals: {
+ 'ts-jest': {
+ tsConfig: path.resolve(__dirname, './tsconfig.json'),
+ diagnostics: {
+ warnOnly: true,
+ },
+ },
+ },
+};
diff --git a/Composer/packages/tools/language-servers/language-understanding/package.json b/Composer/packages/tools/language-servers/language-understanding/package.json
new file mode 100644
index 0000000000..fa8ebaddee
--- /dev/null
+++ b/Composer/packages/tools/language-servers/language-understanding/package.json
@@ -0,0 +1,51 @@
+{
+ "name": "@bfc/lu-languageserver",
+ "license": "MIT",
+ "version": "0.1.0",
+ "main": "lib/index.js",
+ "scripts": {
+ "build": "tsc --build tsconfig.build.json",
+ "build:demo": "cd demo && tsc --build tsconfig.json",
+ "prepublishOnly": "npm run build",
+ "clean": "rimraf lib demo/dist",
+ "start": "cd demo && ts-node ./src/server.ts",
+ "test": "jest",
+ "lint": "eslint --quiet --ext .js,.jsx,.ts,.tsx ./src ./__tests__",
+ "lint:fix": "yarn lint --fix",
+ "lint:typecheck": "tsc --noEmit",
+ "startDemo": "concurrently --kill-others \"yarn start:server\" \"yarn start:client\"",
+ "start:client": "cd demo && webpack-dev-server",
+ "start:server": "cd demo && ts-node ./src/server.ts"
+ },
+ "dependencies": {
+ "@microsoft/bf-cli-command": "https://botbuilder.myget.org/F/botbuilder-declarative/npm/@microsoft/bf-cli-command/-/@microsoft/bf-cli-command-1.0.0.tgz",
+ "@bfcomposer/bf-lu": "https://botbuilder.myget.org/F/botbuilder-declarative/npm/@bfcomposer/bf-lu/-/@bfcomposer/bf-lu-1.1.8.tgz",
+ "@types/node": "^12.0.4",
+ "express": "^4.15.2",
+ "monaco-editor-core": "^0.17.0",
+ "monaco-languageclient": "^0.10.0",
+ "normalize-url": "^2.0.1",
+ "reconnecting-websocket": "^3.2.2",
+ "request-light": "^0.2.2",
+ "vscode": "^1.1.34",
+ "vscode-json-languageservice": "^3.3.1",
+ "vscode-languageserver": "^5.3.0-next",
+ "vscode-ws-jsonrpc": "^0.1.1",
+ "ws": "^5.0.0"
+ },
+ "devDependencies": {
+ "clean-webpack-plugin": "^3.0.0",
+ "css-loader": "^3.2.0",
+ "html-webpack-plugin": "^3.2.0",
+ "source-map-loader": "^0.2.4",
+ "style-loader": "^1.0.0",
+ "ts-loader": "^6.1.0",
+ "ts-node": "^8.3.0",
+ "typescript": "^3.6.3",
+ "uglifyjs-webpack-plugin": "^2.2.0",
+ "webpack": "^4.39.3",
+ "webpack-cli": "^3.3.8",
+ "webpack-dev-server": "^3.8.0",
+ "webpack-merge": "^4.2.2"
+ }
+}
diff --git a/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts b/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts
new file mode 100644
index 0000000000..d5f6855cb6
--- /dev/null
+++ b/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts
@@ -0,0 +1,551 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+import * as fs from 'fs';
+
+import { xhr, getErrorStatusDescription } from 'request-light';
+import URI from 'vscode-uri';
+import { IConnection, TextDocuments } from 'vscode-languageserver';
+import {
+ TextDocument,
+ Diagnostic,
+ CompletionList,
+ Position,
+ CompletionItem,
+ CompletionItemKind,
+ Range,
+ DiagnosticSeverity,
+ TextEdit,
+} from 'vscode-languageserver-types';
+import { TextDocumentPositionParams, DocumentOnTypeFormattingParams } from 'vscode-languageserver-protocol';
+
+import { EntityTypesObj, LineState } from './entityEnum';
+import * as util from './matchingPattern';
+
+const parseFile = require('@bfcomposer/bf-lu/lib/parser/lufile/parseFileContents.js').parseFile;
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+const validateLUISBlob = require('@bfcomposer/bf-lu/lib/parser/luis/luisValidator');
+const LABELEXPERIENCEREQUEST = 'labelingExperienceRequest';
+export class LUServer {
+ protected workspaceRoot: URI | undefined;
+ protected readonly documents = new TextDocuments();
+ protected readonly pendingValidationRequests = new Map();
+
+ constructor(protected readonly connection: IConnection) {
+ this.documents.listen(this.connection);
+ this.documents.onDidChangeContent(change => this.validate(change.document));
+ this.documents.onDidClose(event => {
+ this.cleanPendingValidation(event.document);
+ this.cleanDiagnostics(event.document);
+ });
+
+ this.connection.onInitialize(params => {
+ if (params.rootPath) {
+ this.workspaceRoot = URI.file(params.rootPath);
+ } else if (params.rootUri) {
+ this.workspaceRoot = URI.parse(params.rootUri);
+ }
+ this.connection.console.log('The server is initialized.');
+ return {
+ capabilities: {
+ textDocumentSync: this.documents.syncKind,
+ codeActionProvider: false,
+ completionProvider: {
+ resolveProvider: true,
+ triggerCharacters: ['@', ' ', '{', ':', '['],
+ },
+ foldingRangeProvider: false,
+ documentOnTypeFormattingProvider: {
+ firstTriggerCharacter: '\n',
+ },
+ },
+ };
+ });
+ this.connection.onCompletion(params => this.completion(params));
+ this.connection.onDocumentOnTypeFormatting(docTypingParams => this.docTypeFormat(docTypingParams));
+ this.connection.onRequest((method, params) => {
+ if (method === LABELEXPERIENCEREQUEST) {
+ this.labelingExperienceHandler(params);
+ }
+ });
+ }
+
+ start() {
+ this.connection.listen();
+ }
+
+ protected labelingExperienceHandler(params: any): void {
+ const document: TextDocument | undefined = this.documents.get(params.uri);
+ if (!document) {
+ return;
+ }
+ const position = params.position;
+ const range = Range.create(position.lineNumber - 1, 0, position.lineNumber - 1, position.column);
+ const curLineContent = document.getText(range);
+ // eslint-disable-next-line security/detect-unsafe-regex
+ const labeledUtterRegex = /^\s*-([^{}]*\s*\{[\w.@:\s]+\s*=\s*[\w.]+\}[^{}]*)+$/;
+
+ if (labeledUtterRegex.test(curLineContent)) {
+ const newText = util.removeLabelsInUtterance(curLineContent);
+
+ const newPos = Position.create(position.lineNumber, 0);
+ const newUnlalbelText = newText + '\n';
+ const editPreviousLine: TextEdit = TextEdit.insert(newPos, newUnlalbelText);
+ const newPos2 = Position.create(position.lineNumber, curLineContent.length + 1);
+ const editNextLine: TextEdit = TextEdit.insert(newPos2, '\n' + newUnlalbelText);
+ const edits: TextEdit[] = [editPreviousLine, editNextLine];
+ this.connection.sendNotification('addUnlabelUtterance', { edits: edits });
+ }
+ }
+
+ protected async docTypeFormat(params: DocumentOnTypeFormattingParams): Promise {
+ const document = this.documents.get(params.textDocument.uri);
+ if (!document) {
+ return Promise.resolve(null);
+ }
+
+ const lastLineContent = this.getLastLineContent(params);
+ const edits: TextEdit[] = [];
+ const curLineNumber = params.position.line;
+ const lineCount = document.lineCount;
+ const text = document.getText();
+ const lines = text.split('\n');
+ const position = params.position;
+ const textBeforeCurLine = lines.slice(0, curLineNumber).join('\n');
+ const range = Range.create(position.line, 0, position.line, position.character);
+ const curLineContent = document.getText(range);
+ const key = params.ch;
+ const inputState = this.getInputLineState(params);
+
+ const pos = params.position;
+ if (
+ key === '\n' &&
+ inputState === 'utterance' &&
+ lastLineContent.trim() !== '-' &&
+ curLineNumber === lineCount - 1
+ ) {
+ const newPos = Position.create(pos.line + 1, 0);
+ const item: TextEdit = TextEdit.insert(newPos, '- ');
+ edits.push(item);
+ }
+
+ if (key === '\n' && inputState === 'mlEntity' && lastLineContent.trim().endsWith('=')) {
+ const mlEntities = util.getMLEntities(textBeforeCurLine);
+ const entityNameRegExp = /^\s*@\s*([0-9a-zA-Z_.-]+)\s*.*/;
+ let entityName = '';
+ if (entityNameRegExp.test(lastLineContent)) {
+ const entityGroup = lastLineContent.match(entityNameRegExp);
+ if (entityGroup && entityGroup.length >= 2) {
+ entityName = entityGroup[1];
+ }
+ if (mlEntities.includes(entityName)) {
+ const newPos = Position.create(pos.line, 0);
+ const item: TextEdit = TextEdit.insert(newPos, '\t-@');
+ edits.push(item);
+ }
+ }
+ }
+
+ if (
+ key === '\n' &&
+ inputState === 'listEntity' &&
+ lastLineContent.trim() !== '-' &&
+ curLineNumber === lineCount - 1
+ ) {
+ const newPos = Position.create(pos.line + 1, 0);
+ let insertStr = '';
+ if (lastLineContent.trim().endsWith(':') || lastLineContent.trim().endsWith('=')) {
+ insertStr = '\t-';
+ } else {
+ insertStr = '-';
+ }
+ const item: TextEdit = TextEdit.insert(newPos, insertStr);
+ edits.push(item);
+ }
+
+ if (lastLineContent.trim() === '-') {
+ const range = Range.create(pos.line - 1, 0, pos.line, curLineContent.length + 1);
+ const item: TextEdit = TextEdit.del(range);
+ edits.push(item);
+ }
+
+ return Promise.resolve(edits);
+ }
+
+ private getLastLineContent(params: TextDocumentPositionParams): string {
+ const document = this.documents.get(params.textDocument.uri);
+ if (!document) {
+ return '';
+ }
+ const content = document.getText();
+ const position = params.position;
+ if (position.line === 0) {
+ return '';
+ } else {
+ return content.split('\n')[position.line - 1];
+ }
+ }
+
+ private getInputLineState(params: DocumentOnTypeFormattingParams): LineState {
+ const document = this.documents.get(params.textDocument.uri);
+ const position = params.position;
+ const regListEnity = /^\s*@\s*list\s*.*$/;
+ const regUtterance = /^\s*#.*$/;
+ const regDashLine = /^\s*-.*$/;
+ const mlEntity = /^\s*@\s*ml\s*.*$/;
+ const regEntityDefLine = /^\s*@.*$/;
+ let state: LineState = 'other';
+ if (!document) {
+ return 'other';
+ }
+ const lineContentList = document.getText().split('\n');
+ for (let i = 0; i < position.line; i++) {
+ const line = lineContentList[i];
+ if (regListEnity.test(line)) {
+ state = 'listEntity';
+ } else if (regUtterance.test(line)) {
+ state = 'utterance';
+ } else if (mlEntity.test(line)) {
+ state = 'mlEntity';
+ } else if (regDashLine.test(line) || regEntityDefLine.test(line) || line.trim().startsWith('>')) {
+ continue;
+ } else {
+ state = 'other';
+ }
+ }
+
+ return state;
+ }
+
+ protected async resovleSchema(url: string): Promise {
+ const uri = URI.parse(url);
+ if (uri.scheme === 'file') {
+ return new Promise((resolve, reject) => {
+ // eslint-disable-next-line security/detect-non-literal-fs-filename
+ fs.readFile(uri.fsPath, 'UTF-8', (err, result) => {
+ err ? reject('') : resolve(result.toString());
+ });
+ });
+ }
+ try {
+ const response = await xhr({ url, followRedirects: 5 });
+ return response.responseText;
+ } catch (error) {
+ return Promise.reject(error.responseText || getErrorStatusDescription(error.status) || error.toString());
+ }
+ }
+
+ private async validateLuBody(content: string): Promise<{ parsedContent: any; errors: any }> {
+ const errors: Diagnostic[] = [];
+ let parsedContent: any;
+ const log = false;
+ const locale = 'en-us';
+ try {
+ parsedContent = await parseFile(content, log, locale);
+ if (parsedContent !== undefined) {
+ try {
+ validateLUISBlob(parsedContent.LUISJsonStructure);
+ } catch (e) {
+ e.diagnostics.forEach(diag => {
+ const range = Range.create(0, 0, 0, 1);
+ const message = diag.Message;
+ const severity = DiagnosticSeverity.Error;
+ errors.push(Diagnostic.create(range, message, severity));
+ });
+ }
+ }
+ } catch (e) {
+ e.diagnostics.forEach(diag => {
+ const range = Range.create(
+ diag.Range.Start.Line - 1,
+ diag.Range.Start.Character,
+ diag.Range.End.Line - 1,
+ diag.Range.End.Character
+ );
+ const message = diag.Message;
+ const severity = DiagnosticSeverity.Error;
+ errors.push(Diagnostic.create(range, message, severity));
+ });
+ }
+
+ return Promise.resolve({ parsedContent, errors });
+ }
+
+ private async extractLUISContent(text: string): Promise {
+ let parsedContent: any;
+ const log = false;
+ const locale = 'en-us';
+ try {
+ parsedContent = await parseFile(text, log, locale);
+ } catch (e) {
+ // nothing to do in catch block
+ }
+
+ if (parsedContent !== undefined) {
+ return Promise.resolve(parsedContent.LUISJsonStructure);
+ } else {
+ return undefined;
+ }
+ }
+
+ protected async completion(params: TextDocumentPositionParams): Promise {
+ const document = this.documents.get(params.textDocument.uri);
+ if (!document) {
+ return Promise.resolve(null);
+ }
+
+ const position = params.position;
+ const range = Range.create(position.line, 0, position.line, position.character);
+ const curLineContent = document.getText(range);
+ const text = document.getText();
+ const lines = text.split('\n');
+ const curLineNumber = params.position.line;
+ //const textBeforeCurLine = lines.slice(0, curLineNumber).join('\n');
+ const textExceptCurLine = lines
+ .slice(0, curLineNumber)
+ .concat(lines.slice(curLineNumber + 1))
+ .join('\n');
+ const completionList: CompletionItem[] = [];
+ if (util.isEntityType(curLineContent)) {
+ const entityTypes: string[] = EntityTypesObj.EntityType;
+ entityTypes.forEach(entity => {
+ const item = {
+ label: entity,
+ kind: CompletionItemKind.Keyword,
+ insertText: `${entity}`,
+ documentation: `Enitity type: ${entity}`,
+ };
+
+ completionList.push(item);
+ });
+ }
+
+ if (util.isPrebuiltEntity(curLineContent)) {
+ const prebuiltTypes: string[] = EntityTypesObj.Prebuilt;
+ prebuiltTypes.forEach(entity => {
+ const item = {
+ label: entity,
+ kind: CompletionItemKind.Keyword,
+ insertText: `${entity}`,
+ documentation: `Prebuilt enitity: ${entity}`,
+ };
+
+ completionList.push(item);
+ });
+ }
+
+ if (util.isRegexEntity(curLineContent)) {
+ const item = {
+ label: 'RegExp Entity',
+ kind: CompletionItemKind.Keyword,
+ insertText: `//`,
+ documentation: `regex enitity`,
+ };
+
+ completionList.push(item);
+ }
+
+ if (util.isEntityName(curLineContent)) {
+ const item = {
+ label: 'hasRoles?',
+ kind: CompletionItemKind.Keyword,
+ insertText: `hasRoles`,
+ documentation: `Entity name hasRole?`,
+ };
+
+ completionList.push(item);
+ const item2 = {
+ label: 'usesFeature?',
+ kind: CompletionItemKind.Keyword,
+ insertText: `usesFeature`,
+ documentation: `Entity name usesFeature?`,
+ };
+
+ completionList.push(item2);
+ }
+
+ // completion for entities and patterns, use the text without current line due to usually it will cause parser errors, the luisjson will be undefined
+
+ let luisJson = await this.extractLUISContent(text);
+ if (!luisJson) {
+ luisJson = await this.extractLUISContent(textExceptCurLine);
+ }
+
+ const suggestionEntityList = util.getSuggestionEntities(luisJson, util.suggestionAllEntityTypes);
+ const regexEntityList = util.getRegexEntities(luisJson);
+
+ //suggest a regex pattern for seperated line definition
+ if (util.isSeperatedEntityDef(curLineContent)) {
+ const seperatedEntityDef = /^\s*@\s*([\w._]+|"[\w._\s]+")+\s*=\s*$/;
+ let entityName = '';
+ const matchGroup = curLineContent.match(seperatedEntityDef);
+ if (matchGroup && matchGroup.length >= 2) {
+ entityName = matchGroup[1].trim();
+ }
+
+ if (regexEntityList.includes(entityName)) {
+ const item = {
+ label: 'RegExp Entity',
+ kind: CompletionItemKind.Keyword,
+ insertText: `//`,
+ documentation: `regex enitity`,
+ };
+
+ completionList.push(item);
+ }
+ }
+
+ // auto suggest pattern
+ if (util.matchedEnterPattern(curLineContent)) {
+ suggestionEntityList.forEach(name => {
+ const item = {
+ label: `Entity: ${name}`,
+ kind: CompletionItemKind.Property,
+ insertText: `${name}`,
+ documentation: `pattern suggestion for entity: ${name}`,
+ };
+
+ completionList.push(item);
+ });
+ }
+
+ // suggestions for entities in a seperated line
+ if (util.isEntityType(curLineContent)) {
+ suggestionEntityList.forEach(entity => {
+ const item = {
+ label: entity,
+ kind: CompletionItemKind.Property,
+ insertText: `${entity}`,
+ documentation: `Enitity type: ${entity}`,
+ };
+
+ completionList.push(item);
+ });
+ }
+
+ if (util.isCompositeEntity(curLineContent)) {
+ util.getSuggestionEntities(luisJson, util.suggestionNoCompositeEntityTypes).forEach(entity => {
+ const item = {
+ label: entity,
+ kind: CompletionItemKind.Property,
+ insertText: `${entity}`,
+ documentation: `Enitity type: ${entity}`,
+ };
+
+ completionList.push(item);
+ });
+ }
+
+ const suggestionRolesList = util.getSuggestionRoles(luisJson, util.suggestionAllEntityTypes);
+ // auto suggest roles
+ if (util.matchedRolesPattern(curLineContent)) {
+ suggestionRolesList.forEach(name => {
+ const item = {
+ label: `Role: ${name}`,
+ kind: CompletionItemKind.Property,
+ insertText: `${name}`,
+ documentation: `roles suggestion for entity name: ${name}`,
+ };
+
+ completionList.push(item);
+ });
+ }
+
+ if (util.matchedEntityPattern(curLineContent)) {
+ suggestionEntityList.forEach(name => {
+ const item = {
+ label: `Entity: ${name}`,
+ kind: CompletionItemKind.Property,
+ insertText: ` ${name}`,
+ documentation: `pattern suggestion for entity: ${name}`,
+ };
+ completionList.push(item);
+ });
+ }
+
+ if (util.matchedEntityCanUsesFeature(curLineContent, text, luisJson)) {
+ const enitityName = util.extractEntityNameInUseFeature(curLineContent);
+ const suggestionFeatureList = util.getSuggestionEntities(luisJson, util.suggestionNoPatternAnyEntityTypes);
+ suggestionFeatureList.forEach(name => {
+ if (name !== enitityName) {
+ const item = {
+ label: `Entity: ${name}`,
+ kind: CompletionItemKind.Method,
+ insertText: `${name}`,
+ documentation: `Feature suggestion for current entity: ${name}`,
+ };
+
+ completionList.push(item);
+ }
+ });
+ }
+
+ if (util.matchIntentInEntityDef(curLineContent)) {
+ const item = {
+ label: 'usesFeature?',
+ kind: CompletionItemKind.Keyword,
+ insertText: `usesFeature`,
+ documentation: `Does this intent usesFeature?`,
+ };
+
+ completionList.push(item);
+ }
+
+ if (util.matchIntentUsesFeatures(curLineContent)) {
+ const suggestionFeatureList = util.getSuggestionEntities(luisJson, util.suggestionNoPatternAnyEntityTypes);
+ suggestionFeatureList.forEach(name => {
+ const item = {
+ label: `Entity: ${name}`,
+ kind: CompletionItemKind.Method,
+ insertText: `${name}`,
+ documentation: `Feature suggestion for current entity: ${name}`,
+ };
+
+ completionList.push(item);
+ });
+ }
+
+ return Promise.resolve({ isIncomplete: false, items: completionList });
+ }
+
+ protected validate(document: TextDocument): void {
+ this.cleanPendingValidation(document);
+ document.uri,
+ setTimeout(() => {
+ this.pendingValidationRequests.delete(document.uri);
+ this.doValidate(document);
+ });
+ }
+
+ protected cleanPendingValidation(document: TextDocument): void {
+ const request = this.pendingValidationRequests.get(document.uri);
+ if (request !== undefined) {
+ clearTimeout(request);
+ this.pendingValidationRequests.delete(document.uri);
+ }
+ }
+
+ protected doValidate(document: TextDocument): void {
+ if (document.getText().length === 0) {
+ this.cleanDiagnostics(document);
+ return;
+ }
+
+ const text = document.getText();
+ this.validateLuBody(text).then(result => {
+ const diagnostics: Diagnostic[] = result.errors;
+ this.sendDiagnostics(document, diagnostics);
+ });
+ }
+
+ protected cleanDiagnostics(document: TextDocument): void {
+ this.sendDiagnostics(document, []);
+ }
+
+ protected sendDiagnostics(document: TextDocument, diagnostics: Diagnostic[]): void {
+ this.connection.sendDiagnostics({
+ uri: document.uri,
+ diagnostics,
+ });
+ }
+}
diff --git a/Composer/packages/tools/language-servers/language-understanding/src/entityEnum.ts b/Composer/packages/tools/language-servers/language-understanding/src/entityEnum.ts
new file mode 100644
index 0000000000..c30b5c1039
--- /dev/null
+++ b/Composer/packages/tools/language-servers/language-understanding/src/entityEnum.ts
@@ -0,0 +1,26 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+export const EntityTypesObj = {
+ EntityType: ['ml', 'prebuilt', 'regex', 'list', 'composite', 'patternany', 'phraselist'],
+ Prebuilt: [
+ 'age',
+ 'datetimeV2',
+ 'dimension',
+ 'email',
+ 'geographyV2',
+ 'keyPhrase',
+ 'money',
+ 'number',
+ 'ordinal',
+ 'ordinalV2',
+ 'percentage',
+ 'personName',
+ 'phonenumber',
+ 'temperature',
+ 'url',
+ 'datetime',
+ ],
+};
+
+export type LineState = 'listEntity' | 'utterance' | 'mlEntity' | 'other';
diff --git a/Composer/packages/tools/language-servers/language-understanding/src/index.ts b/Composer/packages/tools/language-servers/language-understanding/src/index.ts
new file mode 100644
index 0000000000..0b5ae34d3c
--- /dev/null
+++ b/Composer/packages/tools/language-servers/language-understanding/src/index.ts
@@ -0,0 +1,4 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+export * from './LUServer';
diff --git a/Composer/packages/tools/language-servers/language-understanding/src/matchingPattern.ts b/Composer/packages/tools/language-servers/language-understanding/src/matchingPattern.ts
new file mode 100644
index 0000000000..6f1cc9fec2
--- /dev/null
+++ b/Composer/packages/tools/language-servers/language-understanding/src/matchingPattern.ts
@@ -0,0 +1,225 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+export function getMLEntities(text: string): string[] {
+ const lines = text.split('\n');
+
+ const mlEntityRegExp = /^\s*@\s*ml\s*([0-9a-zA-Z_.-]+)\s*.*$/;
+ const mlEntities: string[] = [];
+ for (const line of lines) {
+ if (mlEntityRegExp.test(line)) {
+ const entityGroup = line.match(mlEntityRegExp);
+ if (entityGroup && entityGroup.length >= 2) {
+ mlEntities.push(entityGroup[1]);
+ }
+ }
+ }
+
+ return mlEntities;
+}
+
+export function getCompositesEntities(luisJson: any): string[] {
+ const suggestionCompositesList: string[] = [];
+ if (luisJson !== undefined) {
+ if (luisJson.composites !== undefined && luisJson.composites.length > 0) {
+ luisJson.composites.forEach(entity => {
+ suggestionCompositesList.push(entity.name);
+ });
+ }
+ }
+
+ return suggestionCompositesList;
+}
+
+export function matchedEntityCanUsesFeature(lineContent: string, text: string, luisJson: any): boolean {
+ const mlTypedEntityusesFeature = /^\s*@\s*ml\s*\w+\s*usesFeature\s*$/;
+ const compositesTypedEntityusesFeature = /^\s*@\s*composites\s*\w+\s*usesFeature\s*$/;
+ const notTypedEntityusesFeature = /^\s*@\s*(\w*)\s*usesFeature\s*$/;
+ if (mlTypedEntityusesFeature.test(lineContent) || compositesTypedEntityusesFeature.test(lineContent)) {
+ return true;
+ } else if (notTypedEntityusesFeature.test(lineContent)) {
+ const matchedGroups = lineContent.match(notTypedEntityusesFeature);
+ if (matchedGroups && matchedGroups.length >= 2) {
+ const entityName = matchedGroups[1];
+ const validEntitiesCanusesFeature = getMLEntities(text).concat(getCompositesEntities(luisJson));
+ if (validEntitiesCanusesFeature.includes(entityName)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+export function matchIntentUsesFeatures(content: string): boolean {
+ const intentUsesFeaturesRegEx = /^\s*@\s*intent\s*\w*\s*usesFeature\s*$/;
+ return intentUsesFeaturesRegEx.test(content);
+}
+
+export function matchIntentInEntityDef(content: string): boolean {
+ const intentInEntityDefRegEx = /^\s*@\s*intent\s*\w+\s*$/;
+ return intentInEntityDefRegEx.test(content);
+}
+
+export function isEntityType(content: string): boolean {
+ const regexEntifyDef = /^\s*@\s*$/;
+ return regexEntifyDef.test(content);
+}
+
+export function isPrebuiltEntity(content: string): boolean {
+ const regexPrebuiltEntifyDef = /^\s*@\s*prebuilt\s*$/;
+ return regexPrebuiltEntifyDef.test(content);
+}
+
+export function isRegexEntity(content: string): boolean {
+ const regexPrebuiltEntifyDef = /^\s*@\s*regex\s*([\w._]+|"[\w._\s]+")+\s*=\s*$/;
+ return regexPrebuiltEntifyDef.test(content);
+}
+
+export function isSeperatedEntityDef(content: string): boolean {
+ const regexPrebuiltEntifyDef = /^\s*@\s*([\w._]+|"[\w._\s]+")+\s*=\s*$/;
+ return regexPrebuiltEntifyDef.test(content);
+}
+
+export function isEntityName(content: string): boolean {
+ const hasNameEntifyDef = /^\s*@\s*(ml|list|regex|prebuilt|composite|patternany|phraselist)\s*([\w._]+|"[\w._\s]+")\s*$/;
+ const hasTypeEntityDef = /^\s*@\s*(ml|list|regex|prebuilt|composite|patternany|phraselist|intent)\s*$/;
+ const hasNameEntifyDef2 = /^\s*@\s*([\w._]+|"[\w._\s]+")\s*$/;
+ return hasNameEntifyDef.test(content) || (!hasTypeEntityDef.test(content) && hasNameEntifyDef2.test(content));
+}
+
+export function isCompositeEntity(content: string): boolean {
+ const compositePatternDef = /^\s*@\s*composite\s*[\w]*\s*=\s*\[\s*.*\s*$/;
+ const compositePatternDef2 = /^\s*@\s*composite\s*[\w]*\s*=\s*\[\s*.*\s*\]\s*$/;
+ return compositePatternDef.test(content) || compositePatternDef2.test(content);
+}
+export function matchedEnterPattern(content: string): boolean {
+ const regexPatternDef = /^\s*-.*{\s*$/;
+ const regexPatternDef2 = /^\s*-.*{\s*}$/;
+ return regexPatternDef.test(content) || regexPatternDef2.test(content);
+}
+
+export function matchedRolesPattern(content: string): boolean {
+ const regexRolesPatternDef = /^\s*-.*{\s*.*:/;
+ const regexRolesPatternDef2 = /^\s*-.*{\s*.*:}/;
+ return regexRolesPatternDef.test(content) || regexRolesPatternDef2.test(content);
+}
+
+export function matchedEntityPattern(content: string): boolean {
+ const regexRolesEntityPatternDef = /^\s*-.*{\s*@\s*$/;
+ const regexRolesEntityPatternDef2 = /^\s*-.*{\s*@\s*}\s*$/;
+ return regexRolesEntityPatternDef.test(content) || regexRolesEntityPatternDef2.test(content);
+}
+
+export function getRegexEntities(luisJson: any): string[] {
+ const suggestionRegexList: string[] = [];
+ if (luisJson !== undefined) {
+ if (luisJson.regex_entities !== undefined && luisJson.regex_entities.length > 0) {
+ luisJson.regex_entities.forEach(entity => {
+ suggestionRegexList.push(entity.name);
+ });
+ }
+ }
+
+ return suggestionRegexList;
+}
+
+export function getSuggestionEntities(luisJson: any, suggestionEntityTypes: string[]): string[] {
+ const suggestionEntityList: string[] = [];
+ if (luisJson !== undefined) {
+ suggestionEntityTypes.forEach(entityType => {
+ if (luisJson[entityType] !== undefined && luisJson[entityType].length > 0) {
+ luisJson[entityType].forEach(entity => {
+ if (entity) {
+ suggestionEntityList.push(entity.name);
+ }
+ });
+ }
+ });
+ }
+
+ return suggestionEntityList;
+}
+
+export const suggestionAllEntityTypes = [
+ 'entities',
+ 'regex_entities',
+ 'patternAnyEntities',
+ 'preBuiltEntities',
+ 'closedLists',
+ 'phraselists',
+ 'composites',
+];
+
+export const suggestionNoPatternAnyEntityTypes = [
+ 'entities',
+ 'regex_entities',
+ 'preBuiltEntities',
+ 'closedLists',
+ 'phraselists',
+ 'composites',
+];
+
+export const suggestionNoCompositeEntityTypes = [
+ 'entities',
+ 'regex_entities',
+ 'patternAnyEntities',
+ 'preBuiltEntities',
+ 'closedLists',
+ 'phraselists',
+];
+
+export function getSuggestionRoles(luisJson: any, suggestionEntityTypes: string[]): string[] {
+ const suggestionRolesList: string[] = [];
+ if (luisJson !== undefined) {
+ suggestionEntityTypes.forEach(entityType => {
+ if (luisJson[entityType] !== undefined && luisJson[entityType].length > 0) {
+ luisJson[entityType].forEach(entity => {
+ if (entity.roles !== undefined && entity.roles.length > 0) {
+ entity.roles.forEach(role => {
+ suggestionRolesList.push(role);
+ });
+ }
+ });
+ }
+ });
+ }
+
+ return suggestionRolesList;
+}
+
+export function extractEntityNameInUseFeature(lineContent: string): string {
+ const notTypedEntityusesFeature = /^\s*@\s*(\w*)\s*usesFeature\s*/;
+ if (notTypedEntityusesFeature.test(lineContent)) {
+ const matchedGroups = lineContent.match(notTypedEntityusesFeature);
+ if (matchedGroups && matchedGroups.length === 2) {
+ const entityName = matchedGroups[1];
+ return entityName;
+ }
+ }
+
+ return '';
+}
+
+export function removeLabelsInUtterance(lineContent: string): string {
+ const entityLabelRegex = /\{\s*[\w.@:\s]+\s*=\s*[\w.]+\s*\}/g;
+ let match: RegExpMatchArray | null;
+ let resultStr = '';
+ let startIdx = 0;
+ while ((match = entityLabelRegex.exec(lineContent))) {
+ const leftBoundIdx = match.index;
+ const rightBoundIdx = entityLabelRegex.lastIndex;
+ resultStr += lineContent.slice(startIdx, leftBoundIdx);
+ if (leftBoundIdx && rightBoundIdx) {
+ const entityStr = lineContent.slice(leftBoundIdx + 1, rightBoundIdx - 1);
+ if (entityStr.split('=').length == 2) {
+ const enitity = entityStr.split('=')[1].trim();
+ resultStr += enitity;
+ }
+
+ startIdx = rightBoundIdx;
+ }
+ }
+
+ return resultStr;
+}
diff --git a/Composer/packages/tools/language-servers/language-understanding/tsconfig.build.json b/Composer/packages/tools/language-servers/language-understanding/tsconfig.build.json
new file mode 100644
index 0000000000..7573b46ddf
--- /dev/null
+++ b/Composer/packages/tools/language-servers/language-understanding/tsconfig.build.json
@@ -0,0 +1,8 @@
+{
+ /* Options used for building production code (tests excluded) */
+ "extends": "./tsconfig.json",
+ "include": [
+ "./src/**/*"
+ ]
+ }
+
\ No newline at end of file
diff --git a/Composer/packages/tools/language-servers/language-understanding/tsconfig.json b/Composer/packages/tools/language-servers/language-understanding/tsconfig.json
new file mode 100644
index 0000000000..960cdb7e22
--- /dev/null
+++ b/Composer/packages/tools/language-servers/language-understanding/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "extends": "../../../../tsconfig.base.json",
+ "compilerOptions": {
+ "outDir": "lib"
+ },
+ "include": [
+ "./src/**/*",
+ "./__tests__/**/*"
+ ]
+ }
+
\ No newline at end of file
diff --git a/Composer/packages/tools/language-servers/package.json b/Composer/packages/tools/language-servers/package.json
index 81f9f1ffd7..0567b63dd1 100644
--- a/Composer/packages/tools/language-servers/package.json
+++ b/Composer/packages/tools/language-servers/package.json
@@ -5,8 +5,9 @@
"description": "",
"main": "index.js",
"scripts": {
- "build": "yarn build:lg",
- "build:lg": "cd language-generation && yarn build"
+ "build": "yarn build:lg && yarn build:lu",
+ "build:lg": "cd language-generation && yarn build",
+ "build:lu": "cd language-understanding && yarn build"
},
"author": ""
}
diff --git a/Composer/yarn.lock b/Composer/yarn.lock
index f3f43aa876..3bb2acdd96 100644
--- a/Composer/yarn.lock
+++ b/Composer/yarn.lock
@@ -2658,7 +2658,7 @@
"@types/istanbul-reports" "^1.1.1"
"@types/yargs" "^13.0.0"
-"@microsoft/bf-cli-command@1.0.0":
+"@microsoft/bf-cli-command@1.0.0", "@microsoft/bf-cli-command@https://botbuilder.myget.org/F/botbuilder-declarative/npm/@microsoft/bf-cli-command/-/@microsoft/bf-cli-command-1.0.0.tgz":
version "1.0.0"
resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/@microsoft/bf-cli-command/-/@microsoft/bf-cli-command-1.0.0.tgz#0d681e93690c4e872bf1181a3ff02f094830929e"
integrity sha1-DWgek2kMTocr8RgaP/AvCUgwkp4=
@@ -3415,7 +3415,7 @@
"@types/source-list-map" "*"
source-map "^0.6.1"
-"@types/webpack@^4.4.19":
+"@types/webpack@^4.4.19", "@types/webpack@^4.4.31":
version "4.39.1"
resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.39.1.tgz#d76cd551cc851198f67f75ff3e26551d204530e9"
integrity sha512-rgO9ihNu/l72Sjx3shqwc9r6gi+tOMsqxhMEZhOEVIZt82GFOeUyEdpTk1BO2HqEHLS/XJW8ldUTIIfIMMyYFQ==
@@ -5520,6 +5520,14 @@ clean-stack@^2.0.0:
resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
integrity sha1-7oRy27Ep5yezHooQpCfe6d/kAIs=
+clean-webpack-plugin@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/clean-webpack-plugin/-/clean-webpack-plugin-3.0.0.tgz#a99d8ec34c1c628a4541567aa7b457446460c62b"
+ integrity sha512-MciirUH5r+cYLGCOL5JX/ZLzOZbVr1ot3Fw+KcvbhUb6PM+yycqd9ZhIlcigQ5gl+XhppNmw3bEFuaaMNyLj3A==
+ dependencies:
+ "@types/webpack" "^4.4.31"
+ del "^4.1.1"
+
cli-boxes@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143"
@@ -6246,7 +6254,7 @@ css-has-pseudo@^0.10.0:
postcss "^7.0.6"
postcss-selector-parser "^5.0.0-rc.4"
-css-loader@3.2.0:
+css-loader@3.2.0, css-loader@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.2.0.tgz#bb570d89c194f763627fcf1f80059c6832d009b2"
integrity sha512-QTF3Ud5H7DaZotgdcJjGMvyDj5F3Pn1j/sC6VBEOVp94cbwqyIBdcs/quzj4MC1BKQSrTpQznegH/5giYbhnCQ==
@@ -8097,75 +8105,75 @@ expect@^24.5.0:
jest-message-util "^24.5.0"
jest-regex-util "^24.3.0"
-express@^4.16.4:
- version "4.16.4"
- resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e"
- integrity sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==
+express@^4.15.2, express@^4.17.1:
+ version "4.17.1"
+ resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
+ integrity sha1-RJH8OGBc9R+GKdOcK10Cb5ikwTQ=
dependencies:
- accepts "~1.3.5"
+ accepts "~1.3.7"
array-flatten "1.1.1"
- body-parser "1.18.3"
- content-disposition "0.5.2"
+ body-parser "1.19.0"
+ content-disposition "0.5.3"
content-type "~1.0.4"
- cookie "0.3.1"
+ cookie "0.4.0"
cookie-signature "1.0.6"
debug "2.6.9"
depd "~1.1.2"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
- finalhandler "1.1.1"
+ finalhandler "~1.1.2"
fresh "0.5.2"
merge-descriptors "1.0.1"
methods "~1.1.2"
on-finished "~2.3.0"
- parseurl "~1.3.2"
+ parseurl "~1.3.3"
path-to-regexp "0.1.7"
- proxy-addr "~2.0.4"
- qs "6.5.2"
- range-parser "~1.2.0"
+ proxy-addr "~2.0.5"
+ qs "6.7.0"
+ range-parser "~1.2.1"
safe-buffer "5.1.2"
- send "0.16.2"
- serve-static "1.13.2"
- setprototypeof "1.1.0"
- statuses "~1.4.0"
- type-is "~1.6.16"
+ send "0.17.1"
+ serve-static "1.14.1"
+ setprototypeof "1.1.1"
+ statuses "~1.5.0"
+ type-is "~1.6.18"
utils-merge "1.0.1"
vary "~1.1.2"
-express@^4.17.1:
- version "4.17.1"
- resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
- integrity sha1-RJH8OGBc9R+GKdOcK10Cb5ikwTQ=
+express@^4.16.4:
+ version "4.16.4"
+ resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e"
+ integrity sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==
dependencies:
- accepts "~1.3.7"
+ accepts "~1.3.5"
array-flatten "1.1.1"
- body-parser "1.19.0"
- content-disposition "0.5.3"
+ body-parser "1.18.3"
+ content-disposition "0.5.2"
content-type "~1.0.4"
- cookie "0.4.0"
+ cookie "0.3.1"
cookie-signature "1.0.6"
debug "2.6.9"
depd "~1.1.2"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
- finalhandler "~1.1.2"
+ finalhandler "1.1.1"
fresh "0.5.2"
merge-descriptors "1.0.1"
methods "~1.1.2"
on-finished "~2.3.0"
- parseurl "~1.3.3"
+ parseurl "~1.3.2"
path-to-regexp "0.1.7"
- proxy-addr "~2.0.5"
- qs "6.7.0"
- range-parser "~1.2.1"
+ proxy-addr "~2.0.4"
+ qs "6.5.2"
+ range-parser "~1.2.0"
safe-buffer "5.1.2"
- send "0.17.1"
- serve-static "1.14.1"
- setprototypeof "1.1.1"
- statuses "~1.5.0"
- type-is "~1.6.18"
+ send "0.16.2"
+ serve-static "1.13.2"
+ setprototypeof "1.1.0"
+ statuses "~1.4.0"
+ type-is "~1.6.16"
utils-merge "1.0.1"
vary "~1.1.2"
@@ -10874,6 +10882,11 @@ json5@^1.0.1:
dependencies:
minimist "^1.2.0"
+jsonc-parser@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.0.tgz#f206f87f9d49d644b7502052c04e82dd6392e9ef"
+ integrity sha512-4fLQxW1j/5fWj6p78vAlAafoCKtuBm6ghv+Ij5W2DrDx0qE+ZdEl2c6Ko1mgJNF5ftX1iEWQQ4Ap7+3GlhjkOA==
+
jsonfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
@@ -11523,6 +11536,11 @@ loglevel@^1.6.4:
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.4.tgz#f408f4f006db8354d0577dcf6d33485b3cb90d56"
integrity sha512-p0b6mOGKcGa+7nnmKbpzR6qloPbrgLcnio++E+14Vo/XffOGwZtRpUhr8dTH/x2oCMmEoIU0Zwm3ZauhvYD17g==
+loglevel@^1.6.6:
+ version "1.6.6"
+ resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.6.tgz#0ee6300cc058db6b3551fa1c4bf73b83bb771312"
+ integrity sha512-Sgr5lbboAUBo3eXCSPL4/KoVz3ROKquOjcctxmHIt+vol2DrqTQe3SwkKKuYhEiWB5kYa13YyopJ69deJ1irzQ==
+
lookup-closest-locale@^6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/lookup-closest-locale/-/lookup-closest-locale-6.2.0.tgz#57f665e604fd26f77142d48152015402b607bcf3"
@@ -12036,7 +12054,7 @@ mocha-multi-reporters@^1.1.7:
debug "^3.1.0"
lodash "^4.16.4"
-mocha@5.2.0:
+mocha@5.2.0, mocha@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6"
integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==
@@ -12070,7 +12088,7 @@ moment@2.24.0, "moment@>= 2.9.0", moment@>=2.14.0:
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
-monaco-editor-core@0.17.0:
+monaco-editor-core@0.17.0, monaco-editor-core@^0.17.0:
version "0.17.0"
resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/monaco-editor-core/-/monaco-editor-core-0.17.0.tgz#8d2b7f7e02ac68f68eecc881d7a26457cbc09b49"
integrity sha1-jSt/fgKsaPaO7MiB16JkV8vAm0k=
@@ -14246,7 +14264,7 @@ querystring@0.2.0:
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
-querystringify@^2.0.0:
+querystringify@^2.0.0, querystringify@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e"
integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==
@@ -15497,6 +15515,14 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2:
source-map-url "^0.4.0"
urix "^0.1.0"
+source-map-support@^0.5.0, source-map-support@~0.5.12:
+ version "0.5.16"
+ resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042"
+ integrity sha1-CuBp5/47p1OMZMmFFeNTOerFoEI=
+ dependencies:
+ buffer-from "^1.0.0"
+ source-map "^0.6.0"
+
source-map-support@^0.5.6:
version "0.5.12"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599"
@@ -15513,14 +15539,6 @@ source-map-support@~0.5.10:
buffer-from "^1.0.0"
source-map "^0.6.0"
-source-map-support@~0.5.12:
- version "0.5.16"
- resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042"
- integrity sha1-CuBp5/47p1OMZMmFFeNTOerFoEI=
- dependencies:
- buffer-from "^1.0.0"
- source-map "^0.6.0"
-
source-map-url@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
@@ -15906,7 +15924,7 @@ strip-json-comments@~2.0.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
-style-loader@1.0.0:
+style-loader@1.0.0, style-loader@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.0.0.tgz#1d5296f9165e8e2c85d24eee0b7caf9ec8ca1f82"
integrity sha512-B0dOCFwv7/eY31a5PCieNwMgMhVGFe9w+rh7s/Bx8kfFkrth9zfTZquoYvdw8URgiqxObQKcpW51Ugz1HjfdZw==
@@ -16422,7 +16440,7 @@ ts-loader@^6.0.3:
micromatch "^4.0.0"
semver "^6.0.0"
-ts-loader@^6.2.1:
+ts-loader@^6.1.0, ts-loader@^6.2.1:
version "6.2.1"
resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-6.2.1.tgz#67939d5772e8a8c6bdaf6277ca023a4812da02ef"
integrity sha512-Dd9FekWuABGgjE1g0TlQJ+4dFUfYGbYcs52/HQObE0ZmUNjQlmLAS7xXsSzy23AMaMwipsx5sNHvoEpT2CZq1g==
@@ -16433,6 +16451,17 @@ ts-loader@^6.2.1:
micromatch "^4.0.0"
semver "^6.0.0"
+ts-node@^8.3.0:
+ version "8.5.4"
+ resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.5.4.tgz#a152add11fa19c221d0b48962c210cf467262ab2"
+ integrity sha512-izbVCRV68EasEPQ8MSIGBNK9dc/4sYJJKYA+IarMQct1RtEot6Xp0bXuClsbUSnKpg50ho+aOAx8en5c+y4OFw==
+ dependencies:
+ arg "^4.1.0"
+ diff "^4.0.1"
+ make-error "^1.1.1"
+ source-map-support "^0.5.6"
+ yn "^3.0.0"
+
ts-node@^8.4.1:
version "8.4.1"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.4.1.tgz#270b0dba16e8723c9fa4f9b4775d3810fd994b4f"
@@ -16546,6 +16575,11 @@ typescript@3.7.2:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb"
integrity sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ==
+typescript@^3.6.3:
+ version "3.6.3"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.3.tgz#fea942fabb20f7e1ca7164ff626f1a9f3f70b4da"
+ integrity sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==
+
ua-parser-js@^0.7.18:
version "0.7.19"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.19.tgz#94151be4c0a7fb1d001af7022fdaca4642659e4b"
@@ -16575,6 +16609,29 @@ uglify-js@^3.5.1:
commander "~2.20.3"
source-map "~0.6.1"
+uglify-js@^3.6.0:
+ version "3.7.3"
+ resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.7.3.tgz#f918fce9182f466d5140f24bb0ff35c2d32dcc6a"
+ integrity sha512-7tINm46/3puUA4hCkKYo4Xdts+JDaVC9ZPRcG8Xw9R4nhO/gZgUM3TENq8IF4Vatk8qCig4MzP/c8G4u2BkVQg==
+ dependencies:
+ commander "~2.20.3"
+ source-map "~0.6.1"
+
+uglifyjs-webpack-plugin@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-2.2.0.tgz#e75bc80e7f1937f725954c9b4c5a1e967ea9d0d7"
+ integrity sha512-mHSkufBmBuJ+KHQhv5H0MXijtsoA1lynJt1lXOaotja8/I0pR4L9oGaPIZw+bQBOFittXZg9OC1sXSGO9D9ZYg==
+ dependencies:
+ cacache "^12.0.2"
+ find-cache-dir "^2.1.0"
+ is-wsl "^1.1.0"
+ schema-utils "^1.0.0"
+ serialize-javascript "^1.7.0"
+ source-map "^0.6.1"
+ uglify-js "^3.6.0"
+ webpack-sources "^1.4.0"
+ worker-farm "^1.7.0"
+
undefsafe@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.2.tgz#225f6b9e0337663e0d8e7cfd686fc2836ccace76"
@@ -16781,6 +16838,14 @@ url-parse@^1.4.3:
querystringify "^2.0.0"
requires-port "^1.0.0"
+url-parse@^1.4.4:
+ version "1.4.7"
+ resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
+ integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==
+ dependencies:
+ querystringify "^2.1.1"
+ requires-port "^1.0.0"
+
url-to-options@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9"
@@ -16935,6 +17000,17 @@ vm-browserify@^1.0.1:
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
+vscode-json-languageservice@^3.3.1:
+ version "3.4.11"
+ resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.4.11.tgz#7c0632bccc4b2b955f99f99f43d96d3eece1de42"
+ integrity sha512-26Qv1SFp6x3XmCqU1BRceRsSKRO3xkQa6/K8ziSRt52/LQPiw5ipSxlGVSlzIoi5LCmQVEqUajhiVEMNlFXhNw==
+ dependencies:
+ jsonc-parser "^2.2.0"
+ vscode-languageserver-textdocument "^1.0.0-next.5"
+ vscode-languageserver-types "^3.15.0-next.9"
+ vscode-nls "^4.1.1"
+ vscode-uri "^2.1.1"
+
vscode-jsonrpc@^4.1.0-next, vscode-jsonrpc@^4.1.0-next.3:
version "4.1.0-next.3"
resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/vscode-jsonrpc/-/vscode-jsonrpc-4.1.0-next.3.tgz#05fe742959a2726020d4d0bfbc3d3c97873c7fde"
@@ -16956,11 +17032,21 @@ vscode-languageserver-protocol@^3.15.0-next.8:
vscode-jsonrpc "^4.1.0-next.3"
vscode-languageserver-types "^3.15.0-next.4"
+vscode-languageserver-textdocument@^1.0.0-next.5:
+ version "1.0.0-next.5"
+ resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.0-next.5.tgz#dbb7a45dd973a19261a7c57ab9a439c40f3799ee"
+ integrity sha512-1jp/zAidN/bF/sqPimhBX1orH5G4rzRw63k75TesukJDuxm8yW79ECStWbDSy41BHGOwSGN4M69QFvhancSr5A==
+
vscode-languageserver-types@^3.15.0-next.4:
version "3.15.0-next.5"
resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.5.tgz#863d711bf47b338ff5e63ae19fb20d4fcd4d713b"
integrity sha1-hj1xG/R7M4/15jrhn7INT81NcTs=
+vscode-languageserver-types@^3.15.0-next.9:
+ version "3.15.0-next.9"
+ resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.9.tgz#957a9d1d5998a02edf62298fb7e37d9efcc6c157"
+ integrity sha512-Rl/8qJ6932nrHCdPn+9y0x08uLVQaSLRG+U4JzhyKpWU4eJbVaDRoAcz1Llj7CErJGbPr6kdBvShPy5fRfR+Uw==
+
vscode-languageserver@^5.3.0-next:
version "5.3.0-next.10"
resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/vscode-languageserver/-/vscode-languageserver-5.3.0-next.10.tgz#995fe8b57fc4eb9fea0d11762d3a803de4278995"
@@ -16969,11 +17055,19 @@ vscode-languageserver@^5.3.0-next:
vscode-languageserver-protocol "^3.15.0-next.8"
vscode-textbuffer "^1.0.0"
-vscode-nls@^4.0.0:
+vscode-nls@^4.0.0, vscode-nls@^4.1.1:
version "4.1.1"
resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c"
integrity sha1-+ZFrZOSUeyAyLe+x5naklYYfEzw=
+vscode-test@^0.4.1:
+ version "0.4.3"
+ resolved "https://registry.yarnpkg.com/vscode-test/-/vscode-test-0.4.3.tgz#461ebf25fc4bc93d77d982aed556658a2e2b90b8"
+ integrity sha512-EkMGqBSefZH2MgW65nY05rdRSko15uvzq4VAPM5jVmwYuFQKE7eikKXNJDRxL+OITXHB6pI+a3XqqD32Y3KC5w==
+ dependencies:
+ http-proxy-agent "^2.1.0"
+ https-proxy-agent "^2.2.1"
+
vscode-textbuffer@^1.0.0:
version "1.0.0"
resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/vscode-textbuffer/-/vscode-textbuffer-1.0.0.tgz#1faee638c8e0e4131c8d5c353993a1874acda086"
@@ -16984,6 +17078,11 @@ vscode-uri@^1.0.5:
resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/vscode-uri/-/vscode-uri-1.0.8.tgz#9769aaececae4026fb6e22359cb38946580ded59"
integrity sha1-l2mq7OyuQCb7biI1nLOJRlgN7Vk=
+vscode-uri@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.1.1.tgz#5aa1803391b6ebdd17d047f51365cf62c38f6e90"
+ integrity sha512-eY9jmGoEnVf8VE8xr5znSah7Qt1P/xsCdErz+g8HYZtJ7bZqKH5E3d+6oVNm1AC/c6IHUDokbmVXKOi4qPAC9A==
+
vscode-ws-jsonrpc@^0.1.1:
version "0.1.1"
resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/vscode-ws-jsonrpc/-/vscode-ws-jsonrpc-0.1.1.tgz#163ff05662635b4fd161ed132e112cec4d83f126"
@@ -16991,6 +17090,19 @@ vscode-ws-jsonrpc@^0.1.1:
dependencies:
vscode-jsonrpc "^4.1.0-next"
+vscode@^1.1.34:
+ version "1.1.36"
+ resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.1.36.tgz#5e1a0d1bf4977d0c7bc5159a9a13d5b104d4b1b6"
+ integrity sha512-cGFh9jmGLcTapCpPCKvn8aG/j9zVQ+0x5hzYJq5h5YyUXVGa1iamOaB2M2PZXoumQPES4qeAP1FwkI0b6tL4bQ==
+ dependencies:
+ glob "^7.1.2"
+ mocha "^5.2.0"
+ request "^2.88.0"
+ semver "^5.4.1"
+ source-map-support "^0.5.0"
+ url-parse "^1.4.4"
+ vscode-test "^0.4.1"
+
w3c-hr-time@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045"
@@ -17065,7 +17177,7 @@ webpack-cli@^3.3.0:
v8-compile-cache "^2.0.2"
yargs "^12.0.5"
-webpack-cli@^3.3.2:
+webpack-cli@^3.3.2, webpack-cli@^3.3.8:
version "3.3.10"
resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/webpack-cli/-/webpack-cli-3.3.10.tgz#17b279267e9b4fb549023fae170da8e6e766da13"
integrity sha1-F7J5Jn6bT7VJAj+uFw2o5udm2hM=
@@ -17132,6 +17244,45 @@ webpack-dev-server@3.9.0, webpack-dev-server@^3.9.0:
ws "^6.2.1"
yargs "12.0.5"
+webpack-dev-server@^3.8.0:
+ version "3.10.1"
+ resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.10.1.tgz#1ff3e5cccf8e0897aa3f5909c654e623f69b1c0e"
+ integrity sha512-AGG4+XrrXn4rbZUueyNrQgO4KGnol+0wm3MPdqGLmmA+NofZl3blZQKxZ9BND6RDNuvAK9OMYClhjOSnxpWRoA==
+ dependencies:
+ ansi-html "0.0.7"
+ bonjour "^3.5.0"
+ chokidar "^2.1.8"
+ compression "^1.7.4"
+ connect-history-api-fallback "^1.6.0"
+ debug "^4.1.1"
+ del "^4.1.1"
+ express "^4.17.1"
+ html-entities "^1.2.1"
+ http-proxy-middleware "0.19.1"
+ import-local "^2.0.0"
+ internal-ip "^4.3.0"
+ ip "^1.1.5"
+ is-absolute-url "^3.0.3"
+ killable "^1.0.1"
+ loglevel "^1.6.6"
+ opn "^5.5.0"
+ p-retry "^3.0.1"
+ portfinder "^1.0.25"
+ schema-utils "^1.0.0"
+ selfsigned "^1.10.7"
+ semver "^6.3.0"
+ serve-index "^1.9.1"
+ sockjs "0.3.19"
+ sockjs-client "1.4.0"
+ spdy "^4.0.1"
+ strip-ansi "^3.0.1"
+ supports-color "^6.1.0"
+ url "^0.11.0"
+ webpack-dev-middleware "^3.7.2"
+ webpack-log "^2.0.0"
+ ws "^6.2.1"
+ yargs "12.0.5"
+
webpack-dev-server@^3.8.2:
version "3.8.2"
resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.8.2.tgz#3292427bf6510da9a3ac2d500b924a4197667ff9"
@@ -17188,6 +17339,13 @@ webpack-manifest-plugin@2.1.0:
lodash ">=3.5 <5"
tapable "^1.0.0"
+webpack-merge@^4.2.2:
+ version "4.2.2"
+ resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d"
+ integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==
+ dependencies:
+ lodash "^4.17.15"
+
webpack-sources@^1.1.0, webpack-sources@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.3.0.tgz#2a28dcb9f1f45fe960d8f1493252b5ee6530fa85"
@@ -17263,6 +17421,35 @@ webpack@^4.30.0:
watchpack "^1.5.0"
webpack-sources "^1.3.0"
+webpack@^4.39.3:
+ version "4.41.0"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.0.tgz#db6a254bde671769f7c14e90a1a55e73602fc70b"
+ integrity sha512-yNV98U4r7wX1VJAj5kyMsu36T8RPPQntcb5fJLOsMz/pt/WrKC0Vp1bAlqPLkA1LegSwQwf6P+kAbyhRKVQ72g==
+ dependencies:
+ "@webassemblyjs/ast" "1.8.5"
+ "@webassemblyjs/helper-module-context" "1.8.5"
+ "@webassemblyjs/wasm-edit" "1.8.5"
+ "@webassemblyjs/wasm-parser" "1.8.5"
+ acorn "^6.2.1"
+ ajv "^6.10.2"
+ ajv-keywords "^3.4.1"
+ chrome-trace-event "^1.0.2"
+ enhanced-resolve "^4.1.0"
+ eslint-scope "^4.0.3"
+ json-parse-better-errors "^1.0.2"
+ loader-runner "^2.4.0"
+ loader-utils "^1.2.3"
+ memory-fs "^0.4.1"
+ micromatch "^3.1.10"
+ mkdirp "^0.5.1"
+ neo-async "^2.6.1"
+ node-libs-browser "^2.2.1"
+ schema-utils "^1.0.0"
+ tapable "^1.1.3"
+ terser-webpack-plugin "^1.4.1"
+ watchpack "^1.6.0"
+ webpack-sources "^1.4.1"
+
websocket-driver@>=0.5.1:
version "0.7.0"
resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.0.tgz#0caf9d2d755d93aee049d4bdd0d3fe2cca2a24eb"