/// <summary> /// Determines if element is a self-closing element (i.e. like <br /> /// </summary> /// <param name="textProvider">Text provider</param> /// <param name="prefixRange">Text range of the element prefix</param> /// <param name="nameRange">Text range of the element name</param> /// <returns>True if element is a self-closing element.</returns> public bool IsSelfClosing(ITextProvider textProvider, ITextRange prefixRange, ITextRange nameRange) { if (nameRange.Length == 0) return false; string name = textProvider.GetText(nameRange); if (name[0] == '!') return true; // bang tags are always self-closing if (prefixRange.Length == 0) return _defaultProvider.IsSelfClosing(textProvider, nameRange); string prefix = textProvider.GetText(prefixRange); IHtmlClosureProvider provider; ; _providers.TryGetValue(prefix, out provider); var textRangeProvider = provider as IHtmlClosureProviderTextRange; if (textRangeProvider != null) return textRangeProvider.IsSelfClosing(textProvider, nameRange); if (provider != null) return provider.IsSelfClosing(name); return false; }
public override bool Parse(IItemFactory itemFactory, ITextProvider text, ITokenStream stream) { if (stream.Current.Type == TokenType.Identifier && IsModifier(text.GetText(stream.Current.Start, stream.Current.Length))) Modifier = Children.AddCurrentAndAdvance(stream); ParseItem mediaType; if (itemFactory.TryCreateParsedOrDefault(this, text, stream, out mediaType)) { MediaType = mediaType; Children.Add(mediaType); } while (!IsTerminator(text, stream)) { var expression = itemFactory.CreateSpecific<MediaQueryExpression>(this, text, stream); if (expression.Parse(itemFactory, text, stream)) { _Expressions.Add(expression); Children.Add(expression); } else { Children.AddCurrentAndAdvance(stream); } } if (stream.Current.Type == TokenType.Comma) Comma = Children.AddCurrentAndAdvance(stream, SassClassifierType.Punctuation); return Children.Count > 0; }
public string GetName(ITextProvider text) { if (IsValid) return text.GetText(Name.Start, Name.Length); return null; }
public static string ResolvePath(StringValue item, ITextProvider text, DirectoryInfo currentDirectory) { var relativePath = text.GetText(item.Start, item.Length).Trim('\'', '"'); var segments = relativePath.Split('/'); if (segments.Length == 0) return null; var path = currentDirectory.FullName; for (int i = 0; i < (segments.Length - 1); i++) path = Path.Combine(path, segments[i]); var directory = new DirectoryInfo(Path.GetFullPath(path)); if (!directory.Exists) return null; var filename = segments[segments.Length - 1]; if (string.IsNullOrEmpty(Path.GetExtension(filename))) filename += ".scss"; var files = directory.GetFiles("*" + filename); var comparer = StringComparer.OrdinalIgnoreCase; return files.Where(x => comparer.Equals(x.Name, filename) || comparer.Equals(x.Name, "_" + filename)) .Select(x => x.FullName) .FirstOrDefault(); }
public string GetName(ITextProvider text) { if (Fragments.Count == 0 || !Fragments.All(x => x is TokenItem)) return ""; var start = Fragments[0]; var end = Fragments[Fragments.Count - 1]; return text.GetText(start.Start, end.End - start.Start); }
public static bool IsRule(ITextProvider text, ITokenStream stream, string name) { if (stream.Current.Type == TokenType.At) { var nameToken = stream.Peek(1); if (nameToken.Type == TokenType.Identifier) return text.GetText(nameToken.Start, name.Length) == name; } return false; }
public override void Add(ParseItem item, ITextProvider text) { if (item is SassImportDirective) { var directive = item as SassImportDirective; foreach (var file in directive.Files.Where(x => x.IsValid)) Containers.Add(new ImportContainer(directive, IntellisenseManager.Get(file.Document))); } else if (item is XmlDocumentationComment) { var comment = item as XmlDocumentationComment; foreach (var tag in comment.Children.OfType<FileReferenceTag>().Where(x => x.Document != null)) Containers.Add(new ImportContainer(tag, IntellisenseManager.Get(tag.Document))); } else if (item is MixinDefinition) { var definition = item as MixinDefinition; if (definition.Name != null && definition.Name.Name != null) { Parse(new MixinContainer(definition, text), definition.Children, text); var name = text.GetText(definition.Name.Name.Start, definition.Name.Name.Length); _Mixins.Add(definition.End, new MixinCompletionValue(name)); } } else if (item is UserFunctionDefinition) { var definition = item as UserFunctionDefinition; if (definition.Name != null) { Parse(new FunctionContainer(definition, text), definition.Children, text); var name = text.GetText(definition.Name.Start, definition.Name.Length); _Functions.Add(definition.End, new UserFunctionCompletionValue(name)); } } else { base.Add(item, text); } }
static ParseItem CreateDocumentationTag(ComplexItem parent, IItemFactory itemFactory, ITextProvider text, ITokenStream stream) { if (!(parent is XmlDocumentationComment)) return null; if (stream.Current.Type == TokenType.LessThan) { var next = stream.Peek(1); if (next.Type != TokenType.Identifier) return null; switch (text.GetText(next.Start, next.Length)) { case "reference": return new FileReferenceTag(); } } return null; }
protected void AddVariable(VariableDefinition variable, ITextProvider text) { if (variable != null && variable.IsValid) { var variableName = variable.Name; var builder = new StringBuilder(variableName.Length); builder.Append(variableName.Prefix.SourceType == TokenType.Dollar ? '$' : '!'); builder.Append(text.GetText(variableName.Name.Start, variableName.Name.Length)); var name = builder.ToString(); _Variables.Add(variable.Start, new VariableCompletionValue { DisplayText = name, CompletionText = name, Start = variableName.Start, End = variableName.End, Length = variableName.Length }); } }
public override bool Parse(IItemFactory itemFactory, ITextProvider text, ITokenStream stream) { if (stream.Current.Type == TokenType.Identifier && IsValidNamedRange(text.GetText(stream.Current.Start, stream.Current.Length))) { AnimationBegin = Children.AddCurrentAndAdvance(stream, SassClassifierType.Keyword); } else if (stream.Current.Type == TokenType.Number && stream.Peek(1).Type == TokenType.PercentSign) { ParseItem begin = itemFactory.Create<PercentageUnit>(this, text, stream); if (begin.Parse(itemFactory, text, stream)) { AnimationBegin = begin; Children.Add(begin); if (stream.Current.Type == TokenType.Comma) Comma = Children.AddCurrentAndAdvance(stream, SassClassifierType.Punctuation); ParseItem end = itemFactory.Create<PercentageUnit>(this, text, stream); if (end.Parse(itemFactory, text, stream)) { AnimationEnd = end; Children.Add(end); } } } if (AnimationBegin != null) { var block = itemFactory.CreateSpecific<RuleBlock>(this, text, stream); if (block.Parse(itemFactory, text, stream)) { Body = block; Children.Add(block); } } return Children.Count > 0; }
/// <summary> /// Incrementally applies whitespace change to the buffer /// having old and new tokens produced from the 'before formatting' /// and 'after formatting' versions of the same text. /// </summary> /// <param name="textBuffer">Text buffer to apply changes to</param> /// <param name="newTextProvider">Text provider of the text fragment before formatting</param> /// <param name="newTextProvider">Text provider of the formatted text</param> /// <param name="oldTokens">Tokens from the 'before' text fragment</param> /// <param name="newTokens">Tokens from the 'after' text fragment</param> /// <param name="formatRange">Range that is being formatted in the text buffer</param> /// <param name="transactionName">Name of the undo transaction to open</param> /// <param name="selectionTracker">Selection tracker object that will save, track /// <param name="additionalAction">Action to perform after changes are applies by undo unit is not yet closed.</param> /// and restore selection after changes have been applied.</param> public static void ApplyChangeByTokens( ITextBuffer textBuffer, ITextProvider oldTextProvider, ITextProvider newTextProvider, IReadOnlyList<ITextRange> oldTokens, IReadOnlyList<ITextRange> newTokens, ITextRange formatRange, string transactionName, ISelectionTracker selectionTracker, IEditorShell editorShell, Action additionalAction = null) { Debug.Assert(oldTokens.Count == newTokens.Count); if (oldTokens.Count == newTokens.Count) { ITextSnapshot snapshot = textBuffer.CurrentSnapshot; using (CreateSelectionUndo(selectionTracker, editorShell, transactionName)) { using (ITextEdit edit = textBuffer.CreateEdit()) { if (oldTokens.Count > 0) { // Replace whitespace between tokens in reverse so relative positions match int oldEnd = oldTextProvider.Length; int newEnd = newTextProvider.Length; string oldText, newText; for (int i = newTokens.Count - 1; i >= 0; i--) { oldText = oldTextProvider.GetText(TextRange.FromBounds(oldTokens[i].End, oldEnd)); newText = newTextProvider.GetText(TextRange.FromBounds(newTokens[i].End, newEnd)); if (oldText != newText) { edit.Replace(formatRange.Start + oldTokens[i].End, oldEnd - oldTokens[i].End, newText); } oldEnd = oldTokens[i].Start; newEnd = newTokens[i].Start; } newText = newTextProvider.GetText(TextRange.FromBounds(0, newEnd)); edit.Replace(formatRange.Start, oldEnd, newText); } else { string newText = newTextProvider.GetText(TextRange.FromBounds(0, newTextProvider.Length)); edit.Replace(formatRange.Start, formatRange.Length, newText); } edit.Apply(); additionalAction?.Invoke(); } } } }
/// <summary> /// Determines if given element can be implicitly closed like <li> or <td> in HTML" /// </summary> /// <param name="text">Text provider</param> /// <param name="name">Element name</param> /// <returns>True if element can be implictly closed</returns> public bool IsImplicitlyClosed(ITextProvider text, ITextRange name, out string[] containerElementNames) { containerElementNames = null; if (name.Length < _minCharCountImplicitClosing || name.Length > _maxCharCountImplicitClosing) return false; bool found = FindElementName(text, name, _implicitlyClosingElementNameIndex, ignoreCase: true); if (found) { string elementName = text.GetText(name); containerElementNames = GetContainerElements(elementName); } return found; }
private bool IsDestructiveChangeForSeparator( ISensitiveFragmentSeparatorsInfo separatorInfo, IEnumerable<IArtifact> itemsInRange, int start, int oldLength, int newLength, ITextProvider oldText, ITextProvider newText ) { if (separatorInfo == null || (separatorInfo.LeftSeparator.Length == 0 && separatorInfo.RightSeparator.Length == 0)) { return false; } // Find out if one of the existing fragments contains position // and if change damages fragment start or end separators string leftSeparator = separatorInfo.LeftSeparator; string rightSeparator = separatorInfo.RightSeparator; var firstTwoItems = itemsInRange.Take(2).ToList(); var item = firstTwoItems.FirstOrDefault(); // If no items are affected, change is unsafe only if new region contains left side separators. if (item == null) { // Simple optimization for whitespace insertion if (oldLength == 0 && string.IsNullOrWhiteSpace(newText.GetText(new TextRange(start, newLength)))) { return false; } // Take into account that user could have deleted space between existing // { and % or added { to the existing % so extend search range accordingly. int fragmentStart = Math.Max(0, start - leftSeparator.Length + 1); int fragmentEnd = Math.Min(newText.Length, start + newLength + leftSeparator.Length - 1); return newText.IndexOf(leftSeparator, TextRange.FromBounds(fragmentStart, fragmentEnd), true) >= 0; } // Is change completely inside an existing item? if (firstTwoItems.Count == 1 && (item.Contains(start) && item.Contains(start + oldLength))) { // Check that change does not affect item left separator if (TextRange.Contains(item.Start, leftSeparator.Length, start)) { return true; } // Check that change does not affect item right separator. Note that we should not be using // TextRange.Intersect since in case oldLength is zero (like when user is typing right before %} or }}) // TextRange.Intersect will determine that zero-length range intersects with the right separator // which is incorrect. Typing at position 10 does not change separator at position 10. Similarly, // deleting text right before %} or }} does not make change destructive. var htmlToken = item as IHtmlToken; if (htmlToken == null || htmlToken.IsWellFormed) { int rightSeparatorStart = item.End - rightSeparator.Length; if (start + oldLength > rightSeparatorStart) { if (TextRange.Intersect(rightSeparatorStart, rightSeparator.Length, start, oldLength)) { return true; } } } // Touching left separator is destructive too, like when changing {{ to {{@ // Check that change does not affect item left separator (whitespace is fine) if (item.Start + leftSeparator.Length == start) { if (oldLength == 0) { string text = newText.GetText(new TextRange(start, newLength)); if (String.IsNullOrWhiteSpace(text)) { return false; } } return true; } int fragmentStart = item.Start + separatorInfo.LeftSeparator.Length; fragmentStart = Math.Max(fragmentStart, start - separatorInfo.RightSeparator.Length + 1); int changeLength = newLength - oldLength; int fragmentEnd = item.End + changeLength; fragmentEnd = Math.Min(fragmentEnd, start + newLength + separatorInfo.RightSeparator.Length - 1); if (newText.IndexOf(separatorInfo.RightSeparator, TextRange.FromBounds(fragmentStart, fragmentEnd), true) >= 0) { return true; } return false; } return true; }
static bool AllowsExpresion(ITextProvider text, TokenItem statement) { var name = text.GetText(statement.Start, statement.Length); return name == "if" || name == "else if"; }
public string GetSubstringAt(int position, int length) => Text.GetText(new TextRange(position, length));
internal void UpdateValue(ITextProvider textProvider) { if (this.HasValue()) { QuotedValue = textProvider.GetText(ValueToken); int start = ValueToken.Start + (ValueToken.OpenQuote != '\0' ? 1 : 0); int end = ValueToken.CloseQuote != '\0' ? ValueToken.End - 1 : ValueToken.End; int length = end - start; Value = length > 0 ? textProvider.GetText(new TextRange(start, length)) : String.Empty; } else { QuotedValue = String.Empty; Value = String.Empty; } }
public override bool Parse(IItemFactory itemFactory, ITextProvider text, ITokenStream stream) { if (AtRule.IsRule(text, stream, "function")) { Rule = AtRule.CreateParsed(itemFactory, text, stream); if (Rule != null) Children.Add(Rule); if (stream.Current.Type == TokenType.Function) { Name = Children.AddCurrentAndAdvance(stream, SassClassifierType.UserFunctionDefinition); FunctionName = text.GetText(Name.Start, Name.Length); } if (stream.Current.Type == TokenType.OpenFunctionBrace) Children.AddCurrentAndAdvance(stream, SassClassifierType.FunctionBrace); while (!IsArgumentTerminator(stream.Current.Type)) { var argument = itemFactory.CreateSpecific<FunctionArgumentDefinition>(this, text, stream); if (argument == null || !argument.Parse(itemFactory, text, stream)) break; Arguments.Add(argument); Children.Add(argument); } if (stream.Current.Type == TokenType.CloseFunctionBrace) Children.AddCurrentAndAdvance(stream, SassClassifierType.FunctionBrace); var body = new UserFunctionBody(); if (body.Parse(itemFactory, text, stream)) { Body = body; Children.Add(body); } } return Children.Count > 0; }
private static bool IsADestructiveChangeForSeparator( ISensitiveFragmentSeparatorsInfo separatorInfo, IEnumerable <IArtifact> itemsInRange, int start, int oldLength, int newLength, ITextProvider oldText, ITextProvider newText ) { if (separatorInfo == null || (separatorInfo.LeftSeparator.Length == 0 && separatorInfo.RightSeparator.Length == 0)) { return(false); } // Find out if one of the existing fragments contains position // and if change damages fragment start or end separators string leftSeparator = separatorInfo.LeftSeparator; string rightSeparator = separatorInfo.RightSeparator; var firstTwoItems = itemsInRange.Take(2).ToList(); var item = firstTwoItems.FirstOrDefault(); // If no items are affected, change is unsafe only if new region contains left side separators. if (item == null) { // Simple optimization for whitespace insertion if (oldLength == 0 && string.IsNullOrWhiteSpace(newText.GetText(start, newLength))) { return(false); } // Take into account that user could have deleted space between existing // { and % or added { to the existing % so extend search range accordingly. int fragmentStart = Math.Max(0, start - leftSeparator.Length + 1); int fragmentEnd = Math.Min(newText.Length, start + newLength + leftSeparator.Length - 1); return(newText.IndexOf(leftSeparator, fragmentStart, fragmentEnd - fragmentStart, true) >= 0); } // Is change completely inside an existing item? if (firstTwoItems.Count == 1 && (item.Contains(start) && item.Contains(start + oldLength))) { // Check that change does not affect item left separator if (TextRange.Contains(item.Start, leftSeparator.Length, start)) { return(true); } // Check that change does not affect item right separator. Note that we should not be using // TextRange.Intersect since in case oldLength is zero (like when user is typing right before %} or }}) // TextRange.Intersect will determine that zero-length range intersects with the right separator // which is incorrect. Typing at position 10 does not change separator at position 10. Similarly, // deleting text right before %} or }} does not make change destructive. var htmlToken = item as IHtmlToken; if (htmlToken == null || htmlToken.IsWellFormed) { int rightSeparatorStart = item.End - rightSeparator.Length; if (start + oldLength > rightSeparatorStart) { if (TextRange.Intersect(rightSeparatorStart, rightSeparator.Length, start, oldLength)) { return(true); } } } // Touching left separator is destructive too, like when changing {{ to {{@ // Check that change does not affect item left separator (whitespace is fine) if (item.Start + leftSeparator.Length == start) { if (oldLength == 0) { string text = newText.GetText(start, newLength); if (String.IsNullOrWhiteSpace(text)) { return(false); } } return(true); } int fragmentStart = item.Start + separatorInfo.LeftSeparator.Length; fragmentStart = Math.Max(fragmentStart, start - separatorInfo.RightSeparator.Length + 1); int changeLength = newLength - oldLength; int fragmentEnd = item.End + changeLength; fragmentEnd = Math.Min(fragmentEnd, start + newLength + separatorInfo.RightSeparator.Length - 1); if (newText.IndexOf(separatorInfo.RightSeparator, fragmentStart, fragmentEnd - fragmentStart, true) >= 0) { return(true); } return(false); } return(true); }
private static bool CheckRange(int surroundingLength, Func<string, bool> predicate, int rangeStart, int rangeLength, ITextProvider rangeText) { if (rangeLength == 0) return false; var surroundingText = rangeText.GetText(new Microsoft.Web.Core.Text.TextRange( rangeStart, Math.Min(rangeText.Length - rangeStart, rangeLength + surroundingLength) )); return predicate(surroundingText); }
private string GetUserIndentString(ITextProvider textProvider, int position, RFormatOptions options) { for (int i = position - 1; i >= 0; i--) { char ch = textProvider[i]; if (!char.IsWhiteSpace(ch)) { break; } if (ch.IsLineBreak()) { string userIndentString = textProvider.GetText(TextRange.FromBounds(i + 1, position)); int indentSize = IndentBuilder.TextIndentInSpaces(userIndentString, options.TabSize); return IndentBuilder.GetIndentString(indentSize, options.IndentType, options.IndentSize); } } return string.Empty; }
public static bool IsWellKnownFunction(ITextProvider text, ITokenStream stream) { if (stream.Current.Type == TokenType.Function || stream.Current.Type == TokenType.Identifier) { var functionName = text.GetText(stream.Current.Start, stream.Current.Length); return WellKnownFunctions.Contains(functionName); } return false; }
static bool IsPseudoSelector(ITextProvider text, Token token) { switch (token.Type) { case TokenType.Identifier: case TokenType.Function: return WellKnownPseudoSelectors.Contains(text.GetText(token.Start, token.Length)); default: return true; } }
public override bool Parse(IItemFactory itemFactory, ITextProvider text, ITokenStream stream) { if (IsFunctionCall(stream) && IsFunctionNameValid(text, stream)) { Name = Children.AddCurrentAndAdvance(stream, FunctionClassifierType); if (Name != null) FunctionName = text.GetText(Name.Start, Name.Length); if (stream.Current.Type == TokenType.OpenFunctionBrace) OpenBrace = Children.AddCurrentAndAdvance(stream, SassClassifierType.FunctionBrace); ParseArguments(itemFactory, text, stream); if (OpenBrace != null && stream.Current.Type == TokenType.CloseFunctionBrace) CloseBrace = Children.AddCurrentAndAdvance(stream, SassClassifierType.FunctionBrace); } return Children.Count > 0; }
public string GetSubstringAt(int position, int length) { return(_text.GetText(new TextRange(position, length))); }
protected override void OnAttributeParsed(XmlAttribute attribute, ITextProvider text) { if (attribute.Name != null && IsPathAttribute(text.GetText(attribute.Name.Start, attribute.Name.Length))) Filename = attribute; }
private bool IsDestructiveChangeForSeparator(ISensitiveFragmentSeparatorsInfo separatorInfo, IReadOnlyList <T> itemsInRange, int start, int oldLength, int newLength, ITextProvider oldText, ITextProvider newText) { if (separatorInfo == null) { return(false); } if (separatorInfo.LeftSeparator.Length == 0 && separatorInfo.RightSeparator.Length == 0) { return(false); } // Find out if one of the existing fragments contains position // and if change damages fragment start or end separators string leftSeparator = separatorInfo.LeftSeparator; string rightSeparator = separatorInfo.RightSeparator; // If no items are affected, change is unsafe only if new region contains left side separators. if (itemsInRange.Count == 0) { // Simple optimization for whitespace insertion if (oldLength == 0 && String.IsNullOrWhiteSpace(newText.GetText(new TextRange(start, newLength)))) { return(false); } int fragmentStart = Math.Max(0, start - leftSeparator.Length + 1); int fragmentEnd; // Take into account that user could have deleted space between existing // <! and -- or added - to the existing <!- so extend search range accordingly. fragmentEnd = Math.Min(newText.Length, start + newLength + leftSeparator.Length - 1); int fragmentStartPosition = newText.IndexOf(leftSeparator, TextRange.FromBounds(fragmentStart, fragmentEnd), true); if (fragmentStartPosition >= 0) { // We could've found the left separator only in the newly inserted text since we extended the range we examined // by one less than the separator length. Return true, no further checks necessary. return(true); } return(false); } // Is change completely inside an existing item? if (itemsInRange.Count == 1 && (itemsInRange[0].Contains(start) && itemsInRange[0].Contains(start + oldLength))) { // Check that change does not affect item left separator if (TextRange.Contains(itemsInRange[0].Start, leftSeparator.Length, start)) { return(true); } // Check that change does not affect item right separator. Note that we should not be using // TextRange.Intersect since in case oldLength is zero (like when user is typing right before %> or ?>) // TextRange.Intersect will determine that zero-length range intersects with the right separator // which is incorrect. Typing at position 10 does not change separator at position 10. Similarly, // deleting text right before %> or ?> does not make change destructive. IHtmlToken htmlToken = itemsInRange[0] as IHtmlToken; if (htmlToken == null || htmlToken.IsWellFormed) { int rightSeparatorStart = itemsInRange[0].End - rightSeparator.Length; if (start + oldLength > rightSeparatorStart) { if (TextRange.Intersect(rightSeparatorStart, rightSeparator.Length, start, oldLength)) { return(true); } } } // Touching left separator is destructive too, like when changing <% to <%@ // Check that change does not affect item left separator (whitespace is fine) if (itemsInRange[0].Start + leftSeparator.Length == start) { if (oldLength == 0) { string text = newText.GetText(new TextRange(start, newLength)); if (String.IsNullOrWhiteSpace(text)) { return(false); } } return(true); } int fragmentStart = itemsInRange[0].Start + separatorInfo.LeftSeparator.Length; fragmentStart = Math.Max(fragmentStart, start - separatorInfo.RightSeparator.Length + 1); int changeLength = newLength - oldLength; int fragmentEnd = itemsInRange[0].End + changeLength; fragmentEnd = Math.Min(fragmentEnd, start + newLength + separatorInfo.RightSeparator.Length - 1); if (newText.IndexOf(separatorInfo.RightSeparator, TextRange.FromBounds(fragmentStart, fragmentEnd), true) >= 0) { return(true); } return(false); } return(true); }