Skip to content
This repository was archived by the owner on Nov 4, 2024. It is now read-only.

Commit 3ba6511

Browse files
authored
Port regex-based block formatting from vscode-python (microsoft#217)
* port block formatting on colon from vscode-python * port tests * basic check in LanguageServerTests * split apart LanguageServer tests for OnTypeFormatting * string.Empty instead of empty literal
1 parent 3c78ba9 commit 3ba6511

14 files changed

+2203
-9
lines changed
Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
// Python Tools for Visual Studio
2+
// Copyright(c) Microsoft Corporation
3+
// All rights reserved.
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the License); you may not use
6+
// this file except in compliance with the License. You may obtain a copy of the
7+
// License at http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
10+
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
11+
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
12+
// MERCHANTABLITY OR NON-INFRINGEMENT.
13+
//
14+
// See the Apache Version 2.0 License for specific language governing
15+
// permissions and limitations under the License.
16+
17+
using System;
18+
using System.IO;
19+
using System.Threading.Tasks;
20+
using FluentAssertions;
21+
using Microsoft.Python.LanguageServer;
22+
using Microsoft.Python.LanguageServer.Implementation;
23+
using Microsoft.PythonTools.Analysis;
24+
using Microsoft.PythonTools.Analysis.FluentAssertions;
25+
using Microsoft.VisualStudio.TestTools.UnitTesting;
26+
using TestUtilities;
27+
28+
namespace AnalysisTests {
29+
[TestClass]
30+
public class BlockFormatterTests {
31+
public TestContext TestContext { get; set; }
32+
33+
[TestInitialize]
34+
public void TestInitialize() {
35+
TestEnvironmentImpl.TestInitialize($"{TestContext.FullyQualifiedTestClassName}.{TestContext.TestName}");
36+
}
37+
38+
[TestCleanup]
39+
public void TestCleanup() {
40+
TestEnvironmentImpl.TestCleanup();
41+
}
42+
43+
[TestMethod, Priority(0)]
44+
public void NullReader() {
45+
Func<Task<TextEdit[]>> func = () => BlockFormatter.ProvideEdits(null, new Position(), new FormattingOptions());
46+
func.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("reader");
47+
}
48+
49+
[TestMethod, Priority(0)]
50+
public async Task FirstLine() {
51+
using (var reader = new StringReader(string.Empty)) {
52+
var edits = await BlockFormatter.ProvideEdits(reader, new Position { line = 0, character = 4 }, new FormattingOptions());
53+
edits.Should().BeEmpty();
54+
}
55+
}
56+
57+
[TestMethod, Priority(0)]
58+
public void TooShort() {
59+
using (var reader = new StringReader("a + b")) {
60+
Func<Task<TextEdit[]>> func = () => BlockFormatter.ProvideEdits(reader, new Position { line = 1, character = 4 }, new FormattingOptions());
61+
func.Should().Throw<ArgumentException>().And.ParamName.Should().Be("position");
62+
}
63+
}
64+
65+
[TestMethod, Priority(0)]
66+
public async Task NoMatch() {
67+
var code = @"d = {
68+
'a': a,
69+
'b':
70+
";
71+
using (var reader = new StringReader(code)) {
72+
var edits = await BlockFormatter.ProvideEdits(reader, new Position { line = 2, character = 7 }, new FormattingOptions());
73+
edits.Should().BeEmpty();
74+
}
75+
}
76+
77+
[DataRow("elseBlocksFirstLine2.py", 3, 7, true, 2, 0, 2)]
78+
[DataRow("elseBlocksFirstLine4.py", 3, 9, true, 4, 0, 4)]
79+
[DataRow("elseBlocksFirstLineTab.py", 3, 6, false, 4, 0, 1)]
80+
[DataTestMethod, Priority(0)]
81+
public async Task ElseBlock(string filename, int line, int col, bool insertSpaces, int tabSize, int startCharacter, int endCharacter) {
82+
var position = new Position { line = line, character = col };
83+
var options = new FormattingOptions { insertSpaces = insertSpaces, tabSize = tabSize };
84+
85+
var src = TestData.GetPath("TestData", "Formatting", filename);
86+
87+
using (var reader = new StreamReader(src)) {
88+
var edits = await BlockFormatter.ProvideEdits(reader, position, options);
89+
edits.Should().OnlyHaveTextEdit(string.Empty, (line, startCharacter, line, endCharacter));
90+
}
91+
}
92+
93+
[DataRow(6, 22, 0, 2)]
94+
[DataRow(35, 13, 0, 2)]
95+
[DataRow(54, 19, 0, 2)]
96+
[DataRow(76, 9, 0, 2)]
97+
[DataRow(143, 22, 0, 2)]
98+
[DataRow(172, 11, 0, 2)]
99+
[DataRow(195, 12, 0, 2)]
100+
[DataTestMethod, Priority(0)]
101+
public async Task TryBlockTwoSpace(int line, int col, int startCharacter, int endCharacter) {
102+
var position = new Position { line = line, character = col };
103+
var options = new FormattingOptions { insertSpaces = true, tabSize = 2 };
104+
105+
var src = TestData.GetPath("TestData", "Formatting", "tryBlocks2.py");
106+
107+
using (var reader = new StreamReader(src)) {
108+
var edits = await BlockFormatter.ProvideEdits(reader, position, options);
109+
edits.Should().OnlyHaveTextEdit(string.Empty, (line, startCharacter, line, endCharacter));
110+
}
111+
}
112+
113+
[DataRow(15, 21)]
114+
[DataRow(47, 12)]
115+
[DataRow(157, 25)]
116+
[DataTestMethod, Priority(0)]
117+
public async Task TryBlockTwoSpaceNoEdits(int line, int col) {
118+
var position = new Position { line = line, character = col };
119+
var options = new FormattingOptions { insertSpaces = true, tabSize = 2 };
120+
121+
var src = TestData.GetPath("TestData", "Formatting", "tryBlocks2.py");
122+
123+
using (var reader = new StreamReader(src)) {
124+
var edits = await BlockFormatter.ProvideEdits(reader, position, options);
125+
edits.Should().BeEmpty();
126+
}
127+
}
128+
129+
[DataRow(6, 22, 0, 4)]
130+
[DataRow(35, 13, 0, 4)]
131+
[DataRow(54, 19, 0, 4)]
132+
[DataRow(76, 9, 0, 4)]
133+
[DataRow(143, 22, 0, 4)]
134+
[DataRow(172, 11, 0, 4)]
135+
[DataRow(195, 12, 0, 4)]
136+
[DataTestMethod, Priority(0)]
137+
public async Task TryBlockFourSpace(int line, int col, int startCharacter, int endCharacter) {
138+
var position = new Position { line = line, character = col };
139+
var options = new FormattingOptions { insertSpaces = true, tabSize = 4 };
140+
141+
var src = TestData.GetPath("TestData", "Formatting", "tryBlocks4.py");
142+
143+
using (var reader = new StreamReader(src)) {
144+
var edits = await BlockFormatter.ProvideEdits(reader, position, options);
145+
edits.Should().OnlyHaveTextEdit(string.Empty, (line, startCharacter, line, endCharacter));
146+
}
147+
}
148+
149+
[DataRow(15, 21)]
150+
[DataRow(47, 12)]
151+
[DataRow(157, 25)]
152+
[DataTestMethod, Priority(0)]
153+
public async Task TryBlockFourSpaceNoEdits(int line, int col) {
154+
var position = new Position { line = line, character = col };
155+
var options = new FormattingOptions { insertSpaces = true, tabSize = 4 };
156+
157+
var src = TestData.GetPath("TestData", "Formatting", "tryBlocks4.py");
158+
159+
using (var reader = new StreamReader(src)) {
160+
var edits = await BlockFormatter.ProvideEdits(reader, position, options);
161+
edits.Should().BeEmpty();
162+
}
163+
}
164+
165+
[DataRow(6, 22, 0, 2)]
166+
[DataRow(35, 13, 0, 2)]
167+
[DataRow(54, 19, 0, 2)]
168+
[DataRow(76, 9, 0, 2)]
169+
[DataRow(143, 22, 0, 2)]
170+
[DataRow(172, 11, 0, 3)]
171+
[DataRow(195, 12, 0, 2)]
172+
[DataTestMethod, Priority(0)]
173+
public async Task TryBlockTab(int line, int col, int startCharacter, int endCharacter) {
174+
var position = new Position { line = line, character = col };
175+
var options = new FormattingOptions { insertSpaces = false, tabSize = 4 };
176+
177+
var src = TestData.GetPath("TestData", "Formatting", "tryBlocksTab.py");
178+
var newText = new string('\t', endCharacter - 1);
179+
180+
using (var reader = new StreamReader(src)) {
181+
var edits = await BlockFormatter.ProvideEdits(reader, position, options);
182+
edits.Should().OnlyHaveTextEdit(newText, (line, startCharacter, line, endCharacter));
183+
}
184+
}
185+
186+
[DataRow(4, 18, 0, 2)]
187+
[DataRow(7, 18, 0, 2)]
188+
[DataRow(21, 18, 0, 2)]
189+
[DataRow(38, 7, 0, 2)]
190+
[DataRow(47, 13, 0, 2)]
191+
[DataRow(57, 9, 0, 2)]
192+
[DataRow(66, 20, 0, 2)]
193+
[DataRow(69, 20, 0, 2)]
194+
[DataRow(83, 20, 0, 2)]
195+
[DataRow(109, 15, 0, 2)]
196+
[DataRow(119, 11, 0, 2)]
197+
[DataRow(134, 9, 0, 2)]
198+
[DataTestMethod, Priority(0)]
199+
public async Task ElseBlockTwoSpace(int line, int col, int startCharacter, int endCharacter) {
200+
var position = new Position { line = line, character = col };
201+
var options = new FormattingOptions { insertSpaces = true, tabSize = 2 };
202+
203+
var src = TestData.GetPath("TestData", "Formatting", "elseBlocks2.py");
204+
205+
using (var reader = new StreamReader(src)) {
206+
var edits = await BlockFormatter.ProvideEdits(reader, position, options);
207+
edits.Should().OnlyHaveTextEdit(string.Empty, (line, startCharacter, line, endCharacter));
208+
}
209+
}
210+
211+
[DataRow(345, 18)]
212+
[DataRow(359, 18)]
213+
[DataTestMethod, Priority(0)]
214+
public async Task ElseBlockTwoSpaceNoEdits(int line, int col) {
215+
var position = new Position { line = line, character = col };
216+
var options = new FormattingOptions { insertSpaces = true, tabSize = 2 };
217+
218+
var src = TestData.GetPath("TestData", "Formatting", "elseBlocks2.py");
219+
220+
using (var reader = new StreamReader(src)) {
221+
var edits = await BlockFormatter.ProvideEdits(reader, position, options);
222+
edits.Should().BeEmpty();
223+
}
224+
}
225+
226+
[DataRow(4, 18, 0, 4)]
227+
[DataRow(7, 18, 0, 4)]
228+
[DataRow(21, 18, 0, 4)]
229+
[DataRow(38, 7, 0, 4)]
230+
[DataRow(47, 13, 0, 4)]
231+
[DataRow(57, 9, 0, 4)]
232+
[DataRow(66, 20, 0, 4)]
233+
[DataRow(69, 20, 0, 4)]
234+
[DataRow(83, 20, 0, 4)]
235+
[DataRow(109, 15, 0, 4)]
236+
[DataRow(119, 11, 0, 4)]
237+
[DataRow(134, 9, 0, 4)]
238+
[DataTestMethod, Priority(0)]
239+
public async Task ElseBlockFourSpace(int line, int col, int startCharacter, int endCharacter) {
240+
var position = new Position { line = line, character = col };
241+
var options = new FormattingOptions { insertSpaces = true, tabSize = 4 };
242+
243+
var src = TestData.GetPath("TestData", "Formatting", "elseBlocks4.py");
244+
245+
using (var reader = new StreamReader(src)) {
246+
var edits = await BlockFormatter.ProvideEdits(reader, position, options);
247+
edits.Should().OnlyHaveTextEdit(string.Empty, (line, startCharacter, line, endCharacter));
248+
}
249+
}
250+
251+
[DataRow(345, 18)]
252+
[DataTestMethod, Priority(0)]
253+
public async Task ElseBlockFourSpaceNoEdits(int line, int col) {
254+
var position = new Position { line = line, character = col };
255+
var options = new FormattingOptions { insertSpaces = true, tabSize = 4 };
256+
257+
var src = TestData.GetPath("TestData", "Formatting", "elseBlocks4.py");
258+
259+
using (var reader = new StreamReader(src)) {
260+
var edits = await BlockFormatter.ProvideEdits(reader, position, options);
261+
edits.Should().BeEmpty();
262+
}
263+
}
264+
265+
[DataRow(4, 18, 0, 1)]
266+
[DataRow(7, 18, 0, 1)]
267+
[DataRow(21, 18, 0, 1)]
268+
[DataRow(38, 7, 0, 1)]
269+
[DataRow(47, 13, 0, 1)]
270+
[DataRow(57, 9, 0, 1)]
271+
[DataRow(66, 20, 0, 1)]
272+
[DataRow(69, 20, 0, 1)]
273+
[DataRow(83, 20, 0, 1)]
274+
[DataRow(109, 15, 0, 1)]
275+
[DataRow(119, 11, 0, 1)]
276+
[DataRow(134, 9, 0, 1)]
277+
[DataTestMethod, Priority(0)]
278+
public async Task ElseBlockTab(int line, int col, int startCharacter, int endCharacter) {
279+
var position = new Position { line = line, character = col };
280+
var options = new FormattingOptions { insertSpaces = true, tabSize = 2 };
281+
282+
var src = TestData.GetPath("TestData", "Formatting", "elseBlocksTab.py");
283+
284+
using (var reader = new StreamReader(src)) {
285+
var edits = await BlockFormatter.ProvideEdits(reader, position, options);
286+
edits.Should().OnlyHaveTextEdit(string.Empty, (line, startCharacter, line, endCharacter));
287+
}
288+
}
289+
290+
[DataRow(345, 18)]
291+
[DataTestMethod, Priority(0)]
292+
public async Task ElseBlockTabNoEdits(int line, int col) {
293+
var position = new Position { line = line, character = col };
294+
var options = new FormattingOptions { insertSpaces = true, tabSize = 2 };
295+
296+
var src = TestData.GetPath("TestData", "Formatting", "elseBlocksTab.py");
297+
298+
using (var reader = new StreamReader(src)) {
299+
var edits = await BlockFormatter.ProvideEdits(reader, position, options);
300+
edits.Should().BeEmpty();
301+
}
302+
}
303+
}
304+
}

