Skip to content

Commit 87eadc2

Browse files
committed
new command: edit
1 parent aeab833 commit 87eadc2

5 files changed

Lines changed: 95 additions & 4 deletions

File tree

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,17 @@ $ mpytool run script.py # run script (fire-and-forget)
222222
$ mpytool run script.py -- monitor # run script and capture output
223223
```
224224

225+
### Edit file on device
226+
```
227+
$ mpytool edit :boot.py # edit file (uses $VISUAL or $EDITOR)
228+
$ mpytool edit :/lib/config.py # absolute path
229+
$ mpytool edit --editor vim :main.py # explicit editor
230+
$ mpytool edit :newfile.py # create new file if doesn't exist
231+
```
232+
233+
Downloads file to a temp file, opens in editor, uploads back if changed.
234+
Editor priority: `--editor` > `$VISUAL` > `$EDITOR` > error.
235+
225236
### Mount local directory on device
226237
```
227238
$ mpytool mount ./src # mount ./src as /remote, auto-start REPL

README_mpremote.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ Detailed comparison between [mpytool](https://github.com/pavelrevak/mpytool) and
3737
| Package install | 🔴 | 🟢 `mip install pkg` |
3838
| RTC control | 🔴 | 🟢 `rtc`, `rtc --set` |
3939
| ROMFS manage | 🔴 | 🟢 `romfs` |
40-
| Edit remote file | 🔴 | 🟢 `edit :file` |
40+
| Edit remote file | 🟢 `edit :file` | 🟢 `edit :file` |
4141
| Flash read/write | 🟢 `flash r/w/erase` | 🔴 |
4242
| OTA update | 🟢 `ota firmware.app-bin` | 🔴 |
4343
| Print CWD | 🟢 `pwd` | 🔴 use `exec` |

completions/_mpytool

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,16 @@ _mpytool() {
268268
fi
269269
[[ $nargs -ge 1 ]] && compadd -- '--'
270270
;;
271+
edit)
272+
# --editor CMD + 1 remote file, -- after file
273+
_mpytool_options edit
274+
if [[ "$words[CURRENT]" == :* ]]; then
275+
_mpytool_complete_remote
276+
else
277+
compadd -S '' ':'
278+
fi
279+
[[ $nargs -ge 1 ]] && compadd -- '--'
280+
;;
271281
pwd)
272282
# No arguments, -- immediately
273283
compadd -- '--'

completions/mpytool.bash

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,16 @@ _mpytool() {
279279
fi
280280
[[ $nargs -ge 1 && ( -z "$cur" || "--" == "$cur"* ) ]] && COMPREPLY+=("--")
281281
;;
282+
edit)
283+
# --editor CMD + 1 remote file, -- after file
284+
COMPREPLY=($(compgen -W "$(_mpytool_get_options edit)" -- "$cur"))
285+
if [[ "$cur" == :* ]]; then
286+
_mpytool_complete_remote "$cur"
287+
else
288+
COMPREPLY+=($(compgen -W ":" -- "$cur"))
289+
fi
290+
[[ $nargs -ge 1 && ( -z "$cur" || "--" == "$cur"* ) ]] && COMPREPLY+=("--")
291+
;;
282292
pwd)
283293
# No arguments, -- immediately
284294
[[ -z "$cur" || "--" == "$cur"* ]] && COMPREPLY+=("--")

mpytool/mpytool.py

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
import fnmatch as _fnmatch
55
import importlib.metadata as _metadata
66
import os as _os
7+
import shlex as _shlex
8+
import subprocess as _subprocess
79
import sys as _sys
10+
import tempfile as _tempfile
811
import time as _time
912

1013
import mpytool as _mpytool
@@ -27,7 +30,7 @@
2730
# Order of commands in help and completion
2831
_CMD_ORDER = [
2932
'ls', 'tree', 'cat', 'cp', 'mv', 'mkdir', 'rm', 'pwd', 'cd', 'path',
30-
'stop', 'reset', 'monitor', 'repl', 'exec', 'run', 'info',
33+
'stop', 'reset', 'monitor', 'repl', 'exec', 'run', 'edit', 'info',
3134
'flash', 'ota', 'mount', 'ln', 'speedtest', 'sleep',
3235
]
3336

