alm
Version:
The best IDE for TypeScript
274 lines (247 loc) • 9.39 kB
text/typescript
/**
* The session manages the state associated with a browser window connection.
* This allows us to restore tabs + userconfiguration etc.
* We just drive the session id from the browser url
*/
/** Imports */
import * as flm from "../workers/fileListing/fileListingMaster";
import * as types from "../../common/types";
import * as json from "../../common/json";
import * as fsu from "../utils/fsu";
import * as utils from "../../common/utils";
import * as workingDir from "./workingDir";
import * as commandLine from "../commandLine";
import * as tsconfig from "../workers/lang/core/tsconfig";
const sessionFile = types.cacheDir + '/sessionsV2.json'
/**
* If there is no session then a default one will be created for you and sent over
*/
export function getDefaultOrNewSession(sessionId: string): types.SessionOnDisk {
let sessions: types.SessionOnDisk[] = readDiskSessionsFile().sessions;
/**
* Cases to handle
* if default select first (but if none create)
* if duplicate then duplicate (but if none create and use that one)
* if session id then search (but if none create)
*/
// if none create
const ifNoneCreate = (session: types.SessionOnDisk, id = utils.createId()) => {
if (!session) {
session = {
id,
tabLayout: {
type: 'stack',
width: 100,
height: 100,
tabs: [],
subItems: [],
activeItemIndex: 0,
},
lastUsed: new Date().getTime(),
selectedTabId: null,
}
writeDiskSession(session);
return session;
} else {
return session;
}
}
let session: types.SessionOnDisk;
if (commandLine.getOptions().debug) {
session = ifNoneCreate(sessions.find(session => session.id === types.urlHashDebugSession), types.urlHashDebugSession);
}
else if (!sessionId || sessionId === types.urlHashNormal) {
session = ifNoneCreate(sessions.filter(s => s.id !== types.urlHashDebugSession)[0]);
}
else if (sessionId === types.urlHashNewSession) {
session = sessions[0]; // last used is always on top
if (session) {
session = {
id: utils.createId(),
tabLayout: session.tabLayout,
lastUsed: new Date().getTime(),
selectedTabId: session.selectedTabId,
}
writeDiskSession(session);
}
else {
session = ifNoneCreate(session);
}
}
else {
session = ifNoneCreate(sessions.find(session => session.id === sessionId));
}
/**
* Update the session on disk for future calls to be stable
*/
let commandLineTabs = getCommandLineTabs();
if (commandLineTabs.length) {
// Utility to find first `stack` and just add to its tabs
let done = false;
const tryAddingToStack = (layout: types.TabLayoutOnDisk) => {
if (done) return;
if (layout.type === 'stack') {
layout.tabs = layout.tabs.concat(commandLineTabs);
done = true;
}
else {
layout.subItems.forEach(tryAddingToStack)
}
}
// Start at root
tryAddingToStack(session.tabLayout);
/** Write it out */
writeDiskSession(session);
}
return session;
}
/**
* Only returns the command line tabs once
* Means you can call it as many times as you like
*/
function getCommandLineTabs(): types.TabInstanceOnDisk[] {
/** Add any command line files to the session */
let files = commandLine.getOptions().filePaths;
let tabs = files
.map((file) => utils.getUrlFromFilePathAndProtocol({ protocol: 'file', filePath: file }))
.map((url) => workingDir.makeRelativeUrl(url))
.map((relativeUrl) => ({ id: utils.createId(), relativeUrl, additionalData: null }));
// clear for future
// Doing it multiple times would mean that we would polute the user session on each new tab opening
commandLine.getOptions().filePaths = [];
return tabs;
}
/**
* UI to disk and Disk to UI helpers
*/
function uiToDiskTab(uiTab: types.TabInstance): types.TabInstanceOnDisk {
let relativeUrl = workingDir.makeRelativeUrl(uiTab.url);
return {
id: uiTab.id,
relativeUrl,
additionalData: uiTab.additionalData,
};
}
function diskToUITab(diskTab: types.TabInstanceOnDisk): types.TabInstance | null {
let url = workingDir.makeAbsoluteUrl(diskTab.relativeUrl);
const { filePath, protocol } = utils.getFilePathAndProtocolFromUrl(url);
if (protocol === 'file' && !fsu.existsSync(filePath)) return null;
return {
id: diskTab.id,
url,
additionalData: diskTab.additionalData,
};
}
function uiToDiskTabLayout(uiLayout: types.TabLayout): types.TabLayoutOnDisk {
return {
type: uiLayout.type,
width: uiLayout.width,
height: uiLayout.height,
tabs: uiLayout.tabs.map(uiToDiskTab),
subItems: uiLayout.subItems.map(uiToDiskTabLayout),
activeItemIndex: uiLayout.activeItemIndex,
}
}
function diskToUITabLayout(diskLayout: types.TabLayoutOnDisk): types.TabLayout {
return {
type: diskLayout.type,
width: diskLayout.width,
height: diskLayout.height,
tabs: diskLayout.tabs.map(diskToUITab).filter(x => !!x),
subItems: diskLayout.subItems.map(diskToUITabLayout),
activeItemIndex: diskLayout.activeItemIndex,
}
}
export function readDiskSessionsFile() {
let sessionFileContents: types.SessionsFileContents = {
sessions: []
};
if (fsu.existsSync(sessionFile) && !commandLine.getOptions().safe) {
let contents = json.parse<types.SessionsFileContents>(fsu.readFile(sessionFile));
if (contents.data) {
sessionFileContents = contents.data;
}
}
/**
* Active project setup logic. In decreasing order
* - If there is an active project in the command line
* - If there is an active project in the last session
* - Common locations
*/
let commonTsconfigLocations = [
'.',
'./src',
'./ts',
'./lib',
'./App-UI/src'
].map(x => x + '/tsconfig.json');
if (commandLine.getOptions().init) {
sessionFileContents.relativePathToTsconfig = './tsconfig.json';
// Write the tsconfig.json file
tsconfig.createProjectRootSync(workingDir.getProjectRoot());
writeDiskSessionFile(sessionFileContents);
// Clear so we don't keep creating
commandLine.getOptions().init = false;
}
else if (commandLine.getOptions().project) {
sessionFileContents.relativePathToTsconfig = workingDir.makeRelative(commandLine.getOptions().project);
writeDiskSessionFile(sessionFileContents);
} else if (!sessionFileContents.relativePathToTsconfig) {
let found = commonTsconfigLocations.find(cl => fsu.existsSync(cl));
if (found) {
sessionFileContents.relativePathToTsconfig = found;
writeDiskSessionFile(sessionFileContents);
}
}
return sessionFileContents;
}
function writeDiskSession(session: types.SessionOnDisk) {
// Update last used time
session.lastUsed = new Date().getTime();
const sessionFileContents = readDiskSessionsFile();
// Merge with what is on disk by id
const sessions = sessionFileContents.sessions
.filter(sesh => sesh.id !== session.id);
sessions.unshift(session); // last used is always on top
sessionFileContents.sessions = sessions;
writeDiskSessionFile(sessionFileContents);
}
function writeDiskSessionFile(sessionFileContents: types.SessionsFileContents) {
fsu.writeFile(sessionFile, json.stringify(sessionFileContents));
}
export function setTsconfigPath(tsconfigFilePath: string) {
let sessionFileContents = readDiskSessionsFile();
sessionFileContents.relativePathToTsconfig = workingDir.makeRelative(tsconfigFilePath);
writeDiskSessionFile(sessionFileContents);
}
export function setOpenUITabs(sessionId: string, layout: types.TabLayout, selectedTabId: string | null) {
let session = getDefaultOrNewSession(sessionId);
session.tabLayout = uiToDiskTabLayout(layout);
session.selectedTabId = selectedTabId;
writeDiskSession(session);
}
export function getOpenUITabs(sessionId: string) {
let session = getDefaultOrNewSession(sessionId);
return { tabLayout: diskToUITabLayout(session.tabLayout), selectedTabId: session.selectedTabId }
}
export function getValidSessionId(sessionId: string) {
let session = getDefaultOrNewSession(sessionId);
return { sessionId: session.id }
}
/**
* For various simple settings
* setter
*/
export function setSetting(config: { sessionId: string, settingId: string, value: any }) {
let session = getDefaultOrNewSession(config.sessionId);
session[config.settingId] = config.value;
writeDiskSession(session);
}
/**
* For various simple settings
* getter
*/
export function getSetting(config: { sessionId: string, settingId: string }): any {
let session = getDefaultOrNewSession(config.sessionId);
return session[config.settingId];
}