From 59ec99c37a141adf6ff3a22672eca138755486a4 Mon Sep 17 00:00:00 2001 From: cosmicshuai Date: Wed, 31 Mar 2021 19:34:39 +0800 Subject: [PATCH 1/7] init --- .../packages/lib/code-editor/src/LuEditor.tsx | 1 + .../language-understanding/src/LUServer.ts | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/Composer/packages/lib/code-editor/src/LuEditor.tsx b/Composer/packages/lib/code-editor/src/LuEditor.tsx index 3b327d6f48..950570f3b4 100644 --- a/Composer/packages/lib/code-editor/src/LuEditor.tsx +++ b/Composer/packages/lib/code-editor/src/LuEditor.tsx @@ -119,6 +119,7 @@ const LuEditor: React.FC = (props) => { autoClosingBrackets: 'always' as const, autoIndent: 'full' as const, folding: true, + definitions: true, lightbulb: { enabled: true, }, diff --git a/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts b/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts index 3c88ed234b..56cf5b76c5 100644 --- a/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts +++ b/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts @@ -12,6 +12,7 @@ import { Range, DiagnosticSeverity, TextEdit, + Location, } from 'vscode-languageserver-types'; import { TextDocument } from 'vscode-languageserver-textdocument'; import { @@ -66,6 +67,7 @@ export class LUServer { capabilities: { textDocumentSync: this.documents.syncKind, codeActionProvider: false, + definitionProvider: true, completionProvider: { resolveProvider: true, triggerCharacters: ['@', ' ', '{', ':', '[', '('], @@ -79,6 +81,7 @@ export class LUServer { }); this.connection.onCompletion((params) => this.completion(params)); this.connection.onDocumentOnTypeFormatting((docTypingParams) => this.docTypeFormat(docTypingParams)); + this.connection.onDefinition((params: TextDocumentPositionParams) => this.definitionHandler(params)); this.connection.onFoldingRanges((foldingRangeParams: FoldingRangeParams) => this.foldingRangeHandler(foldingRangeParams) ); @@ -101,6 +104,18 @@ export class LUServer { this.connection.listen(); } + protected definitionHandler(params: TextDocumentPositionParams): Location | undefined { + const document = this.documents.get(params.textDocument.uri); + if (!document) { + return; + } + + // eslint-disable-next-line security/detect-unsafe-regex + const importRegex = /^\s*-?\s*\[[^[\]]+\](\([^()#]+(#[a-zA-Z0-9_-]*)?(\*[a-zA-Z0-9_-]+\*)?\))/; + + return; + } + protected async foldingRangeHandler(params: FoldingRangeParams): Promise { const document = this.documents.get(params.textDocument.uri); if (!document) { From 78c4395cd0bcbac469146a867513dadb624b2289 Mon Sep 17 00:00:00 2001 From: cosmicshuai Date: Thu, 1 Apr 2021 12:28:50 +0800 Subject: [PATCH 2/7] add import regex rules --- .../language-understanding/src/LUServer.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts b/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts index 56cf5b76c5..a7fa2b103f 100644 --- a/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts +++ b/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts @@ -110,9 +110,18 @@ export class LUServer { return; } + const curLine = document.getText().split(/\r?\n/g)[params.position.line]; // eslint-disable-next-line security/detect-unsafe-regex const importRegex = /^\s*-?\s*\[[^[\]]+\](\([^()#]+(#[a-zA-Z0-9_-]*)?(\*[a-zA-Z0-9_-]+\*)?\))/; - + if (importRegex.test(curLine)) { + const importedFile = curLine.match(importRegex); + if (importedFile) { + const importFilename = importedFile[2]; + const intent = importedFile[3]; + const untterance = importedFile[4]; + return; + } + } return; } From 012aedba19b350ba8bd4a2faf069ff568e023fb0 Mon Sep 17 00:00:00 2001 From: Shuai Wang Date: Thu, 1 Apr 2021 16:13:11 +0800 Subject: [PATCH 3/7] add navigation --- .../language-understanding/code-editor.tsx | 18 +++++++++++++++- .../packages/lib/code-editor/src/LuEditor.tsx | 21 ++++++++++++++++++- .../language-understanding/src/LUServer.ts | 18 ++++++++++++---- 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/Composer/packages/client/src/pages/language-understanding/code-editor.tsx b/Composer/packages/client/src/pages/language-understanding/code-editor.tsx index 72fd35e5cb..4db4524cae 100644 --- a/Composer/packages/client/src/pages/language-understanding/code-editor.tsx +++ b/Composer/packages/client/src/pages/language-understanding/code-editor.tsx @@ -13,8 +13,9 @@ import { CodeEditorSettings } from '@bfc/shared'; import { useRecoilValue } from 'recoil'; import { LuFile } from '@bfc/shared'; -import { localeState, settingsState } from '../../recoilModel/atoms'; +import { dialogState, localeState, settingsState } from '../../recoilModel/atoms'; import { userSettingsState, dispatcherState, luFilesSelectorFamily } from '../../recoilModel'; +import { navigateTo } from '../../utils/navigation'; import TelemetryClient from '../../telemetry/TelemetryClient'; import { DiffCodeEditor } from './diff-editor'; @@ -43,6 +44,7 @@ const CodeEditor: React.FC = (props) => { const luFiles = useRecoilValue(luFilesSelectorFamily(actualProjectId)); const locale = useRecoilValue(localeState(actualProjectId)); const settings = useRecoilValue(settingsState(actualProjectId)); + const currentDialog = useRecoilValue(dialogState({ projectId: actualProjectId, dialogId })); const { languages, defaultLanguage } = settings; @@ -145,6 +147,19 @@ const CodeEditor: React.FC = (props) => { updateUserSettings({ codeEditor: settings }); }; + const navigateToLuPage = useCallback( + (luFileId: string, sectionId?: string) => { + // eslint-disable-next-line security/detect-non-literal-regexp + const pattern = new RegExp(`.${locale}`, 'g'); + const fileId = currentDialog.isFormDialog ? luFileId : luFileId.replace(pattern, ''); + const url = currentDialog.isFormDialog + ? `/bot/${projectId}/language-understanding/${currentDialog.id}/item/${fileId}` + : `/bot/${projectId}/language-understanding/${fileId}${sectionId ? `/edit?t=${sectionId}` : ''}`; + navigateTo(url); + }, + [projectId, locale, currentDialog] + ); + const currentLanguageFileEditor = useMemo(() => { return ( = (props) => { value={content} onChange={onChange} onChangeSettings={handleSettingsChange} + onNavigateToLuPage={navigateToLuPage} /> ); }, [luOption, file]); diff --git a/Composer/packages/lib/code-editor/src/LuEditor.tsx b/Composer/packages/lib/code-editor/src/LuEditor.tsx index 4730847532..f7aaab0ea2 100644 --- a/Composer/packages/lib/code-editor/src/LuEditor.tsx +++ b/Composer/packages/lib/code-editor/src/LuEditor.tsx @@ -155,6 +155,10 @@ const LuEditor: React.FC = (props) => { const editorDomRef = useRef(null); useEffect(() => { + if (props.options?.readOnly) { + return; + } + if (!editor) return; if (!window.monacoServiceInstance) { @@ -170,6 +174,13 @@ const LuEditor: React.FC = (props) => { webSocket, onConnection: (connection: MessageConnection) => { const languageClient = createLanguageClient(formatMessage('LU Language Client'), ['lu'], connection); + languageClient.onReady().then(() => + languageClient.onNotification('LuGotoDefinition', (result) => { + if (luOption?.projectId) { + onNavigateToLuPage?.(result.fileId, result.intent); + } + }) + ); const m = monacoRef.current; if (m) { @@ -199,6 +210,14 @@ const LuEditor: React.FC = (props) => { } else { const m = monacoRef.current; const languageClient = window.monacoLUEditorInstance; + languageClient.onReady().then(() => + languageClient.onNotification('LuGotoDefinition', (result) => { + if (luOption?.projectId) { + onNavigateToLuPage?.(result.fileId, result.intent); + } + }) + ); + if (m) { // this is the correct way to combine keycodes in Monaco // eslint-disable-next-line no-bitwise @@ -209,7 +228,7 @@ const LuEditor: React.FC = (props) => { } sendRequestWithRetry(languageClient, 'initializeDocuments', { luOption, uri }); } - }, [editor]); + }, [editor, onNavigateToLuPage]); const onInit: OnInit = (monaco) => { registerLULanguage(monaco); diff --git a/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts b/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts index a7fa2b103f..5a72fa6d70 100644 --- a/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts +++ b/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts @@ -1,5 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +import path from 'path'; import URI from 'vscode-uri'; import { FoldingRangeParams, IConnection, TextDocuments } from 'vscode-languageserver'; @@ -112,16 +113,25 @@ export class LUServer { const curLine = document.getText().split(/\r?\n/g)[params.position.line]; // eslint-disable-next-line security/detect-unsafe-regex - const importRegex = /^\s*-?\s*\[[^[\]]+\](\([^()#]+(#[a-zA-Z0-9_-]*)?(\*[a-zA-Z0-9_-]+\*)?\))/; + const importRegex = /^\s*-?\s*\[[^[\]]+\](\(([^()#]+)(#([a-zA-Z0-9_-]*))?(\*([a-zA-Z0-9_-]+)\*)?\))/; if (importRegex.test(curLine)) { const importedFile = curLine.match(importRegex); if (importedFile) { - const importFilename = importedFile[2]; - const intent = importedFile[3]; - const untterance = importedFile[4]; + const source = importedFile[2]; + const intent = importedFile[4]; + const utterance = importedFile[6]; + const fileId = path.parse(source).name; + + this.connection.sendNotification('LuGotoDefinition', { + fileId: fileId, + intent: intent, + utterance: utterance, + }); + return; } } + return; } From f736a802a216f6d1d952e6450e5ea944b089ae00 Mon Sep 17 00:00:00 2001 From: cosmicshuai Date: Fri, 2 Apr 2021 15:19:03 +0800 Subject: [PATCH 4/7] add a check file existence before navigation --- .../language-understanding/src/LUServer.ts | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts b/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts index 5a72fa6d70..8443785495 100644 --- a/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts +++ b/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts @@ -38,6 +38,8 @@ export class LUServer { protected readonly pendingValidationRequests = new Map(); protected LUDocuments: LUDocument[] = []; private luParser = new LuParser(); + private _curFileId = ''; + private _curProjectId = ''; constructor( protected readonly connection: IConnection, @@ -117,16 +119,16 @@ export class LUServer { if (importRegex.test(curLine)) { const importedFile = curLine.match(importRegex); if (importedFile) { - const source = importedFile[2]; + const target = importedFile[2]; const intent = importedFile[4]; - const utterance = importedFile[6]; - const fileId = path.parse(source).name; - - this.connection.sendNotification('LuGotoDefinition', { - fileId: fileId, - intent: intent, - utterance: utterance, - }); + const fileId = path.parse(target).name; + const targetFile = this.importResolver?.(this._curFileId, target, this._curProjectId); + if (targetFile) { + this.connection.sendNotification('LuGotoDefinition', { + fileId: fileId, + intent: intent, + }); + } return; } @@ -225,6 +227,8 @@ export class LUServer { } const id = fileId || uri; + this._curFileId = id; + this._curProjectId = projectId || ''; const { intents: sections, diagnostics } = await this.luParser.parse(content, id, luFeatures); return { sections, diagnostics, content }; From 5dc19f393484c7f975bca5bcf03f2f729fc53a0c Mon Sep 17 00:00:00 2001 From: cosmicshuai Date: Tue, 6 Apr 2021 10:03:15 +0800 Subject: [PATCH 5/7] simplify code --- .../packages/lib/code-editor/src/LuEditor.tsx | 28 +++++++++---------- .../language-understanding/src/LUServer.ts | 3 +- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/Composer/packages/lib/code-editor/src/LuEditor.tsx b/Composer/packages/lib/code-editor/src/LuEditor.tsx index f7aaab0ea2..1e0c6c3f65 100644 --- a/Composer/packages/lib/code-editor/src/LuEditor.tsx +++ b/Composer/packages/lib/code-editor/src/LuEditor.tsx @@ -154,6 +154,16 @@ const LuEditor: React.FC = (props) => { const [labelingMenuVisible, setLabelingMenuVisible] = useState(false); const editorDomRef = useRef(null); + const onLuNavigationMsg = (languageClient: MonacoLanguageClient, onNavigateToLuPage:((luFileId: string, luSectionId?: string | undefined) => void) | undefined) => { + return languageClient.onReady().then(() => + languageClient.onNotification('LuGotoDefinition', (result) => { + if (luOption?.projectId) { + onNavigateToLuPage?.(result.fileId, result.intent); + } + }) + ) + } + useEffect(() => { if (props.options?.readOnly) { return; @@ -174,14 +184,7 @@ const LuEditor: React.FC = (props) => { webSocket, onConnection: (connection: MessageConnection) => { const languageClient = createLanguageClient(formatMessage('LU Language Client'), ['lu'], connection); - languageClient.onReady().then(() => - languageClient.onNotification('LuGotoDefinition', (result) => { - if (luOption?.projectId) { - onNavigateToLuPage?.(result.fileId, result.intent); - } - }) - ); - + onLuNavigationMsg(languageClient, onNavigateToLuPage); const m = monacoRef.current; if (m) { // this is the correct way to combine key codes in Monaco @@ -202,6 +205,7 @@ const LuEditor: React.FC = (props) => { editor.executeEdits(uri, edits); }) ); + const disposable = languageClient.start(); connection.onClose(() => disposable.dispose()); window.monacoLUEditorInstance = languageClient; @@ -210,13 +214,7 @@ const LuEditor: React.FC = (props) => { } else { const m = monacoRef.current; const languageClient = window.monacoLUEditorInstance; - languageClient.onReady().then(() => - languageClient.onNotification('LuGotoDefinition', (result) => { - if (luOption?.projectId) { - onNavigateToLuPage?.(result.fileId, result.intent); - } - }) - ); + onLuNavigationMsg(languageClient, onNavigateToLuPage); if (m) { // this is the correct way to combine keycodes in Monaco diff --git a/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts b/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts index 8443785495..79893969ab 100644 --- a/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts +++ b/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts @@ -13,7 +13,6 @@ import { Range, DiagnosticSeverity, TextEdit, - Location, } from 'vscode-languageserver-types'; import { TextDocument } from 'vscode-languageserver-textdocument'; import { @@ -107,7 +106,7 @@ export class LUServer { this.connection.listen(); } - protected definitionHandler(params: TextDocumentPositionParams): Location | undefined { + protected definitionHandler(params: TextDocumentPositionParams): undefined { const document = this.documents.get(params.textDocument.uri); if (!document) { return; From bfcbb0dd547527af3dc8b2b34720b900a5e16831 Mon Sep 17 00:00:00 2001 From: cosmicshuai Date: Tue, 6 Apr 2021 10:33:14 +0800 Subject: [PATCH 6/7] fix lint issue --- .../packages/lib/code-editor/src/LuEditor.tsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Composer/packages/lib/code-editor/src/LuEditor.tsx b/Composer/packages/lib/code-editor/src/LuEditor.tsx index 1e0c6c3f65..f913bb9b73 100644 --- a/Composer/packages/lib/code-editor/src/LuEditor.tsx +++ b/Composer/packages/lib/code-editor/src/LuEditor.tsx @@ -154,15 +154,18 @@ const LuEditor: React.FC = (props) => { const [labelingMenuVisible, setLabelingMenuVisible] = useState(false); const editorDomRef = useRef(null); - const onLuNavigationMsg = (languageClient: MonacoLanguageClient, onNavigateToLuPage:((luFileId: string, luSectionId?: string | undefined) => void) | undefined) => { + const onLuNavigationMsg = ( + languageClient: MonacoLanguageClient, + onNavigateToLuPage: ((luFileId: string, luSectionId?: string | undefined) => void) | undefined + ) => { return languageClient.onReady().then(() => - languageClient.onNotification('LuGotoDefinition', (result) => { - if (luOption?.projectId) { - onNavigateToLuPage?.(result.fileId, result.intent); - } - }) - ) - } + languageClient.onNotification('LuGotoDefinition', (result) => { + if (luOption?.projectId) { + onNavigateToLuPage?.(result.fileId, result.intent); + } + }) + ); + }; useEffect(() => { if (props.options?.readOnly) { From 8b704755e2d118dd22befc2943e141d7a7291abe Mon Sep 17 00:00:00 2001 From: cosmicshuai Date: Fri, 9 Apr 2021 11:27:43 +0800 Subject: [PATCH 7/7] re-enable context menu in lu editor --- Composer/packages/lib/code-editor/src/LuEditor.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Composer/packages/lib/code-editor/src/LuEditor.tsx b/Composer/packages/lib/code-editor/src/LuEditor.tsx index f913bb9b73..dab0448575 100644 --- a/Composer/packages/lib/code-editor/src/LuEditor.tsx +++ b/Composer/packages/lib/code-editor/src/LuEditor.tsx @@ -124,7 +124,7 @@ const LuEditor: React.FC = (props) => { lightbulb: { enabled: true, }, - contextmenu: false, + contextmenu: true, ...props.options, };