Skip to content

Commit e919491

Browse files
committed
simplify-logic
1 parent 44a6b61 commit e919491

2 files changed

Lines changed: 41 additions & 64 deletions

File tree

IPython/core/completer.py

Lines changed: 28 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2383,49 +2383,16 @@ def _determine_completion_context(self, line):
23832383
# Recursively determine the context of the expression
23842384
return self._determine_completion_context(expr)
23852385

2386-
# Match tuples followed by a dot (e.g., (a, b).index)
2387-
tuple_attr_match = re.search(
2388-
r"\(\s*[a-zA-Z_][a-zA-Z0-9_]*\s*(,\s*[a-zA-Z_][a-zA-Z0-9_]*\s*)+\)\.$", line
2389-
)
2390-
if tuple_attr_match:
2391-
return self._CompletionContextType.ATTRIBUTE
2392-
2393-
# Match for number literals should come first
23942386
# Handle plain number literals - should be global context
2395-
if re.search(r"^[-+]?\d+\.(\d+)?$", line):
2387+
# Ex: 3. -42.14 but not 3.1.
2388+
if re.search(r"(?<!\w)(?<!\d\.)([-+]?\d+\.(\d+)?)(?!\w)$", line):
23962389
return self._CompletionContextType.GLOBAL
23972390

2398-
# Match numeric literals in parentheses followed by dot
2399-
# Handles cases like (3).to_
2400-
numeric_paren_attr_match = re.search(
2401-
r"\([-+]?\d+(\.\d*)?\)(\.([a-zA-Z_][a-zA-Z0-9_]*)?)?$", line
2402-
)
2403-
if numeric_paren_attr_match:
2404-
return self._CompletionContextType.ATTRIBUTE
2405-
2406-
# Match float literals followed by dot and optional attribute
2407-
# Handles cases like 3.1.as_, -3.1.r_
2408-
float_attr_match = re.search(r"[-+]?\d+\.\d+\.([a-zA-Z_][a-zA-Z0-9_]*)?$", line)
2409-
if float_attr_match:
2410-
return self._CompletionContextType.ATTRIBUTE
2411-
2412-
# Handle indexed access followed by dot - like d[0].k
2413-
indexed_attr_match = re.search(
2414-
r"[a-zA-Z_][a-zA-Z0-9_]*\[[^\]]+\]\.([a-zA-Z_][a-zA-Z0-9_]*)?$", line
2415-
)
2416-
if indexed_attr_match:
2417-
return self._CompletionContextType.ATTRIBUTE
2418-
2419-
# Match 'word-dot-word' at end for attribute context.
2420-
# Ex: 'obj.', 'np.random.ran'.
2421-
chain_match = re.search(
2422-
r"([a-zA-Z_][a-zA-Z0-9_]*)\.([a-zA-Z_][a-zA-Z0-9_]*)?$", line
2423-
)
2391+
# Handle all other attribute matches np.ran, d[0].k, (a,b).count
2392+
chain_match = re.search(r".*(.+\.(?:[a-zA-Z]\w*)?)$", line)
24242393
if chain_match:
24252394
return self._CompletionContextType.ATTRIBUTE
24262395

2427-
# No attribute pattern → GLOBAL.
2428-
# Ex: 'var', '', '3.', '-42.5.'
24292396
return self._CompletionContextType.GLOBAL
24302397

24312398
def _is_in_string_or_comment(self, text):
@@ -2488,28 +2455,31 @@ def _is_in_string_or_comment(self, text):
24882455
i += 2
24892456
continue
24902457

2491-
# Handle expressions in f-strings or t-strings
2492-
if (
2493-
in_template_string
2494-
and text[i] == "{"
2495-
and not (in_expression and expression_depth > 0 and text[i - 1] != "{")
2496-
):
2497-
in_expression = True
2498-
expression_depth += 1
2499-
i += 1
2500-
continue
2501-
elif in_template_string and text[i] == "}" and in_expression:
2502-
expression_depth -= 1
2503-
if expression_depth == 0:
2504-
in_expression = False
2505-
i += 1
2506-
continue
2458+
# Handle nested braces within f-strings
2459+
if in_template_string:
2460+
# Special handling for consecutive opening braces
2461+
if i + 1 < len(text) and text[i : i + 2] == "{{":
2462+
i += 2
2463+
continue
25072464

