зеркало из https://github.com/dotnet/razor.git
feedback
This commit is contained in:
Родитель
ee127559a0
Коммит
a6d803b280
|
@ -12,7 +12,6 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer
|
|||
{
|
||||
internal class DefaultRazorDocumentMappingService : RazorDocumentMappingService
|
||||
{
|
||||
|
||||
public override bool TryMapFromProjectedDocumentRange(RazorCodeDocument codeDocument, Range projectedRange, out Range originalRange)
|
||||
{
|
||||
if (codeDocument is null)
|
||||
|
|
|
@ -65,12 +65,12 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting
|
|||
var result = await _server.Client.SendRequest<RazorDocumentRangeFormattingParams, RazorDocumentRangeFormattingResponse>(
|
||||
"razor/rangeFormatting", @params);
|
||||
|
||||
var mappedEdits = MapProjectedCSharpEdits(codeDocument, result.Edits);
|
||||
var mappedEdits = MapEditsToHostDocument(codeDocument, result.Edits);
|
||||
|
||||
return mappedEdits;
|
||||
}
|
||||
|
||||
private TextEdit[] MapProjectedCSharpEdits(RazorCodeDocument codeDocument, TextEdit[] csharpEdits)
|
||||
private TextEdit[] MapEditsToHostDocument(RazorCodeDocument codeDocument, TextEdit[] csharpEdits)
|
||||
{
|
||||
var actualEdits = new List<TextEdit>();
|
||||
foreach (var edit in csharpEdits)
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting
|
|||
internal class DefaultRazorFormattingService : RazorFormattingService
|
||||
{
|
||||
private readonly ILanguageServer _server;
|
||||
private readonly CSharpFormatter _cSharpFormatter;
|
||||
private readonly CSharpFormatter _csharpFormatter;
|
||||
private readonly HtmlFormatter _htmlFormatter;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
|
@ -52,7 +52,7 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting
|
|||
}
|
||||
|
||||
_server = server;
|
||||
_cSharpFormatter = new CSharpFormatter(documentMappingService, server, filePathNormalizer);
|
||||
_csharpFormatter = new CSharpFormatter(documentMappingService, server, filePathNormalizer);
|
||||
_htmlFormatter = new HtmlFormatter(server, filePathNormalizer);
|
||||
_logger = loggerFactory.CreateLogger<DefaultRazorFormattingService>();
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting
|
|||
var rangeToFormat = codeBlockRange.Overlap(context.Range);
|
||||
if (rangeToFormat != null)
|
||||
{
|
||||
var codeEdits = await _cSharpFormatter.FormatAsync(context.CodeDocument, rangeToFormat, context.Uri, context.Options);
|
||||
var codeEdits = await _csharpFormatter.FormatAsync(context.CodeDocument, rangeToFormat, context.Uri, context.Options);
|
||||
changedText = ApplyCSharpEdits(context, codeBlockRange, codeEdits, minCSharpIndentLevel: 2);
|
||||
}
|
||||
|
||||
|
@ -144,8 +144,11 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting
|
|||
return allEdits.ToArray();
|
||||
}
|
||||
|
||||
// minCSharpIndentLevel refers to the minimum level of how much the C# formatter would indent code.
|
||||
// This is typically 2 for @code/@functions blocks and 3 for @{} blocks and other Razor blocks.
|
||||
//
|
||||
// 'minCSharpIndentLevel' refers to the minimum level of how much the C# formatter would indent code.
|
||||
// @code/@functions blocks contain class members and so are typically indented by 2 levels.
|
||||
// @{} blocks are put inside method body which means they are typically indented by 3 levels.
|
||||
//
|
||||
private SourceText ApplyCSharpEdits(FormattingContext context, Range codeBlockRange, TextEdit[] edits, int minCSharpIndentLevel)
|
||||
{
|
||||
var originalText = context.SourceText;
|
||||
|
@ -187,7 +190,7 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting
|
|||
}
|
||||
|
||||
var leadingWhitespace = line.GetLeadingWhitespace();
|
||||
var minCSharpIndentLength = context.Options.TabSize * minCSharpIndentLevel;
|
||||
var minCSharpIndentLength = GetIndentationString(context, minCSharpIndentLevel).Length;
|
||||
if (leadingWhitespace.Length < minCSharpIndentLength)
|
||||
{
|
||||
// For whatever reason, the C# formatter decided to not indent this. Leave it as is.
|
||||
|
@ -196,17 +199,16 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting
|
|||
else
|
||||
{
|
||||
var effectiveDesiredIndentationLevel = desiredIndentationLevel - minCSharpIndentLevel;
|
||||
var effectiveDesiredIndentation = GetIndentationString(context, Math.Abs(effectiveDesiredIndentationLevel));
|
||||
if (effectiveDesiredIndentationLevel < 0)
|
||||
{
|
||||
// This means that we need to unindent.
|
||||
var length = Math.Abs(effectiveDesiredIndentationLevel * context.Options.TabSize);
|
||||
var span = new TextSpan(line.Start, (int)length);
|
||||
var span = new TextSpan(line.Start, effectiveDesiredIndentation.Length);
|
||||
editsToApply.Add(new TextChange(span, string.Empty));
|
||||
}
|
||||
else if (effectiveDesiredIndentationLevel > 0)
|
||||
{
|
||||
// This means that we need to indent.
|
||||
var effectiveDesiredIndentation = GetIndentationString(context, effectiveDesiredIndentationLevel);
|
||||
var span = new TextSpan(line.Start, 0);
|
||||
editsToApply.Add(new TextChange(span, effectiveDesiredIndentation));
|
||||
}
|
||||
|
@ -363,13 +365,21 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting
|
|||
private void TrackChangeInSpan(SourceText oldText, TextSpan originalSpan, SourceText newText, out TextSpan changedSpan, out TextSpan changeEncompassingSpan)
|
||||
{
|
||||
var affectedRange = newText.GetEncompassingTextChangeRange(oldText);
|
||||
|
||||
// The span of text before the edit which is being changed
|
||||
changeEncompassingSpan = affectedRange.Span;
|
||||
|
||||
if (!originalSpan.Contains(changeEncompassingSpan))
|
||||
{
|
||||
_logger.LogDebug($"The changed region {changeEncompassingSpan} was not a subset of the span {originalSpan} being tracked. This is unexpected.");
|
||||
}
|
||||
|
||||
changedSpan = TextSpan.FromBounds(originalSpan.Start, originalSpan.End + affectedRange.NewLength - affectedRange.Span.Length);
|
||||
// We now know what was the range that changed and the length of that span after the change.
|
||||
// Let's now compute what the original span looks like after the change.
|
||||
// We know it still starts from the same location but could have grown or shrunk in length.
|
||||
// Compute the change in length and then update the original span.
|
||||
var changeInOriginalSpanLength = affectedRange.NewLength - changeEncompassingSpan.Length;
|
||||
changedSpan = TextSpan.FromBounds(originalSpan.Start, originalSpan.End + changeInOriginalSpanLength);
|
||||
}
|
||||
|
||||
private TextChange[] Diff(SourceText oldText, SourceText newText, TextSpan? spanToDiff = default)
|
||||
|
@ -395,7 +405,12 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting
|
|||
private static string GetIndentationString(FormattingContext context, int indentationLevel)
|
||||
{
|
||||
var indentChar = context.Options.InsertSpaces ? ' ' : '\t';
|
||||
var indentation = new string(indentChar, (int)context.Options.TabSize * indentationLevel);
|
||||
var indentationLength = indentationLevel;
|
||||
if (context.Options.InsertSpaces)
|
||||
{
|
||||
indentationLength *= (int)context.Options.TabSize;
|
||||
}
|
||||
var indentation = new string(indentChar, indentationLength);
|
||||
return indentation;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,22 +25,4 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting
|
|||
|
||||
public Dictionary<int, IndentationContext> Indentations { get; } = new Dictionary<int, IndentationContext>();
|
||||
}
|
||||
|
||||
internal class IndentationContext
|
||||
{
|
||||
public int Line { get; set; }
|
||||
|
||||
public int IndentationLevel { get; set; }
|
||||
|
||||
public int RelativeIndentationLevel { get; set; }
|
||||
|
||||
public int ExistingIndentation { get; set; }
|
||||
|
||||
public FormattingSpan FirstSpan { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Line: {Line}, IndentationLevel: {IndentationLevel}, RelativeIndentationLevel: {RelativeIndentationLevel}, ExistingIndentation: {ExistingIndentation}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting
|
||||
{
|
||||
internal class IndentationContext
|
||||
{
|
||||
public int Line { get; set; }
|
||||
|
||||
public int IndentationLevel { get; set; }
|
||||
|
||||
public int RelativeIndentationLevel { get; set; }
|
||||
|
||||
public int ExistingIndentation { get; set; }
|
||||
|
||||
public FormattingSpan FirstSpan { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Line: {Line}, IndentationLevel: {IndentationLevel}, RelativeIndentationLevel: {RelativeIndentationLevel}, ExistingIndentation: {ExistingIndentation}";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -85,7 +85,7 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting
|
|||
return documentSnapshot;
|
||||
}, cancellationToken, TaskCreationOptions.None, _foregroundDispatcher.ForegroundScheduler);
|
||||
|
||||
if (document is null)
|
||||
if (document is null || cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -40,8 +40,10 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting
|
|||
|
||||
var startLocation = source.Lines.GetLocation(start);
|
||||
var endLocation = source.Lines.GetLocation(end);
|
||||
var startPosition = GetLinePosition(startLocation);
|
||||
var endPosition = GetLinePosition(endLocation);
|
||||
|
||||
return new LinePositionSpan(GetLinePosition(startLocation), GetLinePosition(endLocation));
|
||||
return new LinePositionSpan(startPosition, endPosition);
|
||||
|
||||
static LinePosition GetLinePosition(SourceLocation location)
|
||||
{
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.LanguageServer
|
||||
|
@ -11,12 +12,12 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer
|
|||
{
|
||||
if (newText is null)
|
||||
{
|
||||
throw new System.ArgumentNullException(nameof(newText));
|
||||
throw new ArgumentNullException(nameof(newText));
|
||||
}
|
||||
|
||||
if (oldText is null)
|
||||
{
|
||||
throw new System.ArgumentNullException(nameof(oldText));
|
||||
throw new ArgumentNullException(nameof(oldText));
|
||||
}
|
||||
|
||||
var ranges = newText.GetChangeRanges(oldText);
|
||||
|
@ -38,7 +39,7 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer
|
|||
{
|
||||
if (source is null)
|
||||
{
|
||||
throw new System.ArgumentNullException(nameof(source));
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
|
||||
var line = source.Lines.GetLineFromPosition(position);
|
||||
|
@ -57,7 +58,7 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer
|
|||
{
|
||||
if (source is null)
|
||||
{
|
||||
throw new System.ArgumentNullException(nameof(source));
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
|
||||
source.GetLineAndOffset(textSpan.Start, out startLineNumber, out startOffset);
|
||||
|
@ -68,7 +69,7 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer
|
|||
{
|
||||
if (source is null)
|
||||
{
|
||||
throw new System.ArgumentNullException(nameof(source));
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
|
||||
var charBuffer = new char[span.Length];
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting
|
||||
|
@ -13,14 +14,14 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting
|
|||
{
|
||||
await RunFormattingTestAsync(
|
||||
input: @"
|
||||
|@functions {
|
||||
|@code {
|
||||
public class Foo{}
|
||||
public interface Bar {
|
||||
}
|
||||
}|
|
||||
",
|
||||
expected: @"
|
||||
@functions {
|
||||
@code {
|
||||
public class Foo { }
|
||||
public interface Bar
|
||||
{
|
||||
|
@ -74,14 +75,14 @@ void Method() { <div></div> }
|
|||
{
|
||||
await RunFormattingTestAsync(
|
||||
input: @"
|
||||
|@functions {
|
||||
|@code {
|
||||
public class Foo{
|
||||
void Method() { @DateTime.Now }
|
||||
}
|
||||
}|
|
||||
",
|
||||
expected: @"
|
||||
@functions {
|
||||
@code {
|
||||
public class Foo{
|
||||
void Method() { @DateTime.Now }
|
||||
}
|
||||
|
@ -106,7 +107,8 @@ expected: @"
|
|||
void Method() { @(DateTime.Now) }
|
||||
}
|
||||
}
|
||||
");
|
||||
",
|
||||
fileKind: FileKinds.Legacy);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -207,7 +209,8 @@ Hello World
|
|||
{ }
|
||||
}
|
||||
}
|
||||
");
|
||||
",
|
||||
fileKind: FileKinds.Legacy);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -216,7 +219,7 @@ Hello World
|
|||
await RunFormattingTestAsync(
|
||||
input: @"|
|
||||
Hello World
|
||||
@functions {
|
||||
@code {
|
||||
public class HelloWorld
|
||||
{
|
||||
}
|
||||
|
@ -229,7 +232,7 @@ public class HelloWorld
|
|||
|",
|
||||
expected: @"
|
||||
Hello World
|
||||
@functions {
|
||||
@code {
|
||||
public class HelloWorld
|
||||
{
|
||||
}
|
||||
|
@ -379,5 +382,116 @@ expected: @"
|
|||
}
|
||||
");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CodeBlockDirective_UseTabs()
|
||||
{
|
||||
await RunFormattingTestAsync(
|
||||
input: @"
|
||||
|@code {
|
||||
public class Foo{}
|
||||
void Method( ) {
|
||||
}
|
||||
}|
|
||||
",
|
||||
expected: @"
|
||||
@code {
|
||||
public class Foo { }
|
||||
void Method()
|
||||
{
|
||||
}
|
||||
}
|
||||
",
|
||||
insertSpaces: false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CodeBlockDirective_UseTabsWithTabSize8()
|
||||
{
|
||||
await RunFormattingTestAsync(
|
||||
input: @"
|
||||
|@code {
|
||||
public class Foo{}
|
||||
void Method( ) {
|
||||
}
|
||||
}|
|
||||
",
|
||||
expected: @"
|
||||
@code {
|
||||
public class Foo { }
|
||||
void Method()
|
||||
{
|
||||
}
|
||||
}
|
||||
",
|
||||
tabSize: 8,
|
||||
insertSpaces: false);
|
||||
}
|
||||
|
||||
[Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/18996")]
|
||||
public async Task CodeBlockDirective_WithTabSize3()
|
||||
{
|
||||
await RunFormattingTestAsync(
|
||||
input: @"
|
||||
|@code {
|
||||
public class Foo{}
|
||||
void Method( ) {
|
||||
}
|
||||
}|
|
||||
",
|
||||
expected: @"
|
||||
@code {
|
||||
public class Foo { }
|
||||
void Method()
|
||||
{
|
||||
}
|
||||
}
|
||||
",
|
||||
tabSize: 3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CodeBlockDirective_WithTabSize8()
|
||||
{
|
||||
await RunFormattingTestAsync(
|
||||
input: @"
|
||||
|@code {
|
||||
public class Foo{}
|
||||
void Method( ) {
|
||||
}
|
||||
}|
|
||||
",
|
||||
expected: @"
|
||||
@code {
|
||||
public class Foo { }
|
||||
void Method()
|
||||
{
|
||||
}
|
||||
}
|
||||
",
|
||||
tabSize: 8);
|
||||
}
|
||||
|
||||
[Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/18996")]
|
||||
public async Task CodeBlockDirective_WithTabSize12()
|
||||
{
|
||||
await RunFormattingTestAsync(
|
||||
input: @"
|
||||
|@code {
|
||||
public class Foo{}
|
||||
void Method( ) {
|
||||
}
|
||||
}|
|
||||
",
|
||||
expected: @"
|
||||
@code {
|
||||
public class Foo { }
|
||||
void Method()
|
||||
{
|
||||
}
|
||||
}
|
||||
",
|
||||
tabSize: 12);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting
|
|||
{
|
||||
public class FormattingTestBase : LanguageServerTestBase
|
||||
{
|
||||
protected async Task RunFormattingTestAsync(string input, string expected, int tabSize = 4, bool insertSpaces = true)
|
||||
protected async Task RunFormattingTestAsync(string input, string expected, int tabSize = 4, bool insertSpaces = true, string fileKind = default)
|
||||
{
|
||||
// Arrange
|
||||
var start = input.IndexOf('|');
|
||||
|
@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting
|
|||
|
||||
var path = "file:///path/to/document.razor";
|
||||
var uri = new Uri(path);
|
||||
var codeDocument = CreateCodeDocument(source, uri.AbsolutePath);
|
||||
var codeDocument = CreateCodeDocument(source, uri.AbsolutePath, fileKind: fileKind);
|
||||
var options = new FormattingOptions()
|
||||
{
|
||||
TabSize = tabSize,
|
||||
|
@ -55,12 +55,13 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting
|
|||
return source.WithChanges(changes);
|
||||
}
|
||||
|
||||
private static RazorCodeDocument CreateCodeDocument(SourceText text, string path, IReadOnlyList<TagHelperDescriptor> tagHelpers = null)
|
||||
private static RazorCodeDocument CreateCodeDocument(SourceText text, string path, IReadOnlyList<TagHelperDescriptor> tagHelpers = null, string fileKind = default)
|
||||
{
|
||||
fileKind ??= FileKinds.Component;
|
||||
tagHelpers ??= Array.Empty<TagHelperDescriptor>();
|
||||
var sourceDocument = text.GetRazorSourceDocument(path, path);
|
||||
var projectEngine = RazorProjectEngine.Create(builder => { });
|
||||
var codeDocument = projectEngine.ProcessDesignTime(sourceDocument, FileKinds.Legacy, Array.Empty<RazorSourceDocument>(), tagHelpers);
|
||||
var codeDocument = projectEngine.ProcessDesignTime(sourceDocument, fileKind, Array.Empty<RazorSourceDocument>(), tagHelpers);
|
||||
return codeDocument;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче