turbo-editors/turbo-gopublic Fork
⑂ b8a15b9
Commits
⬇ Clone ▾
git clone https://git.rickub.com/turbo-editors/turbo-go.git
git clone ssh://git@rickub.com/turbo-editors/turbo-go.git

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

Download ZIP

📦 Turbo Go

k33g committed 2026-09-29T18:47:00+02:00 Browse files
b8a15b9 parent: 76f0b70
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-go -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-go -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-go -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-go -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
@@ -536,3 +536,11 @@
536536
537537 - **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.
538538 - **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.
539+
540+## 2026-09-29 — `-ssh` / `--ssh`: edit another machine's files (ticket #33)
541+
542+- **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).
543+- **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.
544+- **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.
545+- **Quality**: gate not run (no `qlty` in the sandbox). `gofmt`, `go vet` clean.
546+- **Not done**: not committed; `go.mod` not re-pinned (no v1.0.5 yet).
@@ -536,3 +536,11 @@
536 536
537 - **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.537 - **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.
538 - **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.538 - **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.
539+
540+## 2026-09-29 — `-ssh` / `--ssh`: edit another machine's files (ticket #33)
541+
542+- **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).
543+- **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.
544+- **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.
545+- **Quality**: gate not run (no `qlty` in the sandbox). `gofmt`, `go vet` clean.
546+- **Not done**: not committed; `go.mod` not re-pinned (no v1.0.5 yet).
modified .memory/summary.md +6 -0
@@ -216,3 +216,9 @@ Reports land in `.quality/`; exit code 0 = pass.
216216
217217 - 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.
218218
219+## State as of 2026-09-29
220+
221+- **`-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`.
222+- **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.
223+- **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 `configrepo` (missing since 2026-09-20), `fsys` and `remote` boxes with `main →` arrows; this editor has no diagram test.
224+- **Quality gate not run** — `qlty` is absent from this sandbox.
@@ -216,3 +216,9 @@ Reports land in `.quality/`; exit code 0 = pass.
216 216
217 - 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.217 - 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.
218 218
219+## State as of 2026-09-29
220+
221+- **`-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`.
222+- **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.
223+- **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 `configrepo` (missing since 2026-09-20), `fsys` and `remote` boxes with `main →` arrows; this editor has no diagram test.
224+- **Quality gate not run** — `qlty` is absent from this sandbox.
modified README.md +2 -0
@@ -55,6 +55,7 @@ The [tutorial](docs/en/tutorials/getting-started.md) walks through a first sessi
5555 - **Completion, hover and go-to-definition** from `gopls`, entirely optional
5656 - **Terminal windows** — `F8` opens a real shell in a window, with its own VT/ANSI emulator, scrollback and job control (Linux, macOS and Windows)
5757 - **Project tree** — `F9` shows the project's files in a window; walk it with the arrows and press Enter to open one
58+- **Editing another machine** — `turbo-go -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 Go menu are that machine's. One login, before the screen is taken; no language server over there yet
5859 - **Snippets** — a `Snippets` menu built from `.turbo-go/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
5960 - **The go toolchain a menu away** — `Alt-G` runs format, vet, build, test and run from `.turbo-go/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
6061 - **Editing** with word movement, block indent, a shared clipboard, and undo that merges a run of typing into one step
@@ -75,6 +76,7 @@ The [tutorial](docs/en/tutorials/getting-started.md) walks through a first sessi
7576 ```bash
7677 turbo-go [-theme name] [-no-lsp] [file...]
7778 turbo-go -list-themes
79+turbo-go -ssh user@machine[:directory] [file...]
7880 ```
7981
8082 ## Documentation
@@ -55,6 +55,7 @@ The [tutorial](docs/en/tutorials/getting-started.md) walks through a first sessi
55 - **Completion, hover and go-to-definition** from `gopls`, entirely optional55 - **Completion, hover and go-to-definition** from `gopls`, entirely optional
56 - **Terminal windows** — `F8` opens a real shell in a window, with its own VT/ANSI emulator, scrollback and job control (Linux, macOS and Windows)56 - **Terminal windows** — `F8` opens a real shell in a window, with its own VT/ANSI emulator, scrollback and job control (Linux, macOS and Windows)
57 - **Project tree** — `F9` shows the project's files in a window; walk it with the arrows and press Enter to open one57 - **Project tree** — `F9` shows the project's files in a window; walk it with the arrows and press Enter to open one
58+- **Editing another machine** — `turbo-go -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 Go menu are that machine's. One login, before the screen is taken; no language server over there yet
58 - **Snippets** — a `Snippets` menu built from `.turbo-go/snippets.toml`, grouped into submenus and filtered by the file you are in; the chosen text is inserted at the cursor, re-indented to match59 - **Snippets** — a `Snippets` menu built from `.turbo-go/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
59 - **The go toolchain a menu away** — `Alt-G` runs format, vet, build, test and run from `.turbo-go/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 bar60 - **The go toolchain a menu away** — `Alt-G` runs format, vet, build, test and run from `.turbo-go/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
60 - **Editing** with word movement, block indent, a shared clipboard, and undo that merges a run of typing into one step61 - **Editing** with word movement, block indent, a shared clipboard, and undo that merges a run of typing into one step
@@ -75,6 +76,7 @@ The [tutorial](docs/en/tutorials/getting-started.md) walks through a first sessi
75 ```bash76 ```bash
76 turbo-go [-theme name] [-no-lsp] [file...]77 turbo-go [-theme name] [-no-lsp] [file...]
77 turbo-go -list-themes78 turbo-go -list-themes
79+turbo-go -ssh user@machine[:directory] [file...]
78 ```80 ```
79 81
80 ## Documentation82 ## Documentation
modified docs/diagrams/packages.drawio +6 -0
@@ -22,6 +22,12 @@
2222 <mxCell id="e17" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="2" target="6"><mxGeometry relative="1" as="geometry"/></mxCell>
2323 <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>
2424 <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="ssh-configrepo" value="&lt;b&gt;configrepo&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="117" y="390" width="190" height="54" as="geometry"/></mxCell>
26+ <mxCell id="e-main-configrepo" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="2" target="ssh-configrepo"><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>
2531 </root>
2632 </mxGraphModel>
2733 </diagram>
@@ -22,6 +22,12 @@
22 <mxCell id="e17" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="2" target="6"><mxGeometry relative="1" as="geometry"/></mxCell>22 <mxCell id="e17" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="2" target="6"><mxGeometry relative="1" as="geometry"/></mxCell>
23 <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>23 <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="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>24 <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="ssh-configrepo" value="&lt;b&gt;configrepo&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="117" y="390" width="190" height="54" as="geometry"/></mxCell>
26+ <mxCell id="e-main-configrepo" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="2" target="ssh-configrepo"><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>
25 </root>31 </root>
26 </mxGraphModel>32 </mxGraphModel>
27 </diagram>33 </diagram>
modified docs/en/README.md +1 -0
@@ -26,6 +26,7 @@ This documentation follows the [Diátaxis](https://diataxis.fr) method. Four kin
2626 - [How to run shell commands without leaving the editor](how-to/use-a-terminal.md)
2727 - [How to give a project its own settings](how-to/configure-a-project.md)
2828 - [How to browse a project and open files from a tree](how-to/browse-a-project.md)
29+- [How to edit the files of a remote machine over ssh](how-to/edit-a-remote-machine.md)
2930 - [How to insert snippets from a menu](how-to/use-snippets.md)
3031 - [How to run go commands from the editor](how-to/run-go-commands.md)
3132 - [How to talk to a coding agent from the editor](how-to/talk-to-an-agent.md)
@@ -26,6 +26,7 @@ This documentation follows the [Diátaxis](https://diataxis.fr) method. Four kin
26 - [How to run shell commands without leaving the editor](how-to/use-a-terminal.md)26 - [How to run shell commands without leaving the editor](how-to/use-a-terminal.md)
27 - [How to give a project its own settings](how-to/configure-a-project.md)27 - [How to give a project its own settings](how-to/configure-a-project.md)
28 - [How to browse a project and open files from a tree](how-to/browse-a-project.md)28 - [How to browse a project and open files from a tree](how-to/browse-a-project.md)
29+- [How to edit the files of a remote machine over ssh](how-to/edit-a-remote-machine.md)
29 - [How to insert snippets from a menu](how-to/use-snippets.md)30 - [How to insert snippets from a menu](how-to/use-snippets.md)
30 - [How to run go commands from the editor](how-to/run-go-commands.md)31 - [How to run go commands from the editor](how-to/run-go-commands.md)
31 - [How to talk to a coding agent from the editor](how-to/talk-to-an-agent.md)32 - [How to talk to a coding agent from the editor](how-to/talk-to-an-agent.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 Go, so that its files, its project tree, its terminal windows and its Go 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-go -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 Go menu runs `.turbo-go/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-go -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-go -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 — `gopls` 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 Go, so that its files, its project tree, its terminal windows and its Go 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-go -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 Go menu runs `.turbo-go/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-go -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-go -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 — `gopls` 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 Go <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-go` 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-go` 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-go/` 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_GO_THEME_DIR/*.toml` | User themes, when the variable is set. |
3738 | `./.turbo-go/settings.toml` | This project's settings, read once at start-up. See [project settings](project-settings.md). |
39+| `<directory>/.turbo-go/*.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-go/` (written) | Created by `-load-config` from a repository's copy, when the project has none yet. |
3942 | `~/.config/turbo-go/themes/*.toml` | User themes on Linux (`os.UserConfigDir`). |
4043 | `~/Library/Application Support/turbo-go/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-go -theme turbo-dark main.go # a different theme
7578 turbo-go -no-lsp main.go # no language server
7679 turbo-go -list-themes # what themes exist
7780 turbo-go -load-config https://rickub.com/turbo-editors/configs/tree/main/golang-init # start from a shared configuration
81+turbo-go -ssh bob@build-box:/srv/app # the files of another machine, over ssh
82+turbo-go --ssh demo main.py # a Host alias from ~/.ssh/config, opening main.py there
7883 ```
7984
8085 ## Installer
@@ -105,3 +110,10 @@ turbo-go -load-config https://rickub.com/turbo-editors/configs/tree/main/golang-
105110 | `turbo-go: configrepo: git is not installed` | `-load-config` needs the `git` command on `PATH`. |
106111 | `turbo-go: configrepo: no configuration directory at that path: …` | The repository was found but holds no `.turbo-go` where the URL points. |
107112 | `turbo-go: configrepo: no repository answered: …` | No clone address for that URL answered; the URL, the network, or a private repository. |
113+| `turbo-go: "<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-go: no ssh command found on PATH …` | `-ssh` needs the `ssh` command. |
115+| `turbo-go: editing over ssh needs OpenSSH's ControlMaster, …` | `-ssh` on Windows. |
116+| `turbo-go: ssh <destination>: exit status 255` | The login failed — wrong password, unknown host, refused key. ssh has printed why just above. |
117+| `turbo-go: finding <dir> on the remote machine: …` | The directory after the colon does not exist there. |
118+| `turbo-go: <dir> on the remote machine is not a directory: …` | The path after the colon names a file. |
119+| `turbo-go: 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 Go <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 Go <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-go` 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-go` already exists. The editor is not started. |21 | `-load-config <url>` | string | none | Copy the `.turbo-go` 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-go` 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-go/` 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_GO_THEME_DIR/*.toml` | User themes, when the variable is set. |37 | `$TURBO_GO_THEME_DIR/*.toml` | User themes, when the variable is set. |
37 | `./.turbo-go/settings.toml` | This project's settings, read once at start-up. See [project settings](project-settings.md). |38 | `./.turbo-go/settings.toml` | This project's settings, read once at start-up. See [project settings](project-settings.md). |
39+| `<directory>/.turbo-go/*.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-go/` (written) | Created by `-load-config` from a repository's copy, when the project has none yet. |41 | `./.turbo-go/` (written) | Created by `-load-config` from a repository's copy, when the project has none yet. |
39 | `~/.config/turbo-go/themes/*.toml` | User themes on Linux (`os.UserConfigDir`). |42 | `~/.config/turbo-go/themes/*.toml` | User themes on Linux (`os.UserConfigDir`). |
40 | `~/Library/Application Support/turbo-go/themes/*.toml` | User themes on macOS. |43 | `~/Library/Application Support/turbo-go/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-go -theme turbo-dark main.go # a different theme
75 turbo-go -no-lsp main.go # no language server78 turbo-go -no-lsp main.go # no language server
76 turbo-go -list-themes # what themes exist79 turbo-go -list-themes # what themes exist
77 turbo-go -load-config https://rickub.com/turbo-editors/configs/tree/main/golang-init # start from a shared configuration80 turbo-go -load-config https://rickub.com/turbo-editors/configs/tree/main/golang-init # start from a shared configuration
81+turbo-go -ssh bob@build-box:/srv/app # the files of another machine, over ssh
82+turbo-go --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-go -load-config https://rickub.com/turbo-editors/configs/tree/main/golang-
105 | `turbo-go: configrepo: git is not installed` | `-load-config` needs the `git` command on `PATH`. |110 | `turbo-go: configrepo: git is not installed` | `-load-config` needs the `git` command on `PATH`. |
106 | `turbo-go: configrepo: no configuration directory at that path: …` | The repository was found but holds no `.turbo-go` where the URL points. |111 | `turbo-go: configrepo: no configuration directory at that path: …` | The repository was found but holds no `.turbo-go` where the URL points. |
107 | `turbo-go: configrepo: no repository answered: …` | No clone address for that URL answered; the URL, the network, or a private repository. |112 | `turbo-go: configrepo: no repository answered: …` | No clone address for that URL answered; the URL, the network, or a private repository. |
113+| `turbo-go: "<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-go: no ssh command found on PATH …` | `-ssh` needs the `ssh` command. |
115+| `turbo-go: editing over ssh needs OpenSSH's ControlMaster, …` | `-ssh` on Windows. |
116+| `turbo-go: ssh <destination>: exit status 255` | The login failed — wrong password, unknown host, refused key. ssh has printed why just above. |
117+| `turbo-go: finding <dir> on the remote machine: …` | The directory after the colon does not exist there. |
118+| `turbo-go: <dir> on the remote machine is not a directory: …` | The path after the colon names a file. |
119+| `turbo-go: opening the file channel to <destination>: …` | The server does not offer the `sftp` subsystem. |
modified docs/fr/README.md +1 -0
@@ -26,6 +26,7 @@ Cette documentation suit la méthode [Diátaxis](https://diataxis.fr). Quatre ty
2626 - [Lancer des commandes shell sans quitter l'éditeur](how-to/use-a-terminal.md)
2727 - [Donner ses propres réglages à un projet](how-to/configure-a-project.md)
2828 - [Parcourir un projet et ouvrir des fichiers depuis un arbre](how-to/browse-a-project.md)
29+- [Éditer les fichiers d'une machine distante via ssh](how-to/edit-a-remote-machine.md)
2930 - [Insérer des snippets depuis un menu](how-to/use-snippets.md)
3031 - [Lancer les commandes go depuis l'éditeur](how-to/run-go-commands.md)
3132 - [Dialoguer avec un agent de code depuis l'éditeur](how-to/talk-to-an-agent.md)
@@ -26,6 +26,7 @@ Cette documentation suit la méthode [Diátaxis](https://diataxis.fr). Quatre ty
26 - [Lancer des commandes shell sans quitter l'éditeur](how-to/use-a-terminal.md)26 - [Lancer des commandes shell sans quitter l'éditeur](how-to/use-a-terminal.md)
27 - [Donner ses propres réglages à un projet](how-to/configure-a-project.md)27 - [Donner ses propres réglages à un projet](how-to/configure-a-project.md)
28 - [Parcourir un projet et ouvrir des fichiers depuis un arbre](how-to/browse-a-project.md)28 - [Parcourir un projet et ouvrir des fichiers depuis un arbre](how-to/browse-a-project.md)
29+- [Éditer les fichiers d'une machine distante via ssh](how-to/edit-a-remote-machine.md)
29 - [Insérer des snippets depuis un menu](how-to/use-snippets.md)30 - [Insérer des snippets depuis un menu](how-to/use-snippets.md)
30 - [Lancer les commandes go depuis l'éditeur](how-to/run-go-commands.md)31 - [Lancer les commandes go depuis l'éditeur](how-to/run-go-commands.md)
31 - [Dialoguer avec un agent de code depuis l'éditeur](how-to/talk-to-an-agent.md)32 - [Dialoguer avec un agent de code depuis l'éditeur](how-to/talk-to-an-agent.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 Go 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 Go 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-go -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 Go y lance `.turbo-go/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-go -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-go -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é — `gopls` 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 Go 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 Go 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-go -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 Go y lance `.turbo-go/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-go -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-go -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é — `gopls` 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 Go <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-go` 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-go` 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-go/`, 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_GO_THEME_DIR/*.toml` | Thèmes utilisateur, quand la variable est définie. |
3738 | `./.turbo-go/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-go/*.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-go/` (é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-go/themes/*.toml` | Thèmes utilisateur sous Linux (`os.UserConfigDir`). |
4043 | `~/Library/Application Support/turbo-go/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-go -theme turbo-dark main.go # un autre thème
7578 turbo-go -no-lsp main.go # sans serveur de langage
7679 turbo-go -list-themes # quels thèmes existent
7780 turbo-go -load-config https://rickub.com/turbo-editors/configs/tree/main/golang-init # partir d'une configuration partagée
81+turbo-go -ssh bob@build-box:/srv/app # les fichiers d'une autre machine, via ssh
82+turbo-go --ssh demo main.py # un alias Host de ~/.ssh/config, en y ouvrant main.py
7883 ```
7984
8085 ## Installateur
@@ -105,3 +110,10 @@ turbo-go -load-config https://rickub.com/turbo-editors/configs/tree/main/golang-
105110 | `turbo-go: configrepo: git is not installed` | `-load-config` a besoin de la commande `git` dans le `PATH`. |
106111 | `turbo-go: configrepo: no configuration directory at that path: …` | Le dépôt existe mais ne contient pas de `.turbo-go` là où l'URL pointe. |
107112 | `turbo-go: 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-go: "<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-go: no ssh command found on PATH …` | `-ssh` a besoin de la commande `ssh`. |
115+| `turbo-go: editing over ssh needs OpenSSH's ControlMaster, …` | `-ssh` sous Windows. |
116+| `turbo-go: 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-go: finding <dir> on the remote machine: …` | Le répertoire après les deux-points n'existe pas là-bas. |
118+| `turbo-go: <dir> on the remote machine is not a directory: …` | Le chemin après les deux-points désigne un fichier. |
119+| `turbo-go: 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 Go <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 Go <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-go` 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-go` existe déjà. L'éditeur n'est pas lancé. |21 | `-load-config <url>` | chaîne | aucune | Copie le répertoire `.turbo-go` 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-go` 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-go/`, 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_GO_THEME_DIR/*.toml` | Thèmes utilisateur, quand la variable est définie. |37 | `$TURBO_GO_THEME_DIR/*.toml` | Thèmes utilisateur, quand la variable est définie. |
37 | `./.turbo-go/settings.toml` | Les réglages de ce projet, lus une fois au démarrage. Voir [réglages de projet](project-settings.md). |38 | `./.turbo-go/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-go/*.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-go/` (écrit) | Créé par `-load-config` à partir de la copie d'un dépôt, quand le projet n'en a pas encore. |41 | `./.turbo-go/` (é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-go/themes/*.toml` | Thèmes utilisateur sous Linux (`os.UserConfigDir`). |42 | `~/.config/turbo-go/themes/*.toml` | Thèmes utilisateur sous Linux (`os.UserConfigDir`). |
40 | `~/Library/Application Support/turbo-go/themes/*.toml` | Thèmes utilisateur sous macOS. |43 | `~/Library/Application Support/turbo-go/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-go -theme turbo-dark main.go # un autre thème
75 turbo-go -no-lsp main.go # sans serveur de langage78 turbo-go -no-lsp main.go # sans serveur de langage
76 turbo-go -list-themes # quels thèmes existent79 turbo-go -list-themes # quels thèmes existent
77 turbo-go -load-config https://rickub.com/turbo-editors/configs/tree/main/golang-init # partir d'une configuration partagée80 turbo-go -load-config https://rickub.com/turbo-editors/configs/tree/main/golang-init # partir d'une configuration partagée
81+turbo-go -ssh bob@build-box:/srv/app # les fichiers d'une autre machine, via ssh
82+turbo-go --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-go -load-config https://rickub.com/turbo-editors/configs/tree/main/golang-
105 | `turbo-go: configrepo: git is not installed` | `-load-config` a besoin de la commande `git` dans le `PATH`. |110 | `turbo-go: configrepo: git is not installed` | `-load-config` a besoin de la commande `git` dans le `PATH`. |
106 | `turbo-go: configrepo: no configuration directory at that path: …` | Le dépôt existe mais ne contient pas de `.turbo-go` là où l'URL pointe. |111 | `turbo-go: configrepo: no configuration directory at that path: …` | Le dépôt existe mais ne contient pas de `.turbo-go` là où l'URL pointe. |
107 | `turbo-go: 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-go: 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-go: "<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-go: no ssh command found on PATH …` | `-ssh` a besoin de la commande `ssh`. |
115+| `turbo-go: editing over ssh needs OpenSSH's ControlMaster, …` | `-ssh` sous Windows. |
116+| `turbo-go: 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-go: finding <dir> on the remote machine: …` | Le répertoire après les deux-points n'existe pas là-bas. |
118+| `turbo-go: <dir> on the remote machine is not a directory: …` | Le chemin après les deux-points désigne un fichier. |
119+| `turbo-go: opening the file channel to <destination>: …` | Le serveur ne propose pas le sous-système `sftp`. |
modified go.mod +4 -1
@@ -4,14 +4,17 @@ 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.4.1 // indirect
15+ github.com/pkg/sftp v1.13.11 // indirect
1416 github.com/rivo/uniseg v0.4.7 // indirect
17+ golang.org/x/crypto v0.54.0 // indirect
1518 golang.org/x/sys v0.48.0 // indirect
1619 golang.org/x/term v0.46.0 // indirect
1720 golang.org/x/text v0.42.0 // indirect
@@ -4,14 +4,17 @@ 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.4.1 // indirect14 github.com/lucasb-eyer/go-colorful v1.4.1 // 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
17+ golang.org/x/crypto v0.54.0 // indirect
15 golang.org/x/sys v0.48.0 // indirect18 golang.org/x/sys v0.48.0 // indirect
16 golang.org/x/term v0.46.0 // indirect19 golang.org/x/term v0.46.0 // indirect
17 golang.org/x/text v0.42.0 // indirect20 golang.org/x/text v0.42.0 // indirect
modified go.sum +16 -2
@@ -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.4.1 h1:1EO+WB73+EH8EVbzlrG3KLAfEypQWVHIBqlTf+2hNss=
812 github.com/lucasb-eyer/go-colorful v1.4.1/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=
@@ -45,5 +57,7 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
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.4.1 h1:1EO+WB73+EH8EVbzlrG3KLAfEypQWVHIBqlTf+2hNss=11 github.com/lucasb-eyer/go-colorful v1.4.1 h1:1EO+WB73+EH8EVbzlrG3KLAfEypQWVHIBqlTf+2hNss=
8 github.com/lucasb-eyer/go-colorful v1.4.1/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=12 github.com/lucasb-eyer/go-colorful v1.4.1/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=
@@ -45,5 +57,7 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
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
@@ -15,6 +15,7 @@
1515 // -list-themes print the available themes and exit
1616 // -no-lsp do not start a language server
1717 // -version print the version and exit
18+// -ssh user@machine[:dir] edit the files of another machine, over ssh (--ssh works too)
1819 // -load-config url copy the .turbo-go directory found at a repository URL into the working directory and exit
1920 package main
2021
@@ -30,7 +31,9 @@ import (
3031
3132 "rickub.com/turbo-editors/turbo-core/app"
3233 "rickub.com/turbo-editors/turbo-core/configrepo"
34+ "rickub.com/turbo-editors/turbo-core/fsys"
3335 "rickub.com/turbo-editors/turbo-core/profile"
36+ "rickub.com/turbo-editors/turbo-core/remote"
3437 "rickub.com/turbo-editors/turbo-core/settings"
3538 "rickub.com/turbo-editors/turbo-core/theme"
3639 "rickub.com/turbo-editors/turbo-core/version"
@@ -50,26 +53,32 @@ type options struct {
5053 theme string
5154 listThemes bool
5255 loadConfig string
56+ ssh string
5357 noLSP bool
5458 version bool
5559 files []string
5660 }
5761
5862 // parseFlags reads the command line.
59-func parseFlags() options {
63+func parseFlags() options { return parseArguments(flag.CommandLine, os.Args[1:]) }
64+
65+// parseArguments reads arguments with flags. Go's flag package accepts every
66+// flag with one dash or two, which is why -ssh and --ssh are the same flag.
67+func parseArguments(flags *flag.FlagSet, arguments []string) options {
6068 var opts options
6169
6270 // The default is empty rather than the theme's name so that "was -theme
6371 // given?" can still be answered afterwards, which is what lets the project
6472 // settings fill it in without overriding an explicit choice.
65- flag.StringVar(&opts.theme, "theme", "", "colour theme to start with (default from the project, else "+theme.DefaultName+")")
66- flag.BoolVar(&opts.listThemes, "list-themes", false, "print the available themes and exit")
67- flag.BoolVar(&opts.noLSP, "no-lsp", false, "do not start a language server")
68- flag.BoolVar(&opts.version, "version", false, "print the version and exit")
69- flag.StringVar(&opts.loadConfig, "load-config", "", "copy the .turbo-go directory found at this repository URL into the working directory, then exit")
70- flag.Parse()
71-
72- opts.files = flag.Args()
73+ flags.StringVar(&opts.theme, "theme", "", "colour theme to start with (default from the project, else "+theme.DefaultName+")")
74+ flags.BoolVar(&opts.listThemes, "list-themes", false, "print the available themes and exit")
75+ flags.BoolVar(&opts.noLSP, "no-lsp", false, "do not start a language server")
76+ flags.BoolVar(&opts.version, "version", false, "print the version and exit")
77+ flags.StringVar(&opts.loadConfig, "load-config", "", "copy the .turbo-go directory found at this repository URL into the working directory, then exit")
78+ flags.StringVar(&opts.ssh, "ssh", "", "edit the files of another machine over ssh: user@machine[:directory]")
79+ _ = flags.Parse(arguments) // a FlagSet that exits on error never returns one
80+
81+ opts.files = flags.Args()
7382 return opts
7483 }
7584
@@ -146,6 +155,15 @@ func describeLoad(result configrepo.Result) string {
146155
147156 // edit opens the terminal and runs the editor until the user leaves.
148157 func edit(opts options, p profile.Profile) error {
158+ // Before the screen is taken: ssh may have a password to ask for.
159+ session, err := connectRemote(opts.ssh)
160+ if err != nil {
161+ return err
162+ }
163+ if session != nil {
164+ defer session.Close()
165+ }
166+
149167 project, projectSettings := loadProjectSettings(p)
150168
151169 screen, err := newScreen()
@@ -160,11 +178,16 @@ func edit(opts options, p profile.Profile) error {
160178 if settings.Exists(p, project) {
161179 editor.UseSettings(projectSettings, settings.Path(p, project))
162180 }
181+ if session != nil {
182+ editor.UseRemote(session)
183+ }
163184 openFiles(editor, opts.files)
164185
165186 ctx, cancel := context.WithCancel(context.Background())
166187 defer cancel()
167- if !opts.noLSP {
188+ // The language servers this editor finds are on this machine, and the
189+ // files they would be asked about are not: over ssh there is none.
190+ if !opts.noLSP && session == nil {
168191 editor.StartLanguageServer(ctx, app.ProjectRoot(p, opts.files))
169192 }
170193 defer editor.Language().Stop(context.Background())
@@ -172,16 +195,33 @@ func edit(opts options, p profile.Profile) error {
172195 return editor.Run()
173196 }
174197
198+// connectRemote logs in to the machine -ssh names and points every file the
199+// editor reads and writes at it, so that the settings, the tree, the Open
200+// dialog and each window see that machine's files. It returns nil, and
201+// changes nothing, when -ssh was not given.
202+func connectRemote(target string) (*remote.Session, error) {
203+ if target == "" {
204+ return nil, nil
205+ }
206+ session, err := remote.Open(context.Background(), target)
207+ if err != nil {
208+ return nil, err
209+ }
210+ fsys.Use(session.FS())
211+ return session, nil
212+}
213+
175214 // loadProjectSettings reads .turbo-go/settings.toml from the working
176215 // directory, and returns that directory along with what it found.
177216 //
178217 // The working directory alone is looked in, with no walk up towards the root:
179-// "the project" is where you started the editor, which is a rule you can hold
180-// in your head. A file that is there but unreadable is reported on standard
181-// error and then ignored — a broken settings file must not stop the editor
182-// opening, because the editor is how you would fix it.
218+// "the project" is where you started the editor — or the directory given to
219+// -ssh — which is a rule you can hold in your head. A file that is there but
220+// unreadable is reported on standard error and then ignored — a broken
221+// settings file must not stop the editor opening, because the editor is how
222+// you would fix it.
183223 func loadProjectSettings(p profile.Profile) (string, settings.Settings) {
184- project, err := os.Getwd()
224+ project, err := fsys.Getwd()
185225 if err != nil {
186226 project = "."
187227 }
@@ -15,6 +15,7 @@
15 // -list-themes print the available themes and exit15 // -list-themes print the available themes and exit
16 // -no-lsp do not start a language server16 // -no-lsp do not start a language server
17 // -version print the version and exit17 // -version print the version and exit
18+// -ssh user@machine[:dir] edit the files of another machine, over ssh (--ssh works too)
18 // -load-config url copy the .turbo-go directory found at a repository URL into the working directory and exit19 // -load-config url copy the .turbo-go directory found at a repository URL into the working directory and exit
19 package main20 package main
20 21
@@ -30,7 +31,9 @@ import (
30 31
31 "rickub.com/turbo-editors/turbo-core/app"32 "rickub.com/turbo-editors/turbo-core/app"
32 "rickub.com/turbo-editors/turbo-core/configrepo"33 "rickub.com/turbo-editors/turbo-core/configrepo"
34+ "rickub.com/turbo-editors/turbo-core/fsys"
33 "rickub.com/turbo-editors/turbo-core/profile"35 "rickub.com/turbo-editors/turbo-core/profile"
36+ "rickub.com/turbo-editors/turbo-core/remote"
34 "rickub.com/turbo-editors/turbo-core/settings"37 "rickub.com/turbo-editors/turbo-core/settings"
35 "rickub.com/turbo-editors/turbo-core/theme"38 "rickub.com/turbo-editors/turbo-core/theme"
36 "rickub.com/turbo-editors/turbo-core/version"39 "rickub.com/turbo-editors/turbo-core/version"
@@ -50,26 +53,32 @@ type options struct {
50 theme string53 theme string
51 listThemes bool54 listThemes bool
52 loadConfig string55 loadConfig string
56+ ssh string
53 noLSP bool57 noLSP bool
54 version bool58 version bool
55 files []string59 files []string
56 }60 }
57 61
58 // parseFlags reads the command line.62 // parseFlags reads the command line.
59-func parseFlags() options {63+func parseFlags() options { return parseArguments(flag.CommandLine, os.Args[1:]) }
64+
65+// parseArguments reads arguments with flags. Go's flag package accepts every
66+// flag with one dash or two, which is why -ssh and --ssh are the same flag.
67+func parseArguments(flags *flag.FlagSet, arguments []string) options {
60 var opts options68 var opts options
61 69
62 // The default is empty rather than the theme's name so that "was -theme70 // The default is empty rather than the theme's name so that "was -theme
63 // given?" can still be answered afterwards, which is what lets the project71 // given?" can still be answered afterwards, which is what lets the project
64 // settings fill it in without overriding an explicit choice.72 // settings fill it in without overriding an explicit choice.
65- flag.StringVar(&opts.theme, "theme", "", "colour theme to start with (default from the project, else "+theme.DefaultName+")")73+ flags.StringVar(&opts.theme, "theme", "", "colour theme to start with (default from the project, else "+theme.DefaultName+")")
66- flag.BoolVar(&opts.listThemes, "list-themes", false, "print the available themes and exit")74+ flags.BoolVar(&opts.listThemes, "list-themes", false, "print the available themes and exit")
67- flag.BoolVar(&opts.noLSP, "no-lsp", false, "do not start a language server")75+ flags.BoolVar(&opts.noLSP, "no-lsp", false, "do not start a language server")
68- flag.BoolVar(&opts.version, "version", false, "print the version and exit")76+ flags.BoolVar(&opts.version, "version", false, "print the version and exit")
69- flag.StringVar(&opts.loadConfig, "load-config", "", "copy the .turbo-go directory found at this repository URL into the working directory, then exit")77+ flags.StringVar(&opts.loadConfig, "load-config", "", "copy the .turbo-go directory found at this repository URL into the working directory, then exit")
70- flag.Parse()78+ flags.StringVar(&opts.ssh, "ssh", "", "edit the files of another machine over ssh: user@machine[:directory]")
71-79+ _ = flags.Parse(arguments) // a FlagSet that exits on error never returns one
72- opts.files = flag.Args()80+
81+ opts.files = flags.Args()
73 return opts82 return opts
74 }83 }
75 84
@@ -146,6 +155,15 @@ func describeLoad(result configrepo.Result) string {
146 155
147 // edit opens the terminal and runs the editor until the user leaves.156 // edit opens the terminal and runs the editor until the user leaves.
148 func edit(opts options, p profile.Profile) error {157 func edit(opts options, p profile.Profile) error {
158+ // Before the screen is taken: ssh may have a password to ask for.
159+ session, err := connectRemote(opts.ssh)
160+ if err != nil {
161+ return err
162+ }
163+ if session != nil {
164+ defer session.Close()
165+ }
166+
149 project, projectSettings := loadProjectSettings(p)167 project, projectSettings := loadProjectSettings(p)
150 168
151 screen, err := newScreen()169 screen, err := newScreen()
@@ -160,11 +178,16 @@ func edit(opts options, p profile.Profile) error {
160 if settings.Exists(p, project) {178 if settings.Exists(p, project) {
161 editor.UseSettings(projectSettings, settings.Path(p, project))179 editor.UseSettings(projectSettings, settings.Path(p, project))
162 }180 }
181+ if session != nil {
182+ editor.UseRemote(session)
183+ }
163 openFiles(editor, opts.files)184 openFiles(editor, opts.files)
164 185
165 ctx, cancel := context.WithCancel(context.Background())186 ctx, cancel := context.WithCancel(context.Background())
166 defer cancel()187 defer cancel()
167- if !opts.noLSP {188+ // The language servers this editor finds are on this machine, and the
189+ // files they would be asked about are not: over ssh there is none.
190+ if !opts.noLSP && session == nil {
168 editor.StartLanguageServer(ctx, app.ProjectRoot(p, opts.files))191 editor.StartLanguageServer(ctx, app.ProjectRoot(p, opts.files))
169 }192 }
170 defer editor.Language().Stop(context.Background())193 defer editor.Language().Stop(context.Background())
@@ -172,16 +195,33 @@ func edit(opts options, p profile.Profile) error {
172 return editor.Run()195 return editor.Run()
173 }196 }
174 197
198+// connectRemote logs in to the machine -ssh names and points every file the
199+// editor reads and writes at it, so that the settings, the tree, the Open
200+// dialog and each window see that machine's files. It returns nil, and
201+// changes nothing, when -ssh was not given.
202+func connectRemote(target string) (*remote.Session, error) {
203+ if target == "" {
204+ return nil, nil
205+ }
206+ session, err := remote.Open(context.Background(), target)
207+ if err != nil {
208+ return nil, err
209+ }
210+ fsys.Use(session.FS())
211+ return session, nil
212+}
213+
175 // loadProjectSettings reads .turbo-go/settings.toml from the working214 // loadProjectSettings reads .turbo-go/settings.toml from the working
176 // directory, and returns that directory along with what it found.215 // directory, and returns that directory along with what it found.
177 //216 //
178 // The working directory alone is looked in, with no walk up towards the root:217 // The working directory alone is looked in, with no walk up towards the root:
179-// "the project" is where you started the editor, which is a rule you can hold218+// "the project" is where you started the editor — or the directory given to
180-// in your head. A file that is there but unreadable is reported on standard219+// -ssh — which is a rule you can hold in your head. A file that is there but
181-// error and then ignored — a broken settings file must not stop the editor220+// unreadable is reported on standard error and then ignored — a broken
182-// opening, because the editor is how you would fix it.221+// settings file must not stop the editor opening, because the editor is how
222+// you would fix it.
183 func loadProjectSettings(p profile.Profile) (string, settings.Settings) {223 func loadProjectSettings(p profile.Profile) (string, settings.Settings) {
184- project, err := os.Getwd()224+ project, err := fsys.Getwd()
185 if err != nil {225 if err != nil {
186 project = "."226 project = "."
187 }227 }
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 Go"
@@ -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 Go"10 ABOUT="Turbo Go"
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-go/internal/golang"
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-go", 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 := golang.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-go/internal/golang"
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-go", 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 := golang.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+}