diff --git a/README.md b/README.md index 0f5512dd..e47d8034 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ * Doesn't choke on fast-flowing outputs * Tab persistence on macOS and Linux * Proper shell-like experience on Windows including tab completion (thanks, Clink!) - * CMD, PowerShell and Bash on Windows support + * CMD, PowerShell, Cygwin, Git-Bash and Bash on Windows support --- diff --git a/scripts/vars.js b/scripts/vars.js index a6398da1..0fc9454b 100755 --- a/scripts/vars.js +++ b/scripts/vars.js @@ -16,5 +16,4 @@ exports.builtinPlugins = [ 'terminus-plugin-manager', ] exports.nativeModules = ['node-pty', 'font-manager'] -exports.version = appInfo.version exports.electronVersion = pkgInfo.devDependencies.electron diff --git a/terminus-core/src/services/docking.service.ts b/terminus-core/src/services/docking.service.ts index c740ec1c..933232a7 100644 --- a/terminus-core/src/services/docking.service.ts +++ b/terminus-core/src/services/docking.service.ts @@ -40,12 +40,12 @@ export class DockingService { newBounds.height = Math.round(fill * display.bounds.height) } if (dockSide === 'right') { - newBounds.x = display.bounds.x + display.bounds.width * (1.0 - fill) + newBounds.x = display.bounds.x + Math.round(display.bounds.width * (1.0 - fill)) } else { newBounds.x = display.bounds.x } if (dockSide === 'bottom') { - newBounds.y = display.bounds.y + display.bounds.height * (1.0 - fill) + newBounds.y = display.bounds.y + Math.round(display.bounds.height * (1.0 - fill)) } else { newBounds.y = display.bounds.y } diff --git a/terminus-settings/src/components/settingsTab.component.pug b/terminus-settings/src/components/settingsTab.component.pug index 48879d67..e20606a3 100644 --- a/terminus-settings/src/components/settingsTab.component.pug +++ b/terminus-settings/src/components/settingsTab.component.pug @@ -109,17 +109,17 @@ ngb-tabset.vertical(type='tabs') label Display on br div( - '[(ngModel)]'='config.store.appearance.dockScreen' - '(ngModelChange)'='config.save(); docking.dock()' + [(ngModel)]='config.store.appearance.dockScreen', + (ngModelChange)='config.save(); docking.dock()', ngbRadioGroup ) label.btn.btn-secondary input( type='radio', - [value]='"current"' + value='current' ) | Current - label.btn.btn-secondary(*ngFor='let screen of docking.getScreens()') + label.btn.btn-secondary(*ngFor='let screen of screens') input( type='radio', [value]='screen.id' diff --git a/terminus-settings/src/components/settingsTab.component.ts b/terminus-settings/src/components/settingsTab.component.ts index 2ffb8d09..e3733121 100644 --- a/terminus-settings/src/components/settingsTab.component.ts +++ b/terminus-settings/src/components/settingsTab.component.ts @@ -14,6 +14,7 @@ import { SettingsTabProvider } from '../api' export class SettingsTabComponent extends BaseTabComponent { hotkeyFilter = '' private hotkeyDescriptions: IHotkeyDescription[] + private screens constructor ( public config: ConfigService, @@ -28,6 +29,7 @@ export class SettingsTabComponent extends BaseTabComponent { this.hotkeyDescriptions = hotkeyProviders.map(x => x.hotkeys).reduce((a, b) => a.concat(b)) this.title$.next('Settings') this.scrollable = true + this.screens = this.docking.getScreens() } getRecoveryToken (): any { diff --git a/terminus-terminal/src/buttonProvider.ts b/terminus-terminal/src/buttonProvider.ts index 975709b1..b5c89b12 100644 --- a/terminus-terminal/src/buttonProvider.ts +++ b/terminus-terminal/src/buttonProvider.ts @@ -1,6 +1,8 @@ import * as path from 'path' +import { exec } from 'mz/child_process' +import * as fs from 'mz/fs' import { Injectable } from '@angular/core' -import { HotkeysService, ToolbarButtonProvider, IToolbarButton, AppService, ConfigService, ElectronService } from 'terminus-core' +import { HotkeysService, ToolbarButtonProvider, IToolbarButton, AppService, ConfigService, ElectronService, HostAppService, Platform } from 'terminus-core' import { SessionsService } from './services/sessions.service' import { TerminalTabComponent } from './components/terminalTab.component' @@ -12,6 +14,7 @@ export class ButtonProvider extends ToolbarButtonProvider { private sessions: SessionsService, private config: ConfigService, private electron: ElectronService, + private hostApp: HostAppService, hotkeys: HotkeysService, ) { super() @@ -43,6 +46,22 @@ export class ButtonProvider extends ToolbarButtonProvider { 'inject', ] } + if (command === '~default-shell~') { + if (this.hostApp.platform === Platform.Linux) { + let line = (await fs.readFile('/etc/passwd', { encoding: 'utf-8' })) + .split('\n').find(x => x.startsWith(process.env.LOGNAME + ':')) + if (!line) { + console.warn('Could not detect user shell') + command = '/bin/sh' + } else { + command = line.split(':')[5] + } + } + if (this.hostApp.platform === Platform.macOS) { + let shellEntry = (await exec(`dscl . -read /Users/${process.env.LOGNAME} UserShell`))[0].toString() + command = shellEntry.split(':')[1].trim() + } + } let sessionOptions = await this.sessions.prepareNewSession({ command, args, cwd }) this.app.openNewTab( TerminalTabComponent, diff --git a/terminus-terminal/src/components/terminalSettingsTab.component.ts b/terminus-terminal/src/components/terminalSettingsTab.component.ts index 1427b071..8dcfb6d7 100644 --- a/terminus-terminal/src/components/terminalSettingsTab.component.ts +++ b/terminus-terminal/src/components/terminalSettingsTab.component.ts @@ -1,5 +1,5 @@ import { Observable } from 'rxjs' -import * as fs from 'fs-promise' +import * as fs from 'mz/fs' import * as path from 'path' import { exec } from 'mz/child_process' const equal = require('deep-equal') @@ -59,16 +59,20 @@ export class TerminalSettingsTabComponent { { name: 'CMD (stock)', command: 'cmd.exe' }, { name: 'PowerShell', command: 'powershell.exe' }, ] + + // Detect whether BoW is installed const wslPath = `${process.env.windir}\\system32\\bash.exe` if (await fs.exists(wslPath)) { this.shells.push({ name: 'Bash on Windows', command: wslPath }) } + // Detect Cygwin let cygwinPath = await new Promise(resolve => { let reg = new Registry({ hive: Registry.HKLM, key: '\\Software\\Cygwin\\setup' }) reg.get('rootdir', (err, item) => { if (err) { resolve(null) + return } resolve(item.value) }) @@ -76,13 +80,29 @@ export class TerminalSettingsTabComponent { if (cygwinPath) { this.shells.push({ name: 'Cygwin', command: path.join(cygwinPath, 'bin', 'bash.exe') }) } + + // Detect Git-Bash + let gitBashPath = await new Promise(resolve => { + let reg = new Registry({ hive: Registry.HKLM, key: '\\Software\\GitForWindows' }) + reg.get('InstallPath', (err, item) => { + if (err) { + resolve(null) + return + } + resolve(item.value) + }) + }) + if (gitBashPath) { + this.shells.push({ name: 'Git-Bash', command: path.join(gitBashPath, 'bin', 'bash.exe') }) + } } if (this.hostApp.platform === Platform.Linux || this.hostApp.platform === Platform.macOS) { - this.shells = (await fs.readFile('/etc/shells', 'utf-8')) + this.shells = [{ name: 'Default shell', command: '~default-shell~' }] + this.shells = this.shells.concat((await fs.readFile('/etc/shells', { encoding: 'utf-8' })) .split('\n') .map(x => x.trim()) .filter(x => x && !x.startsWith('#')) - .map(x => ({ name: x, command: x })) + .map(x => ({ name: x, command: x }))) } this.colorSchemes = (await Promise.all(this.colorSchemeProviders.map(x => x.getSchemes()))).reduce((a, b) => a.concat(b)) } diff --git a/terminus-terminal/src/config.ts b/terminus-terminal/src/config.ts index 60851bd0..f657ca91 100644 --- a/terminus-terminal/src/config.ts +++ b/terminus-terminal/src/config.ts @@ -40,7 +40,7 @@ export class TerminalConfigProvider extends ConfigProvider { [Platform.macOS]: { terminal: { font: 'Menlo', - shell: '/bin/zsh', + shell: '~default-shell~', }, hotkeys: { 'new-tab': [ @@ -67,7 +67,7 @@ export class TerminalConfigProvider extends ConfigProvider { [Platform.Linux]: { terminal: { font: 'Liberation Mono', - shell: '/bin/bash', + shell: '~default-shell~', }, hotkeys: { 'new-tab': [ diff --git a/terminus-terminal/src/persistenceProviders.ts b/terminus-terminal/src/persistenceProviders.ts index 4f8ac9e0..d0154079 100644 --- a/terminus-terminal/src/persistenceProviders.ts +++ b/terminus-terminal/src/persistenceProviders.ts @@ -100,7 +100,7 @@ export class ScreenPersistenceProvider extends SessionPersistenceProvider { altscreen on `, 'utf-8') let recoveryId = `term-tab-${Date.now()}` - let args = ['-d', '-m', '-c', configPath, '-U', '-S', recoveryId, '-T', 'xterm-256color', '--', options.command].concat(options.args || []) + let args = ['-d', '-m', '-c', configPath, '-U', '-S', recoveryId, '-T', 'xterm-256color', '--', '-' + options.command].concat(options.args || []) this.logger.debug('Spawning screen with', args.join(' ')) await spawn('screen', args, { cwd: options.cwd, diff --git a/terminus-terminal/src/services/sessions.service.ts b/terminus-terminal/src/services/sessions.service.ts index 8441502f..f8d64cec 100644 --- a/terminus-terminal/src/services/sessions.service.ts +++ b/terminus-terminal/src/services/sessions.service.ts @@ -28,10 +28,6 @@ export class Session { ...options.env, TERM: 'xterm-256color', } - if (options.command.includes(' ')) { - options.args = ['-c', options.command] - options.command = 'sh' - } this.pty = nodePTY.spawn(options.command, options.args || [], { name: 'xterm-256color', cols: options.width || 80,