public void ParseModelKeyword_HandlesSingleInstance() { // Arrange var document = "@model Foo"; var factory = SpanFactory.CreateCsHtml(); var errors = new List<RazorError>(); var expectedSpans = new Span[] { factory.EmptyHtml(), factory.CodeTransition(SyntaxConstants.TransitionString) .Accepts(AcceptedCharacters.None), factory.MetaCode("model ") .Accepts(AcceptedCharacters.None), factory.Code(" Foo") .As(new ModelChunkGenerator("Foo")) .Accepts(AcceptedCharacters.AnyExceptNewline), factory.EmptyHtml() }; // Act var spans = ParseDocument(document, errors); // Assert Assert.Equal(expectedSpans, spans); Assert.Empty(errors); }
internal int CalculatePadding(Span target, int generatedStart) { if (target == null) { throw new ArgumentNullException("target"); } int padding; padding = CollectSpacesAndTabs(target, _host.TabSize) - generatedStart; // if we add generated text that is longer than the padding we wanted to insert we have no recourse and we have to skip padding // example: // Razor code at column zero: @somecode() // Generated code will be: // In design time: __o = somecode(); // In Run time: Write(somecode()); // // In both cases the padding would have been 1 space to remote the space the @ symbol takes, which will be smaller than the 6 // chars the hidden generated code takes. if (padding < 0) { padding = 0; } return padding; }
public virtual bool OwnsChange(Span target, TextChange change) { int end = target.Start.AbsoluteIndex + target.Length; int changeOldEnd = change.OldPosition + change.OldLength; return change.OldPosition >= target.Start.AbsoluteIndex && (changeOldEnd < end || (changeOldEnd == end && AcceptedCharacters != AcceptedCharacters.None)); }
public override void VisitSpan(Span span) { // We're only interested in spans with an AddOrRemoveTagHelperChunkGenerator. if (span.ChunkGenerator is AddOrRemoveTagHelperChunkGenerator) { var chunkGenerator = (AddOrRemoveTagHelperChunkGenerator)span.ChunkGenerator; var directive = chunkGenerator.RemoveTagHelperDescriptors ? TagHelperDirectiveType.RemoveTagHelper : TagHelperDirectiveType.AddTagHelper; var directiveDescriptor = new TagHelperDirectiveDescriptor( chunkGenerator.LookupText, span.Start, directive); _directiveDescriptors.Add(directiveDescriptor); } else if (span.ChunkGenerator is TagHelperPrefixDirectiveChunkGenerator) { var chunkGenerator = (TagHelperPrefixDirectiveChunkGenerator)span.ChunkGenerator; var directiveDescriptor = new TagHelperDirectiveDescriptor( chunkGenerator.Prefix, span.Start, TagHelperDirectiveType.TagHelperPrefix); _directiveDescriptors.Add(directiveDescriptor); } }
// Special case for statement padding to account for brace positioning in the editor. public static string PadStatement(RazorEngineHost host, string code, Span target, ref int startGeneratedCode, out int paddingCharCount) { if (host == null) { throw new ArgumentNullException("host"); } if (target == null) { throw new ArgumentNullException("target"); } // We are passing 0 rather than startgeneratedcode intentionally (keeping v2 behavior). int padding = CalculatePadding(host, target, 0); // We treat statement padding specially so for brace positioning, so that in the following example: // @if (foo > 0) // { // } // // the braces shows up under the @ rather than under the if. if (host.DesignTimeMode && padding > 0 && target.Previous.Kind == SpanKind.Transition && // target.Previous is guaranteed to be none null if you got any padding. String.Equals(target.Previous.Content, SyntaxConstants.TransitionString)) { padding--; startGeneratedCode--; } string generatedCode = PadInternal(host, code, padding, out paddingCharCount); return generatedCode; }
public void ParseModelKeyword_InfersBaseType_FromModelName(string modelName, string expectedModel) { // Arrange var documentContent = "@model " + modelName + Environment.NewLine + "Bar"; var factory = SpanFactory.CreateCsHtml(); var errors = new List<RazorError>(); var expectedSpans = new Span[] { factory.EmptyHtml(), factory.CodeTransition(SyntaxConstants.TransitionString) .Accepts(AcceptedCharacters.None), factory.MetaCode("model ") .Accepts(AcceptedCharacters.None), factory.Code(modelName + Environment.NewLine) .As(new ModelChunkGenerator("RazorView", expectedModel)) .Accepts(AcceptedCharacters.AnyExceptNewline), factory.Markup("Bar") .With(new MarkupChunkGenerator()) }; // Act var spans = ParseDocument(documentContent, errors); // Assert Assert.Equal(expectedSpans, spans); Assert.Empty(errors); }
public override void GenerateCode(Span target, CodeGeneratorContext context) { // Try to find the namespace in the existing imports string ns = Namespace; if (!String.IsNullOrEmpty(ns) && Char.IsWhiteSpace(ns[0])) { ns = ns.Substring(1); } CodeNamespaceImport import = context.Namespace .Imports .OfType<CodeNamespaceImport>() .Where(i => String.Equals(i.Namespace, ns.Trim(), StringComparison.Ordinal)) .FirstOrDefault(); if (import == null) { // It doesn't exist, create it import = new CodeNamespaceImport(ns); context.Namespace.Imports.Add(import); } // Attach our info to the existing/new import. import.LinePragma = context.GenerateLinePragma(target); }
public override void GenerateCode(Span target, CodeGeneratorContext context) { if (Name == SyntaxConstants.CSharp.SessionStateKeyword) { context.CodeTreeBuilder.AddSessionStateChunk(Value, target); } }
// Special case for statement padding to account for brace positioning in the editor. public string BuildStatementPadding(Span target) { if (target == null) { throw new ArgumentNullException("target"); } int padding = CalculatePadding(target, generatedStart: 0); // We treat statement padding specially so for brace positioning, so that in the following example: // @if (foo > 0) // { // } // // the braces shows up under the @ rather than under the if. if (_host.DesignTimeMode && padding > 0 && target.Previous.Kind == SpanKind.Transition && // target.Previous is guaranteed to not be null if you have padding. String.Equals(target.Previous.Content, SyntaxConstants.TransitionString, StringComparison.Ordinal)) { padding--; } string generatedCode = BuildPaddingInternal(padding); return generatedCode; }
public SpanBuilder(Span original) { Kind = original.Kind; _symbols = new List<ISymbol>(original.Symbols); EditHandler = original.EditHandler; CodeGenerator = original.CodeGenerator; Start = original.Start; }
public override void GenerateCode(Span target, CodeGeneratorContext context) { var attributeType = new CodeTypeReference(typeof(RazorDirectiveAttribute)); var attributeDeclaration = new CodeAttributeDeclaration( attributeType, new CodeAttributeArgument(new CodePrimitiveExpression(Name)), new CodeAttributeArgument(new CodePrimitiveExpression(Value))); context.GeneratedClass.CustomAttributes.Add(attributeDeclaration); }
public override void GenerateCode(Span target, CodeGeneratorContext context) { if (!context.Host.DesignTimeMode && !String.IsNullOrEmpty(context.Host.GeneratedClassContext.LayoutPropertyName)) { context.TargetMethod.Statements.Add( new CodeAssignStatement( new CodePropertyReferenceExpression(null, context.Host.GeneratedClassContext.LayoutPropertyName), new CodePrimitiveExpression(LayoutPath))); } }
public void CalculatePaddingForEmptySpanReturnsZero() { RazorEngineHost host = CreateHost(designTime: true); Span span = new Span(new SpanBuilder()); int padding = CodeGeneratorPaddingHelper.CalculatePadding(host, span, 0); Assert.Equal(0, padding); }
public override void GenerateChunk(Span target, ChunkGeneratorContext context) { var ns = Namespace; if (!string.IsNullOrEmpty(ns) && char.IsWhiteSpace(ns[0])) { ns = ns.Substring(1); } context.ChunkTreeBuilder.AddUsingChunk(ns, target); }
public string BuildExpressionPadding(Span target, int generatedStart) { if (target == null) { throw new ArgumentNullException(nameof(target)); } var padding = CalculatePadding(target, generatedStart); return BuildPaddingInternal(padding); }
public override void GenerateCode(Span target, CodeGeneratorContext context) { if (context.Host.DesignTimeMode) { return; } ExpressionRenderingMode oldMode = context.ExpressionRenderingMode; context.BufferStatementFragment(context.BuildCodeString(cw => { cw.WriteParameterSeparator(); cw.WriteStartMethodInvoke("Tuple.Create"); cw.WriteLocationTaggedString(Prefix); cw.WriteParameterSeparator(); if (ValueGenerator != null) { cw.WriteStartMethodInvoke("Tuple.Create", "System.Object", "System.Int32"); context.ExpressionRenderingMode = ExpressionRenderingMode.InjectCode; } else { cw.WriteLocationTaggedString(Value); cw.WriteParameterSeparator(); // literal: true - This attribute value is a literal value cw.WriteBooleanLiteral(true); cw.WriteEndMethodInvoke(); // In VB, we need a line continuation cw.WriteLineContinuation(); } })); if (ValueGenerator != null) { ValueGenerator.Value.GenerateCode(target, context); context.FlushBufferedStatement(); context.ExpressionRenderingMode = oldMode; context.AddStatement(context.BuildCodeString(cw => { cw.WriteParameterSeparator(); cw.WriteSnippet(ValueGenerator.Location.AbsoluteIndex.ToString(CultureInfo.CurrentCulture)); cw.WriteEndMethodInvoke(); cw.WriteParameterSeparator(); // literal: false - This attribute value is not a literal value, it is dynamically generated cw.WriteBooleanLiteral(false); cw.WriteEndMethodInvoke(); // In VB, we need a line continuation cw.WriteLineContinuation(); })); } else { context.FlushBufferedStatement(); } }
internal void CalculateStart(Span prev) { if (prev == null) { Start = SourceLocation.Zero; } else { Start = new SourceLocationTracker(prev.Start).UpdateLocation(prev.Content).CurrentLocation; } }
protected override PartialParseResult CanAcceptChange(Span target, TextChange normalizedChange) { if (((AutoCompleteAtEndOfSpan && IsAtEndOfSpan(target, normalizedChange)) || IsAtEndOfFirstLine(target, normalizedChange)) && normalizedChange.IsInsert && ParserHelpers.IsNewLine(normalizedChange.NewText) && AutoCompleteString != null) { return PartialParseResult.Rejected | PartialParseResult.AutoCompleteBlock; } return PartialParseResult.Rejected; }
public override void GenerateCode(Span target, CodeGeneratorContext context) { string ns = Namespace; if (!String.IsNullOrEmpty(ns) && Char.IsWhiteSpace(ns[0])) { ns = ns.Substring(1); } context.CodeTreeBuilder.AddUsingChunk(ns, target); }
/// <summary> /// Generates <see cref="AddTagHelperChunk"/>s if <see cref="RemoveTagHelperDescriptors"/> is /// <c>true</c>, otherwise <see cref="RemoveTagHelperChunk"/>s are generated. /// </summary> /// <param name="target"> /// The <see cref="Span"/> responsible for this <see cref="AddOrRemoveTagHelperChunkGenerator"/>. /// </param> /// <param name="context">A <see cref="ChunkGeneratorContext"/> instance that contains information about /// the current chunk generation process.</param> public override void GenerateChunk(Span target, ChunkGeneratorContext context) { if (RemoveTagHelperDescriptors) { context.ChunkTreeBuilder.AddRemoveTagHelperChunk(LookupText, target); } else { context.ChunkTreeBuilder.AddAddTagHelperChunk(LookupText, target); } }
public override void GenerateCode(Span target, CodeGeneratorContext context) { // Check if the host supports it if (String.IsNullOrEmpty(context.Host.GeneratedClassContext.ResolveUrlMethodName)) { // Nope, just use the default MarkupCodeGenerator behavior new MarkupCodeGenerator().GenerateCode(target, context); return; } context.CodeTreeBuilder.AddResolveUrlChunk(target.Content, target); }
protected override PartialParseResult CanAcceptChange(Span target, TextChange normalizedChange) { if (AcceptedCharacters == AcceptedCharacters.Any) { return PartialParseResult.Rejected; } // In some editors intellisense insertions are handled as "dotless commits". If an intellisense selection is confirmed // via something like '.' a dotless commit will append a '.' and then insert the remaining intellisense selection prior // to the appended '.'. This 'if' statement attempts to accept the intermediate steps of a dotless commit via // intellisense. It will accept two cases: // 1. '@foo.' -> '@foobaz.'. // 2. '@foobaz..' -> '@foobaz.bar.'. Includes Sub-cases '@foobaz()..' -> '@foobaz().bar.' etc. // The key distinction being the double '.' in the second case. if (IsDotlessCommitInsertion(target, normalizedChange)) { return HandleDotlessCommitInsertion(target); } if (IsAcceptableReplace(target, normalizedChange)) { return HandleReplacement(target, normalizedChange); } var changeRelativePosition = normalizedChange.OldPosition - target.Start.AbsoluteIndex; // Get the edit context char? lastChar = null; if (changeRelativePosition > 0 && target.Content.Length > 0) { lastChar = target.Content[changeRelativePosition - 1]; } // Don't support 0->1 length edits if (lastChar == null) { return PartialParseResult.Rejected; } // Accepts cases when insertions are made at the end of a span or '.' is inserted within a span. if (IsAcceptableInsertion(target, normalizedChange)) { // Handle the insertion return HandleInsertion(target, lastChar.Value, normalizedChange); } if (IsAcceptableDeletion(target, normalizedChange)) { return HandleDeletion(target, lastChar.Value, normalizedChange); } return PartialParseResult.Rejected; }
public void CalculatePaddingForEmptySpanReturnsZero() { // Arrange RazorEngineHost host = CreateHost(designTime: true); Span span = new Span(new SpanBuilder()); var paddingBuilder = new CSharpPaddingBuilder(host); // Act int padding = paddingBuilder.CalculatePadding(span, 0); // Assert Assert.Equal(0, padding); }
public override void VisitSpan(Span span) { if (CanRewrite(span)) { var newNode = RewriteSpan(_blocks.Peek(), span); if (newNode != null) { _blocks.Peek().Children.Add(newNode); } } else { _blocks.Peek().Children.Add(span); } }
protected override SyntaxTreeNode RewriteSpan(BlockBuilder parent, Span span) { // Only rewrite if we have a previous that is also markup (CanRewrite does this check for us!) Span previous = parent.Children.LastOrDefault() as Span; if (previous == null || !CanRewrite(previous)) { return span; } // Merge spans parent.Children.Remove(previous); SpanBuilder merged = new SpanBuilder(); FillSpan(merged, previous.Start, previous.Content + span.Content); return merged.Build(); }
// there is some duplicity of code here, but its very simple and since this is a host path, I'd rather not create another class to encapsulate the data. public static int PaddingCharCount(RazorEngineHost host, Span target, int generatedStart) { int padding = CalculatePadding(host, target, generatedStart); if (host.DesignTimeMode && host.IsIndentingWithTabs) { int spaces; int tabs = Math.DivRem(padding, host.TabSize, out spaces); return tabs + spaces; } else { return padding; } }
public virtual EditResult ApplyChange(Span target, TextChange change, bool force) { PartialParseResult result = PartialParseResult.Accepted; TextChange normalized = change.Normalize(); if (!force) { result = CanAcceptChange(target, normalized); } // If the change is accepted then apply the change if (result.HasFlag(PartialParseResult.Accepted)) { return new EditResult(result, UpdateSpan(target, normalized)); } return new EditResult(result, new SpanBuilder(target)); }
protected virtual SpanBuilder UpdateSpan(Span target, TextChange normalizedChange) { string newContent = normalizedChange.ApplyChange(target); SpanBuilder newSpan = new SpanBuilder(target); newSpan.ClearSymbols(); foreach (ISymbol sym in Tokenizer(newContent)) { sym.OffsetStart(target.Start); newSpan.Accept(sym); } if (target.Next != null) { SourceLocation newEnd = SourceLocationTracker.CalculateNewLocation(target.Start, newContent); target.Next.ChangeStart(newEnd); } return newSpan; }
public override void GenerateCode(Span target, CodeGeneratorContext context) { context.FlushBufferedStatement(); string generatedCode = context.BuildCodeString(cw => { cw.WriteSnippet(target.Content); }); int startGeneratedCode = target.Start.CharacterIndex; int paddingCharCount; generatedCode = CodeGeneratorPaddingHelper.PadStatement(context.Host, generatedCode, target, ref startGeneratedCode, out paddingCharCount); context.AddStatement( generatedCode, context.GenerateLinePragma(target, paddingCharCount)); }
public override void GenerateChunk(Span target, ChunkGeneratorContext context) { var chunk = context.ChunkTreeBuilder.StartParentChunk<LiteralCodeAttributeChunk>(target); chunk.Prefix = Prefix; chunk.Value = Value; if (ValueGenerator != null) { chunk.ValueLocation = ValueGenerator.Location; ValueGenerator.Value.GenerateChunk(target, context); chunk.ValueLocation = ValueGenerator.Location; } context.ChunkTreeBuilder.EndParentChunk(); }