Skip to content

Commit

Permalink
Add option to choose root offset for previews (#405)
Browse files Browse the repository at this point in the history
* progress on adding root prefix

* fix bugs with over-eager serverroot applying
Fixes #155
  • Loading branch information
andreamah authored Jan 10, 2023
1 parent e763b37 commit efbca27
Show file tree
Hide file tree
Showing 14 changed files with 160 additions and 87 deletions.
10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@
"command": "livePreview.start.preview.atFile",
"when": "false"
},
{
"command": "livePreview.start.preview.atFileString",
"when": "false"
},
{
"command": "livePreview.start.debugPreview.atFile",
"when": "false"
Expand Down Expand Up @@ -241,6 +245,12 @@
"default": "",
"description": "%settings.defaultPreviewPath%"
},
"livePreview.serverRoot": {
"type": "string",
"default": "",
"description": "%settings.serverRoot%",
"scope": "resource"
},
"livePreview.debugOnExternalPreview": {
"type": "boolean",
"default": false,
Expand Down
1 change: 1 addition & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"settings.tasks.runTaskWithExternalPreview": "Whether or not to pair external preview instances with the auto-generated server task. When disabled, the server will also not automatically close (until the window is closed).",
"settings.defaultPreviewPath": "The file to automatically show upon starting the server. Leave blank to open at the index.",
"settings.debugOnExternalPreview": "Whether or not to attach the JavaScript debugger on external preview launches.",
"settings.serverRoot": "The server root to host the server from. Files will be previewed as if the workspace root is at this offset. If this directory path doesn't exist your workspace, it will default to the workspace root. This will not apply to the root of your drive if you are not in a workspace.",
"settings.hostIP": "The local IP host address to host your files on.",
"settings.customExternalBrowser": "The browser you want to launch when previewing a file in an external browser. Only works for normal preview (non-debug). Select `None` to use your default browser.",
"tasks.workspacePathDesc": "The path for the workspace that you want to start the server in."
Expand Down
35 changes: 26 additions & 9 deletions src/connectionInfo/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface ConnectionInfo {
httpURI: vscode.Uri;
wsURI: vscode.Uri;
workspace: vscode.WorkspaceFolder | undefined;
rootPrefix: string | undefined;
httpPort: number;
}

Expand All @@ -46,6 +47,7 @@ export class Connection extends Disposable {

constructor(
private readonly _workspace: vscode.WorkspaceFolder | undefined,
private readonly _rootPrefix: string,
public httpPort: number,
private _wsPort: number,
public host: string
Expand Down Expand Up @@ -82,6 +84,7 @@ export class Connection extends Disposable {
wsURI: externalWSUri,
workspace: this._workspace,
httpPort: httpPort,
rootPrefix: this._rootPrefix
});
}

Expand Down Expand Up @@ -115,8 +118,15 @@ export class Connection extends Disposable {
return this._workspace;
}

public get workspacePath(): string | undefined {
return this._workspace?.uri.fsPath;
public get rootURI(): vscode.Uri | undefined {
if (this.workspace) {
return vscode.Uri.joinPath(this.workspace.uri, this._rootPrefix);
}
return undefined;
}

public get rootPath(): string | undefined {
return this.rootURI?.fsPath;
}

/**
Expand Down Expand Up @@ -146,10 +156,10 @@ export class Connection extends Disposable {
* @returns {boolean} whether the path exists relative the default workspace
*/
public async pathExistsRelativeToWorkspace(file: string): Promise<boolean> {
if (!this.workspacePath) {
if (!this.rootPath) {
return false;
}
const fullPath = path.join(this.workspacePath, file);
const fullPath = path.join(this.rootPath, file);
return (await PathUtil.FileExistsStat(fullPath)).exists;
}

Expand All @@ -160,10 +170,10 @@ export class Connection extends Disposable {
* @returns {string} the equivalent relative path.
*/
public getFileRelativeToWorkspace(path: string): string | undefined {
const workspaceFolder = this.workspacePath;
const workspaceRoot = this.rootPath;

if (workspaceFolder && this._absPathInWorkspace(path)) {
return PathUtil.ConvertToPosixPath(path.substr(workspaceFolder.length));
if (workspaceRoot && this._absPathInWorkspace(path)) {
return PathUtil.ConvertToPosixPath(path.substring(workspaceRoot.length));
} else {
return undefined;
}
Expand All @@ -177,8 +187,15 @@ export class Connection extends Disposable {
* @returns whether the path is in the workspace
*/
private _absPathInWorkspace(path: string): boolean {
return this.workspacePath
? PathUtil.PathBeginsWith(path, this.workspacePath)
return this.rootPath
? PathUtil.PathBeginsWith(path, this.rootPath)
: false;
}

/**
* Get the URI given the relative path
*/
public getAppendedURI(path: string): vscode.Uri {
return this.rootURI ? vscode.Uri.joinPath(this.rootURI, path) : vscode.Uri.file(path);
}
}
10 changes: 7 additions & 3 deletions src/connectionInfo/connectionManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import { DEFAULT_HOST } from '../utils/constants';
import { Disposable } from '../utils/dispose';
import { PathUtil } from '../utils/pathUtil';
import { SETTINGS_SECTION_ID, SettingUtil } from '../utils/settingsUtil';
import { Connection, ConnectionInfo } from './connection';

Expand Down Expand Up @@ -110,12 +111,15 @@ export class ConnectionManager extends Disposable {
* @param workspaceFolder
* @returns connection
*/
public createAndAddNewConnection(
workspaceFolder: vscode.WorkspaceFolder | undefined
): Connection {
public async createAndAddNewConnection(
workspaceFolder: vscode.WorkspaceFolder | undefined,
): Promise<Connection> {
const serverRootPrefix = workspaceFolder ? await PathUtil.GetValidServerRootForWorkspace(workspaceFolder) : '';

const connection = this._register(
new Connection(
workspaceFolder,
serverRootPrefix,
this._initHttpPort,
this._initWSPort,
this._initHost
Expand Down
1 change: 0 additions & 1 deletion src/editorPreview/browserPreview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,6 @@ export class BrowserPreview extends Disposable {
});
}
} else {
const uri = vscode.Uri.parse(givenURL);
vscode.window
.showInformationMessage(
localize(
Expand Down
8 changes: 4 additions & 4 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ export function activate(context: vscode.ExtensionContext): void {

context.subscriptions.push(
vscode.commands.registerCommand(`${SETTINGS_SECTION_ID}.start`, async () => {
const filePath = SettingUtil.GetConfig().defaultPreviewPath;
await serverPreview.openPreviewAtFileString(filePath);
const defaultPreviewPath = SettingUtil.GetConfig().defaultPreviewPath;
await serverPreview.openPreviewAtFileString(defaultPreviewPath);
})
);

Expand Down Expand Up @@ -180,12 +180,12 @@ export function activate(context: vscode.ExtensionContext): void {
context.subscriptions.push(
vscode.commands.registerCommand(
`${SETTINGS_SECTION_ID}.setDefaultOpenFile`,
(file: vscode.Uri) => {
async (file: vscode.Uri) => {
// Will set the path on workspace settings if workspace is open
// otherwise, it will set user setting.

const numWorkspaceFolders = vscode.workspace.workspaceFolders?.length ?? 0;
const relativePath = PathUtil.getPathRelativeToWorkspace(file);
const relativePath = await PathUtil.getPathRelativeToWorkspace(file);

if (relativePath) {
const setPath = (numWorkspaceFolders === 1) ? relativePath : file.fsPath;
Expand Down
2 changes: 1 addition & 1 deletion src/infoManagers/endpointManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class EndpointManager extends Disposable {
while (i < workspaceDocuments.length) {
if (
!workspaceDocuments[i].isUntitled &&
!PathUtil.AbsPathInAnyWorkspace(workspaceDocuments[i].fileName)
!PathUtil.GetWorkspaceFromAbsolutePath(workspaceDocuments[i].fileName)
) {
this.encodeLooseFileEndpoint(workspaceDocuments[i].fileName);
}
Expand Down
73 changes: 48 additions & 25 deletions src/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { LIVE_PREVIEW_SERVER_ON } from './utils/constants';
import { ServerGrouping } from './server/serverGrouping';
import { UpdateListener } from './updateListener';
import { URL } from 'url';
import path = require('path');

const localize = nls.loadMessageBundle();

Expand Down Expand Up @@ -152,7 +153,7 @@ export class Manager extends Disposable {

this._register(
this._serverTaskProvider.onRequestToOpenServer(async (workspace) => {
const serverGrouping = this._getServerGroupingFromWorkspace(workspace);
const serverGrouping = await this._getServerGroupingFromWorkspace(workspace);
// running this with `fromTask = true` will still inform the task if the server is already open
await serverGrouping.openServer(true);
})
Expand All @@ -179,12 +180,12 @@ export class Manager extends Disposable {
let relative = false;
let file: string = e.state.currentAddress ?? '/';

let workspace = await PathUtil.PathExistsRelativeToAnyWorkspace(file);
let workspace = await PathUtil.GetWorkspaceFromRelativePath(file);
if (workspace) {
relative = true;
} else {
// path isn't relative to workspaces, try checking absolute path for workspace
workspace = PathUtil.AbsPathInAnyWorkspace(file);
workspace = await PathUtil.GetWorkspaceFromAbsolutePath(file);
}

if (!workspace) {
Expand All @@ -201,9 +202,10 @@ export class Manager extends Disposable {

let fileUri;
// loose file workspace will be fetched if workspace is still undefined
const grouping = this._getServerGroupingFromWorkspace(workspace);
const grouping = await this._getServerGroupingFromWorkspace(workspace);
if (workspace) {
fileUri = vscode.Uri.joinPath(workspace.uri, file);
// PathExistsRelativeToAnyWorkspace already makes sure that file is under correct root prefix
fileUri = vscode.Uri.joinPath(workspace.uri, await PathUtil.GetValidServerRootForWorkspace(workspace), file);
} else {
fileUri = vscode.Uri.parse(file);
}
Expand Down Expand Up @@ -287,7 +289,7 @@ export class Manager extends Disposable {
}
if (!serverGrouping) {
if (workspace) {
serverGrouping = this._getServerGroupingFromWorkspace(workspace);
serverGrouping = await this._getServerGroupingFromWorkspace(await this._shouldUseWorkspaceForFile(workspace, file) ? workspace : undefined);
} else if (port) {
this._serverGroupings.forEach((potentialServerGrouping) => {
if (potentialServerGrouping.port === port) {
Expand All @@ -296,14 +298,14 @@ export class Manager extends Disposable {
}
});
} else {
workspace = vscode.workspace.getWorkspaceFolder(file);
serverGrouping = this._getServerGroupingFromWorkspace(workspace);
workspace = await PathUtil.GetWorkspaceFromURI(file);
serverGrouping = await this._getServerGroupingFromWorkspace(workspace);
}
}

if (!serverGrouping) {
// last-resort: use loose workspace server.
serverGrouping = this._getServerGroupingFromWorkspace(undefined);
serverGrouping = await this._getServerGroupingFromWorkspace(undefined);
}

return this._openPreview(internal, serverGrouping, file, debug);
Expand Down Expand Up @@ -370,12 +372,11 @@ export class Manager extends Disposable {
*/
public async openPreviewAtFileString(filePath: string): Promise<void> {
if (filePath === '') {
this._openPreviewWithNoTarget();
return;
return this._openPreviewWithNoTarget();
}
const workspace = await PathUtil.PathExistsRelativeToAnyWorkspace(filePath);
const workspace = await PathUtil.GetWorkspaceFromRelativePath(filePath);
if (workspace) {
const file = vscode.Uri.joinPath(workspace.uri, filePath);
const file = vscode.Uri.joinPath(workspace.uri, await PathUtil.GetValidServerRootForWorkspace(workspace), filePath);
this.openPreviewAtFileUri(file, {
workspace: workspace,
});
Expand Down Expand Up @@ -405,7 +406,7 @@ export class Manager extends Disposable {

let workspace;
if (file) {
workspace = vscode.workspace.getWorkspaceFolder(file);
workspace = await PathUtil.GetWorkspaceFromURI(file);
} else if (
vscode.workspace.workspaceFolders &&
vscode.workspace.workspaceFolders?.length > 0
Expand Down Expand Up @@ -453,10 +454,11 @@ export class Manager extends Disposable {
throw Error;
}

const serverGrouping = this._getServerGroupingFromWorkspace(
const serverGrouping = await this._getServerGroupingFromWorkspace(
connection.workspace
);
if (!connection.workspace) {
if (!connection.rootURI) {
// using server grouping with undefined workspace
return this._openPreview(
internal,
serverGrouping,
Expand All @@ -465,7 +467,7 @@ export class Manager extends Disposable {
);
}

const file = vscode.Uri.joinPath(connection.workspace.uri, link.path);
const file = connection.getAppendedURI(link.path);
this._openPreview(internal, serverGrouping, file, debug);
} catch (e) {
vscode.window.showErrorMessage(
Expand All @@ -482,9 +484,9 @@ export class Manager extends Disposable {
let fileUri: vscode.Uri;
if (!file) {
if (this._previewManager.currentPanel?.panel.active) {
if (this._previewManager.currentPanel.currentConnection.workspace) {
if (this._previewManager.currentPanel.currentConnection.rootURI) {
fileUri = vscode.Uri.joinPath(
this._previewManager.currentPanel.currentConnection.workspace.uri,
this._previewManager.currentPanel.currentConnection.rootURI,
this._previewManager.currentPanel.currentAddress
);
} else {
Expand Down Expand Up @@ -526,19 +528,40 @@ export class Manager extends Disposable {
});
}

private async _shouldUseWorkspaceForFile(workspace: vscode.WorkspaceFolder | undefined, file: vscode.Uri): Promise<boolean> {

if (!workspace) {
// never use the root prefix path on non-workspace paths
return false;
}

const serverRootPrefix = await PathUtil.GetValidServerRootForWorkspace(workspace);
if (serverRootPrefix !== '' && file) {
const workspaceURIWithServerRoot = vscode.Uri.joinPath(workspace.uri, serverRootPrefix);

if (workspaceURIWithServerRoot) {
if (file.fsPath.startsWith(workspaceURIWithServerRoot.fsPath)) {
return true;
}
}
}

return false;
}

/**
* Creates a serverGrouping and connection object for a workspace if it doesn't already have an existing one.
* Otherwise, return the existing serverGrouping.
* @param workspace
* @returns serverGrouping for this workspace (or, when `workspace == undefined`, the serverGrouping for the loose file workspace)
*/
private _getServerGroupingFromWorkspace(
private async _getServerGroupingFromWorkspace(
workspace: vscode.WorkspaceFolder | undefined
): ServerGrouping {
): Promise<ServerGrouping> {
let serverGrouping = this._serverGroupings.get(workspace?.uri.toString());
if (!serverGrouping) {
const connection =
this._connectionManager.createAndAddNewConnection(workspace);
await this._connectionManager.createAndAddNewConnection(workspace);

this._register(
connection.onConnected(() => {
Expand Down Expand Up @@ -630,7 +653,7 @@ export class Manager extends Disposable {
}
return previewType === PreviewType.internalPreview;
}
private _openPreviewWithNoTarget(): void {
private async _openPreviewWithNoTarget(): Promise<void> {
// opens index at first open server or opens a loose workspace at root

const internal = this._isInternalPreview();
Expand All @@ -648,10 +671,10 @@ export class Manager extends Disposable {
}
}

const grouping = this._getServerGroupingFromWorkspace(workspaces[0]);
const grouping = await this._getServerGroupingFromWorkspace(workspaces[0]);
this._openPreview(internal, grouping, undefined);
} else {
const grouping = this._getServerGroupingFromWorkspace(undefined);
const grouping = await this._getServerGroupingFromWorkspace(undefined);
this._openPreview(internal, grouping, undefined);
}
}
Expand Down
Loading

0 comments on commit efbca27

Please sign in to comment.