src/Analysis/Engine/Test/LanguageServerTests.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -991,7 +991,7 @@ public async Task ParseAndAnalysisDiagnostics() {
991991
}
992992

993993
[TestMethod, Priority(0)]
994-
public async Task OnTypeFormatting() {
994+
public async Task OnTypeFormattingLine() {
995995
using (var s = await CreateServer()) {
996996
var uri = await AddModule(s, "def foo ( ) :\n x = a + b\n x+= 1");
997997

@@ -1008,6 +1008,16 @@ public async Task OnTypeFormatting() {
10081008
}
10091009
}
10101010

1011+
[TestMethod, Priority(0)]
1012+
public async Task OnTypeFormattingBlock() {
1013+
using (var s = await CreateServer()) {
1014+
var uri = await AddModule(s, "if x:\n pass\n else:");
1015+
1016+
var edits = await s.SendDocumentOnTypeFormatting(uri, new SourceLocation(3, 9), ":");
1017+
edits.Should().OnlyHaveTextEdit("", (2, 0, 2, 4));
1018+
}
1019+
}
1020+
10111021
class GetAllExtensionProvider : ILanguageServerExtensionProvider {
10121022
public Task<ILanguageServerExtension> CreateAsync(IPythonLanguageServer server, IReadOnlyDictionary<string, object> properties, CancellationToken cancellationToken) {
10131023
return Task.FromResult<ILanguageServerExtension>(new GetAllExtension((Server)server, properties));

0 commit comments

Comments
 (0)