Skip to content

Commit 60eaab4

Browse files
committed
Simplify folding to not be extension dependent
Fixes #125
1 parent 75263e0 commit 60eaab4

File tree

4 files changed

+105
-8
lines changed

4 files changed

+105
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
### Changed
44

55
- Let the stress tester run indefinitely by default
6+
- Folding now doesn't depend on extensions and correctly folds the entire inserted content
67

78
### Fixed
89

README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -370,8 +370,6 @@ Interactive testcases have a special badge to make them distinguishable. If ther
370370
### 👜 Inserting Prewritten Code
371371

372372
- Add the root directory of the templates to the settings
373-
- **NOTE**: Remove trailing newlines for fold to work (folding is optional via settings)
374-
- Folding depends on VSCode support, which may require other extensions depending on the language.
375373

376374
| ![Insert File Template Gif](media/insert_file_template.gif) |
377375
| :---------------------------------------------------------: |

src/extension/index.ts

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
resolveVariables,
1212
} from "./utils/vscode";
1313
import { initLogging } from "./utils/logging";
14+
import { TemplateFoldingProvider, type TemplateRange } from "./utils/folding";
1415
import JudgeViewProvider from "./providers/JudgeViewProvider";
1516
import StressViewProvider from "./providers/StressViewProvider";
1617
import PanelViewProvider from "./providers/PanelViewProvider";
@@ -20,9 +21,13 @@ import { createStatusBarItem } from "./statusBar";
2021
let judgeViewProvider: JudgeViewProvider;
2122
let stressViewProvider: StressViewProvider;
2223
let panelViewProvider: PanelViewProvider;
24+
let templateFoldingProvider: TemplateFoldingProvider;
2325

2426
type Dependencies = Record<string, string[]>;
2527

28+
// Track inserted template ranges by document URI for folding
29+
const templateRangesByUri = new Map<string, TemplateRange>();
30+
2631
async function getTemplateContent(
2732
relativeFile: string,
2833
baseDirectory: string,
@@ -123,10 +128,27 @@ function registerDocumentContentProviders(context: vscode.ExtensionContext): voi
123128
context.subscriptions.push(
124129
vscode.workspace.onDidCloseTextDocument((document) => {
125130
ReadonlyStringProvider.cleanup(document.uri);
131+
// Also clean up template ranges for closed documents
132+
templateRangesByUri.delete(document.uri.toString());
126133
})
127134
);
128135
}
129136

137+
function registerFoldingProvider(context: vscode.ExtensionContext): void {
138+
// Create the folding provider with a callback to get template ranges
139+
templateFoldingProvider = new TemplateFoldingProvider((documentUri) => {
140+
return templateRangesByUri.get(documentUri);
141+
});
142+
143+
// Register for all file scheme documents
144+
context.subscriptions.push(
145+
vscode.languages.registerFoldingRangeProvider({ scheme: "file" }, templateFoldingProvider)
146+
);
147+
148+
// Dispose the provider when extension deactivates
149+
context.subscriptions.push(templateFoldingProvider);
150+
}
151+
130152
function registerCommands(context: vscode.ExtensionContext): void {
131153
registerRunSettingsCommands(context);
132154

@@ -254,14 +276,37 @@ function registerCommands(context: vscode.ExtensionContext): void {
254276
return;
255277
}
256278

257-
const inserted = vscode.window.activeTextEditor?.edit((edit: vscode.TextEditorEdit) => {
258-
if (vscode.window.activeTextEditor) {
259-
edit.insert(vscode.window.activeTextEditor.selection.active, content);
260-
}
279+
const editor = vscode.window.activeTextEditor;
280+
if (!editor) {
281+
return;
282+
}
283+
284+
const startLine = editor.selection.active.line;
285+
const templateLineCount = content.split("\n").length - 1;
286+
287+
const inserted = await editor.edit((edit: vscode.TextEditorEdit) => {
288+
edit.insert(editor.selection.active, content);
261289
});
290+
291+
if (!inserted) {
292+
return;
293+
}
294+
262295
const foldTemplate = config.get<boolean>("foldFileTemplate")!;
263-
if (inserted && foldTemplate) {
264-
vscode.commands.executeCommand("editor.fold");
296+
if (foldTemplate) {
297+
// Track the template range for folding
298+
const endLine = startLine + templateLineCount;
299+
const documentUri = editor.document.uri.toString();
300+
templateRangesByUri.set(documentUri, { startLine, endLine });
301+
302+
// Notify the folding provider that ranges have changed
303+
templateFoldingProvider.notifyFoldingRangesChanged();
304+
305+
// Wait for VS Code's internal folding debounce (~200ms), then fold
306+
setTimeout(async () => {
307+
await vscode.commands.executeCommand("editor.fold", { levels: 1, direction: "down" });
308+
templateRangesByUri.delete(documentUri);
309+
}, 200);
265310
}
266311
})();
267312
})
@@ -361,6 +406,7 @@ export function activate(context: vscode.ExtensionContext): void {
361406
registerViewProviders(context);
362407
registerCommands(context);
363408
registerDocumentContentProviders(context);
409+
registerFoldingProvider(context);
364410

365411
createStatusBarItem(context);
366412

src/extension/utils/folding.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import * as vscode from "vscode";
2+
import { getLogger } from "./logging";
3+
4+
export interface TemplateRange {
5+
startLine: number;
6+
endLine: number;
7+
}
8+
9+
type GetTemplateRangesCallback = (documentUri: string) => TemplateRange | undefined;
10+
11+
/**
12+
* Custom folding provider that folds inserted template regions.
13+
* Returns a single folding range for the entire template content.
14+
*/
15+
export class TemplateFoldingProvider implements vscode.FoldingRangeProvider {
16+
private readonly _onDidChangeFoldingRangesEmitter = new vscode.EventEmitter<void>();
17+
public readonly onDidChangeFoldingRanges = this._onDidChangeFoldingRangesEmitter.event;
18+
19+
constructor(private readonly getTemplateRanges: GetTemplateRangesCallback) {}
20+
21+
provideFoldingRanges(
22+
document: vscode.TextDocument
23+
): vscode.ProviderResult<vscode.FoldingRange[]> {
24+
const templateRange = this.getTemplateRanges(document.uri.toString());
25+
if (!templateRange) {
26+
return [];
27+
}
28+
29+
const logger = getLogger("folding");
30+
logger.debug(
31+
`Providing folding range for ${document.uri.fsPath}: ${templateRange.startLine}-${templateRange.endLine}`
32+
);
33+
34+
// Simply fold the entire inserted template region
35+
return [new vscode.FoldingRange(templateRange.startLine, templateRange.endLine)];
36+
}
37+
38+
/**
39+
* Notify VS Code that folding ranges have changed.
40+
* Call this after inserting a template to trigger re-evaluation.
41+
*/
42+
notifyFoldingRangesChanged(): void {
43+
this._onDidChangeFoldingRangesEmitter.fire();
44+
}
45+
46+
/**
47+
* Dispose of resources.
48+
*/
49+
dispose(): void {
50+
this._onDidChangeFoldingRangesEmitter.dispose();
51+
}
52+
}

0 commit comments

Comments
 (0)