Skip to content

Commit

Permalink
feat: split screen Terminal
Browse files Browse the repository at this point in the history
  • Loading branch information
starpit committed Jun 6, 2020
1 parent 7252ad4 commit 0f9700a
Show file tree
Hide file tree
Showing 61 changed files with 1,218 additions and 345 deletions.
231 changes: 219 additions & 12 deletions packages/core/src/core/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* eslint-disable no-dupe-class-members */

import { EventEmitter } from 'events'
import { Tab } from '../webapp/tab'

import { ExecType } from '../models/command'
import { ScalarResponse } from '../models/entity'
import MultiModalResponse from '../models/mmr/types'
import NavResponse from '../models/NavResponse'
import Tab, { getPrimaryTabId } from '../webapp/tab'
import { CommandStartEvent, CommandCompleteEvent, CommandStartHandler, CommandCompleteHandler } from '../repl/events'

const eventChannelUnsafe = new EventEmitter()
eventChannelUnsafe.setMaxListeners(100)
Expand All @@ -33,16 +41,49 @@ class EventBusBase {
}

class WriteEventBus extends EventBusBase {
public emit(channel: '/tab/new' | '/tab/close/request' | '/tab/close' | '/tab/offline', tab: Tab): void
public emit(channel: '/tab/new' | '/tab/close' | '/tab/offline', tab: Tab): void
public emit(channel: '/tab/new/request'): void
public emit(channel: '/tab/switch/request', idx: number): void
public emit(channel: string, args?: any) {
return this.eventBus.emit(channel, args)
}

public emitWithTabId(channel: '/tab/offline', tabId: string): void
public emitWithTabId(channel: string, tabId: string) {
return this.eventBus.emit(`${channel}/${tabId}`)
private emitCommandEvent(which: 'start' | 'complete', event: CommandStartEvent | CommandCompleteEvent) {
this.eventBus.emit(`/command/${which}`, event)

if (event.execType !== ExecType.Nested) {
this.eventBus.emit(`/command/${which}/fromuser`, event)
this.eventBus.emit(`/command/${which}/fromuser/${event.tab.uuid}`, event)

const primary = getPrimaryTabId(event.tab)
if (event.tab.uuid !== primary) {
this.eventBus.emit(`/command/${which}/fromuser/${primary}`, event)
}

this.eventBus.emit(`/command/${which}/fromuser/${primary}/type/${event.execType}`, event)
}
}

public emitCommandStart(event: CommandStartEvent): void {
this.emitCommandEvent('start', event)
}

public emitCommandComplete(event: CommandCompleteEvent): void {
this.emitCommandEvent('complete', event)

if (event.execType !== ExecType.Nested) {
this.eventBus.emit(`/command/complete/fromuser/${event.responseType}`, event)
this.eventBus.emit(`/command/complete/fromuser/${event.responseType}/${event.tab.uuid}`, event)

const primary = getPrimaryTabId(event.tab)
if (primary !== event.tab.uuid) {
this.eventBus.emit(`/command/complete/fromuser/${event.responseType}/${primary}`, event)
}
}
}

public emitWithTabId(channel: '/tab/offline' | '/tab/close/request', tabId: string, tab?: Tab): void {
this.eventBus.emit(`${channel}/${tabId}`, tabId, tab)
}
}

Expand All @@ -58,14 +99,180 @@ class ReadEventBus extends WriteEventBus {
return this.eventBus.on(channel, listener)
}

public onWithTabId(channel: '/tab/offline', tabId: string, listener: (tabId: string) => void): void
public onWithTabId(channel: string, tabId: string, listener: any) {
return this.eventBus.on(`${channel}/${tabId}`, listener)
private onCommand(
which: 'start' | 'complete',
splitId: string,
splitHandler: CommandStartHandler | CommandCompleteHandler,
tabId?: string,
tabHandler = splitHandler
): void {
this.eventBus.on(`/command/${which}/fromuser/${splitId}`, splitHandler)

if (tabId) {
this.eventBus.on(`/command/${which}/fromuser/${tabId}/type/${ExecType.ClickHandler}`, tabHandler)
}
}

private offCommand(
which: 'start' | 'complete',
splitId: string,
splitHandler: CommandStartHandler | CommandCompleteHandler,
tabId?: string,
tabHandler = splitHandler
): void {
this.eventBus.off(`/command/${which}/fromuser/${splitId}`, splitHandler)

if (tabId) {
this.eventBus.off(`/command/${which}/fromuser/${tabId}/type/${ExecType.ClickHandler}`, tabHandler)
}
}

public onAnyCommandStart(handler: CommandStartHandler) {
this.eventBus.on('/command/start/fromuser', handler)
}

public offAnyCommandStart(handler: CommandStartHandler) {
this.eventBus.on('/command/start/fromuser', handler)
}

public onAnyCommandComplete(handler: CommandStartHandler) {
this.eventBus.on('/command/complete/fromuser', handler)
}

public offAnyCommandComplete(handler: CommandStartHandler) {
this.eventBus.on('/command/complete/fromuser', handler)
}

public onCommandStart(
splitId: string,
splitHandler: CommandStartHandler,
tabId?: string,
tabHandler = splitHandler
): void {
this.onCommand('start', splitId, splitHandler, tabId, tabHandler)
}

public onceCommandStarts(tabId: string, handler: CommandStartHandler): void {
this.eventBus.once(`/command/start/fromuser/${tabId}`, handler)
}

private onResponseType(
responseType: 'ScalarResponse' | 'MultiModalResponse' | 'NavResponse',
splitId: string,
splitHandler: CommandCompleteHandler,
tabId?: string,
tabHandler = splitHandler
): void {
this.eventBus.on(`/command/complete/fromuser/${responseType}/${splitId}`, splitHandler)

if (tabId) {
this.eventBus.on(`/command/complete/fromuser/${responseType}/${tabId}`, tabHandler)
}
}

private offResponseType(
responseType: 'ScalarResponse' | 'MultiModalResponse' | 'NavResponse',
splitId: string,
splitHandler: CommandCompleteHandler,
tabId?: string,
tabHandler = splitHandler
): void {
this.eventBus.off(`/command/complete/fromuser/${responseType}/${splitId}`, splitHandler)

if (tabId) {
this.eventBus.off(`/command/complete/fromuser/${responseType}/${tabId}`, tabHandler)
}
}

public onScalarResponse(
splitId: string,
splitHandler: CommandCompleteHandler<ScalarResponse>,
tabId?: string,
tabHandler = splitHandler
): void {
this.onResponseType('ScalarResponse', splitId, splitHandler, tabId, tabHandler)
}

public offScalarResponse(
splitId: string,
splitHandler: CommandCompleteHandler<ScalarResponse>,
tabId?: string,
tabHandler = splitHandler
): void {
this.offResponseType('ScalarResponse', splitId, splitHandler, tabId, tabHandler)
}

public onMultiModalResponse(
tabId: string,
handler: CommandCompleteHandler<MultiModalResponse, 'MultiModalResponse'>
): void {
this.onResponseType('MultiModalResponse', tabId, handler)
}

public offMultiModalResponse(
tabId: string,
handler: CommandCompleteHandler<MultiModalResponse, 'MultiModalResponse'>
): void {
this.offResponseType('MultiModalResponse', tabId, handler)
}

public onNavResponse(tabId: string, handler: CommandCompleteHandler<NavResponse, 'NavResponse'>): void {
this.onResponseType('NavResponse', tabId, handler)
}

public offNavResponse(tabId: string, handler: CommandCompleteHandler<NavResponse, 'NavResponse'>): void {
this.offResponseType('NavResponse', tabId, handler)
}

public onCommandComplete(
splitId: string,
splitHandler: CommandCompleteHandler,
tabId?: string,
tabHandler = splitHandler
): void {
this.onCommand('complete', splitId, splitHandler, tabId, tabHandler)
}

public offCommandStart(
splitId: string,
splitHandler: CommandStartHandler,
tabId?: string,
tabHandler = splitHandler
): void {
this.offCommand('start', splitId, splitHandler, tabId, tabHandler)
}

public offCommandComplete(
splitId: string,
splitHandler: CommandCompleteHandler,
tabId?: string,
tabHandler = splitHandler
): void {
this.offCommand('complete', splitId, splitHandler, tabId, tabHandler)
}

public onWithTabId(
channel: '/tab/offline' | '/tab/close/request',
tabId: string,
listener: (tabId: string, tab: Tab) => void
): void {
this.eventBus.on(`${channel}/${tabId}`, listener)
}

public offWithTabId(
channel: '/tab/offline' | '/tab/close/request',
tabId: string,
listener: (tabId: string, tab: Tab) => void
): void {
this.eventBus.off(`${channel}/${tabId}`, listener)
}

public offWithTabId(channel: '/tab/offline', tabId: string, listener: () => void): void
public offWithTabId(channel: string, tabId: string, listener: any) {
return this.eventBus.off(`${channel}/${tabId}`, listener)
public onceWithTabId(
channel: '/tab/offline' | '/tab/close/request',
tabId: string,
listener: (tabId: string, tab: Tab) => void
): void {
this.eventBus.once(`${channel}/${tabId}`, listener)
}

public once(channel: '/tab/new', listener: (tab: Tab) => void): void
Expand All @@ -85,5 +292,5 @@ export const eventBus = new EventBus()
export function wireToStandardEvents(listener: () => void) {
eventBus.on('/tab/new', listener)
eventBus.on('/tab/switch/request', listener)
eventChannelUnsafe.on('/command/complete/fromuser', listener)
eventBus.onAnyCommandComplete(listener)
}
3 changes: 2 additions & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,10 @@ export { split, _split, Split } from './repl/split'
export { ReplEval, DirectReplEval } from './repl/types'
export { default as encodeComponent } from './repl/encode'
export { exec as internalBeCarefulExec, pexec as internalBeCarefulPExec, setEvaluatorImpl, doEval } from './repl/exec'
export { CommandStartEvent, CommandCompleteEvent } from './repl/events'

// Tabs
export { Tab, getCurrentTab, getTabId, sameTab } from './webapp/tab'
export { Tab, getCurrentTab, pexecInCurrentTab, getTabId, getPrimaryTabId, sameTab } from './webapp/tab'
export { default as TabState } from './models/tab-state'

// Themes
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/models/NavResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,5 @@ export function isNavResponse(entity: Entity): entity is NavResponse {
const nav = entity as NavResponse
return nav.apiVersion === 'kui-shell/v1' && nav.kind === 'NavResponse'
}

export default NavResponse
2 changes: 2 additions & 0 deletions packages/core/src/models/execOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,5 @@ export class DefaultExecOptionsForTab extends DefaultExecOptions {
this.block = block
}
}

export default ExecOptions
2 changes: 2 additions & 0 deletions packages/core/src/models/mmr/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,5 @@ export interface VisibilityTraits {
export interface IconTrait {
icon: ReactElement
}

export default MultiModalResponse
54 changes: 54 additions & 0 deletions packages/core/src/repl/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 2017-20 IBM Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import Tab from '../webapp/tab'
import ExecOptions from '../models/execOptions'
import { CommandOptions, ExecType, KResponse, ParsedOptions } from '../models/command'

export interface CommandStartEvent {
tab: Tab
route: string
command: string
execUUID: string
execType: ExecType
echo: boolean
}

type ResponseTypeStr = 'MultiModalResponse' | 'NavResponse' | 'ScalarResponse' | 'Incomplete' | 'Error'

export interface CommandCompleteEvent<R extends KResponse = KResponse, T extends ResponseTypeStr = ResponseTypeStr> {
tab: Tab

command: string
argvNoOptions: string[]
parsedOptions: ParsedOptions
execOptions: ExecOptions

execUUID: string
execType: ExecType
cancelled: boolean
echo: boolean
evaluatorOptions: CommandOptions

response: R
responseType: T
}

export type CommandStartHandler = (event: CommandStartEvent) => void

export type CommandCompleteHandler<R extends KResponse = KResponse, T extends ResponseTypeStr = ResponseTypeStr> = (
event: CommandCompleteEvent<R, T>
) => void
Loading

0 comments on commit 0f9700a

Please sign in to comment.