@involvex/fresh-editor 0.1.76 → 0.1.78
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/bin/CHANGELOG.md +1017 -0
- package/bin/LICENSE +117 -0
- package/bin/README.md +248 -0
- package/bin/fresh.exe +0 -0
- package/bin/plugins/README.md +71 -0
- package/bin/plugins/audit_mode.i18n.json +821 -0
- package/bin/plugins/audit_mode.ts +1810 -0
- package/bin/plugins/buffer_modified.i18n.json +67 -0
- package/bin/plugins/buffer_modified.ts +281 -0
- package/bin/plugins/calculator.i18n.json +93 -0
- package/bin/plugins/calculator.ts +770 -0
- package/bin/plugins/clangd-lsp.ts +168 -0
- package/bin/plugins/clangd_support.i18n.json +223 -0
- package/bin/plugins/clangd_support.md +20 -0
- package/bin/plugins/clangd_support.ts +325 -0
- package/bin/plugins/color_highlighter.i18n.json +145 -0
- package/bin/plugins/color_highlighter.ts +304 -0
- package/bin/plugins/config-schema.json +768 -0
- package/bin/plugins/csharp-lsp.ts +147 -0
- package/bin/plugins/csharp_support.i18n.json +80 -0
- package/bin/plugins/csharp_support.ts +170 -0
- package/bin/plugins/css-lsp.ts +143 -0
- package/bin/plugins/diagnostics_panel.i18n.json +236 -0
- package/bin/plugins/diagnostics_panel.ts +642 -0
- package/bin/plugins/examples/README.md +85 -0
- package/bin/plugins/examples/async_demo.ts +165 -0
- package/bin/plugins/examples/bookmarks.ts +329 -0
- package/bin/plugins/examples/buffer_query_demo.ts +110 -0
- package/bin/plugins/examples/git_grep.ts +262 -0
- package/bin/plugins/examples/hello_world.ts +93 -0
- package/bin/plugins/examples/virtual_buffer_demo.ts +116 -0
- package/bin/plugins/find_references.i18n.json +275 -0
- package/bin/plugins/find_references.ts +359 -0
- package/bin/plugins/git_blame.i18n.json +496 -0
- package/bin/plugins/git_blame.ts +707 -0
- package/bin/plugins/git_find_file.i18n.json +314 -0
- package/bin/plugins/git_find_file.ts +300 -0
- package/bin/plugins/git_grep.i18n.json +171 -0
- package/bin/plugins/git_grep.ts +191 -0
- package/bin/plugins/git_gutter.i18n.json +93 -0
- package/bin/plugins/git_gutter.ts +477 -0
- package/bin/plugins/git_log.i18n.json +481 -0
- package/bin/plugins/git_log.ts +1285 -0
- package/bin/plugins/go-lsp.ts +143 -0
- package/bin/plugins/html-lsp.ts +145 -0
- package/bin/plugins/json-lsp.ts +145 -0
- package/bin/plugins/lib/fresh.d.ts +1321 -0
- package/bin/plugins/lib/index.ts +24 -0
- package/bin/plugins/lib/navigation-controller.ts +214 -0
- package/bin/plugins/lib/panel-manager.ts +220 -0
- package/bin/plugins/lib/types.ts +72 -0
- package/bin/plugins/lib/virtual-buffer-factory.ts +130 -0
- package/bin/plugins/live_grep.i18n.json +171 -0
- package/bin/plugins/live_grep.ts +422 -0
- package/bin/plugins/markdown_compose.i18n.json +223 -0
- package/bin/plugins/markdown_compose.ts +630 -0
- package/bin/plugins/merge_conflict.i18n.json +821 -0
- package/bin/plugins/merge_conflict.ts +1810 -0
- package/bin/plugins/path_complete.i18n.json +80 -0
- package/bin/plugins/path_complete.ts +165 -0
- package/bin/plugins/python-lsp.ts +162 -0
- package/bin/plugins/rust-lsp.ts +166 -0
- package/bin/plugins/search_replace.i18n.json +405 -0
- package/bin/plugins/search_replace.ts +484 -0
- package/bin/plugins/test_i18n.i18n.json +67 -0
- package/bin/plugins/test_i18n.ts +18 -0
- package/bin/plugins/theme_editor.i18n.json +3746 -0
- package/bin/plugins/theme_editor.ts +2063 -0
- package/bin/plugins/todo_highlighter.i18n.json +184 -0
- package/bin/plugins/todo_highlighter.ts +206 -0
- package/bin/plugins/typescript-lsp.ts +167 -0
- package/bin/plugins/vi_mode.i18n.json +1549 -0
- package/bin/plugins/vi_mode.ts +2747 -0
- package/bin/plugins/welcome.i18n.json +236 -0
- package/bin/plugins/welcome.ts +76 -0
- package/bin/themes/dark.json +102 -0
- package/bin/themes/dracula.json +62 -0
- package/bin/themes/high-contrast.json +102 -0
- package/bin/themes/light.json +102 -0
- package/bin/themes/nord.json +62 -0
- package/bin/themes/nostalgia.json +102 -0
- package/bin/themes/solarized-dark.json +62 -0
- package/binary-install.js +1 -1
- package/dist/bin/fresh.js +9 -0
- package/dist/binary-install.js +149 -0
- package/dist/binary.js +30 -0
- package/dist/fresh-6yhknp07.exe +0 -0
- package/dist/install.js +158 -0
- package/dist/run-fresh.js +43 -0
- package/package.json +7 -2
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fresh Editor Plugin Library
|
|
3
|
+
*
|
|
4
|
+
* Shared utilities for building LSP-related plugins with common patterns.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import { PanelManager, NavigationController, VirtualBufferFactory } from "./lib/index.ts";
|
|
9
|
+
* import type { Location, RGB, PanelOptions } from "./lib/index.ts";
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// Types
|
|
14
|
+
export type { RGB, Location, PanelOptions, PanelState, NavigationOptions, HighlightPattern } from "./types.ts";
|
|
15
|
+
|
|
16
|
+
// Panel Management
|
|
17
|
+
export { PanelManager } from "./panel-manager.ts";
|
|
18
|
+
|
|
19
|
+
// Navigation
|
|
20
|
+
export { NavigationController } from "./navigation-controller.ts";
|
|
21
|
+
|
|
22
|
+
// Buffer Creation
|
|
23
|
+
export { createVirtualBufferFactory } from "./virtual-buffer-factory.ts";
|
|
24
|
+
export type { VirtualBufferOptions, SplitBufferOptions } from "./virtual-buffer-factory.ts";
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/// <reference path="../../types/fresh.d.ts" />
|
|
2
|
+
|
|
3
|
+
import type { NavigationOptions } from "./types.ts";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* NavigationController - Generic list navigation for panel plugins
|
|
7
|
+
*
|
|
8
|
+
* Handles the common pattern of:
|
|
9
|
+
* - Maintaining selected index
|
|
10
|
+
* - Boundary checking
|
|
11
|
+
* - Status message updates
|
|
12
|
+
* - Callback on selection change
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* const nav = new NavigationController<DiagnosticItem>({
|
|
17
|
+
* itemLabel: "Diagnostic",
|
|
18
|
+
* onSelectionChange: (item, index) => {
|
|
19
|
+
* updateHighlight(index);
|
|
20
|
+
* }
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // Set items when panel opens
|
|
24
|
+
* nav.setItems(diagnostics);
|
|
25
|
+
*
|
|
26
|
+
* // Navigation commands
|
|
27
|
+
* function next() { nav.next(); }
|
|
28
|
+
* function prev() { nav.prev(); }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export class NavigationController<T> {
|
|
32
|
+
private items: T[] = [];
|
|
33
|
+
private currentIndex: number = 0;
|
|
34
|
+
private options: NavigationOptions<T>;
|
|
35
|
+
|
|
36
|
+
constructor(private readonly editor: EditorAPI, options: NavigationOptions<T> = {}) {
|
|
37
|
+
this.options = {
|
|
38
|
+
itemLabel: "Item",
|
|
39
|
+
wrap: false,
|
|
40
|
+
...options,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Set the items to navigate through
|
|
46
|
+
*
|
|
47
|
+
* @param items - Array of items
|
|
48
|
+
* @param resetIndex - Whether to reset index to 0 (default true)
|
|
49
|
+
*/
|
|
50
|
+
setItems(items: T[], resetIndex: boolean = true): void {
|
|
51
|
+
this.items = items;
|
|
52
|
+
if (resetIndex) {
|
|
53
|
+
this.currentIndex = 0;
|
|
54
|
+
} else {
|
|
55
|
+
// Clamp to valid range
|
|
56
|
+
this.currentIndex = Math.min(this.currentIndex, Math.max(0, items.length - 1));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get all items
|
|
62
|
+
*/
|
|
63
|
+
getItems(): T[] {
|
|
64
|
+
return this.items;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get the current selected index
|
|
69
|
+
*/
|
|
70
|
+
get selectedIndex(): number {
|
|
71
|
+
return this.currentIndex;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Set the selected index directly
|
|
76
|
+
*/
|
|
77
|
+
set selectedIndex(index: number) {
|
|
78
|
+
if (this.items.length === 0) return;
|
|
79
|
+
this.currentIndex = Math.max(0, Math.min(index, this.items.length - 1));
|
|
80
|
+
this.notifyChange();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Get the currently selected item
|
|
85
|
+
*/
|
|
86
|
+
get selected(): T | null {
|
|
87
|
+
if (this.items.length === 0 || this.currentIndex >= this.items.length) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
return this.items[this.currentIndex];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get the total number of items
|
|
95
|
+
*/
|
|
96
|
+
get count(): number {
|
|
97
|
+
return this.items.length;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Check if there are any items
|
|
102
|
+
*/
|
|
103
|
+
get isEmpty(): boolean {
|
|
104
|
+
return this.items.length === 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Move to the next item
|
|
109
|
+
*/
|
|
110
|
+
next(): void {
|
|
111
|
+
if (this.items.length === 0) return;
|
|
112
|
+
|
|
113
|
+
if (this.options.wrap) {
|
|
114
|
+
this.currentIndex = (this.currentIndex + 1) % this.items.length;
|
|
115
|
+
} else {
|
|
116
|
+
this.currentIndex = Math.min(this.currentIndex + 1, this.items.length - 1);
|
|
117
|
+
}
|
|
118
|
+
this.notifyChange();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Move to the previous item
|
|
123
|
+
*/
|
|
124
|
+
prev(): void {
|
|
125
|
+
if (this.items.length === 0) return;
|
|
126
|
+
|
|
127
|
+
if (this.options.wrap) {
|
|
128
|
+
this.currentIndex = (this.currentIndex - 1 + this.items.length) % this.items.length;
|
|
129
|
+
} else {
|
|
130
|
+
this.currentIndex = Math.max(this.currentIndex - 1, 0);
|
|
131
|
+
}
|
|
132
|
+
this.notifyChange();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Move to the first item
|
|
137
|
+
*/
|
|
138
|
+
first(): void {
|
|
139
|
+
if (this.items.length === 0) return;
|
|
140
|
+
this.currentIndex = 0;
|
|
141
|
+
this.notifyChange();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Move to the last item
|
|
146
|
+
*/
|
|
147
|
+
last(): void {
|
|
148
|
+
if (this.items.length === 0) return;
|
|
149
|
+
this.currentIndex = this.items.length - 1;
|
|
150
|
+
this.notifyChange();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Jump to a specific index
|
|
155
|
+
*
|
|
156
|
+
* @param index - Target index
|
|
157
|
+
*/
|
|
158
|
+
jumpTo(index: number): void {
|
|
159
|
+
if (this.items.length === 0) return;
|
|
160
|
+
this.currentIndex = Math.max(0, Math.min(index, this.items.length - 1));
|
|
161
|
+
this.notifyChange();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Update the status message with current position
|
|
166
|
+
*
|
|
167
|
+
* @param customMessage - Optional custom message (overrides default)
|
|
168
|
+
*/
|
|
169
|
+
showStatus(customMessage?: string): void {
|
|
170
|
+
if (this.items.length === 0) {
|
|
171
|
+
this.editor.setStatus(`No ${this.options.itemLabel}s`);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const message = customMessage ||
|
|
176
|
+
`${this.options.itemLabel} ${this.currentIndex + 1}/${this.items.length}`;
|
|
177
|
+
this.editor.setStatus(message);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Reset the controller state
|
|
182
|
+
*/
|
|
183
|
+
reset(): void {
|
|
184
|
+
this.items = [];
|
|
185
|
+
this.currentIndex = 0;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Find and select an item matching a predicate
|
|
190
|
+
*
|
|
191
|
+
* @param predicate - Function to test items
|
|
192
|
+
* @returns true if found and selected, false otherwise
|
|
193
|
+
*/
|
|
194
|
+
findAndSelect(predicate: (item: T) => boolean): boolean {
|
|
195
|
+
const index = this.items.findIndex(predicate);
|
|
196
|
+
if (index !== -1) {
|
|
197
|
+
this.currentIndex = index;
|
|
198
|
+
this.notifyChange();
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Internal: Notify about selection change
|
|
206
|
+
*/
|
|
207
|
+
private notifyChange(): void {
|
|
208
|
+
this.showStatus();
|
|
209
|
+
|
|
210
|
+
if (this.options.onSelectionChange && this.selected !== null) {
|
|
211
|
+
this.options.onSelectionChange(this.selected, this.currentIndex);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/// <reference path="../../types/fresh.d.ts" />
|
|
2
|
+
|
|
3
|
+
import type { PanelOptions, PanelState } from "./types.ts";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* PanelManager - Manages panel lifecycle for split-view plugins
|
|
7
|
+
*
|
|
8
|
+
* Handles the common pattern of:
|
|
9
|
+
* - Opening a virtual buffer in a split
|
|
10
|
+
* - Tracking source split for navigation
|
|
11
|
+
* - Closing and restoring previous state
|
|
12
|
+
* - Updating panel content
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* const panel = new PanelManager("diagnostics", "diagnostics-list");
|
|
17
|
+
*
|
|
18
|
+
* async function open() {
|
|
19
|
+
* await panel.open({ entries: buildEntries(), ratio: 0.3 });
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* function close() {
|
|
23
|
+
* panel.close();
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* function update() {
|
|
27
|
+
* panel.updateContent(buildEntries());
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export class PanelManager {
|
|
32
|
+
private state: PanelState = {
|
|
33
|
+
isOpen: false,
|
|
34
|
+
bufferId: null,
|
|
35
|
+
splitId: null,
|
|
36
|
+
sourceSplitId: null,
|
|
37
|
+
sourceBufferId: null,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Create a new PanelManager
|
|
42
|
+
*
|
|
43
|
+
* @param editor - The editor API instance
|
|
44
|
+
* @param panelName - Display name for the panel (e.g., "*Diagnostics*")
|
|
45
|
+
* @param modeName - Mode name for keybindings (e.g., "diagnostics-list")
|
|
46
|
+
*/
|
|
47
|
+
constructor(
|
|
48
|
+
private readonly editor: EditorAPI,
|
|
49
|
+
private readonly panelName: string,
|
|
50
|
+
private readonly modeName: string
|
|
51
|
+
) {}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Check if the panel is currently open
|
|
55
|
+
*/
|
|
56
|
+
get isOpen(): boolean {
|
|
57
|
+
return this.state.isOpen;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get the panel's buffer ID (null if not open)
|
|
62
|
+
*/
|
|
63
|
+
get bufferId(): number | null {
|
|
64
|
+
return this.state.bufferId;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get the panel's split ID (null if not open)
|
|
69
|
+
*/
|
|
70
|
+
get splitId(): number | null {
|
|
71
|
+
return this.state.splitId;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Get the source split ID (where user was before opening panel)
|
|
76
|
+
*/
|
|
77
|
+
get sourceSplitId(): number | null {
|
|
78
|
+
return this.state.sourceSplitId;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get the source buffer ID (to restore when closing)
|
|
83
|
+
*/
|
|
84
|
+
get sourceBufferId(): number | null {
|
|
85
|
+
return this.state.sourceBufferId;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Open the panel in a new split
|
|
90
|
+
*
|
|
91
|
+
* If already open, updates the content instead.
|
|
92
|
+
*
|
|
93
|
+
* @param options - Panel configuration
|
|
94
|
+
* @returns The buffer ID of the panel
|
|
95
|
+
*/
|
|
96
|
+
async open(options: PanelOptions): Promise<number> {
|
|
97
|
+
const { entries, ratio = 0.3, showLineNumbers = false, editingDisabled = true } = options;
|
|
98
|
+
|
|
99
|
+
if (this.state.isOpen && this.state.bufferId !== null) {
|
|
100
|
+
// Panel already open - just update content
|
|
101
|
+
this.updateContent(entries);
|
|
102
|
+
return this.state.bufferId;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Save current context
|
|
106
|
+
this.state.sourceSplitId = this.editor.getActiveSplitId();
|
|
107
|
+
this.state.sourceBufferId = this.editor.getActiveBufferId();
|
|
108
|
+
|
|
109
|
+
// Create virtual buffer in split
|
|
110
|
+
const bufferId = await this.editor.createVirtualBufferInSplit({
|
|
111
|
+
name: this.panelName,
|
|
112
|
+
mode: this.modeName,
|
|
113
|
+
read_only: true,
|
|
114
|
+
entries,
|
|
115
|
+
ratio,
|
|
116
|
+
panel_id: this.panelName,
|
|
117
|
+
show_line_numbers: showLineNumbers,
|
|
118
|
+
editing_disabled: editingDisabled,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// Track state
|
|
122
|
+
this.state.bufferId = bufferId;
|
|
123
|
+
this.state.splitId = this.editor.getActiveSplitId();
|
|
124
|
+
this.state.isOpen = true;
|
|
125
|
+
|
|
126
|
+
return bufferId;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Close the panel and restore previous state
|
|
131
|
+
*/
|
|
132
|
+
close(): void {
|
|
133
|
+
if (!this.state.isOpen) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Close the split containing the panel
|
|
138
|
+
if (this.state.splitId !== null) {
|
|
139
|
+
this.editor.closeSplit(this.state.splitId);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Focus back on source split
|
|
143
|
+
if (this.state.sourceSplitId !== null) {
|
|
144
|
+
this.editor.focusSplit(this.state.sourceSplitId);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Reset state
|
|
148
|
+
this.reset();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Update the panel content without closing/reopening
|
|
153
|
+
*
|
|
154
|
+
* @param entries - New entries to display
|
|
155
|
+
*/
|
|
156
|
+
updateContent(entries: TextPropertyEntry[]): void {
|
|
157
|
+
if (!this.state.isOpen || this.state.bufferId === null) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
this.editor.setVirtualBufferContent(this.state.bufferId, entries);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Reset panel state (called internally on close)
|
|
166
|
+
*/
|
|
167
|
+
reset(): void {
|
|
168
|
+
this.state = {
|
|
169
|
+
isOpen: false,
|
|
170
|
+
bufferId: null,
|
|
171
|
+
splitId: null,
|
|
172
|
+
sourceSplitId: null,
|
|
173
|
+
sourceBufferId: null,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Focus the source split (useful for "goto" operations)
|
|
179
|
+
*/
|
|
180
|
+
focusSource(): void {
|
|
181
|
+
if (this.state.sourceSplitId !== null) {
|
|
182
|
+
this.editor.focusSplit(this.state.sourceSplitId);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Focus the panel split
|
|
188
|
+
*/
|
|
189
|
+
focusPanel(): void {
|
|
190
|
+
if (this.state.splitId !== null) {
|
|
191
|
+
this.editor.focusSplit(this.state.splitId);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Open a file in the source split (for navigation operations)
|
|
197
|
+
*
|
|
198
|
+
* @param filePath - Path to the file to open
|
|
199
|
+
* @param line - Line number to jump to (1-indexed)
|
|
200
|
+
* @param column - Column number to jump to (1-indexed)
|
|
201
|
+
*/
|
|
202
|
+
async openInSource(filePath: string, line: number, column: number): Promise<void> {
|
|
203
|
+
if (this.state.sourceSplitId === null) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Focus source split and open file
|
|
208
|
+
this.editor.focusSplit(this.state.sourceSplitId);
|
|
209
|
+
await this.editor.openFile(filePath);
|
|
210
|
+
|
|
211
|
+
// Jump to location
|
|
212
|
+
this.editor.gotoLine(line);
|
|
213
|
+
if (column > 1) {
|
|
214
|
+
this.editor.gotoColumn(column);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Focus back on panel
|
|
218
|
+
this.focusPanel();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/// <reference path="./fresh.d.ts" />
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared Types for Fresh Editor Plugin Library
|
|
5
|
+
*
|
|
6
|
+
* Common interfaces and types used across LSP-related plugins.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* RGB color tuple for overlays and highlighting
|
|
11
|
+
*/
|
|
12
|
+
export type RGB = [number, number, number];
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* File location with line and column
|
|
16
|
+
*/
|
|
17
|
+
export interface Location {
|
|
18
|
+
file: string;
|
|
19
|
+
line: number;
|
|
20
|
+
column: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Options for opening a panel
|
|
25
|
+
*/
|
|
26
|
+
export interface PanelOptions {
|
|
27
|
+
/** Text property entries to display */
|
|
28
|
+
entries: TextPropertyEntry[];
|
|
29
|
+
/** Split ratio (0.0 to 1.0), default 0.3 */
|
|
30
|
+
ratio?: number;
|
|
31
|
+
/** Whether to show line numbers, default false for panels */
|
|
32
|
+
showLineNumbers?: boolean;
|
|
33
|
+
/** Whether editing is disabled, default true for panels */
|
|
34
|
+
editingDisabled?: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* State of a managed panel
|
|
39
|
+
*/
|
|
40
|
+
export interface PanelState {
|
|
41
|
+
isOpen: boolean;
|
|
42
|
+
bufferId: number | null;
|
|
43
|
+
splitId: number | null;
|
|
44
|
+
sourceSplitId: number | null;
|
|
45
|
+
sourceBufferId: number | null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Options for NavigationController
|
|
50
|
+
*/
|
|
51
|
+
export interface NavigationOptions<T> {
|
|
52
|
+
/** Function to call when selection changes */
|
|
53
|
+
onSelectionChange?: (item: T, index: number) => void;
|
|
54
|
+
/** Label for status messages (e.g., "Diagnostic", "Reference") */
|
|
55
|
+
itemLabel?: string;
|
|
56
|
+
/** Whether to wrap around at boundaries */
|
|
57
|
+
wrap?: boolean;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Highlight pattern for syntax highlighting
|
|
62
|
+
*/
|
|
63
|
+
export interface HighlightPattern {
|
|
64
|
+
/** Function to test if line matches */
|
|
65
|
+
match: (line: string) => boolean;
|
|
66
|
+
/** Color to apply */
|
|
67
|
+
rgb: RGB;
|
|
68
|
+
/** Whether to underline */
|
|
69
|
+
underline?: boolean;
|
|
70
|
+
/** Prefix for overlay IDs */
|
|
71
|
+
overlayIdPrefix?: string;
|
|
72
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/// <reference path="../../types/fresh.d.ts" />
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Options for creating a virtual buffer
|
|
5
|
+
*/
|
|
6
|
+
export interface VirtualBufferOptions {
|
|
7
|
+
/** Display name (e.g., "*Commit Details*") */
|
|
8
|
+
name: string;
|
|
9
|
+
/** Mode name for keybindings */
|
|
10
|
+
mode: string;
|
|
11
|
+
/** Text property entries */
|
|
12
|
+
entries: TextPropertyEntry[];
|
|
13
|
+
/** Whether to show line numbers (default false) */
|
|
14
|
+
showLineNumbers?: boolean;
|
|
15
|
+
/** Whether editing is disabled (default true) */
|
|
16
|
+
editingDisabled?: boolean;
|
|
17
|
+
/** Whether buffer is read-only (default true) */
|
|
18
|
+
readOnly?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Options for creating a virtual buffer in a new split
|
|
23
|
+
*/
|
|
24
|
+
export interface SplitBufferOptions extends VirtualBufferOptions {
|
|
25
|
+
/** Split ratio (default 0.3) */
|
|
26
|
+
ratio?: number;
|
|
27
|
+
/** Panel ID for idempotent operations */
|
|
28
|
+
panelId?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Create a VirtualBufferFactory bound to a specific editor instance.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* const editor = getEditor();
|
|
37
|
+
* const bufferFactory = createVirtualBufferFactory(editor);
|
|
38
|
+
*
|
|
39
|
+
* // Create buffer as a tab in current split
|
|
40
|
+
* const bufferId = await bufferFactory.create({
|
|
41
|
+
* name: "*Help*",
|
|
42
|
+
* mode: "help-manual",
|
|
43
|
+
* entries: helpEntries,
|
|
44
|
+
* });
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export function createVirtualBufferFactory(editor: EditorAPI) {
|
|
48
|
+
return {
|
|
49
|
+
/**
|
|
50
|
+
* Create a virtual buffer as a new tab in the current split
|
|
51
|
+
*/
|
|
52
|
+
async create(options: VirtualBufferOptions): Promise<number> {
|
|
53
|
+
const {
|
|
54
|
+
name,
|
|
55
|
+
mode,
|
|
56
|
+
entries,
|
|
57
|
+
showLineNumbers = false,
|
|
58
|
+
editingDisabled = true,
|
|
59
|
+
readOnly = true,
|
|
60
|
+
} = options;
|
|
61
|
+
|
|
62
|
+
return await editor.createVirtualBuffer({
|
|
63
|
+
name,
|
|
64
|
+
mode,
|
|
65
|
+
read_only: readOnly,
|
|
66
|
+
entries,
|
|
67
|
+
show_line_numbers: showLineNumbers,
|
|
68
|
+
editing_disabled: editingDisabled,
|
|
69
|
+
});
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Create a virtual buffer in an existing split
|
|
74
|
+
*/
|
|
75
|
+
async createInSplit(splitId: number, options: VirtualBufferOptions): Promise<number> {
|
|
76
|
+
const {
|
|
77
|
+
name,
|
|
78
|
+
mode,
|
|
79
|
+
entries,
|
|
80
|
+
showLineNumbers = false,
|
|
81
|
+
editingDisabled = true,
|
|
82
|
+
readOnly = true,
|
|
83
|
+
} = options;
|
|
84
|
+
|
|
85
|
+
return await editor.createVirtualBufferInExistingSplit({
|
|
86
|
+
name,
|
|
87
|
+
mode,
|
|
88
|
+
read_only: readOnly,
|
|
89
|
+
entries,
|
|
90
|
+
split_id: splitId,
|
|
91
|
+
show_line_numbers: showLineNumbers,
|
|
92
|
+
editing_disabled: editingDisabled,
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Create a virtual buffer in a new split
|
|
98
|
+
*/
|
|
99
|
+
async createWithSplit(options: SplitBufferOptions): Promise<number> {
|
|
100
|
+
const {
|
|
101
|
+
name,
|
|
102
|
+
mode,
|
|
103
|
+
entries,
|
|
104
|
+
ratio = 0.3,
|
|
105
|
+
panelId,
|
|
106
|
+
showLineNumbers = false,
|
|
107
|
+
editingDisabled = true,
|
|
108
|
+
readOnly = true,
|
|
109
|
+
} = options;
|
|
110
|
+
|
|
111
|
+
return await editor.createVirtualBufferInSplit({
|
|
112
|
+
name,
|
|
113
|
+
mode,
|
|
114
|
+
read_only: readOnly,
|
|
115
|
+
entries,
|
|
116
|
+
ratio,
|
|
117
|
+
panel_id: panelId,
|
|
118
|
+
show_line_numbers: showLineNumbers,
|
|
119
|
+
editing_disabled: editingDisabled,
|
|
120
|
+
});
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Update content of an existing virtual buffer
|
|
125
|
+
*/
|
|
126
|
+
updateContent(bufferId: number, entries: TextPropertyEntry[]): void {
|
|
127
|
+
editor.setVirtualBufferContent(bufferId, entries);
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|