made cli handling extensible - fixes #3763

This commit is contained in:
Eugene Pankov
2021-05-16 15:44:04 +02:00
parent 8fb2bc1ba0
commit fa07fdcb64
11 changed files with 203 additions and 125 deletions

View File

@@ -19,7 +19,6 @@
"devDependencies": {
"@electron/remote": "1.1.0",
"@types/js-yaml": "^4.0.0",
"@types/shell-escape": "^0.2.0",
"@types/winston": "^2.3.6",
"axios": "^0.21.1",
"bootstrap": "^4.1.3",
@@ -32,7 +31,6 @@
"ng2-dnd": "^5.0.2",
"ngx-perfect-scrollbar": "^10.1.0",
"readable-stream": "3.6.0",
"shell-escape": "^0.2.0",
"uuid": "^8.0.0",
"winston": "^3.3.3"
},

View File

@@ -0,0 +1,12 @@
export interface CLIEvent {
argv: any
cwd: string
secondInstance: boolean
}
export abstract class CLIHandler {
priority: number
firstMatchOnly: boolean
abstract handle (event: CLIEvent): Promise<boolean>
}

View File

@@ -9,6 +9,7 @@ export { HotkeyProvider, HotkeyDescription } from './hotkeyProvider'
export { Theme } from './theme'
export { TabContextMenuItemProvider } from './tabContextMenuProvider'
export { SelectorOption } from './selector'
export { CLIHandler, CLIEvent } from './cli'
export { AppService } from '../services/app.service'
export { ConfigService } from '../services/config.service'

20
terminus-core/src/cli.ts Normal file
View File

@@ -0,0 +1,20 @@
import { Injectable } from '@angular/core'
import { HostAppService } from './services/hostApp.service'
import { CLIHandler, CLIEvent } from './api/cli'
@Injectable()
export class LastCLIHandler extends CLIHandler {
firstMatchOnly = true
priority = -999
constructor (private hostApp: HostAppService) {
super()
}
async handle (event: CLIEvent): Promise<boolean> {
if (event.secondInstance) {
this.hostApp.newWindow()
}
return true
}
}

View File

@@ -27,6 +27,7 @@ import { FastHtmlBindDirective } from './directives/fastHtmlBind.directive'
import { HotkeyProvider } from './api/hotkeyProvider'
import { ConfigProvider } from './api/configProvider'
import { Theme } from './api/theme'
import { CLIHandler } from './api/cli'
import { TabContextMenuItemProvider } from './api/tabContextMenuProvider'
import { TabRecoveryProvider } from './api/tabRecovery'
@@ -37,6 +38,7 @@ import { StandardTheme, StandardCompactTheme, PaperTheme } from './theme'
import { CoreConfigProvider } from './config'
import { AppHotkeyProvider } from './hotkeys'
import { TaskCompletionContextMenu, CommonOptionsContextMenu, TabManagementContextMenu } from './tabContextMenu'
import { LastCLIHandler } from './cli'
import 'perfect-scrollbar/css/perfect-scrollbar.css'
import 'ng2-dnd/bundles/style.css'
@@ -51,6 +53,7 @@ const PROVIDERS = [
{ provide: TabContextMenuItemProvider, useClass: TabManagementContextMenu, multi: true },
{ provide: TabContextMenuItemProvider, useClass: TaskCompletionContextMenu, multi: true },
{ provide: TabRecoveryProvider, useClass: SplitTabRecoveryProvider, multi: true },
{ provide: CLIHandler, useClass: LastCLIHandler, multi: true },
{ provide: PERFECT_SCROLLBAR_CONFIG, useValue: { suppressScrollX: true } },
]

View File

