Skip to content

Reduce REST traffic for server-side folders #1532

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions src/commands/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -680,11 +680,6 @@ export async function modifyProject(
if (!args) return;
const { node, api, project } = args;

// Technically a project is a "document", so tell the server that we're opening it
await new StudioActions().fireProjectUserAction(api, project, OtherStudioAction.OpenedDocument).catch(() => {
// Swallow error because showing it is more disruptive than using a potentially outdated project definition
});

let items: ProjectItem[] = await api
.actionQuery("SELECT Name, Type FROM %Studio.Project_ProjectItemsList(?,?) WHERE Type != 'GBL'", [project, "1"])
.then((data) => data.result.content);
Expand Down Expand Up @@ -1149,11 +1144,6 @@ export async function modifyProjectMetadata(nodeOrUri: NodeBase | vscode.Uri | u
if (!args) return;
const { api, project } = args;

// Technically a project is a "document", so tell the server that we're opening it
await new StudioActions().fireProjectUserAction(api, project, OtherStudioAction.OpenedDocument).catch(() => {
// Swallow error because showing it is more disruptive than using a potentially outdated project definition
});

try {
const oldDesc: string = await api
.actionQuery("SELECT Description FROM %Studio.Project WHERE Name = ?", [project])
Expand Down
26 changes: 26 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import {
OtherStudioAction,
contextSourceControlMenu,
mainSourceControlMenu,
StudioActions,
} from "./commands/studio";
import { addServerNamespaceToWorkspace, pickServerAndNamespace } from "./commands/addServerNamespaceToWorkspace";
import { jumpToTagAndOffset, openErrorLocation } from "./commands/jumpToTagAndOffset";
Expand Down Expand Up @@ -153,6 +154,7 @@ import {
} from "./utils/documentIndex";
import { WorkspaceNode, NodeBase } from "./explorer/nodes";
import { showPlanWebview } from "./commands/showPlanPanel";
import { isfsConfig } from "./utils/FileProviderUtil";

const packageJson = vscode.extensions.getExtension(extensionId).packageJSON;
const extensionVersion = packageJson.version;
Expand Down Expand Up @@ -708,6 +710,25 @@ async function systemModeWarning(wsFolders: readonly vscode.WorkspaceFolder[]):
}
}

/**
* Fire the `OpenedDocument` UserAction for any workspace folders
* that are showing the contents of a server-side project.
* This must be done because technically a project is a "document".
*/
async function fireOpenProjectUserAction(wsFolders: readonly vscode.WorkspaceFolder[]): Promise<void> {
if (!wsFolders || wsFolders.length == 0) return;
for (const wsFolder of wsFolders) {
if (notIsfs(wsFolder.uri)) return;
const { project } = isfsConfig(wsFolder.uri);
if (!project) return;
const api = new AtelierAPI(wsFolder.uri);
if (!api.active) return;
new StudioActions().fireProjectUserAction(api, project, OtherStudioAction.OpenedDocument).catch(() => {
// Swallow error because showing it is more disruptive than using a potentially outdated project definition
});
}
}

/**
* Set when clause context keys so the ObjectScript Explorer and
* Projects Explorer views are correctly shown or hidden depending
Expand Down Expand Up @@ -941,6 +962,9 @@ export async function activate(context: vscode.ExtensionContext): Promise<any> {
// Warn about SystemMode
systemModeWarning(vscode.workspace.workspaceFolders);

// Fire OpenedDocument UserAction for folders showing the contents of a server-side project
fireOpenProjectUserAction(vscode.workspace.workspaceFolders);

iscIcon = vscode.Uri.joinPath(context.extensionUri, "images", "fileIcon.svg");

// Index documents in all local workspace folders
Expand Down Expand Up @@ -1531,6 +1555,8 @@ export async function activate(context: vscode.ExtensionContext): Promise<any> {
for (const r of e.removed) removeIndexOfWorkspaceFolder(r);
// Show or hide explorer views as needed
setExplorerContextKeys();
// Fire OpenedDocument UserAction for added folders showing the contents of a server-side project
fireOpenProjectUserAction(e.added);
}),
vscode.commands.registerCommand("vscode-objectscript.importXMLFiles", importXMLFiles),
vscode.commands.registerCommand("vscode-objectscript.exportToXMLFile", exportDocumentsToXMLFile),
Expand Down
37 changes: 22 additions & 15 deletions src/providers/FileSystemProvider/FileSystemProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as path from "path";
import * as vscode from "vscode";
import { isText } from "istextorbinary";
import { AtelierAPI } from "../../api";
import { fireOtherStudioAction, OtherStudioAction, StudioActions } from "../../commands/studio";
import { fireOtherStudioAction, OtherStudioAction } from "../../commands/studio";
import { isfsConfig, projectContentsFromUri, studioOpenDialogFromURI } from "../../utils/FileProviderUtil";
import {
classNameRegex,
Expand Down Expand Up @@ -234,6 +234,9 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
}

public async stat(uri: vscode.Uri): Promise<vscode.FileStat> {
if (uri.path.includes(".vscode/") && !uri.path.endsWith("/settings.json")) {
throw vscode.FileSystemError.NoPermissions("Only settings.json is allowed within the /.vscode directory");
}
let entryPromise: Promise<Entry>;
let result: Entry;
const redirectedUri = redirectDotvscodeRoot(uri);
Expand Down Expand Up @@ -284,19 +287,14 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
}

public async readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> {
uri = redirectDotvscodeRoot(uri);
if (uri.path.includes(".vscode/")) {
throw vscode.FileSystemError.NoPermissions("Cannot read the /.vscode directory");
}
const parent = await this._lookupAsDirectory(uri);
const api = new AtelierAPI(uri);
if (!api.active) throw vscode.FileSystemError.Unavailable(uri);
const { csp, project } = isfsConfig(uri);
if (project) {
if (["", "/"].includes(uri.path)) {
// Technically a project is a "document", so tell the server that we're opening it
await new StudioActions().fireProjectUserAction(api, project, OtherStudioAction.OpenedDocument).catch(() => {
// Swallow error because showing it is more disruptive than using a potentially outdated project definition
});
}

// Get all items in the project
return projectContentsFromUri(uri).then((entries) =>
entries.map((entry) => {
Expand Down Expand Up @@ -405,7 +403,9 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
}

public createDirectory(uri: vscode.Uri): void | Thenable<void> {
uri = redirectDotvscodeRoot(uri);
if (uri.path.includes(".vscode/")) {
throw vscode.FileSystemError.NoPermissions("Cannot create a subdirectory of the /.vscode directory");
}
const basename = path.posix.basename(uri.path);
const dirname = uri.with({ path: path.posix.dirname(uri.path) });
return this._lookupAsDirectory(dirname).then((parent) => {
Expand All @@ -421,6 +421,9 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
}

public async readFile(uri: vscode.Uri): Promise<Uint8Array> {
if (uri.path.includes(".vscode/") && !uri.path.endsWith("/settings.json")) {
throw vscode.FileSystemError.NoPermissions("Only settings.json is allowed within the /.vscode directory");
}
// Use _lookup() instead of _lookupAsFile() so we send
// our cached mtime with the GET /doc request if we have it
return this._lookup(uri, true).then((file: File) => {
Expand All @@ -439,6 +442,9 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
overwrite: boolean;
}
): void | Thenable<void> {
if (uri.path.includes(".vscode/") && !uri.path.endsWith("/settings.json")) {
throw vscode.FileSystemError.NoPermissions("Only settings.json is allowed within the /.vscode directory");
}
uri = redirectDotvscodeRoot(uri);
if (uri.path.startsWith("/.")) {
throw vscode.FileSystemError.NoPermissions("dot-folders not supported by server");
Expand Down Expand Up @@ -606,6 +612,9 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
}

public async delete(uri: vscode.Uri, options: { recursive: boolean }): Promise<void> {
if (uri.path.includes(".vscode/") && !uri.path.endsWith("/settings.json")) {
throw vscode.FileSystemError.NoPermissions("Only settings.json is allowed within the /.vscode directory");
}
uri = redirectDotvscodeRoot(uri);
const { project } = isfsConfig(uri);
const csp = isCSP(uri);
Expand Down Expand Up @@ -699,6 +708,9 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
if (vscode.workspace.getWorkspaceFolder(oldUri) != vscode.workspace.getWorkspaceFolder(newUri)) {
throw vscode.FileSystemError.NoPermissions("Cannot rename a file across workspace folders");
}
if (oldUri.path.includes(".vscode/") || newUri.path.includes(".vscode/")) {
throw vscode.FileSystemError.NoPermissions("Cannot rename a file in the /.vscode directory");
}
// Check if the destination exists
let newFileStat: vscode.FileStat;
try {
Expand Down Expand Up @@ -864,11 +876,6 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
// Fetch entry (a file or directory) from cache, else from server
private async _lookup(uri: vscode.Uri, fillInPath?: boolean): Promise<Entry> {
const api = new AtelierAPI(uri);
if (uri.path === "/") {
await api.serverInfo().catch((error) => {
throw vscode.FileSystemError.Unavailable(stringifyError(error) || uri);
});
}
const config = api.config;
const rootName = `${config.username}@${config.host}:${config.port}${config.pathPrefix}/${config.ns.toUpperCase()}`;
let entry: Entry = this.superRoot.entries.get(rootName);
Expand Down