WSL2 support - fixes #271

This commit is contained in:
Eugene Pankov 2020-04-19 14:56:31 +02:00
parent f58cab0820
commit b29ab2690f
9 changed files with 34 additions and 20 deletions

View File

@ -155,6 +155,8 @@ export class ConfigService {
} }
save (): void { save (): void {
// Scrub undefined values
this._store = JSON.parse(JSON.stringify(this._store))
fs.writeFileSync(this.path, yaml.safeDump(this._store), 'utf8') fs.writeFileSync(this.path, yaml.safeDump(this._store), 'utf8')
this.emitChange() this.emitChange()
this.hostApp.broadcastConfigChange(this.store) this.hostApp.broadcastConfigChange(this.store)

View File

@ -19,6 +19,7 @@ export interface Profile {
name: string name: string
color?: string color?: string
sessionOptions: SessionOptions sessionOptions: SessionOptions
shell?: string
isBuiltin?: boolean isBuiltin?: boolean
icon?: string icon?: string
} }

View File

@ -11,7 +11,7 @@ h3.mb-3 Shell
) )
option( option(
*ngFor='let profile of profiles', *ngFor='let profile of profiles',
[ngValue]='slugify(profile.name).toLowerCase()' [ngValue]='terminal.getProfileID(profile)'
) {{profile.name}} ) {{profile.name}}

View File

