mirror of
https://github.com/Eugeny/tabby.git
synced 2024-11-24 06:04:04 +03:00
make shell providers pluggable
This commit is contained in:
parent
17ad43bf65
commit
1f825b16c1
@ -5,14 +5,15 @@ export class Logger {
|
|||||||
private name: string,
|
private name: string,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
log (level: string, ...args: any[]) {
|
doLog (level: string, ...args: any[]) {
|
||||||
console[level](`%c[${this.name}]`, 'color: #aaa', ...args)
|
console[level](`%c[${this.name}]`, 'color: #aaa', ...args)
|
||||||
}
|
}
|
||||||
|
|
||||||
debug (...args: any[]) { this.log('debug', ...args) }
|
debug (...args: any[]) { this.doLog('debug', ...args) }
|
||||||
info (...args: any[]) { this.log('info', ...args) }
|
info (...args: any[]) { this.doLog('info', ...args) }
|
||||||
warn (...args: any[]) { this.log('warn', ...args) }
|
warn (...args: any[]) { this.doLog('warn', ...args) }
|
||||||
error (...args: any[]) { this.log('error', ...args) }
|
error (...args: any[]) { this.doLog('error', ...args) }
|
||||||
|
log (...args: any[]) { this.doLog('log', ...args) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -44,3 +44,15 @@ export interface ITerminalColorScheme {
|
|||||||
export abstract class TerminalColorSchemeProvider {
|
export abstract class TerminalColorSchemeProvider {
|
||||||
abstract async getSchemes (): Promise<ITerminalColorScheme[]>
|
abstract async getSchemes (): Promise<ITerminalColorScheme[]>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IShell {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
command: string
|
||||||
|
args?: string[]
|
||||||
|
env?: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class ShellProvider {
|
||||||
|
abstract async provide (): Promise<IShell[]>
|
||||||
|
}
|
||||||
|
@ -1,24 +1,34 @@
|
|||||||
|
import { AsyncSubject } from 'rxjs'
|
||||||
import * as fs from 'mz/fs'
|
import * as fs from 'mz/fs'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable, Inject } from '@angular/core'
|
||||||
import { HotkeysService, ToolbarButtonProvider, IToolbarButton, AppService, ConfigService, HostAppService, Platform, ElectronService } from 'terminus-core'
|
import { HotkeysService, ToolbarButtonProvider, IToolbarButton, AppService, ConfigService, HostAppService, ElectronService, Logger, LogService } from 'terminus-core'
|
||||||
|
|
||||||
|
import { IShell, ShellProvider } from './api'
|
||||||
import { SessionsService } from './services/sessions.service'
|
import { SessionsService } from './services/sessions.service'
|
||||||
import { ShellsService } from './services/shells.service'
|
|
||||||
import { TerminalTabComponent } from './components/terminalTab.component'
|
import { TerminalTabComponent } from './components/terminalTab.component'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ButtonProvider extends ToolbarButtonProvider {
|
export class ButtonProvider extends ToolbarButtonProvider {
|
||||||
|
private shells$ = new AsyncSubject<IShell[]>()
|
||||||
|
private logger: Logger
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private app: AppService,
|
private app: AppService,
|
||||||
private sessions: SessionsService,
|
private sessions: SessionsService,
|
||||||
private config: ConfigService,
|
private config: ConfigService,
|
||||||
private shells: ShellsService,
|
log: LogService,
|
||||||
private hostApp: HostAppService,
|
hostApp: HostAppService,
|
||||||
|
@Inject(ShellProvider) shellProviders: ShellProvider[],
|
||||||
electron: ElectronService,
|
electron: ElectronService,
|
||||||
hotkeys: HotkeysService,
|
hotkeys: HotkeysService,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
|
this.logger = log.create('newTerminalButton')
|
||||||
|
Promise.all(shellProviders.map(x => x.provide())).then(shellLists => {
|
||||||
|
this.shells$.next(shellLists.reduce((a, b) => a.concat(b)))
|
||||||
|
this.shells$.complete()
|
||||||
|
})
|
||||||
hotkeys.matchedHotkey.subscribe(async (hotkey) => {
|
hotkeys.matchedHotkey.subscribe(async (hotkey) => {
|
||||||
if (hotkey === 'new-tab') {
|
if (hotkey === 'new-tab') {
|
||||||
this.openNewTab()
|
this.openNewTab()
|
||||||
@ -50,24 +60,20 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
|||||||
if (!cwd && this.app.activeTab instanceof TerminalTabComponent) {
|
if (!cwd && this.app.activeTab instanceof TerminalTabComponent) {
|
||||||
cwd = await this.app.activeTab.session.getWorkingDirectory()
|
cwd = await this.app.activeTab.session.getWorkingDirectory()
|
||||||
}
|
}
|
||||||
let command = this.config.store.terminal.shell
|
let shells = await this.shells$.first().toPromise()
|
||||||
let env: any = process.env
|
let shell = shells.find(x => x.id === this.config.store.terminal.shell) || shells[0]
|
||||||
let args: string[] = []
|
let env: any = Object.assign({}, process.env, shell.env || {})
|
||||||
if (command === '~clink~') {
|
|
||||||
({ command, args } = this.shells.getClinkOptions())
|
this.logger.log(`Starting shell ${shell.name}`, shell)
|
||||||
}
|
|
||||||
if (command === '~default-shell~') {
|
|
||||||
command = await this.shells.getDefaultShell()
|
|
||||||
}
|
|
||||||
if (this.hostApp.platform === Platform.Windows) {
|
|
||||||
env.TERM = 'cygwin'
|
|
||||||
}
|
|
||||||
let sessionOptions = await this.sessions.prepareNewSession({
|
let sessionOptions = await this.sessions.prepareNewSession({
|
||||||
command,
|
command: shell.command,
|
||||||
args,
|
args: shell.args || [],
|
||||||
cwd,
|
cwd,
|
||||||
env,
|
env,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.logger.log('Using session options:', sessionOptions)
|
||||||
|
|
||||||
this.app.openNewTab(
|
this.app.openNewTab(
|
||||||
TerminalTabComponent,
|
TerminalTabComponent,
|
||||||
{ sessionOptions }
|
{ sessionOptions }
|
||||||
|
@ -1,23 +1,11 @@
|
|||||||
import { Observable } from 'rxjs'
|
import { Observable } from 'rxjs'
|
||||||
import * as fs from 'mz/fs'
|
|
||||||
import * as path from 'path'
|
|
||||||
import { exec } from 'mz/child_process'
|
import { exec } from 'mz/child_process'
|
||||||
const equal = require('deep-equal')
|
const equal = require('deep-equal')
|
||||||
const fontManager = require('font-manager')
|
const fontManager = require('font-manager')
|
||||||
|
|
||||||
import { Component, Inject } from '@angular/core'
|
import { Component, Inject } from '@angular/core'
|
||||||
import { ConfigService, HostAppService, Platform } from 'terminus-core'
|
import { ConfigService, HostAppService, Platform } from 'terminus-core'
|
||||||
import { TerminalColorSchemeProvider, ITerminalColorScheme } from '../api'
|
import { TerminalColorSchemeProvider, ITerminalColorScheme, IShell, ShellProvider } from '../api'
|
||||||
|
|
||||||
let Registry = null
|
|
||||||
try {
|
|
||||||
Registry = require('winreg')
|
|
||||||
} catch (_) { } // tslint:disable-line no-empty
|
|
||||||
|
|
||||||
interface IShell {
|
|
||||||
name: string
|
|
||||||
command: string
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: require('./terminalSettingsTab.component.pug'),
|
template: require('./terminalSettingsTab.component.pug'),
|
||||||
@ -34,6 +22,7 @@ export class TerminalSettingsTabComponent {
|
|||||||
constructor (
|
constructor (
|
||||||
public config: ConfigService,
|
public config: ConfigService,
|
||||||
private hostApp: HostAppService,
|
private hostApp: HostAppService,
|
||||||
|
@Inject(ShellProvider) private shellProviders: ShellProvider[],
|
||||||
@Inject(TerminalColorSchemeProvider) private colorSchemeProviders: TerminalColorSchemeProvider[],
|
@Inject(TerminalColorSchemeProvider) private colorSchemeProviders: TerminalColorSchemeProvider[],
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
@ -53,71 +42,8 @@ export class TerminalSettingsTabComponent {
|
|||||||
this.fonts.sort()
|
this.fonts.sort()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (this.hostApp.platform === Platform.Windows) {
|
|
||||||
this.shells = [
|
|
||||||
{ name: 'CMD (clink)', command: '~clink~' },
|
|
||||||
{ 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<string>(resolve => {
|
|
||||||
let reg = new Registry({ hive: Registry.HKLM, key: '\\Software\\Cygwin\\setup', arch: 'x64' })
|
|
||||||
reg.get('rootdir', (err, item) => {
|
|
||||||
if (err) {
|
|
||||||
return resolve(null)
|
|
||||||
}
|
|
||||||
resolve(item.value)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
if (cygwinPath) {
|
|
||||||
this.shells.push({ name: 'Cygwin', command: path.join(cygwinPath, 'bin', 'bash.exe') })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect 32-bit Cygwin
|
|
||||||
let cygwin32Path = await new Promise<string>(resolve => {
|
|
||||||
let reg = new Registry({ hive: Registry.HKLM, key: '\\Software\\Cygwin\\setup', arch: 'x86' })
|
|
||||||
reg.get('rootdir', (err, item) => {
|
|
||||||
if (err) {
|
|
||||||
return resolve(null)
|
|
||||||
}
|
|
||||||
resolve(item.value)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
if (cygwin32Path) {
|
|
||||||
this.shells.push({ name: 'Cygwin (32 bit)', command: path.join(cygwin32Path, 'bin', 'bash.exe') })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect Git-Bash
|
|
||||||
let gitBashPath = await new Promise<string>(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 = [{ 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 })))
|
|
||||||
}
|
|
||||||
this.colorSchemes = (await Promise.all(this.colorSchemeProviders.map(x => x.getSchemes()))).reduce((a, b) => a.concat(b))
|
this.colorSchemes = (await Promise.all(this.colorSchemeProviders.map(x => x.getSchemes()))).reduce((a, b) => a.concat(b))
|
||||||
|
this.shells = (await Promise.all(this.shellProviders.map(x => x.provide()))).reduce((a, b) => a.concat(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
fontAutocomplete = (text$: Observable<string>) => {
|
fontAutocomplete = (text$: Observable<string>) => {
|
||||||
|
@ -11,18 +11,27 @@ import { TerminalSettingsTabComponent } from './components/terminalSettingsTab.c
|
|||||||
import { ColorPickerComponent } from './components/colorPicker.component'
|
import { ColorPickerComponent } from './components/colorPicker.component'
|
||||||
|
|
||||||
import { SessionsService } from './services/sessions.service'
|
import { SessionsService } from './services/sessions.service'
|
||||||
import { ShellsService } from './services/shells.service'
|
|
||||||
|
|
||||||
import { ScreenPersistenceProvider } from './persistenceProviders'
|
import { ScreenPersistenceProvider } from './persistenceProviders'
|
||||||
import { TMuxPersistenceProvider } from './tmux'
|
import { TMuxPersistenceProvider } from './tmux'
|
||||||
import { ButtonProvider } from './buttonProvider'
|
import { ButtonProvider } from './buttonProvider'
|
||||||
import { RecoveryProvider } from './recoveryProvider'
|
import { RecoveryProvider } from './recoveryProvider'
|
||||||
import { SessionPersistenceProvider, TerminalColorSchemeProvider, TerminalDecorator } from './api'
|
import { SessionPersistenceProvider, TerminalColorSchemeProvider, TerminalDecorator, ShellProvider } from './api'
|
||||||
import { TerminalSettingsTabProvider } from './settings'
|
import { TerminalSettingsTabProvider } from './settings'
|
||||||
import { PathDropDecorator } from './pathDrop'
|
import { PathDropDecorator } from './pathDrop'
|
||||||
import { TerminalConfigProvider } from './config'
|
import { TerminalConfigProvider } from './config'
|
||||||
import { TerminalHotkeyProvider } from './hotkeys'
|
import { TerminalHotkeyProvider } from './hotkeys'
|
||||||
import { HyperColorSchemes } from './colorSchemes'
|
import { HyperColorSchemes } from './colorSchemes'
|
||||||
|
|
||||||
|
import { Cygwin32ShellProvider } from './shells/cygwin32'
|
||||||
|
import { Cygwin64ShellProvider } from './shells/cygwin64'
|
||||||
|
import { GitBashShellProvider } from './shells/gitBash'
|
||||||
|
import { LinuxDefaultShellProvider } from './shells/linuxDefault'
|
||||||
|
import { MacOSDefaultShellProvider } from './shells/macDefault'
|
||||||
|
import { POSIXShellsProvider } from './shells/posix'
|
||||||
|
import { WindowsStockShellsProvider } from './shells/windowsStock'
|
||||||
|
import { WSLShellProvider } from './shells/wsl'
|
||||||
|
|
||||||
import { hterm } from './hterm'
|
import { hterm } from './hterm'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@ -33,7 +42,6 @@ import { hterm } from './hterm'
|
|||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
SessionsService,
|
SessionsService,
|
||||||
ShellsService,
|
|
||||||
ScreenPersistenceProvider,
|
ScreenPersistenceProvider,
|
||||||
TMuxPersistenceProvider,
|
TMuxPersistenceProvider,
|
||||||
{ provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true },
|
{ provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true },
|
||||||
@ -63,6 +71,15 @@ import { hterm } from './hterm'
|
|||||||
{ provide: HotkeyProvider, useClass: TerminalHotkeyProvider, multi: true },
|
{ provide: HotkeyProvider, useClass: TerminalHotkeyProvider, multi: true },
|
||||||
{ provide: TerminalColorSchemeProvider, useClass: HyperColorSchemes, multi: true },
|
{ provide: TerminalColorSchemeProvider, useClass: HyperColorSchemes, multi: true },
|
||||||
{ provide: TerminalDecorator, useClass: PathDropDecorator, multi: true },
|
{ provide: TerminalDecorator, useClass: PathDropDecorator, multi: true },
|
||||||
|
|
||||||
|
{ provide: ShellProvider, useClass: WindowsStockShellsProvider, multi: true },
|
||||||
|
{ provide: ShellProvider, useClass: MacOSDefaultShellProvider, multi: true },
|
||||||
|
{ provide: ShellProvider, useClass: LinuxDefaultShellProvider, multi: true },
|
||||||
|
{ provide: ShellProvider, useClass: Cygwin32ShellProvider, multi: true },
|
||||||
|
{ provide: ShellProvider, useClass: Cygwin64ShellProvider, multi: true },
|
||||||
|
{ provide: ShellProvider, useClass: GitBashShellProvider, multi: true },
|
||||||
|
{ provide: ShellProvider, useClass: POSIXShellsProvider, multi: true },
|
||||||
|
{ provide: ShellProvider, useClass: WSLShellProvider, multi: true },
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
TerminalTabComponent,
|
TerminalTabComponent,
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
import * as path from 'path'
|
|
||||||
import { exec } from 'mz/child_process'
|
|
||||||
import * as fs from 'mz/fs'
|
|
||||||
import { Injectable } from '@angular/core'
|
|
||||||
import { ElectronService, HostAppService, Platform, Logger, LogService } from 'terminus-core'
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class ShellsService {
|
|
||||||
private logger: Logger
|
|
||||||
|
|
||||||
constructor (
|
|
||||||
log: LogService,
|
|
||||||
private electron: ElectronService,
|
|
||||||
private hostApp: HostAppService,
|
|
||||||
) {
|
|
||||||
this.logger = log.create('shells')
|
|
||||||
}
|
|
||||||
|
|
||||||
getClinkOptions (): { command, args } {
|
|
||||||
return {
|
|
||||||
command: 'cmd.exe',
|
|
||||||
args: [
|
|
||||||
'/k',
|
|
||||||
path.join(
|
|
||||||
path.dirname(this.electron.app.getPath('exe')),
|
|
||||||
'resources',
|
|
||||||
'clink',
|
|
||||||
`clink_${process.arch}.exe`,
|
|
||||||
),
|
|
||||||
'inject',
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getDefaultShell (): Promise<string> {
|
|
||||||
if (this.hostApp.platform === Platform.macOS) {
|
|
||||||
return this.getDefaultMacOSShell()
|
|
||||||
} else {
|
|
||||||
return this.getDefaultLinuxShell()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getDefaultMacOSShell (): Promise<string> {
|
|
||||||
let shellEntry = (await exec(`dscl . -read /Users/${process.env.LOGNAME} UserShell`))[0].toString()
|
|
||||||
return shellEntry.split(' ')[1].trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
async getDefaultLinuxShell (): Promise<string> {
|
|
||||||
let line = (await fs.readFile('/etc/passwd', { encoding: 'utf-8' }))
|
|
||||||
.split('\n').find(x => x.startsWith(process.env.LOGNAME + ':'))
|
|
||||||
if (!line) {
|
|
||||||
this.logger.warn('Could not detect user shell')
|
|
||||||
return '/bin/sh'
|
|
||||||
} else {
|
|
||||||
return line.split(':')[6]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
19
terminus-terminal/src/shellProviders.ts
Normal file
19
terminus-terminal/src/shellProviders.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import * as fs from 'mz/fs'
|
||||||
|
import * as path from 'path'
|
||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { ConfigService, HostAppService, Platform, ElectronService } from 'terminus-core'
|
||||||
|
|
||||||
|
import { ShellProvider, IShell } from './api'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class POSIXShellsProvider extends ShellProvider {
|
||||||
|
constructor (
|
||||||
|
private hostApp: HostAppService,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
async provide (): Promise<IShell[]> {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
48
terminus-terminal/src/shells/cygwin32.ts
Normal file
48
terminus-terminal/src/shells/cygwin32.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import * as path from 'path'
|
||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { HostAppService, Platform } from 'terminus-core'
|
||||||
|
|
||||||
|
import { ShellProvider, IShell } from '../api'
|
||||||
|
|
||||||
|
let Registry = null
|
||||||
|
try {
|
||||||
|
Registry = require('winreg')
|
||||||
|
} catch (_) { } // tslint:disable-line no-empty
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class Cygwin32ShellProvider extends ShellProvider {
|
||||||
|
constructor (
|
||||||
|
private hostApp: HostAppService,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
async provide (): Promise<IShell[]> {
|
||||||
|
if (this.hostApp.platform !== Platform.Windows) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
let cygwinPath = await new Promise<string>(resolve => {
|
||||||
|
let reg = new Registry({ hive: Registry.HKLM, key: '\\Software\\Cygwin\\setup', arch: 'x86' })
|
||||||
|
reg.get('rootdir', (err, item) => {
|
||||||
|
if (err) {
|
||||||
|
return resolve(null)
|
||||||
|
}
|
||||||
|
resolve(item.value)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!cygwinPath) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return [{
|
||||||
|
id: 'cygwin32',
|
||||||
|
name: 'Cygwin (32 bit)',
|
||||||
|
command: path.join(cygwinPath, 'bin', 'bash.exe'),
|
||||||
|
env: {
|
||||||
|
TERM: 'cygwin',
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
48
terminus-terminal/src/shells/cygwin64.ts
Normal file
48
terminus-terminal/src/shells/cygwin64.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import * as path from 'path'
|
||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { HostAppService, Platform } from 'terminus-core'
|
||||||
|
|
||||||
|
import { ShellProvider, IShell } from '../api'
|
||||||
|
|
||||||
|
let Registry = null
|
||||||
|
try {
|
||||||
|
Registry = require('winreg')
|
||||||
|
} catch (_) { } // tslint:disable-line no-empty
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class Cygwin64ShellProvider extends ShellProvider {
|
||||||
|
constructor (
|
||||||
|
private hostApp: HostAppService,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
async provide (): Promise<IShell[]> {
|
||||||
|
if (this.hostApp.platform !== Platform.Windows) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
let cygwinPath = await new Promise<string>(resolve => {
|
||||||
|
let reg = new Registry({ hive: Registry.HKLM, key: '\\Software\\Cygwin\\setup', arch: 'x64' })
|
||||||
|
reg.get('rootdir', (err, item) => {
|
||||||
|
if (err) {
|
||||||
|
return resolve(null)
|
||||||
|
}
|
||||||
|
resolve(item.value)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!cygwinPath) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return [{
|
||||||
|
id: 'cygwin64',
|
||||||
|
name: 'Cygwin',
|
||||||
|
command: path.join(cygwinPath, 'bin', 'bash.exe'),
|
||||||
|
env: {
|
||||||
|
TERM: 'cygwin',
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
49
terminus-terminal/src/shells/gitBash.ts
Normal file
49
terminus-terminal/src/shells/gitBash.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import * as path from 'path'
|
||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { HostAppService, Platform } from 'terminus-core'
|
||||||
|
|
||||||
|
import { ShellProvider, IShell } from '../api'
|
||||||
|
|
||||||
|
let Registry = null
|
||||||
|
try {
|
||||||
|
Registry = require('winreg')
|
||||||
|
} catch (_) { } // tslint:disable-line no-empty
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GitBashShellProvider extends ShellProvider {
|
||||||
|
constructor (
|
||||||
|
private hostApp: HostAppService,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
async provide (): Promise<IShell[]> {
|
||||||
|
if (this.hostApp.platform !== Platform.Windows) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
let gitBashPath = await new Promise<string>(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) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return [{
|
||||||
|
id: 'git-bash',
|
||||||
|
name: 'Git-Bash',
|
||||||
|
command: path.join(gitBashPath, 'bin', 'bash.exe'),
|
||||||
|
env: {
|
||||||
|
TERM: 'cygwin',
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
40
terminus-terminal/src/shells/linuxDefault.ts
Normal file
40
terminus-terminal/src/shells/linuxDefault.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import * as fs from 'mz/fs'
|
||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { HostAppService, Platform, LogService, Logger } from 'terminus-core'
|
||||||
|
|
||||||
|
import { ShellProvider, IShell } from '../api'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class LinuxDefaultShellProvider extends ShellProvider {
|
||||||
|
private logger: Logger
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
private hostApp: HostAppService,
|
||||||
|
log: LogService,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
this.logger = log.create('linuxDefaultShell')
|
||||||
|
}
|
||||||
|
|
||||||
|
async provide (): Promise<IShell[]> {
|
||||||
|
if (this.hostApp.platform !== Platform.Linux) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
let line = (await fs.readFile('/etc/passwd', { encoding: 'utf-8' }))
|
||||||
|
.split('\n').find(x => x.startsWith(process.env.LOGNAME + ':'))
|
||||||
|
if (!line) {
|
||||||
|
this.logger.warn('Could not detect user shell')
|
||||||
|
return [{
|
||||||
|
id: 'default',
|
||||||
|
name: 'User default',
|
||||||
|
command: '/bin/sh'
|
||||||
|
}]
|
||||||
|
} else {
|
||||||
|
return [{
|
||||||
|
id: 'default',
|
||||||
|
name: 'User default',
|
||||||
|
command: line.split(':')[6]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
terminus-terminal/src/shells/macDefault.ts
Normal file
26
terminus-terminal/src/shells/macDefault.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { exec } from 'mz/child_process'
|
||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { HostAppService, Platform } from 'terminus-core'
|
||||||
|
|
||||||
|
import { ShellProvider, IShell } from '../api'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MacOSDefaultShellProvider extends ShellProvider {
|
||||||
|
constructor (
|
||||||
|
private hostApp: HostAppService,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
async provide (): Promise<IShell[]> {
|
||||||
|
if (this.hostApp.platform !== Platform.macOS) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
let shellEntry = (await exec(`dscl . -read /Users/${process.env.LOGNAME} UserShell`))[0].toString()
|
||||||
|
return [{
|
||||||
|
id: 'default',
|
||||||
|
name: 'User default',
|
||||||
|
command: shellEntry.split(' ')[1].trim()
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
29
terminus-terminal/src/shells/posix.ts
Normal file
29
terminus-terminal/src/shells/posix.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import * as fs from 'mz/fs'
|
||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { HostAppService, Platform } from 'terminus-core'
|
||||||
|
|
||||||
|
import { ShellProvider, IShell } from '../api'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class POSIXShellsProvider extends ShellProvider {
|
||||||
|
constructor (
|
||||||
|
private hostApp: HostAppService,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
async provide (): Promise<IShell[]> {
|
||||||
|
if (this.hostApp.platform === Platform.Windows) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return (await fs.readFile('/etc/shells', { encoding: 'utf-8' }))
|
||||||
|
.split('\n')
|
||||||
|
.map(x => x.trim())
|
||||||
|
.filter(x => x && !x.startsWith('#'))
|
||||||
|
.map(x => ({
|
||||||
|
id: x,
|
||||||
|
name: x,
|
||||||
|
command: x,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
40
terminus-terminal/src/shells/windowsStock.ts
Normal file
40
terminus-terminal/src/shells/windowsStock.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import * as path from 'path'
|
||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { HostAppService, Platform, ElectronService } from 'terminus-core'
|
||||||
|
|
||||||
|
import { ShellProvider, IShell } from '../api'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class WindowsStockShellsProvider extends ShellProvider {
|
||||||
|
constructor (
|
||||||
|
private hostApp: HostAppService,
|
||||||
|
private electron: ElectronService,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
async provide (): Promise<IShell[]> {
|
||||||
|
if (this.hostApp.platform !== Platform.Windows) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: 'clink',
|
||||||
|
name: 'CMD (clink)',
|
||||||
|
command: 'cmd.exe',
|
||||||
|
args: [
|
||||||
|
'/k',
|
||||||
|
path.join(
|
||||||
|
path.dirname(this.electron.app.getPath('exe')),
|
||||||
|
'resources',
|
||||||
|
'clink',
|
||||||
|
`clink_${process.arch}.exe`,
|
||||||
|
),
|
||||||
|
'inject',
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{ id: 'cmd', name: 'CMD (stock)', command: 'cmd.exe' },
|
||||||
|
{ id: 'powershell', name: 'PowerShell', command: 'powershell.exe' },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
31
terminus-terminal/src/shells/wsl.ts
Normal file
31
terminus-terminal/src/shells/wsl.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import * as fs from 'mz/fs'
|
||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { HostAppService, Platform } from 'terminus-core'
|
||||||
|
|
||||||
|
import { ShellProvider, IShell } from '../api'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class WSLShellProvider extends ShellProvider {
|
||||||
|
constructor (
|
||||||
|
private hostApp: HostAppService,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
async provide (): Promise<IShell[]> {
|
||||||
|
if (this.hostApp.platform !== Platform.Windows) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const wslPath = `${process.env.windir}\\system32\\bash.exe`
|
||||||
|
if (!await fs.exists(wslPath)) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return [{
|
||||||
|
id: 'wsl',
|
||||||
|
name: 'Bash on Windows',
|
||||||
|
command: wslPath
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user