// internal for unit testing only, not intended to be used directly in code internal static int CalculatePadding(RazorEngineHost host, Span target, int generatedStart) { if (host == null) { throw new ArgumentNullException("host"); } 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 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); }
// 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 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 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 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(); } }
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; }
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); } int 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 override void VisitSpan(Span span) { if (CanRewrite(span)) { SyntaxTreeNode 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)); }
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)); }
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) { string generatedCode = context.BuildCodeString(cw => { cw.WriteSnippet(target.Content); }); int paddingCharCount; string paddedCode = CodeGeneratorPaddingHelper.Pad(context.Host, generatedCode, target, out paddingCharCount); Contract.Assert(paddingCharCount > 0); context.GeneratedClass.Members.Add( new CodeSnippetTypeMember(paddedCode) { LinePragma = context.GenerateLinePragma(target, paddingCharCount) }); }
public override void GenerateCode(Span target, CodeGeneratorContext context) { if (!context.Host.DesignTimeMode && String.IsNullOrEmpty(target.Content)) { return; } if (context.Host.EnableInstrumentation) { context.AddContextCall(target, context.Host.GeneratedClassContext.BeginContextMethodName, isLiteral: true); } if (!String.IsNullOrEmpty(target.Content) && !context.Host.DesignTimeMode) { string code = context.BuildCodeString(cw => { if (!String.IsNullOrEmpty(context.TargetWriterName)) { cw.WriteStartMethodInvoke(context.Host.GeneratedClassContext.WriteLiteralToMethodName); cw.WriteSnippet(context.TargetWriterName); cw.WriteParameterSeparator(); } else { cw.WriteStartMethodInvoke(context.Host.GeneratedClassContext.WriteLiteralMethodName); } cw.WriteStringLiteral(target.Content); cw.WriteEndMethodInvoke(); cw.WriteEndStatement(); }); context.AddStatement(code); } if (context.Host.EnableInstrumentation) { context.AddContextCall(target, context.Host.GeneratedClassContext.EndContextMethodName, isLiteral: true); } }
public override void GenerateCode(Span target, CodeGeneratorContext context) { context.GeneratedClass.BaseTypes.Clear(); context.GeneratedClass.BaseTypes.Add(new CodeTypeReference(ResolveType(context, BaseType.Trim()))); if (context.Host.DesignTimeMode) { int generatedCodeStart = 0; string code = context.BuildCodeString(cw => { generatedCodeStart = cw.WriteVariableDeclaration(target.Content, "__inheritsHelper", null); cw.WriteEndStatement(); }); int paddingCharCount; CodeSnippetStatement stmt = new CodeSnippetStatement( CodeGeneratorPaddingHelper.Pad(context.Host, code, target, generatedCodeStart, out paddingCharCount)) { LinePragma = context.GenerateLinePragma(target, generatedCodeStart + paddingCharCount) }; context.AddDesignTimeHelperStatement(stmt); } }
/// <summary> /// Applies the text change to the content of the span and returns the new content. /// This method doesn't update the span content. /// </summary> public string ApplyChange(Span span) { return ApplyChange(span.Content, span.Start.AbsoluteIndex); }
private PartialParseResult TryAcceptChange(Span target, TextChange change, PartialParseResult acceptResult = PartialParseResult.Accepted) { string content = change.ApplyChange(target); if (StartsWithKeyword(content)) { return PartialParseResult.Rejected | PartialParseResult.SpanContextChanged; } return acceptResult; }
private PartialParseResult HandleInsertionAfterDot(Span target, TextChange change) { // If the insertion is a full identifier or another dot, accept it if (ParserHelpers.IsIdentifier(change.NewText) || change.NewText == ".") { return TryAcceptChange(target, change); } return PartialParseResult.Rejected; }
private PartialParseResult HandleInsertionAfterIdPart(Span target, TextChange change) { // If the insertion is a full identifier part, accept it if (ParserHelpers.IsIdentifier(change.NewText, requireIdentifierStart: false)) { return TryAcceptChange(target, change); } else if (EndsWithDot(change.NewText)) { // Accept it, possibly provisionally PartialParseResult result = PartialParseResult.Accepted; if (!AcceptTrailingDot) { result |= PartialParseResult.Provisional; } return TryAcceptChange(target, change, result); } else { return PartialParseResult.Rejected; } }
private PartialParseResult HandleInsertion(Span target, char previousChar, TextChange change) { // What are we inserting after? if (previousChar == '.') { return HandleInsertionAfterDot(target, change); } else if (ParserHelpers.IsIdentifierPart(previousChar) || previousChar == ')' || previousChar == ']') { return HandleInsertionAfterIdPart(target, change); } else { return PartialParseResult.Rejected; } }
public override void VisitSpan(Span span) { span.CodeGenerator.GenerateCode(span, Context); }
protected virtual bool CanRewrite(Span span) { return false; }
protected virtual SyntaxTreeNode RewriteSpan(BlockBuilder parent, Span span) { throw new NotImplementedException(); }
private PartialParseResult HandleReplacement(Span target, TextChange change) { // Special Case for IntelliSense commits. // When IntelliSense commits, we get two changes (for example user typed "Date", then committed "DateTime" by pressing ".") // 1. Insert "." at the end of this span // 2. Replace the "Date." at the end of the span with "DateTime." // We need partial parsing to accept case #2. string oldText = GetOldText(target, change); PartialParseResult result = PartialParseResult.Rejected; if (EndsWithDot(oldText) && EndsWithDot(change.NewText)) { result = PartialParseResult.Accepted; if (!AcceptTrailingDot) { result |= PartialParseResult.Provisional; } } return result; }
public virtual void GenerateCode(Span target, CodeGeneratorContext context) { }
private PartialParseResult HandleDeletion(Span target, char previousChar, TextChange change) { // What's left after deleting? if (previousChar == '.') { return TryAcceptChange(target, change, PartialParseResult.Accepted | PartialParseResult.Provisional); } else if (ParserHelpers.IsIdentifierPart(previousChar)) { return TryAcceptChange(target, change); } else { return PartialParseResult.Rejected; } }