2508-
# Handle nested braces within expressions
2509-
if in_expression and text[i] == "{":
2510-
expression_depth += 1
2511-
elif in_expression and text[i] == "}" and expression_depth > 0:
2512-
expression_depth -= 1
2465+
# Detect start of an expression
2466+
if text[i] == "{":
2467+
# Only increment depth and mark as expression if not already in an expression
2468+
# or if we're at a top-level nested brace
2469+
if not in_expression or (in_expression and expression_depth == 0):
2470+
in_expression = True
2471+
expression_depth += 1
2472+
i += 1
2473+
continue
2474+
2475+
# Detect end of an expression
2476+
if text[i] == "}":
2477+
expression_depth -= 1
2478+
if expression_depth <= 0:
2479+
in_expression = False
2480+
expression_depth = 0
2481+
i += 1
2482+
continue
25132483

25142484
# Handle quotes - also reset template string when closing quotes are encountered
25152485
if (
@@ -2555,7 +2525,6 @@ def _is_in_string_or_comment(self, text):
25552525
)
25562526

25572527
# Return tuple (is_string, is_in_expression)
2558-
# For nested f-strings, we're in a string but not necessarily in an expression
25592528
return (
25602529
is_string or (in_template_string and not in_expression),
25612530
in_expression and expression_depth > 0,

tests/test_completer.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1805,6 +1805,8 @@ def _(expected):
18051805
('f"greeting {user.na', "attribute"), # Cursor in f-string expression
18061806
('t"welcome {guest.na', "attribute"), # Cursor in t-string expression
18071807
('f"hello {name} worl', "global"), # Cursor in f-string outside expression
1808+
('f"hello {{a.', "global"),
1809+
('f"hello {{{a.', "attribute"),
18081810
# Backslash escapes in strings
18091811
('var = "string with \\"escaped quote and a dot.', "global"),
18101812
("escaped = 'single \\'quote\\' with a dot.", "global"),
@@ -1821,17 +1823,21 @@ def _(expected):
18211823
("multiple_nesting = {key: [value.attr", "attribute"),
18221824
# Numbers
18231825
("3.", "global"),
1826+
("3.14", "global"),
18241827
("-42.14", "global"),
1828+
("x = func(3.14", "global"),
1829+
("x = func(a3.", "attribute"),
1830+
("x = func(a3.12", "global"),
1831+
("3.1.", "attribute"),
1832+
("-3.1.", "attribute"),
1833+
("(3).", "attribute"),
18251834
# Additional cases
1835+
("", "global"),
18261836
('str_with_code = "x.attr', "global"),
18271837
('f"formatted {obj.attr', "attribute"),
18281838
('f"formatted {obj.attr}', "global"),
18291839
("dict_with_dots = {'key.with.dots': value.attr", "attribute"),
18301840
("d[f'{a}']['{a.", "global"),
1831-
("3.", "global"),
1832-
("3.1.", "attribute"),
1833-
("-3.1.", "attribute"),
1834-
("(3).", "attribute"),
18351841
],
18361842
)
18371843
def test_completion_context(line, expected):
@@ -1846,7 +1852,9 @@ def test_completion_context(line, expected):
18461852
@pytest.mark.parametrize(
18471853
"line, expected",
18481854
[
1849-
("f'{f'a.", "global"),
1855+
("f'{f'a.", "global"), # Nested f-string
1856+
("3a.", "global"), # names starting with numbers or other symbols
1857+
("$).", "global"), # random things with dot at end
18501858
],
18511859
)
18521860
def test_unsupported_completion_context(line, expected):

0 commit comments

Comments
 (0)