@@ -1,11 +1,9 @@
import type { BrowserWindow, TouchBar, MenuItemConstructorOptions } from 'electron'
import * as path from 'path'
import * as fs from 'mz/fs'
import shellEscape from 'shell-escape'
import { Observable, Subject } from 'rxjs'
import { Injectable, NgZone, EventEmitter } from '@angular/core'
import { Injectable, NgZone, EventEmitter, Injector } from '@angular/core'
import { ElectronService } from './electron.service'
import { Logger, LogService } from './log.service'
import { CLIHandler } from '../api/cli'
import { isWindowsBuild, WIN_BUILD_FLUENT_BG_SUPPORTED } from '../utils'
/* eslint-disable block-scoped-var */
@@ -42,11 +40,6 @@ export class HostAppService {
isPortable = !!process.env.PORTABLE_EXECUTABLE_FILE
private preferencesMenu = new Subject<void>()
private secondInstance = new Subject<void>()
private cliOpenDirectory = new Subject<string>()
private cliRunCommand = new Subject<string[]>()
private cliPaste = new Subject<string>()
private cliOpenProfile = new Subject<string>()
private configChangeBroadcast = new Subject<void>()
private windowCloseRequest = new Subject<void>()
private windowMoved = new Subject<void>()
@@ -61,31 +54,6 @@ export class HostAppService {
*/
get preferencesMenu$ (): Observable<void> { return this.preferencesMenu }
/**
* Fired when a second instance of Terminus is launched
*/
get secondInstance$ (): Observable<void> { return this.secondInstance }
/**
* Fired for the `terminus open` CLI command
*/
get cliOpenDirectory$ (): Observable<string> { return this.cliOpenDirectory }
/**
* Fired for the `terminus run` CLI command
*/
get cliRunCommand$ (): Observable<string[]> { return this.cliRunCommand }
/**
* Fired for the `terminus paste` CLI command
*/
get cliPaste$ (): Observable<string> { return this.cliPaste }
/**
* Fired for the `terminus profile` CLI command
*/
get cliOpenProfile$ (): Observable<string> { return this.cliOpenProfile }
/**
* Fired when another window modified the config file
*/
@@ -107,6 +75,7 @@ export class HostAppService {
private constructor (
private zone: NgZone,
private electron: ElectronService,
injector: Injector,
log: LogService,
) {
this.logger = log.create('hostApp')
@@ -159,28 +128,18 @@ export class HostAppService {
electron.ipcRenderer.on('cli', (_$event, argv: any, cwd: string, secondInstance: boolean) => this.zone.run(async () => {
this.logger.info('CLI arguments received:', argv)
const op = argv._[0]
const opAsPath = op ? path.resolve(cwd, op) : null
if (op === 'open') {
this.cliOpenDirectory.next(path.resolve(cwd, argv.directory))
} else if (op === 'run') {
this.cliRunCommand.next(argv.command)
} else if (op === 'paste') {
let text = argv.text
if (argv.escape) {
text = shellEscape([text])
}
this.cliPaste.next(text)
} else if (op === 'profile') {
this.cliOpenProfile.next(argv.profileName)
} else if (secondInstance && op === undefined) {
this.newWindow()
} else if (opAsPath && (await fs.lstat(opAsPath)).isDirectory()) {
this.cliOpenDirectory.next(opAsPath)
}
if (secondInstance) {
this.secondInstance.next()
const cliHandlers = injector.get(CLIHandler) as unknown as CLIHandler[]
cliHandlers.sort((a, b) => b.priority - a.priority)
let handled = false
for (const handler of cliHandlers) {
if (handled && handler.firstMatchOnly) {
continue
}
if (await handler.handle({ argv, cwd, secondInstance })) {
handled = true
}
}
}))

View File

@@ -26,11 +26,6 @@
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.5.tgz#74deebbbcb1e86634dbf10a5b5e8798626f5a597"
integrity sha512-iotVxtCCsPLRAvxMFFgxL8HD2l4mAZ2Oin7/VJ2ooWO0VOK4EGOGmZWZn1uCq7RofR3I/1IOSjCHlFT71eVK0Q==
"@types/shell-escape@^0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@types/shell-escape/-/shell-escape-0.2.0.tgz#cd2f0df814388599dd07196dcc510de2669d1ed2"
integrity sha512-7kUdtJtUylvyISJbe9FMcvMTjRdP0EvNDO1WbT0lT22k/IPBiPRTpmWaKu5HTWLCGLQRWVHrzVHZktTDvvR23g==
"@types/winston@^2.3.6":
version "2.4.4"
resolved "https://registry.yarnpkg.com/@types/winston/-/winston-2.4.4.tgz#48cc744b7b42fad74b9a2e8490e0112bd9a3d08d"
@@ -410,11 +405,6 @@ shallow-clone@^3.0.0:
dependencies:
kind-of "^6.0.2"
shell-escape@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/shell-escape/-/shell-escape-0.2.0.tgz#68fd025eb0490b4f567a027f0bf22480b5f84133"
integrity sha1-aP0CXrBJC09WegJ/C/IkgLX4QTM=
simple-swizzle@^0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a"