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 | IPython requires Python 2.3 or newer. |
|
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 | # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu> |
|
33 | # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu> | |
@@ -51,7 +51,7 b' sys.path.append(os.path.dirname(__file__) + "/Extensions")' | |||||
51 | __all__ = ['deep_reload','genutils','ipstruct','ultraTB','DPyGetOpt', |
|
51 | __all__ = ['deep_reload','genutils','ipstruct','ultraTB','DPyGetOpt', | |
52 | 'Itpl','hooks','ConfigLoader','OutputTrap','Release','Shell', |
|
52 | 'Itpl','hooks','ConfigLoader','OutputTrap','Release','Shell', | |
53 | 'platutils','platutils_win32','platutils_posix','platutils_dummy', |
|
53 | 'platutils','platutils_win32','platutils_posix','platutils_dummy', | |
54 | 'ipapi','rlineimpl'] |
|
54 | 'ipapi','rlineimpl', 'strdispatch'] | |
55 |
|
55 | |||
56 | # Load __all__ in IPython namespace so that a simple 'import IPython' gives |
|
56 | # Load __all__ in IPython namespace so that a simple 'import IPython' gives | |
57 | # access to them via IPython.<name> |
|
57 | # access to them via IPython.<name> |
@@ -72,6 +72,8 b' import re' | |||||
72 | import shlex |
|
72 | import shlex | |
73 | import sys |
|
73 | import sys | |
74 | import IPython.rlineimpl as readline |
|
74 | import IPython.rlineimpl as readline | |
|
75 | from IPython.ipstruct import Struct | |||
|
76 | from IPython import ipapi | |||
75 |
|
77 | |||
76 | import types |
|
78 | import types | |
77 |
|
79 | |||
@@ -341,7 +343,7 b' class IPCompleter(Completer):' | |||||
341 | current (as of Python 2.3) Python readline it's possible to do |
|
343 | current (as of Python 2.3) Python readline it's possible to do | |
342 | better.""" |
|
344 | better.""" | |
343 |
|
345 | |||
344 | #print 'Completer->file_matches: <%s>' % text # dbg |
|
346 | # print 'Completer->file_matches: <%s>' % text # dbg | |
345 |
|
347 | |||
346 | # chars that require escaping with backslash - i.e. chars |
|
348 | # chars that require escaping with backslash - i.e. chars | |
347 | # that readline treats incorrectly as delimiters, but we |
|
349 | # that readline treats incorrectly as delimiters, but we | |
@@ -526,6 +528,25 b' class IPCompleter(Completer):' | |||||
526 | argMatches.append("%s=" %namedArg) |
|
528 | argMatches.append("%s=" %namedArg) | |
527 | return argMatches |
|
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 | def complete(self, text, state): |
|
550 | def complete(self, text, state): | |
530 | """Return the next possible completion for 'text'. |
|
551 | """Return the next possible completion for 'text'. | |
531 |
|
552 | |||
@@ -547,6 +568,7 b' class IPCompleter(Completer):' | |||||
547 | self.readline.insert_text('\t') |
|
568 | self.readline.insert_text('\t') | |
548 | return None |
|
569 | return None | |
549 |
|
570 | |||
|
571 | ||||
550 | magic_escape = self.magic_escape |
|
572 | magic_escape = self.magic_escape | |
551 | magic_prefix = self.magic_prefix |
|
573 | magic_prefix = self.magic_prefix | |
552 |
|
574 | |||
@@ -556,26 +578,31 b' class IPCompleter(Completer):' | |||||
556 | elif text.startswith('~'): |
|
578 | elif text.startswith('~'): | |
557 | text = os.path.expanduser(text) |
|
579 | text = os.path.expanduser(text) | |
558 | if state == 0: |
|
580 | if state == 0: | |
559 | # Extend the list of completions with the results of each |
|
581 | custom_res = self.dispatch_custom_completer(text) | |
560 | # matcher, so we return results to the user from all |
|
582 | if custom_res is not None: | |
561 | # namespaces. |
|
583 | # did custom completers produce something? | |
562 |
|
|
584 | self.matches = custom_res | |
563 | self.matches = [] |
|
|||
564 | for matcher in self.matchers: |
|
|||
565 | self.matches.extend(matcher(text)) |
|
|||
566 | else: |
|
585 | else: | |
567 | for matcher in self.matchers: |
|
586 | # Extend the list of completions with the results of each | |
568 | self.matches = matcher(text) |
|
587 | # matcher, so we return results to the user from all | |
569 |
|
|
588 | # namespaces. | |
570 | break |
|
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 | try: |
|
599 | try: | |
573 | return self.matches[state].replace(magic_prefix,magic_escape) |
|
600 | return self.matches[state].replace(magic_prefix,magic_escape) | |
574 | except IndexError: |
|
601 | except IndexError: | |
575 | return None |
|
602 | return None | |
576 | except: |
|
603 | except: | |
577 |
|
|
604 | from IPython.ultraTB import AutoFormattedTB; # dbg | |
578 |
|
|
605 | tb=AutoFormattedTB('Verbose');tb() #dbg | |
579 |
|
606 | |||
580 | # If completion fails, don't annoy the user. |
|
607 | # If completion fails, don't annoy the user. | |
581 | return None |
|
608 | return None |
@@ -32,7 +32,7 b" ip.set_hook('editor', calljed)" | |||||
32 | You can then enable the functionality by doing 'import myiphooks' |
|
32 | You can then enable the functionality by doing 'import myiphooks' | |
33 | somewhere in your configuration files or ipython command line. |
|
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 | # Copyright (C) 2005 Fernando Perez. <fperez@colorado.edu> |
|
38 | # Copyright (C) 2005 Fernando Perez. <fperez@colorado.edu> | |
@@ -145,6 +145,13 b' class CommandChainDispatcher:' | |||||
145 | """ Add a func to the cmd chain with given priority """ |
|
145 | """ Add a func to the cmd chain with given priority """ | |
146 | bisect.insort(self.chain,(priority,func)) |
|
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 | def result_display(self,arg): |
|
155 | def result_display(self,arg): | |
149 | """ Default display hook. |
|
156 | """ Default display hook. | |
150 |
|
157 |
@@ -6,7 +6,7 b' Requires Python 2.3 or newer.' | |||||
6 |
|
6 | |||
7 | This file contains all the classes and helper functions specific to IPython. |
|
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 | from IPython.background_jobs import BackgroundJobManager |
|
72 | from IPython.background_jobs import BackgroundJobManager | |
73 | from IPython.usage import cmd_line_usage,interactive_usage |
|
73 | from IPython.usage import cmd_line_usage,interactive_usage | |
74 | from IPython.genutils import * |
|
74 | from IPython.genutils import * | |
|
75 | from IPython.strdispatch import StrDispatch | |||
75 | import IPython.ipapi |
|
76 | import IPython.ipapi | |
76 |
|
77 | |||
77 | # Globals |
|
78 | # Globals | |
@@ -404,6 +405,8 b' class InteractiveShell(object,Magic):' | |||||
404 | # hooks holds pointers used for user-side customizations |
|
405 | # hooks holds pointers used for user-side customizations | |
405 | self.hooks = Struct() |
|
406 | self.hooks = Struct() | |
406 |
|
407 | |||
|
408 | self.strdispatchers = {} | |||
|
409 | ||||
407 | # Set all default hooks, defined in the IPython.hooks module. |
|
410 | # Set all default hooks, defined in the IPython.hooks module. | |
408 | hooks = IPython.hooks |
|
411 | hooks = IPython.hooks | |
409 | for hook_name in hooks.__all__: |
|
412 | for hook_name in hooks.__all__: | |
@@ -737,7 +740,7 b' class InteractiveShell(object,Magic):' | |||||
737 | __builtin__.__dict__[biname] = bival |
|
740 | __builtin__.__dict__[biname] = bival | |
738 | self.builtins_added.clear() |
|
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 | """set_hook(name,hook) -> sets an internal IPython hook. |
|
744 | """set_hook(name,hook) -> sets an internal IPython hook. | |
742 |
|
745 | |||
743 | IPython exposes some of its internal API as user-modifiable hooks. By |
|
746 | IPython exposes some of its internal API as user-modifiable hooks. By | |
@@ -747,13 +750,27 b' class InteractiveShell(object,Magic):' | |||||
747 | # At some point in the future, this should validate the hook before it |
|
750 | # At some point in the future, this should validate the hook before it | |
748 | # accepts it. Probably at least check that the hook takes the number |
|
751 | # accepts it. Probably at least check that the hook takes the number | |
749 | # of args it's supposed to. |
|
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 | dp = getattr(self.hooks, name, None) |
|
768 | dp = getattr(self.hooks, name, None) | |
751 | if name not in IPython.hooks.__all__: |
|
769 | if name not in IPython.hooks.__all__: | |
752 | print "Warning! Hook '%s' is not one of %s" % (name, IPython.hooks.__all__ ) |
|
770 | print "Warning! Hook '%s' is not one of %s" % (name, IPython.hooks.__all__ ) | |
753 | if not dp: |
|
771 | if not dp: | |
754 | dp = IPython.hooks.CommandChainDispatcher() |
|
772 | dp = IPython.hooks.CommandChainDispatcher() | |
755 |
|
773 | |||
756 | f = new.instancemethod(hook,self,self.__class__) |
|
|||
757 | try: |
|
774 | try: | |
758 | dp.add(f,priority) |
|
775 | dp.add(f,priority) | |
759 | except AttributeError: |
|
776 | except AttributeError: | |
@@ -1220,7 +1237,9 b' want to merge them back into the new files.""" % locals()' | |||||
1220 | self.user_global_ns, |
|
1237 | self.user_global_ns, | |
1221 | self.rc.readline_omit__names, |
|
1238 | self.rc.readline_omit__names, | |
1222 | self.alias_table) |
|
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 | # Platform-specific configuration |
|
1243 | # Platform-specific configuration | |
1225 | if os.name == 'nt': |
|
1244 | if os.name == 'nt': | |
1226 | self.readline_startup_hook = readline.set_pre_input_hook |
|
1245 | self.readline_startup_hook = readline.set_pre_input_hook |
@@ -3,6 +3,12 b'' | |||||
3 | * Debugger.py, iplib.py (debugger()): Add last set of Rocky |
|
3 | * Debugger.py, iplib.py (debugger()): Add last set of Rocky | |
4 | Bernsteins's patches for pydb integration. |
|
4 | Bernsteins's patches for pydb integration. | |
5 | http://bashdb.sourceforge.net/pydb/ |
|
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 | 2006-10-28 Fernando Perez <Fernando.Perez@colorado.edu> |
|
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