| 466 | |
| 467 | class DictKeyCompletion(BaseCompletionType): |
| 468 | def matches( |
| 469 | self, |
| 470 | cursor_offset: int, |
| 471 | line: str, |
| 472 | *, |
| 473 | locals_: dict[str, Any] | None = None, |
| 474 | **kwargs: Any, |
| 475 | ) -> set[str] | None: |
| 476 | if locals_ is None: |
| 477 | return None |
| 478 | |
| 479 | r = self.locate(cursor_offset, line) |
| 480 | if r is None: |
| 481 | return None |
| 482 | current_dict_parts = lineparts.current_dict(cursor_offset, line) |
| 483 | if current_dict_parts is None: |
| 484 | return None |
| 485 | |
| 486 | dexpr = current_dict_parts.word |
| 487 | try: |
| 488 | obj = safe_eval(dexpr, locals_) |
| 489 | except EvaluationError: |
| 490 | return None |
| 491 | if isinstance(obj, dict) and obj.keys(): |
| 492 | matches = { |
| 493 | f"{k!r}]" for k in obj.keys() if repr(k).startswith(r.word) |
| 494 | } |
| 495 | return matches if matches else None |
| 496 | else: |
| 497 | return None |
| 498 | |
| 499 | def locate(self, cursor_offset: int, line: str) -> LinePart | None: |
| 500 | return lineparts.current_dict_key(cursor_offset, line) |