Skip to content

Commit 41d3eeb

Browse files
committed
Support opened files without workspaces
Fixes #132
1 parent 2a63121 commit 41d3eeb

File tree

4 files changed

+80
-64
lines changed

4 files changed

+80
-64
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# 4.0.4
2+
3+
### Added
4+
5+
- Support for opened files without active workspaces
6+
17
# 4.0.3
28

39
### Changed

src/extension/competitiveCompanion.ts

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import * as v from "valibot";
88
import { ProblemSchema, type Testcase } from "../shared/schemas";
99
import type JudgeViewProvider from "./providers/JudgeViewProvider";
1010
import { getLogger } from "./utils/logging";
11+
import { getFileWorkspace } from "./utils/vscode";
1112

1213
type Problem = v.InferOutput<typeof ProblemSchema>;
1314

@@ -49,13 +50,13 @@ let prevBatchId: string | undefined;
4950
*/
5051
async function promptForTargetFile(
5152
problem: Problem,
52-
workspaceRoot: string,
53-
files: vscode.Uri[],
53+
files: string[],
54+
workspace?: string,
5455
currentFileRelativePath?: string
5556
): Promise<string> {
5657
const options: vscode.QuickPickItem[] = files.map((file) => ({
57-
label: path.parse(file.fsPath).base,
58-
description: path.relative(workspaceRoot, file.fsPath),
58+
label: path.parse(file).base,
59+
description: path.relative(workspace ?? "", file),
5960
}));
6061

6162
const pick = vscode.window.createQuickPick();
@@ -126,7 +127,7 @@ async function promptForTargetFile(
126127
*/
127128
async function processProblem(problem: Problem, judge: JudgeViewProvider): Promise<void> {
128129
const activeFile = vscode.window.activeTextEditor?.document.fileName;
129-
const workspaceRoot = vscode.workspace.workspaceFolders?.at(0)?.uri.fsPath ?? "";
130+
const workspace = await getFileWorkspace(activeFile);
130131
const config = vscode.workspace.getConfiguration("fastolympiccoding");
131132
const openSelectedFiles = config.get<boolean>("openSelectedFiles")!;
132133
const askForWhichFile = config.get<boolean>("askForWhichFile")!;
@@ -138,29 +139,51 @@ async function processProblem(problem: Problem, judge: JudgeViewProvider): Promi
138139
if (prevBatchId === problem.batch.id) {
139140
currentFileRelativePath = prevSelection;
140141
} else {
141-
currentFileRelativePath = activeFile ? path.relative(workspaceRoot, activeFile) : undefined;
142+
currentFileRelativePath =
143+
workspace && activeFile ? path.relative(workspace, activeFile) : undefined;
142144
}
143145
let relativePath = isSingleProblem && currentFileRelativePath ? currentFileRelativePath : "";
144146

145147
if (needsPrompt) {
146148
// Gather files before prompting to include newly created files from previous problems
147149
const include = config.get<string>("includePattern")!;
148150
const exclude = config.get<string>("excludePattern")!;
149-
const updatedFiles = await vscode.workspace.findFiles(include, exclude);
150-
relativePath = await promptForTargetFile(
151-
problem,
152-
workspaceRoot,
153-
updatedFiles,
154-
currentFileRelativePath
155-
);
151+
152+
let files: string[];
153+
if (vscode.workspace.workspaceFolders) {
154+
files = await vscode.workspace
155+
.findFiles(include, exclude)
156+
.then((uris) => uris.map((uri) => uri.fsPath));
157+
} else if (activeFile) {
158+
files = [activeFile];
159+
} else {
160+
const choice = await vscode.window.showWarningMessage(
161+
`No active workspace nor file opened to infer files to put testcases for "${problem.name}".`,
162+
"Select A File",
163+
"Dismiss"
164+
);
165+
if (choice === "Dismiss") {
166+
return;
167+
}
168+
169+
const result = await vscode.window.showOpenDialog({
170+
canSelectFiles: true,
171+
canSelectFolders: false,
172+
canSelectMany: false,
173+
title: `Select a file to put testcases for "${problem.name}"`,
174+
});
175+
files = result ? [result[0].fsPath] : [];
176+
}
177+
178+
relativePath = await promptForTargetFile(problem, files, workspace, currentFileRelativePath);
156179
}
157180

158181
if (relativePath === "") {
159182
vscode.window.showWarningMessage(`No file to write testcases for "${problem.name}"`);
160183
return;
161184
}
162185

163-
const absolutePath = path.join(workspaceRoot, relativePath);
186+
const absolutePath = path.resolve(path.join(workspace ?? "", relativePath));
164187
try {
165188
await fs.writeFile(absolutePath, "", { flag: "a" }); // Create file if it doesn't exist
166189
} catch (error) {

src/extension/runSettingsCommands.ts

Lines changed: 10 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as fs from "node:fs/promises";
22
import * as path from "node:path";
33
import * as vscode from "vscode";
44

5-
import { deepMerge } from "./utils/vscode";
5+
import { deepMerge, getFileWorkspace } from "./utils/vscode";
66

77
const gdbAttachDebugConfig = {
88
debugCommand: [
@@ -388,30 +388,14 @@ export function registerRunSettingsCommands(context: vscode.ExtensionContext): v
388388
context.subscriptions.push(
389389
vscode.commands.registerCommand(
390390
"fastolympiccoding.createRunSettings",
391-
async (options?: { extension?: string; workspaceFolder?: vscode.WorkspaceFolder }) => {
392-
const workspaceFolders = vscode.workspace.workspaceFolders;
393-
if (!workspaceFolders || workspaceFolders.length === 0) {
394-
void vscode.window.showErrorMessage("No workspace folder is open");
391+
async (options?: { extension?: string; workspaceFolder?: string }) => {
392+
const runSettingsFolder = options?.workspaceFolder ?? (await getFileWorkspace());
393+
if (!runSettingsFolder) {
395394
return;
396395
}
397396

398-
let workspaceFolder: vscode.WorkspaceFolder;
399-
if (options?.workspaceFolder) {
400-
workspaceFolder = options.workspaceFolder;
401-
} else if (workspaceFolders.length === 1) {
402-
workspaceFolder = workspaceFolders[0];
403-
} else {
404-
const picked = await vscode.window.showWorkspaceFolderPick({
405-
placeHolder: "Select workspace folder for run settings",
406-
});
407-
if (!picked) {
408-
return;
409-
}
410-
workspaceFolder = picked;
411-
}
412-
413-
const runSettingsPath = path.join(workspaceFolder.uri.fsPath, "runSettings.json");
414-
const dotVscodeDir = path.join(workspaceFolder.uri.fsPath, ".vscode");
397+
const runSettingsPath = path.join(runSettingsFolder, "runSettings.json");
398+
const dotVscodeDir = path.join(runSettingsFolder, ".vscode");
415399
const launchJsonPath = path.join(dotVscodeDir, "launch.json");
416400

417401
const fileExists = await fs
@@ -574,28 +558,14 @@ export function registerRunSettingsCommands(context: vscode.ExtensionContext): v
574558
);
575559

576560
context.subscriptions.push(
577-
vscode.commands.registerCommand("fastolympiccoding.openRunSettings", () => {
561+
vscode.commands.registerCommand("fastolympiccoding.openRunSettings", async () => {
578562
void (async () => {
579-
const workspaceFolders = vscode.workspace.workspaceFolders;
580-
if (!workspaceFolders || workspaceFolders.length === 0) {
581-
void vscode.window.showErrorMessage("No workspace folder is open");
563+
const runSettingsFolder = await getFileWorkspace();
564+
if (!runSettingsFolder) {
582565
return;
583566
}
584567

585-
let workspaceFolder: vscode.WorkspaceFolder;
586-
if (workspaceFolders.length === 1) {
587-
workspaceFolder = workspaceFolders[0];
588-
} else {
589-
const picked = await vscode.window.showWorkspaceFolderPick({
590-
placeHolder: "Select workspace folder",
591-
});
592-
if (!picked) {
593-
return;
594-
}
595-
workspaceFolder = picked;
596-
}
597-
598-
const runSettingsPath = path.join(workspaceFolder.uri.fsPath, "runSettings.json");
568+
const runSettingsPath = path.join(runSettingsFolder, "runSettings.json");
599569
try {
600570
await fs.access(runSettingsPath);
601571
const doc = await vscode.workspace.openTextDocument(runSettingsPath);

src/extension/utils/vscode.ts

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,29 @@ function loadRunSettingsFromDirectory(directory: string): Record<string, unknown
569569
}
570570
}
571571

572+
/**
573+
* Returns VSCode workspace folder if it exists otherwise returns the directory of the file
574+
*/
575+
export async function getFileWorkspace(file?: string): Promise<string | undefined> {
576+
let runSettingsFolder: string | undefined = undefined;
577+
if (vscode.workspace.workspaceFolders?.length === 1) {
578+
runSettingsFolder = vscode.workspace.workspaceFolders[0].uri.fsPath;
579+
} else if (file) {
580+
runSettingsFolder = path.dirname(file);
581+
} else if (vscode.window.activeTextEditor) {
582+
runSettingsFolder = path.dirname(vscode.window.activeTextEditor.document.fileName);
583+
} else {
584+
const picked = await vscode.window.showWorkspaceFolderPick({
585+
placeHolder: "Select workspace folder for run settings",
586+
});
587+
if (!picked) {
588+
return;
589+
}
590+
runSettingsFolder = picked.uri.fsPath;
591+
}
592+
return runSettingsFolder;
593+
}
594+
572595
/**
573596
* Traverses from the file directory up to the workspace root,
574597
* loading and merging runSettings.json files along the way.
@@ -585,24 +608,18 @@ export function getFileRunSettings(
585608
return null;
586609
}
587610

588-
const workspaceFolder = vscode.workspace.getWorkspaceFolder(vscode.Uri.file(file));
589-
if (!workspaceFolder) {
590-
logger.error(`No workspace folder found for file ${file}`);
591-
vscode.window.showErrorMessage(`No workspace folder found for file ${file}`);
592-
return null;
593-
}
594-
595-
const workspaceRoot = workspaceFolder.uri.fsPath;
596611
let currentDir = path.dirname(file);
612+
const workspaceFolder = vscode.workspace.getWorkspaceFolder(vscode.Uri.file(file));
613+
const rootDir = workspaceFolder?.uri.fsPath ?? currentDir;
597614

598615
// Iterate from file folder back down to workspace root
599616
const settingsStack: Record<string, unknown>[] = [];
600-
while (currentDir.startsWith(workspaceRoot)) {
617+
while (currentDir.startsWith(rootDir)) {
601618
const dirSettings = loadRunSettingsFromDirectory(currentDir);
602619
if (dirSettings) {
603620
settingsStack.push(dirSettings);
604621
}
605-
if (currentDir === workspaceRoot) {
622+
if (currentDir === rootDir) {
606623
break;
607624
}
608625
const parentDir = path.dirname(currentDir);

0 commit comments

Comments
 (0)