/// <summary> /// Returns text representation for tag from <paramref name="expression"/>. /// </summary> /// <remarks> /// Getting text representation of a tag is widely used operation that could appear on the hot path. /// To avoid performance and memory problems this function should be used instead of more generic function of getting text /// representation, like <see cref="ReformatterHelper.GetFormattedText"/>. /// </remarks> public static string GetTagText(this ITaggedTemplateExpression expression) { Contract.Requires(expression != null); // There is two common cases for tagged expression in the system: // 1) custom factory, like p``, d``, f`` or // 2) string interpolation, like `` // This implementation is optimized for them. if (expression.Tag == null) { // Second case: this is `` return(string.Empty); } var identifier = expression.Tag as IIdentifier; if (identifier != null) { // First case: tag is just a function. return(identifier.Text); } // Falling back to generic implementation. return(expression.Tag.GetFormattedText()); }
/// <summary> /// Method that converts tagged ast node into <see cref="Expression"/>. /// </summary> public Expression ConvertInterpolation(ITaggedTemplateExpression source, FunctionScope escapes, QualifierSpaceId currentQualifierSpaceId) { (var interpolationKind, ILiteralExpression literal, ITemplateLiteralFragment head, INodeArray <ITemplateSpan> templateSpans) = source; var tagTemplate = new ProcessedTagTemplateExpression(source, interpolationKind, literal, head, templateSpans); switch (interpolationKind) { case InterpolationKind.PathInterpolation: return(ConvertPathInterpolation(ref tagTemplate, escapes, currentQualifierSpaceId)); case InterpolationKind.FileInterpolation: return(ConvertFileInterpolation(ref tagTemplate, escapes, currentQualifierSpaceId)); case InterpolationKind.DirectoryInterpolation: return(ConvertDirectoryInterpolation(ref tagTemplate, escapes, currentQualifierSpaceId)); case InterpolationKind.PathAtomInterpolation: return(ConvertPathAtomInterpolation(ref tagTemplate, escapes, currentQualifierSpaceId)); case InterpolationKind.RelativePathInterpolation: return(ConvertRelativePathInterpolation(ref tagTemplate, escapes, currentQualifierSpaceId)); default: throw Contract.AssertFailure(I($"Unknown interpolation kind '{interpolationKind}'.")); } }
private static int FitsOnOneLine(ITaggedTemplateExpression expression, int remainingSpace) { var space = FitsOnOneLine(expression.Tag, remainingSpace); if (space >= 0) { // This is a recursion on the pretty printer which is not nice, but it is the simplest for now space -= expression.GetFormattedText().Length; } return(space); }
/// <summary> /// Returns true if a given node is a special template expression like p``, d``, f``, a`` etc. /// </summary> public static bool IsWellKnownTemplateExpression(this ITaggedTemplateExpression node, out string name) { Contract.Requires(node != null); if (node.Tag.Kind == SyntaxKind.Identifier) { name = node.Tag.Cast <IIdentifier>().Text; return(Scanner.IsPathLikeInterpolationFactory(name)); } name = null; return(false); }
/// <summary> /// Checks if this tagged template is a path interpolation such that it can contains path separators /// </summary> public static bool IsPathInterpolation(this ITaggedTemplateExpression taggedTemplateExpression) { switch (taggedTemplateExpression.GetInterpolationKind()) { case InterpolationKind.FileInterpolation: case InterpolationKind.DirectoryInterpolation: case InterpolationKind.PathInterpolation: case InterpolationKind.RelativePathInterpolation: return(true); default: return(false); } }
public ProcessedTagTemplateExpression( ITaggedTemplateExpression taggedTemplate, InterpolationKind kind, ILiteralExpression literal, ITemplateLiteralFragment head, INodeArray <ITemplateSpan> templateSpans) { Contract.Requires(taggedTemplate != null); Contract.Requires( kind == InterpolationKind.Unknown || (literal != null || (head != null && templateSpans != null)), "If interpolation is a well-known factory method, then Literal or Head+Templates should be valid."); TaggedTemplate = taggedTemplate; Kind = kind; Literal = literal; Head = head; TemplateSpans = templateSpans; }
/// <summary> /// Returns kind of interpolated string. /// </summary> public static InterpolationKind GetInterpolationKind(this ITaggedTemplateExpression taggedTemplateExpression) { Contract.Requires(taggedTemplateExpression != null); string tag = taggedTemplateExpression.GetTagText(); if (string.IsNullOrEmpty(tag)) { return(InterpolationKind.StringInterpolation); } if (tag.Length != 1) { return(InterpolationKind.Unknown); } switch (tag[0]) { case Names.PathInterpolationFactory: return(InterpolationKind.PathInterpolation); case Names.FileInterpolationFactory: return(InterpolationKind.FileInterpolation); case Names.DirectoryInterpolationFactory: return(InterpolationKind.DirectoryInterpolation); case Names.RelativePathInterpolationFactory: return(InterpolationKind.RelativePathInterpolation); case Names.PathAtomInterpolationFactory: return(InterpolationKind.PathAtomInterpolation); default: return(InterpolationKind.Unknown); } }
private static int CompareTaggedTemplateExpression(ITaggedTemplateExpression left, ITaggedTemplateExpression right) { // Standard left,right and null checks if (left == null && right == null) { return(0); } if (left == null) { return(1); } if (right == null) { return(-1); } // Compare tag var result = CompareNodeAsText(left.Tag, right.Tag); if (result != 0) { return(result); } // Special case check for path literal. var interpolationKind = left.GetInterpolationKind(); bool usePathCompareSemantics = false; switch (interpolationKind) { case InterpolationKind.PathInterpolation: case InterpolationKind.FileInterpolation: case InterpolationKind.DirectoryInterpolation: case InterpolationKind.RelativePathInterpolation: usePathCompareSemantics = true; break; } var leftIsLiteral = IsTemplateLiteral(left.TemplateExpression); var rightIsLiteral = IsTemplateLiteral(right.TemplateExpression); ITemplateExpression leftTemplate; ITemplateExpression rightTemplate; if (leftIsLiteral) { if (rightIsLiteral) { return(CompareStringLiteralExpression(left.TemplateExpression.Cast <ILiteralExpression>(), right.TemplateExpression.Cast <ILiteralExpression>(), usePathCompareSemantics)); } leftTemplate = new TemplateExpression() { Head = new TemplateLiteralFragment() { Text = left.TemplateExpression.GetTemplateText() }, TemplateSpans = NodeArray <ITemplateSpan> .Empty }; rightTemplate = right.TemplateExpression.Cast <ITemplateExpression>(); } else if (rightIsLiteral) { leftTemplate = left.TemplateExpression.Cast <ITemplateExpression>(); rightTemplate = new TemplateExpression() { Head = new TemplateLiteralFragment() { Text = right.TemplateExpression.GetTemplateText() }, TemplateSpans = NodeArray <ITemplateSpan> .Empty }; } else { leftTemplate = left.TemplateExpression.Cast <ITemplateExpression>(); rightTemplate = right.TemplateExpression.Cast <ITemplateExpression>(); } return(CompareTemplateExpression(leftTemplate, rightTemplate, usePathCompareSemantics)); }
/// <summary> /// Checks if this tagged template is a path interpolation such that it can contains path separators /// </summary> public static bool IsPathInterpolation(this ITaggedTemplateExpression taggedTemplateExpression) { return(InterpolationUtilities.IsPathInterpolation(taggedTemplateExpression)); }
/// <summary> /// Deconstructs a template expression. /// </summary> /// <remarks> /// "pattern matches" a tagged template expression into two cases: /// 1. Literal case like <code>p`string literal`</code> (in this case <paramref name="literal"/> would not be null). /// 2. Template expression case like <code>p`{foo}</code> (in this case <paramref name="head"/> and <paramref name="templateSpans"/> are not null). /// </remarks> public static void Deconstruct( [CanBeNull] this ITaggedTemplateExpression node, out InterpolationKind kind, out ILiteralExpression literal, out ITemplateLiteralFragment head, out INodeArray <ITemplateSpan> templateSpans) { kind = InterpolationKind.Unknown; literal = null; head = null; templateSpans = null; if (node == null) { return; } if (node.Tag.Kind != SyntaxKind.Identifier) { // Looks like the tagged expression is invalid. return; } var text = node.Tag.Cast <IIdentifier>().Text; kind = GetInterpolationKind(text); if (kind == InterpolationKind.Unknown) { return; } literal = node.TemplateExpression.As <ILiteralExpression>(); if (literal == null) { // This is another case: tagged template actually has template expressions. var template = node.TemplateExpression.Cast <ITemplateExpression>(); head = template.Head; templateSpans = template?.TemplateSpans; Contract.Assert(head != null); Contract.Assert(templateSpans != null); } InterpolationKind GetInterpolationKind(string factoryName) { if (factoryName.Length == 0) { return(InterpolationKind.StringInterpolation); } var c = factoryName[0]; switch (c) { case Names.PathInterpolationFactory: return(InterpolationKind.PathInterpolation); case Names.DirectoryInterpolationFactory: return(InterpolationKind.DirectoryInterpolation); case Names.FileInterpolationFactory: return(InterpolationKind.FileInterpolation); case Names.RelativePathInterpolationFactory: return(InterpolationKind.RelativePathInterpolation); case Names.PathAtomInterpolationFactory: return(InterpolationKind.PathAtomInterpolation); default: return(InterpolationKind.Unknown); } } }