@fresh-editor/fresh-editor 0.1.4
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/.gitignore +2 -0
- package/LICENSE +117 -0
- package/README.md +54 -0
- package/binary-install.js +212 -0
- package/binary.js +126 -0
- package/install.js +4 -0
- package/npm-shrinkwrap.json +900 -0
- package/package.json +100 -0
- package/plugins/README.md +121 -0
- package/plugins/clangd_support.md +20 -0
- package/plugins/clangd_support.ts +323 -0
- package/plugins/color_highlighter.ts +302 -0
- package/plugins/diagnostics_panel.ts +308 -0
- package/plugins/examples/README.md +245 -0
- package/plugins/examples/async_demo.ts +165 -0
- package/plugins/examples/bookmarks.ts +329 -0
- package/plugins/examples/buffer_query_demo.ts +110 -0
- package/plugins/examples/git_grep.ts +262 -0
- package/plugins/examples/hello_world.ts +93 -0
- package/plugins/examples/virtual_buffer_demo.ts +116 -0
- package/plugins/find_references.ts +357 -0
- package/plugins/git_find_file.ts +298 -0
- package/plugins/git_grep.ts +188 -0
- package/plugins/git_log.ts +1283 -0
- package/plugins/lib/fresh.d.ts +849 -0
- package/plugins/lib/index.ts +24 -0
- package/plugins/lib/navigation-controller.ts +214 -0
- package/plugins/lib/panel-manager.ts +218 -0
- package/plugins/lib/types.ts +72 -0
- package/plugins/lib/virtual-buffer-factory.ts +158 -0
- package/plugins/manual_help.ts +243 -0
- package/plugins/markdown_compose.ts +1207 -0
- package/plugins/merge_conflict.ts +1811 -0
- package/plugins/path_complete.ts +163 -0
- package/plugins/search_replace.ts +481 -0
- package/plugins/todo_highlighter.ts +204 -0
- package/plugins/welcome.ts +74 -0
- package/run-fresh.js +4 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# Example Plugins
|
|
2
|
+
|
|
3
|
+
This directory contains example plugins demonstrating the editor's plugin system.
|
|
4
|
+
|
|
5
|
+
## Available Examples
|
|
6
|
+
|
|
7
|
+
### hello.lua
|
|
8
|
+
A simple "Hello World" plugin that demonstrates:
|
|
9
|
+
- Registering a custom command
|
|
10
|
+
- Setting status messages
|
|
11
|
+
- Basic plugin structure
|
|
12
|
+
|
|
13
|
+
### highlight_demo.lua
|
|
14
|
+
Demonstrates visual overlays:
|
|
15
|
+
- Multiple command registration
|
|
16
|
+
- Adding colored overlays to buffers
|
|
17
|
+
- Using the overlay API
|
|
18
|
+
|
|
19
|
+
### git_grep_demo.lua
|
|
20
|
+
Simple demo of git integration and file navigation:
|
|
21
|
+
- Spawning async git processes
|
|
22
|
+
- Parsing git grep output
|
|
23
|
+
- Opening files at specific line:column positions
|
|
24
|
+
- Basic prototype (see full plugins in parent directory)
|
|
25
|
+
|
|
26
|
+
### async_demo.lua
|
|
27
|
+
Demonstrates async process spawning:
|
|
28
|
+
- Running external commands
|
|
29
|
+
- Processing stdout/stderr
|
|
30
|
+
- Handling exit codes
|
|
31
|
+
|
|
32
|
+
### buffer_query_demo.lua
|
|
33
|
+
Demonstrates buffer queries:
|
|
34
|
+
- Getting buffer metadata
|
|
35
|
+
- Listing all open buffers
|
|
36
|
+
- Querying cursor and viewport information
|
|
37
|
+
|
|
38
|
+
## Plugin API
|
|
39
|
+
|
|
40
|
+
### Available Functions
|
|
41
|
+
|
|
42
|
+
#### editor.register_command(command_table)
|
|
43
|
+
Register a new command in the command palette.
|
|
44
|
+
|
|
45
|
+
```lua
|
|
46
|
+
editor.register_command({
|
|
47
|
+
name = "My Command",
|
|
48
|
+
description = "What this command does",
|
|
49
|
+
action = "my_command_function", -- Name of global Lua function to call
|
|
50
|
+
contexts = {"normal"} -- or {"help", "prompt", "popup", "file_explorer"}
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
-- Define the global function
|
|
54
|
+
function my_command_function()
|
|
55
|
+
editor.set_status("My command executed!")
|
|
56
|
+
end
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The `action` field should be the name of a global Lua function that will be called when the command is executed from the command palette.
|
|
60
|
+
|
|
61
|
+
#### editor.set_status(message)
|
|
62
|
+
Set the status bar message.
|
|
63
|
+
|
|
64
|
+
```lua
|
|
65
|
+
editor.set_status("Plugin loaded successfully")
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
#### editor.insert_text(buffer_id, position, text)
|
|
69
|
+
Insert text at a specific position in a buffer.
|
|
70
|
+
|
|
71
|
+
```lua
|
|
72
|
+
editor.insert_text(0, 0, "Hello, World!")
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
#### editor.add_overlay(buffer_id, overlay_id, start, end, r, g, b, underline)
|
|
76
|
+
Add a visual overlay (highlight/underline) to a buffer.
|
|
77
|
+
|
|
78
|
+
```lua
|
|
79
|
+
-- Add red underline to positions 0-10 in buffer 0
|
|
80
|
+
editor.add_overlay(0, "my-overlay", 0, 10, 255, 0, 0, true)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
#### editor.on(hook_name, callback)
|
|
84
|
+
Register a hook callback (currently simplified).
|
|
85
|
+
|
|
86
|
+
```lua
|
|
87
|
+
editor.on("after-file-save", function(args)
|
|
88
|
+
print("File saved!")
|
|
89
|
+
return true -- return false to cancel operation
|
|
90
|
+
end)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
#### editor.spawn(command, args, callback) or editor.spawn(command, args, options, callback)
|
|
94
|
+
Spawn an async process and get its output.
|
|
95
|
+
|
|
96
|
+
```lua
|
|
97
|
+
-- Simple form
|
|
98
|
+
editor.spawn("git", {"status", "--porcelain"}, function(stdout, stderr, exit_code)
|
|
99
|
+
editor.set_status("Git status: " .. stdout)
|
|
100
|
+
end)
|
|
101
|
+
|
|
102
|
+
-- With options (e.g., working directory)
|
|
103
|
+
editor.spawn("ls", {"-la"}, {cwd = "/tmp"}, function(stdout, stderr, exit_code)
|
|
104
|
+
print("Files: " .. stdout)
|
|
105
|
+
end)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
#### editor.open_file(path) or editor.open_file({path, line, column})
|
|
109
|
+
Open a file, optionally jumping to a specific line and column.
|
|
110
|
+
|
|
111
|
+
```lua
|
|
112
|
+
-- Open file at start
|
|
113
|
+
editor.open_file("src/main.rs")
|
|
114
|
+
|
|
115
|
+
-- Open file and jump to line 42, column 10 (1-indexed)
|
|
116
|
+
editor.open_file({
|
|
117
|
+
path = "src/main.rs",
|
|
118
|
+
line = 42,
|
|
119
|
+
column = 10
|
|
120
|
+
})
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
This is particularly useful for implementing features like git grep, LSP go-to-definition, etc.
|
|
124
|
+
|
|
125
|
+
#### editor.start_prompt(options) and editor.set_prompt_suggestions(suggestions)
|
|
126
|
+
Create interactive prompts with hook-based event handling.
|
|
127
|
+
|
|
128
|
+
```lua
|
|
129
|
+
-- Start a prompt
|
|
130
|
+
editor.start_prompt({
|
|
131
|
+
label = "Git grep: ",
|
|
132
|
+
prompt_type = "git-grep" -- Custom identifier for hook filtering
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
-- React to input changes via hooks
|
|
136
|
+
editor.on("prompt-changed", function(args)
|
|
137
|
+
if args.prompt_type == "git-grep" then
|
|
138
|
+
local query = args.input
|
|
139
|
+
|
|
140
|
+
-- Spawn async git grep
|
|
141
|
+
editor.spawn("git", {"grep", "-n", "--column", "-I", "--", query},
|
|
142
|
+
function(stdout, stderr, exit_code)
|
|
143
|
+
if exit_code == 0 then
|
|
144
|
+
local results = parse_git_grep(stdout)
|
|
145
|
+
|
|
146
|
+
-- Update suggestions
|
|
147
|
+
editor.set_prompt_suggestions(results)
|
|
148
|
+
end
|
|
149
|
+
end)
|
|
150
|
+
end
|
|
151
|
+
end)
|
|
152
|
+
|
|
153
|
+
-- Handle selection
|
|
154
|
+
editor.on("prompt-confirmed", function(args)
|
|
155
|
+
if args.prompt_type == "git-grep" and args.selected_index then
|
|
156
|
+
local selected = prompt_suggestions[args.selected_index + 1] -- Lua is 1-indexed
|
|
157
|
+
editor.open_file({
|
|
158
|
+
path = selected.file,
|
|
159
|
+
line = selected.line,
|
|
160
|
+
column = selected.column
|
|
161
|
+
})
|
|
162
|
+
end
|
|
163
|
+
end)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Suggestions format:
|
|
167
|
+
```lua
|
|
168
|
+
editor.set_prompt_suggestions({
|
|
169
|
+
{
|
|
170
|
+
text = "src/main.rs:42:10: match found here",
|
|
171
|
+
value = "src/main.rs:42:10", -- Optional: value to use when selected
|
|
172
|
+
description = "Path to file", -- Optional
|
|
173
|
+
disabled = false, -- Optional: grey out this suggestion
|
|
174
|
+
keybinding = nil -- Optional: show keyboard shortcut
|
|
175
|
+
},
|
|
176
|
+
-- ... more suggestions
|
|
177
|
+
})
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
This hook-based approach is simpler than callbacks and matches Emacs' extensibility model.
|
|
181
|
+
|
|
182
|
+
## Available Hooks
|
|
183
|
+
|
|
184
|
+
### File Hooks
|
|
185
|
+
- `before-file-open` - Before a file is opened
|
|
186
|
+
- `after-file-open` - After a file is successfully opened
|
|
187
|
+
- `before-file-save` - Before a file is saved
|
|
188
|
+
- `after-file-save` - After a file is saved
|
|
189
|
+
|
|
190
|
+
### Edit Hooks
|
|
191
|
+
- `after-insert` - After text is inserted
|
|
192
|
+
- `after-delete` - After text is deleted
|
|
193
|
+
|
|
194
|
+
### Command Hooks
|
|
195
|
+
- `pre-command` - Before a command executes
|
|
196
|
+
- `post-command` - After a command executes
|
|
197
|
+
|
|
198
|
+
### Prompt Hooks (NEW - Jan 2025)
|
|
199
|
+
- `prompt-changed` - User typed/edited prompt input (args: `prompt_type`, `input`)
|
|
200
|
+
- `prompt-confirmed` - User pressed Enter (args: `prompt_type`, `input`, `selected_index`)
|
|
201
|
+
- `prompt-cancelled` - User pressed Escape/Ctrl+G (args: `prompt_type`, `input`)
|
|
202
|
+
|
|
203
|
+
These prompt hooks enable plugins to create interactive prompts like git grep, file finders, etc.
|
|
204
|
+
|
|
205
|
+
## Writing Your Own Plugin
|
|
206
|
+
|
|
207
|
+
1. Create a `.lua` file in the plugins directory
|
|
208
|
+
2. Use the API functions above to add functionality
|
|
209
|
+
3. The plugin will be automatically loaded when the editor starts
|
|
210
|
+
|
|
211
|
+
Example template:
|
|
212
|
+
|
|
213
|
+
```lua
|
|
214
|
+
-- My Custom Plugin
|
|
215
|
+
|
|
216
|
+
-- Register commands
|
|
217
|
+
editor.register_command({
|
|
218
|
+
name = "My Custom Command",
|
|
219
|
+
description = "Does something cool",
|
|
220
|
+
action = "none",
|
|
221
|
+
contexts = {"normal"}
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
-- Add hooks if needed
|
|
225
|
+
editor.on("after-file-save", function(args)
|
|
226
|
+
editor.set_status("File saved - plugin notified!")
|
|
227
|
+
return true
|
|
228
|
+
end)
|
|
229
|
+
|
|
230
|
+
-- Initialization message
|
|
231
|
+
print("My custom plugin loaded")
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Testing Plugins
|
|
235
|
+
|
|
236
|
+
Currently, plugins are unit tested through the plugin_manager tests. Integration tests will be added in a future update.
|
|
237
|
+
|
|
238
|
+
## Future Enhancements
|
|
239
|
+
|
|
240
|
+
Planned features:
|
|
241
|
+
- Buffer query API (get content, cursor position, etc.)
|
|
242
|
+
- Popup API (custom dialogs, menus)
|
|
243
|
+
- Async task spawning (for git operations, external commands)
|
|
244
|
+
- More comprehensive hook system
|
|
245
|
+
- WASM plugin support for multi-language plugins
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/// <reference path="../../types/fresh.d.ts" />
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Async Process Demo Plugin
|
|
5
|
+
* Demonstrates spawning external processes asynchronously with async/await
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Git status
|
|
9
|
+
globalThis.async_git_status = async function(): Promise<void> {
|
|
10
|
+
editor.setStatus("Running git status...");
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const result = await editor.spawnProcess("git", ["status", "--short"]);
|
|
14
|
+
if (result.exit_code === 0) {
|
|
15
|
+
if (result.stdout === "" || result.stdout === "\n") {
|
|
16
|
+
editor.setStatus("Git: Working tree clean");
|
|
17
|
+
} else {
|
|
18
|
+
const count = result.stdout.split("\n").filter(line => line.trim()).length;
|
|
19
|
+
editor.setStatus(`Git: ${count} files changed`);
|
|
20
|
+
}
|
|
21
|
+
} else {
|
|
22
|
+
editor.setStatus(`Git status failed: ${result.stderr}`);
|
|
23
|
+
}
|
|
24
|
+
} catch (e) {
|
|
25
|
+
editor.setStatus(`Git status error: ${e}`);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
editor.registerCommand(
|
|
30
|
+
"Async Demo: Git Status",
|
|
31
|
+
"Run git status and show output",
|
|
32
|
+
"async_git_status",
|
|
33
|
+
"normal"
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// Current directory
|
|
37
|
+
globalThis.async_pwd = async function(): Promise<void> {
|
|
38
|
+
try {
|
|
39
|
+
const result = await editor.spawnProcess("pwd");
|
|
40
|
+
if (result.exit_code === 0) {
|
|
41
|
+
const dir = result.stdout.trim();
|
|
42
|
+
editor.setStatus(`Current directory: ${dir}`);
|
|
43
|
+
} else {
|
|
44
|
+
editor.setStatus("pwd failed");
|
|
45
|
+
}
|
|
46
|
+
} catch (e) {
|
|
47
|
+
editor.setStatus(`pwd error: ${e}`);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
editor.registerCommand(
|
|
52
|
+
"Async Demo: Current Directory",
|
|
53
|
+
"Show current directory using pwd",
|
|
54
|
+
"async_pwd",
|
|
55
|
+
"normal"
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// List files
|
|
59
|
+
globalThis.async_ls = async function(): Promise<void> {
|
|
60
|
+
editor.setStatus("Listing files...");
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const result = await editor.spawnProcess("ls", ["-1"]);
|
|
64
|
+
if (result.exit_code === 0) {
|
|
65
|
+
const count = result.stdout.split("\n").filter(line => line.trim()).length;
|
|
66
|
+
editor.setStatus(`Found ${count} files/directories`);
|
|
67
|
+
} else {
|
|
68
|
+
editor.setStatus("ls failed");
|
|
69
|
+
}
|
|
70
|
+
} catch (e) {
|
|
71
|
+
editor.setStatus(`ls error: ${e}`);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
editor.registerCommand(
|
|
76
|
+
"Async Demo: List Files",
|
|
77
|
+
"List files in current directory",
|
|
78
|
+
"async_ls",
|
|
79
|
+
"normal"
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// Git branch
|
|
83
|
+
globalThis.async_git_branch = async function(): Promise<void> {
|
|
84
|
+
try {
|
|
85
|
+
const result = await editor.spawnProcess("git", ["branch", "--show-current"]);
|
|
86
|
+
if (result.exit_code === 0) {
|
|
87
|
+
const branch = result.stdout.trim();
|
|
88
|
+
if (branch !== "") {
|
|
89
|
+
editor.setStatus(`Git branch: ${branch}`);
|
|
90
|
+
} else {
|
|
91
|
+
editor.setStatus("Not on any branch (detached HEAD)");
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
editor.setStatus("Not a git repository");
|
|
95
|
+
}
|
|
96
|
+
} catch (e) {
|
|
97
|
+
editor.setStatus(`Git branch error: ${e}`);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
editor.registerCommand(
|
|
102
|
+
"Async Demo: Git Branch",
|
|
103
|
+
"Show current git branch",
|
|
104
|
+
"async_git_branch",
|
|
105
|
+
"normal"
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
// Echo test
|
|
109
|
+
globalThis.async_echo = async function(): Promise<void> {
|
|
110
|
+
try {
|
|
111
|
+
const result = await editor.spawnProcess("echo", ["Hello from async process!"]);
|
|
112
|
+
editor.setStatus(`Echo output: ${result.stdout.trim()}`);
|
|
113
|
+
} catch (e) {
|
|
114
|
+
editor.setStatus(`Echo error: ${e}`);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
editor.registerCommand(
|
|
119
|
+
"Async Demo: Echo Test",
|
|
120
|
+
"Test with simple echo command",
|
|
121
|
+
"async_echo",
|
|
122
|
+
"normal"
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// With working directory
|
|
126
|
+
globalThis.async_with_cwd = async function(): Promise<void> {
|
|
127
|
+
try {
|
|
128
|
+
const result = await editor.spawnProcess("pwd", [], "/tmp");
|
|
129
|
+
const dir = result.stdout.trim();
|
|
130
|
+
editor.setStatus(`Working dir was: ${dir}`);
|
|
131
|
+
} catch (e) {
|
|
132
|
+
editor.setStatus(`pwd error: ${e}`);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
editor.registerCommand(
|
|
137
|
+
"Async Demo: With Working Dir",
|
|
138
|
+
"Run command in /tmp directory",
|
|
139
|
+
"async_with_cwd",
|
|
140
|
+
"normal"
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
// Error handling
|
|
144
|
+
globalThis.async_error = async function(): Promise<void> {
|
|
145
|
+
try {
|
|
146
|
+
const result = await editor.spawnProcess("this_command_does_not_exist");
|
|
147
|
+
if (result.exit_code !== 0) {
|
|
148
|
+
editor.setStatus(`Command failed (as expected): exit ${result.exit_code}`);
|
|
149
|
+
} else {
|
|
150
|
+
editor.setStatus("Unexpected success");
|
|
151
|
+
}
|
|
152
|
+
} catch (e) {
|
|
153
|
+
editor.setStatus(`Command failed with error: ${e}`);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
editor.registerCommand(
|
|
158
|
+
"Async Demo: Error Handling",
|
|
159
|
+
"Demonstrate error handling with non-existent command",
|
|
160
|
+
"async_error",
|
|
161
|
+
"normal"
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
editor.setStatus("Async Demo plugin loaded! Try the 'Async Demo' commands.");
|
|
165
|
+
editor.debug("Async Demo plugin initialized with native async/await support");
|