8000 Indentation for match-case statements · codemirror/lang-python@0f6499a · GitHub
[go: up one dir, main page]

Skip to content

Commit 0f6499a

Browse files
authored
Indentation for match-case statements
FIX: Properly indent match/case statements.
1 parent fec0c16 commit 0f6499a

File tree

2 files changed

+178
-4
lines changed

2 files changed

+178
-4
lines changed

src/python.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ function innerBody(context: TreeIndentContext) {
1515
break
1616
} else if (before.name == "Comment") {
1717
pos = before.from
18-
} else if (before.name == "Body") {
18+
} else if (before.name == "Body" || before.name == "MatchBody") {
1919
if (context.baseIndentFor(before) + context.unit <= lineIndent) found = before
2020
node = before
21+
} else if (before.name == "MatchClause") {
22+
node = before
2123
} else if (before.type.is("Statement")) {
2224
node = before
2325
} else {
@@ -40,7 +42,7 @@ function indentBody(context: TreeIndentContext, node: SyntaxNode) {
4042
// A normally deindenting keyword that appears at a higher
4143
// indentation than the block should probably be handled by the next
4244
// level
43-
if (/^\s*(else:|elif |except |finally:)/.test(context.textAfter) && context.lineIndent(context.pos, -1) > base)
45+
if (/^\s*(else:|elif |except |finally:|case\s+[^=:]+:)/.test(context.textAfter) && context.lineIndent(context.pos, -1) > base)
4446
return null
4547
return base + context.unit
4648
}
@@ -57,9 +59,20 @@ export const pythonLanguage = LRLanguage.define({
5759
let inner = innerBody(context)
5860
return indentBody(context, inner || context.node) ?? context.continue()
5961
},
62+
63+
MatchBody: context => {
64+
let inner = innerBody(context)
65+
return indentBody(context, inner || context.node) ?? context.continue()
66+
},
67+
6068
IfStatement: cx => /^\s*(else:|elif )/.test(cx.textAfter) ? cx.baseIndent : cx.continue(),
6169
"ForStatement WhileStatement": cx => /^\s*else:/.test(cx.textAfter) ? cx.baseIndent : cx.continue(),
6270
TryStatement: cx => /^\s*(except |finally:|else:)/.test(cx.textAfter) ? cx.baseIndent : cx.continue(),
71+
MatchStatement: cx => {
72+
if (/^\s*case /.test(cx.textAfter)) return cx.baseIndent + cx.unit
73+
return cx.continue()
74+
},
75+
6376
"TupleExpression ComprehensionExpression ParamList ArgList ParenthesizedExpression": delimitedIndent({closing: ")"}),
6477
"DictionaryExpression DictionaryComprehensionExpression SetExpression SetComprehensionExpression": delimitedIndent({closing: "}"}),
6578
"ArrayExpression ArrayComprehensionExpression": delimitedIndent({closing: "]"}),
@@ -69,6 +82,7 @@ export const pythonLanguage = LRLanguage.define({
6982
return (inner && indentBody(context, inner)) ?? context.continue()
7083
}
7184
}),
85+
7286
foldNodeProp.add({
7387
"ArrayExpression DictionaryExpression SetExpression TupleExpression": foldInside,
7488
Body: (node, state) => ({from: node.from + 1, to: node.to - (node.to == state.doc.length ? 0 : 1)})
@@ -82,7 +96,8 @@ export const pythonLanguage = LRLanguage.define({
8296
"F", "FR", "RF", "R", "U", "B", "BR", "RB"]
8397
},
8498
commentTokens: {line: "#"},
85-
indentOnInput: /^\s*([\}\]\)]|else:|elif |except |finally:)$/
99+
// Indent logic logic are triggered upon below input patterns
100+
indentOnInput: /^\s*([\}\]\)]|else:|elif |except |finally:|case\s+[^:]*:?)$/,
86101
}
87102
})
88103

test/test-indent.ts

