##// END OF EJS Templates
Fixed variable names and path delimiter for #11441
Elliott Morgan Jobson -
Show More
@@ -1,165 +1,166 b''
1 1 """prompt-toolkit utilities
2 2
3 3 Everything in this module is a private API,
4 4 not to be used outside IPython.
5 5 """
6 6
7 7 # Copyright (c) IPython Development Team.
8 8 # Distributed under the terms of the Modified BSD License.
9 9
10 10 import unicodedata
11 11 from wcwidth import wcwidth
12 12
13 13 from IPython.core.completer import (
14 14 provisionalcompleter, cursor_to_position,
15 15 _deduplicate_completions)
16 16 from prompt_toolkit.completion import Completer, Completion
17 17 from prompt_toolkit.lexers import Lexer
18 18 from prompt_toolkit.lexers import PygmentsLexer
19 19 from prompt_toolkit.patch_stdout import patch_stdout
20 20
21 21 import pygments.lexers as pygments_lexers
22 import os
22 23
23 24 _completion_sentinel = object()
24 25
25 26 def _elide(string, *, min_elide=30):
26 27 """
27 28 If a string is long enough, and has at least 3 dots,
28 29 replace the middle part with ellipses.
29 30
30 31 If a string naming a file is long enough, and has at least 3 slashes,
31 32 replace the middle part with ellipses.
32 33
33 34 If three consecutive dots, or two consecutive dots are encountered these are
34 35 replaced by the equivalents HORIZONTAL ELLIPSIS or TWO DOT LEADER unicode
35 36 equivalents
36 37 """
37 38 string = string.replace('...','\N{HORIZONTAL ELLIPSIS}')
38 39 string = string.replace('..','\N{TWO DOT LEADER}')
39 40 if len(string) < min_elide:
40 41 return string
41 42
42 43 object_parts = string.split('.')
43 file_parts = string.split('/')
44 file_parts = string.split(os.sep)
44 45
45 46 if len(object_parts) > 3:
46 return '{}.{}\N{HORIZONTAL ELLIPSIS}{}.{}'.format(parts[0], parts[1][0], parts[-2][-1], parts[-1])
47 return '{}.{}\N{HORIZONTAL ELLIPSIS}{}.{}'.format(object_parts[0], object_parts[1][0], object_parts[-2][-1], object_parts[-1])
47 48
48 49 elif len(file_parts) > 3:
49 return '{}/{}\N{HORIZONTAL ELLIPSIS}{}/{}'.format(parts[0], parts[1][0], parts[-2][-1], parts[-1])
50 return '{}/{}\N{HORIZONTAL ELLIPSIS}{}/{}'.format(file_parts[0], file_parts[1][0], file_parts[-2][-1], file_parts[-1])
50 51
51 52 return string
52 53
53 54
54 55 def _adjust_completion_text_based_on_context(text, body, offset):
55 56 if text.endswith('=') and len(body) > offset and body[offset] is '=':
56 57 return text[:-1]
57 58 else:
58 59 return text
59 60
60 61
61 62 class IPythonPTCompleter(Completer):
62 63 """Adaptor to provide IPython completions to prompt_toolkit"""
63 64 def __init__(self, ipy_completer=None, shell=None):
64 65 if shell is None and ipy_completer is None:
65 66 raise TypeError("Please pass shell=an InteractiveShell instance.")
66 67 self._ipy_completer = ipy_completer
67 68 self.shell = shell
68 69
69 70 @property
70 71 def ipy_completer(self):
71 72 if self._ipy_completer:
72 73 return self._ipy_completer
73 74 else:
74 75 return self.shell.Completer
75 76
76 77 def get_completions(self, document, complete_event):
77 78 if not document.current_line.strip():
78 79 return
79 80 # Some bits of our completion system may print stuff (e.g. if a module
80 81 # is imported). This context manager ensures that doesn't interfere with
81 82 # the prompt.
82 83
83 84 with patch_stdout(), provisionalcompleter():
84 85 body = document.text
85 86 cursor_row = document.cursor_position_row
86 87 cursor_col = document.cursor_position_col
87 88 cursor_position = document.cursor_position
88 89 offset = cursor_to_position(body, cursor_row, cursor_col)
89 90 yield from self._get_completions(body, offset, cursor_position, self.ipy_completer)
90 91
91 92 @staticmethod
92 93 def _get_completions(body, offset, cursor_position, ipyc):
93 94 """
94 95 Private equivalent of get_completions() use only for unit_testing.
95 96 """
96 97 debug = getattr(ipyc, 'debug', False)
97 98 completions = _deduplicate_completions(
98 99 body, ipyc.completions(body, offset))
99 100 for c in completions:
100 101 if not c.text:
101 102 # Guard against completion machinery giving us an empty string.
102 103 continue
103 104 text = unicodedata.normalize('NFC', c.text)
104 105 # When the first character of the completion has a zero length,
105 106 # then it's probably a decomposed unicode character. E.g. caused by
106 107 # the "\dot" completion. Try to compose again with the previous
107 108 # character.
108 109 if wcwidth(text[0]) == 0:
109 110 if cursor_position + c.start > 0:
110 111 char_before = body[c.start - 1]
111 112 fixed_text = unicodedata.normalize(
112 113 'NFC', char_before + text)
113 114
114 115 # Yield the modified completion instead, if this worked.
115 116 if wcwidth(text[0:1]) == 1:
116 117 yield Completion(fixed_text, start_position=c.start - offset - 1)
117 118 continue
118 119
119 120 # TODO: Use Jedi to determine meta_text
120 121 # (Jedi currently has a bug that results in incorrect information.)
121 122 # meta_text = ''
122 123 # yield Completion(m, start_position=start_pos,
123 124 # display_meta=meta_text)
124 125 display_text = c.text
125 126
126 127 adjusted_text = _adjust_completion_text_based_on_context(c.text, body, offset)
127 128 if c.type == 'function':
128 129 yield Completion(adjusted_text, start_position=c.start - offset, display=_elide(display_text+'()'), display_meta=c.type+c.signature)
129 130 else:
130 131 yield Completion(adjusted_text, start_position=c.start - offset, display=_elide(display_text), display_meta=c.type)
131 132
132 133 class IPythonPTLexer(Lexer):
133 134 """
134 135 Wrapper around PythonLexer and BashLexer.
135 136 """
136 137 def __init__(self):
137 138 l = pygments_lexers
138 139 self.python_lexer = PygmentsLexer(l.Python3Lexer)
139 140 self.shell_lexer = PygmentsLexer(l.BashLexer)
140 141
141 142 self.magic_lexers = {
142 143 'HTML': PygmentsLexer(l.HtmlLexer),
143 144 'html': PygmentsLexer(l.HtmlLexer),
144 145 'javascript': PygmentsLexer(l.JavascriptLexer),
145 146 'js': PygmentsLexer(l.JavascriptLexer),
146 147 'perl': PygmentsLexer(l.PerlLexer),
147 148 'ruby': PygmentsLexer(l.RubyLexer),
148 149 'latex': PygmentsLexer(l.TexLexer),
149 150 }
150 151
151 152 def lex_document(self, document):
152 153 text = document.text.lstrip()
153 154
154 155 lexer = self.python_lexer
155 156
156 157 if text.startswith('!') or text.startswith('%%bash'):
157 158 lexer = self.shell_lexer
158 159
159 160 elif text.startswith('%%'):
160 161 for magic, l in self.magic_lexers.items():
161 162 if text.startswith('%%' + magic):
162 163 lexer = l
163 164 break
164 165
165 166 return lexer.lex_document(document)
General Comments 0
You need to be logged in to leave comments. Login now