From 12d5749e0ab5e47227cf5d32d9c3eb19f12babbb Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Mon, 8 Oct 2018 15:39:33 -0700
Subject: [PATCH 1/5] port block formatting on colon from vscode-python
---
.../Impl/Implementation/BlockFormatter.cs | 144 ++++++++++++++++++
.../Implementation/Server.OnTypeFormatting.cs | 13 +-
.../Impl/Implementation/Server.cs | 8 +-
3 files changed, 157 insertions(+), 8 deletions(-)
create mode 100644 src/LanguageServer/Impl/Implementation/BlockFormatter.cs
diff --git a/src/LanguageServer/Impl/Implementation/BlockFormatter.cs b/src/LanguageServer/Impl/Implementation/BlockFormatter.cs
new file mode 100644
index 000000000..a47f1bf92
--- /dev/null
+++ b/src/LanguageServer/Impl/Implementation/BlockFormatter.cs
@@ -0,0 +1,144 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using Microsoft.PythonTools.Analysis;
+using Microsoft.PythonTools.Analysis.Infrastructure;
+
+namespace Microsoft.Python.LanguageServer.Implementation {
+ ///
+ /// Port of BlockFormatProviders in vscode-python.
+ ///
+ internal class BlockFormatter {
+ private static readonly Regex IfRegex = CompileRegex(@"^( |\t)*if +.*: *$");
+ private static readonly Regex ElIfRegex = CompileRegex(@"^( |\t)*elif +.*: *$");
+ private static readonly Regex ElseRegex = CompileRegex(@"^( |\t)*else *: *$");
+ private static readonly Regex ForInRegex = CompileRegex(@"^( |\t)*for \w in .*: *$");
+ private static readonly Regex AsyncForInRegex = CompileRegex(@"^( |\t)*async *for \w in .*: *$");
+ private static readonly Regex WhileRegex = CompileRegex(@"^( |\t)*while .*: *$");
+ private static readonly Regex TryRegex = CompileRegex(@"^( |\t)*try *: *$");
+ private static readonly Regex FinallyRegex = CompileRegex(@"^( |\t)*finally *: *$");
+ private static readonly Regex ExceptRegex = CompileRegex(@"^( |\t)*except *\w* *(as)? *\w* *: *$");
+ private static readonly Regex DefRegex = CompileRegex(@"^( |\t)*def \w *\(.*$");
+ private static readonly Regex AsyncDefRegex = CompileRegex(@"^( |\t)*async *def \w *\(.*$");
+ private static readonly Regex ClassRegex = CompileRegex(@"^( |\t)*class *\w* *.*: *$");
+
+ private static readonly IEnumerable BoundaryBlocks = new[] { DefRegex, AsyncDefRegex, ClassRegex };
+
+ private static readonly IEnumerable Formatters = new[] {
+ new BlockFormatter(ElseRegex, new[] { IfRegex, ElIfRegex, ForInRegex, AsyncForInRegex, WhileRegex, TryRegex, ExceptRegex }),
+ new BlockFormatter(ElIfRegex, new[] { IfRegex, ElIfRegex }),
+ new BlockFormatter(ExceptRegex, new[] { TryRegex, ExceptRegex }),
+ new BlockFormatter(ExceptRegex, new[] { TryRegex, ExceptRegex }),
+ new BlockFormatter(FinallyRegex, new[] { TryRegex, ExceptRegex }),
+ };
+
+ public static async Task ProvideEdits(TextReader reader, Position position, FormattingOptions options) {
+ Check.ArgumentOutOfRange(nameof(position), () => position.line < 0);
+
+ if (position.line == 0) {
+ return Array.Empty();
+ }
+
+ var lines = new List(position.line + 1);
+
+ for (var i = 0; i <= position.line; i++) {
+ var curr = await reader.ReadLineAsync();
+
+ if (curr == null) {
+ throw new ArgumentException($"reached end of file before {nameof(position)}", nameof(position));
+ }
+
+ lines.Add(curr);
+ }
+
+ var line = lines[position.line];
+
+ var formatter = Formatters.FirstOrDefault(f => f.CanProvideEdits(line));
+ if (formatter == null) {
+ return Array.Empty();
+ }
+
+ return formatter.ProvideEdits(lines, position, options);
+ }
+
+ private readonly Regex _blockRegexp;
+ private readonly IEnumerable _previousBlockRegexps;
+
+ private BlockFormatter(Regex blockRegexp, IEnumerable previousBlockRegexps) {
+ Check.ArgumentNull(nameof(blockRegexp), blockRegexp);
+ Check.Argument(nameof(previousBlockRegexps), () => !previousBlockRegexps.IsNullOrEmpty());
+
+ _blockRegexp = blockRegexp;
+ _previousBlockRegexps = previousBlockRegexps;
+ }
+
+ private bool CanProvideEdits(string line) => _blockRegexp.IsMatch(line);
+
+ private TextEdit[] ProvideEdits(IList lines, Position position, FormattingOptions options) {
+ var line = lines[position.line];
+ var lineFirstNonWhitespace = FirstNonWhitespaceCharacterIndex(line);
+
+ // We can have else for the following blocks:
+ // if:
+ // elif x:
+ // for x in y:
+ // while x:
+
+ // We need to find a block statement that is less than or equal to this statement block (but not greater)
+ for (var lineNum = position.line - 1; lineNum >= 0; lineNum--) {
+ var prevLine = lines[lineNum];
+
+ // Oops, we've reached a boundary (like the function or class definition)
+ // Get out of here
+ if (BoundaryBlocks.Any(regexp => regexp.IsMatch(prevLine))) {
+ return Array.Empty();
+ }
+
+ var blockRegex = _previousBlockRegexps.FirstOrDefault(regex => regex.IsMatch(prevLine));
+ if (blockRegex == null) {
+ continue;
+ }
+
+ var startOfBlockInLine = FirstNonWhitespaceCharacterIndex(prevLine);
+ if (startOfBlockInLine > lineFirstNonWhitespace) {
+ continue;
+ }
+
+ var startPosition = new Position { line = position.line, character = 0 };
+ var endPosition = new Position { line = position.line, character = lineFirstNonWhitespace - startOfBlockInLine };
+
+ if (endPosition.character == 0) {
+ // current block cannot be at the same level as a preivous block
+ continue;
+ }
+
+ if (options.insertSpaces) {
+ return new[] { new TextEdit {
+ range = new Range {start = startPosition, end = endPosition }
+ } };
+ }
+
+ var prefixOfPreviousBlock = prevLine.Substring(0, startOfBlockInLine);
+
+ return new[] {
+ new TextEdit {
+ range = new Range {
+ start = startPosition,
+ end = new Position {line = position.line, character = lineFirstNonWhitespace }
+ },
+ newText = prefixOfPreviousBlock
+ }
+ };
+ }
+
+ return Array.Empty();
+ }
+
+ private int FirstNonWhitespaceCharacterIndex(string s) => s.TakeWhile(c => char.IsWhiteSpace(c)).Count();
+
+ private static Regex CompileRegex(string pattern) => new Regex(pattern, RegexOptions.Compiled);
+ }
+}
diff --git a/src/LanguageServer/Impl/Implementation/Server.OnTypeFormatting.cs b/src/LanguageServer/Impl/Implementation/Server.OnTypeFormatting.cs
index 046596b21..f8de29250 100644
--- a/src/LanguageServer/Impl/Implementation/Server.OnTypeFormatting.cs
+++ b/src/LanguageServer/Impl/Implementation/Server.OnTypeFormatting.cs
@@ -21,14 +21,15 @@
namespace Microsoft.Python.LanguageServer.Implementation {
public sealed partial class Server {
- public override Task DocumentOnTypeFormatting(DocumentOnTypeFormattingParams @params, CancellationToken cancellationToken) {
- int targetLine; // One-indexed line number
+ public override async Task DocumentOnTypeFormatting(DocumentOnTypeFormattingParams @params, CancellationToken cancellationToken) {
+ int targetLine;
switch (@params.ch) {
case "\n":
targetLine = @params.position.line - 1;
break;
case ";":
+ case ":":
targetLine = @params.position.line;
break;
default:
@@ -38,13 +39,17 @@ public override Task DocumentOnTypeFormatting(DocumentOnTypeFormatti
var uri = @params.textDocument.uri;
if (!(ProjectFiles.GetEntry(uri) is IDocument doc)) {
- return Task.FromResult(Array.Empty());
+ return Array.Empty();
}
var part = ProjectFiles.GetPart(uri);
using (var reader = doc.ReadDocument(part, out _)) {
+ if (@params.ch == ":") {
+ return await BlockFormatter.ProvideEdits(reader, @params.position, @params.options);
+ }
+
var lineFormatter = new LineFormatter(reader, Analyzer.LanguageVersion);
- return Task.FromResult(lineFormatter.FormatLine(targetLine));
+ return lineFormatter.FormatLine(targetLine);
}
}
}
diff --git a/src/LanguageServer/Impl/Implementation/Server.cs b/src/LanguageServer/Impl/Implementation/Server.cs
index 10940c63e..9c88428dd 100644
--- a/src/LanguageServer/Impl/Implementation/Server.cs
+++ b/src/LanguageServer/Impl/Implementation/Server.cs
@@ -148,10 +148,10 @@ public void TraceMessage(IFormattable message) {
workspaceSymbolProvider = true,
documentSymbolProvider = true,
renameProvider = true,
- //documentOnTypeFormattingProvider = new DocumentOnTypeFormattingOptions {
- // firstTriggerCharacter = "\n",
- // moreTriggerCharacter = new[] { ";" }
- //},
+ documentOnTypeFormattingProvider = new DocumentOnTypeFormattingOptions {
+ firstTriggerCharacter = "\n",
+ moreTriggerCharacter = new[] { ";", ":" }
+ },
}
};
From 3741fd82cbab513699de80387ddc73d5779548a7 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Tue, 9 Oct 2018 11:32:41 -0700
Subject: [PATCH 2/5] port tests
---
.../Engine/Test/BlockFormatterTests.cs | 305 +++++++++++++++
.../Impl/Implementation/BlockFormatter.cs | 38 +-
.../TestData/Formatting/elseBlocks2.py | 365 ++++++++++++++++++
.../TestData/Formatting/elseBlocks4.py | 351 +++++++++++++++++
.../Formatting/elseBlocksFirstLine2.py | 4 +
.../Formatting/elseBlocksFirstLine4.py | 4 +
.../Formatting/elseBlocksFirstLineTab.py | 4 +
.../TestData/Formatting/elseBlocksTab.py | 351 +++++++++++++++++
.../TestData/Formatting/tryBlocks2.py | 208 ++++++++++
.../TestData/Formatting/tryBlocks4.py | 208 ++++++++++
.../TestData/Formatting/tryBlocksTab.py | 208 ++++++++++
11 files changed, 2041 insertions(+), 5 deletions(-)
create mode 100644 src/Analysis/Engine/Test/BlockFormatterTests.cs
create mode 100644 src/UnitTests/TestData/Formatting/elseBlocks2.py
create mode 100644 src/UnitTests/TestData/Formatting/elseBlocks4.py
create mode 100644 src/UnitTests/TestData/Formatting/elseBlocksFirstLine2.py
create mode 100644 src/UnitTests/TestData/Formatting/elseBlocksFirstLine4.py
create mode 100644 src/UnitTests/TestData/Formatting/elseBlocksFirstLineTab.py
create mode 100644 src/UnitTests/TestData/Formatting/elseBlocksTab.py
create mode 100644 src/UnitTests/TestData/Formatting/tryBlocks2.py
create mode 100644 src/UnitTests/TestData/Formatting/tryBlocks4.py
create mode 100644 src/UnitTests/TestData/Formatting/tryBlocksTab.py
diff --git a/src/Analysis/Engine/Test/BlockFormatterTests.cs b/src/Analysis/Engine/Test/BlockFormatterTests.cs
new file mode 100644
index 000000000..e2a4e54b0
--- /dev/null
+++ b/src/Analysis/Engine/Test/BlockFormatterTests.cs
@@ -0,0 +1,305 @@
+// Python Tools for Visual Studio
+// Copyright(c) Microsoft Corporation
+// All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the License); you may not use
+// this file except in compliance with the License. You may obtain a copy of the
+// License at http://www.apache.org/licenses/LICENSE-2.0
+//
+// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
+// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
+// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+// MERCHANTABLITY OR NON-INFRINGEMENT.
+//
+// See the Apache Version 2.0 License for specific language governing
+// permissions and limitations under the License.
+
+using System;
+using System.IO;
+using System.Threading.Tasks;
+using FluentAssertions;
+using Microsoft.Python.LanguageServer;
+using Microsoft.Python.LanguageServer.Implementation;
+using Microsoft.PythonTools.Analysis;
+using Microsoft.PythonTools.Analysis.FluentAssertions;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using TestUtilities;
+
+namespace AnalysisTests {
+ [TestClass]
+ public class BlockFormatterTests {
+ public TestContext TestContext { get; set; }
+
+ [TestInitialize]
+ public void TestInitialize() {
+ TestEnvironmentImpl.TestInitialize($"{TestContext.FullyQualifiedTestClassName}.{TestContext.TestName}");
+ }
+
+ [TestCleanup]
+ public void TestCleanup() {
+ TestEnvironmentImpl.TestCleanup();
+ }
+
+ [TestMethod, Priority(0)]
+ public void NullReader() {
+ Func> func = () => BlockFormatter.ProvideEdits(null, new Position(), new FormattingOptions());
+ func.Should().Throw().And.ParamName.Should().Be("reader");
+ }
+
+ [TestMethod, Priority(0)]
+ public async Task FirstLine() {
+ using (var reader = new StringReader("")) {
+ var edits = await BlockFormatter.ProvideEdits(reader, new Position { line = 0, character = 4 }, new FormattingOptions());
+ edits.Should().BeEmpty();
+ }
+ }
+
+ [TestMethod, Priority(0)]
+ public void TooShort() {
+ using (var reader = new StringReader("a + b")) {
+ Func> func = () => BlockFormatter.ProvideEdits(reader, new Position { line = 1, character = 4 }, new FormattingOptions());
+ func.Should().Throw().And.ParamName.Should().Be("position");
+ }
+ }
+
+ [TestMethod, Priority(0)]
+ public async Task NoMatch() {
+ var code = @"d = {
+ 'a': a,
+ 'b':
+";
+ using (var reader = new StringReader(code)) {
+ var edits = await BlockFormatter.ProvideEdits(reader, new Position { line = 2, character = 7 }, new FormattingOptions());
+ edits.Should().BeEmpty();
+ }
+ }
+
+
+ [DataRow("elseBlocksFirstLine2.py", 3, 7, true, 2, 0, 2)]
+ [DataRow("elseBlocksFirstLine4.py", 3, 9, true, 4, 0, 4)]
+ [DataRow("elseBlocksFirstLineTab.py", 3, 6, false, 4, 0, 1)]
+ [DataTestMethod, Priority(0)]
+ public async Task ElseBlock(string filename, int line, int col, bool insertSpaces, int tabSize, int startCharacter, int endCharacter) {
+ var position = new Position { line = line, character = col };
+ var options = new FormattingOptions { insertSpaces = insertSpaces, tabSize = tabSize };
+
+ var src = TestData.GetPath("TestData", "Formatting", filename);
+
+ using (var reader = new StreamReader(src)) {
+ var edits = await BlockFormatter.ProvideEdits(reader, position, options);
+ edits.Should().OnlyHaveTextEdit("", (line, startCharacter, line, endCharacter));
+ }
+ }
+
+ [DataRow(6, 22, 0, 2)]
+ [DataRow(35, 13, 0, 2)]
+ [DataRow(54, 19, 0, 2)]
+ [DataRow(76, 9, 0, 2)]
+ [DataRow(143, 22, 0, 2)]
+ [DataRow(172, 11, 0, 2)]
+ [DataRow(195, 12, 0, 2)]
+ [DataTestMethod, Priority(0)]
+ public async Task TryBlockTwoSpace(int line, int col, int startCharacter, int endCharacter) {
+ var position = new Position { line = line, character = col };
+ var options = new FormattingOptions { insertSpaces = true, tabSize = 2 };
+
+ var src = TestData.GetPath("TestData", "Formatting", "tryBlocks2.py");
+
+ using (var reader = new StreamReader(src)) {
+ var edits = await BlockFormatter.ProvideEdits(reader, position, options);
+ edits.Should().OnlyHaveTextEdit("", (line, startCharacter, line, endCharacter));
+ }
+ }
+
+ [DataRow(15, 21)]
+ [DataRow(47, 12)]
+ [DataRow(157, 25)]
+ [DataTestMethod, Priority(0)]
+ public async Task TryBlockTwoSpaceNoEdits(int line, int col) {
+ var position = new Position { line = line, character = col };
+ var options = new FormattingOptions { insertSpaces = true, tabSize = 2 };
+
+ var src = TestData.GetPath("TestData", "Formatting", "tryBlocks2.py");
+
+ using (var reader = new StreamReader(src)) {
+ var edits = await BlockFormatter.ProvideEdits(reader, position, options);
+ edits.Should().BeEmpty();
+ }
+ }
+
+ [DataRow(6, 22, 0, 4)]
+ [DataRow(35, 13, 0, 4)]
+ [DataRow(54, 19, 0, 4)]
+ [DataRow(76, 9, 0, 4)]
+ [DataRow(143, 22, 0, 4)]
+ [DataRow(172, 11, 0, 4)]
+ [DataRow(195, 12, 0, 4)]
+ [DataTestMethod, Priority(0)]
+ public async Task TryBlockFourSpace(int line, int col, int startCharacter, int endCharacter) {
+ var position = new Position { line = line, character = col };
+ var options = new FormattingOptions { insertSpaces = true, tabSize = 4 };
+
+ var src = TestData.GetPath("TestData", "Formatting", "tryBlocks4.py");
+
+ using (var reader = new StreamReader(src)) {
+ var edits = await BlockFormatter.ProvideEdits(reader, position, options);
+ edits.Should().OnlyHaveTextEdit("", (line, startCharacter, line, endCharacter));
+ }
+ }
+
+ [DataRow(15, 21)]
+ [DataRow(47, 12)]
+ [DataRow(157, 25)]
+ [DataTestMethod, Priority(0)]
+ public async Task TryBlockFourSpaceNoEdits(int line, int col) {
+ var position = new Position { line = line, character = col };
+ var options = new FormattingOptions { insertSpaces = true, tabSize = 4 };
+
+ var src = TestData.GetPath("TestData", "Formatting", "tryBlocks4.py");
+
+ using (var reader = new StreamReader(src)) {
+ var edits = await BlockFormatter.ProvideEdits(reader, position, options);
+ edits.Should().BeEmpty();
+ }
+ }
+
+ [DataRow(6, 22, 0, 2)]
+ [DataRow(35, 13, 0, 2)]
+ [DataRow(54, 19, 0, 2)]
+ [DataRow(76, 9, 0, 2)]
+ [DataRow(143, 22, 0, 2)]
+ [DataRow(172, 11, 0, 3)]
+ [DataRow(195, 12, 0, 2)]
+ [DataTestMethod, Priority(0)]
+ public async Task TryBlockTab(int line, int col, int startCharacter, int endCharacter) {
+ var position = new Position { line = line, character = col };
+ var options = new FormattingOptions { insertSpaces = false, tabSize = 4 };
+
+ var src = TestData.GetPath("TestData", "Formatting", "tryBlocksTab.py");
+ var newText = new string('\t', endCharacter - 1);
+
+ using (var reader = new StreamReader(src)) {
+ var edits = await BlockFormatter.ProvideEdits(reader, position, options);
+ edits.Should().OnlyHaveTextEdit(newText, (line, startCharacter, line, endCharacter));
+ }
+ }
+
+ [DataRow(4, 18, 0, 2)]
+ [DataRow(7, 18, 0, 2)]
+ [DataRow(21, 18, 0, 2)]
+ [DataRow(38, 7, 0, 2)]
+ [DataRow(47, 13, 0, 2)]
+ [DataRow(57, 9, 0, 2)]
+ [DataRow(66, 20, 0, 2)]
+ [DataRow(69, 20, 0, 2)]
+ [DataRow(83, 20, 0, 2)]
+ [DataRow(109, 15, 0, 2)]
+ [DataRow(119, 11, 0, 2)]
+ [DataRow(134, 9, 0, 2)]
+ [DataTestMethod, Priority(0)]
+ public async Task ElseBlockTwoSpace(int line, int col, int startCharacter, int endCharacter) {
+ var position = new Position { line = line, character = col };
+ var options = new FormattingOptions { insertSpaces = true, tabSize = 2 };
+
+ var src = TestData.GetPath("TestData", "Formatting", "elseBlocks2.py");
+
+ using (var reader = new StreamReader(src)) {
+ var edits = await BlockFormatter.ProvideEdits(reader, position, options);
+ edits.Should().OnlyHaveTextEdit("", (line, startCharacter, line, endCharacter));
+ }
+ }
+
+ [DataRow(345, 18)]
+ [DataRow(359, 18)]
+ [DataTestMethod, Priority(0)]
+ public async Task ElseBlockTwoSpaceNoEdits(int line, int col) {
+ var position = new Position { line = line, character = col };
+ var options = new FormattingOptions { insertSpaces = true, tabSize = 2 };
+
+ var src = TestData.GetPath("TestData", "Formatting", "elseBlocks2.py");
+
+ using (var reader = new StreamReader(src)) {
+ var edits = await BlockFormatter.ProvideEdits(reader, position, options);
+ edits.Should().BeEmpty();
+ }
+ }
+
+ [DataRow(4, 18, 0, 4)]
+ [DataRow(7, 18, 0, 4)]
+ [DataRow(21, 18, 0, 4)]
+ [DataRow(38, 7, 0, 4)]
+ [DataRow(47, 13, 0, 4)]
+ [DataRow(57, 9, 0, 4)]
+ [DataRow(66, 20, 0, 4)]
+ [DataRow(69, 20, 0, 4)]
+ [DataRow(83, 20, 0, 4)]
+ [DataRow(109, 15, 0, 4)]
+ [DataRow(119, 11, 0, 4)]
+ [DataRow(134, 9, 0, 4)]
+ [DataTestMethod, Priority(0)]
+ public async Task ElseBlockFourSpace(int line, int col, int startCharacter, int endCharacter) {
+ var position = new Position { line = line, character = col };
+ var options = new FormattingOptions { insertSpaces = true, tabSize = 4 };
+
+ var src = TestData.GetPath("TestData", "Formatting", "elseBlocks4.py");
+
+ using (var reader = new StreamReader(src)) {
+ var edits = await BlockFormatter.ProvideEdits(reader, position, options);
+ edits.Should().OnlyHaveTextEdit("", (line, startCharacter, line, endCharacter));
+ }
+ }
+
+ [DataRow(345, 18)]
+ [DataTestMethod, Priority(0)]
+ public async Task ElseBlockFourSpaceNoEdits(int line, int col) {
+ var position = new Position { line = line, character = col };
+ var options = new FormattingOptions { insertSpaces = true, tabSize = 4 };
+
+ var src = TestData.GetPath("TestData", "Formatting", "elseBlocks4.py");
+
+ using (var reader = new StreamReader(src)) {
+ var edits = await BlockFormatter.ProvideEdits(reader, position, options);
+ edits.Should().BeEmpty();
+ }
+ }
+
+ [DataRow(4, 18, 0, 1)]
+ [DataRow(7, 18, 0, 1)]
+ [DataRow(21, 18, 0, 1)]
+ [DataRow(38, 7, 0, 1)]
+ [DataRow(47, 13, 0, 1)]
+ [DataRow(57, 9, 0, 1)]
+ [DataRow(66, 20, 0, 1)]
+ [DataRow(69, 20, 0, 1)]
+ [DataRow(83, 20, 0, 1)]
+ [DataRow(109, 15, 0, 1)]
+ [DataRow(119, 11, 0, 1)]
+ [DataRow(134, 9, 0, 1)]
+ [DataTestMethod, Priority(0)]
+ public async Task ElseBlockTab(int line, int col, int startCharacter, int endCharacter) {
+ var position = new Position { line = line, character = col };
+ var options = new FormattingOptions { insertSpaces = true, tabSize = 2 };
+
+ var src = TestData.GetPath("TestData", "Formatting", "elseBlocksTab.py");
+
+ using (var reader = new StreamReader(src)) {
+ var edits = await BlockFormatter.ProvideEdits(reader, position, options);
+ edits.Should().OnlyHaveTextEdit("", (line, startCharacter, line, endCharacter));
+ }
+ }
+
+ [DataRow(345, 18)]
+ [DataTestMethod, Priority(0)]
+ public async Task ElseBlockTabNoEdits(int line, int col) {
+ var position = new Position { line = line, character = col };
+ var options = new FormattingOptions { insertSpaces = true, tabSize = 2 };
+
+ var src = TestData.GetPath("TestData", "Formatting", "elseBlocksTab.py");
+
+ using (var reader = new StreamReader(src)) {
+ var edits = await BlockFormatter.ProvideEdits(reader, position, options);
+ edits.Should().BeEmpty();
+ }
+ }
+ }
+}
diff --git a/src/LanguageServer/Impl/Implementation/BlockFormatter.cs b/src/LanguageServer/Impl/Implementation/BlockFormatter.cs
index a47f1bf92..b03cfeda3 100644
--- a/src/LanguageServer/Impl/Implementation/BlockFormatter.cs
+++ b/src/LanguageServer/Impl/Implementation/BlockFormatter.cs
@@ -1,4 +1,20 @@
-using System;
+// Python Tools for Visual Studio
+// Copyright(c) Microsoft Corporation
+// All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the License); you may not use
+// this file except in compliance with the License. You may obtain a copy of the
+// License at http://www.apache.org/licenses/LICENSE-2.0
+//
+// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
+// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
+// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+// MERCHANTABLITY OR NON-INFRINGEMENT.
+//
+// See the Apache Version 2.0 License for specific language governing
+// permissions and limitations under the License.
+
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -36,6 +52,7 @@ internal class BlockFormatter {
};
public static async Task ProvideEdits(TextReader reader, Position position, FormattingOptions options) {
+ Check.ArgumentNull(nameof(reader), reader);
Check.ArgumentOutOfRange(nameof(position), () => position.line < 0);
if (position.line == 0) {
@@ -55,6 +72,13 @@ public static async Task ProvideEdits(TextReader reader, Position po
}
var line = lines[position.line];
+ var prevLine = lines[position.line - 1];
+
+ // We're only interested in cases where the current block is at the same indentation level as the previous line
+ // E.g. if we have an if..else block, generally the else statement would be at the same level as the code in the if...
+ if (FirstNonWhitespaceCharacterIndex(line) != FirstNonWhitespaceCharacterIndex(prevLine)) {
+ return Array.Empty();
+ }
var formatter = Formatters.FirstOrDefault(f => f.CanProvideEdits(line));
if (formatter == null) {
@@ -116,11 +140,15 @@ private TextEdit[] ProvideEdits(IList lines, Position position, Formatti
}
if (options.insertSpaces) {
- return new[] { new TextEdit {
- range = new Range {start = startPosition, end = endPosition }
- } };
+ return new[] {
+ new TextEdit {
+ range = new Range {start = startPosition, end = endPosition },
+ newText = string.Empty
+ }
+ };
}
+ // Delete everything before the block and insert the same characters we have in the previous block
var prefixOfPreviousBlock = prevLine.Substring(0, startOfBlockInLine);
return new[] {
@@ -137,7 +165,7 @@ private TextEdit[] ProvideEdits(IList lines, Position position, Formatti
return Array.Empty();
}
- private int FirstNonWhitespaceCharacterIndex(string s) => s.TakeWhile(c => char.IsWhiteSpace(c)).Count();
+ private static int FirstNonWhitespaceCharacterIndex(string s) => s.TakeWhile(char.IsWhiteSpace).Count();
private static Regex CompileRegex(string pattern) => new Regex(pattern, RegexOptions.Compiled);
}
diff --git a/src/UnitTests/TestData/Formatting/elseBlocks2.py b/src/UnitTests/TestData/Formatting/elseBlocks2.py
new file mode 100644
index 000000000..da4614982
--- /dev/null
+++ b/src/UnitTests/TestData/Formatting/elseBlocks2.py
@@ -0,0 +1,365 @@
+var = 100
+if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+else:
+ print "4 - Got a false expression value"
+ print var
+
+var = 100
+if var == 200:
+ print "1 - Got a true expression value"
+ print var
+elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+else:
+ print "4 - Got a false expression value"
+ print var
+
+var = 100
+if var == 200:
+ print "1 - Got a true expression value"
+ print var
+elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+ else:
+ print "4 - Got a false expression value"
+ print var
+
+ for n in range(2, 10):
+ for x in range(2, n):
+ if n % x == 0:
+ print n, 'equals', x, '*', n/x
+ break
+ else:
+ # loop fell through without finding a factor
+ print n, 'is a prime number'
+
+for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #except should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+def test():
+ var = 100
+ if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+ else:
+ print "4 - Got a false expression value"
+ print var
+
+ var = 100
+ if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+ else:
+ print "4 - Got a false expression value"
+ print var
+
+ var = 100
+ if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+ else:
+ print "4 - Got a false expression value"
+ print var
+
+ for n in range(2, 10):
+ for x in range(2, n):
+ if n % x == 0:
+ print n, 'equals', x, '*', n/x
+ break
+ else:
+ # loop fell through without finding a factor
+ print n, 'is a prime number'
+
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #except should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
+ while True:
+ ok = raw_input(prompt)
+ if ok in ('y', 'ye', 'yes'):
+ return True
+ if ok in ('n', 'no', 'nop', 'nope'):
+ return False
+ retries = retries - 1
+ if retries < 0:
+ raise IOError('refusenik user')
+ print complaint
+ else:
+ pass
+
+for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #else should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+def minus():
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #except should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #else should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+def two():
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ # else should be in same level as except
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ # else should be in same level as except
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+def divide(x, y):
+ try:
+ result = x / y
+ except ZeroDivisionError:
+ print("division by zero!")
+ else:
+ print("result is", result)
+ finally:
+ print("executing finally clause")
+
+class DoSomething():
+ def test():
+ var = 100
+ if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+ else:
+ print "4 - Got a false expression value"
+ print var
+
+ var = 100
+ if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+ else:
+ print "4 - Got a false expression value"
+ print var
+
+ var = 100
+ if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+ else:
+ print "4 - Got a false expression value"
+ print var
+
+ for n in range(2, 10):
+ for x in range(2, n):
+ if n % x == 0:
+ print n, 'equals', x, '*', n/x
+ break
+ else:
+ # loop fell through without finding a factor
+ print n, 'is a prime number'
+
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #except should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+ def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
+ while True:
+ ok = raw_input(prompt)
+ if ok in ('y', 'ye', 'yes'):
+ return True
+ if ok in ('n', 'no', 'nop', 'nope'):
+ return False
+ retries = retries - 1
+ if retries < 0:
+ raise IOError('refusenik user')
+ print complaint
+ else:
+ pass
+
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #else should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+ def minus():
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #except should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #else should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+ def two():
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ # else should be in same level as except
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ # else should be in same level as except
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+ def divide(x, y):
+ try:
+ result = x / y
+ except ZeroDivisionError:
+ print("division by zero!")
+ else:
+ print("result is", result)
+ finally:
+ print("executing finally clause")
+
+var = 100
+if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+else:
+ print "4 - Got a false expression value"
+ print var
+
+var = 100
+if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ if var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+else:
+ print "4 - Got a false expression value"
+ print var
diff --git a/src/UnitTests/TestData/Formatting/elseBlocks4.py b/src/UnitTests/TestData/Formatting/elseBlocks4.py
new file mode 100644
index 000000000..c8213d6c4
--- /dev/null
+++ b/src/UnitTests/TestData/Formatting/elseBlocks4.py
@@ -0,0 +1,351 @@
+var = 100
+if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+else:
+ print "4 - Got a false expression value"
+ print var
+
+var = 100
+if var == 200:
+ print "1 - Got a true expression value"
+ print var
+elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+else:
+ print "4 - Got a false expression value"
+ print var
+
+var = 100
+if var == 200:
+ print "1 - Got a true expression value"
+ print var
+elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+ else:
+ print "4 - Got a false expression value"
+ print var
+
+ for n in range(2, 10):
+ for x in range(2, n):
+ if n % x == 0:
+ print n, 'equals', x, '*', n/x
+ break
+ else:
+ # loop fell through without finding a factor
+ print n, 'is a prime number'
+
+for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #except should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+def test():
+ var = 100
+ if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+ else:
+ print "4 - Got a false expression value"
+ print var
+
+ var = 100
+ if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+ else:
+ print "4 - Got a false expression value"
+ print var
+
+ var = 100
+ if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+ else:
+ print "4 - Got a false expression value"
+ print var
+
+ for n in range(2, 10):
+ for x in range(2, n):
+ if n % x == 0:
+ print n, 'equals', x, '*', n/x
+ break
+ else:
+ # loop fell through without finding a factor
+ print n, 'is a prime number'
+
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #except should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
+ while True:
+ ok = raw_input(prompt)
+ if ok in ('y', 'ye', 'yes'):
+ return True
+ if ok in ('n', 'no', 'nop', 'nope'):
+ return False
+ retries = retries - 1
+ if retries < 0:
+ raise IOError('refusenik user')
+ print complaint
+ else:
+ pass
+
+for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #else should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+def minus():
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #except should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #else should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+def two():
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ # else should be in same level as except
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ # else should be in same level as except
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+def divide(x, y):
+ try:
+ result = x / y
+ except ZeroDivisionError:
+ print("division by zero!")
+ else:
+ print("result is", result)
+ finally:
+ print("executing finally clause")
+
+class DoSomething():
+ def test():
+ var = 100
+ if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+ else:
+ print "4 - Got a false expression value"
+ print var
+
+ var = 100
+ if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+ else:
+ print "4 - Got a false expression value"
+ print var
+
+ var = 100
+ if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+ else:
+ print "4 - Got a false expression value"
+ print var
+
+ for n in range(2, 10):
+ for x in range(2, n):
+ if n % x == 0:
+ print n, 'equals', x, '*', n/x
+ break
+ else:
+ # loop fell through without finding a factor
+ print n, 'is a prime number'
+
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #except should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+ def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
+ while True:
+ ok = raw_input(prompt)
+ if ok in ('y', 'ye', 'yes'):
+ return True
+ if ok in ('n', 'no', 'nop', 'nope'):
+ return False
+ retries = retries - 1
+ if retries < 0:
+ raise IOError('refusenik user')
+ print complaint
+ else:
+ pass
+
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #else should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+ def minus():
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #except should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #else should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+ def two():
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ # else should be in same level as except
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ # else should be in same level as except
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+ def divide(x, y):
+ try:
+ result = x / y
+ except ZeroDivisionError:
+ print("division by zero!")
+ else:
+ print("result is", result)
+ finally:
+ print("executing finally clause")
+
+ var = 100
+if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+else:
+ print "4 - Got a false expression value"
+ print var
diff --git a/src/UnitTests/TestData/Formatting/elseBlocksFirstLine2.py b/src/UnitTests/TestData/Formatting/elseBlocksFirstLine2.py
new file mode 100644
index 000000000..b99c1738d
--- /dev/null
+++ b/src/UnitTests/TestData/Formatting/elseBlocksFirstLine2.py
@@ -0,0 +1,4 @@
+if True == True:
+ a = 2
+ b = 3
+ else:
\ No newline at end of file
diff --git a/src/UnitTests/TestData/Formatting/elseBlocksFirstLine4.py b/src/UnitTests/TestData/Formatting/elseBlocksFirstLine4.py
new file mode 100644
index 000000000..64ad7dfb7
--- /dev/null
+++ b/src/UnitTests/TestData/Formatting/elseBlocksFirstLine4.py
@@ -0,0 +1,4 @@
+if True == True:
+ a = 2
+ b = 3
+ else:
\ No newline at end of file
diff --git a/src/UnitTests/TestData/Formatting/elseBlocksFirstLineTab.py b/src/UnitTests/TestData/Formatting/elseBlocksFirstLineTab.py
new file mode 100644
index 000000000..39cea5e8c
--- /dev/null
+++ b/src/UnitTests/TestData/Formatting/elseBlocksFirstLineTab.py
@@ -0,0 +1,4 @@
+if True == True:
+ a = 2
+ b = 3
+ else:
\ No newline at end of file
diff --git a/src/UnitTests/TestData/Formatting/elseBlocksTab.py b/src/UnitTests/TestData/Formatting/elseBlocksTab.py
new file mode 100644
index 000000000..e92233ea9
--- /dev/null
+++ b/src/UnitTests/TestData/Formatting/elseBlocksTab.py
@@ -0,0 +1,351 @@
+var = 100
+if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+else:
+ print "4 - Got a false expression value"
+ print var
+
+var = 100
+if var == 200:
+ print "1 - Got a true expression value"
+ print var
+elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+else:
+ print "4 - Got a false expression value"
+ print var
+
+var = 100
+if var == 200:
+ print "1 - Got a true expression value"
+ print var
+elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+ else:
+ print "4 - Got a false expression value"
+ print var
+
+ for n in range(2, 10):
+ for x in range(2, n):
+ if n % x == 0:
+ print n, 'equals', x, '*', n/x
+ break
+ else:
+ # loop fell through without finding a factor
+ print n, 'is a prime number'
+
+for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #except should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+def test():
+ var = 100
+ if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+ else:
+ print "4 - Got a false expression value"
+ print var
+
+ var = 100
+ if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+ else:
+ print "4 - Got a false expression value"
+ print var
+
+ var = 100
+ if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+ else:
+ print "4 - Got a false expression value"
+ print var
+
+ for n in range(2, 10):
+ for x in range(2, n):
+ if n % x == 0:
+ print n, 'equals', x, '*', n/x
+ break
+ else:
+ # loop fell through without finding a factor
+ print n, 'is a prime number'
+
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #except should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
+ while True:
+ ok = raw_input(prompt)
+ if ok in ('y', 'ye', 'yes'):
+ return True
+ if ok in ('n', 'no', 'nop', 'nope'):
+ return False
+ retries = retries - 1
+ if retries < 0:
+ raise IOError('refusenik user')
+ print complaint
+ else:
+ pass
+
+for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #else should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+def minus():
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #except should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #else should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+def two():
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ # else should be in same level as except
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ # else should be in same level as except
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+def divide(x, y):
+ try:
+ result = x / y
+ except ZeroDivisionError:
+ print("division by zero!")
+ else:
+ print("result is", result)
+ finally:
+ print("executing finally clause")
+
+class DoSomething():
+ def test():
+ var = 100
+ if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+ else:
+ print "4 - Got a false expression value"
+ print var
+
+ var = 100
+ if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+ else:
+ print "4 - Got a false expression value"
+ print var
+
+ var = 100
+ if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+ else:
+ print "4 - Got a false expression value"
+ print var
+
+ for n in range(2, 10):
+ for x in range(2, n):
+ if n % x == 0:
+ print n, 'equals', x, '*', n/x
+ break
+ else:
+ # loop fell through without finding a factor
+ print n, 'is a prime number'
+
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #except should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+ def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
+ while True:
+ ok = raw_input(prompt)
+ if ok in ('y', 'ye', 'yes'):
+ return True
+ if ok in ('n', 'no', 'nop', 'nope'):
+ return False
+ retries = retries - 1
+ if retries < 0:
+ raise IOError('refusenik user')
+ print complaint
+ else:
+ pass
+
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #else should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+ def minus():
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #except should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #else should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+ def two():
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ # else should be in same level as except
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ # else should be in same level as except
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+ def divide(x, y):
+ try:
+ result = x / y
+ except ZeroDivisionError:
+ print("division by zero!")
+ else:
+ print("result is", result)
+ finally:
+ print("executing finally clause")
+
+ var = 100
+if var == 200:
+ print "1 - Got a true expression value"
+ print var
+ elif var == 150:
+ print "2 - Got a true expression value"
+ print var
+ elif var == 100:
+ print "3 - Got a true expression value"
+ print var
+else:
+ print "4 - Got a false expression value"
+ print var
diff --git a/src/UnitTests/TestData/Formatting/tryBlocks2.py b/src/UnitTests/TestData/Formatting/tryBlocks2.py
new file mode 100644
index 000000000..504feeeb3
--- /dev/null
+++ b/src/UnitTests/TestData/Formatting/tryBlocks2.py
@@ -0,0 +1,208 @@
+
+while True:
+ try:
+ x = int(input("Please enter a number: "))
+ break
+ # except should be in same column as try:
+ except ValueError:
+ print("Oops! That was no valid number. Try again...")
+
+
+while True:
+ try:
+ x = int(input("Please enter a number: "))
+ break
+ # except should be in same column as try:
+ except ValueError:
+ print("Oops! That was no valid number. Try again...")
+
+class B(Exception):
+ pass
+
+class C(B):
+ pass
+
+class D(C):
+ pass
+
+for cls in [B, C, D]:
+ try:
+ raise cls()
+ except D:
+ print("D")
+ except C:
+ print("C")
+ # except should be in same level as except
+ except B:
+ print("B")
+
+
+for cls in [B, C, D]:
+ try:
+ raise cls()
+ except D:
+ print("D")
+ except C:
+ print("C")
+ # except should be in same level as except
+ except B:
+ print("B")
+
+for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ #except should be in same level as try
+ except IOError:
+ print('cannot open', arg)
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ #except should be in same level as try
+ except IOError:
+ print('cannot open', arg)
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #else should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #else should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+def minus():
+ while True:
+ try:
+ x = int(input("Please enter a number: "))
+ break
+ #except should be in same level as try:
+ except ValueError:
+ print("Oops! That was no valid number. Try again...")
+
+def minus():
+ while True:
+ try:
+ x = int(input("Please enter a number: "))
+ break
+ #except should be in same level as try:
+ except ValueError:
+ print("Oops! That was no valid number. Try again...")
+
+
+def zero():
+ for cls in [B, C, D]:
+ try:
+ raise cls()
+ #except should be in same level as try:
+ except D:
+ print("D")
+ except C:
+ print("C")
+ except B:
+ print("B")
+
+def zero():
+ for cls in [B, C, D]:
+ try:
+ raise cls()
+ except D:
+ print("D")
+ #except should be in same level as try:
+ except C:
+ print("C")
+ except B:
+ print("B")
+
+def one():
+ import sys
+
+ try:
+ f = open('myfile.txt')
+ s = f.readline()
+ i = int(s.strip())
+ except OSError as err:
+ print("OS error: {0}".format(err))
+ # except should be in same level as except
+ except ValueError:
+ print("Could not convert data to an integer.")
+ except:
+ print("Unexpected error:", sys.exc_info()[0])
+ raise
+
+def one():
+ import sys
+
+ try:
+ f = open('myfile.txt')
+ s = f.readline()
+ i = int(s.strip())
+ # except should be in same level as except
+ except OSError as err:
+ print("OS error: {0}".format(err))
+ except ValueError:
+ print("Could not convert data to an integer.")
+ except:
+ print("Unexpected error:", sys.exc_info()[0])
+ raise
+
+def two():
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ # else should be in same level as except
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+def two():
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ # else should be in same level as except
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+def divide(x, y):
+ try:
+ result = x / y
+ except ZeroDivisionError:
+ print("division by zero!")
+ else:
+ print("result is", result)
+ # finally should be in same level as except
+ finally:
+ print("executing finally clause")
+
+def divide(x, y):
+ try:
+ result = x / y
+ except ZeroDivisionError:
+ print("division by zero!")
+ else:
+ print("result is", result)
+ # finally should be in same level as except
+ finally:
+ print("executing finally clause")
\ No newline at end of file
diff --git a/src/UnitTests/TestData/Formatting/tryBlocks4.py b/src/UnitTests/TestData/Formatting/tryBlocks4.py
new file mode 100644
index 000000000..ce9e444ca
--- /dev/null
+++ b/src/UnitTests/TestData/Formatting/tryBlocks4.py
@@ -0,0 +1,208 @@
+
+while True:
+ try:
+ x = int(input("Please enter a number: "))
+ break
+ # except should be in same column as try:
+ except ValueError:
+ print("Oops! That was no valid number. Try again...")
+
+
+while True:
+ try:
+ x = int(input("Please enter a number: "))
+ break
+ # except should be in same column as try:
+ except ValueError:
+ print("Oops! That was no valid number. Try again...")
+
+class B(Exception):
+ pass
+
+class C(B):
+ pass
+
+class D(C):
+ pass
+
+for cls in [B, C, D]:
+ try:
+ raise cls()
+ except D:
+ print("D")
+ except C:
+ print("C")
+ # except should be in same level as except
+ except B:
+ print("B")
+
+
+for cls in [B, C, D]:
+ try:
+ raise cls()
+ except D:
+ print("D")
+ except C:
+ print("C")
+ # except should be in same level as except
+ except B:
+ print("B")
+
+for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ #except should be in same level as try
+ except IOError:
+ print('cannot open', arg)
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ #except should be in same level as try
+ except IOError:
+ print('cannot open', arg)
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #else should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #else should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+def minus():
+ while True:
+ try:
+ x = int(input("Please enter a number: "))
+ break
+ #except should be in same level as try:
+ except ValueError:
+ print("Oops! That was no valid number. Try again...")
+
+def minus():
+ while True:
+ try:
+ x = int(input("Please enter a number: "))
+ break
+ #except should be in same level as try:
+ except ValueError:
+ print("Oops! That was no valid number. Try again...")
+
+
+def zero():
+ for cls in [B, C, D]:
+ try:
+ raise cls()
+ #except should be in same level as try:
+ except D:
+ print("D")
+ except C:
+ print("C")
+ except B:
+ print("B")
+
+def zero():
+ for cls in [B, C, D]:
+ try:
+ raise cls()
+ except D:
+ print("D")
+ #except should be in same level as try:
+ except C:
+ print("C")
+ except B:
+ print("B")
+
+def one():
+ import sys
+
+ try:
+ f = open('myfile.txt')
+ s = f.readline()
+ i = int(s.strip())
+ except OSError as err:
+ print("OS error: {0}".format(err))
+ # except should be in same level as except
+ except ValueError:
+ print("Could not convert data to an integer.")
+ except:
+ print("Unexpected error:", sys.exc_info()[0])
+ raise
+
+def one():
+ import sys
+
+ try:
+ f = open('myfile.txt')
+ s = f.readline()
+ i = int(s.strip())
+ # except should be in same level as except
+ except OSError as err:
+ print("OS error: {0}".format(err))
+ except ValueError:
+ print("Could not convert data to an integer.")
+ except:
+ print("Unexpected error:", sys.exc_info()[0])
+ raise
+
+def two():
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ # else should be in same level as except
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+def two():
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ # else should be in same level as except
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+def divide(x, y):
+ try:
+ result = x / y
+ except ZeroDivisionError:
+ print("division by zero!")
+ else:
+ print("result is", result)
+ # finally should be in same level as except
+ finally:
+ print("executing finally clause")
+
+def divide(x, y):
+ try:
+ result = x / y
+ except ZeroDivisionError:
+ print("division by zero!")
+ else:
+ print("result is", result)
+ # finally should be in same level as except
+ finally:
+ print("executing finally clause")
\ No newline at end of file
diff --git a/src/UnitTests/TestData/Formatting/tryBlocksTab.py b/src/UnitTests/TestData/Formatting/tryBlocksTab.py
new file mode 100644
index 000000000..d94e057d4
--- /dev/null
+++ b/src/UnitTests/TestData/Formatting/tryBlocksTab.py
@@ -0,0 +1,208 @@
+
+while True:
+ try:
+ x = int(input("Please enter a number: "))
+ break
+ # except should be in same column as try:
+ except ValueError:
+ print("Oops! That was no valid number. Try again...")
+
+
+while True:
+ try:
+ x = int(input("Please enter a number: "))
+ break
+ # except should be in same column as try:
+ except ValueError:
+ print("Oops! That was no valid number. Try again...")
+
+class B(Exception):
+ pass
+
+class C(B):
+ pass
+
+class D(C):
+ pass
+
+for cls in [B, C, D]:
+ try:
+ raise cls()
+ except D:
+ print("D")
+ except C:
+ print("C")
+ # except should be in same level as except
+ except B:
+ print("B")
+
+
+for cls in [B, C, D]:
+ try:
+ raise cls()
+ except D:
+ print("D")
+ except C:
+ print("C")
+ # except should be in same level as except
+ except B:
+ print("B")
+
+for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ #except should be in same level as try
+ except IOError:
+ print('cannot open', arg)
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ #except should be in same level as try
+ except IOError:
+ print('cannot open', arg)
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #else should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ #else should be in same level as try
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+def minus():
+ while True:
+ try:
+ x = int(input("Please enter a number: "))
+ break
+ #except should be in same level as try:
+ except ValueError:
+ print("Oops! That was no valid number. Try again...")
+
+def minus():
+ while True:
+ try:
+ x = int(input("Please enter a number: "))
+ break
+ #except should be in same level as try:
+ except ValueError:
+ print("Oops! That was no valid number. Try again...")
+
+
+def zero():
+ for cls in [B, C, D]:
+ try:
+ raise cls()
+ #except should be in same level as try:
+ except D:
+ print("D")
+ except C:
+ print("C")
+ except B:
+ print("B")
+
+def zero():
+ for cls in [B, C, D]:
+ try:
+ raise cls()
+ except D:
+ print("D")
+ #except should be in same level as try:
+ except C:
+ print("C")
+ except B:
+ print("B")
+
+def one():
+ import sys
+
+ try:
+ f = open('myfile.txt')
+ s = f.readline()
+ i = int(s.strip())
+ except OSError as err:
+ print("OS error: {0}".format(err))
+ # except should be in same level as except
+ except ValueError:
+ print("Could not convert data to an integer.")
+ except:
+ print("Unexpected error:", sys.exc_info()[0])
+ raise
+
+def one():
+ import sys
+
+ try:
+ f = open('myfile.txt')
+ s = f.readline()
+ i = int(s.strip())
+ # except should be in same level as except
+ except OSError as err:
+ print("OS error: {0}".format(err))
+ except ValueError:
+ print("Could not convert data to an integer.")
+ except:
+ print("Unexpected error:", sys.exc_info()[0])
+ raise
+
+def two():
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ # else should be in same level as except
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+def two():
+ for arg in sys.argv[1:]:
+ try:
+ f = open(arg, 'r')
+ except IOError:
+ print('cannot open', arg)
+ # else should be in same level as except
+ else:
+ print(arg, 'has', len(f.readlines()), 'lines')
+ f.close()
+
+def divide(x, y):
+ try:
+ result = x / y
+ except ZeroDivisionError:
+ print("division by zero!")
+ else:
+ print("result is", result)
+ # finally should be in same level as except
+ finally:
+ print("executing finally clause")
+
+def divide(x, y):
+ try:
+ result = x / y
+ except ZeroDivisionError:
+ print("division by zero!")
+ else:
+ print("result is", result)
+ # finally should be in same level as except
+ finally:
+ print("executing finally clause")
\ No newline at end of file
From 8248f723e2af9c71b9571b128d89c519372d1af6 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Tue, 9 Oct 2018 12:36:14 -0700
Subject: [PATCH 3/5] basic check in LanguageServerTests
---
src/Analysis/Engine/Test/LanguageServerTests.cs | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/Analysis/Engine/Test/LanguageServerTests.cs b/src/Analysis/Engine/Test/LanguageServerTests.cs
index 4cd1fdc16..179afda32 100644
--- a/src/Analysis/Engine/Test/LanguageServerTests.cs
+++ b/src/Analysis/Engine/Test/LanguageServerTests.cs
@@ -1006,6 +1006,13 @@ public async Task OnTypeFormatting() {
edits = await s.SendDocumentOnTypeFormatting(uri, new SourceLocation(4, 1), "\n");
edits.Should().OnlyHaveTextEdit("x += 1", (2, 4, 2, 9));
}
+
+ using (var s = await CreateServer()) {
+ var uri = await AddModule(s, "if x:\n pass\n else:");
+
+ var edits = await s.SendDocumentOnTypeFormatting(uri, new SourceLocation(3, 9), ":");
+ edits.Should().OnlyHaveTextEdit("", (2, 0, 2, 4));
+ }
}
class GetAllExtensionProvider : ILanguageServerExtensionProvider {
From 04e2c3f166cafeadaed46e1ab2221debaea5aa0f Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Tue, 9 Oct 2018 13:16:17 -0700
Subject: [PATCH 4/5] split apart LanguageServer tests for OnTypeFormatting
---
src/Analysis/Engine/Test/LanguageServerTests.cs | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/Analysis/Engine/Test/LanguageServerTests.cs b/src/Analysis/Engine/Test/LanguageServerTests.cs
index 179afda32..715a663a8 100644
--- a/src/Analysis/Engine/Test/LanguageServerTests.cs
+++ b/src/Analysis/Engine/Test/LanguageServerTests.cs
@@ -991,7 +991,7 @@ public async Task ParseAndAnalysisDiagnostics() {
}
[TestMethod, Priority(0)]
- public async Task OnTypeFormatting() {
+ public async Task OnTypeFormattingLine() {
using (var s = await CreateServer()) {
var uri = await AddModule(s, "def foo ( ) :\n x = a + b\n x+= 1");
@@ -1006,7 +1006,10 @@ public async Task OnTypeFormatting() {
edits = await s.SendDocumentOnTypeFormatting(uri, new SourceLocation(4, 1), "\n");
edits.Should().OnlyHaveTextEdit("x += 1", (2, 4, 2, 9));
}
+ }
+ [TestMethod, Priority(0)]
+ public async Task OnTypeFormattingBlock() {
using (var s = await CreateServer()) {
var uri = await AddModule(s, "if x:\n pass\n else:");
From 503cb9535164a725851cc51630710e88f0478b27 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Tue, 9 Oct 2018 13:33:39 -0700
Subject: [PATCH 5/5] string.Empty instead of empty literal
---
src/Analysis/Engine/Test/BlockFormatterTests.cs | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/src/Analysis/Engine/Test/BlockFormatterTests.cs b/src/Analysis/Engine/Test/BlockFormatterTests.cs
index e2a4e54b0..280fea2b6 100644
--- a/src/Analysis/Engine/Test/BlockFormatterTests.cs
+++ b/src/Analysis/Engine/Test/BlockFormatterTests.cs
@@ -48,7 +48,7 @@ public void NullReader() {
[TestMethod, Priority(0)]
public async Task FirstLine() {
- using (var reader = new StringReader("")) {
+ using (var reader = new StringReader(string.Empty)) {
var edits = await BlockFormatter.ProvideEdits(reader, new Position { line = 0, character = 4 }, new FormattingOptions());
edits.Should().BeEmpty();
}
@@ -74,7 +74,6 @@ public async Task NoMatch() {
}
}
-
[DataRow("elseBlocksFirstLine2.py", 3, 7, true, 2, 0, 2)]
[DataRow("elseBlocksFirstLine4.py", 3, 9, true, 4, 0, 4)]
[DataRow("elseBlocksFirstLineTab.py", 3, 6, false, 4, 0, 1)]
@@ -87,7 +86,7 @@ public async Task ElseBlock(string filename, int line, int col, bool insertSpace
using (var reader = new StreamReader(src)) {
var edits = await BlockFormatter.ProvideEdits(reader, position, options);
- edits.Should().OnlyHaveTextEdit("", (line, startCharacter, line, endCharacter));
+ edits.Should().OnlyHaveTextEdit(string.Empty, (line, startCharacter, line, endCharacter));
}
}
@@ -107,7 +106,7 @@ public async Task TryBlockTwoSpace(int line, int col, int startCharacter, int en
using (var reader = new StreamReader(src)) {
var edits = await BlockFormatter.ProvideEdits(reader, position, options);
- edits.Should().OnlyHaveTextEdit("", (line, startCharacter, line, endCharacter));
+ edits.Should().OnlyHaveTextEdit(string.Empty, (line, startCharacter, line, endCharacter));
}
}
@@ -143,7 +142,7 @@ public async Task TryBlockFourSpace(int line, int col, int startCharacter, int e
using (var reader = new StreamReader(src)) {
var edits = await BlockFormatter.ProvideEdits(reader, position, options);
- edits.Should().OnlyHaveTextEdit("", (line, startCharacter, line, endCharacter));
+ edits.Should().OnlyHaveTextEdit(string.Empty, (line, startCharacter, line, endCharacter));
}
}
@@ -205,7 +204,7 @@ public async Task ElseBlockTwoSpace(int line, int col, int startCharacter, int e
using (var reader = new StreamReader(src)) {
var edits = await BlockFormatter.ProvideEdits(reader, position, options);
- edits.Should().OnlyHaveTextEdit("", (line, startCharacter, line, endCharacter));
+ edits.Should().OnlyHaveTextEdit(string.Empty, (line, startCharacter, line, endCharacter));
}
}
@@ -245,7 +244,7 @@ public async Task ElseBlockFourSpace(int line, int col, int startCharacter, int
using (var reader = new StreamReader(src)) {
var edits = await BlockFormatter.ProvideEdits(reader, position, options);
- edits.Should().OnlyHaveTextEdit("", (line, startCharacter, line, endCharacter));
+ edits.Should().OnlyHaveTextEdit(string.Empty, (line, startCharacter, line, endCharacter));
}
}
@@ -284,7 +283,7 @@ public async Task ElseBlockTab(int line, int col, int startCharacter, int endCha
using (var reader = new StreamReader(src)) {
var edits = await BlockFormatter.ProvideEdits(reader, position, options);
- edits.Should().OnlyHaveTextEdit("", (line, startCharacter, line, endCharacter));
+ edits.Should().OnlyHaveTextEdit(string.Empty, (line, startCharacter, line, endCharacter));
}
}