151 lines
5.2 KiB
C#
151 lines
5.2 KiB
C#
// 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.ComponentModel.Composition;
|
|
using Microsoft.AspNetCore.Razor.Language;
|
|
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
|
using Microsoft.VisualStudio.Text;
|
|
using Span = Microsoft.AspNetCore.Razor.Language.Legacy.Span;
|
|
|
|
namespace Microsoft.VisualStudio.Editor.Razor
|
|
{
|
|
[System.Composition.Shared]
|
|
[Export(typeof(RazorIndentationFactsService))]
|
|
internal class DefaultRazorIndentationFactsService : RazorIndentationFactsService
|
|
{
|
|
// This method dives down a syntax tree looking for open curly braces, every time
|
|
// it finds one it increments its indent until it finds the provided "line".
|
|
//
|
|
// Examples:
|
|
// @{
|
|
// <strong>Hello World</strong>
|
|
// }
|
|
// Asking for desired indentation of the @{ or } lines should result in a desired indentation of 4.
|
|
//
|
|
// <div>
|
|
// @{
|
|
// <strong>Hello World</strong>
|
|
// }
|
|
// </div>
|
|
// Asking for desired indentation of the @{ or } lines should result in a desired indentation of 8.
|
|
public override int? GetDesiredIndentation(
|
|
RazorSyntaxTree syntaxTree,
|
|
ITextSnapshot syntaxTreeSnapshot,
|
|
ITextSnapshotLine line,
|
|
int indentSize,
|
|
int tabSize)
|
|
{
|
|
if (syntaxTree == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(syntaxTree));
|
|
}
|
|
|
|
if (syntaxTreeSnapshot == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(syntaxTreeSnapshot));
|
|
}
|
|
|
|
if (line == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(line));
|
|
}
|
|
|
|
if (indentSize < 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(indentSize));
|
|
}
|
|
|
|
if (tabSize < 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(tabSize));
|
|
}
|
|
|
|
var previousLineEndIndex = GetPreviousLineEndIndex(syntaxTreeSnapshot, line);
|
|
var simulatedChange = new SourceChange(previousLineEndIndex, 0, string.Empty);
|
|
var owningSpan = syntaxTree.Root.LocateOwner(simulatedChange);
|
|
if (owningSpan == null || 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;
|
|
SyntaxTreeNode owningChild = owningSpan;
|
|
while (owningChild.Parent != null)
|
|
{
|
|
var owningParent = owningChild.Parent;
|
|
for (var i = 0; i < owningParent.Children.Count; i++)
|
|
{
|
|
var currentChild = owningParent.Children[i];
|
|
if (IsCSharpOpenCurlyBrace(currentChild))
|
|
{
|
|
var lineText = line.Snapshot.GetLineFromLineNumber(currentChild.Start.LineIndex).GetText();
|
|
desiredIndentation = GetIndentLevelOfLine(lineText, tabSize) + indentSize;
|
|
}
|
|
|
|
if (currentChild == owningChild)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (desiredIndentation.HasValue)
|
|
{
|
|
return desiredIndentation;
|
|
}
|
|
|
|
owningChild = owningParent;
|
|
}
|
|
|
|
// Couldn't determine indentation
|
|
return null;
|
|
}
|
|
|
|
// Internal for testing
|
|
internal int GetIndentLevelOfLine(string line, int tabSize)
|
|
{
|
|
var indentLevel = 0;
|
|
|
|
foreach (var c in line)
|
|
{
|
|
if (!char.IsWhiteSpace(c))
|
|
{
|
|
break;
|
|
}
|
|
else if (c == '\t')
|
|
{
|
|
indentLevel += tabSize;
|
|
}
|
|
else
|
|
{
|
|
indentLevel++;
|
|
}
|
|
}
|
|
|
|
return indentLevel;
|
|
}
|
|
|
|
// Internal for testing
|
|
internal static int GetPreviousLineEndIndex(ITextSnapshot syntaxTreeSnapshot, ITextSnapshotLine line)
|
|
{
|
|
var previousLine = line.Snapshot.GetLineFromLineNumber(line.LineNumber - 1);
|
|
var trackingPoint = previousLine.Snapshot.CreateTrackingPoint(previousLine.End, PointTrackingMode.Negative);
|
|
var previousLineEndIndex = trackingPoint.GetPosition(syntaxTreeSnapshot);
|
|
return previousLineEndIndex;
|
|
}
|
|
|
|
// Internal for testing
|
|
internal static bool IsCSharpOpenCurlyBrace(SyntaxTreeNode currentChild)
|
|
{
|
|
return currentChild is Span currentSpan &&
|
|
currentSpan.Tokens.Count == 1 &&
|
|
currentSpan.Tokens[0] is CSharpToken symbol &&
|
|
symbol.Type == CSharpTokenType.LeftBrace;
|
|
}
|
|
}
|
|
}
|