Show More
@@ -0,0 +1,47 b'' | |||
|
1 | """ Tab completion support for a couple of linux package managers | |
|
2 | ||
|
3 | This is also an example of how to write custom completer plugins | |
|
4 | or hooks. | |
|
5 | ||
|
6 | Practical use: | |
|
7 | ||
|
8 | [ipython]|1> import ipy_linux_package_managers | |
|
9 | [ipython]|2> apt-get u<<< press tab here >>> | |
|
10 | update upgrade | |
|
11 | [ipython]|2> apt-get up | |
|
12 | ||
|
13 | """ | |
|
14 | import IPython.ipapi | |
|
15 | ||
|
16 | ip = IPython.ipapi.get() | |
|
17 | ||
|
18 | def apt_completers(self, event): | |
|
19 | """ This should return a list of strings with possible completions. | |
|
20 | ||
|
21 | Note that all the included strings that don't start with event.symbol | |
|
22 | are removed, in order to not confuse readline. | |
|
23 | ||
|
24 | """ | |
|
25 | # print event # dbg | |
|
26 | ||
|
27 | # commands are only suggested for the 'command' part of package manager | |
|
28 | # invocation | |
|
29 | ||
|
30 | cmd = (event.line + "<placeholder>").rsplit(None,1)[0] | |
|
31 | # print cmd | |
|
32 | if cmd.endswith('apt-get') or cmd.endswith('yum'): | |
|
33 | return ['update', 'upgrade', 'install', 'remove'] | |
|
34 | ||
|
35 | # later on, add dpkg -l / whatever to get list of possible | |
|
36 | # packages, add switches etc. for the rest of command line | |
|
37 | # filling | |
|
38 | ||
|
39 | raise IPython.ipapi.TryNext | |
|
40 | ||
|
41 | ||
|
42 | # re_key specifies the regexp that triggers the specified completer | |
|
43 | ||
|
44 | ip.set_hook('complete_command', apt_completers, re_key = '.*apt-get') | |
|
45 | ||
|
46 | ip.set_hook('complete_command', apt_completers, re_key = '.*yum') | |
|
47 | No newline at end of file |
@@ -0,0 +1,57 b'' | |||
|
1 | from IPython.hooks import CommandChainDispatcher | |
|
2 | import IPython.hooks | |
|
3 | ||
|
4 | import re | |
|
5 | ||
|
6 | class StrDispatch(object): | |
|
7 | """ Dispatch (lookup) a set of strings / regexps for match """ | |
|
8 | def __init__(self): | |
|
9 | self.strs = {} | |
|
10 | self.regexs = {} | |
|
11 | def add_s(self, s, obj, priority= 0 ): | |
|
12 | """ Adds a target 'string' for dispatching """ | |
|
13 | ||
|
14 | chain = self.strs.get(s, CommandChainDispatcher()) | |
|
15 | chain.add(obj,priority) | |
|
16 | self.strs[s] = chain | |
|
17 | ||
|
18 | def add_re(self, regex, obj, priority= 0 ): | |
|
19 | """ Adds a target regexp for dispatching """ | |
|
20 | ||
|
21 | chain = self.regexs.get(regex, CommandChainDispatcher()) | |
|
22 | chain.add(obj,priority) | |
|
23 | self.regexs[regex] = chain | |
|
24 | ||
|
25 | def dispatch(self, key): | |
|
26 | """ Get a seq of Commandchain objects that match key """ | |
|
27 | if key in self.strs: | |
|
28 | yield self.strs[key] | |
|
29 | ||
|
30 | for r, obj in self.regexs.items(): | |
|
31 | if re.match(r, key): | |
|
32 | yield obj | |
|
33 | else: | |
|
34 | #print "nomatch",key | |
|
35 | pass | |
|
36 | ||
|
37 | ||
|
38 | def __repr__(self): | |
|
39 | return "<Strdispatch %s, %s>" % (self.strs, self.regexs) | |
|
40 | def flat_matches(self, key): | |
|
41 | """ Yield all 'value' targets, without priority """ | |
|
42 | for val in self.dispatch(key): | |
|
43 | for el in val: | |
|
44 | yield el[1] # only value, no priority | |
|
45 | return | |
|
46 | ||
|
47 | ||
|
48 | def test(): | |
|
49 | d = StrDispatch() | |
|
50 | d.add_s('hei',34, priority = 4) | |
|
51 | d.add_s('hei',123, priority = 2) | |
|
52 | print list(d.dispatch('hei')) | |
|
53 | d.add_re('h.i', 686) | |
|
54 | print list(d.flat_matches('hei')) | |
|
55 | ||
|
56 | if __name__ == '__main__': | |
|
57 | test() No newline at end of file |
@@ -27,7 +27,7 b' IPython tries to:' | |||
|
27 | 27 | |
|
28 | 28 | IPython requires Python 2.3 or newer. |
|
29 | 29 | |
|
30 |
$Id: __init__.py 1 |
|
|
30 | $Id: __init__.py 1854 2006-10-30 19:54:25Z vivainio $""" | |
|
31 | 31 | |
|
32 | 32 | #***************************************************************************** |
|
33 | 33 | # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu> |
@@ -51,7 +51,7 b' sys.path.append(os.path.dirname(__file__) + "/Extensions")' | |||
|
51 | 51 | __all__ = ['deep_reload','genutils','ipstruct','ultraTB','DPyGetOpt', |
|
52 | 52 | 'Itpl','hooks','ConfigLoader','OutputTrap','Release','Shell', |
|
53 | 53 | 'platutils','platutils_win32','platutils_posix','platutils_dummy', |
|
54 | 'ipapi','rlineimpl'] | |
|
54 | 'ipapi','rlineimpl', 'strdispatch'] | |
|
55 | 55 | |
|
56 | 56 | # Load __all__ in IPython namespace so that a simple 'import IPython' gives |
|
57 | 57 | # access to them via IPython.<name> |
@@ -72,6 +72,8 b' import re' | |||
|
72 | 72 | import shlex |
|
73 | 73 | import sys |
|
74 | 74 | import IPython.rlineimpl as readline |
|
75 | from IPython.ipstruct import Struct | |
|
76 | from IPython import ipapi | |
|
75 | 77 | |
|
76 | 78 | import types |
|
77 | 79 | |
@@ -341,7 +343,7 b' class IPCompleter(Completer):' | |||
|
341 | 343 | current (as of Python 2.3) Python readline it's possible to do |
|
342 | 344 | better.""" |
|
343 | 345 | |
|
344 | #print 'Completer->file_matches: <%s>' % text # dbg | |
|
346 | # print 'Completer->file_matches: <%s>' % text # dbg | |
|
345 | 347 | |
|
346 | 348 | # chars that require escaping with backslash - i.e. chars |
|
347 | 349 | # that readline treats incorrectly as delimiters, but we |
@@ -526,6 +528,25 b' class IPCompleter(Completer):' | |||
|
526 | 528 | argMatches.append("%s=" %namedArg) |
|
527 | 529 | return argMatches |
|
528 | 530 | |
|
531 | def dispatch_custom_completer(self,text): | |
|
532 | # print "Custom! '%s' %s" % (text, self.custom_completers) # dbg | |
|
533 | line = self.lbuf | |
|
534 | event = Struct() | |
|
535 | event.line = line | |
|
536 | event.symbol = text | |
|
537 | event.command = None | |
|
538 | for c in self.custom_completers.flat_matches(self.lbuf): | |
|
539 | # print "try",c # dbg | |
|
540 | try: | |
|
541 | res = c(event) | |
|
542 | return [r for r in res if r.startswith(text)] | |
|
543 | except ipapi.TryNext: | |
|
544 | pass | |
|
545 | ||
|
546 | return None | |
|
547 | ||
|
548 | ||
|
549 | ||
|
529 | 550 | def complete(self, text, state): |
|
530 | 551 | """Return the next possible completion for 'text'. |
|
531 | 552 | |
@@ -547,6 +568,7 b' class IPCompleter(Completer):' | |||
|
547 | 568 | self.readline.insert_text('\t') |
|
548 | 569 | return None |
|
549 | 570 | |
|
571 | ||
|
550 | 572 | magic_escape = self.magic_escape |
|
551 | 573 | magic_prefix = self.magic_prefix |
|
552 | 574 | |
@@ -556,26 +578,31 b' class IPCompleter(Completer):' | |||
|
556 | 578 | elif text.startswith('~'): |
|
557 | 579 | text = os.path.expanduser(text) |
|
558 | 580 | if state == 0: |
|
559 | # Extend the list of completions with the results of each | |
|
560 | # matcher, so we return results to the user from all | |
|
561 | # namespaces. | |
|
562 |
|
|
|
563 | self.matches = [] | |
|
564 | for matcher in self.matchers: | |
|
565 | self.matches.extend(matcher(text)) | |
|
581 | custom_res = self.dispatch_custom_completer(text) | |
|
582 | if custom_res is not None: | |
|
583 | # did custom completers produce something? | |
|
584 | self.matches = custom_res | |
|
566 | 585 | else: |
|
567 | for matcher in self.matchers: | |
|
568 | self.matches = matcher(text) | |
|
569 |
|
|
|
570 | break | |
|
586 | # Extend the list of completions with the results of each | |
|
587 | # matcher, so we return results to the user from all | |
|
588 | # namespaces. | |
|
589 | if self.merge_completions: | |
|
590 | self.matches = [] | |
|
591 | for matcher in self.matchers: | |
|
592 | self.matches.extend(matcher(text)) | |
|
593 | else: | |
|
594 | for matcher in self.matchers: | |
|
595 | self.matches = matcher(text) | |
|
596 | if self.matches: | |
|
597 | break | |
|
571 | 598 | |
|
572 | 599 | try: |
|
573 | 600 | return self.matches[state].replace(magic_prefix,magic_escape) |
|
574 | 601 | except IndexError: |
|
575 | 602 | return None |
|
576 | 603 | except: |
|
577 |
|
|
|
578 |
|
|
|
604 | from IPython.ultraTB import AutoFormattedTB; # dbg | |
|
605 | tb=AutoFormattedTB('Verbose');tb() #dbg | |
|
579 | 606 | |
|
580 | 607 | # If completion fails, don't annoy the user. |
|
581 | 608 | return None |
@@ -32,7 +32,7 b" ip.set_hook('editor', calljed)" | |||
|
32 | 32 | You can then enable the functionality by doing 'import myiphooks' |
|
33 | 33 | somewhere in your configuration files or ipython command line. |
|
34 | 34 | |
|
35 |
$Id: hooks.py 1 |
|
|
35 | $Id: hooks.py 1854 2006-10-30 19:54:25Z vivainio $""" | |
|
36 | 36 | |
|
37 | 37 | #***************************************************************************** |
|
38 | 38 | # Copyright (C) 2005 Fernando Perez. <fperez@colorado.edu> |
@@ -145,6 +145,13 b' class CommandChainDispatcher:' | |||
|
145 | 145 | """ Add a func to the cmd chain with given priority """ |
|
146 | 146 | bisect.insort(self.chain,(priority,func)) |
|
147 | 147 | |
|
148 | def __iter__(self): | |
|
149 | """ Return all objects in chain. | |
|
150 | ||
|
151 | Handy if the objects are not callable. | |
|
152 | """ | |
|
153 | return iter(self.chain) | |
|
154 | ||
|
148 | 155 | def result_display(self,arg): |
|
149 | 156 | """ Default display hook. |
|
150 | 157 |
@@ -6,7 +6,7 b' Requires Python 2.3 or newer.' | |||
|
6 | 6 | |
|
7 | 7 | This file contains all the classes and helper functions specific to IPython. |
|
8 | 8 | |
|
9 |
$Id: iplib.py 185 |
|
|
9 | $Id: iplib.py 1854 2006-10-30 19:54:25Z vivainio $ | |
|
10 | 10 | """ |
|
11 | 11 | |
|
12 | 12 | #***************************************************************************** |
@@ -72,6 +72,7 b' from IPython.ipstruct import Struct' | |||
|
72 | 72 | from IPython.background_jobs import BackgroundJobManager |
|
73 | 73 | from IPython.usage import cmd_line_usage,interactive_usage |
|
74 | 74 | from IPython.genutils import * |
|
75 | from IPython.strdispatch import StrDispatch | |
|
75 | 76 | import IPython.ipapi |
|
76 | 77 | |
|
77 | 78 | # Globals |
@@ -404,6 +405,8 b' class InteractiveShell(object,Magic):' | |||
|
404 | 405 | # hooks holds pointers used for user-side customizations |
|
405 | 406 | self.hooks = Struct() |
|
406 | 407 | |
|
408 | self.strdispatchers = {} | |
|
409 | ||
|
407 | 410 | # Set all default hooks, defined in the IPython.hooks module. |
|
408 | 411 | hooks = IPython.hooks |
|
409 | 412 | for hook_name in hooks.__all__: |
@@ -737,7 +740,7 b' class InteractiveShell(object,Magic):' | |||
|
737 | 740 | __builtin__.__dict__[biname] = bival |
|
738 | 741 | self.builtins_added.clear() |
|
739 | 742 | |
|
740 | def set_hook(self,name,hook, priority = 50): | |
|
743 | def set_hook(self,name,hook, priority = 50, str_key = None, re_key = None): | |
|
741 | 744 | """set_hook(name,hook) -> sets an internal IPython hook. |
|
742 | 745 | |
|
743 | 746 | IPython exposes some of its internal API as user-modifiable hooks. By |
@@ -747,13 +750,27 b' class InteractiveShell(object,Magic):' | |||
|
747 | 750 | # At some point in the future, this should validate the hook before it |
|
748 | 751 | # accepts it. Probably at least check that the hook takes the number |
|
749 | 752 | # of args it's supposed to. |
|
753 | ||
|
754 | f = new.instancemethod(hook,self,self.__class__) | |
|
755 | ||
|
756 | # check if the hook is for strdispatcher first | |
|
757 | if str_key is not None: | |
|
758 | sdp = self.strdispatchers.get(name, StrDispatch()) | |
|
759 | sdp.add_s(str_key, f, priority ) | |
|
760 | self.strdispatchers[name] = sdp | |
|
761 | return | |
|
762 | if re_key is not None: | |
|
763 | sdp = self.strdispatchers.get(name, StrDispatch()) | |
|
764 | sdp.add_re(re.compile(re_key), f, priority ) | |
|
765 | self.strdispatchers[name] = sdp | |
|
766 | return | |
|
767 | ||
|
750 | 768 | dp = getattr(self.hooks, name, None) |
|
751 | 769 | if name not in IPython.hooks.__all__: |
|
752 | 770 | print "Warning! Hook '%s' is not one of %s" % (name, IPython.hooks.__all__ ) |
|
753 | 771 | if not dp: |
|
754 | 772 | dp = IPython.hooks.CommandChainDispatcher() |
|
755 | 773 | |
|
756 | f = new.instancemethod(hook,self,self.__class__) | |
|
757 | 774 | try: |
|
758 | 775 | dp.add(f,priority) |
|
759 | 776 | except AttributeError: |
@@ -1220,7 +1237,9 b' want to merge them back into the new files.""" % locals()' | |||
|
1220 | 1237 | self.user_global_ns, |
|
1221 | 1238 | self.rc.readline_omit__names, |
|
1222 | 1239 | self.alias_table) |
|
1223 | ||
|
1240 | sdisp = self.strdispatchers.get('complete_command', StrDispatch()) | |
|
1241 | self.strdispatchers['complete_command'] = sdisp | |
|
1242 | self.Completer.custom_completers = sdisp | |
|
1224 | 1243 | # Platform-specific configuration |
|
1225 | 1244 | if os.name == 'nt': |
|
1226 | 1245 | self.readline_startup_hook = readline.set_pre_input_hook |
@@ -3,6 +3,12 b'' | |||
|
3 | 3 | * Debugger.py, iplib.py (debugger()): Add last set of Rocky |
|
4 | 4 | Bernsteins's patches for pydb integration. |
|
5 | 5 | http://bashdb.sourceforge.net/pydb/ |
|
6 | ||
|
7 | * strdispatch.py, iplib.py, completer.py, IPython/__init__.py, | |
|
8 | Extensions/ipy_linux_package_managers.py, hooks.py: Implement | |
|
9 | custom completer hook to allow the users to implement their own | |
|
10 | completers. See ipy_linux_package_managers.py for example. The | |
|
11 | hook name is 'complete_command'. | |
|
6 | 12 | |
|
7 | 13 | 2006-10-28 Fernando Perez <Fernando.Perez@colorado.edu> |
|
8 | 14 |
General Comments 0
You need to be logged in to leave comments.
Login now