@fresh-editor/fresh-editor 0.3.5 → 0.3.6
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 +56 -0
- package/README.md +9 -2
- package/package.json +1 -1
- package/plugins/config-schema.json +7 -1
- package/plugins/dashboard.ts +16 -93
- package/plugins/git_log.ts +196 -224
- package/plugins/goto_with_selection.i18n.json +58 -0
- package/plugins/goto_with_selection.ts +17 -0
- package/plugins/lib/fresh.d.ts +606 -9
- package/plugins/lib/index.ts +34 -0
- package/plugins/lib/widgets.ts +796 -0
- package/plugins/live_diff.ts +324 -29
- package/plugins/orchestrator.ts +1685 -0
- package/plugins/pkg.ts +234 -53
- package/plugins/rust-lsp.ts +58 -40
- package/plugins/schemas/theme.schema.json +4 -0
- package/plugins/search_replace.ts +780 -517
- package/plugins/theme_editor.i18n.json +84 -0
- package/plugins/theme_editor.ts +30 -5
- package/plugins/tsconfig.json +2 -0
- package/plugins/vi_mode.ts +38 -17
- package/themes/terminal.json +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,61 @@
|
|
|
1
1
|
# Release Notes
|
|
2
2
|
|
|
3
|
+
## 0.3.6
|
|
4
|
+
|
|
5
|
+
This version includes a major internal refactoring to support multiple windows in a single Fresh process. The work will be used to add a multi-window orchestrator in a future version.
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
* **Go to line with selection** (#1764, thanks @PavelLoparev!): New action extends the selection from cursor to target line. Supports absolute and relative (`+10`) jumps.
|
|
10
|
+
|
|
11
|
+
* **File explorer "follow active buffer"** (#1569, #1803, thanks @ko3n1g!): New `file_explorer.follow_active_buffer` setting (default off). When on, switching tabs expands and highlights the corresponding file without stealing focus.
|
|
12
|
+
|
|
13
|
+
* **Live Diff**: Near-identical lines now render as modified lines, with new words decorated as underline (instead of two separate add/remove rows). Deletion virtual lines get gutter markers. Fixed live diff colors in `terminal` theme.
|
|
14
|
+
|
|
15
|
+
* **Auto-start `vi_mode`** (#1086, reported by @jmcblane): Vi mode plugin now exposes `toggle / enable / disable` so `init.ts` can flip vi mode at startup. Add this to your `init.ts` to enable: `editor.getPluginApi("vi-mode")?.enable();`.
|
|
16
|
+
|
|
17
|
+
In the future I plan to add a way for plugins to register custom config sections in the Settings UI, and then enabling vi-mode auto-start will be available through the UI.
|
|
18
|
+
|
|
19
|
+
### Improvements
|
|
20
|
+
|
|
21
|
+
* **openSUSE install instructions** (#1897, thanks @ilmanzo!) added to the README.
|
|
22
|
+
|
|
23
|
+
* **Quick Open ranking**: Matches at the start of a path segment now beat arbitrary substring hits — `ts` surfaces `tsconfig.json` and `tsc/...` before random `.ts` files.
|
|
24
|
+
|
|
25
|
+
* **Search & Replace no longer freezes** on result sets in the thousands.
|
|
26
|
+
|
|
27
|
+
* **Rust code actions** (#1915): `rust-analyzer` now returns `WorkspaceEdit`-based assists like "Fill struct fields" instead of "No code actions available" — Fresh wasn't advertising the capability.
|
|
28
|
+
|
|
29
|
+
* **Status-bar indicator menus**: Clicking another indicator's icon dismisses the currently-open menu instead of stacking a second one on top.
|
|
30
|
+
|
|
31
|
+
* **LSP-Servers popup is extensible**: Plugins (starting with the bundled `rust-lsp`) can contribute their own rows.
|
|
32
|
+
|
|
33
|
+
* **`terminal` built-in theme** (#1914): Diff backgrounds no longer collide with syntax foregrounds, so keywords on deletion lines and strings on addition lines stay readable on terminals (e.g. xfce-terminal) where each ANSI palette index renders identically for fg and bg.
|
|
34
|
+
|
|
35
|
+
* **Dashboard** matches the editor background and no longer slides in.
|
|
36
|
+
|
|
37
|
+
* **AppImage install script** has error handling and a `/tmp` fallback so it no longer silently fails when the target directory isn't writable.
|
|
38
|
+
|
|
39
|
+
### Bug Fixes
|
|
40
|
+
|
|
41
|
+
* **Crash on startup from a corrupt workspace** (#1939, reported by @zdooder): Restoring a session whose persisted split pointed at a buffer no longer in the buffer map crashed in `render.rs`. The active-buffer pointer is now validated and falls back to a live buffer if it's stale.
|
|
42
|
+
|
|
43
|
+
* **Completion popup + multicursor** (#1901, reported by @dtwilliamson): Typing a word character or `Backspace` while the popup was open only edited the primary cursor — secondary cursors silently went out of sync after the first keystroke. Accepting a completion now also applies at every cursor.
|
|
44
|
+
|
|
45
|
+
* **LSP indicator click** (#1941): Bugs fixed: plugin popup stacked under the built-in one; plugin popup ignored the active theme's `popup_bg`; state stayed stale after the LSP process was killed externally; "Disable LSP" didn't actually stop the running server; repeated clicks stacked further popups.
|
|
46
|
+
|
|
47
|
+
* **Scrollbar theme overrides** (#1554, reported by @klonuo): User themes setting `ui.scrollbar_track_fg`, `ui.scrollbar_track_hover_fg`, or `ui.scrollbar_thumb_hover_fg` had their overrides silently dropped.
|
|
48
|
+
|
|
49
|
+
* **Utility dock routing** (#1932): `Ctrl+P` → filename now opens the file in the editor area instead of inside the bottom dock when the dock has focus. The split tab-bar `□` button now maximizes the split you clicked, not the active one.
|
|
50
|
+
|
|
51
|
+
* **Tab drag**: Dropping a tab onto a fresh split (e.g. a Search & Replace result row) no longer panics on the next keystroke.
|
|
52
|
+
|
|
53
|
+
* **File explorer**: A directory whose `.gitignore` filters out all its contents (e.g. `build/` with `*` inside) stays visible when expanded.
|
|
54
|
+
|
|
55
|
+
* **Live Diff visual-line motion**: `Down`/`Up` no longer freezes when traversing a deletion block that starts with a blank line.
|
|
56
|
+
|
|
57
|
+
* **`S-Tab` plugin bindings** now register correctly (terminals deliver Shift+Tab as `BackTab`).
|
|
58
|
+
|
|
3
59
|
## 0.3.5
|
|
4
60
|
|
|
5
61
|
### Improvements
|
package/README.md
CHANGED
|
@@ -65,7 +65,8 @@ Or, pick your preferred method:
|
|
|
65
65
|
| Windows | [winget](#windows-winget) |
|
|
66
66
|
| Arch Linux | [AUR](#arch-linux-aur) |
|
|
67
67
|
| Debian/Ubuntu | [.deb](#debianubuntu-deb) |
|
|
68
|
-
| Fedora/RHEL | [.rpm](#
|
|
68
|
+
| Fedora/RHEL | [.rpm](#fedorarhel-rpm), [Terra](https://terra.fyralabs.com/) |
|
|
69
|
+
| OpenSUSE | [zypper](#opensuse-zypper), [.rpm](#fedorarhel-rpm) |
|
|
69
70
|
| FreeBSD | [ports / pkg](https://www.freshports.org/editors/fresh) |
|
|
70
71
|
| Gentoo | [GURU](#gentoo-guru) |
|
|
71
72
|
| Linux (any distro) | [AppImage](#appimage), [Flatpak](#flatpak) |
|
|
@@ -133,7 +134,7 @@ curl -sL $(curl -s https://api.github.com/repos/sinelaw/fresh/releases/latest |
|
|
|
133
134
|
|
|
134
135
|
Or download the `.deb` file manually from the [releases page](https://github.com/sinelaw/fresh/releases).
|
|
135
136
|
|
|
136
|
-
### Fedora/RHEL
|
|
137
|
+
### Fedora/RHEL (.rpm)
|
|
137
138
|
|
|
138
139
|
Download and install the latest release:
|
|
139
140
|
|
|
@@ -143,6 +144,12 @@ curl -sL $(curl -s https://api.github.com/repos/sinelaw/fresh/releases/latest |
|
|
|
143
144
|
|
|
144
145
|
Or download the `.rpm` file manually from the [releases page](https://github.com/sinelaw/fresh/releases).
|
|
145
146
|
|
|
147
|
+
### OpenSUSE (zypper)
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
zypper install fresh-editor
|
|
151
|
+
```
|
|
152
|
+
|
|
146
153
|
### Gentoo ([GURU](https://wiki.gentoo.org/wiki/Project:GURU))
|
|
147
154
|
|
|
148
155
|
Enable the repository as read in [Project:GURU/Information for End Users](https://wiki.gentoo.org/wiki/Project:GURU/Information_for_End_Users) then emerge the package:
|
package/package.json
CHANGED
|
@@ -133,7 +133,8 @@
|
|
|
133
133
|
"width": "30%",
|
|
134
134
|
"preview_tabs": true,
|
|
135
135
|
"side": "left",
|
|
136
|
-
"auto_open_on_last_buffer_close": true
|
|
136
|
+
"auto_open_on_last_buffer_close": true,
|
|
137
|
+
"follow_active_buffer": false
|
|
137
138
|
}
|
|
138
139
|
},
|
|
139
140
|
"file_browser": {
|
|
@@ -952,6 +953,11 @@
|
|
|
952
953
|
"description": "Automatically focus the file explorer when the last buffer is\nclosed. Set to `false` for a \"blank workspace\" workflow where\nnothing opens automatically and the user explicitly invokes the\nfile explorer (e.g. via keybinding or command palette).\nDefault: true",
|
|
953
954
|
"type": "boolean",
|
|
954
955
|
"default": true
|
|
956
|
+
},
|
|
957
|
+
"follow_active_buffer": {
|
|
958
|
+
"description": "When the file explorer sidebar is open, automatically expand the\ntree and highlight the file that corresponds to the active buffer\nwhenever you switch tabs. Set to `true` to keep the explorer\nselection in sync with the active tab.\nDefault: false",
|
|
959
|
+
"type": "boolean",
|
|
960
|
+
"default": false
|
|
955
961
|
}
|
|
956
962
|
}
|
|
957
963
|
},
|
package/plugins/dashboard.ts
CHANGED
|
@@ -201,11 +201,6 @@ type RegisteredSection = {
|
|
|
201
201
|
let dashboardBufferId: number | null = null;
|
|
202
202
|
let fetchToken = 0; // bumped each open; late fetches from a prior open no-op.
|
|
203
203
|
|
|
204
|
-
// Id of the in-flight slide-in, so we can cancel it when starting a
|
|
205
|
-
// new one (on content change) or when the dashboard is closed
|
|
206
|
-
// mid-slide. Null once the animation settles or is cleared.
|
|
207
|
-
let activeAnimationId: number | null = null;
|
|
208
|
-
|
|
209
204
|
// Hash of all entries at the last paint (post-focus-highlight too —
|
|
210
205
|
// it's what ultimately lands in the virtual buffer). Used to decide
|
|
211
206
|
// whether setVirtualBufferContent needs to run at all: identical
|
|
@@ -213,43 +208,11 @@ let activeAnimationId: number | null = null;
|
|
|
213
208
|
// round-trip entirely.
|
|
214
209
|
let lastPaintedFullKey: string | null = null;
|
|
215
210
|
|
|
216
|
-
// Hash of the entries with the clock stamp stripped. Animations only
|
|
217
|
-
// fire when THIS hash changes, so the 1 Hz clock tick on the top
|
|
218
|
-
// frame updates in place without re-sliding the whole dashboard.
|
|
219
|
-
// Keyboard focus changes don't move this hash either (the hash is
|
|
220
|
-
// taken before the focus overlay is laid on top), so Tab/Shift-Tab
|
|
221
|
-
// pan the highlight without re-animating.
|
|
222
|
-
let lastPaintedStructuralKey: string | null = null;
|
|
223
|
-
|
|
224
211
|
// focusedIndex the last successful setVirtualBufferContent ran with.
|
|
225
|
-
// Paired with the
|
|
226
|
-
//
|
|
212
|
+
// Paired with the key above so we can tell "focus moved but content
|
|
213
|
+
// is the same" and still update VB for the highlight.
|
|
227
214
|
let lastPaintedFocusedIndex = -1;
|
|
228
215
|
|
|
229
|
-
// Matches an HH:MM:SS clock stamp. Anything shaped like that is
|
|
230
|
-
// stripped from the structural hash so clock ticks don't animate.
|
|
231
|
-
// The frame renderer is the only dashboard author that emits such a
|
|
232
|
-
// string; if a third-party section happens to show a value in the
|
|
233
|
-
// same shape, the worst case is "we don't re-animate when that
|
|
234
|
-
// value changes" — acceptable noise floor.
|
|
235
|
-
const CLOCK_RE = /\d\d:\d\d:\d\d/g;
|
|
236
|
-
|
|
237
|
-
// Edge the slide-in enters from. Maps 1:1 to the plugin API's `from`
|
|
238
|
-
// field and is resolved from config (plugins.dashboard.slide_from) on
|
|
239
|
-
// each paint() so hot-reload of the setting Just Works. Defaults to
|
|
240
|
-
// "right" (new content pushes in from the right, old exits left).
|
|
241
|
-
type SlideFrom = "top" | "bottom" | "left" | "right";
|
|
242
|
-
function resolveSlideFrom(): SlideFrom {
|
|
243
|
-
const config = editor.getConfig() as Record<string, unknown> | null;
|
|
244
|
-
const plugins = config?.plugins as Record<string, unknown> | undefined;
|
|
245
|
-
const dashCfg = plugins?.dashboard as Record<string, unknown> | undefined;
|
|
246
|
-
const raw = dashCfg?.slide_from;
|
|
247
|
-
if (raw === "top" || raw === "bottom" || raw === "left" || raw === "right") {
|
|
248
|
-
return raw;
|
|
249
|
-
}
|
|
250
|
-
return "right";
|
|
251
|
-
}
|
|
252
|
-
|
|
253
216
|
// Registered sections, in render order. Built-ins are registered at
|
|
254
217
|
// plugin load (see the bottom of this file); third-party plugins
|
|
255
218
|
// append via the exported `registerSection` API.
|
|
@@ -735,6 +698,13 @@ function paint(dims?: { width: number; height: number }) {
|
|
|
735
698
|
const entries: TextPropertyEntry[] = [];
|
|
736
699
|
for (let i = 0; i < topPad; i++) entries.push({ text: "\n" });
|
|
737
700
|
for (const e of drawToEntries(drawn)) entries.push(e);
|
|
701
|
+
// Pad below the frame so the buffer covers the full viewport height.
|
|
702
|
+
// Without this, rows past the last frame line render with
|
|
703
|
+
// `editor.after_eof_bg` (a deliberate shade off from `editor.bg` to mark
|
|
704
|
+
// end-of-file in code buffers) and show up as a different-colored strip
|
|
705
|
+
// at the bottom of the dashboard.
|
|
706
|
+
const bottomPad = Math.max(0, height - topPad - frameHeight);
|
|
707
|
+
for (let i = 0; i < bottomPad; i++) entries.push({ text: "\n" });
|
|
738
708
|
|
|
739
709
|
// Translate frame-relative row actions to absolute buffer rows by
|
|
740
710
|
// shifting by the vertical padding we just prepended. Columns are
|
|
@@ -775,32 +745,15 @@ function paint(dims?: { width: number; height: number }) {
|
|
|
775
745
|
((focusedIndex % targets.length) + targets.length) % targets.length;
|
|
776
746
|
}
|
|
777
747
|
|
|
778
|
-
//
|
|
779
|
-
//
|
|
780
|
-
//
|
|
781
|
-
//
|
|
782
|
-
// structuralKey — clock stamps stripped. Drives the animation.
|
|
783
|
-
// A clock tick alone does not flip this, so it
|
|
784
|
-
// updates silently; a real section data change
|
|
785
|
-
// does, and the slide fires.
|
|
748
|
+
// Taken BEFORE the focus highlight goes on top — fullKey captures
|
|
749
|
+
// everything (including the clock) that ultimately lands in the
|
|
750
|
+
// virtual buffer. Drives the setVirtualBufferContent skip check,
|
|
751
|
+
// so the clock still redraws in place every second.
|
|
786
752
|
const fullKey = JSON.stringify(entries);
|
|
787
|
-
const structuralKey = fullKey.replace(CLOCK_RE, "##:##:##");
|
|
788
753
|
const fullChanged = fullKey !== lastPaintedFullKey;
|
|
789
|
-
const structuralChanged = structuralKey !== lastPaintedStructuralKey;
|
|
790
754
|
const focusChanged = focusedIndex !== lastPaintedFocusedIndex;
|
|
791
|
-
|
|
792
|
-
//
|
|
793
|
-
// section data is unchanged. We repaint so the new layout takes
|
|
794
|
-
// effect but skip the slide — nothing NEW showed up, the user is
|
|
795
|
-
// just resizing a window. openDashboard clears lastPaintedW/H to
|
|
796
|
-
// -1 so the first paint after open doesn't trip this guard.
|
|
797
|
-
const dimsChanged =
|
|
798
|
-
lastPaintedW !== -1 &&
|
|
799
|
-
lastPaintedH !== -1 &&
|
|
800
|
-
(width !== lastPaintedW || height !== lastPaintedH);
|
|
801
|
-
|
|
802
|
-
// Identical render → short-circuit. Nothing to push to the
|
|
803
|
-
// buffer, nothing to animate.
|
|
755
|
+
|
|
756
|
+
// Identical render → short-circuit. Nothing to push to the buffer.
|
|
804
757
|
if (!fullChanged && !focusChanged) {
|
|
805
758
|
return;
|
|
806
759
|
}
|
|
@@ -838,25 +791,7 @@ function paint(dims?: { width: number; height: number }) {
|
|
|
838
791
|
lastPaintedW = width;
|
|
839
792
|
lastPaintedH = height;
|
|
840
793
|
lastPaintedFullKey = fullKey;
|
|
841
|
-
lastPaintedStructuralKey = structuralKey;
|
|
842
794
|
lastPaintedFocusedIndex = focusedIndex;
|
|
843
|
-
|
|
844
|
-
// Structural-change-driven re-animation: fire only when the
|
|
845
|
-
// section payload actually differs AND the dashboard isn't just
|
|
846
|
-
// reshaping in place (clock tick, focus move, and resize all
|
|
847
|
-
// land here without animating). Cancel any in-flight slide
|
|
848
|
-
// first so the new one snapshots the fresh content.
|
|
849
|
-
if (structuralChanged && !dimsChanged) {
|
|
850
|
-
if (activeAnimationId !== null) {
|
|
851
|
-
editor.cancelAnimation(activeAnimationId);
|
|
852
|
-
}
|
|
853
|
-
activeAnimationId = editor.animateVirtualBuffer(bufferId, {
|
|
854
|
-
kind: "slideIn",
|
|
855
|
-
from: resolveSlideFrom(),
|
|
856
|
-
durationMs: 520,
|
|
857
|
-
delayMs: 0,
|
|
858
|
-
});
|
|
859
|
-
}
|
|
860
795
|
}
|
|
861
796
|
|
|
862
797
|
// Open a URL in the user's browser via the platform's "open" helper.
|
|
@@ -1613,12 +1548,8 @@ async function openDashboard() {
|
|
|
1613
1548
|
}
|
|
1614
1549
|
|
|
1615
1550
|
// Clear the content/focus keys and dims so the first paint after
|
|
1616
|
-
// open is treated as a content change
|
|
1617
|
-
// Dim reset is needed because open is the one case where we DO
|
|
1618
|
-
// want the animation despite "dims changed" (there was no prior
|
|
1619
|
-
// dimension, so the change is really "buffer just appeared").
|
|
1551
|
+
// open is treated as a content change.
|
|
1620
1552
|
lastPaintedFullKey = null;
|
|
1621
|
-
lastPaintedStructuralKey = null;
|
|
1622
1553
|
lastPaintedFocusedIndex = -1;
|
|
1623
1554
|
lastPaintedW = -1;
|
|
1624
1555
|
lastPaintedH = -1;
|
|
@@ -1694,10 +1625,6 @@ registerHandler(
|
|
|
1694
1625
|
// If the dashboard itself was closed, clear our handle so we'll
|
|
1695
1626
|
// re-open on the next "last tab closed" event.
|
|
1696
1627
|
if (dashboardBufferId !== null && e.buffer_id === dashboardBufferId) {
|
|
1697
|
-
if (activeAnimationId !== null) {
|
|
1698
|
-
editor.cancelAnimation(activeAnimationId);
|
|
1699
|
-
activeAnimationId = null;
|
|
1700
|
-
}
|
|
1701
1628
|
dashboardBufferId = null;
|
|
1702
1629
|
return;
|
|
1703
1630
|
}
|
|
@@ -1717,10 +1644,6 @@ registerHandler(
|
|
|
1717
1644
|
"dashboardOnAfterFileOpen",
|
|
1718
1645
|
(_e: { buffer_id: number; path: string }) => {
|
|
1719
1646
|
if (dashboardBufferId === null) return;
|
|
1720
|
-
if (activeAnimationId !== null) {
|
|
1721
|
-
editor.cancelAnimation(activeAnimationId);
|
|
1722
|
-
activeAnimationId = null;
|
|
1723
|
-
}
|
|
1724
1647
|
editor.closeBuffer(dashboardBufferId);
|
|
1725
1648
|
dashboardBufferId = null;
|
|
1726
1649
|
},
|