Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 0 additions & 4 deletions src/commands/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,13 +181,9 @@ What do you want to do?`,
// Overwrite
return importFile(file, true, true);
case "Pull Server Changes":
outputChannel.appendLine(`${file.name}: Loading changes from server`);
outputChannel.show(true);
loadChanges([file]);
return Promise.reject();
case "Cancel":
outputChannel.appendLine(`${file.name}: Import and Compile canceled by user`);
outputChannel.show(true);
return Promise.reject();
}
return Promise.reject();
Expand Down
6 changes: 3 additions & 3 deletions src/commands/connectFolderToServerNamespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
serverManagerApi,
resolveUsernameAndPassword,
} from "../extension";
import { handleError, isUnauthenticated, notIsfs } from "../utils";
import { handleError, isUnauthenticated, notIsfs, displayableUri } from "../utils";

interface ConnSettings {
server: string;
Expand Down Expand Up @@ -131,8 +131,8 @@ export async function connectFolderToServerNamespace(): Promise<void> {
// the server may be configured at the workspace folder level.
const answer = await vscode.window.showQuickPick(
[
{ label: `Workspace Folder ${folder.name}`, detail: folder.uri.toString(true) },
{ label: "Workspace File", detail: vscode.workspace.workspaceFile.toString(true) },
{ label: `Workspace Folder ${folder.name}`, detail: displayableUri(folder.uri) },
{ label: "Workspace File", detail: displayableUri(vscode.workspace.workspaceFile) },
],
{ title: "Store the server connection at the workspace or folder level?" }
);
Expand Down
5 changes: 3 additions & 2 deletions src/commands/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
lastUsedLocalUri,
notNull,
outputChannel,
displayableUri,
RateLimiter,
replaceFile,
stringifyError,
Expand Down Expand Up @@ -96,7 +97,7 @@ async function exportFile(wsFolderUri: vscode.Uri, namespace: string, name: stri
let fileUri = vscode.Uri.file(fileName);
if (wsFolderUri.scheme != "file") fileUri = wsFolderUri.with({ path: fileUri.path });
const log = (status: string) =>
outputChannel.appendLine(`Export '${name}' to '${fileUri.toString(true)}' - ${status}`);
outputChannel.appendLine(`Export '${name}' to '${displayableUri(fileUri)}' - ${status}`);

try {
const data = await api.getDoc(name, wsFolderUri);
Expand Down Expand Up @@ -380,7 +381,7 @@ export async function exportDocumentsToXMLFile(): Promise<void> {
const xmlContent = await api.actionXMLExport(documents).then((data) => data.result.content);
// Save the file
await replaceFile(uri, xmlContent);
outputChannel.appendLine(`Exported to ${uri.scheme == "file" ? uri.fsPath : uri.toString(true)}`);
outputChannel.appendLine(`Exported to ${displayableUri(uri)}`);
}
} catch (error) {
handleError(error, "Error executing 'Export Documents to XML File...' command.");
Expand Down
4 changes: 2 additions & 2 deletions src/commands/newFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import path = require("path");
import { AtelierAPI } from "../api";
import { FILESYSTEM_SCHEMA } from "../extension";
import { DocumentContentProvider } from "../providers/DocumentContentProvider";
import { replaceFile, getWsFolder, handleError } from "../utils";
import { replaceFile, getWsFolder, handleError, displayableUri } from "../utils";
import { getFileName } from "./export";
import { getUrisForDocument } from "../utils/documentIndex";

Expand Down Expand Up @@ -949,7 +949,7 @@ Parameter ENSPURGE As BOOLEAN = 1;
const inputBox = vscode.window.createInputBox();
inputBox.ignoreFocusOut = true;
inputBox.buttons = [{ iconPath: new vscode.ThemeIcon("save-as"), tooltip: "Show 'Save As' dialog" }];
inputBox.prompt = `The path is relative to the workspace folder root (${wsFolder.uri.toString(true)}). Intermediate folders that do not exist will be created. Click the 'Save As' icon to open the standard save dialog instead.`;
inputBox.prompt = `The path is relative to the workspace folder root (${displayableUri(wsFolder.uri)}). Intermediate folders that do not exist will be created. Click the 'Save As' icon to open the standard save dialog instead.`;
inputBox.title = "Enter a file path for the new class";
inputBox.value = localUri.path.slice(wsFolder.uri.path.length);
inputBox.valueSelection = [inputBox.value.length, inputBox.value.length];
Expand Down
3 changes: 2 additions & 1 deletion src/commands/unitTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
handleError,
methodOffsetToLine,
notIsfs,
displayableUri,
stripClassMemberNameQuotes,
uriIsParentOf,
} from "../utils";
Expand Down Expand Up @@ -396,7 +397,7 @@ async function runHandler(
roots.map((i) => {
return {
label: i.label,
detail: i.uri.toString(true),
detail: displayableUri(i.uri),
item: i,
};
}),
Expand Down
20 changes: 8 additions & 12 deletions src/commands/xmlToUdl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import * as vscode from "vscode";
import path = require("path");
import { config, OBJECTSCRIPTXML_FILE_SCHEMA, xmlContentProvider } from "../extension";
import { AtelierAPI } from "../api";
import { replaceFile, fileExists, getWsFolder, handleError, notIsfs, outputChannel } from "../utils";
import { replaceFile, fileExists, getWsFolder, handleError, notIsfs, outputChannel, displayableUri } from "../utils";
import { getFileName } from "./export";

const exportHeader = /^\s*<Export generator="(Cache|IRIS)" version="\d+"/;

export async function previewXMLAsUDL(textEditor: vscode.TextEditor, auto = false): Promise<void> {
const uri = textEditor.document.uri;
const content = textEditor.document.getText();
const uriString = displayableUri(uri);
if (notIsfs(uri) && uri.path.toLowerCase().endsWith("xml") && textEditor.document.lineCount > 2) {
if (exportHeader.test(textEditor.document.lineAt(1).text)) {
const api = new AtelierAPI(uri);
Expand All @@ -23,10 +24,7 @@ export async function previewXMLAsUDL(textEditor: vscode.TextEditor, auto = fals
.cvtXmlUdl(content)
.then((data) => data.result.content);
if (udlDocs.length == 0) {
vscode.window.showErrorMessage(
`File '${uri.toString(true)}' contains no documents that can be previewed.`,
"Dismiss"
);
vscode.window.showErrorMessage(`File '${uriString}' contains no documents that can be previewed.`, "Dismiss");
return;
}
// Prompt the user for documents to preview
Expand Down Expand Up @@ -77,7 +75,7 @@ export async function previewXMLAsUDL(textEditor: vscode.TextEditor, auto = fals
handleError(error, "Error executing 'Preview XML as UDL' command.");
}
} else if (!auto) {
vscode.window.showErrorMessage(`XML file '${uri.toString(true)}' is not an InterSystems export.`, "Dismiss");
vscode.window.showErrorMessage(`XML file '${uriString}' is not an InterSystems export.`, "Dismiss");
}
}
}
Expand Down Expand Up @@ -137,19 +135,17 @@ export async function extractXMLFileContents(xmlUri?: vscode.Uri): Promise<void>
}
// Read the XML file
const xmlContent = new TextDecoder().decode(await vscode.workspace.fs.readFile(xmlUri)).split(/\r?\n/);
const xmlUriString = displayableUri(xmlUri);
if (xmlContent.length < 3 || !exportHeader.test(xmlContent[1])) {
vscode.window.showErrorMessage(`XML file '${xmlUri.toString(true)}' is not an InterSystems export.`, "Dismiss");
vscode.window.showErrorMessage(`XML file '${xmlUriString}' is not an InterSystems export.`, "Dismiss");
return;
}
// Convert the file
const udlDocs: { name: string; content: string[] }[] = await api
.cvtXmlUdl(xmlContent.join("\n"))
.then((data) => data.result.content);
if (udlDocs.length == 0) {
vscode.window.showErrorMessage(
`File '${xmlUri.toString(true)}' contains no documents that can be extracted.`,
"Dismiss"
);
vscode.window.showErrorMessage(`File '${xmlUriString}' contains no documents that can be extracted.`, "Dismiss");
return;
}
// Prompt the user for documents to extract
Expand Down Expand Up @@ -177,7 +173,7 @@ export async function extractXMLFileContents(xmlUri?: vscode.Uri): Promise<void>
if (!docWhitelist.includes(udlDoc.name)) continue; // This file wasn't selected
const fileUri = wsFolder.uri.with({ path: getFileName(rootFolder, udlDoc.name, atelier, addCategory, map, "/") });
if (await fileExists(fileUri)) {
outputChannel.appendLine(`File '${fileUri.toString(true)}' already exists.`);
outputChannel.appendLine(`File '${displayableUri(fileUri)}' already exists.`);
errs++;
continue;
}
Expand Down
3 changes: 2 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ import {
addWsServerRootFolderData,
getWsFolder,
exportedUris,
displayableUri,
} from "./utils";
import { ObjectScriptDiagnosticProvider } from "./providers/ObjectScriptDiagnosticProvider";
import { DocumentLinkProvider } from "./providers/DocumentLinkProvider";
Expand Down Expand Up @@ -1163,7 +1164,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<any> {
} catch (error) {
handleError(
error,
`Failed to overwrite contents of file '${file.uri.toString(true)}' with server copy of '${file.fileName}'.`
`Failed to overwrite contents of file '${displayableUri(file.uri)}' with server copy of '${file.fileName}'.`
);
}
}),
Expand Down
59 changes: 44 additions & 15 deletions src/utils/documentIndex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
notIsfs,
openLowCodeEditors,
outputChannel,
displayableUri,
} from ".";
import { isText } from "istextorbinary";
import { AtelierAPI } from "../api";
Expand Down Expand Up @@ -67,11 +68,11 @@ async function getCurrentFile(
// or a TypeError from decode(). Don't log TypeError
// since the file may be a non-text file that has
// an extension that we interpret as text (like cls or mac).
// Also don't log "FileNotFound" errors, which are probably
// caused by concurrency issues. We should ignore such files
// rather than alerting the user.
if (error instanceof vscode.FileSystemError && error.code != "FileNotFound") {
outputChannel.appendLine(`Failed to read contents of '${uri.toString(true)}': ${error.toString()}`);
// Don't log "FileNotFound" errors, which are probably
// caused by concurrency issues, or "FileIsADirectory"
// issues, since we don't care about directories.
if (error instanceof vscode.FileSystemError && !["FileNotFound", "FileIsADirectory"].includes(error.code)) {
outputChannel.appendLine(`Failed to read contents of '${displayableUri(uri)}': ${error.toString()}`);
}
}
}
Expand Down Expand Up @@ -114,17 +115,9 @@ function generateDeleteFn(wsFolderUri: vscode.Uri): (doc: string) => void {
docs.length = 0;
api.deleteDocs(docsCopy).then((data) => {
let failed = 0;
const ts = tsString();
for (const doc of data.result) {
if (doc.status != "" && !doc.status.includes("#16005:")) {
// The document was not deleted, so log the error.
// Error 16005 means we tried to delete a document
// that didn't exist. Since the purpose of this
// call was to delete the document, and at the
// end the document isn't there, we should ignore
// this error so the user doesn't get confused.
failed++;
outputChannel.appendLine(`${failed == 1 ? "\n" : ""}${doc.status}`);
}
failed += outputDelete(doc.name, doc.status, ts);
}
if (failed > 0) {
outputChannel.show(true);
Expand All @@ -151,6 +144,37 @@ export function storeTouchedByVSCode(uri: vscode.Uri): void {
}
}

/** Create a timestamp string for use in a log entry */
function tsString(): string {
const date = new Date();
return `${date.toISOString().split("T").shift()} ${date.toLocaleTimeString(undefined, { hour12: false })}`;
}

/** Output a log entry */
function output(docName: string, msg: string, ts?: string): void {
outputChannel.appendLine(`${ts ?? tsString()} [${docName}] ${msg}`);
}

/** Output a log entry for a successful import */
function outputImport(docName: string, uri: vscode.Uri): void {
output(docName, `Imported from '${displayableUri(uri)}'`);
}

/**
* Output a log entry for a successful or failed delete.
* Does not output a log entry if the file did not exist on the server.
* Returns `1` if the deleton failed, else `0`.
*/
function outputDelete(docName: string, status: string, ts: string): number {
if (status == "") {
output(docName, "Deleted", ts);
} else if (!status.includes("#16005:")) {
output(docName, `Deletion failed: ${status}`, ts);
return 1;
}
return 0;
}

/** Create index of `wsFolder` and set up a `FileSystemWatcher` to keep the index up to date */
export async function indexWorkspaceFolder(wsFolder: vscode.WorkspaceFolder): Promise<void> {
if (!notIsfs(wsFolder.uri)) return;
Expand Down Expand Up @@ -186,6 +210,10 @@ export async function indexWorkspaceFolder(wsFolder: vscode.WorkspaceFolder): Pr
// safely ignore the event.
return;
}
if (!uri.path.split("/").pop().includes(".")) {
// Ignore creation and change events for folders
return;
}
const uriString = uri.toString();
if (!created) {
const stat = await vscode.workspace.fs.stat(uri).then(undefined, () => {});
Expand Down Expand Up @@ -237,6 +265,7 @@ export async function indexWorkspaceFolder(wsFolder: vscode.WorkspaceFolder): Pr
// Create or update the document on the server
importFile(change.addedOrChanged)
.then(() => {
outputImport(change.addedOrChanged.name, uri);
if (conf.get("compileOnSave")) {
// Compile right away if this document is in the active text editor.
// This is needed to avoid noticeable latency when a user is editing
Expand Down
9 changes: 7 additions & 2 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -966,7 +966,7 @@ export async function getWsFolder(
return vscode.window
.showQuickPick(
folders.map((f) => {
return { label: f.name, detail: f.uri.toString(true), f };
return { label: f.name, detail: displayableUri(f.uri), f };
}),
{
canPickMany: false,
Expand Down Expand Up @@ -1006,7 +1006,7 @@ export async function replaceFile(uri: vscode.Uri, content: string | string[] |
: new TextEncoder().encode(Array.isArray(content) ? content.join("\n") : content),
});
const success = await vscode.workspace.applyEdit(wsEdit);
if (!success) throw `Failed to create or replace contents of file '${uri.toString(true)}'`;
if (!success) throw `Failed to create or replace contents of file '${displayableUri(uri)}'`;
}

/** Show the compilation failure error message if required. */
Expand All @@ -1025,6 +1025,11 @@ export function compileErrorMsg(conf: vscode.WorkspaceConfiguration): void {
});
}

/** Return a string containing the displayable form of `uri` */
export function displayableUri(uri: vscode.Uri): string {
return uri.scheme == "file" ? uri.fsPath : uri.toString(true);
}

class Semaphore {
/** Queue of tasks waiting to acquire the semaphore */
private _tasks: (() => void)[] = [];
Expand Down