Lines changed: 160 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ function check(code: string) {
99
let state = EditorState.create({doc: code, extensions: [python().language]})
1010
for (let pos = 0, lines = code.split("\n"), i = 0; i < lines.length; i++) {
1111
let line = lines[i], indent = /^\s*/.exec(line)![0].length
12-
ist(`${getIndentation(state, pos)} (${i + 1})`, `${indent} (${i + 1})`)
12+
ist(`indent=${getIndentation(state, pos)} (line-numer=${i + 1})`, `indent=${indent} (line-numer=${i + 1})`)
1313
pos += line.length + 1
1414
}
1515
}
@@ -45,4 +45,163 @@ try:
4545
except e:
4646
bar()
4747
`))
48+
49+
it("multi-line-block try-except", check(`
50+
try:
51+
foo()
52+
fooz()
53+
except e:
54+
bar()
55+
barz()
56+
finally:
57+
baz()
58+
bazz()
59+
`))
60+
61+
62+
it("multi-line-nested-block try-except", check(`
63+
try:
64+
foo()
65+
fooz()
66+
try:
67+
inner()
68+
inner2()
69+
except e2:
70+
f3()
71+
f4()
72+
else:
73+
f5()
74+
f6()
75+
finally:
76+
f7()
77+
f8()
78+
except e:
79+
bar()
80+
barz()
81+
finally:
82+
baz()
83+
bazz()
84+
`))
85+
86+
it("match-case", check(`
87+
match x:
88+
case 1:
89+
foo()
90+
case 2:
91+
bar()
92+
case _:
93+
bar()
94+
`))
95+
96+
it("match-case-multi-line-block", check(`
97+
def func():
98+
match x:
99+
case 1:
100+
foo()
101+
fooz()
102+
case 2:
103+
bar()
104+
bar()
105+
bar()
106+
match y:
107+
case 3:
108+
bar()
109+
case 4:
110+
bar()
111+
case _:
112+
bar()
113+
`))
114+
115+
it("class-with-decorators", check(`
116+
@decorator1
117+
@decorator2(
118+
param1,
119+
param2
120+
)
121+
class MyClass:
122+
def method(self):
123+
pass
124+
`))
125+
126+
it("list-comprehension", check(`
127+
result = [
128+
x * y
129+
for x in range(10)
130+
for y in range(5)
131+
if x > y
132+
]
133+
`))
134+
135+
it("multi-line-expressions", check(`
136+
result = (
137+
very_long_variable_name +
138+
another_long_variable *
139+
some_computation(
140+
arg1,
141+
arg2
142+
)
143+
)
144+
`))
145+
146+
it("async-function-and-with", check(`
147+
async def process_data():
148+
async with context() as ctx:
149+
result = await ctx.fetch(
150+
url,
151+
timeout=30
152+
)
153+
return result
154+
`))
155+
156+
it("nested-functions", check(`
157+
def outer():
158+
x = 1
159+
def inner1():
160+
y = 2
161+
def inner2():
162+
z = 3
163+
return x + y + z
164+
return inner2()
165+
return inner1()
166+
`))
167+
168+
it("type-hints-and-annotations", check(`
169+
def process_data(
170+
data: list[str],
171+
config: dict[str, Any]
172+
) -> tuple[int, str]:
173+
result: Optional[str] = None
174+
if data:
175+
result = data[0]
176+
return len(data), result
177+
`))
178+
179+
it("multi-line-dict-comprehension", check(`
180+
config = {
181+
key: value
182+
for key, value in items
183+
if is_valid(
184+
key,
185+
value
186+
)
187+
}
188+
`))
189+
190+
it("multi-line-with-comments", check(`
191+
def process(
192+
x: int, # The input value
193+
y: float # The coefficient
194+
):
195+
# Compute first step
196+
result = x * y
197+
198+
# Apply additional processing
199+
if result > 0:
200+
# Positive case
201+
return result
202+
else:
203+
# Negative case
204+
return -result
205+
`))
206+
48207
})

0 commit comments

Comments
 (0)
0