From e71d404c2b2e846466a4d0d9eb53b5131fb0ab52 Mon Sep 17 00:00:00 2001 From: Eugene Pankov Date: Sun, 23 Sep 2018 16:33:57 +0200 Subject: [PATCH] added CLI option to paste text into terminal --- app/lib/cli.ts | 27 +++++++++++--- .../Contents/_CodeSignature/CodeDirectory | Bin 168 -> 168 bytes .../_CodeSignature/CodeRequirements-1 | Bin 216 -> 216 bytes .../Contents/_CodeSignature/CodeResources | 2 +- .../Contents/_CodeSignature/CodeSignature | Bin 9042 -> 9042 bytes .../Contents/document.wflow | 2 +- terminus-core/package.json | 1 + terminus-core/src/services/app.service.ts | 2 +- terminus-core/src/services/hostApp.service.ts | 9 +++++ terminus-core/yarn.lock | 4 +++ terminus-terminal/src/buttonProvider.ts | 24 ------------- terminus-terminal/src/index.ts | 34 ++++++++++++++++++ 12 files changed, 73 insertions(+), 32 deletions(-) diff --git a/app/lib/cli.ts b/app/lib/cli.ts index c5f36643..6e7294c0 100644 --- a/app/lib/cli.ts +++ b/app/lib/cli.ts @@ -1,3 +1,4 @@ +import * as fs from 'fs' import { app } from 'electron' export function parseArgs (argv, cwd) { @@ -13,11 +14,27 @@ export function parseArgs (argv, cwd) { .command('run [command...]', 'run a command in the terminal', { command: { type: 'string' }, }) - .version('v', 'Show version and exit', app.getVersion()) - .alias('d', 'debug') - .describe('d', 'Show DevTools on start') - .alias('h', 'help') - .help('h') + .command('paste [text]', 'paste stdin into the active tab', yargs => { + return yargs.option('escape', { + alias: 'e', + type: 'boolean', + describe: 'Perform shell escaping' + }).positional('text', { + type: 'string' + }) + }) + .version('version', '', app.getVersion()) + .option('debug', { + alias: 'd', + describe: 'Show DevTools on start', + type: 'boolean' + }) + .option('version', { + alias: 'v', + describe: 'Show version and exit', + type: 'boolean' + }) + .help('help') .strict() .parse(argv.slice(1)) } diff --git a/extras/automator-workflows/Paste path into Terminus.workflow/Contents/_CodeSignature/CodeDirectory b/extras/automator-workflows/Paste path into Terminus.workflow/Contents/_CodeSignature/CodeDirectory index 117417740ff6cea59f2ce6191ac0eea885e11d9f..96e8319130ec6cd645d4fd2c8b22ac6062c34ea0 100644 GIT binary patch delta 30 ocmV+(0O9|r0jL3xSQPf!__X_NKeN1f^3132oH9^yDY21kJpQE+Q2+n{ delta 30 ocmV+(0O9|r0jL3xSQP2K@6VpXnkR?h2u+{oCl~Ppv5t{!Jm?Vv?|X;mv_?wSAFzV3U^< AcK`qY delta 42 zcmV+_0M-B40oVbMSRix|>pN#UT<$%rc3r2|{%fu&&{OGY%>RQDmF7KnZ&;CdV6d|k A{Qv*} diff --git a/extras/automator-workflows/Paste path into Terminus.workflow/Contents/_CodeSignature/CodeResources b/extras/automator-workflows/Paste path into Terminus.workflow/Contents/_CodeSignature/CodeResources index 1bed7a73..539527c3 100644 --- a/extras/automator-workflows/Paste path into Terminus.workflow/Contents/_CodeSignature/CodeResources +++ b/extras/automator-workflows/Paste path into Terminus.workflow/Contents/_CodeSignature/CodeResources @@ -21,7 +21,7 @@ cdhash - x6j3FhtlZlqVwCjiTDDdZxRQkDk= + DwLo2M9xZ+aZGtMzRCGHhHB/wMY= requirement identifier document and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = V4JSMC46SY diff --git a/extras/automator-workflows/Paste path into Terminus.workflow/Contents/_CodeSignature/CodeSignature b/extras/automator-workflows/Paste path into Terminus.workflow/Contents/_CodeSignature/CodeSignature index 9a3b946745a7665dbc8fcb1686f00e1b596f08d3..0350c333d732585ef4036b94173ec20addf75411 100644 GIT binary patch delta 1543 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 COMMAND_STRING - echo -n $1 | /Applications/Terminus.app/Contents/MacOS/terminus paste --escape + /Applications/Terminus.app/Contents/MacOS/terminus paste --escape "$1" CheckedForUserDefaultShell inputMethod diff --git a/terminus-core/package.json b/terminus-core/package.json index 5020c5df..06a6c2ab 100644 --- a/terminus-core/package.json +++ b/terminus-core/package.json @@ -27,6 +27,7 @@ "electron-updater": "^2.8.9", "ng2-dnd": "^5.0.2", "ngx-perfect-scrollbar": "^6.0.0", + "shell-escape": "^0.2.0", "universal-analytics": "^0.4.17" }, "peerDependencies": { diff --git a/terminus-core/src/services/app.service.ts b/terminus-core/src/services/app.service.ts index d275b85b..6784a785 100644 --- a/terminus-core/src/services/app.service.ts +++ b/terminus-core/src/services/app.service.ts @@ -74,8 +74,8 @@ export class AppService { this.activeTabChange.next(tab) if (this.activeTab) { this.activeTab.emitFocused() + this.hostApp.setTitle(this.activeTab.title) } - this.hostApp.setTitle(this.activeTab.title) } toggleLastTab () { diff --git a/terminus-core/src/services/hostApp.service.ts b/terminus-core/src/services/hostApp.service.ts index 495c5d6c..9a3a6cfa 100644 --- a/terminus-core/src/services/hostApp.service.ts +++ b/terminus-core/src/services/hostApp.service.ts @@ -1,4 +1,5 @@ import * as path from 'path' +import shellEscape = require('shell-escape') import { Observable, Subject } from 'rxjs' import { Injectable, NgZone, EventEmitter } from '@angular/core' import { ElectronService } from './electron.service' @@ -25,6 +26,7 @@ export class HostAppService { private secondInstance = new Subject() private cliOpenDirectory = new Subject() private cliRunCommand = new Subject() + private cliPaste = new Subject() private configChangeBroadcast = new Subject() private logger: Logger private windowId: number @@ -33,6 +35,7 @@ export class HostAppService { get secondInstance$ (): Observable { return this.secondInstance } get cliOpenDirectory$ (): Observable { return this.cliOpenDirectory } get cliRunCommand$ (): Observable { return this.cliRunCommand } + get cliPaste$ (): Observable { return this.cliPaste } get configChangeBroadcast$ (): Observable { return this.configChangeBroadcast } constructor ( @@ -76,6 +79,12 @@ export class HostAppService { 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) } })) diff --git a/terminus-core/yarn.lock b/terminus-core/yarn.lock index c3dcb948..a9678215 100644 --- a/terminus-core/yarn.lock +++ b/terminus-core/yarn.lock @@ -460,6 +460,10 @@ semver@^5.4.1: version "5.4.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" +shell-escape@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/shell-escape/-/shell-escape-0.2.0.tgz#68fd025eb0490b4f567a027f0bf22480b5f84133" + sntp@2.x.x: version "2.1.0" resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8" diff --git a/terminus-terminal/src/buttonProvider.ts b/terminus-terminal/src/buttonProvider.ts index 8de05816..b37b39bb 100644 --- a/terminus-terminal/src/buttonProvider.ts +++ b/terminus-terminal/src/buttonProvider.ts @@ -15,30 +15,6 @@ export class ButtonProvider extends ToolbarButtonProvider { hotkeys: HotkeysService, ) { super() - hotkeys.matchedHotkey.subscribe(async (hotkey) => { - if (hotkey === 'new-tab') { - terminal.openTab() - } - }) - hotkeys.matchedHotkey.subscribe(async (hotkey) => { - if (hotkey === 'new-window') { - hostApp.newWindow() - } - }) - hostApp.cliOpenDirectory$.subscribe(async directory => { - if (await fs.exists(directory)) { - if ((await fs.stat(directory)).isDirectory()) { - this.terminal.openTab(null, directory) - } - } - }) - hostApp.cliRunCommand$.subscribe(async command => { - this.terminal.openTab({ - id: '', - command: command[0], - args: command.slice(1), - }, null, true) - }) if (!electron.remote.process.env.DEV) { setImmediate(async () => { let argv: string[] = electron.remote.process.argv diff --git a/terminus-terminal/src/index.ts b/terminus-terminal/src/index.ts index 7bde6ecc..a71c815d 100644 --- a/terminus-terminal/src/index.ts +++ b/terminus-terminal/src/index.ts @@ -1,9 +1,12 @@ +import * as fs from 'mz/fs' + import { NgModule } from '@angular/core' import { BrowserModule } from '@angular/platform-browser' import { FormsModule } from '@angular/forms' import { NgbModule } from '@ng-bootstrap/ng-bootstrap' import { ToastrModule } from 'ngx-toastr' import TerminusCorePlugin from 'terminus-core' +import { HostAppService } from 'terminus-core' import { ToolbarButtonProvider, TabRecoveryProvider, ConfigProvider, HotkeysService, HotkeyProvider, AppService, ConfigService } from 'terminus-core' import { SettingsTabProvider } from 'terminus-settings' @@ -100,6 +103,7 @@ export default class TerminalModule { config: ConfigService, hotkeys: HotkeysService, terminal: TerminalService, + hostApp: HostAppService, ) { let events = [ { @@ -130,6 +134,36 @@ export default class TerminalModule { terminal.openTab() }) } + + hotkeys.matchedHotkey.subscribe(async (hotkey) => { + if (hotkey === 'new-tab') { + terminal.openTab() + } + }) + hotkeys.matchedHotkey.subscribe(async (hotkey) => { + if (hotkey === 'new-window') { + hostApp.newWindow() + } + }) + hostApp.cliOpenDirectory$.subscribe(async directory => { + if (await fs.exists(directory)) { + if ((await fs.stat(directory)).isDirectory()) { + terminal.openTab(null, directory) + } + } + }) + hostApp.cliRunCommand$.subscribe(async command => { + terminal.openTab({ + id: '', + command: command[0], + args: command.slice(1), + }, null, true) + }) + hostApp.cliPaste$.subscribe(text => { + if (app.activeTab instanceof TerminalTabComponent && app.activeTab.session) { + (app.activeTab as TerminalTabComponent).sendInput(text) + } + }) } }