@@ -897,6 +900,63 @@ def _dispatch_run(self, commands, is_last_group):
897900
self.verbose(f"RUN: {args.file} ({len(code)} bytes)", 1)
898901
self.mpy.comm.try_raw_paste(code, timeout=0)
899902

903+
def _get_editor(self, editor_arg=None):
904+
"""Get editor from --editor, $VISUAL, or $EDITOR"""
905+
if editor_arg:
906+
return editor_arg
907+
editor = _os.environ.get('VISUAL') or _os.environ.get('EDITOR')
908+
if not editor:
909+
raise ParamsError(
910+
'No editor configured. '
911+
'Set $VISUAL or $EDITOR, or use --editor')
912+
return editor
913+
914+
def cmd_edit(self, path, editor=None):
915+
"""Edit file on device using local editor"""
916+
editor_cmd = self._get_editor(editor)
917+
self.verbose(f"EDIT: {path}", 1)
918+
try:
919+
data = self.mpy.get(path)
920+
except _mpytool.FileNotFound:
921+
data = b''
922+
self.verbose(" (new file)", 1)
923+
suffix = '.' + path.rsplit('.', 1)[-1] if '.' in path else ''
924+
with _tempfile.NamedTemporaryFile(
925+
mode='wb', suffix=suffix, delete=False) as tmp:
926+
tmp.write(data)
927+
tmp_path = tmp.name
928+
try:
929+
self.verbose(f" editor: {editor_cmd}", 2)
930+
result = _subprocess.run(_shlex.split(editor_cmd) + [tmp_path])
931+
if result.returncode != 0:
932+
self.verbose(
933+
f" editor exited with code {result.returncode}, "
934+
"file not uploaded", 1, color='yellow')
935+
return
936+
with open(tmp_path, 'rb') as f:
937+
new_data = f.read()
938+
if new_data == data:
939+
self.verbose(" (no changes)", 1)
940+
return
941+
self.verbose(
942+
f" uploading {len(new_data)} bytes...", 1, color='cyan')
943+
self.mpy.put(new_data, path)
944+
self.verbose(" done", 1, color='green')
945+
finally:
946+
try:
947+
_os.unlink(tmp_path)
948+
except OSError:
949+
pass
950+
951+
@command('edit', 'Edit file on device in local editor.')
952+
@option('--editor', metavar='CMD', help='editor command')
953+
@argument('path', metavar='remote', help='device file to edit (: prefix)')
954+
def _dispatch_edit(self, commands, is_last_group):
955+
args = _make_parser(self._dispatch_edit).parse_args(commands)
956+
commands.clear()
957+
path = _parse_device_path(args.path, 'edit')
958+
self.cmd_edit(path, editor=args.editor)
959+
900960
@command('info', 'Show device info (platform, memory, filesystem).')
901961
def _dispatch_info(self, commands, is_last_group):
902962
_, commands[:] = _make_parser(self._dispatch_info).parse_known_args(
@@ -1213,8 +1273,8 @@ def _dispatch_args(self, commands, is_last_group):
12131273

12141274
_COMMANDS = frozenset({
12151275
'ls', 'tree', 'cat', 'mkdir', 'rm', 'pwd', 'cd', 'path',
1216-
'reset', 'stop', 'monitor', 'repl', 'exec', 'run', 'info', 'flash',
1217-
'ota', 'sleep', 'cp', 'mv', 'mount', 'ln', 'speedtest',
1276+
'reset', 'stop', 'monitor', 'repl', 'exec', 'run', 'edit', 'info',
1277+
'flash', 'ota', 'sleep', 'cp', 'mv', 'mount', 'ln', 'speedtest',
12181278
'_paths', '_ports', '_commands', '_options', '_args',
12191279
})
12201280

0 commit comments

Comments
 (0)