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 AugmentQuickInfoSession(IQuickInfoSession session, IList <object> qiContent, out ITrackingSpan applicableToSpan) { applicableToSpan = null; if (session == null || qiContent == null || qiContent.Count > 0 || !WESettings.Instance.Css.ShowBrowserTooltip) { return; } SnapshotPoint?point = session.GetTriggerPoint(_buffer.CurrentSnapshot); if (!point.HasValue) { return; } var tree = CssEditorDocument.FromTextBuffer(_buffer); ParseItem item = tree.StyleSheet.ItemBeforePosition(point.Value.Position); if (item == null || !item.IsValid) { return; } ICssSchemaInstance schema = CssSchemaManager.SchemaManager.GetSchemaForItem(_rootSchema, item); Tuple <ParseItem, ICssCompletionListEntry> tuple = GetEntriesAndPoint(item, point.Value, schema); if (tuple == null) { return; } ParseItem tipItem = tuple.Item1; ICssCompletionListEntry entry = tuple.Item2; if (tipItem == null || entry == null) { return; } var ruleSet = item.FindType <RuleSet>(); //If the selector's full name would require computation (it's nested), compute it and add it to the output if (ruleSet != null && ruleSet.Parent.FindType <RuleSet>() != null) { qiContent.Add(LessDocument.GetLessSelectorName(ruleSet)); } applicableToSpan = _buffer.CurrentSnapshot.CreateTrackingSpan(tipItem.Start, tipItem.Length, SpanTrackingMode.EdgeNegative); string syntax = entry.GetSyntax(schema.Version); string b = entry.GetAttribute("browsers"); if (string.IsNullOrEmpty(b) && tipItem.Parent != null && tipItem.Parent is Declaration) { b = schema.GetProperty(((Declaration)tipItem.Parent).PropertyName.Text).GetAttribute("browsers"); if (string.IsNullOrEmpty(syntax)) { syntax = tipItem.Text; } } if (!string.IsNullOrEmpty(syntax)) { //var example = CreateExample(syntax); qiContent.Add("Example: " + syntax); } Dictionary <string, string> browsers = GetBrowsers(b); qiContent.Add(CreateBrowserList(browsers)); }
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); } }