|
58 | 58 |
|
59 | 59 | from sage.cpython.string import bytes_to_str
|
60 | 60 |
|
| 61 | +from sage.misc.cachefunc import cached_method |
61 | 62 | from sage.misc.multireplace import multiple_replace
|
62 | 63 | from sage.structure.richcmp import richcmp, rich_to_bool
|
63 | 64 |
|
@@ -291,36 +292,63 @@ def completions(self, s, verbose=True):
|
291 | 292 | cmd_list = [x for x in cmd_list[1:-1].split(',') if x[0] != '?' and not x.endswith('-impl')]
|
292 | 293 | return [x for x in cmd_list if x.find(s) == 0]
|
293 | 294 |
|
294 |
| - def _commands(self, verbose=True): |
| 295 | + @cached_method |
| 296 | + def _commands(self): |
295 | 297 | """
|
296 | 298 | Return list of all commands defined in Maxima.
|
297 | 299 |
|
298 |
| - INPUT: |
| 300 | + OUTPUT: |
299 | 301 |
|
300 |
| - - ``verbose`` -- boolean (default: ``True``) |
| 302 | + A list of strings. |
301 | 303 |
|
302 |
| - OUTPUT: array of strings |
| 304 | + EXAMPLES: |
303 | 305 |
|
304 |
| - EXAMPLES:: |
| 306 | + The list changes from time to time (with new versions of |
| 307 | + Maxima), so we look only for a few reliable commands:: |
| 308 | +
|
| 309 | + sage: # long time |
| 310 | + sage: cs = maxima_calculus._commands() |
| 311 | + sage: "display" in cs |
| 312 | + True |
| 313 | + sage: "gcd" in cs |
| 314 | + True |
| 315 | + sage: "verbose" in cs |
| 316 | + True |
305 | 317 |
|
306 |
| - # The output is kind of random |
307 |
| - sage: sorted(maxima._commands(verbose=False)) # long time (:issue:`39569`) |
308 |
| - [... |
309 |
| - 'display', |
310 |
| - ... |
311 |
| - 'gcd', |
312 |
| - ... |
313 |
| - 'verbose', |
314 |
| - ...] |
315 | 318 | """
|
316 |
| - try: |
317 |
| - return self.__commands |
318 |
| - except AttributeError: |
319 |
| - self.__commands = sum( |
320 |
| - [self.completions(chr(65+n), verbose=verbose) + |
321 |
| - self.completions(chr(97+n), verbose=verbose) |
322 |
| - for n in range(26)], []) |
323 |
| - return self.__commands |
| 319 | + # Passing the empty string to apropos() gets ALL names. |
| 320 | + all_names = self._eval_line('apropos("")', |
| 321 | + error_check=False).split(",") |
| 322 | + |
| 323 | + # At the time of writing, searching a string for a specific |
| 324 | + # character was much much faster than searching a list/tuple. |
| 325 | + a_to_Z = "".join(chr(i+j) |
| 326 | + for i in range(ord('A'),ord('Z')+1) |
| 327 | + for j in (0, 32)) # 'a' = 'A' + 32 |
| 328 | + |
| 329 | + # Whack-a-mole to kill junk entries: |
| 330 | + # |
| 331 | + # * 'erf_%iargs', |
| 332 | + # * 'exp\\-form' |
| 333 | + # * 'is\\-boole\\-eval' |
| 334 | + # * 'is\\-boole\\-verify' |
| 335 | + # * 'maybe\\-boole\\-verify' |
| 336 | + # * 'time\\/\\/call' |
| 337 | + # * 'unknown\\?' |
| 338 | + # * 'SPLITS\\ IN\\ Q' |
| 339 | + # |
| 340 | + # None of these are documented, and the backslash / question |
| 341 | + # mark / percent symbol probably aren't going to do what you |
| 342 | + # think they're going to do if you type them in an ipython |
| 343 | + # shell. We have to trim spaces too because some names show up |
| 344 | + # with random leading spaces: ' tminverse', ' toeplitz', etc. |
| 345 | + # |
| 346 | + bad_chars = ("\\", "/", "?", "%") |
| 347 | + return [c |
| 348 | + for n in all_names |
| 349 | + if (c := n.strip()) |
| 350 | + and c[0] in a_to_Z |
| 351 | + and not any(bad in c for bad in bad_chars)] |
324 | 352 |
|
325 | 353 | def _tab_completion(self, verbose=True, use_disk_cache=True):
|
326 | 354 | r"""
|
@@ -357,7 +385,7 @@ def _tab_completion(self, verbose=True, use_disk_cache=True):
|
357 | 385 | print("\nBuilding Maxima command completion list (this takes")
|
358 | 386 | print("a few seconds only the first time you do it).")
|
359 | 387 | print("To force rebuild later, delete %s." % COMMANDS_CACHE)
|
360 |
| - v = self._commands(verbose=verbose) |
| 388 | + v = self._commands() |
361 | 389 | if verbose:
|
362 | 390 | print("\nDone!")
|
363 | 391 | self.__tab_completion = v
|
|
0 commit comments