@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.
Files changed (38) hide show
  1. package/.gitignore +2 -0
  2. package/LICENSE +117 -0
  3. package/README.md +54 -0
  4. package/binary-install.js +212 -0
  5. package/binary.js +126 -0
  6. package/install.js +4 -0
  7. package/npm-shrinkwrap.json +900 -0
  8. package/package.json +100 -0
  9. package/plugins/README.md +121 -0
  10. package/plugins/clangd_support.md +20 -0
  11. package/plugins/clangd_support.ts +323 -0
  12. package/plugins/color_highlighter.ts +302 -0
  13. package/plugins/diagnostics_panel.ts +308 -0
  14. package/plugins/examples/README.md +245 -0
  15. package/plugins/examples/async_demo.ts +165 -0
  16. package/plugins/examples/bookmarks.ts +329 -0
  17. package/plugins/examples/buffer_query_demo.ts +110 -0
  18. package/plugins/examples/git_grep.ts +262 -0
  19. package/plugins/examples/hello_world.ts +93 -0
  20. package/plugins/examples/virtual_buffer_demo.ts +116 -0
  21. package/plugins/find_references.ts +357 -0
  22. package/plugins/git_find_file.ts +298 -0
  23. package/plugins/git_grep.ts +188 -0
  24. package/plugins/git_log.ts +1283 -0
  25. package/plugins/lib/fresh.d.ts +849 -0
  26. package/plugins/lib/index.ts +24 -0
  27. package/plugins/lib/navigation-controller.ts +214 -0
  28. package/plugins/lib/panel-manager.ts +218 -0
  29. package/plugins/lib/types.ts +72 -0
  30. package/plugins/lib/virtual-buffer-factory.ts +158 -0
  31. package/plugins/manual_help.ts +243 -0
  32. package/plugins/markdown_compose.ts +1207 -0
  33. package/plugins/merge_conflict.ts +1811 -0
  34. package/plugins/path_complete.ts +163 -0
  35. package/plugins/search_replace.ts +481 -0
  36. package/plugins/todo_highlighter.ts +204 -0
  37. package/plugins/welcome.ts +74 -0
  38. 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");