Merge in 'release/5.0-preview8' changes
This commit is contained in:
commit
4d23ec028e
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// 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;
|
||||
|
|
@ -74,26 +74,27 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
var resultBuilder = new StringBuilder();
|
||||
var previousInsertionPosition = 0;
|
||||
|
||||
var scopeInsertionPositionsVisitor = new FindScopeInsertionPositionsVisitor(stylesheet);
|
||||
var scopeInsertionPositionsVisitor = new FindScopeInsertionEdits(stylesheet);
|
||||
scopeInsertionPositionsVisitor.Visit();
|
||||
foreach (var (currentInsertionPosition, insertionType) in scopeInsertionPositionsVisitor.InsertionPositions)
|
||||
foreach (var edit in scopeInsertionPositionsVisitor.Edits)
|
||||
{
|
||||
resultBuilder.Append(inputText.Substring(previousInsertionPosition, currentInsertionPosition - previousInsertionPosition));
|
||||
|
||||
switch (insertionType)
|
||||
resultBuilder.Append(inputText.Substring(previousInsertionPosition, edit.Position - previousInsertionPosition));
|
||||
previousInsertionPosition = edit.Position;
|
||||
|
||||
switch (edit)
|
||||
{
|
||||
case ScopeInsertionType.Selector:
|
||||
case InsertSelectorScopeEdit _:
|
||||
resultBuilder.AppendFormat("[{0}]", cssScope);
|
||||
break;
|
||||
case ScopeInsertionType.KeyframesName:
|
||||
case InsertKeyframesNameScopeEdit _:
|
||||
resultBuilder.AppendFormat("-{0}", cssScope);
|
||||
break;
|
||||
case DeleteContentEdit deleteContentEdit:
|
||||
previousInsertionPosition += deleteContentEdit.DeleteLength;
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException($"Unknown insertion type: '{insertionType}'");
|
||||
throw new NotImplementedException($"Unknown edit type: '{edit}'");
|
||||
}
|
||||
|
||||
|
||||
previousInsertionPosition = currentInsertionPosition;
|
||||
}
|
||||
|
||||
resultBuilder.Append(inputText.Substring(previousInsertionPosition));
|
||||
|
|
@ -118,19 +119,13 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
return false;
|
||||
}
|
||||
|
||||
private enum ScopeInsertionType
|
||||
private class FindScopeInsertionEdits : Visitor
|
||||
{
|
||||
Selector,
|
||||
KeyframesName,
|
||||
}
|
||||
|
||||
private class FindScopeInsertionPositionsVisitor : Visitor
|
||||
{
|
||||
public List<(int, ScopeInsertionType)> InsertionPositions { get; } = new List<(int, ScopeInsertionType)>();
|
||||
public List<CssEdit> Edits { get; } = new List<CssEdit>();
|
||||
|
||||
private readonly HashSet<string> _keyframeIdentifiers;
|
||||
|
||||
public FindScopeInsertionPositionsVisitor(ComplexItem root) : base(root)
|
||||
public FindScopeInsertionEdits(ComplexItem root) : base(root)
|
||||
{
|
||||
// Before we start, we need to know the full set of keyframe names declared in this document
|
||||
var keyframesIdentifiersVisitor = new FindKeyframesIdentifiersVisitor(root);
|
||||
|
|
@ -146,11 +141,34 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
// ".first child," containing two simple selectors: ".first" and "child"
|
||||
// ".second", containing one simple selector: ".second"
|
||||
// Our goal is to insert immediately after the final simple selector within each selector
|
||||
var lastSimpleSelector = selector.Children.OfType<SimpleSelector>().LastOrDefault();
|
||||
|
||||
// If there's a deep combinator among the sequence of simple selectors, we consider that to signal
|
||||
// the end of the set of simple selectors for us to look at, plus we strip it out
|
||||
var allSimpleSelectors = selector.Children.OfType<SimpleSelector>();
|
||||
var firstDeepCombinator = allSimpleSelectors.FirstOrDefault(s => IsDeepCombinator(s.Text));
|
||||
|
||||
var lastSimpleSelector = allSimpleSelectors.TakeWhile(s => s != firstDeepCombinator).LastOrDefault();
|
||||
if (lastSimpleSelector != null)
|
||||
{
|
||||
InsertionPositions.Add((lastSimpleSelector.AfterEnd, ScopeInsertionType.Selector));
|
||||
Edits.Add(new InsertSelectorScopeEdit { Position = lastSimpleSelector.AfterEnd });
|
||||
}
|
||||
else if (firstDeepCombinator != null)
|
||||
{
|
||||
// For a leading deep combinator, we want to insert the scope attribute at the start
|
||||
// Otherwise the result would be a CSS rule that isn't scoped at all
|
||||
Edits.Add(new InsertSelectorScopeEdit { Position = firstDeepCombinator.Start });
|
||||
}
|
||||
|
||||
// Also remove the deep combinator if we matched one
|
||||
if (firstDeepCombinator != null)
|
||||
{
|
||||
Edits.Add(new DeleteContentEdit { Position = firstDeepCombinator.Start, DeleteLength = firstDeepCombinator.Length });
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsDeepCombinator(string simpleSelectorText)
|
||||
{
|
||||
return string.Equals(simpleSelectorText, "::deep", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
protected override void VisitAtDirective(AtDirective item)
|
||||
|
|
@ -158,7 +176,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
// Whenever we see "@keyframes something { ... }", we want to insert right after "something"
|
||||
if (TryFindKeyframesIdentifier(item, out var identifier))
|
||||
{
|
||||
InsertionPositions.Add((identifier.AfterEnd, ScopeInsertionType.KeyframesName));
|
||||
Edits.Add(new InsertKeyframesNameScopeEdit { Position = identifier.AfterEnd });
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -183,7 +201,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
.Where(x => x.TokenType == CssTokenType.Identifier && _keyframeIdentifiers.Contains(x.Text));
|
||||
foreach (var token in animationNameTokens)
|
||||
{
|
||||
InsertionPositions.Add((token.AfterEnd, ScopeInsertionType.KeyframesName));
|
||||
Edits.Add(new InsertKeyframesNameScopeEdit { Position = token.AfterEnd });
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
|
@ -273,5 +291,23 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class CssEdit
|
||||
{
|
||||
public int Position { get; set; }
|
||||
}
|
||||
|
||||
private class InsertSelectorScopeEdit : CssEdit
|
||||
{
|
||||
}
|
||||
|
||||
private class InsertKeyframesNameScopeEdit : CssEdit
|
||||
{
|
||||
}
|
||||
|
||||
private class DeleteContentEdit : CssEdit
|
||||
{
|
||||
public int DeleteLength { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// 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 Xunit;
|
||||
|
|
@ -75,6 +75,54 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RespectsDeepCombinator()
|
||||
{
|
||||
// Arrange/act
|
||||
var result = RewriteCssCommand.AddScopeToSelectors(@"
|
||||
.first ::deep .second { color: red; }
|
||||
a ::deep b, c ::deep d { color: blue; }
|
||||
", "TestScope");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(@"
|
||||
.first[TestScope] .second { color: red; }
|
||||
a[TestScope] b, c[TestScope] d { color: blue; }
|
||||
", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IgnoresMultipleDeepCombinators()
|
||||
{
|
||||
// Arrange/act
|
||||
var result = RewriteCssCommand.AddScopeToSelectors(@"
|
||||
.first ::deep .second ::deep .third { color:red; }
|
||||
", "TestScope");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(@"
|
||||
.first[TestScope] .second ::deep .third { color:red; }
|
||||
", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RespectsDeepCombinatorWithSpacesAndComments()
|
||||
{
|
||||
// Arrange/act
|
||||
var result = RewriteCssCommand.AddScopeToSelectors(@"
|
||||
.a .b /* comment ::deep 1 */ ::deep /* comment ::deep 2 */ .c /* ::deep */ .d { color: red; }
|
||||
::deep * { color: blue; } /* Leading deep combinator */
|
||||
another ::deep { color: green } /* Trailing deep combinator */
|
||||
", "TestScope");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(@"
|
||||
.a .b[TestScope] /* comment ::deep 1 */ /* comment ::deep 2 */ .c /* ::deep */ .d { color: red; }
|
||||
[TestScope] * { color: blue; } /* Leading deep combinator */
|
||||
another[TestScope] { color: green } /* Trailing deep combinator */
|
||||
", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlesAtBlocks()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -55,17 +55,19 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
return builder.ToString();
|
||||
}
|
||||
|
||||
private string ToBase36(byte[] hash)
|
||||
private static string ToBase36(byte[] hash)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
const string chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||
var dividend = new BigInteger(hash.AsSpan().Slice(0,8).ToArray());
|
||||
while (dividend > 36)
|
||||
const string chars = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
var result = new char[10];
|
||||
var dividend = BigInteger.Abs(new BigInteger(hash.AsSpan().Slice(0, 9).ToArray()));
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
dividend = BigInteger.DivRem(dividend, 36, out var remainder);
|
||||
builder.Insert(0, chars[Math.Abs(((int)remainder))]);
|
||||
result[i] = chars[(int)remainder];
|
||||
}
|
||||
return builder.ToString();
|
||||
|
||||
return new string(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue