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

Commit 2025599

Browse files
Merge pull request microsoft#168 from AlexanderSher/master
Removed 'goto' from 'LineFormatter' and other code refactorings
2 parents bb1fb8c + 13d219d commit 2025599

File tree

12 files changed

+360
-313
lines changed

12 files changed

+360
-313
lines changed

src/Analysis/Engine/Impl/Definitions/Position.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,6 @@ public struct Position {
4040
public static bool operator >(Position p1, Position p2) => p1.line > p2.line || p1.line == p2.line && p1.character > p2.character;
4141
public static bool operator <(Position p1, Position p2) => p1.line < p2.line || p1.line == p2.line && p1.character < p2.character;
4242

43-
public override string ToString() => ((SourceLocation)this).ToString();
43+
public override string ToString() => $"({line}, {character})";
4444
}
4545
}

src/Analysis/Engine/Impl/Definitions/Range.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,6 @@ public struct Range {
2424
public static implicit operator SourceSpan(Range r) => new SourceSpan(r.start, r.end);
2525
public static implicit operator Range(SourceSpan span) => new Range { start = span.Start, end = span.End };
2626

27-
public override string ToString() => ((SourceSpan)this).ToString();
27+
public override string ToString() => $"{start} - {end}";
2828
}
2929
}

src/Analysis/Engine/Impl/Infrastructure/Extensions/StringBuilderExtensions.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,19 @@ public static StringBuilder TrimEnd(this StringBuilder sb) {
2525
return sb;
2626
}
2727

