This repository was archived by the owner on Dec 19, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 221
/
Copy pathDefaultRazorIndentationFactsService.cs
149 lines (131 loc) · 5.01 KB
/
DefaultRazorIndentationFactsService.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// 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.AspNetCore.Razor.Language.Syntax;
using Microsoft.VisualStudio.Text;
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 owner = syntaxTree.Root.LocateOwner(simulatedChange);
if (owner == null || owner.IsCodeSpanKind())
{
// 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;
while (owner.Parent != null)
{
var children = owner.Parent.ChildNodes();
for (var i = 0; i < children.Count; i++)
{
var currentChild = children[i];
if (IsCSharpOpenCurlyBrace(currentChild))
{
var lineText = line.Snapshot.GetLineFromLineNumber(currentChild.GetSourceLocation(syntaxTree.Source).LineIndex).GetText();
desiredIndentation = GetIndentLevelOfLine(lineText, tabSize) + indentSize;
}
if (currentChild == owner)
{
break;
}
}
if (desiredIndentation.HasValue)
{
return desiredIndentation;
}
owner = owner.Parent;
}
// 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(SyntaxNode node)
{
var children = node.ChildNodes();
return children.Count == 1 &&
children[0].IsToken &&
children[0].Kind == SyntaxKind.LeftBrace;
}
}
}