turbo-editors/turbo-moonbitpublic Fork
⑂ ac56478
Commits
⬇ Clone ▾
git clone https://git.rickub.com/turbo-editors/turbo-moonbit.git
git clone ssh://git@rickub.com/turbo-editors/turbo-moonbit.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP

📦 Turbo MoonBit

k33g committed 2026-09-29T18:54:01+02:00 Browse files
ac56478 parent: f5a8f18
added .memory/handoffs/2026-09-29-ssh-remote-editing.md +16 -0
new file mode 100644
@@ -0,0 +1,16 @@
1+# Handoff — 2026-09-29 — `-ssh` remote editing
2+
3+## State
4+
5+`turbo-moonbit -ssh user@machine[:dir]` is wired and tested under a `go.work` pointing at `../turbo-core`, which holds the uncommitted `fsys`/`remote` packages. Docs EN+FR done.
6+
7+## Next steps
8+
9+1. Once turbo-core v1.0.5 is published: remove the workspace, `go get rickub.com/turbo-editors/turbo-core@v1.0.5 && go mod tidy && GOWORK=off make check` — the two `TestTheTagScript*` tests must then pass.
10+2. Run the quality gate (needs `qlty`).
11+3. Try `turbo-moonbit -ssh turbo-demo:workspace` against `../demo-remote-machine` (its README has the `~/.ssh/config` block). Only Python is installed there, so this editor's tools menu will find no toolchain — the files, tree and terminal are what to check.
12+
13+## Watch out for
14+
15+- `parseFlags` now calls `parseArguments(flag.CommandLine, os.Args[1:])`; add new flags inside `parseArguments`, on `flags`, not on the `flag` package.
16+- Over ssh no language server starts, by design (`session == nil` guard in `edit`).
new file mode 100644
@@ -0,0 +1,16 @@
1+# Handoff — 2026-09-29 — `-ssh` remote editing
2+
3+## State
4+
5+`turbo-moonbit -ssh user@machine[:dir]` is wired and tested under a `go.work` pointing at `../turbo-core`, which holds the uncommitted `fsys`/`remote` packages. Docs EN+FR done.
6+
7+## Next steps
8+
9+1. Once turbo-core v1.0.5 is published: remove the workspace, `go get rickub.com/turbo-editors/turbo-core@v1.0.5 && go mod tidy && GOWORK=off make check` — the two `TestTheTagScript*` tests must then pass.
10+2. Run the quality gate (needs `qlty`).
11+3. Try `turbo-moonbit -ssh turbo-demo:workspace` against `../demo-remote-machine` (its README has the `~/.ssh/config` block). Only Python is installed there, so this editor's tools menu will find no toolchain — the files, tree and terminal are what to check.
12+
13+## Watch out for
14+
15+- `parseFlags` now calls `parseArguments(flag.CommandLine, os.Args[1:])`; add new flags inside `parseArguments`, on `flags`, not on the `flag` package.
16+- Over ssh no language server starts, by design (`session == nil` guard in `edit`).
modified .memory/history.md +8 -0
@@ -134,3 +134,11 @@ One dated entry per session, appended. Never rewritten.
134134
135135 - **Changed here** (docs only, EN+FR): `how-to/use-a-terminal.md` gained "Copy from the terminal into a file, and back"; `reference/terminal.md` gained the selection keys, the drag row, a "Copying and pasting" table, private mode 2004, `editor.selection` in the colours, and lost "bracketed paste" from the not-implemented list; `reference/keyboard.md` gained the terminal and mouse rows; `explanation/terminal-windows.md` gained "Copying out and pasting in". Applied from the umbrella session that added the feature to turbo-core, by one anchored script across the six editors.
136136 - **Not done**: `go.mod` still pins v1.0.3, so the binary built without the workspace does not yet have the feature the docs describe; nothing committed.
137+
138+## 2026-09-29 — `-ssh` / `--ssh`: edit another machine's files (ticket #33)
139+
140+- **Asked** (umbrella session): `turbo-python -ssh user@machine`, then all six editors; the work is turbo-core's (`fsys`, `remote`, `app.UseRemote` — its history has the design and the rejected alternatives).
141+- **Changes here**: `main.go` (see summary), new `ssh_test.go`, docs EN+FR (`reference/cli.md`, `how-to/edit-a-remote-machine.md`, indexes), `README.md`, `docs/diagrams/packages.drawio`. Applied from one script across the six editors, anchored and asserting each anchor.
142+- **Tests**: `ssh_test.go` — `-ssh` and `--ssh` and `--ssh=` read the same; files after the flag stay files; no flag keeps `fsys` local; `-oProxyCommand=evil` refused with `remote.ErrBadTarget` before anything runs; project settings read from an in-memory `fsys`. Green under the workspace; the two `TestTheTagScript*` tests fail until turbo-core v1.0.5 is re-pinned.
143+- **Quality**: gate not run (no `qlty` in the sandbox). `gofmt`, `go vet` clean.
144+- **Not done**: not committed; `go.mod` not re-pinned (no v1.0.5 yet).
@@ -134,3 +134,11 @@ One dated entry per session, appended. Never rewritten.
134 134
135 - **Changed here** (docs only, EN+FR): `how-to/use-a-terminal.md` gained "Copy from the terminal into a file, and back"; `reference/terminal.md` gained the selection keys, the drag row, a "Copying and pasting" table, private mode 2004, `editor.selection` in the colours, and lost "bracketed paste" from the not-implemented list; `reference/keyboard.md` gained the terminal and mouse rows; `explanation/terminal-windows.md` gained "Copying out and pasting in". Applied from the umbrella session that added the feature to turbo-core, by one anchored script across the six editors.135 - **Changed here** (docs only, EN+FR): `how-to/use-a-terminal.md` gained "Copy from the terminal into a file, and back"; `reference/terminal.md` gained the selection keys, the drag row, a "Copying and pasting" table, private mode 2004, `editor.selection` in the colours, and lost "bracketed paste" from the not-implemented list; `reference/keyboard.md` gained the terminal and mouse rows; `explanation/terminal-windows.md` gained "Copying out and pasting in". Applied from the umbrella session that added the feature to turbo-core, by one anchored script across the six editors.
136 - **Not done**: `go.mod` still pins v1.0.3, so the binary built without the workspace does not yet have the feature the docs describe; nothing committed.136 - **Not done**: `go.mod` still pins v1.0.3, so the binary built without the workspace does not yet have the feature the docs describe; nothing committed.
137+
138+## 2026-09-29 — `-ssh` / `--ssh`: edit another machine's files (ticket #33)
139+
140+- **Asked** (umbrella session): `turbo-python -ssh user@machine`, then all six editors; the work is turbo-core's (`fsys`, `remote`, `app.UseRemote` — its history has the design and the rejected alternatives).
141+- **Changes here**: `main.go` (see summary), new `ssh_test.go`, docs EN+FR (`reference/cli.md`, `how-to/edit-a-remote-machine.md`, indexes), `README.md`, `docs/diagrams/packages.drawio`. Applied from one script across the six editors, anchored and asserting each anchor.
142+- **Tests**: `ssh_test.go` — `-ssh` and `--ssh` and `--ssh=` read the same; files after the flag stay files; no flag keeps `fsys` local; `-oProxyCommand=evil` refused with `remote.ErrBadTarget` before anything runs; project settings read from an in-memory `fsys`. Green under the workspace; the two `TestTheTagScript*` tests fail until turbo-core v1.0.5 is re-pinned.
143+- **Quality**: gate not run (no `qlty` in the sandbox). `gofmt`, `go vet` clean.
144+- **Not done**: not committed; `go.mod` not re-pinned (no v1.0.5 yet).
modified .memory/summary.md +6 -0
@@ -118,3 +118,9 @@ go list -f '{{.Dir}}' rickub.com/turbo-editors/turbo-core/app # must NOT be un
118118
119119 - Docs and the snippets template already describe **turbo-core v1.0.4's AsciiDoc** (ninth built-in language) while `go.mod` still pins v1.0.3; the suite passes either way. Re-pin when v1.0.4 is tagged.
120120
121+## State as of 2026-09-29
122+
123+- **`-ssh user@machine[:dir]` / `--ssh` is wired** (ticket #33), on top of turbo-core's new `fsys` and `remote` (unreleased — see turbo-core's memory). `main.go`: `ssh` option, `parseFlags` now a wrapper over `parseArguments(*flag.FlagSet, args)` so tests can drive it, `connectRemote` (before `newScreen`: `remote.Open`, then `fsys.Use(session.FS())`), `editor.UseRemote(session)`, no `StartLanguageServer` over ssh, `loadProjectSettings` reads `fsys.Getwd()`. `ssh_test.go`: 5 tests. Not run interactively in this editor; the same `main.go` wiring was driven end to end in turbo-python, and this editor's `ssh_test.go` covers the flag, the refusal of a bad target, and the settings read through `fsys`.
124+- **Builds only under `go.work`** until turbo-core v1.0.5 is published and re-pinned: `go.mod` still pins v1.0.4, which has no `fsys`/`remote`. Under the workspace `go test ./...` is green **except `TestTheTagScriptTagsAndPushesForReal` and `TestTheTagScriptRefusesATagItAlreadyPublished`**, which copy the module and build with `GOWORK=off` against the published v1.0.4 — expected, same situation as `-load-config` on 2026-09-20.
125+- **Docs** (EN+FR): `reference/cli.md` (flag, files, exit status, examples, seven error rows), new `how-to/edit-a-remote-machine.md`, both indexes, `README.md` (feature bullet, command line). `docs/diagrams/packages.drawio` gained `fsys` and `remote` boxes and `main →` arrows (its diagram test demanded them).
126+- **Quality gate not run** — `qlty` is absent from this sandbox.
@@ -118,3 +118,9 @@ go list -f '{{.Dir}}' rickub.com/turbo-editors/turbo-core/app # must NOT be un
118 118
119 - Docs and the snippets template already describe **turbo-core v1.0.4's AsciiDoc** (ninth built-in language) while `go.mod` still pins v1.0.3; the suite passes either way. Re-pin when v1.0.4 is tagged.119 - Docs and the snippets template already describe **turbo-core v1.0.4's AsciiDoc** (ninth built-in language) while `go.mod` still pins v1.0.3; the suite passes either way. Re-pin when v1.0.4 is tagged.
120 120
121+## State as of 2026-09-29
122+
123+- **`-ssh user@machine[:dir]` / `--ssh` is wired** (ticket #33), on top of turbo-core's new `fsys` and `remote` (unreleased — see turbo-core's memory). `main.go`: `ssh` option, `parseFlags` now a wrapper over `parseArguments(*flag.FlagSet, args)` so tests can drive it, `connectRemote` (before `newScreen`: `remote.Open`, then `fsys.Use(session.FS())`), `editor.UseRemote(session)`, no `StartLanguageServer` over ssh, `loadProjectSettings` reads `fsys.Getwd()`. `ssh_test.go`: 5 tests. Not run interactively in this editor; the same `main.go` wiring was driven end to end in turbo-python, and this editor's `ssh_test.go` covers the flag, the refusal of a bad target, and the settings read through `fsys`.
124+- **Builds only under `go.work`** until turbo-core v1.0.5 is published and re-pinned: `go.mod` still pins v1.0.4, which has no `fsys`/`remote`. Under the workspace `go test ./...` is green **except `TestTheTagScriptTagsAndPushesForReal` and `TestTheTagScriptRefusesATagItAlreadyPublished`**, which copy the module and build with `GOWORK=off` against the published v1.0.4 — expected, same situation as `-load-config` on 2026-09-20.
125+- **Docs** (EN+FR): `reference/cli.md` (flag, files, exit status, examples, seven error rows), new `how-to/edit-a-remote-machine.md`, both indexes, `README.md` (feature bullet, command line). `docs/diagrams/packages.drawio` gained `fsys` and `remote` boxes and `main →` arrows (its diagram test demanded them).
126+- **Quality gate not run** — `qlty` is absent from this sandbox.
modified README.md +2 -0
@@ -58,6 +58,7 @@ The [tutorial](docs/en/tutorials/getting-started.md) walks through a first sessi
5858 - **Completion, hover, go-to-definition, references, symbols and diagnostics** from `moon-lsp`, entirely optional. The server is looked for in `$MOON_HOME/bin` and `~/.moon/bin` as well as on `PATH`, so an editor started from a shell that never read the installer's profile still finds it
5959 - **Terminal windows** — `F8` opens a real shell in a window, with its own VT/ANSI emulator, scrollback and job control (Linux, macOS and Windows)
6060 - **Project tree** — `F9` shows the project's files in a window; walk it with the arrows and press Enter to open one
61+- **Editing another machine** — `turbo-moonbit -ssh user@machine[:dir]` opens a project on a server, a VM or a container over your own ssh — its `~/.ssh/config`, keys, agent or password — so the files, the tree, the terminal windows and the MoonBit menu are that machine's. One login, before the screen is taken; no language server over there yet
6162 - **Snippets** — a `Snippets` menu built from `.turbo-moonbit/snippets.toml`, grouped into submenus and filtered by the file you are in; the chosen text is inserted at the cursor, re-indented to match, and indented two spaces because that is what `moon fmt` writes
6263 - **The MoonBit toolchain a menu away** — `Alt-M` runs `moon check`, `moon fmt`, `moon build`, `moon test`, `moon run`, `moon add`, `moon info` and `moon clean` from `.turbo-moonbit/tools.toml`, each showing its output where the tool asked: a popup that fills in as it goes, a terminal window, or an editing window to search. Files the command rewrote are re-read for you, and a tool naming a `menu` of its own gets that menu on the bar
6364 - **Editing** with word movement, block indent, a shared clipboard, and undo that merges a run of typing into one step
@@ -78,6 +79,7 @@ The [tutorial](docs/en/tutorials/getting-started.md) walks through a first sessi
7879 ```bash
7980 turbo-moonbit [-theme name] [-no-lsp] [file...]
8081 turbo-moonbit -list-themes
82+turbo-moonbit -ssh user@machine[:directory] [file...]
8183 ```
8284
8385 ## Documentation
@@ -58,6 +58,7 @@ The [tutorial](docs/en/tutorials/getting-started.md) walks through a first sessi
58 - **Completion, hover, go-to-definition, references, symbols and diagnostics** from `moon-lsp`, entirely optional. The server is looked for in `$MOON_HOME/bin` and `~/.moon/bin` as well as on `PATH`, so an editor started from a shell that never read the installer's profile still finds it58 - **Completion, hover, go-to-definition, references, symbols and diagnostics** from `moon-lsp`, entirely optional. The server is looked for in `$MOON_HOME/bin` and `~/.moon/bin` as well as on `PATH`, so an editor started from a shell that never read the installer's profile still finds it
59 - **Terminal windows** — `F8` opens a real shell in a window, with its own VT/ANSI emulator, scrollback and job control (Linux, macOS and Windows)59 - **Terminal windows** — `F8` opens a real shell in a window, with its own VT/ANSI emulator, scrollback and job control (Linux, macOS and Windows)
60 - **Project tree** — `F9` shows the project's files in a window; walk it with the arrows and press Enter to open one60 - **Project tree** — `F9` shows the project's files in a window; walk it with the arrows and press Enter to open one
61+- **Editing another machine** — `turbo-moonbit -ssh user@machine[:dir]` opens a project on a server, a VM or a container over your own ssh — its `~/.ssh/config`, keys, agent or password — so the files, the tree, the terminal windows and the MoonBit menu are that machine's. One login, before the screen is taken; no language server over there yet
61 - **Snippets** — a `Snippets` menu built from `.turbo-moonbit/snippets.toml`, grouped into submenus and filtered by the file you are in; the chosen text is inserted at the cursor, re-indented to match, and indented two spaces because that is what `moon fmt` writes62 - **Snippets** — a `Snippets` menu built from `.turbo-moonbit/snippets.toml`, grouped into submenus and filtered by the file you are in; the chosen text is inserted at the cursor, re-indented to match, and indented two spaces because that is what `moon fmt` writes
62 - **The MoonBit toolchain a menu away** — `Alt-M` runs `moon check`, `moon fmt`, `moon build`, `moon test`, `moon run`, `moon add`, `moon info` and `moon clean` from `.turbo-moonbit/tools.toml`, each showing its output where the tool asked: a popup that fills in as it goes, a terminal window, or an editing window to search. Files the command rewrote are re-read for you, and a tool naming a `menu` of its own gets that menu on the bar63 - **The MoonBit toolchain a menu away** — `Alt-M` runs `moon check`, `moon fmt`, `moon build`, `moon test`, `moon run`, `moon add`, `moon info` and `moon clean` from `.turbo-moonbit/tools.toml`, each showing its output where the tool asked: a popup that fills in as it goes, a terminal window, or an editing window to search. Files the command rewrote are re-read for you, and a tool naming a `menu` of its own gets that menu on the bar
63 - **Editing** with word movement, block indent, a shared clipboard, and undo that merges a run of typing into one step64 - **Editing** with word movement, block indent, a shared clipboard, and undo that merges a run of typing into one step
@@ -78,6 +79,7 @@ The [tutorial](docs/en/tutorials/getting-started.md) walks through a first sessi
78 ```bash79 ```bash
79 turbo-moonbit [-theme name] [-no-lsp] [file...]80 turbo-moonbit [-theme name] [-no-lsp] [file...]
80 turbo-moonbit -list-themes81 turbo-moonbit -list-themes
82+turbo-moonbit -ssh user@machine[:directory] [file...]
81 ```83 ```
82 84
83 ## Documentation85 ## Documentation
modified docs/diagrams/packages.drawio +4 -0
@@ -24,6 +24,10 @@
2424 <mxCell id="e18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="2" target="8"><mxGeometry relative="1" as="geometry"/></mxCell>
2525 <mxCell id="e19" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="2" target="9"><mxGeometry relative="1" as="geometry"/></mxCell>
2626 <mxCell id="e20" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="2" target="20"><mxGeometry relative="1" as="geometry"/></mxCell>
27+ <mxCell id="ssh-fsys" value="&lt;b&gt;fsys&lt;/b&gt;&lt;br/&gt;&lt;font style=&#x27;font-size:9px&#x27;&gt;turbo-core&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontSize=11;" vertex="1" parent="1"><mxGeometry x="329" y="390" width="190" height="54" as="geometry"/></mxCell>
28+ <mxCell id="e-main-fsys" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="2" target="ssh-fsys"><mxGeometry relative="1" as="geometry"/></mxCell>
29+ <mxCell id="ssh-remote" value="&lt;b&gt;remote&lt;/b&gt;&lt;br/&gt;&lt;font style=&#x27;font-size:9px&#x27;&gt;turbo-core&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontSize=11;" vertex="1" parent="1"><mxGeometry x="541" y="390" width="190" height="54" as="geometry"/></mxCell>
30+ <mxCell id="e-main-remote" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="2" target="ssh-remote"><mxGeometry relative="1" as="geometry"/></mxCell>
2731 </root>
2832 </mxGraphModel>
2933 </diagram>
@@ -24,6 +24,10 @@
24 <mxCell id="e18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="2" target="8"><mxGeometry relative="1" as="geometry"/></mxCell>24 <mxCell id="e18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="2" target="8"><mxGeometry relative="1" as="geometry"/></mxCell>
25 <mxCell id="e19" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="2" target="9"><mxGeometry relative="1" as="geometry"/></mxCell>25 <mxCell id="e19" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="2" target="9"><mxGeometry relative="1" as="geometry"/></mxCell>
26 <mxCell id="e20" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="2" target="20"><mxGeometry relative="1" as="geometry"/></mxCell>26 <mxCell id="e20" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="2" target="20"><mxGeometry relative="1" as="geometry"/></mxCell>
27+ <mxCell id="ssh-fsys" value="&lt;b&gt;fsys&lt;/b&gt;&lt;br/&gt;&lt;font style=&#x27;font-size:9px&#x27;&gt;turbo-core&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontSize=11;" vertex="1" parent="1"><mxGeometry x="329" y="390" width="190" height="54" as="geometry"/></mxCell>
28+ <mxCell id="e-main-fsys" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="2" target="ssh-fsys"><mxGeometry relative="1" as="geometry"/></mxCell>
29+ <mxCell id="ssh-remote" value="&lt;b&gt;remote&lt;/b&gt;&lt;br/&gt;&lt;font style=&#x27;font-size:9px&#x27;&gt;turbo-core&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontSize=11;" vertex="1" parent="1"><mxGeometry x="541" y="390" width="190" height="54" as="geometry"/></mxCell>
30+ <mxCell id="e-main-remote" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="2" target="ssh-remote"><mxGeometry relative="1" as="geometry"/></mxCell>
27 </root>31 </root>
28 </mxGraphModel>32 </mxGraphModel>
29 </diagram>33 </diagram>
modified docs/en/README.md +1 -0
@@ -28,6 +28,7 @@ This documentation follows the [Diátaxis](https://diataxis.fr) method. Four kin
2828 - [How to run shell commands without leaving the editor](how-to/use-a-terminal.md)
2929 - [How to give a project its own settings](how-to/configure-a-project.md)
3030 - [How to browse a project and open files from a tree](how-to/browse-a-project.md)
31+- [How to edit the files of a remote machine over ssh](how-to/edit-a-remote-machine.md)
3132 - [How to insert snippets from a menu](how-to/use-snippets.md)
3233 - [How to run moon commands from the editor](how-to/run-moon-commands.md)
3334 - [How to make a release](how-to/make-a-release.md)
@@ -28,6 +28,7 @@ This documentation follows the [Diátaxis](https://diataxis.fr) method. Four kin
28 - [How to run shell commands without leaving the editor](how-to/use-a-terminal.md)28 - [How to run shell commands without leaving the editor](how-to/use-a-terminal.md)
29 - [How to give a project its own settings](how-to/configure-a-project.md)29 - [How to give a project its own settings](how-to/configure-a-project.md)
30 - [How to browse a project and open files from a tree](how-to/browse-a-project.md)30 - [How to browse a project and open files from a tree](how-to/browse-a-project.md)
31+- [How to edit the files of a remote machine over ssh](how-to/edit-a-remote-machine.md)
31 - [How to insert snippets from a menu](how-to/use-snippets.md)32 - [How to insert snippets from a menu](how-to/use-snippets.md)
32 - [How to run moon commands from the editor](how-to/run-moon-commands.md)33 - [How to run moon commands from the editor](how-to/run-moon-commands.md)
33 - [How to make a release](how-to/make-a-release.md)34 - [How to make a release](how-to/make-a-release.md)
added docs/en/how-to/edit-a-remote-machine.md +55 -0
new file mode 100644
@@ -0,0 +1,55 @@
1+# How to edit the files of a remote machine over ssh
2+
3+This guide opens a project that lives on another machine — a server, a VM, a container — in Turbo MoonBit, so that its files, its project tree, its terminal windows and its MoonBit menu are that machine's. It assumes you can already `ssh` into the machine.
4+
5+## Steps
6+
7+1. Check that plain ssh works, from the terminal you will start the editor in:
8+
9+ ```bash
10+ ssh bob@build-box true
11+ ```
12+
13+ Answer any question it asks — a new host key, a password — here first: it is the same ssh the editor runs.
14+
15+2. Start the editor with the machine and the directory:
16+
17+ ```bash
18+ turbo-moonbit -ssh bob@build-box:/srv/app
19+ ```
20+
21+ It prints `Connecting to bob@build-box:/srv/app…`. If ssh needs a password or a key's passphrase it asks now, on the terminal, once. The editor then opens, and the status bar ends with `⇄ bob@build-box`.
22+
23+3. Work as usual. `F3` opens the remote files, `F9` shows the remote tree, `F2` saves to the remote machine, `F8` opens a shell there in the project directory, and the MoonBit menu runs `.turbo-moonbit/tools.toml` there, with `/bin/sh`, in the project directory.
24+
25+4. Leave with `Alt-X`. The connection is closed with the editor.
26+
27+## Variants
28+
29+- **Another directory.** `bob@build-box` alone opens bob's home; `bob@build-box:app` and `bob@build-box:~/app` open `~/app`; `bob@build-box:/srv/app` an absolute path.
30+- **Files to open at once.** Name them after the target, relative to the remote directory: `turbo-moonbit -ssh bob@build-box:/srv/app main.py`.
31+- **Two dashes.** `--ssh` is the same flag as `-ssh`, and `--ssh=bob@build-box` works too.
32+- **A port, a key, a jump host.** The target has no port syntax, as with scp. Give the machine a `Host` block in `~/.ssh/config` and use its name:
33+
34+ ```
35+ Host build-box
36+ HostName 192.0.2.10
37+ Port 2222
38+ User bob
39+ IdentityFile ~/.ssh/id_build
40+ ```
41+
42+ then `turbo-moonbit -ssh build-box:/srv/app`.
43+- **A password rather than a key.** Nothing to do: ssh asks for it before the editor opens. To force it when you have keys loaded, add `PubkeyAuthentication no` to that host's block.
44+- **Trying it without a server.** `turbo-editors/demo-remote-machine` is a Docker Compose machine accepting a key and a password; its README gives the exact commands.
45+
46+## What is not available over ssh
47+
48+- **Completion and diagnostics.** No language server is started — `moon-lsp` would have to run on the remote machine. The status bar says `LSP: off`.
49+- **Agent windows.** The Agent menu says they are not available over ssh.
50+- **Windows** as the machine you run the editor on: its ssh has no ControlMaster.
51+
52+## See also
53+
54+- The flag and its errors: [command line](../reference/cli.md)
55+- The terminal windows it opens: [terminal](../reference/terminal.md)
new file mode 100644
@@ -0,0 +1,55 @@
1+# How to edit the files of a remote machine over ssh
2+
3+This guide opens a project that lives on another machine — a server, a VM, a container — in Turbo MoonBit, so that its files, its project tree, its terminal windows and its MoonBit menu are that machine's. It assumes you can already `ssh` into the machine.
4+
5+## Steps
6+
7+1. Check that plain ssh works, from the terminal you will start the editor in:
8+
9+ ```bash
10+ ssh bob@build-box true
11+ ```
12+
13+ Answer any question it asks — a new host key, a password — here first: it is the same ssh the editor runs.
14+
15+2. Start the editor with the machine and the directory:
16+
17+ ```bash
18+ turbo-moonbit -ssh bob@build-box:/srv/app
19+ ```
20+
21+ It prints `Connecting to bob@build-box:/srv/app…`. If ssh needs a password or a key's passphrase it asks now, on the terminal, once. The editor then opens, and the status bar ends with `⇄ bob@build-box`.
22+
23+3. Work as usual. `F3` opens the remote files, `F9` shows the remote tree, `F2` saves to the remote machine, `F8` opens a shell there in the project directory, and the MoonBit menu runs `.turbo-moonbit/tools.toml` there, with `/bin/sh`, in the project directory.
24+
25+4. Leave with `Alt-X`. The connection is closed with the editor.
26+
27+## Variants
28+
29+- **Another directory.** `bob@build-box` alone opens bob's home; `bob@build-box:app` and `bob@build-box:~/app` open `~/app`; `bob@build-box:/srv/app` an absolute path.
30+- **Files to open at once.** Name them after the target, relative to the remote directory: `turbo-moonbit -ssh bob@build-box:/srv/app main.py`.
31+- **Two dashes.** `--ssh` is the same flag as `-ssh`, and `--ssh=bob@build-box` works too.
32+- **A port, a key, a jump host.** The target has no port syntax, as with scp. Give the machine a `Host` block in `~/.ssh/config` and use its name:
33+
34+ ```
35+ Host build-box
36+ HostName 192.0.2.10
37+ Port 2222
38+ User bob
39+ IdentityFile ~/.ssh/id_build
40+ ```
41+
42+ then `turbo-moonbit -ssh build-box:/srv/app`.
43+- **A password rather than a key.** Nothing to do: ssh asks for it before the editor opens. To force it when you have keys loaded, add `PubkeyAuthentication no` to that host's block.
44+- **Trying it without a server.** `turbo-editors/demo-remote-machine` is a Docker Compose machine accepting a key and a password; its README gives the exact commands.
45+
46+## What is not available over ssh
47+
48+- **Completion and diagnostics.** No language server is started — `moon-lsp` would have to run on the remote machine. The status bar says `LSP: off`.
49+- **Agent windows.** The Agent menu says they are not available over ssh.
50+- **Windows** as the machine you run the editor on: its ssh has no ControlMaster.
51+
52+## See also
53+
54+- The flag and its errors: [command line](../reference/cli.md)
55+- The terminal windows it opens: [terminal](../reference/terminal.md)
modified docs/en/reference/cli.md +13 -1
@@ -19,6 +19,7 @@ Each `file` is opened in its own window. A file that does not exist yet is opene
1919 | `-no-lsp` | bool | `false` | Do not start a language server. Colouring and editing are unaffected. |
2020 | `-version` | bool | `false` | Print `Turbo MoonBit <version>` on one line, with the commit and build date when the build recorded them, and exit. See [the version number](versioning.md). |
2121 | `-load-config <url>` | string | none | Copy the `.turbo-moonbit` directory found at a repository URL — rickub, GitHub, GitLab or Codeberg, the URL of the directory as the browser shows it — into the working directory, print what arrived, and exit. Needs `git`. Refuses when `./.turbo-moonbit` already exists. The editor is not started. |
22+| `-ssh <target>`, `--ssh <target>` | string | none | Edit the files of another machine over ssh. `<target>` is `[user@]host[:directory]`, as scp writes it; no directory means the remote home, a relative one is relative to it. Before the screen is taken, prints `Connecting to <target>…` and runs `ssh`, which asks on the terminal for a password or passphrase if it needs one. The project is then that directory: its files, tree, `.turbo-moonbit/` files, terminal windows and tools are the remote machine's. No language server is started, and agent windows are unavailable. Needs OpenSSH's `ssh` on `PATH`, on Linux or macOS. See [edit a remote machine](../how-to/edit-a-remote-machine.md). |
2223 | `-h`, `-help` | bool | `false` | Print the flag list and exit. |
2324
2425 ## Environment
@@ -35,6 +36,8 @@ Each `file` is opened in its own window. A file that does not exist yet is opene
3536 | --- | --- |
3637 | `$TURBO_MOONBIT_THEME_DIR/*.toml` | User themes, when the variable is set. |
3738 | `./.turbo-moonbit/settings.toml` | This project's settings, read once at start-up. See [project settings](project-settings.md). |
39+| `<directory>/.turbo-moonbit/*.toml` on the remote machine | Under `-ssh`: the project's settings, snippets and tools, read and written over SFTP. |
40+| `~/.ssh/config` | Under `-ssh`: read by `ssh` — `Host` aliases, `Port`, `User`, `IdentityFile`, `ProxyJump`. |
3841 | `./.turbo-moonbit/` (written) | Created by `-load-config` from a repository's copy, when the project has none yet. |
3942 | `~/.config/turbo-moonbit/themes/*.toml` | User themes on Linux (`os.UserConfigDir`). |
4043 | `~/Library/Application Support/turbo-moonbit/themes/*.toml` | User themes on macOS. |
@@ -45,7 +48,7 @@ Each `file` is opened in its own window. A file that does not exist yet is opene
4548 | Status | Meaning |
4649 | --- | --- |
4750 | `0` | The editor exited normally, or an informational flag was used. |
48-| `1` | The terminal could not be opened or initialised. The reason is printed to standard error. |
51+| `1` | The terminal could not be opened or initialised, or the `-ssh` connection failed. The reason is printed to standard error. |
4952
5053 ## Make targets
5154
@@ -75,6 +78,8 @@ turbo-moonbit -theme turbo-dark main.mbt # a different theme
7578 turbo-moonbit -no-lsp main.mbt # no language server
7679 turbo-moonbit -list-themes # what themes exist
7780 turbo-moonbit -load-config https://rickub.com/turbo-editors/configs/tree/main/golang-init # start from a shared configuration
81+turbo-moonbit -ssh bob@build-box:/srv/app # the files of another machine, over ssh
82+turbo-moonbit --ssh demo main.py # a Host alias from ~/.ssh/config, opening main.py there
7883 ```
7984
8085 ## Installer
@@ -105,3 +110,10 @@ turbo-moonbit -load-config https://rickub.com/turbo-editors/configs/tree/main/go
105110 | `turbo-moonbit: configrepo: git is not installed` | `-load-config` needs the `git` command on `PATH`. |
106111 | `turbo-moonbit: configrepo: no configuration directory at that path: …` | The repository was found but holds no `.turbo-moonbit` where the URL points. |
107112 | `turbo-moonbit: configrepo: no repository answered: …` | No clone address for that URL answered; the URL, the network, or a private repository. |
113+| `turbo-moonbit: "<arg>": not a remote target; expected user@machine or user@machine:/path` | The `-ssh` argument is empty, starts with `-`, or holds a space or `/` in the host or user. Nothing was run. |
114+| `turbo-moonbit: no ssh command found on PATH …` | `-ssh` needs the `ssh` command. |
115+| `turbo-moonbit: editing over ssh needs OpenSSH's ControlMaster, …` | `-ssh` on Windows. |
116+| `turbo-moonbit: ssh <destination>: exit status 255` | The login failed — wrong password, unknown host, refused key. ssh has printed why just above. |
117+| `turbo-moonbit: finding <dir> on the remote machine: …` | The directory after the colon does not exist there. |
118+| `turbo-moonbit: <dir> on the remote machine is not a directory: …` | The path after the colon names a file. |
119+| `turbo-moonbit: opening the file channel to <destination>: …` | The server does not offer the `sftp` subsystem. |
@@ -19,6 +19,7 @@ Each `file` is opened in its own window. A file that does not exist yet is opene
19 | `-no-lsp` | bool | `false` | Do not start a language server. Colouring and editing are unaffected. |19 | `-no-lsp` | bool | `false` | Do not start a language server. Colouring and editing are unaffected. |
20 | `-version` | bool | `false` | Print `Turbo MoonBit <version>` on one line, with the commit and build date when the build recorded them, and exit. See [the version number](versioning.md). |20 | `-version` | bool | `false` | Print `Turbo MoonBit <version>` on one line, with the commit and build date when the build recorded them, and exit. See [the version number](versioning.md). |
21 | `-load-config <url>` | string | none | Copy the `.turbo-moonbit` directory found at a repository URL — rickub, GitHub, GitLab or Codeberg, the URL of the directory as the browser shows it — into the working directory, print what arrived, and exit. Needs `git`. Refuses when `./.turbo-moonbit` already exists. The editor is not started. |21 | `-load-config <url>` | string | none | Copy the `.turbo-moonbit` directory found at a repository URL — rickub, GitHub, GitLab or Codeberg, the URL of the directory as the browser shows it — into the working directory, print what arrived, and exit. Needs `git`. Refuses when `./.turbo-moonbit` already exists. The editor is not started. |
22+| `-ssh <target>`, `--ssh <target>` | string | none | Edit the files of another machine over ssh. `<target>` is `[user@]host[:directory]`, as scp writes it; no directory means the remote home, a relative one is relative to it. Before the screen is taken, prints `Connecting to <target>…` and runs `ssh`, which asks on the terminal for a password or passphrase if it needs one. The project is then that directory: its files, tree, `.turbo-moonbit/` files, terminal windows and tools are the remote machine's. No language server is started, and agent windows are unavailable. Needs OpenSSH's `ssh` on `PATH`, on Linux or macOS. See [edit a remote machine](../how-to/edit-a-remote-machine.md). |
22 | `-h`, `-help` | bool | `false` | Print the flag list and exit. |23 | `-h`, `-help` | bool | `false` | Print the flag list and exit. |
23 24
24 ## Environment25 ## Environment
@@ -35,6 +36,8 @@ Each `file` is opened in its own window. A file that does not exist yet is opene
35 | --- | --- |36 | --- | --- |
36 | `$TURBO_MOONBIT_THEME_DIR/*.toml` | User themes, when the variable is set. |37 | `$TURBO_MOONBIT_THEME_DIR/*.toml` | User themes, when the variable is set. |
37 | `./.turbo-moonbit/settings.toml` | This project's settings, read once at start-up. See [project settings](project-settings.md). |38 | `./.turbo-moonbit/settings.toml` | This project's settings, read once at start-up. See [project settings](project-settings.md). |
39+| `<directory>/.turbo-moonbit/*.toml` on the remote machine | Under `-ssh`: the project's settings, snippets and tools, read and written over SFTP. |
40+| `~/.ssh/config` | Under `-ssh`: read by `ssh` — `Host` aliases, `Port`, `User`, `IdentityFile`, `ProxyJump`. |
38 | `./.turbo-moonbit/` (written) | Created by `-load-config` from a repository's copy, when the project has none yet. |41 | `./.turbo-moonbit/` (written) | Created by `-load-config` from a repository's copy, when the project has none yet. |
39 | `~/.config/turbo-moonbit/themes/*.toml` | User themes on Linux (`os.UserConfigDir`). |42 | `~/.config/turbo-moonbit/themes/*.toml` | User themes on Linux (`os.UserConfigDir`). |
40 | `~/Library/Application Support/turbo-moonbit/themes/*.toml` | User themes on macOS. |43 | `~/Library/Application Support/turbo-moonbit/themes/*.toml` | User themes on macOS. |
@@ -45,7 +48,7 @@ Each `file` is opened in its own window. A file that does not exist yet is opene
45 | Status | Meaning |48 | Status | Meaning |
46 | --- | --- |49 | --- | --- |
47 | `0` | The editor exited normally, or an informational flag was used. |50 | `0` | The editor exited normally, or an informational flag was used. |
48-| `1` | The terminal could not be opened or initialised. The reason is printed to standard error. |51+| `1` | The terminal could not be opened or initialised, or the `-ssh` connection failed. The reason is printed to standard error. |
49 52
50 ## Make targets53 ## Make targets
51 54
@@ -75,6 +78,8 @@ turbo-moonbit -theme turbo-dark main.mbt # a different theme
75 turbo-moonbit -no-lsp main.mbt # no language server78 turbo-moonbit -no-lsp main.mbt # no language server
76 turbo-moonbit -list-themes # what themes exist79 turbo-moonbit -list-themes # what themes exist
77 turbo-moonbit -load-config https://rickub.com/turbo-editors/configs/tree/main/golang-init # start from a shared configuration80 turbo-moonbit -load-config https://rickub.com/turbo-editors/configs/tree/main/golang-init # start from a shared configuration
81+turbo-moonbit -ssh bob@build-box:/srv/app # the files of another machine, over ssh
82+turbo-moonbit --ssh demo main.py # a Host alias from ~/.ssh/config, opening main.py there
78 ```83 ```
79 84
80 ## Installer85 ## Installer
@@ -105,3 +110,10 @@ turbo-moonbit -load-config https://rickub.com/turbo-editors/configs/tree/main/go
105 | `turbo-moonbit: configrepo: git is not installed` | `-load-config` needs the `git` command on `PATH`. |110 | `turbo-moonbit: configrepo: git is not installed` | `-load-config` needs the `git` command on `PATH`. |
106 | `turbo-moonbit: configrepo: no configuration directory at that path: …` | The repository was found but holds no `.turbo-moonbit` where the URL points. |111 | `turbo-moonbit: configrepo: no configuration directory at that path: …` | The repository was found but holds no `.turbo-moonbit` where the URL points. |
107 | `turbo-moonbit: configrepo: no repository answered: …` | No clone address for that URL answered; the URL, the network, or a private repository. |112 | `turbo-moonbit: configrepo: no repository answered: …` | No clone address for that URL answered; the URL, the network, or a private repository. |
113+| `turbo-moonbit: "<arg>": not a remote target; expected user@machine or user@machine:/path` | The `-ssh` argument is empty, starts with `-`, or holds a space or `/` in the host or user. Nothing was run. |
114+| `turbo-moonbit: no ssh command found on PATH …` | `-ssh` needs the `ssh` command. |
115+| `turbo-moonbit: editing over ssh needs OpenSSH's ControlMaster, …` | `-ssh` on Windows. |
116+| `turbo-moonbit: ssh <destination>: exit status 255` | The login failed — wrong password, unknown host, refused key. ssh has printed why just above. |
117+| `turbo-moonbit: finding <dir> on the remote machine: …` | The directory after the colon does not exist there. |
118+| `turbo-moonbit: <dir> on the remote machine is not a directory: …` | The path after the colon names a file. |
119+| `turbo-moonbit: opening the file channel to <destination>: …` | The server does not offer the `sftp` subsystem. |
modified docs/fr/README.md +1 -0
@@ -30,6 +30,7 @@ Cette documentation suit la méthode [Diátaxis](https://diataxis.fr). Quatre ty
3030 - [Lancer des commandes shell sans quitter l'éditeur](how-to/use-a-terminal.md)
3131 - [Donner ses propres réglages à un projet](how-to/configure-a-project.md)
3232 - [Parcourir un projet et ouvrir des fichiers depuis un arbre](how-to/browse-a-project.md)
33+- [Éditer les fichiers d'une machine distante via ssh](how-to/edit-a-remote-machine.md)
3334 - [Insérer des snippets depuis un menu](how-to/use-snippets.md)
3435 - [Lancer les commandes moon depuis l'éditeur](how-to/run-moon-commands.md)
3536 - [Faire une release](how-to/make-a-release.md)
@@ -30,6 +30,7 @@ Cette documentation suit la méthode [Diátaxis](https://diataxis.fr). Quatre ty
30 - [Lancer des commandes shell sans quitter l'éditeur](how-to/use-a-terminal.md)30 - [Lancer des commandes shell sans quitter l'éditeur](how-to/use-a-terminal.md)
31 - [Donner ses propres réglages à un projet](how-to/configure-a-project.md)31 - [Donner ses propres réglages à un projet](how-to/configure-a-project.md)
32 - [Parcourir un projet et ouvrir des fichiers depuis un arbre](how-to/browse-a-project.md)32 - [Parcourir un projet et ouvrir des fichiers depuis un arbre](how-to/browse-a-project.md)
33+- [Éditer les fichiers d'une machine distante via ssh](how-to/edit-a-remote-machine.md)
33 - [Insérer des snippets depuis un menu](how-to/use-snippets.md)34 - [Insérer des snippets depuis un menu](how-to/use-snippets.md)
34 - [Lancer les commandes moon depuis l'éditeur](how-to/run-moon-commands.md)35 - [Lancer les commandes moon depuis l'éditeur](how-to/run-moon-commands.md)
35 - [Faire une release](how-to/make-a-release.md)36 - [Faire une release](how-to/make-a-release.md)
added docs/fr/how-to/edit-a-remote-machine.md +55 -0
new file mode 100644
@@ -0,0 +1,55 @@
1+# Éditer les fichiers d'une machine distante via ssh
2+
3+Ce guide ouvre dans Turbo MoonBit un projet qui vit sur une autre machine — un serveur, une VM, un conteneur — de sorte que ses fichiers, son arborescence, ses fenêtres terminal et son menu MoonBit soient ceux de cette machine. Il suppose que vous savez déjà vous connecter à la machine avec `ssh`.
4+
5+## Étapes
6+
7+1. Vérifiez que ssh fonctionne tout seul, depuis le terminal où vous lancerez l'éditeur :
8+
9+ ```bash
10+ ssh bob@build-box true
11+ ```
12+
13+ Répondez ici d'abord à toute question qu'il pose — nouvelle clé d'hôte, mot de passe : c'est le même ssh que lance l'éditeur.
14+
15+2. Lancez l'éditeur avec la machine et le répertoire :
16+
17+ ```bash
18+ turbo-moonbit -ssh bob@build-box:/srv/app
19+ ```
20+
21+ Il affiche `Connecting to bob@build-box:/srv/app…`. Si ssh a besoin d'un mot de passe ou de la passphrase d'une clé, il la demande maintenant, sur le terminal, une seule fois. L'éditeur s'ouvre ensuite, et la barre d'état se termine par `⇄ bob@build-box`.
22+
23+3. Travaillez comme d'habitude. `F3` ouvre les fichiers distants, `F9` montre l'arborescence distante, `F2` enregistre sur la machine distante, `F8` y ouvre un shell dans le répertoire du projet, et le menu MoonBit y lance `.turbo-moonbit/tools.toml`, avec `/bin/sh`, dans le répertoire du projet.
24+
25+4. Quittez avec `Alt-X`. La connexion se ferme avec l'éditeur.
26+
27+## Variantes
28+
29+- **Un autre répertoire.** `bob@build-box` seul ouvre le home de bob ; `bob@build-box:app` et `bob@build-box:~/app` ouvrent `~/app` ; `bob@build-box:/srv/app` un chemin absolu.
30+- **Des fichiers à ouvrir d'emblée.** Nommez-les après la cible, relativement au répertoire distant : `turbo-moonbit -ssh bob@build-box:/srv/app main.py`.
31+- **Deux tirets.** `--ssh` est la même option que `-ssh`, et `--ssh=bob@build-box` fonctionne aussi.
32+- **Un port, une clé, un rebond.** La cible n'a pas de syntaxe de port, comme avec scp. Donnez à la machine un bloc `Host` dans `~/.ssh/config` et utilisez son nom :
33+
34+ ```
35+ Host build-box
36+ HostName 192.0.2.10
37+ Port 2222
38+ User bob
39+ IdentityFile ~/.ssh/id_build
40+ ```
41+
42+ puis `turbo-moonbit -ssh build-box:/srv/app`.
43+- **Un mot de passe plutôt qu'une clé.** Rien à faire : ssh le demande avant l'ouverture de l'éditeur. Pour l'imposer alors que des clés sont chargées, ajoutez `PubkeyAuthentication no` au bloc de cet hôte.
44+- **Essayer sans serveur.** `turbo-editors/demo-remote-machine` est une machine Docker Compose qui accepte une clé et un mot de passe ; son README donne les commandes exactes.
45+
46+## Ce qui n'est pas disponible via ssh
47+
48+- **La complétion et les diagnostics.** Aucun serveur de langage n'est lancé — `moon-lsp` devrait tourner sur la machine distante. La barre d'état indique `LSP: off`.
49+- **Les fenêtres d'agent.** Le menu Agent indique qu'elles ne sont pas disponibles via ssh.
50+- **Windows** comme machine où tourne l'éditeur : son ssh n'a pas de ControlMaster.
51+
52+## Voir aussi
53+
54+- L'option et ses erreurs : [ligne de commande](../reference/cli.md)
55+- Les fenêtres terminal qu'il ouvre : [terminal](../reference/terminal.md)
new file mode 100644
@@ -0,0 +1,55 @@
1+# Éditer les fichiers d'une machine distante via ssh
2+
3+Ce guide ouvre dans Turbo MoonBit un projet qui vit sur une autre machine — un serveur, une VM, un conteneur — de sorte que ses fichiers, son arborescence, ses fenêtres terminal et son menu MoonBit soient ceux de cette machine. Il suppose que vous savez déjà vous connecter à la machine avec `ssh`.
4+
5+## Étapes
6+
7+1. Vérifiez que ssh fonctionne tout seul, depuis le terminal où vous lancerez l'éditeur :
8+
9+ ```bash
10+ ssh bob@build-box true
11+ ```
12+
13+ Répondez ici d'abord à toute question qu'il pose — nouvelle clé d'hôte, mot de passe : c'est le même ssh que lance l'éditeur.
14+
15+2. Lancez l'éditeur avec la machine et le répertoire :
16+
17+ ```bash
18+ turbo-moonbit -ssh bob@build-box:/srv/app
19+ ```
20+
21+ Il affiche `Connecting to bob@build-box:/srv/app…`. Si ssh a besoin d'un mot de passe ou de la passphrase d'une clé, il la demande maintenant, sur le terminal, une seule fois. L'éditeur s'ouvre ensuite, et la barre d'état se termine par `⇄ bob@build-box`.
22+
23+3. Travaillez comme d'habitude. `F3` ouvre les fichiers distants, `F9` montre l'arborescence distante, `F2` enregistre sur la machine distante, `F8` y ouvre un shell dans le répertoire du projet, et le menu MoonBit y lance `.turbo-moonbit/tools.toml`, avec `/bin/sh`, dans le répertoire du projet.
24+
25+4. Quittez avec `Alt-X`. La connexion se ferme avec l'éditeur.
26+
27+## Variantes
28+
29+- **Un autre répertoire.** `bob@build-box` seul ouvre le home de bob ; `bob@build-box:app` et `bob@build-box:~/app` ouvrent `~/app` ; `bob@build-box:/srv/app` un chemin absolu.
30+- **Des fichiers à ouvrir d'emblée.** Nommez-les après la cible, relativement au répertoire distant : `turbo-moonbit -ssh bob@build-box:/srv/app main.py`.
31+- **Deux tirets.** `--ssh` est la même option que `-ssh`, et `--ssh=bob@build-box` fonctionne aussi.
32+- **Un port, une clé, un rebond.** La cible n'a pas de syntaxe de port, comme avec scp. Donnez à la machine un bloc `Host` dans `~/.ssh/config` et utilisez son nom :
33+
34+ ```
35+ Host build-box
36+ HostName 192.0.2.10
37+ Port 2222
38+ User bob
39+ IdentityFile ~/.ssh/id_build
40+ ```
41+
42+ puis `turbo-moonbit -ssh build-box:/srv/app`.
43+- **Un mot de passe plutôt qu'une clé.** Rien à faire : ssh le demande avant l'ouverture de l'éditeur. Pour l'imposer alors que des clés sont chargées, ajoutez `PubkeyAuthentication no` au bloc de cet hôte.
44+- **Essayer sans serveur.** `turbo-editors/demo-remote-machine` est une machine Docker Compose qui accepte une clé et un mot de passe ; son README donne les commandes exactes.
45+
46+## Ce qui n'est pas disponible via ssh
47+
48+- **La complétion et les diagnostics.** Aucun serveur de langage n'est lancé — `moon-lsp` devrait tourner sur la machine distante. La barre d'état indique `LSP: off`.
49+- **Les fenêtres d'agent.** Le menu Agent indique qu'elles ne sont pas disponibles via ssh.
50+- **Windows** comme machine où tourne l'éditeur : son ssh n'a pas de ControlMaster.
51+
52+## Voir aussi
53+
54+- L'option et ses erreurs : [ligne de commande](../reference/cli.md)
55+- Les fenêtres terminal qu'il ouvre : [terminal](../reference/terminal.md)
modified docs/fr/reference/cli.md +13 -1
@@ -19,6 +19,7 @@ Chaque `fichier` est ouvert dans sa propre fenêtre. Un fichier qui n'existe pas
1919 | `-no-lsp` | booléen | `false` | Ne démarre pas de serveur de langage. La coloration et l'édition sont inchangées. |
2020 | `-version` | booléen | `false` | Affiche `Turbo MoonBit <version>` sur une ligne, avec le commit et la date de build quand le build les a enregistrés, puis quitte. Voir [le numéro de version](versioning.md). |
2121 | `-load-config <url>` | chaîne | aucune | Copie le répertoire `.turbo-moonbit` trouvé à l'URL d'un dépôt — rickub, GitHub, GitLab ou Codeberg, l'URL du dossier telle que le navigateur l'affiche — dans le répertoire courant, affiche ce qui est arrivé, et quitte. Nécessite `git`. Refuse si `./.turbo-moonbit` existe déjà. L'éditeur n'est pas lancé. |
22+| `-ssh <cible>`, `--ssh <cible>` | chaîne | aucune | Édite les fichiers d'une autre machine via ssh. `<cible>` s'écrit `[user@]hôte[:répertoire]`, comme avec scp ; sans répertoire, c'est le home distant, et un répertoire relatif est relatif à lui. Avant de prendre l'écran, affiche `Connecting to <cible>…` et lance `ssh`, qui demande sur le terminal un mot de passe ou une passphrase s'il en a besoin. Le projet est alors ce répertoire : ses fichiers, son arborescence, ses fichiers `.turbo-moonbit/`, les fenêtres terminal et les outils sont ceux de la machine distante. Aucun serveur de langage n'est lancé, et les fenêtres d'agent sont indisponibles. Nécessite le `ssh` d'OpenSSH dans le `PATH`, sous Linux ou macOS. Voir [éditer une machine distante](../how-to/edit-a-remote-machine.md). |
2223 | `-h`, `-help` | booléen | `false` | Affiche la liste des options et quitte. |
2324
2425 ## Environnement
@@ -35,6 +36,8 @@ Chaque `fichier` est ouvert dans sa propre fenêtre. Un fichier qui n'existe pas
3536 | --- | --- |
3637 | `$TURBO_MOONBIT_THEME_DIR/*.toml` | Thèmes utilisateur, quand la variable est définie. |
3738 | `./.turbo-moonbit/settings.toml` | Les réglages de ce projet, lus une fois au démarrage. Voir [réglages de projet](project-settings.md). |
39+| `<répertoire>/.turbo-moonbit/*.toml` sur la machine distante | Avec `-ssh` : les réglages, extraits et outils du projet, lus et écrits en SFTP. |
40+| `~/.ssh/config` | Avec `-ssh` : lu par `ssh` — alias `Host`, `Port`, `User`, `IdentityFile`, `ProxyJump`. |
3841 | `./.turbo-moonbit/` (écrit) | Créé par `-load-config` à partir de la copie d'un dépôt, quand le projet n'en a pas encore. |
3942 | `~/.config/turbo-moonbit/themes/*.toml` | Thèmes utilisateur sous Linux (`os.UserConfigDir`). |
4043 | `~/Library/Application Support/turbo-moonbit/themes/*.toml` | Thèmes utilisateur sous macOS. |
@@ -45,7 +48,7 @@ Chaque `fichier` est ouvert dans sa propre fenêtre. Un fichier qui n'existe pas
4548 | Code | Signification |
4649 | --- | --- |
4750 | `0` | L'éditeur s'est terminé normalement, ou une option d'information a été utilisée. |
48-| `1` | Le terminal n'a pas pu être ouvert ou initialisé. La raison est écrite sur la sortie d'erreur. |
51+| `1` | Le terminal n'a pas pu être ouvert ou initialisé, ou la connexion `-ssh` a échoué. La raison est écrite sur la sortie d'erreur. |
4952
5053 ## Cibles make
5154
@@ -75,6 +78,8 @@ turbo-moonbit -theme turbo-dark main.mbt # un autre thème
7578 turbo-moonbit -no-lsp main.mbt # sans serveur de langage
7679 turbo-moonbit -list-themes # quels thèmes existent
7780 turbo-moonbit -load-config https://rickub.com/turbo-editors/configs/tree/main/golang-init # partir d'une configuration partagée
81+turbo-moonbit -ssh bob@build-box:/srv/app # les fichiers d'une autre machine, via ssh
82+turbo-moonbit --ssh demo main.py # un alias Host de ~/.ssh/config, en y ouvrant main.py
7883 ```
7984
8085 ## Installateur
@@ -105,3 +110,10 @@ turbo-moonbit -load-config https://rickub.com/turbo-editors/configs/tree/main/go
105110 | `turbo-moonbit: configrepo: git is not installed` | `-load-config` a besoin de la commande `git` dans le `PATH`. |
106111 | `turbo-moonbit: configrepo: no configuration directory at that path: …` | Le dépôt existe mais ne contient pas de `.turbo-moonbit` là où l'URL pointe. |
107112 | `turbo-moonbit: configrepo: no repository answered: …` | Aucune adresse de clone pour cette URL n'a répondu : l'URL, le réseau, ou un dépôt privé. |
113+| `turbo-moonbit: "<arg>": not a remote target; expected user@machine or user@machine:/path` | L'argument de `-ssh` est vide, commence par `-`, ou contient une espace ou un `/` dans l'hôte ou l'utilisateur. Rien n'a été lancé. |
114+| `turbo-moonbit: no ssh command found on PATH …` | `-ssh` a besoin de la commande `ssh`. |
115+| `turbo-moonbit: editing over ssh needs OpenSSH's ControlMaster, …` | `-ssh` sous Windows. |
116+| `turbo-moonbit: ssh <destination>: exit status 255` | La connexion a échoué — mauvais mot de passe, hôte inconnu, clé refusée. ssh a écrit pourquoi juste au-dessus. |
117+| `turbo-moonbit: finding <dir> on the remote machine: …` | Le répertoire après les deux-points n'existe pas là-bas. |
118+| `turbo-moonbit: <dir> on the remote machine is not a directory: …` | Le chemin après les deux-points désigne un fichier. |
119+| `turbo-moonbit: opening the file channel to <destination>: …` | Le serveur ne propose pas le sous-système `sftp`. |
@@ -19,6 +19,7 @@ Chaque `fichier` est ouvert dans sa propre fenêtre. Un fichier qui n'existe pas
19 | `-no-lsp` | booléen | `false` | Ne démarre pas de serveur de langage. La coloration et l'édition sont inchangées. |19 | `-no-lsp` | booléen | `false` | Ne démarre pas de serveur de langage. La coloration et l'édition sont inchangées. |
20 | `-version` | booléen | `false` | Affiche `Turbo MoonBit <version>` sur une ligne, avec le commit et la date de build quand le build les a enregistrés, puis quitte. Voir [le numéro de version](versioning.md). |20 | `-version` | booléen | `false` | Affiche `Turbo MoonBit <version>` sur une ligne, avec le commit et la date de build quand le build les a enregistrés, puis quitte. Voir [le numéro de version](versioning.md). |
21 | `-load-config <url>` | chaîne | aucune | Copie le répertoire `.turbo-moonbit` trouvé à l'URL d'un dépôt — rickub, GitHub, GitLab ou Codeberg, l'URL du dossier telle que le navigateur l'affiche — dans le répertoire courant, affiche ce qui est arrivé, et quitte. Nécessite `git`. Refuse si `./.turbo-moonbit` existe déjà. L'éditeur n'est pas lancé. |21 | `-load-config <url>` | chaîne | aucune | Copie le répertoire `.turbo-moonbit` trouvé à l'URL d'un dépôt — rickub, GitHub, GitLab ou Codeberg, l'URL du dossier telle que le navigateur l'affiche — dans le répertoire courant, affiche ce qui est arrivé, et quitte. Nécessite `git`. Refuse si `./.turbo-moonbit` existe déjà. L'éditeur n'est pas lancé. |
22+| `-ssh <cible>`, `--ssh <cible>` | chaîne | aucune | Édite les fichiers d'une autre machine via ssh. `<cible>` s'écrit `[user@]hôte[:répertoire]`, comme avec scp ; sans répertoire, c'est le home distant, et un répertoire relatif est relatif à lui. Avant de prendre l'écran, affiche `Connecting to <cible>…` et lance `ssh`, qui demande sur le terminal un mot de passe ou une passphrase s'il en a besoin. Le projet est alors ce répertoire : ses fichiers, son arborescence, ses fichiers `.turbo-moonbit/`, les fenêtres terminal et les outils sont ceux de la machine distante. Aucun serveur de langage n'est lancé, et les fenêtres d'agent sont indisponibles. Nécessite le `ssh` d'OpenSSH dans le `PATH`, sous Linux ou macOS. Voir [éditer une machine distante](../how-to/edit-a-remote-machine.md). |
22 | `-h`, `-help` | booléen | `false` | Affiche la liste des options et quitte. |23 | `-h`, `-help` | booléen | `false` | Affiche la liste des options et quitte. |
23 24
24 ## Environnement25 ## Environnement
@@ -35,6 +36,8 @@ Chaque `fichier` est ouvert dans sa propre fenêtre. Un fichier qui n'existe pas
35 | --- | --- |36 | --- | --- |
36 | `$TURBO_MOONBIT_THEME_DIR/*.toml` | Thèmes utilisateur, quand la variable est définie. |37 | `$TURBO_MOONBIT_THEME_DIR/*.toml` | Thèmes utilisateur, quand la variable est définie. |
37 | `./.turbo-moonbit/settings.toml` | Les réglages de ce projet, lus une fois au démarrage. Voir [réglages de projet](project-settings.md). |38 | `./.turbo-moonbit/settings.toml` | Les réglages de ce projet, lus une fois au démarrage. Voir [réglages de projet](project-settings.md). |
39+| `<répertoire>/.turbo-moonbit/*.toml` sur la machine distante | Avec `-ssh` : les réglages, extraits et outils du projet, lus et écrits en SFTP. |
40+| `~/.ssh/config` | Avec `-ssh` : lu par `ssh` — alias `Host`, `Port`, `User`, `IdentityFile`, `ProxyJump`. |
38 | `./.turbo-moonbit/` (écrit) | Créé par `-load-config` à partir de la copie d'un dépôt, quand le projet n'en a pas encore. |41 | `./.turbo-moonbit/` (écrit) | Créé par `-load-config` à partir de la copie d'un dépôt, quand le projet n'en a pas encore. |
39 | `~/.config/turbo-moonbit/themes/*.toml` | Thèmes utilisateur sous Linux (`os.UserConfigDir`). |42 | `~/.config/turbo-moonbit/themes/*.toml` | Thèmes utilisateur sous Linux (`os.UserConfigDir`). |
40 | `~/Library/Application Support/turbo-moonbit/themes/*.toml` | Thèmes utilisateur sous macOS. |43 | `~/Library/Application Support/turbo-moonbit/themes/*.toml` | Thèmes utilisateur sous macOS. |
@@ -45,7 +48,7 @@ Chaque `fichier` est ouvert dans sa propre fenêtre. Un fichier qui n'existe pas
45 | Code | Signification |48 | Code | Signification |
46 | --- | --- |49 | --- | --- |
47 | `0` | L'éditeur s'est terminé normalement, ou une option d'information a été utilisée. |50 | `0` | L'éditeur s'est terminé normalement, ou une option d'information a été utilisée. |
48-| `1` | Le terminal n'a pas pu être ouvert ou initialisé. La raison est écrite sur la sortie d'erreur. |51+| `1` | Le terminal n'a pas pu être ouvert ou initialisé, ou la connexion `-ssh` a échoué. La raison est écrite sur la sortie d'erreur. |
49 52
50 ## Cibles make53 ## Cibles make
51 54
@@ -75,6 +78,8 @@ turbo-moonbit -theme turbo-dark main.mbt # un autre thème
75 turbo-moonbit -no-lsp main.mbt # sans serveur de langage78 turbo-moonbit -no-lsp main.mbt # sans serveur de langage
76 turbo-moonbit -list-themes # quels thèmes existent79 turbo-moonbit -list-themes # quels thèmes existent
77 turbo-moonbit -load-config https://rickub.com/turbo-editors/configs/tree/main/golang-init # partir d'une configuration partagée80 turbo-moonbit -load-config https://rickub.com/turbo-editors/configs/tree/main/golang-init # partir d'une configuration partagée
81+turbo-moonbit -ssh bob@build-box:/srv/app # les fichiers d'une autre machine, via ssh
82+turbo-moonbit --ssh demo main.py # un alias Host de ~/.ssh/config, en y ouvrant main.py
78 ```83 ```
79 84
80 ## Installateur85 ## Installateur
@@ -105,3 +110,10 @@ turbo-moonbit -load-config https://rickub.com/turbo-editors/configs/tree/main/go
105 | `turbo-moonbit: configrepo: git is not installed` | `-load-config` a besoin de la commande `git` dans le `PATH`. |110 | `turbo-moonbit: configrepo: git is not installed` | `-load-config` a besoin de la commande `git` dans le `PATH`. |
106 | `turbo-moonbit: configrepo: no configuration directory at that path: …` | Le dépôt existe mais ne contient pas de `.turbo-moonbit` là où l'URL pointe. |111 | `turbo-moonbit: configrepo: no configuration directory at that path: …` | Le dépôt existe mais ne contient pas de `.turbo-moonbit` là où l'URL pointe. |
107 | `turbo-moonbit: configrepo: no repository answered: …` | Aucune adresse de clone pour cette URL n'a répondu : l'URL, le réseau, ou un dépôt privé. |112 | `turbo-moonbit: configrepo: no repository answered: …` | Aucune adresse de clone pour cette URL n'a répondu : l'URL, le réseau, ou un dépôt privé. |
113+| `turbo-moonbit: "<arg>": not a remote target; expected user@machine or user@machine:/path` | L'argument de `-ssh` est vide, commence par `-`, ou contient une espace ou un `/` dans l'hôte ou l'utilisateur. Rien n'a été lancé. |
114+| `turbo-moonbit: no ssh command found on PATH …` | `-ssh` a besoin de la commande `ssh`. |
115+| `turbo-moonbit: editing over ssh needs OpenSSH's ControlMaster, …` | `-ssh` sous Windows. |
116+| `turbo-moonbit: ssh <destination>: exit status 255` | La connexion a échoué — mauvais mot de passe, hôte inconnu, clé refusée. ssh a écrit pourquoi juste au-dessus. |
117+| `turbo-moonbit: finding <dir> on the remote machine: …` | Le répertoire après les deux-points n'existe pas là-bas. |
118+| `turbo-moonbit: <dir> on the remote machine is not a directory: …` | Le chemin après les deux-points désigne un fichier. |
119+| `turbo-moonbit: opening the file channel to <destination>: …` | Le serveur ne propose pas le sous-système `sftp`. |
modified go.mod +7 -4
@@ -4,17 +4,20 @@ go 1.26.1
44
55 require (
66 github.com/gdamore/tcell/v2 v2.13.10
7- rickub.com/turbo-editors/turbo-core v1.0.4
7+ rickub.com/turbo-editors/turbo-core v1.0.5
88 )
99
1010 require (
1111 github.com/BurntSushi/toml v1.6.0 // indirect
1212 github.com/gdamore/encoding v1.0.1 // indirect
13+ github.com/kr/fs v0.1.0 // indirect
1314 github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
15+ github.com/pkg/sftp v1.13.11 // indirect
1416 github.com/rivo/uniseg v0.4.7 // indirect
15- golang.org/x/sys v0.38.0 // indirect
16- golang.org/x/term v0.37.0 // indirect
17- golang.org/x/text v0.31.0 // indirect
17+ golang.org/x/crypto v0.54.0 // indirect
18+ golang.org/x/sys v0.47.0 // indirect
19+ golang.org/x/term v0.45.0 // indirect
20+ golang.org/x/text v0.40.0 // indirect
1821 )
1922
2023 // turbo-core is developed alongside the editors that use it. Point this at the
@@ -4,17 +4,20 @@ go 1.26.1
4 4
5 require (5 require (
6 github.com/gdamore/tcell/v2 v2.13.106 github.com/gdamore/tcell/v2 v2.13.10
7- rickub.com/turbo-editors/turbo-core v1.0.47+ rickub.com/turbo-editors/turbo-core v1.0.5
8 )8 )
9 9
10 require (10 require (
11 github.com/BurntSushi/toml v1.6.0 // indirect11 github.com/BurntSushi/toml v1.6.0 // indirect
12 github.com/gdamore/encoding v1.0.1 // indirect12 github.com/gdamore/encoding v1.0.1 // indirect
13+ github.com/kr/fs v0.1.0 // indirect
13 github.com/lucasb-eyer/go-colorful v1.3.0 // indirect14 github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
15+ github.com/pkg/sftp v1.13.11 // indirect
14 github.com/rivo/uniseg v0.4.7 // indirect16 github.com/rivo/uniseg v0.4.7 // indirect
15- golang.org/x/sys v0.38.0 // indirect17+ golang.org/x/crypto v0.54.0 // indirect
16- golang.org/x/term v0.37.0 // indirect18+ golang.org/x/sys v0.47.0 // indirect
17- golang.org/x/text v0.31.0 // indirect19+ golang.org/x/term v0.45.0 // indirect
20+ golang.org/x/text v0.40.0 // indirect
18 )21 )
19 22
20 // turbo-core is developed alongside the editors that use it. Point this at the23 // turbo-core is developed alongside the editors that use it. Point this at the
modified go.sum +22 -8
@@ -1,16 +1,28 @@
11 github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
22 github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
3+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
4+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
35 github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw=
46 github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo=
57 github.com/gdamore/tcell/v2 v2.13.10 h1:Afs3JKt83HnhuUKdZ3MnxUgOqQRWftj5JyDqv1LLynA=
68 github.com/gdamore/tcell/v2 v2.13.10/go.mod h1:+Wfe208WDdB7INEtCsNrAN6O2m+wsTPk1RAovjaILlo=
9+github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
10+github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
711 github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
812 github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
13+github.com/pkg/sftp v1.13.11 h1:0N92SLTB8JqASJB14ZLHHzFnBV8mG9zw4K7jghEFWuE=
14+github.com/pkg/sftp v1.13.11/go.mod h1:uNkH9roSXglNJqM+glJJi+TQXQUm0fXFWqCFmT8hsN0=
15+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
16+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
917 github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
1018 github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
19+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
20+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
1121 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
1222 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
1323 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
24+golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
25+golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
1426 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
1527 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
1628 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -26,24 +38,26 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
2638 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
2739 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
2840 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
29-golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
30-golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
41+golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
42+golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
3143 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
3244 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
3345 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
34-golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
35-golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
46+golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
47+golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
3648 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
3749 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
3850 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
3951 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
4052 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
41-golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
42-golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
53+golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
54+golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
4355 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
4456 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
4557 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
4658 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
4759 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
48-rickub.com/turbo-editors/turbo-core v1.0.4 h1:oxySn9S6aEoHC1HPrarkxCnW5fTcPPXym7dMjSLRKaY=
49-rickub.com/turbo-editors/turbo-core v1.0.4/go.mod h1:rmfIY5gsFEo3sC5IIJFapwsGvdKG6NjrD7ACmnNTKq8=
60+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
61+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
62+rickub.com/turbo-editors/turbo-core v1.0.5 h1:1CDKkW+xJpmNM8ab7KSMorXgYMsX5BTGaz0JgkxSO3E=
63+rickub.com/turbo-editors/turbo-core v1.0.5/go.mod h1:M+f/mLHiWhqQCIRr88JSPJ6nxY2ALkEhDUuTkXi79dM=
@@ -1,16 +1,28 @@
1 github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=1 github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
2 github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=2 github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
3+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
4+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3 github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw=5 github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw=
4 github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo=6 github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo=
5 github.com/gdamore/tcell/v2 v2.13.10 h1:Afs3JKt83HnhuUKdZ3MnxUgOqQRWftj5JyDqv1LLynA=7 github.com/gdamore/tcell/v2 v2.13.10 h1:Afs3JKt83HnhuUKdZ3MnxUgOqQRWftj5JyDqv1LLynA=
6 github.com/gdamore/tcell/v2 v2.13.10/go.mod h1:+Wfe208WDdB7INEtCsNrAN6O2m+wsTPk1RAovjaILlo=8 github.com/gdamore/tcell/v2 v2.13.10/go.mod h1:+Wfe208WDdB7INEtCsNrAN6O2m+wsTPk1RAovjaILlo=
9+github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
10+github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
7 github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=11 github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
8 github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=12 github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
13+github.com/pkg/sftp v1.13.11 h1:0N92SLTB8JqASJB14ZLHHzFnBV8mG9zw4K7jghEFWuE=
14+github.com/pkg/sftp v1.13.11/go.mod h1:uNkH9roSXglNJqM+glJJi+TQXQUm0fXFWqCFmT8hsN0=
15+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
16+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
9 github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=17 github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
10 github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=18 github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
19+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
20+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
11 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=21 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
12 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=22 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
13 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=23 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
24+golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
25+golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
14 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=26 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
15 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=27 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
16 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=28 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -26,24 +38,26 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
26 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=38 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
27 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=39 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
28 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=40 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
29-golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=41+golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
30-golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=42+golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
31 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=43 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
32 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=44 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
33 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=45 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
34-golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=46+golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
35-golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=47+golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
36 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=48 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
37 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=49 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
38 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=50 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
39 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=51 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
40 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=52 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
41-golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=53+golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
42-golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=54+golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
43 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=55 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
44 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=56 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
45 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=57 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
46 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=58 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
47 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=59 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
48-rickub.com/turbo-editors/turbo-core v1.0.4 h1:oxySn9S6aEoHC1HPrarkxCnW5fTcPPXym7dMjSLRKaY=60+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
49-rickub.com/turbo-editors/turbo-core v1.0.4/go.mod h1:rmfIY5gsFEo3sC5IIJFapwsGvdKG6NjrD7ACmnNTKq8=61+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
62+rickub.com/turbo-editors/turbo-core v1.0.5 h1:1CDKkW+xJpmNM8ab7KSMorXgYMsX5BTGaz0JgkxSO3E=
63+rickub.com/turbo-editors/turbo-core v1.0.5/go.mod h1:M+f/mLHiWhqQCIRr88JSPJ6nxY2ALkEhDUuTkXi79dM=
modified main.go +55 -15
@@ -16,6 +16,7 @@
1616 // -list-themes print the available themes and exit
1717 // -no-lsp do not start a language server
1818 // -version print the version and exit
19+// -ssh user@machine[:dir] edit the files of another machine, over ssh (--ssh works too)
1920 // -load-config url copy the .turbo-moonbit directory found at a repository URL into the working directory and exit
2021 package main
2122
@@ -31,7 +32,9 @@ import (
3132
3233 "rickub.com/turbo-editors/turbo-core/app"
3334 "rickub.com/turbo-editors/turbo-core/configrepo"
35+ "rickub.com/turbo-editors/turbo-core/fsys"
3436 "rickub.com/turbo-editors/turbo-core/profile"
37+ "rickub.com/turbo-editors/turbo-core/remote"
3538 "rickub.com/turbo-editors/turbo-core/settings"
3639 "rickub.com/turbo-editors/turbo-core/theme"
3740 "rickub.com/turbo-editors/turbo-core/version"
@@ -51,26 +54,32 @@ type options struct {
5154 theme string
5255 listThemes bool
5356 loadConfig string
57+ ssh string
5458 noLSP bool
5559 version bool
5660 files []string
5761 }
5862
5963 // parseFlags reads the command line.
60-func parseFlags() options {
64+func parseFlags() options { return parseArguments(flag.CommandLine, os.Args[1:]) }
65+
66+// parseArguments reads arguments with flags. Go's flag package accepts every
67+// flag with one dash or two, which is why -ssh and --ssh are the same flag.
68+func parseArguments(flags *flag.FlagSet, arguments []string) options {
6169 var opts options
6270
6371 // The default is empty rather than the theme's name so that "was -theme
6472 // given?" can still be answered afterwards, which is what lets the project
6573 // settings fill it in without overriding an explicit choice.
66- flag.StringVar(&opts.theme, "theme", "", "colour theme to start with (default from the project, else "+theme.DefaultName+")")
67- flag.BoolVar(&opts.listThemes, "list-themes", false, "print the available themes and exit")
68- flag.BoolVar(&opts.noLSP, "no-lsp", false, "do not start a language server")
69- flag.BoolVar(&opts.version, "version", false, "print the version and exit")
70- flag.StringVar(&opts.loadConfig, "load-config", "", "copy the .turbo-moonbit directory found at this repository URL into the working directory, then exit")
71- flag.Parse()
72-
73- opts.files = flag.Args()
74+ flags.StringVar(&opts.theme, "theme", "", "colour theme to start with (default from the project, else "+theme.DefaultName+")")
75+ flags.BoolVar(&opts.listThemes, "list-themes", false, "print the available themes and exit")
76+ flags.BoolVar(&opts.noLSP, "no-lsp", false, "do not start a language server")
77+ flags.BoolVar(&opts.version, "version", false, "print the version and exit")
78+ flags.StringVar(&opts.loadConfig, "load-config", "", "copy the .turbo-moonbit directory found at this repository URL into the working directory, then exit")
79+ flags.StringVar(&opts.ssh, "ssh", "", "edit the files of another machine over ssh: user@machine[:directory]")
80+ _ = flags.Parse(arguments) // a FlagSet that exits on error never returns one
81+
82+ opts.files = flags.Args()
7483 return opts
7584 }
7685
@@ -147,6 +156,15 @@ func describeLoad(result configrepo.Result) string {
147156
148157 // edit opens the terminal and runs the editor until the user leaves.
149158 func edit(opts options, p profile.Profile) error {
159+ // Before the screen is taken: ssh may have a password to ask for.
160+ session, err := connectRemote(opts.ssh)
161+ if err != nil {
162+ return err
163+ }
164+ if session != nil {
165+ defer session.Close()
166+ }
167+
150168 project, projectSettings := loadProjectSettings(p)
151169
152170 screen, err := newScreen()
@@ -161,11 +179,16 @@ func edit(opts options, p profile.Profile) error {
161179 if settings.Exists(p, project) {
162180 editor.UseSettings(projectSettings, settings.Path(p, project))
163181 }
182+ if session != nil {
183+ editor.UseRemote(session)
184+ }
164185 openFiles(editor, opts.files)
165186
166187 ctx, cancel := context.WithCancel(context.Background())
167188 defer cancel()
168- if !opts.noLSP {
189+ // The language servers this editor finds are on this machine, and the
190+ // files they would be asked about are not: over ssh there is none.
191+ if !opts.noLSP && session == nil {
169192 editor.StartLanguageServer(ctx, app.ProjectRoot(p, opts.files))
170193 }
171194 defer editor.Language().Stop(context.Background())
@@ -173,16 +196,33 @@ func edit(opts options, p profile.Profile) error {
173196 return editor.Run()
174197 }
175198
199+// connectRemote logs in to the machine -ssh names and points every file the
200+// editor reads and writes at it, so that the settings, the tree, the Open
201+// dialog and each window see that machine's files. It returns nil, and
202+// changes nothing, when -ssh was not given.
203+func connectRemote(target string) (*remote.Session, error) {
204+ if target == "" {
205+ return nil, nil
206+ }
207+ session, err := remote.Open(context.Background(), target)
208+ if err != nil {
209+ return nil, err
210+ }
211+ fsys.Use(session.FS())
212+ return session, nil
213+}
214+
176215 // loadProjectSettings reads .turbo-moonbit/settings.toml from the working
177216 // directory, and returns that directory along with what it found.
178217 //
179218 // The working directory alone is looked in, with no walk up towards the root:
180-// "the project" is where you started the editor, which is a rule you can hold
181-// in your head. A file that is there but unreadable is reported on standard
182-// error and then ignored — a broken settings file must not stop the editor
183-// opening, because the editor is how you would fix it.
219+// "the project" is where you started the editor — or the directory given to
220+// -ssh — which is a rule you can hold in your head. A file that is there but
221+// unreadable is reported on standard error and then ignored — a broken
222+// settings file must not stop the editor opening, because the editor is how
223+// you would fix it.
184224 func loadProjectSettings(p profile.Profile) (string, settings.Settings) {
185- project, err := os.Getwd()
225+ project, err := fsys.Getwd()
186226 if err != nil {
187227 project = "."
188228 }
@@ -16,6 +16,7 @@
16 // -list-themes print the available themes and exit16 // -list-themes print the available themes and exit
17 // -no-lsp do not start a language server17 // -no-lsp do not start a language server
18 // -version print the version and exit18 // -version print the version and exit
19+// -ssh user@machine[:dir] edit the files of another machine, over ssh (--ssh works too)
19 // -load-config url copy the .turbo-moonbit directory found at a repository URL into the working directory and exit20 // -load-config url copy the .turbo-moonbit directory found at a repository URL into the working directory and exit
20 package main21 package main
21 22
@@ -31,7 +32,9 @@ import (
31 32
32 "rickub.com/turbo-editors/turbo-core/app"33 "rickub.com/turbo-editors/turbo-core/app"
33 "rickub.com/turbo-editors/turbo-core/configrepo"34 "rickub.com/turbo-editors/turbo-core/configrepo"
35+ "rickub.com/turbo-editors/turbo-core/fsys"
34 "rickub.com/turbo-editors/turbo-core/profile"36 "rickub.com/turbo-editors/turbo-core/profile"
37+ "rickub.com/turbo-editors/turbo-core/remote"
35 "rickub.com/turbo-editors/turbo-core/settings"38 "rickub.com/turbo-editors/turbo-core/settings"
36 "rickub.com/turbo-editors/turbo-core/theme"39 "rickub.com/turbo-editors/turbo-core/theme"
37 "rickub.com/turbo-editors/turbo-core/version"40 "rickub.com/turbo-editors/turbo-core/version"
@@ -51,26 +54,32 @@ type options struct {
51 theme string54 theme string
52 listThemes bool55 listThemes bool
53 loadConfig string56 loadConfig string
57+ ssh string
54 noLSP bool58 noLSP bool
55 version bool59 version bool
56 files []string60 files []string
57 }61 }
58 62
59 // parseFlags reads the command line.63 // parseFlags reads the command line.
60-func parseFlags() options {64+func parseFlags() options { return parseArguments(flag.CommandLine, os.Args[1:]) }
65+
66+// parseArguments reads arguments with flags. Go's flag package accepts every
67+// flag with one dash or two, which is why -ssh and --ssh are the same flag.
68+func parseArguments(flags *flag.FlagSet, arguments []string) options {
61 var opts options69 var opts options
62 70
63 // The default is empty rather than the theme's name so that "was -theme71 // The default is empty rather than the theme's name so that "was -theme
64 // given?" can still be answered afterwards, which is what lets the project72 // given?" can still be answered afterwards, which is what lets the project
65 // settings fill it in without overriding an explicit choice.73 // settings fill it in without overriding an explicit choice.
66- flag.StringVar(&opts.theme, "theme", "", "colour theme to start with (default from the project, else "+theme.DefaultName+")")74+ flags.StringVar(&opts.theme, "theme", "", "colour theme to start with (default from the project, else "+theme.DefaultName+")")
67- flag.BoolVar(&opts.listThemes, "list-themes", false, "print the available themes and exit")75+ flags.BoolVar(&opts.listThemes, "list-themes", false, "print the available themes and exit")
68- flag.BoolVar(&opts.noLSP, "no-lsp", false, "do not start a language server")76+ flags.BoolVar(&opts.noLSP, "no-lsp", false, "do not start a language server")
69- flag.BoolVar(&opts.version, "version", false, "print the version and exit")77+ flags.BoolVar(&opts.version, "version", false, "print the version and exit")
70- flag.StringVar(&opts.loadConfig, "load-config", "", "copy the .turbo-moonbit directory found at this repository URL into the working directory, then exit")78+ flags.StringVar(&opts.loadConfig, "load-config", "", "copy the .turbo-moonbit directory found at this repository URL into the working directory, then exit")
71- flag.Parse()79+ flags.StringVar(&opts.ssh, "ssh", "", "edit the files of another machine over ssh: user@machine[:directory]")
72-80+ _ = flags.Parse(arguments) // a FlagSet that exits on error never returns one
73- opts.files = flag.Args()81+
82+ opts.files = flags.Args()
74 return opts83 return opts
75 }84 }
76 85
@@ -147,6 +156,15 @@ func describeLoad(result configrepo.Result) string {
147 156
148 // edit opens the terminal and runs the editor until the user leaves.157 // edit opens the terminal and runs the editor until the user leaves.
149 func edit(opts options, p profile.Profile) error {158 func edit(opts options, p profile.Profile) error {
159+ // Before the screen is taken: ssh may have a password to ask for.
160+ session, err := connectRemote(opts.ssh)
161+ if err != nil {
162+ return err
163+ }
164+ if session != nil {
165+ defer session.Close()
166+ }
167+
150 project, projectSettings := loadProjectSettings(p)168 project, projectSettings := loadProjectSettings(p)
151 169
152 screen, err := newScreen()170 screen, err := newScreen()
@@ -161,11 +179,16 @@ func edit(opts options, p profile.Profile) error {
161 if settings.Exists(p, project) {179 if settings.Exists(p, project) {
162 editor.UseSettings(projectSettings, settings.Path(p, project))180 editor.UseSettings(projectSettings, settings.Path(p, project))
163 }181 }
182+ if session != nil {
183+ editor.UseRemote(session)
184+ }
164 openFiles(editor, opts.files)185 openFiles(editor, opts.files)
165 186
166 ctx, cancel := context.WithCancel(context.Background())187 ctx, cancel := context.WithCancel(context.Background())
167 defer cancel()188 defer cancel()
168- if !opts.noLSP {189+ // The language servers this editor finds are on this machine, and the
190+ // files they would be asked about are not: over ssh there is none.
191+ if !opts.noLSP && session == nil {
169 editor.StartLanguageServer(ctx, app.ProjectRoot(p, opts.files))192 editor.StartLanguageServer(ctx, app.ProjectRoot(p, opts.files))
170 }193 }
171 defer editor.Language().Stop(context.Background())194 defer editor.Language().Stop(context.Background())
@@ -173,16 +196,33 @@ func edit(opts options, p profile.Profile) error {
173 return editor.Run()196 return editor.Run()
174 }197 }
175 198
199+// connectRemote logs in to the machine -ssh names and points every file the
200+// editor reads and writes at it, so that the settings, the tree, the Open
201+// dialog and each window see that machine's files. It returns nil, and
202+// changes nothing, when -ssh was not given.
203+func connectRemote(target string) (*remote.Session, error) {
204+ if target == "" {
205+ return nil, nil
206+ }
207+ session, err := remote.Open(context.Background(), target)
208+ if err != nil {
209+ return nil, err
210+ }
211+ fsys.Use(session.FS())
212+ return session, nil
213+}
214+
176 // loadProjectSettings reads .turbo-moonbit/settings.toml from the working215 // loadProjectSettings reads .turbo-moonbit/settings.toml from the working
177 // directory, and returns that directory along with what it found.216 // directory, and returns that directory along with what it found.
178 //217 //
179 // The working directory alone is looked in, with no walk up towards the root:218 // The working directory alone is looked in, with no walk up towards the root:
180-// "the project" is where you started the editor, which is a rule you can hold219+// "the project" is where you started the editor — or the directory given to
181-// in your head. A file that is there but unreadable is reported on standard220+// -ssh — which is a rule you can hold in your head. A file that is there but
182-// error and then ignored — a broken settings file must not stop the editor221+// unreadable is reported on standard error and then ignored — a broken
183-// opening, because the editor is how you would fix it.222+// settings file must not stop the editor opening, because the editor is how
223+// you would fix it.
184 func loadProjectSettings(p profile.Profile) (string, settings.Settings) {224 func loadProjectSettings(p profile.Profile) (string, settings.Settings) {
185- project, err := os.Getwd()225+ project, err := fsys.Getwd()
186 if err != nil {226 if err != nil {
187 project = "."227 project = "."
188 }228 }
modified release.env +1 -1
@@ -6,5 +6,5 @@
66 #
77 # ./02-build-releases.sh v1.0.0 to build the binaries by hand and see what
88 # the CI will publish, without publishing anything
9-TAG="v1.0.4"
9+TAG="v1.0.5"
1010 ABOUT="Turbo MoonBit"
@@ -6,5 +6,5 @@
6 #6 #
7 # ./02-build-releases.sh v1.0.0 to build the binaries by hand and see what7 # ./02-build-releases.sh v1.0.0 to build the binaries by hand and see what
8 # the CI will publish, without publishing anything8 # the CI will publish, without publishing anything
9-TAG="v1.0.4"9+TAG="v1.0.5"
10 ABOUT="Turbo MoonBit"10 ABOUT="Turbo MoonBit"
added ssh_test.go +81 -0
new file mode 100644
@@ -0,0 +1,81 @@
1+package main
2+
3+import (
4+ "errors"
5+ "flag"
6+ "io"
7+ "testing"
8+
9+ "rickub.com/turbo-editors/turbo-core/fsys"
10+ "rickub.com/turbo-editors/turbo-core/fsys/fsystest"
11+ "rickub.com/turbo-editors/turbo-core/remote"
12+ "rickub.com/turbo-editors/turbo-core/settings"
13+
14+ "rickub.com/turbo-editors/turbo-moonbit/internal/moonbitlang"
15+)
16+
17+// parse reads a command line the way main does, into a flag set of its own.
18+func parse(t *testing.T, arguments ...string) options {
19+ t.Helper()
20+ flags := flag.NewFlagSet("turbo-moonbit", flag.ContinueOnError)
21+ flags.SetOutput(io.Discard)
22+ return parseArguments(flags, arguments)
23+}
24+
25+func TestTheSSHFlagIsReadWithOneDashOrTwo(t *testing.T) {
26+ cases := []struct {
27+ arguments []string
28+ want string
29+ }{
30+ {[]string{"-ssh", "bob@machine"}, "bob@machine"},
31+ {[]string{"--ssh", "bob@machine:/srv/app"}, "bob@machine:/srv/app"},
32+ {[]string{"--ssh=demo:workspace"}, "demo:workspace"},
33+ }
34+ for _, c := range cases {
35+ if got := parse(t, c.arguments...).ssh; got != c.want {
36+ t.Errorf("%v: ssh = %q, want %q", c.arguments, got, c.want)
37+ }
38+ }
39+}
40+
41+func TestFilesAfterTheSSHFlagAreStillFiles(t *testing.T) {
42+ opts := parse(t, "-ssh", "bob@machine", "main.x", "other.x")
43+ if len(opts.files) != 2 || opts.files[0] != "main.x" {
44+ t.Errorf("files = %v, want the two named after the flag", opts.files)
45+ }
46+}
47+
48+func TestWithoutSSHTheEditorStaysLocal(t *testing.T) {
49+ session, err := connectRemote("")
50+ if session != nil || err != nil {
51+ t.Errorf("connectRemote(\"\") = %v, %v; want nothing at all", session, err)
52+ }
53+ if !fsys.IsLocal() {
54+ t.Error("the file system is no longer the local disk")
55+ }
56+}
57+
58+func TestABadSSHTargetIsRefusedBeforeAnythingIsStarted(t *testing.T) {
59+ _, err := connectRemote("-oProxyCommand=evil")
60+ if !errors.Is(err, remote.ErrBadTarget) {
61+ t.Errorf("err = %v, want remote.ErrBadTarget", err)
62+ }
63+ if !fsys.IsLocal() {
64+ t.Error("a refused target still switched the file system")
65+ }
66+}
67+
68+// Under -ssh the project is the remote directory: the settings are read
69+// through fsys, from wherever it points.
70+func TestProjectSettingsAreReadFromTheCurrentFileSystem(t *testing.T) {
71+ p := moonbitlang.Profile()
72+ memory := fsystest.New("/home/bob/project")
73+ previous := fsys.Use(memory)
74+ t.Cleanup(func() { fsys.Use(previous) })
75+ memory.AddFile(settings.Path(p, "/home/bob/project"), "[editor]\ntheme = \"turbo-dark\"\n")
76+
77+ project, loaded := loadProjectSettings(p)
78+ if project != "/home/bob/project" || loaded.Theme != "turbo-dark" {
79+ t.Errorf("loadProjectSettings = %q, %+v; want the remote project and its theme", project, loaded)
80+ }
81+}
new file mode 100644
@@ -0,0 +1,81 @@
1+package main
2+
3+import (
4+ "errors"
5+ "flag"
6+ "io"
7+ "testing"
8+
9+ "rickub.com/turbo-editors/turbo-core/fsys"
10+ "rickub.com/turbo-editors/turbo-core/fsys/fsystest"
11+ "rickub.com/turbo-editors/turbo-core/remote"
12+ "rickub.com/turbo-editors/turbo-core/settings"
13+
14+ "rickub.com/turbo-editors/turbo-moonbit/internal/moonbitlang"
15+)
16+
17+// parse reads a command line the way main does, into a flag set of its own.
18+func parse(t *testing.T, arguments ...string) options {
19+ t.Helper()
20+ flags := flag.NewFlagSet("turbo-moonbit", flag.ContinueOnError)
21+ flags.SetOutput(io.Discard)
22+ return parseArguments(flags, arguments)
23+}
24+
25+func TestTheSSHFlagIsReadWithOneDashOrTwo(t *testing.T) {
26+ cases := []struct {
27+ arguments []string
28+ want string
29+ }{
30+ {[]string{"-ssh", "bob@machine"}, "bob@machine"},
31+ {[]string{"--ssh", "bob@machine:/srv/app"}, "bob@machine:/srv/app"},
32+ {[]string{"--ssh=demo:workspace"}, "demo:workspace"},
33+ }
34+ for _, c := range cases {
35+ if got := parse(t, c.arguments...).ssh; got != c.want {
36+ t.Errorf("%v: ssh = %q, want %q", c.arguments, got, c.want)
37+ }
38+ }
39+}
40+
41+func TestFilesAfterTheSSHFlagAreStillFiles(t *testing.T) {
42+ opts := parse(t, "-ssh", "bob@machine", "main.x", "other.x")
43+ if len(opts.files) != 2 || opts.files[0] != "main.x" {
44+ t.Errorf("files = %v, want the two named after the flag", opts.files)
45+ }
46+}
47+
48+func TestWithoutSSHTheEditorStaysLocal(t *testing.T) {
49+ session, err := connectRemote("")
50+ if session != nil || err != nil {
51+ t.Errorf("connectRemote(\"\") = %v, %v; want nothing at all", session, err)
52+ }
53+ if !fsys.IsLocal() {
54+ t.Error("the file system is no longer the local disk")
55+ }
56+}
57+
58+func TestABadSSHTargetIsRefusedBeforeAnythingIsStarted(t *testing.T) {
59+ _, err := connectRemote("-oProxyCommand=evil")
60+ if !errors.Is(err, remote.ErrBadTarget) {
61+ t.Errorf("err = %v, want remote.ErrBadTarget", err)
62+ }
63+ if !fsys.IsLocal() {
64+ t.Error("a refused target still switched the file system")
65+ }
66+}
67+
68+// Under -ssh the project is the remote directory: the settings are read
69+// through fsys, from wherever it points.
70+func TestProjectSettingsAreReadFromTheCurrentFileSystem(t *testing.T) {
71+ p := moonbitlang.Profile()
72+ memory := fsystest.New("/home/bob/project")
73+ previous := fsys.Use(memory)
74+ t.Cleanup(func() { fsys.Use(previous) })
75+ memory.AddFile(settings.Path(p, "/home/bob/project"), "[editor]\ntheme = \"turbo-dark\"\n")
76+
77+ project, loaded := loadProjectSettings(p)
78+ if project != "/home/bob/project" || loaded.Theme != "turbo-dark" {
79+ t.Errorf("loadProjectSettings = %q, %+v; want the remote project and its theme", project, loaded)
80+ }
81+}