-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathzsh-history-to-fish
executable file
·96 lines (75 loc) · 3.41 KB
/
zsh-history-to-fish
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#!/usr/bin/env python3
#
# Based on /~https://github.com/rsalmei/zsh-history-to-fish
#
# v2023-11-09: Replaced click by argparse in order to minimize dependencies
import argparse
import os
import sys
from contextlib import contextmanager
import subprocess
from itertools import starmap
ZSH_HISTORY_FILE = '~/.zhistory'
FISH_HISTORY_FILE = '~/.local/share/fish/fish_history'
DIM, Z, BLUE, YELLOW = '\033[2m', '\033[0m', '\033[94m', '\033[93m'
# use zsh itself to read history file, since it is not utf-8 encoded.
# suggested in /~https://github.com/rsalmei/zsh-history-to-fish/issues/2
ZSH_HISTORY_READER = "zsh -i -c 'fc -R {}; fc -l -t \"%s\" 0'"
def read_history(input_file):
command = ZSH_HISTORY_READER.format(input_file)
p = subprocess.run(command, capture_output=True, shell=True, encoding='utf8')
lines = p.stdout.splitlines()
yield from map(lambda x: x.replace('\\n', '\n'), lines)
def parse_history(input_file):
stream = map(lambda x: x.split(maxsplit=2)[1:], read_history(input_file))
yield from filter(lambda x: len(x) > 1, stream)
def naive_zsh_to_fish(cmd):
result = cmd \
.replace(' && ', '&&') \
.replace('&&', '; and ') \
.replace(' || ', '||') \
.replace('||', '; or ')
return result
def display_changed(zsh, fish):
if zsh != fish:
return f'{DIM}zsh {Z}: {zsh}\n' \
f'{DIM}fish{Z}: {YELLOW}{fish}{Z}'
@contextmanager
def writer_factory(output_file, dry_run):
if dry_run:
yield lambda x: None
print(f'No file has been written.')
return
with open(output_file, 'a') as out:
yield lambda x: out.write(x)
print(f'\nFile "{output_file}" has been written successfully.')
def exporter(input_file, output_file, dry_run, no_convert):
print('ZSH history to Fish')
print('===================')
print(f'{DIM}input {Z}: {BLUE}{input_file}{Z} ({YELLOW}naive-convert={not no_convert}{Z})')
print(f'{DIM}output{Z}: {BLUE}{"dry run mode" if dry_run else output_file}{Z}')
converter = (lambda x: x) if no_convert else naive_zsh_to_fish
changed = []
with writer_factory(output_file, dry_run) as writer:
for i, (timestamp, command_zsh) in enumerate(parse_history(input_file)):
command_fish = converter(command_zsh)
fish_history = f'- cmd: {command_fish}\n when: {timestamp}\n'
writer(fish_history)
if command_zsh != command_fish:
changed.append((command_zsh, command_fish))
if i % 1000 == 0:
print('.', end='')
sys.stdout.flush()
print(f'\nProcessed {BLUE}{i + 1}{Z} commands.')
if changed:
#print('Converted commands:\n', '\n'.join(starmap(display_changed, changed)), sep='')
print(f'Converted {BLUE}{len(changed)}{Z} commands.')
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--input_file", default=os.path.expanduser(ZSH_HISTORY_FILE))
parser.add_argument("-o", "--output_file", default=os.path.expanduser(FISH_HISTORY_FILE),
help = 'Optional output, will append to fish history by default')
parser.add_argument("-d", "--dry-run", help='Do not write anything to filesystem', action='store_true')
parser.add_argument("-n", "--no-convert", help='Do not naively convert commands', action='store_true')
args = parser.parse_args()
if __name__ == '__main__':
exporter(args.input_file, args.output_file, args.dry_run, args.no_convert)