diff --git a/lib/actions/term-groups.ts b/lib/actions/term-groups.ts index 154d07a4..d2a83d4e 100644 --- a/lib/actions/term-groups.ts +++ b/lib/actions/term-groups.ts @@ -11,7 +11,6 @@ import findBySession from '../utils/term-groups'; import {getRootGroups} from '../selectors'; import {setActiveSession, ptyExitSession, userExitSession} from './sessions'; import {ITermState, ITermGroup, HyperState, HyperDispatch, HyperActions} from '../hyper'; -import {Immutable} from 'seamless-immutable'; function requestSplit(direction: 'VERTICAL' | 'HORIZONTAL') { return (activeUid: string) => (dispatch: HyperDispatch, getState: () => HyperState): void => { @@ -67,7 +66,7 @@ export function setActiveGroup(uid: string) { // When we've found the next group which we want to // set as active (after closing something), we also need // to find the first child group which has a sessionUid. -const findFirstSession = (state: Immutable, group: Immutable): string | undefined => { +const findFirstSession = (state: ITermState, group: ITermGroup): string | undefined => { if (group.sessionUid) { return group.sessionUid; } @@ -90,7 +89,7 @@ const findPrevious = (list: T[], old: T) => { return index ? list[index - 1] : list[1]; }; -const findNextSessionUid = (state: Immutable, group: Immutable) => { +const findNextSessionUid = (state: ITermState, group: ITermGroup) => { // If we're closing a root group (i.e. a whole tab), // the next group needs to be a root group as well: if (state.activeRootGroup === group.uid) { diff --git a/lib/actions/ui.ts b/lib/actions/ui.ts index 53907ccf..af947222 100644 --- a/lib/actions/ui.ts +++ b/lib/actions/ui.ts @@ -28,7 +28,7 @@ import { import {setActiveGroup} from './term-groups'; import parseUrl from 'parse-url'; -import {HyperState, HyperDispatch, HyperActions} from '../hyper'; +import {HyperState, HyperDispatch, HyperActions, ITermGroups} from '../hyper'; import {stat, Stats} from 'fs'; export function openContextMenu(uid: string, selection: any) { @@ -110,7 +110,7 @@ export function windowGeometryUpdated(): HyperActions { // Find all sessions that are below the given // termGroup uid in the hierarchy: -const findChildSessions = (termGroups: HyperState['termGroups']['termGroups'], uid: string): string[] => { +const findChildSessions = (termGroups: ITermGroups, uid: string): string[] => { const group = termGroups[uid]; if (group.sessionUid) { return [uid]; diff --git a/lib/config.d.ts b/lib/config.d.ts index 807b463e..9e451a0c 100644 --- a/lib/config.d.ts +++ b/lib/config.d.ts @@ -1,5 +1,24 @@ import {FontWeight} from 'xterm'; +export type ColorMap = { + black: string; + blue: string; + cyan: string; + green: string; + lightBlack: string; + lightBlue: string; + lightCyan: string; + lightGreen: string; + lightMagenta: string; + lightRed: string; + lightWhite: string; + lightYellow: string; + magenta: string; + red: string; + white: string; + yellow: string; +}; + export type configOptions = { autoUpdatePlugins: boolean | string; backgroundColor: string; @@ -7,24 +26,7 @@ export type configOptions = { bellSound: string | null; bellSoundURL: string | null; borderColor: string; - colors: { - black: string; - blue: string; - cyan: string; - green: string; - lightBlack: string; - lightBlue: string; - lightCyan: string; - lightGreen: string; - lightMagenta: string; - lightRed: string; - lightWhite: string; - lightYellow: string; - magenta: string; - red: string; - white: string; - yellow: string; - }; + colors: ColorMap; copyOnSelect: boolean; css: string; cursorAccentColor: string; diff --git a/lib/hyper.d.ts b/lib/hyper.d.ts index 8914f372..97e3eca7 100644 --- a/lib/hyper.d.ts +++ b/lib/hyper.d.ts @@ -9,27 +9,28 @@ declare global { } } -export type ITermGroup = { +export type ITermGroup = Immutable<{ uid: string; sessionUid: string | null; parentUid: string | null; direction: 'HORIZONTAL' | 'VERTICAL' | null; sizes: number[] | null; children: string[]; -}; +}>; -export type ITermGroups = Record; +export type ITermGroups = Immutable>; -export type ITermState = { - termGroups: ITermGroups; +export type ITermState = Immutable<{ + termGroups: Mutable; activeSessions: Record; activeRootGroup: string | null; -}; +}>; export type cursorShapes = 'BEAM' | 'UNDERLINE' | 'BLOCK'; import {FontWeight, Terminal} from 'xterm'; +import {ColorMap} from './config'; -export type uiState = { +export type uiState = Immutable<{ _lastUpdate: number | null; activeUid: string | null; activityMarkers: Record; @@ -38,24 +39,7 @@ export type uiState = { bellSoundURL: string | null; bellSound: string | null; borderColor: string; - colors: { - black: string; - blue: string; - cyan: string; - green: string; - lightBlack: string; - lightBlue: string; - lightCyan: string; - lightGreen: string; - lightMagenta: string; - lightRed: string; - lightWhite: string; - lightYellow: string; - magenta: string; - red: string; - white: string; - yellow: string; - }; + colors: ColorMap; cols: number | null; copyOnSelect: boolean; css: string; @@ -107,7 +91,7 @@ export type uiState = { updateVersion: string | null; webGLRenderer: boolean; webLinksActivationKey: string; -}; +}>; export type session = { cleared: boolean; @@ -123,22 +107,19 @@ export type session = { splitDirection?: 'HORIZONTAL' | 'VERTICAL'; activeUid?: string; }; -export type sessionState = { +export type sessionState = Immutable<{ sessions: Record; activeUid: string | null; write?: any; -}; +}>; -export {ITermGroupReducer} from './reducers/term-groups'; -import {ITermGroupReducer} from './reducers/term-groups'; +export type ITermGroupReducer = Reducer; -export {IUiReducer} from './reducers/ui'; -import {IUiReducer} from './reducers/ui'; +export type IUiReducer = Reducer; -export {ISessionReducer} from './reducers/sessions'; -import {ISessionReducer} from './reducers/sessions'; +export type ISessionReducer = Reducer; -import {Middleware} from 'redux'; +import {Middleware, Reducer} from 'redux'; export type hyperPlugin = { getTabProps: any; getTabsProps: any; @@ -163,9 +144,9 @@ export type hyperPlugin = { }; export type HyperState = { - ui: Immutable; - sessions: Immutable; - termGroups: Immutable; + ui: uiState; + sessions: sessionState; + termGroups: ITermState; }; import {UIActions} from './constants/ui'; @@ -188,8 +169,6 @@ export type HyperActions = ( | TabActions ) & {effect?: () => void}; -type immutableRecord = {[k in keyof T]: Immutable}; - import configureStore from './store/configure-store'; export type HyperDispatch = ReturnType['dispatch']; @@ -276,7 +255,7 @@ export type TermGroupOwnProps = { fontSmoothing?: string; parentProps: TermsProps; ref_: (uid: string, term: Term | null) => void; - termGroup: Immutable; + termGroup: ITermGroup; terms: Record; } & Pick< TermsProps, @@ -336,7 +315,7 @@ export type TermProps = { bellSoundURL: string | null; borderColor: string; cleared: boolean; - colors: uiState['colors']; + colors: ColorMap; cols: number | null; copyOnSelect: boolean; cursorAccentColor?: string; @@ -379,4 +358,10 @@ export type TermProps = { ref_: (uid: string, term: Term | null) => void; } & extensionProps; +// Utility types + +export type Mutable = T extends Immutable ? (Exclude extends never ? U : Exclude) : T; + +export type immutableRecord = {[k in keyof T]: Immutable}; + export type Assignable = {[k in keyof U]: k extends keyof T ? T[k] : U[k]} & Partial; diff --git a/lib/reducers/sessions.ts b/lib/reducers/sessions.ts index 5054bc7f..1850df2f 100644 --- a/lib/reducers/sessions.ts +++ b/lib/reducers/sessions.ts @@ -1,4 +1,4 @@ -import Immutable, {Immutable as ImmutableType} from 'seamless-immutable'; +import Immutable from 'seamless-immutable'; import {decorateSessionsReducer} from '../utils/plugins'; import { SESSION_ADD, @@ -13,10 +13,10 @@ import { SESSION_SEARCH, SESSION_SEARCH_CLOSE } from '../constants/sessions'; -import {sessionState, session, HyperActions} from '../hyper'; +import {sessionState, session, Mutable, ISessionReducer} from '../hyper'; -const initialState: ImmutableType = Immutable({ - sessions: {} as Record, +const initialState: sessionState = Immutable>({ + sessions: {}, activeUid: null }); @@ -35,7 +35,7 @@ function Session(obj: Immutable.DeepPartial) { return Immutable(x).merge(obj); } -function deleteSession(state: ImmutableType, uid: string) { +function deleteSession(state: sessionState, uid: string) { return state.updateIn(['sessions'], (sessions: typeof state['sessions']) => { const sessions_ = sessions.asMutable(); delete sessions_[uid]; @@ -43,7 +43,7 @@ function deleteSession(state: ImmutableType, uid: string) { }); } -const reducer = (state: ImmutableType = initialState, action: HyperActions) => { +const reducer: ISessionReducer = (state = initialState, action) => { switch (action.type) { case SESSION_ADD: return state.set('activeUid', action.uid).setIn( @@ -134,6 +134,4 @@ const reducer = (state: ImmutableType = initialState, action: Hype } }; -export type ISessionReducer = typeof reducer; - export default decorateSessionsReducer(reducer); diff --git a/lib/reducers/term-groups.ts b/lib/reducers/term-groups.ts index e4e24169..3880f7ab 100644 --- a/lib/reducers/term-groups.ts +++ b/lib/reducers/term-groups.ts @@ -4,17 +4,17 @@ import {TERM_GROUP_EXIT, TERM_GROUP_RESIZE} from '../constants/term-groups'; import {SESSION_ADD, SESSION_SET_ACTIVE, SessionAddAction} from '../constants/sessions'; import findBySession from '../utils/term-groups'; import {decorateTermGroupsReducer} from '../utils/plugins'; -import {ITermGroup, ITermState, ITermGroups, HyperActions} from '../hyper'; +import {ITermGroup, ITermState, ITermGroups, ITermGroupReducer, Mutable} from '../hyper'; const MIN_SIZE = 0.05; -const initialState = Immutable({ +const initialState: ITermState = Immutable>({ termGroups: {}, activeSessions: {}, activeRootGroup: null }); -function TermGroup(obj: Immutable.DeepPartial) { - const x: ITermGroup = { +function TermGroup(obj: Immutable.DeepPartial>) { + const x: Mutable = { uid: '', sessionUid: null, parentUid: null, @@ -26,7 +26,7 @@ function TermGroup(obj: Immutable.DeepPartial) { } // Recurse upwards until we find a root term group (no parent). -const findRootGroup = (termGroups: ImmutableType, uid: string): ImmutableType => { +const findRootGroup = (termGroups: ITermGroups, uid: string): ITermGroup => { const current = termGroups[uid]; if (!current.parentUid) { return current; @@ -35,7 +35,7 @@ const findRootGroup = (termGroups: ImmutableType, uid: string): Imm return findRootGroup(termGroups, current.parentUid); }; -const setActiveGroup = (state: ImmutableType, action: {uid: string}) => { +const setActiveGroup = (state: ITermState, action: {uid: string}) => { if (!action.uid) { return state.set('activeRootGroup', null); } @@ -66,7 +66,7 @@ const removalRebalance = (oldSizes: ImmutableType, index: number) => { ); }; -const splitGroup = (state: ImmutableType, action: SessionAddAction) => { +const splitGroup = (state: ITermState, action: SessionAddAction) => { const {splitDirection, uid, activeUid} = action; const activeGroup = findBySession(state, activeUid!)!; // If we're splitting in the same direction as the current active @@ -132,11 +132,7 @@ const splitGroup = (state: ImmutableType, action: SessionAddAction) // Replace the parent by the given child in the tree, // used when we remove another child and we're left // with a one-to-one mapping between parent and child. -const replaceParent = ( - state: ImmutableType, - parent: ImmutableType, - child: ImmutableType -) => { +const replaceParent = (state: ITermState, parent: ITermGroup, child: ITermGroup) => { if (parent.parentUid) { const parentParent = state.termGroups[parent.parentUid]; // If the parent we're replacing has a parent, @@ -161,7 +157,7 @@ const replaceParent = ( .setIn(['termGroups', child.uid, 'parentUid'], parent.parentUid); }; -const removeGroup = (state: ImmutableType, uid: string) => { +const removeGroup = (state: ITermState, uid: string) => { const group = state.termGroups[uid]; // when close tab with multiple panes, it remove group from parent to child. so maybe the parentUid exists but parent group have removed. // it's safe to remove the group. @@ -188,7 +184,7 @@ const removeGroup = (state: ImmutableType, uid: string) => { .set('activeSessions', state.activeSessions.without(uid)); }; -const resizeGroup = (state: ImmutableType, uid: string, sizes: number[]) => { +const resizeGroup = (state: ITermState, uid: string, sizes: number[]) => { // Make sure none of the sizes fall below MIN_SIZE: if (sizes.find((size) => size < MIN_SIZE)) { return state; @@ -197,7 +193,7 @@ const resizeGroup = (state: ImmutableType, uid: string, sizes: numbe return state.setIn(['termGroups', uid, 'sizes'], sizes); }; -const reducer = (state = initialState, action: HyperActions) => { +const reducer: ITermGroupReducer = (state = initialState, action) => { switch (action.type) { case SESSION_ADD: { if (action.splitDirection) { @@ -227,6 +223,4 @@ const reducer = (state = initialState, action: HyperActions) => { } }; -export type ITermGroupReducer = typeof reducer; - export default decorateTermGroupsReducer(reducer); diff --git a/lib/reducers/ui.ts b/lib/reducers/ui.ts index 26e7f1b2..4405f6fc 100644 --- a/lib/reducers/ui.ts +++ b/lib/reducers/ui.ts @@ -24,7 +24,7 @@ import { SESSION_SET_CWD } from '../constants/sessions'; import {UPDATE_AVAILABLE} from '../constants/updater'; -import {uiState, HyperActions} from '../hyper'; +import {uiState, Mutable, IUiReducer} from '../hyper'; const allowedCursorShapes = new Set(['BEAM', 'BLOCK', 'UNDERLINE']); const allowedCursorBlinkValues = new Set([true, false]); @@ -33,7 +33,7 @@ const allowedHamburgerMenuValues = new Set([true, false]); const allowedWindowControlsValues = new Set([true, false, 'left']); // Populate `config-default.js` from this :) -const initial: ImmutableType = Immutable({ +const initial: uiState = Immutable>({ cols: null, rows: null, scrollback: 1000, @@ -57,7 +57,7 @@ const initial: ImmutableType = Immutable({ letterSpacing: 0, css: '', termCSS: '', - openAt: {} as Record, + openAt: {}, resizeAt: 0, colors: { black: '#000000', @@ -77,7 +77,7 @@ const initial: ImmutableType = Immutable({ lightCyan: '#68FDFE', lightWhite: '#FFFFFF' }, - activityMarkers: {} as Record, + activityMarkers: {}, notifications: { font: false, resize: false, @@ -115,7 +115,7 @@ const initial: ImmutableType = Immutable({ const currentWindow = remote.getCurrentWindow(); -const reducer = (state = initial, action: HyperActions) => { +const reducer: IUiReducer = (state = initial, action) => { let state_ = state; let isMax; switch (action.type) { @@ -127,7 +127,7 @@ const reducer = (state = initial, action: HyperActions) => { // font size changed from the config .merge( (() => { - const ret: Immutable.DeepPartial = {}; + const ret: Immutable.DeepPartial> = {}; if (config.scrollback) { ret.scrollback = config.scrollback; @@ -452,6 +452,4 @@ const reducer = (state = initial, action: HyperActions) => { return state_; }; -export type IUiReducer = typeof reducer; - export default decorateUIReducer(reducer); diff --git a/lib/utils/term-groups.ts b/lib/utils/term-groups.ts index b59731a3..9e3706e1 100644 --- a/lib/utils/term-groups.ts +++ b/lib/utils/term-groups.ts @@ -1,7 +1,6 @@ import {ITermState} from '../hyper'; -import {Immutable} from 'seamless-immutable'; -export default function findBySession(termGroupState: Immutable, sessionUid: string) { +export default function findBySession(termGroupState: ITermState, sessionUid: string) { const {termGroups} = termGroupState; return Object.keys(termGroups) .map((uid) => termGroups[uid])