@ -1,4 +1,3 @@
import slugify from 'slugify'
import { Component } from '@angular/core' import { Component } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { Subscription } from 'rxjs' import { Subscription } from 'rxjs'
@ -17,14 +16,13 @@ export class ShellSettingsTabComponent {
Platform = Platform Platform = Platform
isConPTYAvailable: boolean isConPTYAvailable: boolean
isConPTYStable: boolean isConPTYStable: boolean
slugify = slugify
private configSubscription: Subscription private configSubscription: Subscription
constructor ( constructor (
public config: ConfigService, public config: ConfigService,
public hostApp: HostAppService, public hostApp: HostAppService,
public terminal: TerminalService,
private electron: ElectronService, private electron: ElectronService,
private terminalService: TerminalService,
private ngbModal: NgbModal, private ngbModal: NgbModal,
) { ) {
config.store.terminal.environment = config.store.terminal.environment || {} config.store.terminal.environment = config.store.terminal.environment || {}
@ -38,7 +36,7 @@ export class ShellSettingsTabComponent {
} }
async ngOnInit (): Promise<void> { async ngOnInit (): Promise<void> {
this.shells = await this.terminalService.shells$.toPromise() this.shells = await this.terminal.shells$.toPromise()
} }
ngOnDestroy (): void { ngOnDestroy (): void {
@ -46,21 +44,22 @@ export class ShellSettingsTabComponent {
} }
async reload (): Promise<void> { async reload (): Promise<void> {
this.profiles = await this.terminalService.getProfiles({ includeHidden: true }) this.profiles = await this.terminal.getProfiles({ includeHidden: true })
} }
pickWorkingDirectory (): void { async pickWorkingDirectory (): Promise<void> {
const shell = this.shells.find(x => x.id === this.config.store.terminal.shell) const profile = await this.terminal.getProfileByID(this.config.store.terminal.profile)
const shell = this.shells.find(x => x.id === profile?.shell)
if (!shell) { if (!shell) {
return return
} }
const paths = this.electron.dialog.showOpenDialog( const paths = (await this.electron.dialog.showOpenDialog(
this.hostApp.getWindow(), this.hostApp.getWindow(),
{ {
defaultPath: shell.fsBase, defaultPath: shell.fsBase,
properties: ['openDirectory', 'showHiddenFiles'], properties: ['openDirectory', 'showHiddenFiles'],
} }
) )).filePaths
if (paths) { if (paths) {
this.config.store.terminal.workingDirectory = paths[0] this.config.store.terminal.workingDirectory = paths[0]
} }
@ -69,7 +68,8 @@ export class ShellSettingsTabComponent {
newProfile (shell: Shell): void { newProfile (shell: Shell): void {
const profile: Profile = { const profile: Profile = {
name: shell.name || '', name: shell.name || '',
sessionOptions: this.terminalService.optionsFromShell(shell), shell: shell.id,
sessionOptions: this.terminal.optionsFromShell(shell),
} }
this.config.store.terminal.profiles = [profile, ...this.config.store.terminal.profiles] this.config.store.terminal.profiles = [profile, ...this.config.store.terminal.profiles]
this.config.save() this.config.save()

View File

@ -30,7 +30,7 @@ h3.mb-3 Terminal
) )
| Audible | Audible
.alert.alert-info.d-flex.align-items-center(*ngIf='config.store.terminal.bell != "audible" && config.store.terminal.shell.startsWith("wsl")') .alert.alert-info.d-flex.align-items-center(*ngIf='config.store.terminal.bell != "audible" && config.store.terminal.profile.startsWith("wsl")')
.mr-auto WSL terminal bell can only be muted via Volume Mixer .mr-auto WSL terminal bell can only be muted via Volume Mixer
button.btn.btn-secondary((click)='openWSLVolumeMixer()') Show Mixer button.btn.btn-secondary((click)='openWSLVolumeMixer()') Show Mixer

View File

@ -1,4 +1,3 @@
import slugify from 'slugify'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { HotkeyDescription, HotkeyProvider } from 'terminus-core' import { HotkeyDescription, HotkeyProvider } from 'terminus-core'
import { TerminalService } from './services/terminal.service' import { TerminalService } from './services/terminal.service'
@ -78,7 +77,7 @@ export class TerminalHotkeyProvider extends HotkeyProvider {
return [ return [
...this.hotkeys, ...this.hotkeys,
...profiles.map(profile => ({ ...profiles.map(profile => ({
id: `profile.${slugify(profile.name).toLowerCase()}`, id: `profile.${this.terminal.getProfileID(profile)}`,
name: `New tab: ${profile.name}`, name: `New tab: ${profile.name}`,
})), })),
] ]

View File

@ -1,5 +1,4 @@
import * as fs from 'mz/fs' import * as fs from 'mz/fs'
import slugify from 'slugify'
import { NgModule } from '@angular/core' import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser' import { BrowserModule } from '@angular/platform-browser'
@ -187,8 +186,7 @@ export default class TerminalModule { // eslint-disable-line @typescript-eslint/
hostApp.newWindow() hostApp.newWindow()
} }
if (hotkey.startsWith('profile.')) { if (hotkey.startsWith('profile.')) {
const profiles = await terminal.getProfiles() const profile = await terminal.getProfileByID(hotkey.split('.')[1])
const profile = profiles.find(x => slugify(x.name).toLowerCase() === hotkey.split('.')[1])
if (profile) { if (profile) {
terminal.openTabWithOptions(profile.sessionOptions) terminal.openTabWithOptions(profile.sessionOptions)
} }

View File

@ -40,6 +40,7 @@ export class TerminalService {
...this.config.store.terminal.profiles, ...this.config.store.terminal.profiles,
...skipDefault ? [] : shells.filter(x => includeHidden || !x.hidden).map(shell => ({ ...skipDefault ? [] : shells.filter(x => includeHidden || !x.hidden).map(shell => ({
name: shell.name, name: shell.name,
shell: shell.id,
icon: shell.icon, icon: shell.icon,
sessionOptions: this.optionsFromShell(shell), sessionOptions: this.optionsFromShell(shell),
isBuiltin: true, isBuiltin: true,
@ -47,14 +48,25 @@ export class TerminalService {
] ]
} }
getProfileID (profile: Profile): string {
return slugify(profile.name).toLowerCase()
}
async getProfileByID (id: string): Promise<Profile> {
const profiles = await this.getProfiles({ includeHidden: true })
return profiles.find(x => this.getProfileID(x) === id) || profiles[0]
}
/** /**
* Launches a new terminal with a specific shell and CWD * Launches a new terminal with a specific shell and CWD
* @param pause Wait for a keypress when the shell exits * @param pause Wait for a keypress when the shell exits
*/ */
async openTab (profile?: Profile, cwd?: string|null, pause?: boolean): Promise<TerminalTabComponent> { async openTab (profile?: Profile, cwd?: string|null, pause?: boolean): Promise<TerminalTabComponent> {
if (!profile) { if (!profile) {
const profiles = await this.getProfiles({ includeHidden: true }) profile = await this.getProfileByID(this.config.store.terminal.profile)
profile = profiles.find(x => slugify(x.name).toLowerCase() === this.config.store.terminal.profile) || profiles[0] if (!profile) {
profile = (await this.getProfiles({ includeHidden: true }))[0]
}
} }
cwd = cwd || profile.sessionOptions.cwd cwd = cwd || profile.sessionOptions.cwd

View File

@ -88,13 +88,15 @@ export class WSLShellProvider extends ShellProvider {
if (!childKey.DistributionName) { if (!childKey.DistributionName) {
continue continue
} }
const wslVersion = (childKey.Flags.value & 8) ? 2 : 1
const name = childKey.DistributionName.value const name = childKey.DistributionName.value
const fsBase = (wslVersion === 2) ? `\\\\wsl$\\${name}` : (childKey.BasePath.value as string + '\\rootfs')
const shell: Shell = { const shell: Shell = {
id: `wsl-${slugify(name)}`, id: `wsl-${slugify(name)}`,
name: `WSL / ${name}`, name: `WSL / ${name}`,
command: wslPath, command: wslPath,
args: ['-d', name], args: ['-d', name],
fsBase: childKey.BasePath.value as string + '\\rootfs', fsBase,
env: { env: {
TERM: 'xterm-color', TERM: 'xterm-color',
COLORTERM: 'truecolor', COLORTERM: 'truecolor',