1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 import sys
18 import os
19 import traceback
20 import __builtin__
21
22 from libxyz.core.utils import ustring
23 from libxyz.core.utils import is_func
24 from libxyz.core.plugins import Namespace
25 from libxyz.core import FSRule
26
27 from libxyz.ui.colors import Palette
28 from libxyz.ui import Shortcut
29
30 from skin import Skin
31
32 import libxyz.exceptions as ex
35 """
36 Ensure the class has been instantiated
37 """
38
39 def wrap(cls, *args, **kwargs):
40 if cls._instance is None:
41 error(_(u"Class must be instantiated first!"))
42 else:
43 return func(cls, *args, **kwargs)
44
45 wrap.__doc__ = func.__doc__
46
47 return wrap
48
49
50
51 -def error(msg, trace=True):
52 if trace and hasattr(__builtin__, "xyzlog"):
53 xyzlog.debug(ustring(traceback.format_exc()))
54 raise ex.DSLError(_(u"DSL Error: %s") % msg)
55
56
57
58 -class XYZ(object):
59 """
60 XYZ DSL implementation object
61 """
62
63 api = ["let",
64 "val",
65 "section",
66 "unlet",
67 "load",
68 "bind",
69 "exec_file",
70 "kbd",
71 "action",
72 "macro",
73 "call",
74 "env",
75 "shell",
76 "alias",
77 "plugins_on",
78 "plugins_off",
79 "plugin_conf",
80 "icmd",
81 "prefix",
82 "help",
83 "vfs",
84 "vfs_path",
85 "hook",
86 "unhook",
87 "fsrule",
88 "palette",
89 "skin"
90 ]
91
92 EVENT_CONF_UPDATE = u"event:conf_update"
93
94 macros = {}
95
96 _instance = None
97 _env = {}
98
114
115
116
117 @classmethod
119 cls.macros["ACT_CWD"] = lambda: cls.xyz.pm.from_load(":sys:panel",
120 "cwd")()
121 cls.macros["INACT_CWD"] = lambda: cls.xyz.pm.from_load(":sys:panel",
122 "cwd")(False)
123
124 cls.macros["ACT_PATH"] = lambda: \
125 cls.xyz.pm.from_load(":sys:panel",
126 "get_selected")().path
127
128 cls.macros["INACT_PATH"] = lambda: \
129 cls.xyz.pm.from_load(":sys:panel",
130 "get_selected"
131 )(False).path
132 cls.macros["ACT_BASE"] = lambda: \
133 os.path.dirname(cls.macros["ACT_CWD"]())
134
135 cls.macros["INACT_BASE"] = lambda: \
136 os.path.dirname(cls.macros["INACT_CWD"]())
137
138 cls.macros["ACT_TAGGED"] = lambda: [x.full_path for x in
139 cls.xyz.pm.from_load(
140 ":sys:panel",
141 "get_tagged")()]
142 cls.macros["INACT_TAGGED"] = lambda: [x.full_path for x in
143 cls.xyz.pm.from_load(
144 ":sys:panel",
145 "get_tagged")(False)]
146
147 cls.macros["ACT_UNTAGGED"] = lambda: [x.full_path for x in
148 cls.xyz.pm.from_load(
149 ":sys:panel",
150 "get_untagged")()]
151 cls.macros["INACT_UNTAGGED"] = lambda: [x.full_path for x in
152 cls.xyz.pm.from_load(
153 ":sys:panel",
154 "get_untagged")(False)]
155
156
157
158 @classmethod
161
162
163
164 @classmethod
165 @instantiated
166 - def let(cls, var, val, sect=u"local"):
167 """
168 Set variable.
169 Variable will be available in xyz.conf[section][varname]
170 If section is not provided - local will be used
171 """
172
173 _conf = cls.xyz.conf
174
175 if sect not in _conf:
176 _conf[sect] = {}
177
178 if var in _conf[sect] and isinstance(_conf[sect][var], dict) and \
179 isinstance(val, dict):
180
181 _conf[sect][var].update(val)
182 else:
183 cls.xyz.conf[sect][var] = val
184
185 cls.xyz.hm.dispatch(cls.EVENT_CONF_UPDATE, var, val, sect)
186
187
188
189 @classmethod
190 @instantiated
191 - def val(cls, var, sect=u"local"):
192 """
193 Return variable value or None if undefined
194 """
195
196 try:
197 return cls.xyz.conf[sect][var]
198 except Exception:
199 return None
200
201
202
203 @classmethod
204 @instantiated
206 """
207 Return whole configuration section contents as a dictionary or None
208 if undefined
209 """
210
211 try:
212 return cls.xyz.conf[sect]
213 except Exception:
214 return None
215
216
217
218 @classmethod
219 @instantiated
220 - def unlet(cls, var, sect=u"local"):
221 """
222 Unset variable
223 """
224
225 if var in cls.xyz.conf[sect]:
226 del(cls.xyz.conf[sect])
227
228
229
230 @classmethod
231 @instantiated
232 - def load(cls, plugin):
233 """
234 Load method[s] from plugin
235 """
236
237 try:
238 cls.xyz.km.load(plugin)
239 except Exception, e:
240 error(_(u"Unable to load plugin %s: %s") %
241 (plugin, ustring(str(e))))
242
243
244
245 @classmethod
246 @instantiated
247 - def bind(cls, method, shortcut, context="DEFAULT"):
248 """
249 Bind method to shortcut
250 """
251
252 try:
253 cls.xyz.km.bind(method, shortcut, context=context)
254 except Exception, e:
255 error(_(u"Unable to bind shortcut %s: %s") % (str(shortcut),
256 ustring(str(e))))
257
258
259
260 @classmethod
261 @instantiated
262 - def kbd(cls, *args):
263 """
264 Create keyboard shortcut
265 """
266
267 return Shortcut(sc=list(args))
268
269
270
271 @classmethod
272 @instantiated
274 """
275 Execute DSL in file
276 """
277
278 f = None
279
280 try:
281 f = open(filename)
282 cls.execute(f.read())
283 except Exception, e:
284 error(_(u"Unable to execute file%s") % (
285 ustring(str(e))))
286
287 if f:
288 f.close()
289
290
291
292 @classmethod
293 @instantiated
295 """
296 Set up an action to be taken upon pressing action key on file
297 """
298
299 try:
300 cls.xyz.am.register(rule, fn)
301 except Exception, e:
302 error(_(u"Unable to register action: %s") % ustring(str(e)))
303
304
305
306 @classmethod
307 @instantiated
308 - def macro(cls, macroname):
309 """
310 Expand macro name.
311
312 Availbale macros:
313 * ACT_CWD -- Working directory in active panel
314 * INACT_CWD -- Working directory in inactive panel
315 * ACT_PATH -- Full selected object path in active panel
316 * INACT_PATH -- Full selected object path in inactive panel
317 * ACT_BASE -- Parent directory in active panel
318 * INACT_BASE -- Parent directory in inactive panel
319 * ACT_TAGGED -- List of tagged files in active panel
320 * INACT_TAGGED -- List of tagged files in inactive panel
321 * ACT_UNTAGGED -- List of not tagged files in active panel
322 * INACT_UNTAGGED -- List of not tagged files in inactive panel
323 """
324
325 if macroname in cls.macros:
326 try:
327 return cls.macros[macroname]()
328 except Exception, e:
329 xyzlog.warning(_(u"Unable to expand macro %s: %s") %
330 (ustring(macroname), ustring(str(e))))
331
332 return macroname
333
334
335
336 @classmethod
337 @instantiated
338 - def call(cls, method, *args):
339 """
340 Call plugin method
341 """
342
343 try:
344 p = Namespace(method)
345 m = cls.xyz.pm.from_load(p.pfull, p.method)
346 return m(*args)
347 except Exception, e:
348 error(_(u"Unable to execute method %s: %s" %
349 (method, ustring(str(e)))))
350
351
352
353 @classmethod
354 @instantiated
355 - def env(cls, var, default=None):
356 """
357 Return environment variable or default if is not set
358 """
359
360 return os.getenv(var, default)
361
362
363
364 @classmethod
365 @instantiated
366 - def shell(cls, cmd, *args, **kwargs):
367 """
368 Execute command via :core:shell plugin
369 Optional boolean argument 'current' can be provided to indicate
370 that cmd is to be run from current directory.
371 Optional boolean argument 'bg' can be provided to indicate that cmd
372 must be executed in background
373 Optional boolean argument 'reload' can be provided to indicate
374 that panel content should/should not be reloaded after execution
375 Optional boolean argument 'wait' can be provided to indicate
376 that shell should/should not wait for user input after command executed
377 The wait flag has higher priority than :core:shell's `wait`
378 configuration flag.
379 """
380
381 if kwargs.get("current", False):
382 cmd = "./%s" % cmd
383
384 if kwargs.get("bg", False):
385 bg = ["&"]
386 else:
387 bg = []
388
389 reloadp = kwargs.get("reload", True)
390 wait = kwargs.get("wait", None)
391
392 try:
393 exef = cls.xyz.pm.from_load(":core:shell", "execute")
394 escapef = cls.xyz.pm.from_load(":sys:cmd", "escape")
395 reloadf = cls.xyz.pm.from_load(":sys:panel", "reload_all")
396 exef(" ".join([escapef(cmd, True)] +
397 [escapef(a, True) for a in args] + bg), wait=wait)
398 if reloadp:
399 reloadf()
400 except Exception, e:
401 error(_(u"Error in DSL shell execution: %s") % ustring(str(e)))
402
403
404
405 @classmethod
406 @instantiated
407 - def alias(cls, alias, replace):
408 """
409 Set an alias which will be expanded in command line before execution
410 @param replace: Either string or function
411 """
412
413 return cls.let(alias, replace, sect="aliases")
414
415
416
417 @classmethod
418 @instantiated
419 - def icmd(cls, command, obj):
420 """
421 Set an internal command.
422 """
423
424 if not is_func(obj):
425 error(_(u"Invalid object type: %s. Function expected") %
426 type(obj), trace=False)
427
428 return cls.let(command, obj, sect="commands")
429
430
431
432 @classmethod
433 @instantiated
435 """
436 Enable plugin[s]
437 """
438
439 for plugin in plugins:
440 cls.let("plugins", {plugin: "ENABLE"}, sect="xyz")
441
442
443
444 @classmethod
445 @instantiated
447 """
448 Disable plugin[s]
449 """
450
451 for plugin in plugins:
452 cls.let("plugins", {plugin: "DISABLE"}, sect="xyz")
453
454
455
456 @classmethod
457 @instantiated
459 """
460 Configure plugin.
461
462 @param plugin: Plugin name
463 @param opts: dict {var1: val1, var2: var2,..}
464 """
465
466 if not isinstance(opts, dict):
467 error(_(u"Invalid opts type: %s. Dict instance expected")
468 % type(opts))
469
470 return cls.let(plugin, opts, sect="plugins")
471
472
473
474 @classmethod
475 @instantiated
482
483
484
485 @classmethod
486 @instantiated
487 - def help(cls, obj=None):
488 """
489 Help
490 """
491
492 fmt = lambda o: "%s\t%s" % (o, getattr(cls, o).__doc__)
493
494 if obj is not None and obj not in cls.api:
495 error(_(u"Invalid function %s") % obj)
496
497 if obj:
498 objs = [obj]
499 else:
500 objs = cls.api
501
502 return "\n".join([fmt(x) for x in objs]).replace("\t", " ")
503
504
505
506 @classmethod
507 @instantiated
508 - def vfs(cls, prefix, vfsclass):
509 """
510 Set prefix and VFSObject class for VFS dispatching
511 """
512
513 try:
514 return cls.xyz.vfs.register(prefix, vfsclass)
515 except Exception, e:
516 error(_(u"Error setting VFS prefix: %s") % ustring(str(e)))
517
518
519
520 @classmethod
521 @instantiated
523 """
524 Construct path using provided VFS driver
525 """
526
527 return path + "#vfs-%s#" % driver
528
529
530
531 @classmethod
532 @instantiated
533 - def hook(cls, event, proc):
534 """
535 Register a new hook.
536 Event is an event string and proc is a procedure to be called
537 """
538
539 try:
540 return cls.xyz.hm.register(event, proc)
541 except Exception, e:
542 error(_(u"Error registering new hook: %s") % ustring(str(e)))
543
544
545
546 @classmethod
547 @instantiated
549 """
550 Remove all hooks for the event
551 """
552
553 return cls.xyz.hm.clear(event)
554
555
556
557 @classmethod
558 @instantiated
560 """
561 Return libxyz.core.FSRule instance
562 """
563
564 try:
565 return FSRule(rule)
566 except Exception, e:
567 error(_(u"Error parsing FSRule: %s") % ustring(str(e)))
568
569
570
571 @classmethod
572 @instantiated
574 """
575 Create internal palette object
576
577 @param config: Dictionary of form:
578 {
579 'foreground': COLOR,
580 'background': COLOR,
581 'fg_attributes': [ATTR],
582 'mono': [ATTR],
583 'foreground_high': HG_COLOR,
584 'background_high': HG_COLOR
585 }
586 """
587
588 try:
589 return Palette(None, *Palette.convert(config))
590 except Exception, e:
591 error(_(u"Error creating Palette instance: %s") % ustring(str(e)))
592
593
594
595 @classmethod
596 @instantiated
597 - def skin(cls, **kwargs):
598 """
599 Make and register new skin
600 """
601
602 try:
603 cls.xyz.sm.add(Skin(**kwargs))
604 except Exception, e:
605 error(_(u"Error creating Skin instance: %s") % ustring(str(e)))
606
607
608
609 @classmethod
610 @instantiated
612 """
613 Execute DSL statements
614 @param source: Either string or open file-object or code object
615 """
616
617 try:
618 exec source in cls._env.copy()
619 except Exception, e:
620 error(_(u"Error in DSL execution: %s") % ustring(str(e)))
621
622
623
624 @classmethod
625 @instantiated
627 """
628 Return copy of global dsl environment
629 """
630
631 return cls._env.copy()
632
633
634
635
636 module = sys.modules[__name__]
637
638 __all__ = ["XYZ"]
639
640 for f in XYZ.api:
641 setattr(module, f, getattr(XYZ, f))
642 __all__.append(f)
643