forked from donnemartin/gitsome
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdirstack.py
365 lines (311 loc) · 11 KB
/
dirstack.py
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
"""Directory stack and associated utilities for the xonsh shell.
"""
import os
import builtins
from glob import iglob
from argparse import ArgumentParser
DIRSTACK = []
"""A list containing the currently remembered directories."""
def _get_cwd():
try:
return os.getcwd()
except (OSError, FileNotFoundError):
return None
def _change_working_directory(newdir):
env = builtins.__xonsh_env__
old = _get_cwd()
try:
os.chdir(newdir)
except (OSError, FileNotFoundError):
return
new = _get_cwd()
if old is not None:
env['OLDPWD'] = old
if new is not None:
env['PWD'] = new
def _try_cdpath(apath):
# NOTE: this CDPATH implementation differs from the bash one.
# In bash if a CDPATH is set, an unqualified local folder
# is considered after all CDPATHs, example:
# CDPATH=$HOME/src (with src/xonsh/ inside)
# $ cd xonsh -> src/xonsh (whith xonsh/xonsh)
# a second $ cd xonsh has no effects, to move in the nested xonsh
# in bash a full $ cd ./xonsh is needed.
# In xonsh a relative folder is allways preferred.
env = builtins.__xonsh_env__
cdpaths = env.get('CDPATH')
for cdp in cdpaths:
for cdpath_prefixed_path in iglob(os.path.join(cdp, apath)):
return cdpath_prefixed_path
return apath
def cd(args, stdin=None):
"""Changes the directory.
If no directory is specified (i.e. if `args` is None) then this
changes to the current user's home directory.
"""
env = builtins.__xonsh_env__
oldpwd = env.get('OLDPWD', None)
cwd = _get_cwd()
if len(args) == 0:
d = os.path.expanduser('~')
elif len(args) == 1:
d = os.path.expanduser(args[0])
if not os.path.isdir(d):
if d == '-':
if oldpwd is not None:
d = oldpwd
else:
return '', 'cd: no previous directory stored\n'
elif d.startswith('-'):
try:
num = int(d[1:])
except ValueError:
return '', 'cd: Invalid destination: {0}\n'.format(d)
if num == 0:
return
elif num < 0:
return '', 'cd: Invalid destination: {0}\n'.format(d)
elif num > len(DIRSTACK):
e = 'cd: Too few elements in dirstack ({0} elements)\n'
return '', e.format(len(DIRSTACK))
else:
d = DIRSTACK[num - 1]
else:
d = _try_cdpath(d)
else:
return '', 'cd takes 0 or 1 arguments, not {0}\n'.format(len(args))
if not os.path.exists(d):
return '', 'cd: no such file or directory: {0}\n'.format(d)
if not os.path.isdir(d):
return '', 'cd: {0} is not a directory\n'.format(d)
# now, push the directory onto the dirstack if AUTO_PUSHD is set
if cwd is not None and env.get('AUTO_PUSHD'):
pushd(['-n', '-q', cwd])
_change_working_directory(os.path.abspath(d))
return None, None
def pushd(args, stdin=None):
"""xonsh command: pushd
Adds a directory to the top of the directory stack, or rotates the stack,
making the new top of the stack the current working directory.
"""
global DIRSTACK
try:
args = pushd_parser.parse_args(args)
except SystemExit:
return None, None
env = builtins.__xonsh_env__
pwd = env['PWD']
if env.get('PUSHD_MINUS', False):
BACKWARD = '-'
FORWARD = '+'
else:
BACKWARD = '+'
FORWARD = '-'
if args.dir is None:
try:
new_pwd = DIRSTACK.pop(0)
except IndexError:
e = 'pushd: Directory stack is empty\n'
return None, e
elif os.path.isdir(args.dir):
new_pwd = args.dir
else:
try:
num = int(args.dir[1:])
except ValueError:
e = 'Invalid argument to pushd: {0}\n'
return None, e.format(args.dir)
if num < 0:
e = 'Invalid argument to pushd: {0}\n'
return None, e.format(args.dir)
if num > len(DIRSTACK):
e = 'Too few elements in dirstack ({0} elements)\n'
return None, e.format(len(DIRSTACK))
elif args.dir.startswith(FORWARD):
if num == len(DIRSTACK):
new_pwd = None
else:
new_pwd = DIRSTACK.pop(len(DIRSTACK) - 1 - num)
elif args.dir.startswith(BACKWARD):
if num == 0:
new_pwd = None
else:
new_pwd = DIRSTACK.pop(num - 1)
else:
e = 'Invalid argument to pushd: {0}\n'
return None, e.format(args.dir)
if new_pwd is not None:
if args.cd:
DIRSTACK.insert(0, os.path.expanduser(pwd))
_change_working_directory(os.path.abspath(new_pwd))
else:
DIRSTACK.insert(0, os.path.expanduser(os.path.abspath(new_pwd)))
maxsize = env.get('DIRSTACK_SIZE')
if len(DIRSTACK) > maxsize:
DIRSTACK = DIRSTACK[:maxsize]
if not args.quiet and not env.get('PUSHD_SILENT'):
return dirs([], None)
return None, None
def popd(args, stdin=None):
"""
xonsh command: popd
Removes entries from the directory stack.
"""
global DIRSTACK
try:
args = pushd_parser.parse_args(args)
except SystemExit:
return None, None
env = builtins.__xonsh_env__
if env.get('PUSHD_MINUS'):
BACKWARD = '-'
FORWARD = '+'
else:
BACKWARD = '-'
FORWARD = '+'
if args.dir is None:
try:
new_pwd = DIRSTACK.pop(0)
except IndexError:
e = 'popd: Directory stack is empty\n'
return None, e
else:
try:
num = int(args.dir[1:])
except ValueError:
e = 'Invalid argument to popd: {0}\n'
return None, e.format(args.dir)
if num < 0:
e = 'Invalid argument to popd: {0}\n'
return None, e.format(args.dir)
if num > len(DIRSTACK):
e = 'Too few elements in dirstack ({0} elements)\n'
return None, e.format(len(DIRSTACK))
elif args.dir.startswith(FORWARD):
if num == len(DIRSTACK):
new_pwd = DIRSTACK.pop(0)
else:
new_pwd = None
DIRSTACK.pop(len(DIRSTACK) - 1 - num)
elif args.dir.startswith(BACKWARD):
if num == 0:
new_pwd = DIRSTACK.pop(0)
else:
new_pwd = None
DIRSTACK.pop(num - 1)
else:
e = 'Invalid argument to popd: {0}\n'
return None, e.format(args.dir)
if new_pwd is not None:
e = None
if args.cd:
_change_working_directory(os.path.abspath(new_pwd))
if not args.quiet and not env.get('PUSHD_SILENT'):
return dirs([], None)
return None, None
def dirs(args, stdin=None):
"""xonsh command: dirs
Displays the list of currently remembered directories. Can also be used
to clear the directory stack.
"""
global DIRSTACK
dirstack = [os.path.expanduser(builtins.__xonsh_env__['PWD'])] + DIRSTACK
try:
args = dirs_parser.parse_args(args)
except SystemExit:
return None, None
env = builtins.__xonsh_env__
if env.get('PUSHD_MINUS'):
BACKWARD = '-'
FORWARD = '+'
else:
BACKWARD = '-'
FORWARD = '+'
if args.clear:
dirstack = []
return None, None
if args.long:
o = dirstack
else:
d = os.path.expanduser('~')
o = [i.replace(d, '~') for i in dirstack]
if args.verbose:
out = ''
pad = len(str(len(o) - 1))
for (ix, e) in enumerate(o):
blanks = ' ' * (pad - len(str(ix)))
out += '\n{0}{1} {2}'.format(blanks, ix, e)
out = out[1:]
elif args.print_long:
out = '\n'.join(o)
else:
out = ' '.join(o)
N = args.N
if N is not None:
try:
num = int(N[1:])
except ValueError:
e = 'Invalid argument to dirs: {0}\n'
return None, e.format(N)
if num < 0:
e = 'Invalid argument to dirs: {0}\n'
return None, e.format(len(o))
if num >= len(o):
e = 'Too few elements in dirstack ({0} elements)\n'
return None, e.format(len(o))
if N.startswith(BACKWARD):
idx = num
elif N.startswith(FORWARD):
idx = len(o) - 1 - num
else:
e = 'Invalid argument to dirs: {0}\n'
return None, e.format(N)
out = o[idx]
return out + '\n', None
pushd_parser = ArgumentParser(prog="pushd")
pushd_parser.add_argument('dir', nargs='?')
pushd_parser.add_argument('-n',
dest='cd',
help='Suppresses the normal change of directory when'
' adding directories to the stack, so that only the'
' stack is manipulated.',
action='store_false')
pushd_parser.add_argument('-q',
dest='quiet',
help='Do not call dirs, regardless of $PUSHD_SILENT',
action='store_true')
popd_parser = ArgumentParser(prog="popd")
popd_parser.add_argument('dir', nargs='?')
popd_parser.add_argument('-n',
dest='cd',
help='Suppresses the normal change of directory when'
' adding directories to the stack, so that only the'
' stack is manipulated.',
action='store_false')
popd_parser.add_argument('-q',
dest='quiet',
help='Do not call dirs, regardless of $PUSHD_SILENT',
action='store_true')
dirs_parser = ArgumentParser(prog="dirs")
dirs_parser.add_argument('N', nargs='?')
dirs_parser.add_argument('-c',
dest='clear',
help='Clears the directory stack by deleting all of'
' the entries.',
action='store_true')
dirs_parser.add_argument('-p',
dest='print_long',
help='Print the directory stack with one entry per'
' line.',
action='store_true')
dirs_parser.add_argument('-v',
dest='verbose',
help='Print the directory stack with one entry per'
' line, prefixing each entry with its index in the'
' stack.',
action='store_true')
dirs_parser.add_argument('-l',
dest='long',
help='Produces a longer listing; the default listing'
' format uses a tilde to denote the home directory.',
action='store_true')