@fresh-editor/fresh-editor 0.1.97 → 0.1.99
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +76 -0
- package/package.json +1 -1
- package/plugins/code-tour.ts +397 -0
- package/plugins/lib/fresh.d.ts +26 -3
- package/plugins/pkg.ts +487 -72
- package/plugins/schemas/package.schema.json +146 -1
- package/plugins/schemas/tour.schema.json +103 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,81 @@
|
|
|
1
1
|
# Release Notes
|
|
2
2
|
|
|
3
|
+
## 0.1.99
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
* **Windows Terminal Support**: Full terminal emulation on Windows using ConPTY (Windows 10 1809+). Handles PowerShell DSR cursor queries, prefers PowerShell over cmd.exe, and supports stdin piping (`type file | fresh`).
|
|
8
|
+
|
|
9
|
+
* **Text Encoding Support**: Detect and convert files in UTF-8, UTF-16 LE/BE, Latin-1, Windows-1252, Windows-1250, GBK, Shift-JIS, EUC-KR, and GB18030. Encoding shown in status bar (clickable to change). "Reload with Encoding..." command in File menu. Confirmation prompt for large files with non-resynchronizable encodings (#488).
|
|
10
|
+
|
|
11
|
+
* **Encoding Selection in File Browser**: Toggle "Detect Encoding" with Alt+E when opening files. When disabled, prompts for manual encoding selection.
|
|
12
|
+
|
|
13
|
+
* **Bundle Package Type**: New package type containing multiple languages, plugins, and themes in a single package. Shown with "B" tag in package manager.
|
|
14
|
+
|
|
15
|
+
* **Space-Separated Fuzzy Search**: Queries with spaces are now split into independent terms, all of which must match. For example, "features groups-view" now matches "/features/groups/groups-view.tsx" (#933).
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
* Fixed Escape key not closing the manual and keyboard shortcuts pages (#840).
|
|
20
|
+
|
|
21
|
+
* Fixed scrollbar and mouse wheel scrolling not working with line wrap enabled.
|
|
22
|
+
|
|
23
|
+
* Fixed scrollbar thumb drag jumping to mouse position instead of following drag movement.
|
|
24
|
+
|
|
25
|
+
* Fixed AltGr character input not working on Windows (#762).
|
|
26
|
+
|
|
27
|
+
* Fixed custom themes not appearing in "Select Theme" on macOS due to incorrect config path resolution.
|
|
28
|
+
|
|
29
|
+
* Fixed LSP servers registered via plugins being disabled by default.
|
|
30
|
+
|
|
31
|
+
* Fixed language packs being installed to plugins directory instead of languages directory.
|
|
32
|
+
|
|
33
|
+
* Fixed theme changes not persisting when selecting the default theme.
|
|
34
|
+
|
|
35
|
+
* Fixed popup positioning not accounting for file explorer width (#898).
|
|
36
|
+
|
|
37
|
+
* Fixed LSP did_open sending wrong language for multi-language LSP servers.
|
|
38
|
+
|
|
39
|
+
* Fixed manual LSP start not working when LSP config was disabled; settings now sync immediately.
|
|
40
|
+
|
|
41
|
+
### Internal
|
|
42
|
+
|
|
43
|
+
* Refactored config path handling to pass DirectoryContext via call chain instead of static methods.
|
|
44
|
+
|
|
45
|
+
* Added shadow model property-based tests for TextBuffer.
|
|
46
|
+
|
|
47
|
+
* Bumped tree-sitter (0.26.5), actions/checkout (v6), actions/upload-pages-artifact (v4) (@dependabot).
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 0.1.98
|
|
52
|
+
|
|
53
|
+
### Features
|
|
54
|
+
|
|
55
|
+
* **File Explorer Quick Search**: Type to filter files/directories with fuzzy matching. ESC or Backspace clears the search (#892).
|
|
56
|
+
|
|
57
|
+
* **Sort Lines Command**: New command to alphabetically sort selected lines.
|
|
58
|
+
|
|
59
|
+
* **Paragraph Selection**: Ctrl+Shift+Up/Down extends selection to previous/next empty line.
|
|
60
|
+
|
|
61
|
+
* **Local Package Install**: Package manager now supports installing plugins/themes from local file paths (e.g., `/path/to/package`, `~/repos/plugin`).
|
|
62
|
+
|
|
63
|
+
* **Plugin API**: Added `setLineWrap` for plugins to control line wrapping.
|
|
64
|
+
|
|
65
|
+
### Bug Fixes
|
|
66
|
+
|
|
67
|
+
* Fixed data corruption when saving large files with in-place writes.
|
|
68
|
+
|
|
69
|
+
* Fixed UI hang when loading shortcuts in Open File dialog (#903).
|
|
70
|
+
|
|
71
|
+
* Fixed file explorer failing to open at root path "/" (#902).
|
|
72
|
+
|
|
73
|
+
* Fixed Settings UI search results not scrolling properly (#905).
|
|
74
|
+
|
|
75
|
+
* Fixed multi-cursor cut operations not batching undo correctly.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
3
79
|
## 0.1.96
|
|
4
80
|
|
|
5
81
|
### Features
|
package/package.json
CHANGED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
/// <reference path="./lib/fresh.d.ts" />
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Code Tour Plugin
|
|
5
|
+
*
|
|
6
|
+
* A JSON-driven walkthrough system that guides users through a codebase
|
|
7
|
+
* using visual overlays and explanatory text.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* 1. Create a .fresh-tour.json file in your project root
|
|
11
|
+
* 2. Use "Tour: Load Definition..." command to start a tour
|
|
12
|
+
* 3. Navigate with Space/Right (next), Backspace/Left (prev), Tab (resume), Esc (exit)
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const editor = getEditor();
|
|
16
|
+
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Types
|
|
19
|
+
// ============================================================================
|
|
20
|
+
|
|
21
|
+
interface OverlayConfig {
|
|
22
|
+
type: "block" | "line";
|
|
23
|
+
focus_mode: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface TourStep {
|
|
27
|
+
step_id: number;
|
|
28
|
+
title: string;
|
|
29
|
+
file_path: string;
|
|
30
|
+
lines: [number, number]; // 1-indexed, inclusive
|
|
31
|
+
explanation: string;
|
|
32
|
+
overlay_config?: OverlayConfig;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface TourManifest {
|
|
36
|
+
$schema?: string;
|
|
37
|
+
title: string;
|
|
38
|
+
description: string;
|
|
39
|
+
schema_version: "1.0";
|
|
40
|
+
commit_hash?: string;
|
|
41
|
+
steps: TourStep[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type TourState =
|
|
45
|
+
| { kind: "idle" }
|
|
46
|
+
| { kind: "active"; currentStep: number; isPaused: boolean };
|
|
47
|
+
|
|
48
|
+
interface TourManager {
|
|
49
|
+
state: TourState;
|
|
50
|
+
manifest: TourManifest | null;
|
|
51
|
+
dockBufferId: number | null;
|
|
52
|
+
dockSplitId: number | null;
|
|
53
|
+
contentBufferId: number | null;
|
|
54
|
+
contentSplitId: number | null;
|
|
55
|
+
overlayNamespace: string;
|
|
56
|
+
lastKnownTopByte: number;
|
|
57
|
+
lastKnownBufferId: number;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ============================================================================
|
|
61
|
+
// State
|
|
62
|
+
// ============================================================================
|
|
63
|
+
|
|
64
|
+
const TOUR_NAMESPACE = "code-tour";
|
|
65
|
+
|
|
66
|
+
const tourManager: TourManager = {
|
|
67
|
+
state: { kind: "idle" },
|
|
68
|
+
manifest: null,
|
|
69
|
+
dockBufferId: null,
|
|
70
|
+
dockSplitId: null,
|
|
71
|
+
contentBufferId: null,
|
|
72
|
+
contentSplitId: null,
|
|
73
|
+
overlayNamespace: TOUR_NAMESPACE,
|
|
74
|
+
lastKnownTopByte: 0,
|
|
75
|
+
lastKnownBufferId: 0,
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// ============================================================================
|
|
79
|
+
// Tour Status Updates
|
|
80
|
+
// ============================================================================
|
|
81
|
+
|
|
82
|
+
const TOUR_POPUP_ID = "code-tour-step";
|
|
83
|
+
|
|
84
|
+
function showStepPopup(
|
|
85
|
+
step: TourStep,
|
|
86
|
+
stepIndex: number,
|
|
87
|
+
totalSteps: number,
|
|
88
|
+
fileError?: string
|
|
89
|
+
): void {
|
|
90
|
+
const manifest = tourManager.manifest;
|
|
91
|
+
if (!manifest) return;
|
|
92
|
+
|
|
93
|
+
const stepInfo = `Step ${stepIndex + 1}/${totalSteps}: ${step.title}`;
|
|
94
|
+
|
|
95
|
+
// Build message with explanation
|
|
96
|
+
let message = step.explanation;
|
|
97
|
+
if (fileError) {
|
|
98
|
+
message = `ERROR: ${fileError}\n\n${step.explanation}`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Build actions based on position
|
|
102
|
+
const actions: Array<{ id: string; label: string }> = [];
|
|
103
|
+
|
|
104
|
+
if (stepIndex > 0) {
|
|
105
|
+
actions.push({ id: "prev", label: "← Previous" });
|
|
106
|
+
}
|
|
107
|
+
if (stepIndex < totalSteps - 1) {
|
|
108
|
+
actions.push({ id: "next", label: "Next →" });
|
|
109
|
+
}
|
|
110
|
+
actions.push({ id: "exit", label: "Exit Tour" });
|
|
111
|
+
|
|
112
|
+
editor.showActionPopup({
|
|
113
|
+
id: TOUR_POPUP_ID,
|
|
114
|
+
title: stepInfo,
|
|
115
|
+
message: message,
|
|
116
|
+
actions: actions,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Handle popup button clicks
|
|
121
|
+
interface ActionPopupResultData {
|
|
122
|
+
popup_id: string;
|
|
123
|
+
action_id: string;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
globalThis.tour_on_action_popup_result = function (data: ActionPopupResultData): void {
|
|
127
|
+
if (data.popup_id !== TOUR_POPUP_ID) return;
|
|
128
|
+
|
|
129
|
+
switch (data.action_id) {
|
|
130
|
+
case "next":
|
|
131
|
+
nextStep();
|
|
132
|
+
break;
|
|
133
|
+
case "prev":
|
|
134
|
+
prevStep();
|
|
135
|
+
break;
|
|
136
|
+
case "exit":
|
|
137
|
+
exitTour();
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// ============================================================================
|
|
143
|
+
// Overlay Rendering
|
|
144
|
+
// ============================================================================
|
|
145
|
+
|
|
146
|
+
async function clearTourOverlays(): Promise<void> {
|
|
147
|
+
// Clear overlays from all open buffers
|
|
148
|
+
const buffers = editor.listBuffers();
|
|
149
|
+
for (const buf of buffers) {
|
|
150
|
+
editor.clearNamespace(buf.id, TOUR_NAMESPACE);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function renderStepOverlays(step: TourStep): Promise<void> {
|
|
155
|
+
const bufferId = editor.findBufferByPath(step.file_path);
|
|
156
|
+
if (!bufferId) return;
|
|
157
|
+
|
|
158
|
+
// Clear previous overlays
|
|
159
|
+
await clearTourOverlays();
|
|
160
|
+
|
|
161
|
+
// Get line positions (convert from 1-indexed to 0-indexed)
|
|
162
|
+
const startLine = step.lines[0] - 1;
|
|
163
|
+
const endLine = step.lines[1] - 1;
|
|
164
|
+
|
|
165
|
+
const startPos = await editor.getLineStartPosition(startLine);
|
|
166
|
+
const endPos = await editor.getLineEndPosition(endLine);
|
|
167
|
+
|
|
168
|
+
if (startPos === null || endPos === null) {
|
|
169
|
+
editor.warn(`Tour: Could not get line positions for lines ${step.lines[0]}-${step.lines[1]}`);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Add highlight overlay for active lines
|
|
174
|
+
editor.addOverlay(bufferId, TOUR_NAMESPACE, startPos, endPos, {
|
|
175
|
+
bg: [42, 74, 106], // Highlighted background color
|
|
176
|
+
extendToLineEnd: true,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// If focus mode is enabled, we could dim surrounding lines
|
|
180
|
+
// For now, just the highlight is sufficient
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ============================================================================
|
|
184
|
+
// Navigation
|
|
185
|
+
// ============================================================================
|
|
186
|
+
|
|
187
|
+
async function navigateToStep(stepIndex: number): Promise<void> {
|
|
188
|
+
if (!tourManager.manifest) return;
|
|
189
|
+
|
|
190
|
+
const step = tourManager.manifest.steps[stepIndex];
|
|
191
|
+
if (!step) return;
|
|
192
|
+
|
|
193
|
+
// Check if file exists (fileExists is sync, not async)
|
|
194
|
+
const fileExists = editor.fileExists(step.file_path);
|
|
195
|
+
|
|
196
|
+
if (!fileExists) {
|
|
197
|
+
// Show error in popup but allow navigation to continue
|
|
198
|
+
showStepPopup(
|
|
199
|
+
step,
|
|
200
|
+
stepIndex,
|
|
201
|
+
tourManager.manifest.steps.length,
|
|
202
|
+
"File not found"
|
|
203
|
+
);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Open the file at the starting line
|
|
208
|
+
editor.openFile(step.file_path, step.lines[0], 1);
|
|
209
|
+
|
|
210
|
+
// Wait a bit for the file to open
|
|
211
|
+
await editor.delay(50);
|
|
212
|
+
|
|
213
|
+
// Get the buffer ID after opening
|
|
214
|
+
const bufferId = editor.findBufferByPath(step.file_path);
|
|
215
|
+
if (bufferId) {
|
|
216
|
+
// Center the view on the middle of the highlighted region
|
|
217
|
+
const middleLine = Math.floor((step.lines[0] + step.lines[1]) / 2) - 1;
|
|
218
|
+
const splitId = editor.getActiveSplitId();
|
|
219
|
+
editor.scrollToLineCenter(splitId, bufferId, middleLine);
|
|
220
|
+
|
|
221
|
+
// Render overlays
|
|
222
|
+
await renderStepOverlays(step);
|
|
223
|
+
|
|
224
|
+
// Track for detour detection
|
|
225
|
+
tourManager.lastKnownBufferId = bufferId;
|
|
226
|
+
const viewport = editor.getViewport();
|
|
227
|
+
if (viewport) {
|
|
228
|
+
tourManager.lastKnownTopByte = viewport.topByte;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Show explanation popup
|
|
233
|
+
showStepPopup(step, stepIndex, tourManager.manifest.steps.length);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ============================================================================
|
|
237
|
+
// Tour Lifecycle
|
|
238
|
+
// ============================================================================
|
|
239
|
+
|
|
240
|
+
async function loadTour(manifestPath: string): Promise<void> {
|
|
241
|
+
try {
|
|
242
|
+
// Read and parse manifest
|
|
243
|
+
const content = editor.readFile(manifestPath);
|
|
244
|
+
if (!content) {
|
|
245
|
+
editor.error("Failed to read tour file: " + manifestPath);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const manifest: TourManifest = JSON.parse(content);
|
|
249
|
+
|
|
250
|
+
// Validate schema version
|
|
251
|
+
if (manifest.schema_version !== "1.0") {
|
|
252
|
+
editor.error(`Unsupported tour schema version: ${manifest.schema_version}`);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Validate steps
|
|
257
|
+
if (!manifest.steps || manifest.steps.length === 0) {
|
|
258
|
+
editor.error("Tour has no steps");
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Check commit hash if specified
|
|
263
|
+
if (manifest.commit_hash) {
|
|
264
|
+
const result = await editor.spawnProcess("git", [
|
|
265
|
+
"rev-parse",
|
|
266
|
+
"--short",
|
|
267
|
+
"HEAD",
|
|
268
|
+
]);
|
|
269
|
+
if (result.exit_code === 0) {
|
|
270
|
+
const currentCommit = result.stdout.trim();
|
|
271
|
+
if (!currentCommit.startsWith(manifest.commit_hash) &&
|
|
272
|
+
!manifest.commit_hash.startsWith(currentCommit)) {
|
|
273
|
+
editor.warn(
|
|
274
|
+
`Tour was created for commit ${manifest.commit_hash}, current: ${currentCommit}`
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Initialize tour
|
|
281
|
+
tourManager.manifest = manifest;
|
|
282
|
+
tourManager.state = { kind: "active", currentStep: 0, isPaused: false };
|
|
283
|
+
|
|
284
|
+
// Navigate to first step
|
|
285
|
+
await navigateToStep(0);
|
|
286
|
+
|
|
287
|
+
// Set tour context for keybindings
|
|
288
|
+
editor.setContext("tour-active", true);
|
|
289
|
+
} catch (e) {
|
|
290
|
+
editor.error(`Failed to load tour: ${e}`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function exitTour(): void {
|
|
295
|
+
if (tourManager.state.kind !== "active") return;
|
|
296
|
+
|
|
297
|
+
// Clear overlays
|
|
298
|
+
clearTourOverlays();
|
|
299
|
+
|
|
300
|
+
// Reset state
|
|
301
|
+
tourManager.state = { kind: "idle" };
|
|
302
|
+
tourManager.manifest = null;
|
|
303
|
+
|
|
304
|
+
// Clear context
|
|
305
|
+
editor.setContext("tour-active", false);
|
|
306
|
+
|
|
307
|
+
editor.setStatus("Tour ended");
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
async function nextStep(): Promise<void> {
|
|
311
|
+
if (tourManager.state.kind !== "active" || !tourManager.manifest) return;
|
|
312
|
+
|
|
313
|
+
const newIndex = tourManager.state.currentStep + 1;
|
|
314
|
+
if (newIndex >= tourManager.manifest.steps.length) {
|
|
315
|
+
editor.setStatus("Tour: Already at last step");
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
tourManager.state = { ...tourManager.state, currentStep: newIndex, isPaused: false };
|
|
320
|
+
await navigateToStep(newIndex);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async function prevStep(): Promise<void> {
|
|
324
|
+
if (tourManager.state.kind !== "active" || !tourManager.manifest) return;
|
|
325
|
+
|
|
326
|
+
const newIndex = tourManager.state.currentStep - 1;
|
|
327
|
+
if (newIndex < 0) {
|
|
328
|
+
editor.setStatus("Tour: Already at first step");
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
tourManager.state = { ...tourManager.state, currentStep: newIndex, isPaused: false };
|
|
333
|
+
await navigateToStep(newIndex);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// ============================================================================
|
|
337
|
+
// Command Handlers
|
|
338
|
+
// ============================================================================
|
|
339
|
+
|
|
340
|
+
globalThis.tour_load = async function (): Promise<void> {
|
|
341
|
+
// Prompt for tour file
|
|
342
|
+
const result = await editor.prompt("Enter tour file path:", ".fresh-tour.json");
|
|
343
|
+
|
|
344
|
+
if (result) {
|
|
345
|
+
await loadTour(result);
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
globalThis.tour_next = async function (): Promise<void> {
|
|
350
|
+
await nextStep();
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
globalThis.tour_prev = async function (): Promise<void> {
|
|
354
|
+
await prevStep();
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
globalThis.tour_exit = function (): void {
|
|
358
|
+
exitTour();
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
// ============================================================================
|
|
362
|
+
// Registration
|
|
363
|
+
// ============================================================================
|
|
364
|
+
|
|
365
|
+
// Register commands
|
|
366
|
+
editor.registerCommand(
|
|
367
|
+
"Tour: Load Definition...",
|
|
368
|
+
"Load a .fresh-tour.json file to start a guided code tour",
|
|
369
|
+
"tour_load",
|
|
370
|
+
null
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
editor.registerCommand(
|
|
374
|
+
"Tour: Next Step",
|
|
375
|
+
"Go to the next step in the tour",
|
|
376
|
+
"tour_next",
|
|
377
|
+
"tour-active"
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
editor.registerCommand(
|
|
381
|
+
"Tour: Previous Step",
|
|
382
|
+
"Go to the previous step in the tour",
|
|
383
|
+
"tour_prev",
|
|
384
|
+
"tour-active"
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
editor.registerCommand(
|
|
388
|
+
"Tour: Exit",
|
|
389
|
+
"Exit the current tour",
|
|
390
|
+
"tour_exit",
|
|
391
|
+
"tour-active"
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
// Subscribe to action popup results for navigation buttons
|
|
395
|
+
editor.on("action_popup_result", "tour_on_action_popup_result");
|
|
396
|
+
|
|
397
|
+
editor.debug("Code Tour plugin loaded");
|
package/plugins/lib/fresh.d.ts
CHANGED
|
@@ -736,6 +736,22 @@ interface EditorAPI {
|
|
|
736
736
|
*/
|
|
737
737
|
getLineStartPosition(line: number): Promise<number | null>;
|
|
738
738
|
/**
|
|
739
|
+
* Get the byte offset of the end of a line (0-indexed line number)
|
|
740
|
+
* Returns the position after the last character of the line (before newline)
|
|
741
|
+
* Returns null if the line number is out of range
|
|
742
|
+
*/
|
|
743
|
+
getLineEndPosition(line: number): Promise<number | null>;
|
|
744
|
+
/**
|
|
745
|
+
* Get the total number of lines in the active buffer
|
|
746
|
+
* Returns null if buffer not found
|
|
747
|
+
*/
|
|
748
|
+
getBufferLineCount(): Promise<number | null>;
|
|
749
|
+
/**
|
|
750
|
+
* Scroll a split to center a specific line in the viewport
|
|
751
|
+
* Line is 0-indexed (0 = first line)
|
|
752
|
+
*/
|
|
753
|
+
scrollToLineCenter(splitId: number, bufferId: number, line: number): boolean;
|
|
754
|
+
/**
|
|
739
755
|
* Find buffer by file path, returns buffer ID or 0 if not found
|
|
740
756
|
*/
|
|
741
757
|
findBufferByPath(path: string): number;
|
|
@@ -957,10 +973,13 @@ interface EditorAPI {
|
|
|
957
973
|
/**
|
|
958
974
|
* Submit a view transform for a buffer/split
|
|
959
975
|
*
|
|
960
|
-
*
|
|
961
|
-
*
|
|
976
|
+
* Accepts tokens in the simple format:
|
|
977
|
+
* {kind: "text"|"newline"|"space"|"break", text: "...", sourceOffset: N, style?: {...}}
|
|
978
|
+
*
|
|
979
|
+
* Also accepts the TypeScript-defined format for backwards compatibility:
|
|
980
|
+
* {kind: {Text: "..."} | "Newline" | "Space" | "Break", source_offset: N, style?: {...}}
|
|
962
981
|
*/
|
|
963
|
-
submitViewTransform(bufferId: number, splitId: number | null, start: number, end: number, tokens: Record<string, unknown>[],
|
|
982
|
+
submitViewTransform(bufferId: number, splitId: number | null, start: number, end: number, tokens: Record<string, unknown>[], layoutHints?: Record<string, unknown>): boolean;
|
|
964
983
|
/**
|
|
965
984
|
* Clear view transform for a buffer/split
|
|
966
985
|
*/
|
|
@@ -1069,6 +1088,10 @@ interface EditorAPI {
|
|
|
1069
1088
|
*/
|
|
1070
1089
|
setLineNumbers(bufferId: number, enabled: boolean): boolean;
|
|
1071
1090
|
/**
|
|
1091
|
+
* Enable or disable line wrapping for a buffer/split
|
|
1092
|
+
*/
|
|
1093
|
+
setLineWrap(bufferId: number, splitId: number | null, enabled: boolean): boolean;
|
|
1094
|
+
/**
|
|
1072
1095
|
* Create a scroll sync group for anchor-based synchronized scrolling
|
|
1073
1096
|
*/
|
|
1074
1097
|
createScrollSyncGroup(groupId: number, leftSplit: number, rightSplit: number): boolean;
|