Only handle indentation for open curly brace instead of all metacode
This commit is contained in:
parent
786f825a3d
commit
f41dc2aef9
|
|
@ -39,52 +39,59 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
|
|
||||||
var simulatedChange = new SourceChange(previousLineEndIndex, 0, string.Empty);
|
var simulatedChange = new SourceChange(previousLineEndIndex, 0, string.Empty);
|
||||||
var owningSpan = LocateOwner(syntaxTree.Root, simulatedChange);
|
var owningSpan = LocateOwner(syntaxTree.Root, simulatedChange);
|
||||||
|
if (owningSpan.Kind == SpanKindInternal.Code)
|
||||||
|
{
|
||||||
|
// Example,
|
||||||
|
// @{\n
|
||||||
|
// ^ - The newline here is a code span and we should just let the default c# editor take care of indentation.
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
int? desiredIndentation = null;
|
int? desiredIndentation = null;
|
||||||
|
|
||||||
if (owningSpan.Kind != SpanKindInternal.Code)
|
SyntaxTreeNode owningChild = owningSpan;
|
||||||
|
while ((owningChild.Parent != null) && !desiredIndentation.HasValue)
|
||||||
{
|
{
|
||||||
SyntaxTreeNode owningChild = owningSpan;
|
var owningParent = owningChild.Parent;
|
||||||
while ((owningChild.Parent != null) && !desiredIndentation.HasValue)
|
var children = new List<SyntaxTreeNode>(owningParent.Children);
|
||||||
|
for (var i = 0; i < children.Count; i++)
|
||||||
{
|
{
|
||||||
Block owningParent = owningChild.Parent;
|
var currentChild = children[i];
|
||||||
List<SyntaxTreeNode> children = new List<SyntaxTreeNode>(owningParent.Children);
|
if (!currentChild.IsBlock)
|
||||||
for (int i = 0; i < children.Count; i++)
|
|
||||||
{
|
{
|
||||||
SyntaxTreeNode curChild = children[i];
|
var currentSpan = currentChild as Span;
|
||||||
if (!curChild.IsBlock)
|
if (currentSpan.Symbols.Count == 1 &&
|
||||||
|
currentSpan.Symbols[0] is CSharpSymbol symbol &&
|
||||||
|
symbol.Type == CSharpSymbolType.LeftBrace)
|
||||||
{
|
{
|
||||||
Span curSpan = curChild as Span;
|
var extraIndent = 0;
|
||||||
if (curSpan.Kind == SpanKindInternal.MetaCode)
|
|
||||||
|
// Dev11 337312: Only indent one level deeper if the item after the open curly brace is a markup block
|
||||||
|
if (i < children.Count - 1)
|
||||||
{
|
{
|
||||||
var extraIndent = 0;
|
var nextChild = children[i + 1];
|
||||||
|
if (nextChild.IsBlock && ((nextChild as Block).Type == BlockKindInternal.Markup))
|
||||||
// Dev11 337312: Only indent one level deeper if the item after the metacode is a markup block
|
|
||||||
if (i < children.Count - 1)
|
|
||||||
{
|
{
|
||||||
SyntaxTreeNode nextChild = children[i + 1];
|
extraIndent = indentSize;
|
||||||
if (nextChild.IsBlock && ((nextChild as Block).Type == BlockKindInternal.Markup))
|
|
||||||
{
|
|
||||||
extraIndent = indentSize;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can't rely on the syntax trees representation of the source document because partial parses may have mutated
|
|
||||||
// the underlying SyntaxTree text buffer. Because of this, if we want to provide accurate indentations we need to
|
|
||||||
// operate on the current line representation as indicated by the provider.
|
|
||||||
var line = getLineContent(curSpan.Start.LineIndex);
|
|
||||||
desiredIndentation = GetIndentLevelOfLine(line, tabSize) + indentSize;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (curChild == owningChild)
|
// We can't rely on the syntax trees representation of the source document because partial parses may have mutated
|
||||||
{
|
// the underlying SyntaxTree text buffer. Because of this, if we want to provide accurate indentations we need to
|
||||||
break;
|
// operate on the current line representation as indicated by the provider.
|
||||||
|
var line = getLineContent(currentSpan.Start.LineIndex);
|
||||||
|
desiredIndentation = GetIndentLevelOfLine(line, tabSize) + indentSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
owningChild = owningParent;
|
if (currentChild == owningChild)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
owningChild = owningParent;
|
||||||
}
|
}
|
||||||
|
|
||||||
return desiredIndentation;
|
return desiredIndentation;
|
||||||
|
|
@ -94,7 +101,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
{
|
{
|
||||||
// Ask each child recursively
|
// Ask each child recursively
|
||||||
Span owner = null;
|
Span owner = null;
|
||||||
foreach (SyntaxTreeNode element in root.Children)
|
foreach (var element in root.Children)
|
||||||
{
|
{
|
||||||
if (element.Start.AbsoluteIndex > change.Span.AbsoluteIndex)
|
if (element.Start.AbsoluteIndex > change.Span.AbsoluteIndex)
|
||||||
{
|
{
|
||||||
|
|
@ -102,7 +109,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
int elementLen = element.Length;
|
var elementLen = element.Length;
|
||||||
if (element.Start.AbsoluteIndex + elementLen < change.Span.AbsoluteIndex)
|
if (element.Start.AbsoluteIndex + elementLen < change.Span.AbsoluteIndex)
|
||||||
{
|
{
|
||||||
// not far enough
|
// not far enough
|
||||||
|
|
@ -111,14 +118,14 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
|
|
||||||
if (element.IsBlock)
|
if (element.IsBlock)
|
||||||
{
|
{
|
||||||
Block block = element as Block;
|
var block = element as Block;
|
||||||
|
|
||||||
if (element.Start.AbsoluteIndex + elementLen == change.Span.AbsoluteIndex)
|
if (element.Start.AbsoluteIndex + elementLen == change.Span.AbsoluteIndex)
|
||||||
{
|
{
|
||||||
Span lastDescendant = block.FindLastDescendentSpan();
|
var lastDescendant = block.FindLastDescendentSpan();
|
||||||
if ((lastDescendant == null) && (block is TagHelperBlock))
|
if ((lastDescendant == null) && (block is TagHelperBlock))
|
||||||
{
|
{
|
||||||
TagHelperBlock tagHelperBlock = (TagHelperBlock)block;
|
var tagHelperBlock = (TagHelperBlock)block;
|
||||||
if (tagHelperBlock.SourceEndTag != null)
|
if (tagHelperBlock.SourceEndTag != null)
|
||||||
{
|
{
|
||||||
lastDescendant = tagHelperBlock.SourceEndTag.FindLastDescendentSpan();
|
lastDescendant = tagHelperBlock.SourceEndTag.FindLastDescendentSpan();
|
||||||
|
|
@ -157,7 +164,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Span span = element as Span;
|
var span = element as Span;
|
||||||
if (span.EditHandler.OwnsChange(span, change))
|
if (span.EditHandler.OwnsChange(span, change))
|
||||||
{
|
{
|
||||||
owner = span;
|
owner = span;
|
||||||
|
|
@ -168,11 +175,10 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
|
|
||||||
if (owner == null)
|
if (owner == null)
|
||||||
{
|
{
|
||||||
TagHelperBlock tagHelperNode = root as TagHelperBlock;
|
if (root is TagHelperBlock tagHelperNode)
|
||||||
if (tagHelperNode != null)
|
|
||||||
{
|
{
|
||||||
Block sourceStartTag = tagHelperNode.SourceStartTag;
|
var sourceStartTag = tagHelperNode.SourceStartTag;
|
||||||
Block sourceEndTag = tagHelperNode.SourceEndTag;
|
var sourceEndTag = tagHelperNode.SourceEndTag;
|
||||||
if ((sourceStartTag.Start.AbsoluteIndex <= change.Span.AbsoluteIndex) &&
|
if ((sourceStartTag.Start.AbsoluteIndex <= change.Span.AbsoluteIndex) &&
|
||||||
(sourceStartTag.Start.AbsoluteIndex + sourceStartTag.Length >= change.Span.AbsoluteIndex))
|
(sourceStartTag.Start.AbsoluteIndex + sourceStartTag.Length >= change.Span.AbsoluteIndex))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,209 @@
|
||||||
|
// 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 System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.CodeAnalysis.Razor
|
||||||
|
{
|
||||||
|
public class DefaultRazorIndentationFactsServiceTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void GetDesiredIndentation_ReturnsNull_IfOwningSpanIsCode()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var source = $@"
|
||||||
|
@{{
|
||||||
|
";
|
||||||
|
var syntaxTree = GetSyntaxTree(source);
|
||||||
|
var service = new DefaultRazorIndentationFactsService();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var indentation = service.GetDesiredIndentation(
|
||||||
|
syntaxTree,
|
||||||
|
previousLineEndIndex: GetLineEndIndexForLine(source, 1),
|
||||||
|
getLineContent: line => GetLineContent(source, line),
|
||||||
|
indentSize: 4,
|
||||||
|
tabSize: 1);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(indentation);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetDesiredIndentation_ReturnsNull_IfOwningSpanIsNone()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var customDirective = DirectiveDescriptor.CreateSingleLineDirective("custom");
|
||||||
|
var source = $@"
|
||||||
|
@custom
|
||||||
|
";
|
||||||
|
var syntaxTree = GetSyntaxTree(source, new[] { customDirective });
|
||||||
|
var service = new DefaultRazorIndentationFactsService();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var indentation = service.GetDesiredIndentation(
|
||||||
|
syntaxTree,
|
||||||
|
previousLineEndIndex: GetLineEndIndexForLine(source, 1),
|
||||||
|
getLineContent: line => GetLineContent(source, line),
|
||||||
|
indentSize: 4,
|
||||||
|
tabSize: 1);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(indentation);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetDesiredIndentation_ReturnsCorrectIndentation_ForMarkupWithinCodeBlock()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var source = $@"@{{
|
||||||
|
<div>
|
||||||
|
";
|
||||||
|
var syntaxTree = GetSyntaxTree(source);
|
||||||
|
var service = new DefaultRazorIndentationFactsService();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var indentation = service.GetDesiredIndentation(
|
||||||
|
syntaxTree,
|
||||||
|
previousLineEndIndex: GetLineEndIndexForLine(source, 1),
|
||||||
|
getLineContent: line => GetLineContent(source, line),
|
||||||
|
indentSize: 4,
|
||||||
|
tabSize: 4);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(4, indentation);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetDesiredIndentation_ReturnsCorrectIndentation_ForMarkupWithinDirectiveBlock()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var customDirective = DirectiveDescriptor.CreateRazorBlockDirective("custom");
|
||||||
|
var source = $@"@custom
|
||||||
|
{{
|
||||||
|
<div>
|
||||||
|
}}";
|
||||||
|
var syntaxTree = GetSyntaxTree(source, new[] { customDirective });
|
||||||
|
var service = new DefaultRazorIndentationFactsService();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var indentation = service.GetDesiredIndentation(
|
||||||
|
syntaxTree,
|
||||||
|
previousLineEndIndex: GetLineEndIndexForLine(source, 2),
|
||||||
|
getLineContent: line => GetLineContent(source, line),
|
||||||
|
indentSize: 4,
|
||||||
|
tabSize: 4);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(4, indentation);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetDesiredIndentation_ReturnsCorrectIndentation_ForNestedMarkupWithinCodeBlock()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var source = $@"
|
||||||
|
<div>
|
||||||
|
@{{
|
||||||
|
<span>
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
";
|
||||||
|
var syntaxTree = GetSyntaxTree(source);
|
||||||
|
var service = new DefaultRazorIndentationFactsService();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var indentation = service.GetDesiredIndentation(
|
||||||
|
syntaxTree,
|
||||||
|
previousLineEndIndex: GetLineEndIndexForLine(source, 3),
|
||||||
|
getLineContent: line => GetLineContent(source, line),
|
||||||
|
indentSize: 4,
|
||||||
|
tabSize: 4);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(8, indentation);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetDesiredIndentation_ReturnsCorrectIndentation_ForMarkupWithinCodeBlockInADirectiveBlock()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var customDirective = DirectiveDescriptor.CreateRazorBlockDirective("custom");
|
||||||
|
var source = $@"@custom
|
||||||
|
{{
|
||||||
|
@{{
|
||||||
|
<div>
|
||||||
|
}}
|
||||||
|
}}";
|
||||||
|
var syntaxTree = GetSyntaxTree(source, new[] { customDirective });
|
||||||
|
var service = new DefaultRazorIndentationFactsService();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var indentation = service.GetDesiredIndentation(
|
||||||
|
syntaxTree,
|
||||||
|
previousLineEndIndex: GetLineEndIndexForLine(source, 3),
|
||||||
|
getLineContent: line => GetLineContent(source, line),
|
||||||
|
indentSize: 4,
|
||||||
|
tabSize: 4);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(8, indentation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RazorSyntaxTree GetSyntaxTree(string source, IEnumerable<DirectiveDescriptor> directives = null)
|
||||||
|
{
|
||||||
|
directives = directives ?? Enumerable.Empty<DirectiveDescriptor>();
|
||||||
|
var engine = RazorEngine.CreateDesignTime(builder =>
|
||||||
|
{
|
||||||
|
foreach (var directive in directives)
|
||||||
|
{
|
||||||
|
builder.AddDirective(directive);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var sourceDocument = RazorSourceDocument.Create(source, "test.cshtml");
|
||||||
|
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||||
|
|
||||||
|
engine.Process(codeDocument);
|
||||||
|
|
||||||
|
return codeDocument.GetSyntaxTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetLineContent(string source, int lineIndex)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(source))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var lines = source.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
|
||||||
|
return lines[lineIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetLineEndIndexForLine(string source, int lineIndex)
|
||||||
|
{
|
||||||
|
var absoluteIndex = 0;
|
||||||
|
if (string.IsNullOrEmpty(source))
|
||||||
|
{
|
||||||
|
return absoluteIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
var lines = source.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
|
||||||
|
for (var i = 0; i <= lineIndex; i++)
|
||||||
|
{
|
||||||
|
absoluteIndex += lines[i].Length;
|
||||||
|
|
||||||
|
if (i < lineIndex)
|
||||||
|
{
|
||||||
|
absoluteIndex += Environment.NewLine.Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return absoluteIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue