UNPKG

@finos/legend-studio

Version:
232 lines (213 loc) 7.27 kB
/** * Copyright (c) 2020-present, Goldman Sachs * * Licensed 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 { computed, observable, action, makeObservable } from 'mobx'; import type { EditorStore } from '../../EditorStore.js'; import { type GeneratorFn, assertErrorThrown, LogEvent, guaranteeType, assertType, } from '@finos/legend-shared'; import { ElementEditorState } from './ElementEditorState.js'; import { type CompilationError, type PackageableElement, GRAPH_MANAGER_EVENT, LAMBDA_PIPE, ParserError, ConcreteFunctionDefinition, RawLambda, buildSourceInformationSourceId, isStubbed_PackageableElement, } from '@finos/legend-graph'; import { LambdaEditorState } from '@finos/legend-application'; export enum FUNCTION_SPEC_TAB { GENERAL = 'GENERAL', TAGGED_VALUES = 'TAGGED_VALUES', STEREOTYPES = 'STEREOTYPES', } export class FunctionBodyEditorState extends LambdaEditorState { editorStore: EditorStore; functionElement: ConcreteFunctionDefinition; isConvertingFunctionBodyToString = false; constructor( functionElement: ConcreteFunctionDefinition, editorStore: EditorStore, ) { super('', LAMBDA_PIPE); makeObservable(this, { functionElement: observable, isConvertingFunctionBodyToString: observable, }); this.functionElement = functionElement; this.editorStore = editorStore; } get lambdaId(): string { return buildSourceInformationSourceId([this.functionElement.path]); } *convertLambdaGrammarStringToObject(): GeneratorFn<void> { if (this.lambdaString) { try { const lambda = (yield this.editorStore.graphManagerState.graphManager.pureCodeToLambda( this.fullLambdaString, this.lambdaId, )) as RawLambda; this.setParserError(undefined); this.functionElement.expressionSequence = lambda.body as object[]; } catch (error) { assertErrorThrown(error); if (error instanceof ParserError) { this.setParserError(error); } this.editorStore.applicationStore.log.error( LogEvent.create(GRAPH_MANAGER_EVENT.PARSING_FAILURE), error, ); } } else { this.clearErrors(); this.functionElement.expressionSequence = []; } } *convertLambdaObjectToGrammarString( pretty: boolean, firstLoad?: boolean, ): GeneratorFn<void> { if (!isStubbed_PackageableElement(this.functionElement)) { this.isConvertingFunctionBodyToString = true; try { const lambdas = new Map<string, RawLambda>(); const functionLamba = new RawLambda( [], this.functionElement.expressionSequence as object, ); lambdas.set(this.lambdaId, functionLamba); const isolatedLambdas = (yield this.editorStore.graphManagerState.graphManager.lambdasToPureCode( lambdas, pretty, )) as Map<string, string>; const grammarText = isolatedLambdas.get(this.lambdaId); if (grammarText) { let grammarString = this.extractLambdaString(grammarText); if ( this.functionElement.expressionSequence.length > 1 && grammarString.endsWith('}') ) { // The lambda object to string converter wraps the lambda inside a '{}' in the case where there are more than one expressions inside the function // causing a parsing error. To handle this we extract only whats inside the '{}' and add ';' to avoid error. grammarString = grammarString.slice(0, -1); grammarString = `${ grammarString.endsWith('\n') ? grammarString.slice(0, -1) : grammarString };`; } this.setLambdaString(grammarString); } else { this.setLambdaString(''); } // `firstLoad` flag is used in the first rendering of the function editor (in a `useEffect`) // This flag helps block editing while the JSON is converting to text and to avoid reseting parser/compiler error in reveal error if (!firstLoad) { this.clearErrors(); } this.isConvertingFunctionBodyToString = false; } catch (error) { assertErrorThrown(error); this.editorStore.applicationStore.log.error( LogEvent.create(GRAPH_MANAGER_EVENT.PARSING_FAILURE), error, ); this.isConvertingFunctionBodyToString = false; } } else { this.clearErrors(); this.setLambdaString(''); } } } export class FunctionEditorState extends ElementEditorState { selectedTab: FUNCTION_SPEC_TAB; functionBodyEditorState: FunctionBodyEditorState; constructor(editorStore: EditorStore, element: PackageableElement) { super(editorStore, element); makeObservable(this, { selectedTab: observable, functionElement: computed, hasCompilationError: computed, setSelectedTab: action, reprocess: action, }); assertType( element, ConcreteFunctionDefinition, 'Element inside function editor state must be a function', ); this.selectedTab = FUNCTION_SPEC_TAB.GENERAL; this.functionBodyEditorState = new FunctionBodyEditorState( element, this.editorStore, ); } get functionElement(): ConcreteFunctionDefinition { return guaranteeType( this.element, ConcreteFunctionDefinition, 'Element inside function editor state must be a function', ); } setSelectedTab(tab: FUNCTION_SPEC_TAB): void { this.selectedTab = tab; } override revealCompilationError(compilationError: CompilationError): boolean { let revealed = false; try { if (compilationError.sourceInformation) { this.setSelectedTab(FUNCTION_SPEC_TAB.GENERAL); this.functionBodyEditorState.setCompilationError(compilationError); revealed = true; } } catch (error) { assertErrorThrown(error); this.editorStore.applicationStore.log.warn( LogEvent.create(GRAPH_MANAGER_EVENT.COMPILATION_FAILURE), `Can't locate error`, error, ); } return revealed; } override get hasCompilationError(): boolean { return Boolean(this.functionBodyEditorState.compilationError); } override clearCompilationError(): void { this.functionBodyEditorState.setCompilationError(undefined); } reprocess( newElement: ConcreteFunctionDefinition, editorStore: EditorStore, ): FunctionEditorState { const functionEditorState = new FunctionEditorState( editorStore, newElement, ); functionEditorState.selectedTab = this.selectedTab; return functionEditorState; } }