public void TestMixinExpansion() { var lessFactory = CssParserLocator.FindComponent(ContentTypeManager.GetContentType(LessContentTypeDefinition.LessContentType)); var lessCode = @"a { .myMixin(@p) { b, code { } } }"; var lessDoc = lessFactory.CreateParser().Parse(lessCode, false); var lessBlocks = new CssItemAggregator <RuleSet>(true) { (RuleSet rs) => rs }.Crawl(lessDoc).ToList(); // Remove all but the deepest blocks while (0 < lessBlocks.RemoveAll(c => lessBlocks.Any(c.IsParentOf))) { ; } var literalExpansions = lessBlocks.SelectMany(rs => LessDocument.GetSelectorNames(rs, LessMixinAction.Literal)).ToList(); literalExpansions.Should().Equal(new[] { "a .myMixin(@p) b", "a .myMixin(@p) code" }); var skipExpansions = lessBlocks.SelectMany(rs => LessDocument.GetSelectorNames(rs, LessMixinAction.Skip)).ToList(); skipExpansions.Should().BeEmpty(); var nestedExpansions = lessBlocks.SelectMany(rs => LessDocument.GetSelectorNames(rs, LessMixinAction.NestedOnly)).ToList(); nestedExpansions.Should().Equal(new[] { "«mixin .myMixin» b", "«mixin .myMixin» code" }); }
public void TestMixinExpansion() { var scssFactory = CssParserLocator.FindComponent(ContentTypeManager.GetContentType(ScssContentTypeDefinition.ScssContentType)); var scssCode = @"a { @mixin myMixin($p) { b, code { width: $p+10+px; } } @include myMixin(1) } "; var scssDoc = scssFactory.CreateParser().Parse(scssCode, false); var scssBlocks = new CssItemAggregator <RuleSet>(true) { (RuleSet rs) => rs }.Crawl(scssDoc).ToList(); // Remove all but the deepest blocks while (0 < scssBlocks.RemoveAll(c => scssBlocks.Any(c.IsParentOf))) { ; } var literalExpansions = scssBlocks.SelectMany(rs => ScssDocument.GetSelectorNames(rs, ScssMixinAction.Literal)).ToList(); literalExpansions.Should().Equal(new[] { "a b", "a code" }); var skipExpansions = scssBlocks.SelectMany(rs => ScssDocument.GetSelectorNames(rs, ScssMixinAction.Skip)).ToList(); skipExpansions.Should().BeEmpty(); }
private static IEnumerable<ParseItem> GetColors(CssTree tree, SnapshotSpan span) { ParseItem complexItem = tree.StyleSheet.ItemFromRange(span.Start, span.Length); if (complexItem == null || (!(complexItem is AtDirective) && !(complexItem is RuleBlock) && !(complexItem is CssVariableDeclaration) && !(complexItem is FunctionArgument))) return Enumerable.Empty<ParseItem>(); var colorCrawler = new CssItemAggregator<ParseItem>(filter: e => e.AfterEnd > span.Start && e.Start < span.End) { (HexColorValue h) => h, (FunctionColor c) => c, (TokenItem i) => (i.PreviousSibling == null || (i.PreviousSibling.Text != "@" && i.PreviousSibling.Text != "$")) // Ignore variable names that happen to be colors && i.TokenType == CssTokenType.Identifier && (i.FindType<Declaration>() != null || i.FindType<CssExpression>() != null) // Ignore classnames that happen to be colors && Color.FromName(i.Text).IsNamedColor ? i : null }; return colorCrawler.Crawl(complexItem).Where(o => o != null); }
public void Reparse(string text) { lock (ParseSync) { if (string.Equals(text, _lastParsedText, StringComparison.Ordinal)) { return; } var parser = CreateParser(); var parseResult = parser.Parse(text, false); Rules = new CssItemAggregator <IStylingRule>(true) { (RuleSet rs) => CssRule.From(_file, text, rs, this) } .Crawl(parseResult) .Where(x => x != null) .ToList(); _lastParsedText = text; } }
public void Reparse(string text) { lock (ParseSync) { if (string.Equals(text, _lastParsedText, StringComparison.Ordinal)) { return; } var parser = CreateParser(); var parseResult = parser.Parse(text, false); Rules = new CssItemAggregator<IStylingRule>(true) { (RuleSet rs) => CssRule.From(_file, text, rs, this) } .Crawl(parseResult) .Where(x => x != null) .ToList(); _lastParsedText = text; } }
public void TestMixinExpansion() { var lessFactory = CssParserLocator.FindComponent(ContentTypeManager.GetContentType(LessContentTypeDefinition.LessContentType)); var lessCode = @"a { .myMixin(@p) { b, code { } } }"; var lessDoc = lessFactory.CreateParser().Parse(lessCode, false); var lessBlocks = new CssItemAggregator<RuleSet>(true) { (RuleSet rs) => rs }.Crawl(lessDoc).ToList(); // Remove all but the deepest blocks while (0 < lessBlocks.RemoveAll(c => lessBlocks.Any(c.IsParentOf))) ; var literalExpansions = lessBlocks.SelectMany(rs => LessDocument.GetSelectorNames(rs, LessMixinAction.Literal)).ToList(); literalExpansions.Should().Equal(new[] { "a .myMixin(@p) b", "a .myMixin(@p) code" }); var skipExpansions = lessBlocks.SelectMany(rs => LessDocument.GetSelectorNames(rs, LessMixinAction.Skip)).ToList(); skipExpansions.Should().BeEmpty(); var nestedExpansions = lessBlocks.SelectMany(rs => LessDocument.GetSelectorNames(rs, LessMixinAction.NestedOnly)).ToList(); nestedExpansions.Should().Equal(new[] { "«mixin .myMixin» b", "«mixin .myMixin» code" }); }
public async Task SelectorExpansionTest() { #region LESS sources var testSources = new[]{ @" @media all { a { @media all { @media all { b { color: goldenrod; em { color: goldenrod; } } } } } } ", @" // Taken from http://blog.slaks.net/2013-09-29/less-css-secrets-of-the-ampersand/ a { color: blue; &:hover { color: green; } } form a { color: purple; body.QuietMode & { color: black; } } .quoted-source { background: #fcc; blockquote& { background: #fdc; } } .btn.btn-primary.btn-lg[disabled] { & + & + & { margin-left: 10px; } } p, blockquote, ul, li { border-top: 1px solid gray; & + & { border-top: 0; } } ", @" // Taken from https://github.com/less/less.js/blob/master/test/less/selectors.less h1, h2, h3 { a, p { &:hover { color: red; } } } #all { color: blue; } #the { color: blue; } #same { color: blue; } ul, li, div, q, blockquote, textarea { margin: 0; } td { margin: 0; padding: 0; } td, input { line-height: 1em; } a { color: red; &:hover { color: blue; } div & { color: green; } p & span { color: yellow; } } .foo { .bar, .baz { & .qux { display: block; } .qux & { display: inline; } .qux& { display: inline-block; } .qux & .biz { display: none; } } } .b { &.c { .a& { color: red; } } } .b { .c & { &.a { color: red; } } } .p { .foo &.bar { color: red; } } .p { .foo&.bar { color: red; } } .foo { .foo + & { background: amber; } & + & { background: amber; } } .foo, .bar { & + & { background: amber; } } .foo, .bar { a, b { & > & { background: amber; } } } .other ::fnord { color: red } .other::fnord { color: red } .other { ::bnord {color: red } &::bnord {color: red } } ", @"// Taken from https://github.com/less/less.js/blob/master/test/less/rulesets.less #first > .one { > #second .two > #deux { width: 50%; #third { &:focus { color: black; #fifth { > #sixth { .seventh #eighth { + #ninth { color: purple; } } } } } height: 100%; } #fourth, #five, #six { color: #110000; .seven, .eight > #nine { border: 1px solid black; } #ten { color: red; } } } font-size: 2em; } "}; #endregion var lessFactory = CssParserLocator.FindComponent(ContentTypeManager.GetContentType(LessContentTypeDefinition.LessContentType)); foreach (var lessCode in testSources) { var cssCode = await new LessCompiler().CompileSourceAsync(lessCode, ".less"); var lessDoc = lessFactory.CreateParser().Parse(lessCode, false); var cssDoc = new CssParser().Parse(cssCode, false); var cssSelectors = new CssItemAggregator<string>(false) { (Selector s) => CssExtensions.SelectorText(s) }.Crawl(cssDoc); var lessSelectors = new CssItemAggregator<RuleSet>(true) { (RuleSet rs) => rs }.Crawl(lessDoc) .Where(rs => rs.Block.Declarations.Any()) // Skip selectors that don't have any rules; these won't end up in the CSS .SelectMany(rs => LessDocument.GetSelectorNames(rs, LessMixinAction.Literal)) .ToList(); lessSelectors.Should().Equal(cssSelectors); } }
public void TestMixinExpansion() { var lessCode = @" a { .myMixin(@p) { b, code { } } }"; var lessDoc = new LessParser().Parse(lessCode, false); var lessBlocks = new CssItemAggregator<RuleSet>(true) { (RuleSet rs) => rs }.Crawl(lessDoc).ToList(); // Remove all but the deepest blocks while (0 < lessBlocks.RemoveAll(c => lessBlocks.Any(c.IsParentOf))) ; var literalExpansions = lessBlocks.SelectMany(rs => LessDocument.GetSelectorNames(rs, LessMixinAction.Literal)).ToList(); CollectionAssert.AreEqual(new[] { "a .myMixin(@p) b", "a .myMixin(@p) code" }, literalExpansions); var skipExpansions = lessBlocks.SelectMany(rs => LessDocument.GetSelectorNames(rs, LessMixinAction.Skip)).ToList(); CollectionAssert.AreEqual(new string[0], skipExpansions); var nestedExpansions = lessBlocks.SelectMany(rs => LessDocument.GetSelectorNames(rs, LessMixinAction.NestedOnly)).ToList(); CollectionAssert.AreEqual(new[] { "«mixin .myMixin» b", "«mixin .myMixin» code" }, nestedExpansions); }
public async Task SelectorExpansionTest() { #region LESS sources var testSources = new[] { @" @media all { a { @media all { @media all { b { color: goldenrod; em { color: goldenrod; } } } } } } ", @" // Taken from http://blog.slaks.net/2013-09-29/less-css-secrets-of-the-ampersand/ a { color: blue; &:hover { color: green; } } form a { color: purple; body.QuietMode & { color: black; } } .quoted-source { background: #fcc; blockquote& { background: #fdc; } } .btn.btn-primary.btn-lg[disabled] { & + & + & { margin-left: 10px; } } p, blockquote, ul, li { border-top: 1px solid gray; & + & { border-top: 0; } } ", @" // Taken from https://github.com/less/less.js/blob/master/test/less/selectors.less h1, h2, h3 { a, p { &:hover { color: red; } } } #all { color: blue; } #the { color: blue; } #same { color: blue; } ul, li, div, q, blockquote, textarea { margin: 0; } td { margin: 0; padding: 0; } td, input { line-height: 1em; } a { color: red; &:hover { color: blue; } div & { color: green; } p & span { color: yellow; } } .foo { .bar, .baz { & .qux { display: block; } .qux & { display: inline; } .qux& { display: inline-block; } .qux & .biz { display: none; } } } .b { &.c { .a& { color: red; } } } .b { .c & { &.a { color: red; } } } .p { .foo &.bar { color: red; } } .p { .foo&.bar { color: red; } } .foo { .foo + & { background: amber; } & + & { background: amber; } } .foo, .bar { & + & { background: amber; } } .foo, .bar { a, b { & > & { background: amber; } } } .other ::fnord { color: red } .other::fnord { color: red } .other { ::bnord {color: red } &::bnord {color: red } } ", @"// Taken from https://github.com/less/less.js/blob/master/test/less/rulesets.less #first > .one { > #second .two > #deux { width: 50%; #third { &:focus { color: black; #fifth { > #sixth { .seventh #eighth { + #ninth { color: purple; } } } } } height: 100%; } #fourth, #five, #six { color: #110000; .seven, .eight > #nine { border: 1px solid black; } #ten { color: red; } } } font-size: 2em; } " }; #endregion var lessFactory = CssParserLocator.FindComponent(ContentTypeManager.GetContentType(LessContentTypeDefinition.LessContentType)); foreach (var lessCode in testSources) { var cssCode = await new LessCompiler().CompileSourceAsync(lessCode, ".less"); var lessDoc = lessFactory.CreateParser().Parse(lessCode, false); var cssDoc = new CssParser().Parse(cssCode, false); var cssSelectors = new CssItemAggregator <string>(false) { (Selector s) => CssExtensions.SelectorText(s) }.Crawl(cssDoc); var lessSelectors = new CssItemAggregator <RuleSet>(true) { (RuleSet rs) => rs }.Crawl(lessDoc) .Where(rs => rs.Block.Declarations.Any()) // Skip selectors that don't have any rules; these won't end up in the CSS .SelectMany(rs => LessDocument.GetSelectorNames(rs, LessMixinAction.Literal)) .ToList(); lessSelectors.Should().Equal(cssSelectors); } }
public void TestMixinExpansion() { var scssFactory = CssParserLocator.FindComponent(ContentTypeManager.GetContentType(ScssContentTypeDefinition.ScssContentType)); var scssCode = @"a { @mixin myMixin($p) { b, code { width: $p+10+px; } } @include myMixin(1) } "; var scssDoc = scssFactory.CreateParser().Parse(scssCode, false); var scssBlocks = new CssItemAggregator<RuleSet>(true) { (RuleSet rs) => rs }.Crawl(scssDoc).ToList(); // Remove all but the deepest blocks while (0 < scssBlocks.RemoveAll(c => scssBlocks.Any(c.IsParentOf))) ; var literalExpansions = scssBlocks.SelectMany(rs => ScssDocument.GetSelectorNames(rs, ScssMixinAction.Literal)).ToList(); literalExpansions.Should().Equal(new[] { "a b", "a code" }); var skipExpansions = scssBlocks.SelectMany(rs => ScssDocument.GetSelectorNames(rs, ScssMixinAction.Skip)).ToList(); skipExpansions.Should().BeEmpty(); }