UNPKG

@finos/legend-application-pure-ide

Version:
276 lines 14.4 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 { DISPLAY_ANSI_ESCAPE } from '@finos/legend-application'; import { assertErrorThrown } from '@finos/legend-shared'; import { flowResult } from 'mobx'; import { deserialize } from 'serializr'; import { ConceptNode, PackageConceptAttribute, } from '../server/models/ConceptTree.js'; import { DirectoryNode } from '../server/models/DirectoryTree.js'; import { FileCoordinate } from '../server/models/File.js'; import { HOME_DIRECTORY_PATH, ROOT_PACKAGE_PATH, WELCOME_FILE_PATH, } from './PureIDEConfig.js'; import { LEGEND_PURE_IDE_TERMINAL_COMMAND } from '../__lib__/LegendPureIDECommand.js'; const PACKAGE_PATH_PATTERN = /^(?:(?:\w[\w$]*)::)*\w[\w$]*$/; const FILE_PATH_PATTERN = /^\/?(?:\w+\/)*\w+(?:\.\w+)*$/; const LEGEND_PURE_IDE_TERMINAL_WEBLINK_REGEX = /(?:(?<url>https?:[/]{2}[^\s"'!*(){}|\\^<>`]*[^\s"':,.!?{}|\\^~[\]`()<>])|(?<path>resource:(?<path_sourceId>\/?(?:\w+\/)*\w+(?:\.\w+)*) (?:line:(?<path_line>\d+)) (?:column:(?<path_column>\d+))))/; export const setupTerminal = (ideStore) => { ideStore.applicationStore.terminalService.terminal.setup({ webLinkProvider: { handler: (event, text) => { const match = text.match(LEGEND_PURE_IDE_TERMINAL_WEBLINK_REGEX); if (match?.groups?.url) { ideStore.applicationStore.navigationService.navigator.visitAddress(match.groups.url); } else if (match?.groups?.path && match.groups.path_sourceId && match.groups.path_column && match.groups.path_line) { flowResult(ideStore.loadFile(match.groups.path_sourceId, new FileCoordinate(match.groups.path_sourceId, Number.parseInt(match.groups.path_line, 10), Number.parseInt(match.groups.path_column, 10)))).catch(ideStore.applicationStore.alertUnhandledError); } }, regex: LEGEND_PURE_IDE_TERMINAL_WEBLINK_REGEX, }, // TODO: for these, we can potentially use `runCommand`, but we need to refactor // command to return promises // that is the cleaner way to do this and make us able to move terminal plugins/extension mechanism // to LegendApplicationPlugin for example, rather than being application specific like this // as for example, this requires access to `EditorStore` right now commands: [ { command: LEGEND_PURE_IDE_TERMINAL_COMMAND.GO, description: 'Run the go() function in welcome file', usage: 'go', aliases: ['compile', 'executeGo'], handler: async (args) => flowResult(ideStore.executeGo()).catch(ideStore.applicationStore.alertUnhandledError), }, { command: LEGEND_PURE_IDE_TERMINAL_COMMAND.TEST, description: 'Run the test suite (by path if specified)', usage: 'test [/some/path]', handler: async (args) => { const path = args[0]; if (path) { if (!path.match(PACKAGE_PATH_PATTERN)) { ideStore.applicationStore.terminalService.terminal.fail(`command requires a valid package/concept path`); return; } } await flowResult(ideStore.executeTests(path ?? ROOT_PACKAGE_PATH)).catch(ideStore.applicationStore.alertUnhandledError); }, }, // io { command: LEGEND_PURE_IDE_TERMINAL_COMMAND.REMOVE, description: 'Remove a file or directory', usage: 'rm /some/path', handler: async (args) => { const path = args[0]; if (!path?.match(FILE_PATH_PATTERN)) { ideStore.applicationStore.terminalService.terminal.fail(`rm command requires a valid file/directory path`); return; } await flowResult(ideStore.deleteDirectoryOrFile(path, undefined, undefined)).catch(ideStore.applicationStore.alertUnhandledError); }, }, { command: LEGEND_PURE_IDE_TERMINAL_COMMAND.MOVE, description: 'Move a file', usage: 'mv /old/path /new/path', handler: async (args) => { const oldPath = args[0]; if (!oldPath?.match(FILE_PATH_PATTERN)) { ideStore.applicationStore.terminalService.terminal.fail(`command requires a valid old file path`); return; } const newPath = args[1]; if (!newPath?.match(FILE_PATH_PATTERN)) { ideStore.applicationStore.terminalService.terminal.fail(`command requires a valid new file path`); return; } await flowResult(ideStore.renameFile(oldPath, newPath)).catch(ideStore.applicationStore.alertUnhandledError); }, }, { command: LEGEND_PURE_IDE_TERMINAL_COMMAND.NEW_DIRECTORY, description: 'Create a new directory', usage: 'mkdir /some/path', handler: async (args) => { const path = args[0]; if (!path?.match(FILE_PATH_PATTERN)) { ideStore.applicationStore.terminalService.terminal.fail(`command requires a valid directory path`); return; } await flowResult(ideStore.createNewDirectory(path)).catch(ideStore.applicationStore.alertUnhandledError); }, }, { command: LEGEND_PURE_IDE_TERMINAL_COMMAND.NEW_FILE, description: 'Create a new file', usage: 'touch /some/path', handler: async (args) => { const path = args[0]; if (!path?.match(FILE_PATH_PATTERN)) { ideStore.applicationStore.terminalService.terminal.fail(`command requires a valid path`); return; } await flowResult(ideStore.createNewDirectory(path)).catch(ideStore.applicationStore.alertUnhandledError); }, }, // navigation { command: LEGEND_PURE_IDE_TERMINAL_COMMAND.WELCOME, description: 'Open the welcome file', usage: 'welcome', aliases: ['start'], handler: async () => { await flowResult(ideStore.loadFile(WELCOME_FILE_PATH)).catch(ideStore.applicationStore.alertUnhandledError); }, }, { command: LEGEND_PURE_IDE_TERMINAL_COMMAND.OPEN_FILE, description: 'Open a file', usage: 'open /some/file/path', aliases: ['edit', 'code', 'vi'], handler: async (args) => { const path = args[0]; if (!path?.match(PACKAGE_PATH_PATTERN)) { ideStore.applicationStore.terminalService.terminal.fail(`command requires a valid file path`); return; } await flowResult(ideStore.loadFile(path)).catch(ideStore.applicationStore.alertUnhandledError); }, }, { command: LEGEND_PURE_IDE_TERMINAL_COMMAND.OPEN_DIRECTORY, description: 'Open a directory or a package', usage: 'cd /some/directory/path | cd some::package::path', handler: async (args) => { const path = args[0]; if (!path || !(path.match(FILE_PATH_PATTERN) ?? path.match(PACKAGE_PATH_PATTERN))) { ideStore.applicationStore.terminalService.terminal.fail(`command requires a valid directory or concept path`); return; } try { // NOTE: favor concept/package path over directory path if (path.match(PACKAGE_PATH_PATTERN)) { await flowResult(ideStore.conceptTreeState.revealConcept(path, { forceOpenExplorerPanel: true, packageOnly: true, })); } else { await flowResult(ideStore.directoryTreeState.revealPath(path, { forceOpenExplorerPanel: true, directoryOnly: true, })); } } catch (error) { assertErrorThrown(error); ideStore.applicationStore.terminalService.terminal.fail(error.message); } }, }, { command: LEGEND_PURE_IDE_TERMINAL_COMMAND.LIST_DIRECTORY, description: 'List children of a directory or package', usage: 'cd /some/directory/path | cd some::package::path | cd ::', handler: async (args) => { const path = args[0]; if (!path || !(path.match(FILE_PATH_PATTERN) ?? path.match(PACKAGE_PATH_PATTERN) ?? [HOME_DIRECTORY_PATH, ROOT_PACKAGE_PATH].includes(path))) { ideStore.applicationStore.terminalService.terminal.fail(`command requires a valid directory or package path`); return; } try { // NOTE: favor concept/package path over directory path if (path.match(PACKAGE_PATH_PATTERN) || path === ROOT_PACKAGE_PATH) { ideStore.applicationStore.terminalService.terminal.output((await ideStore.client.getConceptChildren(path)) .map((child) => deserialize(ConceptNode, child)) .map((child) => child.li_attr instanceof PackageConceptAttribute ? `${DISPLAY_ANSI_ESCAPE.BRIGHT_CYAN}${child.text}${DISPLAY_ANSI_ESCAPE.RESET}` : child.text) .join('\n')); } else { ideStore.applicationStore.terminalService.terminal.output((await ideStore.client.getDirectoryChildren(path)) .map((child) => deserialize(DirectoryNode, child)) .map((child) => child.isFolderNode ? `${DISPLAY_ANSI_ESCAPE.BRIGHT_CYAN}${child.text}${DISPLAY_ANSI_ESCAPE.RESET}` : child.text) .join('\n')); } } catch (error) { assertErrorThrown(error); ideStore.applicationStore.terminalService.terminal.fail(error.message); } }, }, // utility { command: LEGEND_PURE_IDE_TERMINAL_COMMAND.CLEAR, description: 'Clear the terminal', usage: 'clear', handler: async (args) => { ideStore.applicationStore.terminalService.terminal.clear(); }, }, { command: LEGEND_PURE_IDE_TERMINAL_COMMAND.ECHO, description: 'Print text', usage: `echo 'some string'`, handler: async (args, command, text) => { const content = text .substring(text.indexOf(command) + command.length) .trim(); ideStore.applicationStore.terminalService.terminal.output(content.replaceAll(/\\u001b/g, '\u001b')); }, }, { command: LEGEND_PURE_IDE_TERMINAL_COMMAND.ANSI, description: 'Show common ANSI escape sequences used for styling', usage: 'ansi', handler: async (args) => { ideStore.applicationStore.terminalService.terminal.showCommonANSIEscapeSequences(); return Promise.resolve(); }, }, { command: LEGEND_PURE_IDE_TERMINAL_COMMAND.DEBUG, description: 'Introspect debug state. When passing no parameters, will display summary of available variables', usage: 'debug [summary | abort | <expression to evaluate> ]', aliases: [], handler: async (args) => { flowResult(ideStore.runDebugger({ args })).catch(ideStore.applicationStore.alertUnhandledError); }, }, { command: LEGEND_PURE_IDE_TERMINAL_COMMAND.HELP, description: 'Show help', usage: 'help', handler: async (args) => { ideStore.applicationStore.terminalService.terminal.showHelp(); return Promise.resolve(); }, }, ], }); }; //# sourceMappingURL=LegendPureIDETerminal.js.map