|
1 | 1 | # The MIT License |
2 | 2 | # |
3 | 3 | # Copyright (c) 2009 the bpython authors. |
4 | | -# Copyright (c) 2012,2015 Sebastian Ramacher |
| 4 | +# Copyright (c) 2012-2021 Sebastian Ramacher |
5 | 5 | # |
6 | 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | 7 | # of this software and associated documentation files (the "Software"), to deal |
|
24 | 24 | import os |
25 | 25 | import stat |
26 | 26 | from itertools import islice |
| 27 | +from typing import Iterable, Optional, List, TextIO |
27 | 28 |
|
28 | 29 | from .translations import _ |
29 | 30 | from .filelock import FileLock |
|
32 | 33 | class History: |
33 | 34 | """Stores readline-style history and current place in it""" |
34 | 35 |
|
35 | | - def __init__(self, entries=None, duplicates=True, hist_size=100): |
| 36 | + def __init__( |
| 37 | + self, |
| 38 | + entries: Optional[Iterable[str]] = None, |
| 39 | + duplicates: bool = True, |
| 40 | + hist_size: int = 100, |
| 41 | + ) -> None: |
36 | 42 | if entries is None: |
37 | 43 | self.entries = [""] |
38 | 44 | else: |
@@ -45,10 +51,10 @@ def __init__(self, entries=None, duplicates=True, hist_size=100): |
45 | 51 | self.duplicates = duplicates |
46 | 52 | self.hist_size = hist_size |
47 | 53 |
|
48 | | - def append(self, line): |
| 54 | + def append(self, line: str) -> None: |
49 | 55 | self.append_to(self.entries, line) |
50 | 56 |
|
51 | | - def append_to(self, entries, line): |
| 57 | + def append_to(self, entries: List[str], line: str) -> None: |
52 | 58 | line = line.rstrip("\n") |
53 | 59 | if line: |
54 | 60 | if not self.duplicates: |
@@ -60,15 +66,19 @@ def append_to(self, entries, line): |
60 | 66 | pass |
61 | 67 | entries.append(line) |
62 | 68 |
|
63 | | - def first(self): |
| 69 | + def first(self) -> str: |
64 | 70 | """Move back to the beginning of the history.""" |
65 | 71 | if not self.is_at_end: |
66 | 72 | self.index = len(self.entries) |
67 | 73 | return self.entries[-self.index] |
68 | 74 |
|
69 | 75 | def back( |
70 | | - self, start=True, search=False, target=None, include_current=False |
71 | | - ): |
| 76 | + self, |
| 77 | + start: bool = True, |
| 78 | + search: bool = False, |
| 79 | + target: Optional[str] = None, |
| 80 | + include_current: bool = False, |
| 81 | + ) -> str: |
72 | 82 | """Move one step back in the history.""" |
73 | 83 | if target is None: |
74 | 84 | target = self.saved_line |
@@ -84,23 +94,27 @@ def back( |
84 | 94 | return self.entry |
85 | 95 |
|
86 | 96 | @property |
87 | | - def entry(self): |
| 97 | + def entry(self) -> str: |
88 | 98 | """The current entry, which may be the saved line""" |
89 | 99 | return self.entries[-self.index] if self.index else self.saved_line |
90 | 100 |
|
91 | 101 | @property |
92 | | - def entries_by_index(self): |
| 102 | + def entries_by_index(self) -> List[str]: |
93 | 103 | return list(reversed(self.entries + [self.saved_line])) |
94 | 104 |
|
95 | | - def find_match_backward(self, search_term, include_current=False): |
| 105 | + def find_match_backward( |
| 106 | + self, search_term: str, include_current: bool = False |
| 107 | + ) -> int: |
96 | 108 | add = 0 if include_current else 1 |
97 | 109 | start = self.index + add |
98 | 110 | for idx, val in enumerate(islice(self.entries_by_index, start, None)): |
99 | 111 | if val.startswith(search_term): |
100 | 112 | return idx + add |
101 | 113 | return 0 |
102 | 114 |
|
103 | | - def find_partial_match_backward(self, search_term, include_current=False): |
| 115 | + def find_partial_match_backward( |
| 116 | + self, search_term: str, include_current: bool = False |
| 117 | + ) -> int: |
104 | 118 | add = 0 if include_current else 1 |
105 | 119 | start = self.index + add |
106 | 120 | for idx, val in enumerate(islice(self.entries_by_index, start, None)): |
@@ -109,8 +123,12 @@ def find_partial_match_backward(self, search_term, include_current=False): |
109 | 123 | return 0 |
110 | 124 |
|
111 | 125 | def forward( |
112 | | - self, start=True, search=False, target=None, include_current=False |
113 | | - ): |
| 126 | + self, |
| 127 | + start: bool = True, |
| 128 | + search: bool = False, |
| 129 | + target: Optional[str] = None, |
| 130 | + include_current: bool = False, |
| 131 | + ) -> str: |
114 | 132 | """Move one step forward in the history.""" |
115 | 133 | if target is None: |
116 | 134 | target = self.saved_line |
@@ -128,7 +146,9 @@ def forward( |
128 | 146 | self.index = 0 |
129 | 147 | return self.saved_line |
130 | 148 |
|
131 | | - def find_match_forward(self, search_term, include_current=False): |
| 149 | + def find_match_forward( |
| 150 | + self, search_term: str, include_current: bool = False |
| 151 | + ) -> int: |
132 | 152 | add = 0 if include_current else 1 |
133 | 153 | end = max(0, self.index - (1 - add)) |
134 | 154 | for idx in range(end): |
@@ -137,7 +157,9 @@ def find_match_forward(self, search_term, include_current=False): |
137 | 157 | return idx + (0 if include_current else 1) |
138 | 158 | return self.index |
139 | 159 |
|
140 | | - def find_partial_match_forward(self, search_term, include_current=False): |
| 160 | + def find_partial_match_forward( |
| 161 | + self, search_term: str, include_current: bool = False |
| 162 | + ) -> int: |
141 | 163 | add = 0 if include_current else 1 |
142 | 164 | end = max(0, self.index - (1 - add)) |
143 | 165 | for idx in range(end): |
@@ -146,40 +168,40 @@ def find_partial_match_forward(self, search_term, include_current=False): |
146 | 168 | return idx + add |
147 | 169 | return self.index |
148 | 170 |
|
149 | | - def last(self): |
| 171 | + def last(self) -> str: |
150 | 172 | """Move forward to the end of the history.""" |
151 | 173 | if not self.is_at_start: |
152 | 174 | self.index = 0 |
153 | 175 | return self.entries[0] |
154 | 176 |
|
155 | 177 | @property |
156 | | - def is_at_end(self): |
| 178 | + def is_at_end(self) -> bool: |
157 | 179 | return self.index >= len(self.entries) or self.index == -1 |
158 | 180 |
|
159 | 181 | @property |
160 | | - def is_at_start(self): |
| 182 | + def is_at_start(self) -> bool: |
161 | 183 | return self.index == 0 |
162 | 184 |
|
163 | | - def enter(self, line): |
| 185 | + def enter(self, line: str) -> None: |
164 | 186 | if self.index == 0: |
165 | 187 | self.saved_line = line |
166 | 188 |
|
167 | | - def reset(self): |
| 189 | + def reset(self) -> None: |
168 | 190 | self.index = 0 |
169 | 191 | self.saved_line = "" |
170 | 192 |
|
171 | | - def load(self, filename, encoding): |
| 193 | + def load(self, filename: str, encoding: str) -> None: |
172 | 194 | with open(filename, encoding=encoding, errors="ignore") as hfile: |
173 | 195 | with FileLock(hfile, filename=filename): |
174 | 196 | self.entries = self.load_from(hfile) |
175 | 197 |
|
176 | | - def load_from(self, fd): |
177 | | - entries = [] |
| 198 | + def load_from(self, fd: TextIO) -> List[str]: |
| 199 | + entries: List[str] = [] |
178 | 200 | for line in fd: |
179 | 201 | self.append_to(entries, line) |
180 | 202 | return entries if len(entries) else [""] |
181 | 203 |
|
182 | | - def save(self, filename, encoding, lines=0): |
| 204 | + def save(self, filename: str, encoding: str, lines: int = 0) -> None: |
183 | 205 | fd = os.open( |
184 | 206 | filename, |
185 | 207 | os.O_WRONLY | os.O_CREAT | os.O_TRUNC, |
@@ -189,14 +211,18 @@ def save(self, filename, encoding, lines=0): |
189 | 211 | with FileLock(hfile, filename=filename): |
190 | 212 | self.save_to(hfile, self.entries, lines) |
191 | 213 |
|
192 | | - def save_to(self, fd, entries=None, lines=0): |
| 214 | + def save_to( |
| 215 | + self, fd: TextIO, entries: Optional[List[str]] = None, lines: int = 0 |
| 216 | + ) -> None: |
193 | 217 | if entries is None: |
194 | 218 | entries = self.entries |
195 | 219 | for line in entries[-lines:]: |
196 | 220 | fd.write(line) |
197 | 221 | fd.write("\n") |
198 | 222 |
|
199 | | - def append_reload_and_write(self, s, filename, encoding): |
| 223 | + def append_reload_and_write( |
| 224 | + self, s: str, filename: str, encoding: str |
| 225 | + ) -> None: |
200 | 226 | if not self.hist_size: |
201 | 227 | return self.append(s) |
202 | 228 |
|
|