Skip to content

Commit

Permalink
fix: suppress websocket error after connection lost (microsoft#3270)
Browse files Browse the repository at this point in the history
* report bug template add `Electron`

* mute LSP WebSocket connection lost pop up error

* refine re-connection

* use SendRequestWithRetry

Co-authored-by: Chris Whitten <[email protected]>
Co-authored-by: Andy Brown <[email protected]>
  • Loading branch information
3 people authored Jun 3, 2020
1 parent 2eba21d commit 0467744
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 100 deletions.
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bot-framework-composer-bug.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ assignees: ""

<!-- What browser are you using? -->

- [ ] Electron distribution
- [ ] Chrome
- [ ] Safari
- [ ] Firefox
Expand Down
71 changes: 28 additions & 43 deletions Composer/packages/lib/code-editor/src/LgEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,22 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import React from 'react';
import React, { useState, useEffect } from 'react';
import { listen, MessageConnection } from 'vscode-ws-jsonrpc';
import get from 'lodash/get';
import { MonacoServices, MonacoLanguageClient } from 'monaco-languageclient';
import { MonacoServices } from 'monaco-languageclient';
import { EditorDidMount } from '@monaco-editor/react';

import { registerLGLanguage } from './languages';
import { createUrl, createWebSocket, createLanguageClient } from './utils/lspUtil';
import { createUrl, createWebSocket, createLanguageClient, SendRequestWithRetry } from './utils/lspUtil';
import { BaseEditor, BaseEditorProps, OnInit } from './BaseEditor';
import { LGOption } from './utils';

const LG_HELP =
'https://github.com/microsoft/BotBuilder-Samples/blob/master/experimental/language-generation/docs/lg-file-format.md';
const placeholder = `> To learn more about the LG file format, read the documentation at
> ${LG_HELP}`;

export interface LGOption {
projectId?: string;
fileId: string;
templateId?: string;
}

export interface LGLSPEditorProps extends BaseEditorProps {
lgOption?: LGOption;
languageServer?:
Expand All @@ -40,15 +35,6 @@ const defaultLGServer = {
declare global {
interface Window {
monacoServiceInstance: MonacoServices;
monacoLGEditorInstance: MonacoLanguageClient;
}
}

async function initializeDocuments(lgOption: LGOption | undefined, uri: string) {
const languageClient = window.monacoLGEditorInstance;
if (languageClient) {
await languageClient.onReady();
languageClient.sendRequest('initializeDocuments', { uri, lgOption });
}
}

Expand All @@ -68,6 +54,29 @@ export function LgEditor(props: LGLSPEditorProps) {
editorId = [projectId, fileId, templateId].join('/');
}

const [editor, setEditor] = useState<any>();

useEffect(() => {
if (!editor) return;

if (!window.monacoServiceInstance) {
window.monacoServiceInstance = MonacoServices.install(editor as any);
}

const uri = get(editor.getModel(), 'uri._formatted', '');
const url = createUrl(lgServer);
const webSocket: WebSocket = createWebSocket(url);
listen({
webSocket,
onConnection: (connection: MessageConnection) => {
const languageClient = createLanguageClient('LG Language Client', ['botbuilderlg'], connection);
SendRequestWithRetry(languageClient, 'initializeDocuments', { lgOption, uri });
const disposable = languageClient.start();
connection.onClose(() => disposable.dispose());
},
});
}, [editor]);

const onInit: OnInit = (monaco) => {
registerLGLanguage(monaco);

Expand All @@ -77,31 +86,7 @@ export function LgEditor(props: LGLSPEditorProps) {
};

const editorDidMount: EditorDidMount = (_getValue, editor) => {
if (!window.monacoServiceInstance) {
window.monacoServiceInstance = MonacoServices.install(editor as any);
}

if (!window.monacoLGEditorInstance) {
const uri = get(editor.getModel(), 'uri._formatted', '');
const url = createUrl(lgServer);
const webSocket: WebSocket = createWebSocket(url);
listen({
webSocket,
onConnection: (connection: MessageConnection) => {
const languageClient = createLanguageClient('LG Language Client', ['botbuilderlg'], connection);
if (!window.monacoLGEditorInstance) {
window.monacoLGEditorInstance = languageClient;
}
initializeDocuments(lgOption, uri);
const disposable = languageClient.start();
connection.onClose(() => disposable.dispose());
},
});
} else {
const uri = get(editor.getModel(), 'uri._formatted', '');
initializeDocuments(lgOption, uri);
}

setEditor(editor);
if (typeof props.editorDidMount === 'function') {
return props.editorDidMount(_getValue, editor);
}
Expand Down
104 changes: 47 additions & 57 deletions Composer/packages/lib/code-editor/src/LuEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import React, { useRef } from 'react';
import React, { useRef, useState, useEffect } from 'react';
import { listen, MessageConnection } from 'vscode-ws-jsonrpc';
import get from 'lodash/get';
import { MonacoServices, MonacoLanguageClient } from 'monaco-languageclient';
import { EditorDidMount, Monaco } from '@monaco-editor/react';

import { registerLULanguage } from './languages';
import { createUrl, createWebSocket, createLanguageClient } from './utils/lspUtil';
import { createUrl, createWebSocket, createLanguageClient, SendRequestWithRetry } from './utils/lspUtil';
import { BaseEditor, BaseEditorProps, OnInit } from './BaseEditor';
import { defaultPlaceholder, LU_HELP } from './constants';

export interface LUOption {
projectId?: string;
fileId: string;
sectionId?: string;
}
import { LUOption } from './utils';

export interface LULSPEditorProps extends BaseEditorProps {
luOption?: LUOption;
Expand Down Expand Up @@ -61,14 +56,6 @@ function convertEdit(serverEdit: ServerEdit) {
};
}

async function initializeDocuments(luOption: LUOption | undefined, uri: string) {
const languageClient = window.monacoLUEditorInstance;
if (languageClient) {
await languageClient.onReady();
languageClient.sendRequest('initializeDocuments', { uri, luOption });
}
}

const LuEditor: React.FC<LULSPEditorProps> = (props) => {
const monacoRef = useRef<Monaco>();
const options = {
Expand All @@ -91,6 +78,49 @@ const LuEditor: React.FC<LULSPEditorProps> = (props) => {
editorId = [projectId, fileId, sectionId].join('/');
}

const [editor, setEditor] = useState<any>();

useEffect(() => {
if (!editor) return;

if (!window.monacoServiceInstance) {
window.monacoServiceInstance = MonacoServices.install(editor as any);
}

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;
}

const m = monacoRef.current;
if (m) {
editor.addCommand(m.KeyMod.Shift | m.KeyCode.Enter, function () {
const position = editor.getPosition();
SendRequestWithRetry(languageClient, 'labelingExperienceRequest', { uri, position });
});
}

SendRequestWithRetry(languageClient, 'initializeDocuments', { luOption, uri });

languageClient.onReady().then(() =>
languageClient.onNotification('addUnlabelUtterance', (result) => {
const edits = result.edits.map((e) => {
return convertEdit(e);
});
editor.executeEdits(uri, edits);
})
);
const disposable = languageClient.start();
connection.onClose(() => disposable.dispose());
},
});
});
const onInit: OnInit = (monaco) => {
registerLULanguage(monaco);
monacoRef.current = monaco;
Expand All @@ -101,47 +131,7 @@ const LuEditor: React.FC<LULSPEditorProps> = (props) => {
};

const editorDidMount: EditorDidMount = (_getValue, editor) => {
if (!window.monacoServiceInstance) {
window.monacoServiceInstance = MonacoServices.install(editor as 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;
}

const m = monacoRef.current;
if (m) {
editor.addCommand(m.KeyMod.Shift | m.KeyCode.Enter, function () {
const position = editor.getPosition();
languageClient.sendRequest('labelingExperienceRequest', { uri, position });
});
}
initializeDocuments(luOption, uri);
languageClient.onReady().then(() =>
languageClient.onNotification('addUnlabelUtterance', (result) => {
const edits = result.edits.map((e) => {
return convertEdit(e);
});
editor.executeEdits(uri, edits);
})
);
const disposable = languageClient.start();
connection.onClose(() => disposable.dispose());
},
});
} else {
const uri = get(editor.getModel(), 'uri._formatted', '');
initializeDocuments(luOption, uri);
}

setEditor(editor);
if (typeof props.editorDidMount === 'function') {
return props.editorDidMount(_getValue, editor);
}
Expand Down
1 change: 1 addition & 0 deletions Composer/packages/lib/code-editor/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
// Licensed under the MIT License.

export * from './common';
export * from './types';
24 changes: 24 additions & 0 deletions Composer/packages/lib/code-editor/src/utils/lspUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,27 @@ export function createLanguageClient(
},
});
}

export async function SendRequestWithRetry(
languageClient: MonacoLanguageClient,
method: string,
data: any,
interval = 1000
) {
let sendTimer;

const send = (data) => {
try {
languageClient.sendRequest(method, data);
if (sendTimer) clearInterval(sendTimer);
} catch (error) {
sendTimer = setTimeout(() => {
send(data);
}, interval);
}
};
if (languageClient) {
await languageClient.onReady();
send(data);
}
}
14 changes: 14 additions & 0 deletions Composer/packages/lib/code-editor/src/utils/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

export interface LGOption {
projectId?: string;
fileId: string;
templateId?: string;
}

export interface LUOption {
projectId?: string;
fileId: string;
sectionId?: string;
}

0 comments on commit 0467744

Please sign in to comment.