From 5b733b13eb4cc48f1ef43c614c839fd8b25aea92 Mon Sep 17 00:00:00 2001 From: Teffen Ellis Date: Thu, 18 Nov 2021 17:18:50 -0500 Subject: [PATCH] Fix issues surrounding persisted workspaces. - Moved URI helper. --- src/vs/base/common/uri.ts | 10 ++++ src/vs/code/browser/workbench/workbench.ts | 20 ++----- src/vs/workbench/browser/web.main.ts | 64 +++++++++++++++++++++- 3 files changed, 76 insertions(+), 18 deletions(-) diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index a87aff7f1dcf0..728ac8aa7348f 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -701,3 +701,13 @@ function percentDecode(str: string): string { } return str.replace(_rEncodedAsHex, (match) => decodeURIComponentGraceful(match)); } + +/** + * Encode a path for opening via the folder or workspace query parameter. This + * preserves slashes so it can be edited by hand more easily. + * + * @author coder + */ +export const encodePath = (path: string): string => { + return path.split('/').map((p) => encodeURIComponent(p)).join('/'); +}; diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index b9835abecea22..17129da15cd07 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -10,7 +10,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { isEqual } from 'vs/base/common/resources'; -import { URI, UriComponents } from 'vs/base/common/uri'; +import { encodePath, URI, UriComponents } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import { request } from 'vs/base/parts/request/browser/request'; import { localize } from 'vs/nls'; @@ -37,16 +37,6 @@ function doCreateUri(path: string, queryValues: Map): URI { return URI.parse(window.location.href).with({ path, query }); } -/** - * Encode a path for opening via the folder or workspace query parameter. This - * preserves slashes so it can be edited by hand more easily. - * - * @author coder - */ -export const encodePath = (path: string): string => { - return path.split('/').map((p) => encodeURIComponent(p)).join('/'); -}; - interface ICredential { service: string; account: string; @@ -454,11 +444,11 @@ class WindowIndicator implements IWindowIndicator { * @author coder */ const toRemote = (value: string): string => { - if (value.startsWith("/")) { - return "vscode-remote://" + value; + if (value.startsWith('/')) { + return 'vscode-remote://' + value; } - return value - } + return value; + }; const query = new URL(document.location.href).searchParams; query.forEach((value, key) => { diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 7d458accca9bd..9b7c3b112aa7e 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -22,12 +22,12 @@ import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteA import { IFileService } from 'vs/platform/files/common/files'; import { FileService } from 'vs/platform/files/common/fileService'; import { Schemas } from 'vs/base/common/network'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, toWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { onUnexpectedError } from 'vs/base/common/errors'; import { setFullscreen } from 'vs/base/browser/browser'; -import { URI } from 'vs/base/common/uri'; -import { IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; +import { encodePath, URI } from 'vs/base/common/uri'; +import { isRecentFolder, IWorkspaceInitializationPayload, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { WorkspaceService } from 'vs/workbench/services/configuration/browser/configurationService'; import { ConfigurationCache } from 'vs/workbench/services/configuration/browser/configurationCache'; import { ISignService } from 'vs/platform/sign/common/sign'; @@ -68,6 +68,7 @@ import { safeStringify } from 'vs/base/common/objects'; import { ICredentialsService } from 'vs/workbench/services/credentials/common/credentials'; import { IndexedDB } from 'vs/base/browser/indexedDB'; import { CodeServerClientAdditions } from 'vs/workbench/browser/client'; +import { BrowserWorkspacesService } from 'vs/workbench/services/workspaces/browser/workspacesService'; class BrowserMain extends Disposable { @@ -219,6 +220,63 @@ class BrowserMain extends Disposable { }) ]); + /** + * Added to persist recent workspaces in the browser. + * @author coder + * @example User specified a directory at startup. + * ```sh + * code-server ./path/to/project/ + * ``` + * + * @example Blank project without CLI arguments, + * using the last opened directory in the browser. + * ```sh + * code-server + * open http://localhost:8000/ + * ``` + * + * @example Query params override CLI arguments. + * ```sh + * code-server ./path/to/project/ + * open http://localhost:8000/?folder=/path/to/different/project + * ``` + */ + const browserWorkspacesService = new BrowserWorkspacesService(storageService, configurationService, logService, fileService, environmentService, uriIdentityService); + serviceCollection.set(IWorkspacesService, browserWorkspacesService); + const workspace = configurationService.getWorkspace(); + + logService.debug('Workspace Folders:', workspace.folders); + + if (workspace.folders.length === 0) { + logService.debug('Workspace is empty. Checking for recent folders...'); + + const recentlyOpened = await browserWorkspacesService.getRecentlyOpened(); + + for (const recent of recentlyOpened.workspaces) { + if (isRecentFolder(recent)) { + logService.debug('Recent folder found...'); + const folder = toWorkspaceFolder(recent.folderUri); + // Note that the `folders` property should be reassigned instead of pushed into. + // This property has a setter which updates the workspace's file cache. + workspace.folders = [folder]; + + + /** + * Opening a folder from the browser navigates to a URL including the folder param. + * However, since we're overriding the default state of a blank editor, + * we update the URL query param to match this behavior. + * This is especially useful when a user wants to share a link to server with a specific folder. + * + * @see `WorkspaceProvider.createTargetUrl` + * @see `WorkspaceProvider.QUERY_PARAM_FOLDER` + */ + const nextQueryParam = `?folder=${encodePath(folder.uri.path)}`; + window.history.replaceState(null, '', nextQueryParam); + break; + } + } + } + // Workspace Trust Service const workspaceTrustEnablementService = new WorkspaceTrustEnablementService(configurationService, environmentService); serviceCollection.set(IWorkspaceTrustEnablementService, workspaceTrustEnablementService);