28-
public static StringBuilder EnsureEndsWithSpace(this StringBuilder sb, int count = 1, bool allowLeading = false) {
29-
if (sb.Length == 0 && !allowLeading) {
28+
public static StringBuilder EnsureEndsWithWhiteSpace(this StringBuilder sb, int whiteSpaceCount = 1) {
29+
if (sb.Length == 0) {
3030
return sb;
3131
}
3232

33-
for (var i = sb.Length - 1; i >= 0 && char.IsWhiteSpace(sb[i]); i--) {
34-
count--;
33+
for (var i = sb.Length - 1; i >= 0 && whiteSpaceCount > 0 && char.IsWhiteSpace(sb[i]); i--) {
34+
whiteSpaceCount--;
3535
}
3636

37-
if (count > 0) {
38-
sb.Append(new string(' ', count));
37+
if (whiteSpaceCount > 0) {
38+
sb.Append(' ', whiteSpaceCount);
3939
}
40-
40+
4141
return sb;
4242
}
4343
}

src/Analysis/Engine/Impl/Parsing/Tokenizer.cs

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -130,17 +130,9 @@ public List<TokenInfo> ReadTokens(int characterCount) {
130130
return tokens;
131131
}
132132

133-
public object CurrentState {
134-
get {
135-
return _state;
136-
}
137-
}
138-
139-
public SourceLocation CurrentPosition {
140-
get {
141-
return IndexToLocation(CurrentIndex);
142-
}
143-
}
133+
public object CurrentState => _state;
134+
public int CurrentLine => _newLineLocations.Count;
135+
public SourceLocation CurrentPosition => IndexToLocation(CurrentIndex);
144136

145137
public SourceLocation IndexToLocation(int index) {
146138
int match = _newLineLocations.BinarySearch(new NewLineLocation(index, NewLineKind.None));
@@ -2235,6 +2227,7 @@ private static void DumpToken(Token token) {
22352227
Console.WriteLine("{0} `{1}`", token.Kind, token.Image.Replace("\r", "\\r").Replace("\n", "\\n").Replace("\t", "\\t"));
22362228
}
22372229

2230+
internal NewLineLocation GetNewLineLocation(int line) => _newLineLocations.Count == line ? new NewLineLocation(CurrentIndex, NewLineKind.None) : _newLineLocations[line];
22382231
internal NewLineLocation[] GetLineLocations() => _newLineLocations.ToArray();
22392232
internal SourceLocation[] GetCommentLocations() => _commentLocations.ToArray();
22402233

@@ -2564,11 +2557,7 @@ private bool AtBeginning {
25642557
}
25652558
}
25662559

2567-
private int CurrentIndex {
2568-
get {
2569-
return _tokenStartIndex + Math.Min(_position, _end) - _start;
2570-
}
2571-
}
2560+
private int CurrentIndex => _tokenStartIndex + Math.Min(_position, _end) - _start;
25722561

25732562
private void DiscardToken() {
25742563
CheckInvariants();

src/Analysis/Engine/Test/FluentAssertions/AssertionsFactory.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,5 +102,8 @@ public static SignatureHelpAssertions Should(this SignatureHelp signatureHelp)
102102

103103
public static SignatureInformationAssertions Should(this SignatureInformation signatureInformation)
104104
=> new SignatureInformationAssertions(signatureInformation);
105+
106+
public static TextEditCollectionAssertions Should(this IEnumerable<TextEdit> textEdits)
107+
=> new TextEditCollectionAssertions(textEdits);
105108
}
106109
}

src/Analysis/Engine/Test/FluentAssertions/AssertionsUtilities.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,9 @@ public static IEnumerable<IAnalysisValue> FlattenAnalysisValues(IEnumerable<IAna
206206
}
207207
}
208208

209+
public static bool RangeEquals(Range r1, Range r2) => PositionEquals(r1.start, r2.start) && PositionEquals(r1.end, r2.end);
210+
public static bool PositionEquals(Position p1, Position p2) => p1.line == p2.line && p1.character == p2.character;
211+
209212
public static string DoubleEscape(string input)
210213
=> input.Replace("\r", "\\\u200Br").Replace("\n", "\\\u200Bn").Replace("\t", @"\t");
211214

src/Analysis/Engine/Test/FluentAssertions/ReferenceCollectionAssertions.cs

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,10 @@ public AndConstraint<ReferenceCollectionAssertions> OnlyHaveReferences(IEnumerab
7575
if (excess.Length > 0) {
7676
var excessString = string.Join(", ", excess.Select(Format));
7777
var errorMessage = expected.Length > 1
78-
? $"Expected {GetName()} to have only {expected.Length} references{{reason}}, but it also has references: {excessString}."
78+
? $"Expected {GetSubjectName()} to have only {expected.Length} references{{reason}}, but it also has references: {excessString}."
7979
: expected.Length > 0
80-
? $"Expected {GetName()} to have only one reference{{reason}}, but it also has references: {excessString}."
81-
: $"Expected {GetName()} to have no references{{reason}}, but it has references: {excessString}.";
80+
? $"Expected {GetSubjectName()} to have only one reference{{reason}}, but it also has references: {excessString}."
81+
: $"Expected {GetSubjectName()} to have no references{{reason}}, but it has references: {excessString}.";
8282

8383
Execute.Assertion.BecauseOf(because, reasonArgs).FailWith(errorMessage);
8484
}
@@ -87,10 +87,7 @@ public AndConstraint<ReferenceCollectionAssertions> OnlyHaveReferences(IEnumerab
8787
}
8888

8989
private static string Format((Uri uri, (int, int, int, int) range, ReferenceKind? kind) reference)
90-
=> $"({TestData.GetTestRelativePath(reference.uri)}, {Format(reference.range)}, {reference.kind})";
91-
92-
private static string Format((int startLine, int startCharacter, int endLine, int endCharacter) range)
93-
=> $"({range.startLine}, {range.startCharacter}) - ({range.endLine}, {range.endCharacter})";
90+
=> $"({TestData.GetTestRelativePath(reference.uri)}, {reference.range.ToString()}, {reference.kind})";
9491

9592
[CustomAssertion]
9693
public AndConstraint<ReferenceCollectionAssertions> HaveReferenceAt(Uri documentUri, int startLine, int startCharacter, int endLine, int endCharacter, ReferenceKind? referenceKind = null, string because = "", params object[] reasonArgs) {
@@ -117,38 +114,29 @@ public AndConstraint<ReferenceCollectionAssertions> HaveReferenceAt(Uri document
117114
private string FindReference(Uri documentUri, string moduleName, Range range, ReferenceKind? referenceKind = null) {
118115
var candidates = Subject.Where(av => Equals(av.uri, documentUri)).ToArray();
119116
if (candidates.Length == 0) {
120-
return $"Expected {GetName()} to have reference in the module '{moduleName}'{{reason}}, but no references has been found.";
117+
return $"Expected {GetSubjectName()} to have reference in the module '{moduleName}'{{reason}}, but no references has been found.";
121118
}
122119

123120
foreach (var candidate in candidates.Where(c => RangeEquals(c.range, range))) {
124121
return referenceKind.HasValue && candidate._kind != referenceKind
125-
? $"Expected {GetName()} to have reference of type '{referenceKind}'{{reason}}, but reference in module '{moduleName}' at {RangeToString(range)} has type '{candidate._kind}'"
122+
? $"Expected {GetSubjectName()} to have reference of type '{referenceKind}'{{reason}}, but reference in module '{moduleName}' at {range.ToString()} has type '{candidate._kind}'"
126123
: string.Empty;
127124
}
128125

129-
var errorMessage = $"Expected {GetName()} to have reference at {RangeToString(range)}{{reason}}, but module '{moduleName}' has no references at that range.";
126+
var errorMessage = $"Expected {GetSubjectName()} to have reference at {range.ToString()}{{reason}}, but module '{moduleName}' has no references at that range.";
130127
if (!referenceKind.HasValue) {
131128
return errorMessage;
132129
}
133130

134131
var matchingTypes = candidates.Where(av => av._kind == referenceKind).ToArray();
135132
var matchingTypesString = matchingTypes.Length > 0
136-
? $"References that match type '{referenceKind}' have spans {string.Join(" ,", matchingTypes.Select(av => RangeToString(av.range)))}"
133+
? $"References that match type '{referenceKind}' have spans {string.Join(" ,", matchingTypes.Select(av => av.range.ToString()))}"
137134
: $"There are no references with type '{referenceKind}' either";
138135

139136
return $"{errorMessage} {matchingTypesString}";
140137
}
141-
142-
private static string RangeToString(Range range)
143-
=> $"({range.start.line}, {range.start.character}) - ({range.end.line}, {range.end.character})";
144-
145-
private static bool RangeEquals(Range r1, Range r2)
146-
=> r1.start.line == r2.start.line
147-
&& r1.start.character == r2.start.character
148-
&& r1.end.line == r2.end.line
149-
&& r1.end.character == r2.end.character;
150-
138+
151139
[CustomAssertion]
152-
private static string GetName() => CallerIdentifier.DetermineCallerIdentity() ?? "collection";
140+
private static string GetSubjectName() => CallerIdentifier.DetermineCallerIdentity() ?? "collection";
153141
}
154142
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
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.Collections.Generic;
19+
using System.Diagnostics.CodeAnalysis;
20+
using System.Linq;
21+
using FluentAssertions;
22+
using FluentAssertions.Collections;
23+
using FluentAssertions.Execution;
24+
using Microsoft.Python.LanguageServer;
25+
using static Microsoft.PythonTools.Analysis.FluentAssertions.AssertionsUtilities;
26+
27+
namespace Microsoft.PythonTools.Analysis.FluentAssertions {
28+
[ExcludeFromCodeCoverage]
29+
internal sealed class TextEditCollectionAssertions : SelfReferencingCollectionAssertions<TextEdit, TextEditCollectionAssertions> {
30+
public TextEditCollectionAssertions(IEnumerable<TextEdit> references) : base(references) { }
31+
32+
protected override string Identifier => nameof(TextEdit) + "Collection";
33+
34+
35+
[CustomAssertion]
36+
public AndConstraint<TextEditCollectionAssertions> OnlyHaveTextEdit(string expectedText, (int startLine, int startCharacter, int endLine, int endCharacter) expectedRange, string because = "", params object[] reasonArgs)
37+
=> OnlyHaveTextEdits(new[] {(expectedText, expectedRange)}, because, reasonArgs);
38+
39+
[CustomAssertion]
40+
public AndConstraint<TextEditCollectionAssertions> OnlyHaveTextEdits(params (string expectedText, (int startLine, int startCharacter, int endLine, int endCharacter) expectedRange)[] textEdits)
41+
=> OnlyHaveTextEdits(textEdits, string.Empty);
42+
43+
[CustomAssertion]
44+
public AndConstraint<TextEditCollectionAssertions> OnlyHaveTextEdits(IEnumerable<(string expectedText, (int startLine, int startCharacter, int endLine, int endCharacter) expectedRange)> textEdits, string because = "", params object[] reasonArgs) {
45+
var expected = textEdits.ToArray();
46+
foreach (var (expectedText, (startLine, startCharacter, endLine, endCharacter)) in expected) {
47+
HaveTextEditAt(expectedText, (startLine, startCharacter, endLine, endCharacter), because, reasonArgs);
48+
}
49+
50+
var excess = Subject.Select(r => (r.newText, (r.range.start.line, r.range.start.character, r.range.end.line, r.range.end.character)))
51+
.Except(expected)
52+
.ToArray();
53+
54+
if (excess.Length > 0) {
55+
var excessString = string.Join(", ", excess.Select(((string text, (int, int, int, int) range) te) => $"({te.text}, {te.range.ToString()})"));
56+
var errorMessage = expected.Length > 1
57+
? $"Expected {GetSubjectName()} to have only {expected.Length} textEdits{{reason}}, but it also has textEdits: {excessString}."
58+
: expected.Length > 0
59+
? $"Expected {GetSubjectName()} to have only one reference{{reason}}, but it also has textEdits: {excessString}."
60+
: $"Expected {GetSubjectName()} to have no textEdits{{reason}}, but it has textEdits: {excessString}.";
61+
62+
Execute.Assertion.BecauseOf(because, reasonArgs).FailWith(errorMessage);
63+
}
64+
65+
return new AndConstraint<TextEditCollectionAssertions>(this);
66+
}
67+
68+
[CustomAssertion]
69+
public AndConstraint<TextEditCollectionAssertions> HaveTextEditAt(string expectedText, (int startLine, int startCharacter, int endLine, int endCharacter) expectedRange, string because = "", params object[] reasonArgs) {
70+
var range = new Range {
71+
start = new Position { line = expectedRange.startLine, character = expectedRange.startCharacter },
72+
end = new Position { line = expectedRange.endLine, character = expectedRange.endCharacter }
73+
};
74+
75+
var errorMessage = GetHaveTextEditErrorMessage(expectedText, range);
76+
if (errorMessage != string.Empty) {
77+
var assertion = Execute.Assertion.BecauseOf(because, reasonArgs);
78+
assertion.AddNonReportable("expectedText", expectedText);
79+
assertion.AddNonReportable("expectedRange", range);
80+
assertion.AddNonReportable("currentTexts", GetQuotedNames(Subject.Select(te => te.newText)));
81+
assertion.FailWith(errorMessage);
82+
}
83+
84+
return new AndConstraint<TextEditCollectionAssertions>(this);
85+
}
86+
87+
[CustomAssertion]
88+
private string GetHaveTextEditErrorMessage(string expectedText, Range expectedRange) {
89+
var candidates = Subject.Where(av => string.Equals(av.newText, expectedText, StringComparison.Ordinal)).ToArray();
90+
if (candidates.Length == 0) {
91+
return "Expected {context:subject} to have text edit with newText \'{expectedText}\'{reason}, but "
92+
+ (Subject.Any() ? "it has {currentTexts}" : "it is empty");
93+
}
94+
95+
var candidatesWithRange = candidates.Where(c => RangeEquals(c.range, expectedRange)).ToArray();
96+
if (candidatesWithRange.Length > 1) {
97+
return $"Expected {{context:subject}} to have only one text edit with newText '{{expectedText}}' and range {{expectedRange}}{{reason}}, but there are {candidatesWithRange.Length}";
98+
}
99+
100+
if (candidatesWithRange.Length == 0) {
101+
return "Expected {context:subject} to have text edit with newText \'{expectedText}\' in range {expectedRange}{reason}, but "
102+
+ (candidates.Length == 1
103+
? $"it has range {candidates[0].range.ToString()}"
104+
: $"they are in ranges {string.Join(", ", candidates.Select(te => te.range.ToString()))}");
105+
}
106+
107+
return string.Empty;
108+
}
109+
110+
[CustomAssertion]
111+
private static string GetSubjectName() => CallerIdentifier.DetermineCallerIdentity() ?? "collection";
112+
}
113+
}

src/Analysis/Engine/Test/LanguageServerTests.cs

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,31 +1010,13 @@ public async Task OnTypeFormatting() {
10101010
// Extended tests for line formatting are in LineFormatterTests.
10111011
// These just verify that the language server formats and returns something correct.
10121012
var edits = await s.SendDocumentOnTypeFormatting(uri, new SourceLocation(2, 1), "\n");
1013-
edits.Should().OnlyContain(new TextEdit {
1014-
newText = "def foo():",
1015-
range = new Range {
1016-
start = new SourceLocation(1, 1),
1017-
end = new SourceLocation(1, 15)
1018-
}
1019-
});
1013+
edits.Should().OnlyHaveTextEdit("def foo():", (0, 0, 0, 14));
10201014

10211015
edits = await s.SendDocumentOnTypeFormatting(uri, new SourceLocation(3, 1), "\n");
1022-
edits.Should().OnlyContain(new TextEdit {
1023-
newText = "x = a + b",
1024-
range = new Range {
1025-
start = new SourceLocation(2, 5),
1026-
end = new SourceLocation(2, 14)
1027-
}
1028-
});
1016+
edits.Should().OnlyHaveTextEdit("x = a + b", (1, 4, 1, 13));
10291017

10301018
edits = await s.SendDocumentOnTypeFormatting(uri, new SourceLocation(4, 1), "\n");
1031-
edits.Should().OnlyContain(new TextEdit {
1032-
newText = "x += 1",
1033-
range = new Range {
1034-
start = new SourceLocation(3, 5),
1035-
end = new SourceLocation(3, 10)
1036-
}
1037-
});
1019+
edits.Should().OnlyHaveTextEdit("x += 1", (2, 4, 2, 9));
10381020
}
10391021
}
10401022

0 commit comments

Comments
 (0)