This is an automated email from the ASF dual-hosted git repository. rantunes pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/incubator-kie-tools-temporary-rnd-do-not-use.git
commit 5d8b2ae53a5a1887223267a31dd4e72f4cd6a6f6 Author: Fabrizio Antonangeli <[email protected]> AuthorDate: Mon Nov 13 18:18:46 2023 +0100 KOGITO-9891: Language Service not available on the SWF Chrome extension (#2046) --- .../package.json | 4 + .../src/api/SwfLanguageServiceChannelApiImpl.ts | 42 ++++++++++ .../src/github-content-script.ts | 44 +++++++++- .../ChromeExtensionSwfLanguageService.ts | 97 ++++++++++++++++++++++ .../src/app/components/common/GlobalContext.tsx | 5 +- .../app/components/common/KogitoEditorIframe.tsx | 12 ++- .../src/app/components/common/Main.tsx | 7 +- .../src/app/components/single/singleEditorEdit.tsx | 2 + packages/chrome-extension/src/index.ts | 20 ++++- pnpm-lock.yaml | 12 +++ 10 files changed, 238 insertions(+), 7 deletions(-) diff --git a/packages/chrome-extension-serverless-workflow-editor/package.json b/packages/chrome-extension-serverless-workflow-editor/package.json index 974312bae3..1fde1c0d21 100644 --- a/packages/chrome-extension-serverless-workflow-editor/package.json +++ b/packages/chrome-extension-serverless-workflow-editor/package.json @@ -38,6 +38,8 @@ "@kie-tools/serverless-workflow-combined-editor": "workspace:*", "@kie-tools/serverless-workflow-diagram-editor-assets": "workspace:*", "@kie-tools/serverless-workflow-diagram-editor-envelope": "workspace:*", + "@kie-tools/serverless-workflow-jq-expressions": "workspace:*", + "@kie-tools/serverless-workflow-language-service": "workspace:*", "@kie-tools/serverless-workflow-text-editor": "workspace:*", "@kie-tools/tsconfig": "workspace:*", "@types/chrome": "^0.0.193", @@ -53,6 +55,8 @@ "start-server-and-test": "^1.12.1", "ts-jest": "^26.5.6", "typescript": "^4.6.2", + "vscode-languageserver-textdocument": "^1.0.4", + "vscode-languageserver-types": "^3.16.0", "webpack": "^5.88.2", "webpack-cli": "^4.10.0", "webpack-dev-server": "^4.15.1", diff --git a/packages/chrome-extension-serverless-workflow-editor/src/api/SwfLanguageServiceChannelApiImpl.ts b/packages/chrome-extension-serverless-workflow-editor/src/api/SwfLanguageServiceChannelApiImpl.ts new file mode 100644 index 0000000000..10950d28c2 --- /dev/null +++ b/packages/chrome-extension-serverless-workflow-editor/src/api/SwfLanguageServiceChannelApiImpl.ts @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SwfLanguageServiceChannelApi } from "@kie-tools/serverless-workflow-language-service/dist/api"; +import { + SwfJsonLanguageService, + SwfYamlLanguageService, +} from "@kie-tools/serverless-workflow-language-service/dist/channel"; +import { CodeLens, CompletionItem, Position, Range } from "vscode-languageserver-types"; + +export class SwfLanguageServiceChannelApiImpl implements SwfLanguageServiceChannelApi { + constructor(private readonly ls: SwfJsonLanguageService | SwfYamlLanguageService) {} + + public async kogitoSwfLanguageService__getCompletionItems(args: { + content: string; + uri: string; + cursorPosition: Position; + cursorWordRange: Range; + }): Promise<CompletionItem[]> { + return this.ls.getCompletionItems(args); + } + + public async kogitoSwfLanguageService__getCodeLenses(args: { uri: string; content: string }): Promise<CodeLens[]> { + return this.ls.getCodeLenses(args); + } +} diff --git a/packages/chrome-extension-serverless-workflow-editor/src/github-content-script.ts b/packages/chrome-extension-serverless-workflow-editor/src/github-content-script.ts index 8eba2e3f59..c80e5b12d7 100644 --- a/packages/chrome-extension-serverless-workflow-editor/src/github-content-script.ts +++ b/packages/chrome-extension-serverless-workflow-editor/src/github-content-script.ts @@ -17,11 +17,52 @@ * under the License. */ -import { ChromeRouter } from "./ChromeRouter"; import { startExtension } from "@kie-tools-core/chrome-extension"; +import { FileInfo } from "@kie-tools-core/chrome-extension/dist/app/components/single/singleEditorView"; +import { Dependencies } from "@kie-tools-core/chrome-extension/dist/app/Dependencies"; +import { GitHubPageType } from "@kie-tools-core/chrome-extension/dist/app/github/GitHubPageType"; import { EditorEnvelopeLocator, EnvelopeContentType, EnvelopeMapping } from "@kie-tools-core/editor/dist/api"; +import { EmbeddedEditorFile, StateControl } from "@kie-tools-core/editor/dist/channel"; +import { EmbeddedEditorChannelApiImpl } from "@kie-tools-core/editor/dist/embedded"; +import { SwfCombinedEditorChannelApiImpl } from "@kie-tools/serverless-workflow-combined-editor/dist/channel"; +import { getFileLanguage } from "@kie-tools/serverless-workflow-language-service/dist/api"; +import { SwfLanguageServiceChannelApiImpl } from "./api/SwfLanguageServiceChannelApiImpl"; +import { ChromeRouter } from "./ChromeRouter"; +import { ChromeExtensionSwfLanguageService } from "./languageService/ChromeExtensionSwfLanguageService"; +import { extractFileExtension, removeDirectories } from "./utils"; const resourcesPathPrefix = new ChromeRouter().getResourcesPathPrefix(); + +function getCustomChannelApiImpl( + pageType: GitHubPageType, + fileInfo: FileInfo, + stateControl: StateControl +): SwfCombinedEditorChannelApiImpl | undefined { + if (!getFileLanguage(fileInfo.path) || pageType !== GitHubPageType.EDIT) { + return; + } + + const dependencies = new Dependencies(); + + const embeddedEditorFile: EmbeddedEditorFile = { + path: fileInfo.path, + getFileContents: () => { + return Promise.resolve(dependencies.all.edit__githubTextAreaWithFileContents()?.textContent ?? ""); + }, + isReadOnly: false, + fileExtension: `sw.${extractFileExtension(fileInfo.path)}`, + fileName: `${removeDirectories(fileInfo.path)}`, + }; + const channelApiImpl = new EmbeddedEditorChannelApiImpl(stateControl, embeddedEditorFile, "en", {}); + + const chromeExtensionSwfLanguageService = new ChromeExtensionSwfLanguageService(); + const languageService = chromeExtensionSwfLanguageService.getLs(fileInfo.path); + return new SwfCombinedEditorChannelApiImpl({ + defaultApiImpl: channelApiImpl, + swfLanguageServiceChannelApiImpl: new SwfLanguageServiceChannelApiImpl(languageService), + }); +} + startExtension({ name: "Kogito :: Serverless workflow editor", extensionIconUrl: chrome.runtime.getURL("/resources/kie_icon_rgb_fullcolor_default.svg"), @@ -37,4 +78,5 @@ startExtension({ }, }), ]), + getCustomChannelApiImpl, }); diff --git a/packages/chrome-extension-serverless-workflow-editor/src/languageService/ChromeExtensionSwfLanguageService.ts b/packages/chrome-extension-serverless-workflow-editor/src/languageService/ChromeExtensionSwfLanguageService.ts new file mode 100644 index 0000000000..06e3f76c86 --- /dev/null +++ b/packages/chrome-extension-serverless-workflow-editor/src/languageService/ChromeExtensionSwfLanguageService.ts @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { FileLanguage, getFileLanguageOrThrow } from "@kie-tools/serverless-workflow-language-service/dist/api"; +import { + SwfJsonLanguageService, + SwfLanguageServiceArgs, + SwfYamlLanguageService, +} from "@kie-tools/serverless-workflow-language-service/dist/channel"; +import { JqExpressionReadSchemasImpl } from "@kie-tools/serverless-workflow-jq-expressions/dist/impl"; +import { TextDocument } from "vscode-languageserver-textdocument"; +import { removeDuplicatedKeyValuePairs } from "@kie-tools/serverless-workflow-jq-expressions/dist/utils"; + +export class ChromeExtensionSwfLanguageService { + constructor() {} + + public getLs(relativePath: string): SwfJsonLanguageService | SwfYamlLanguageService { + const swfLanguageLsArgs = this.getDefaultLsArgs({}); + + const fileLanguage = getFileLanguageOrThrow(relativePath); + if (fileLanguage === FileLanguage.YAML) { + return new SwfYamlLanguageService(swfLanguageLsArgs); + } else if (fileLanguage === FileLanguage.JSON) { + return new SwfJsonLanguageService(swfLanguageLsArgs); + } else { + throw new Error(`Could not determine LS for ${relativePath}`); + } + } + + private getDefaultLsArgs( + configOverrides: Partial<SwfLanguageServiceArgs["config"]> + ): Omit<SwfLanguageServiceArgs, "lang"> { + return { + fs: {}, + serviceCatalog: { + global: { + getServices: async () => [], + }, + relative: { + getServices: async (_textDocument) => [], + }, + getServiceFileNameFromSwfServiceCatalogServiceId: async ( + registryName: string, + swfServiceCatalogServiceId: string + ) => `${registryName}__${swfServiceCatalogServiceId}__latest.yaml`, + }, + jqCompletions: { + remote: { + getJqAutocompleteProperties: async (args: { + textDocument: TextDocument; + schemaPaths: string[]; + }): Promise<Record<string, string>[]> => { + const jqExpressionReadSchema = new JqExpressionReadSchemasImpl(); + const contentArray = await jqExpressionReadSchema.getContentFromRemoteUrl(args.schemaPaths); + return removeDuplicatedKeyValuePairs(jqExpressionReadSchema.parseSchemaProperties(contentArray)); + }, + }, + relative: { + getJqAutocompleteProperties: (_args: any) => Promise.resolve([]), + }, + }, + config: { + shouldDisplayServiceRegistriesIntegration: async () => false, + shouldIncludeJsonSchemaDiagnostics: async () => true, + shouldReferenceServiceRegistryFunctionsWithUrls: async () => true, + getSpecsDirPosixPaths: async (_textDocument) => ({ + specsDirRelativePosixPath: "", + specsDirAbsolutePosixPath: "", + }), + getRoutesDirPosixPaths: async (_textDocument) => ({ + routesDirRelativePosixPath: "", + routesDirAbsolutePosixPath: "", + }), + shouldConfigureServiceRegistries: () => false, + shouldServiceRegistriesLogIn: () => false, + canRefreshServices: () => true, + ...configOverrides, + }, + }; + } +} diff --git a/packages/chrome-extension/src/app/components/common/GlobalContext.tsx b/packages/chrome-extension/src/app/components/common/GlobalContext.tsx index eb7c216fc0..9a11b26413 100644 --- a/packages/chrome-extension/src/app/components/common/GlobalContext.tsx +++ b/packages/chrome-extension/src/app/components/common/GlobalContext.tsx @@ -18,11 +18,12 @@ */ import * as React from "react"; -import { EditorEnvelopeLocator } from "@kie-tools-core/editor/dist/api"; +import { EditorEnvelopeLocator, KogitoEditorChannelApi } from "@kie-tools-core/editor/dist/api"; import { Logger } from "../../../Logger"; import { ExternalEditorManager } from "../../../ExternalEditorManager"; import { ResourceContentServiceFactory } from "./ChromeResourceContentService"; import { Dependencies } from "../../Dependencies"; +import { StateControl } from "@kie-tools-core/editor/dist/channel"; export interface GlobalContextType { id: string; @@ -33,6 +34,8 @@ export interface GlobalContextType { extensionIconUrl: string; resourceContentServiceFactory: ResourceContentServiceFactory; externalEditorManager?: ExternalEditorManager; + stateControl?: StateControl; + customChannelApiImpl?: KogitoEditorChannelApi; } export const GlobalContext = React.createContext<GlobalContextType>({} as any); diff --git a/packages/chrome-extension/src/app/components/common/KogitoEditorIframe.tsx b/packages/chrome-extension/src/app/components/common/KogitoEditorIframe.tsx index 610d75fbea..f932473485 100644 --- a/packages/chrome-extension/src/app/components/common/KogitoEditorIframe.tsx +++ b/packages/chrome-extension/src/app/components/common/KogitoEditorIframe.tsx @@ -28,6 +28,7 @@ import { useGlobals } from "./GlobalContext"; import { IsolatedEditorContext } from "./IsolatedEditorContext"; import { IsolatedEditorRef } from "./IsolatedEditorRef"; import { useChromeExtensionI18n } from "../../i18n"; +import { StateControl } from "@kie-tools-core/editor/dist/channel"; interface Props { openFileExtension: string; @@ -43,11 +44,18 @@ const RefForwardingKogitoEditorIframe: React.ForwardRefRenderFunction<IsolatedEd ) => { const githubApi = useGitHubApi(); const { editor, editorRef } = useEditorRef(); - const { envelopeLocator, resourceContentServiceFactory } = useGlobals(); + const { + envelopeLocator, + resourceContentServiceFactory, + customChannelApiImpl, + stateControl: globalStateControl, + } = useGlobals(); const { repoInfo, textMode, fullscreen, onEditorReady } = useContext(IsolatedEditorContext); const { locale } = useChromeExtensionI18n(); const wasOnTextMode = usePrevious(textMode); + const stateControl = useMemo(() => globalStateControl || new StateControl(), [globalStateControl]); + const resourceContentService = useMemo(() => { return resourceContentServiceFactory.createNew(githubApi.octokit(), repoInfo); }, [repoInfo]); @@ -126,6 +134,8 @@ const RefForwardingKogitoEditorIframe: React.ForwardRefRenderFunction<IsolatedEd kogitoEditor_setContentError={props.onSetContentError} editorEnvelopeLocator={envelopeLocator} locale={locale} + customChannelApiImpl={customChannelApiImpl} + stateControl={stateControl} /> </div> </> diff --git a/packages/chrome-extension/src/app/components/common/Main.tsx b/packages/chrome-extension/src/app/components/common/Main.tsx index 784aada68b..95896a8356 100644 --- a/packages/chrome-extension/src/app/components/common/Main.tsx +++ b/packages/chrome-extension/src/app/components/common/Main.tsx @@ -27,9 +27,10 @@ import { Dependencies } from "../../Dependencies"; import { kogitoMenuContainer } from "../../utils"; import { ExternalEditorManager } from "../../../ExternalEditorManager"; import { ResourceContentServiceFactory } from "./ChromeResourceContentService"; -import { EditorEnvelopeLocator } from "@kie-tools-core/editor/dist/api"; +import { EditorEnvelopeLocator, KogitoEditorChannelApi } from "@kie-tools-core/editor/dist/api"; import { I18nDictionariesProvider } from "@kie-tools-core/i18n/dist/react-components"; import { chromeExtensionI18nDictionaries, chromeExtensionI18nDefaults, ChromeExtensionI18nContext } from "../../i18n"; +import { StateControl } from "@kie-tools-core/editor/dist/channel"; export interface Globals { id: string; @@ -40,6 +41,8 @@ export interface Globals { extensionIconUrl: string; resourceContentServiceFactory: ResourceContentServiceFactory; externalEditorManager?: ExternalEditorManager; + customChannelApiImpl?: KogitoEditorChannelApi; + stateControl?: StateControl; } function KogitoMenuPortal(props: { id: string }) { @@ -85,6 +88,8 @@ export const Main: React.FunctionComponent<Globals> = (props) => { extensionIconUrl: props.extensionIconUrl, resourceContentServiceFactory: props.resourceContentServiceFactory, externalEditorManager: props.externalEditorManager, + customChannelApiImpl: props.customChannelApiImpl, + stateControl: props.stateControl, }} > <GitHubContextProvider> diff --git a/packages/chrome-extension/src/app/components/single/singleEditorEdit.tsx b/packages/chrome-extension/src/app/components/single/singleEditorEdit.tsx index 967e01f83f..659fce1f7b 100644 --- a/packages/chrome-extension/src/app/components/single/singleEditorEdit.tsx +++ b/packages/chrome-extension/src/app/components/single/singleEditorEdit.tsx @@ -72,6 +72,8 @@ export async function renderSingleEditorApp(args: Globals & { fileInfo: FileInfo extensionIconUrl={args.extensionIconUrl} resourceContentServiceFactory={args.resourceContentServiceFactory} externalEditorManager={args.externalEditorManager} + customChannelApiImpl={args.customChannelApiImpl} + stateControl={args.stateControl} > <SingleEditorEditApp openFileExtension={openFileExtension} fileInfo={args.fileInfo} /> </Main>, diff --git a/packages/chrome-extension/src/index.ts b/packages/chrome-extension/src/index.ts index a208ab9e80..68101799e1 100644 --- a/packages/chrome-extension/src/index.ts +++ b/packages/chrome-extension/src/index.ts @@ -19,18 +19,19 @@ import { GitHubPageType } from "./app/github/GitHubPageType"; import { renderSingleEditorApp } from "./app/components/single/singleEditorEdit"; -import { iframeContainer, renderSingleEditorReadonlyApp } from "./app/components/single/singleEditorView"; +import { FileInfo, iframeContainer, renderSingleEditorReadonlyApp } from "./app/components/single/singleEditorView"; import { renderPrEditorsApp } from "./app/components/pr/prEditors"; import { mainContainer, runAfterUriChange } from "./app/utils"; import { Dependencies } from "./app/Dependencies"; import * as ReactDOM from "react-dom"; -import { EditorEnvelopeLocator } from "@kie-tools-core/editor/dist/api"; +import { EditorEnvelopeLocator, KogitoEditorChannelApi } from "@kie-tools-core/editor/dist/api"; import "../resources/style.css"; import { Logger } from "./Logger"; import { Globals } from "./app/components/common/Main"; import { ExternalEditorManager } from "./ExternalEditorManager"; import { ResourceContentServiceFactory } from "./app/components/common/ChromeResourceContentService"; import { renderOpenRepoInExternalEditorApp } from "./app/components/openRepoInExternalEditor/openRepoInExternalEditorApp"; +import { StateControl } from "@kie-tools-core/editor/dist/channel"; /** * Starts a Kogito extension. @@ -40,6 +41,7 @@ import { renderOpenRepoInExternalEditorApp } from "./app/components/openRepoInEx * @param args.githubAuthTokenCookieName The name of the cookie that will hold a GitHub PAT for your extension. * @param args.editorEnvelopeLocator The file extension mapping to the provided Editors. * @param args.externalEditorManager The implementation of ExternalEditorManager for your extension. + * @param args.customChannelApiImpl Optional channelApi implementation. */ export function startExtension(args: { name: string; @@ -47,12 +49,21 @@ export function startExtension(args: { githubAuthTokenCookieName: string; editorEnvelopeLocator: EditorEnvelopeLocator; externalEditorManager?: ExternalEditorManager; + getCustomChannelApiImpl?: ( + pageType: GitHubPageType, + fileInfo: FileInfo, + stateControl: StateControl + ) => KogitoEditorChannelApi | undefined; }) { const logger = new Logger(args.name); const resourceContentServiceFactory = new ResourceContentServiceFactory(); const dependencies = new Dependencies(); - const runInit = () => + const runInit = () => { + const pageType = discoverCurrentGitHubPageType(); + const fileInfo = extractFileInfoFromUrl(); + const stateControl = new StateControl(); + init({ id: chrome.runtime.id, logger: logger, @@ -62,7 +73,10 @@ export function startExtension(args: { editorEnvelopeLocator: args.editorEnvelopeLocator, resourceContentServiceFactory: resourceContentServiceFactory, externalEditorManager: args.externalEditorManager, + stateControl, + customChannelApiImpl: args.getCustomChannelApiImpl?.(pageType, fileInfo, stateControl), }); + }; runAfterUriChange(logger, () => setTimeout(runInit, 0)); setTimeout(runInit, 0); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6f7f6080bc..2f94ffe73e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1282,6 +1282,12 @@ importers: "@kie-tools/serverless-workflow-diagram-editor-envelope": specifier: workspace:* version: link:../serverless-workflow-diagram-editor-envelope + "@kie-tools/serverless-workflow-jq-expressions": + specifier: workspace:* + version: link:../serverless-workflow-jq-expressions + "@kie-tools/serverless-workflow-language-service": + specifier: workspace:* + version: link:../serverless-workflow-language-service "@kie-tools/serverless-workflow-text-editor": specifier: workspace:* version: link:../serverless-workflow-text-editor @@ -1327,6 +1333,12 @@ importers: typescript: specifier: ^4.6.2 version: 4.8.4 + vscode-languageserver-textdocument: + specifier: ^1.0.4 + version: 1.0.7 + vscode-languageserver-types: + specifier: ^3.16.0 + version: 3.17.2 webpack: specifier: ^5.88.2 version: 5.88.2([email protected]) --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
