@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,298 @@
1
+ /// <reference path="../types/fresh.d.ts" />
2
+
3
+ /**
4
+ * Git Find File Plugin
5
+ *
6
+ * Provides interactive file finding functionality with fuzzy search
7
+ * for git-tracked files. Uses the prompt API for interactive selection.
8
+ */
9
+
10
+ // State management
11
+ let allFiles: string[] = [];
12
+ let filteredFiles: string[] = [];
13
+ let isLoading = false;
14
+
15
+ // Simple fuzzy filter function
16
+ function fuzzyMatch(str: string, pattern: string): boolean {
17
+ if (pattern === "") {
18
+ return true;
19
+ }
20
+
21
+ str = str.toLowerCase();
22
+ pattern = pattern.toLowerCase();
23
+
24
+ let strIdx = 0;
25
+ let patIdx = 0;
26
+
27
+ while (strIdx < str.length && patIdx < pattern.length) {
28
+ if (str[strIdx] === pattern[patIdx]) {
29
+ patIdx++;
30
+ }
31
+ strIdx++;
32
+ }
33
+
34
+ return patIdx >= pattern.length;
35
+ }
36
+
37
+ // Score a fuzzy match (higher is better)
38
+ function fuzzyScore(str: string, pattern: string): number {
39
+ if (pattern === "") return 0;
40
+
41
+ str = str.toLowerCase();
42
+ pattern = pattern.toLowerCase();
43
+
44
+ let score = 0;
45
+ let strIdx = 0;
46
+ let patIdx = 0;
47
+ let consecutiveMatches = 0;
48
+ let lastMatchIdx = -1;
49
+
50
+ while (strIdx < str.length && patIdx < pattern.length) {
51
+ if (str[strIdx] === pattern[patIdx]) {
52
+ // Bonus for consecutive matches
53
+ if (lastMatchIdx === strIdx - 1) {
54
+ consecutiveMatches++;
55
+ score += consecutiveMatches * 10;
56
+ } else {
57
+ consecutiveMatches = 1;
58
+ score += 1;
59
+ }
60
+
61
+ // Bonus for matching at start of path segments
62
+ if (strIdx === 0 || str[strIdx - 1] === "/" || str[strIdx - 1] === "_" || str[strIdx - 1] === "-") {
63
+ score += 15;
64
+ }
65
+
66
+ // Bonus for matching filename (after last /)
67
+ const lastSlash = str.lastIndexOf("/");
68
+ if (strIdx > lastSlash) {
69
+ score += 5;
70
+ }
71
+
72
+ lastMatchIdx = strIdx;
73
+ patIdx++;
74
+ }
75
+ strIdx++;
76
+ }
77
+
78
+ // Penalty for longer paths
79
+ score -= str.length * 0.1;
80
+
81
+ return patIdx >= pattern.length ? score : -1;
82
+ }
83
+
84
+ // Filter and sort files by query using fuzzy matching
85
+ function filterFiles(files: string[], query: string): string[] {
86
+ if (query === "" || query.trim() === "") {
87
+ // Return first 100 files for empty query
88
+ return files.slice(0, 100);
89
+ }
90
+
91
+ const scored: Array<{ file: string; score: number }> = [];
92
+
93
+ for (const file of files) {
94
+ const score = fuzzyScore(file, query);
95
+ if (score > 0) {
96
+ scored.push({ file, score });
97
+ }
98
+
99
+ // Stop early if we have enough high-quality matches
100
+ if (scored.length >= 500) {
101
+ break;
102
+ }
103
+ }
104
+
105
+ // Sort by score descending
106
+ scored.sort((a, b) => b.score - a.score);
107
+
108
+ // Return top 100 results
109
+ return scored.slice(0, 100).map((s) => s.file);
110
+ }
111
+
112
+ // Load git-tracked files asynchronously
113
+ async function loadGitFiles(): Promise<void> {
114
+ if (isLoading) {
115
+ return;
116
+ }
117
+
118
+ isLoading = true;
119
+ editor.setStatus("Loading git files...");
120
+
121
+ try {
122
+ const result = await editor.spawnProcess("git", ["ls-files"]);
123
+
124
+ if (result.exit_code === 0) {
125
+ allFiles = result.stdout.split("\n").filter((line) => line.trim() !== "");
126
+
127
+ editor.debug(`Loaded ${allFiles.length} git-tracked files`);
128
+ editor.setStatus(`Git Find File: ${allFiles.length} files indexed`);
129
+ } else {
130
+ editor.debug(`Failed to load git files: ${result.stderr}`);
131
+ editor.setStatus(`Error loading git files: ${result.stderr}`);
132
+ allFiles = [];
133
+ }
134
+ } catch (e) {
135
+ editor.debug(`Exception loading git files: ${e}`);
136
+ editor.setStatus("Failed to load git files");
137
+ allFiles = [];
138
+ } finally {
139
+ isLoading = false;
140
+ }
141
+ }
142
+
143
+ // Convert filtered files to prompt suggestions
144
+ function filesToSuggestions(files: string[]): PromptSuggestion[] {
145
+ return files.map((file) => {
146
+ return {
147
+ text: file,
148
+ description: undefined,
149
+ value: file,
150
+ disabled: false,
151
+ };
152
+ });
153
+ }
154
+
155
+ // Global function to start file finder
156
+ globalThis.start_git_find_file = async function (): Promise<void> {
157
+ // Load files if not already loaded
158
+ if (allFiles.length === 0 && !isLoading) {
159
+ await loadGitFiles();
160
+ }
161
+
162
+ if (allFiles.length === 0) {
163
+ editor.setStatus("No git-tracked files found");
164
+ return;
165
+ }
166
+
167
+ // Clear previous results
168
+ filteredFiles = [];
169
+
170
+ // Start the prompt
171
+ editor.startPrompt("Find file: ", "git-find-file");
172
+
173
+ // Show initial suggestions (first 100 files)
174
+ const initial = filterFiles(allFiles, "");
175
+ filteredFiles = initial;
176
+ editor.setPromptSuggestions(filesToSuggestions(initial));
177
+ editor.setStatus(`${allFiles.length} files available - type to filter`);
178
+ };
179
+
180
+ // React to prompt input changes
181
+ globalThis.onGitFindFilePromptChanged = function (args: { prompt_type: string; input: string }): boolean {
182
+ if (args.prompt_type !== "git-find-file") {
183
+ return true; // Not our prompt
184
+ }
185
+
186
+ const query = args.input;
187
+
188
+ // Filter files based on query
189
+ const matches = filterFiles(allFiles, query);
190
+ filteredFiles = matches;
191
+
192
+ // Update suggestions
193
+ editor.setPromptSuggestions(filesToSuggestions(matches));
194
+
195
+ // Update status
196
+ if (matches.length > 0) {
197
+ if (query.trim() === "") {
198
+ editor.setStatus(`Showing first ${matches.length} of ${allFiles.length} files`);
199
+ } else {
200
+ editor.setStatus(`Found ${matches.length} files matching "${query}"`);
201
+ }
202
+ } else {
203
+ editor.setStatus(`No files matching "${query}"`);
204
+ }
205
+
206
+ return true;
207
+ };
208
+
209
+ // Handle prompt confirmation (user pressed Enter)
210
+ globalThis.onGitFindFilePromptConfirmed = function (args: {
211
+ prompt_type: string;
212
+ selected_index: number | null;
213
+ input: string;
214
+ }): boolean {
215
+ if (args.prompt_type !== "git-find-file") {
216
+ return true; // Not our prompt
217
+ }
218
+
219
+ editor.debug(`git-find-file confirmed: selected_index=${args.selected_index}, input=${args.input}`);
220
+
221
+ // Check if user selected a suggestion
222
+ if (args.selected_index !== null && filteredFiles[args.selected_index]) {
223
+ const selectedFile = filteredFiles[args.selected_index];
224
+
225
+ editor.debug(`Opening file: ${selectedFile}`);
226
+
227
+ // Open the file at line 1
228
+ editor.openFile(selectedFile, 1, 1);
229
+ editor.setStatus(`Opened ${selectedFile}`);
230
+ } else if (args.input.trim() !== "") {
231
+ // Try to open input directly if it's a valid file path
232
+ const inputFile = args.input.trim();
233
+
234
+ // Check if the exact input matches any file
235
+ if (allFiles.includes(inputFile)) {
236
+ editor.openFile(inputFile, 1, 1);
237
+ editor.setStatus(`Opened ${inputFile}`);
238
+ } else {
239
+ editor.setStatus(`File not found: ${inputFile}`);
240
+ }
241
+ } else {
242
+ editor.setStatus("No file selected");
243
+ }
244
+
245
+ return true;
246
+ };
247
+
248
+ // Handle prompt cancellation (user pressed Escape)
249
+ globalThis.onGitFindFilePromptCancelled = function (args: { prompt_type: string }): boolean {
250
+ if (args.prompt_type !== "git-find-file") {
251
+ return true; // Not our prompt
252
+ }
253
+
254
+ // Clear results
255
+ filteredFiles = [];
256
+ editor.setStatus("File finder cancelled");
257
+
258
+ return true;
259
+ };
260
+
261
+ // Register event handlers
262
+ editor.on("prompt_changed", "onGitFindFilePromptChanged");
263
+ editor.on("prompt_confirmed", "onGitFindFilePromptConfirmed");
264
+ editor.on("prompt_cancelled", "onGitFindFilePromptCancelled");
265
+
266
+ // Reload git files command
267
+ globalThis.git_reload_files = async function (): Promise<void> {
268
+ allFiles = [];
269
+ await loadGitFiles();
270
+ };
271
+
272
+ // Show file count command
273
+ globalThis.git_file_count = function (): void {
274
+ if (allFiles.length === 0) {
275
+ editor.setStatus("No git files loaded. Use 'Git Find File: Reload' to index files.");
276
+ } else {
277
+ editor.setStatus(`${allFiles.length} git-tracked files indexed`);
278
+ }
279
+ };
280
+
281
+ // Register commands
282
+ editor.registerCommand("Git Find File", "Find and open a git-tracked file", "start_git_find_file", "normal");
283
+
284
+ editor.registerCommand(
285
+ "Git Find File: Reload",
286
+ "Reload the git file index",
287
+ "git_reload_files",
288
+ "normal"
289
+ );
290
+
291
+ editor.registerCommand("Git Find File: Count", "Show number of indexed git files", "git_file_count", "normal");
292
+
293
+ // Note: We don't load git files on plugin init because spawning processes requires async context
294
+ // Files will be loaded lazily on first use
295
+
296
+ editor.debug("Git Find File plugin loaded successfully (TypeScript)");
297
+ editor.debug("Usage: Call start_git_find_file() or use command palette 'Git Find File'");
298
+ editor.setStatus("Git Find File plugin ready");
@@ -0,0 +1,188 @@
1
+ /// <reference path="../types/fresh.d.ts" />
2
+
3
+ /**
4
+ * Git Grep Plugin
5
+ *
6
+ * Provides interactive git grep functionality with live search results.
7
+ */
8
+
9
+ interface GrepMatch {
10
+ file: string;
11
+ line: number;
12
+ column: number;
13
+ content: string;
14
+ }
15
+
16
+ // State management
17
+ let gitGrepResults: GrepMatch[] = [];
18
+
19
+ // Parse git grep output line
20
+ // Format: file:line:column:content
21
+ function parseGitGrepLine(line: string): GrepMatch | null {
22
+ const match = line.match(/^([^:]+):(\d+):(\d+):(.*)$/);
23
+ if (match) {
24
+ return {
25
+ file: match[1],
26
+ line: parseInt(match[2], 10),
27
+ column: parseInt(match[3], 10),
28
+ content: match[4].trimStart(),
29
+ };
30
+ }
31
+ return null;
32
+ }
33
+
34
+ // Parse git grep output into suggestions
35
+ function parseGitGrepOutput(stdout: string): {
36
+ results: GrepMatch[];
37
+ suggestions: PromptSuggestion[];
38
+ } {
39
+ const results: GrepMatch[] = [];
40
+ const suggestions: PromptSuggestion[] = [];
41
+
42
+ for (const line of stdout.split("\n")) {
43
+ if (!line.trim()) continue;
44
+ const match = parseGitGrepLine(line);
45
+ if (match) {
46
+ results.push(match);
47
+ suggestions.push({
48
+ text: `${match.file}:${match.line}:${match.column}`,
49
+ description: match.content,
50
+ value: `${match.file}:${match.line}:${match.column}`,
51
+ disabled: false,
52
+ });
53
+
54
+ // Limit to 100 results for performance
55
+ if (results.length >= 100) {
56
+ break;
57
+ }
58
+ }
59
+ }
60
+
61
+ return { results, suggestions };
62
+ }
63
+
64
+ // Global function to start git grep
65
+ globalThis.start_git_grep = function(): void {
66
+ // Clear previous results
67
+ gitGrepResults = [];
68
+
69
+ // Start the prompt
70
+ editor.startPrompt("Git grep: ", "git-grep");
71
+ editor.setStatus("Type to search...");
72
+ };
73
+
74
+ // React to prompt input changes
75
+ globalThis.onGitGrepPromptChanged = function(args: {
76
+ prompt_type: string;
77
+ input: string;
78
+ }): boolean {
79
+ if (args.prompt_type !== "git-grep") {
80
+ return true; // Not our prompt
81
+ }
82
+
83
+ const query = args.input;
84
+
85
+ // Don't search for empty queries
86
+ if (!query || query.trim() === "") {
87
+ editor.setPromptSuggestions([]);
88
+ return true;
89
+ }
90
+
91
+ // Spawn git grep asynchronously
92
+ editor.spawnProcess("git", ["grep", "-n", "--column", "-I", "--", query])
93
+ .then((result) => {
94
+ if (result.exit_code === 0) {
95
+ // Parse results and update suggestions
96
+ const { results, suggestions } = parseGitGrepOutput(result.stdout);
97
+ gitGrepResults = results;
98
+
99
+ // Update prompt with suggestions
100
+ editor.setPromptSuggestions(suggestions);
101
+
102
+ // Update status
103
+ if (results.length > 0) {
104
+ editor.setStatus(`Found ${results.length} matches`);
105
+ } else {
106
+ editor.setStatus("No matches found");
107
+ }
108
+ } else if (result.exit_code === 1) {
109
+ // No matches found (git grep returns 1)
110
+ gitGrepResults = [];
111
+ editor.setPromptSuggestions([]);
112
+ editor.setStatus("No matches found");
113
+ } else {
114
+ // Error occurred
115
+ editor.setStatus(`Git grep error: ${result.stderr}`);
116
+ }
117
+ })
118
+ .catch((e) => {
119
+ editor.setStatus(`Git grep error: ${e}`);
120
+ });
121
+
122
+ return true;
123
+ };
124
+
125
+ // Handle prompt confirmation (user pressed Enter)
126
+ globalThis.onGitGrepPromptConfirmed = function(args: {
127
+ prompt_type: string;
128
+ selected_index: number | null;
129
+ input: string;
130
+ }): boolean {
131
+ if (args.prompt_type !== "git-grep") {
132
+ return true; // Not our prompt
133
+ }
134
+
135
+ editor.debug(
136
+ `prompt-confirmed: selected_index=${args.selected_index}, num_results=${gitGrepResults.length}`
137
+ );
138
+
139
+ // Check if user selected a suggestion
140
+ if (args.selected_index !== null && gitGrepResults[args.selected_index]) {
141
+ const selected = gitGrepResults[args.selected_index];
142
+
143
+ editor.debug(`Opening file: ${selected.file}:${selected.line}:${selected.column}`);
144
+
145
+ // Open the file at the specific location
146
+ editor.openFile(selected.file, selected.line, selected.column);
147
+ editor.setStatus(`Opened ${selected.file}:${selected.line}:${selected.column}`);
148
+ } else {
149
+ // No selection
150
+ editor.debug("No file selected - selected_index is null or out of bounds");
151
+ editor.setStatus("No file selected");
152
+ }
153
+
154
+ return true;
155
+ };
156
+
157
+ // Handle prompt cancellation (user pressed Escape)
158
+ globalThis.onGitGrepPromptCancelled = function(args: {
159
+ prompt_type: string;
160
+ }): boolean {
161
+ if (args.prompt_type !== "git-grep") {
162
+ return true; // Not our prompt
163
+ }
164
+
165
+ // Clear results
166
+ gitGrepResults = [];
167
+ editor.setStatus("Git grep cancelled");
168
+
169
+ return true;
170
+ };
171
+
172
+ // Register event handlers
173
+ editor.on("prompt_changed", "onGitGrepPromptChanged");
174
+ editor.on("prompt_confirmed", "onGitGrepPromptConfirmed");
175
+ editor.on("prompt_cancelled", "onGitGrepPromptCancelled");
176
+
177
+ // Register command
178
+ editor.registerCommand(
179
+ "Git Grep",
180
+ "Search for text in git-tracked files",
181
+ "start_git_grep",
182
+ "normal"
183
+ );
184
+
185
+ // Log that plugin loaded successfully
186
+ editor.debug("Git Grep plugin loaded successfully (TypeScript)");
187
+ editor.debug("Usage: Call start_git_grep() or use command palette 'Git Grep'");
188
+ editor.setStatus("Git Grep plugin ready");