internal static void WriteDebugTree(string sourceFile, Block document, PartialParseResult result, TextChange change, RazorEditorParser parser, bool treeStructureChanged) { if (!OutputDebuggingEnabled) { return; } RunTask(() => { string outputFileName = Normalize(sourceFile) + "_tree"; string outputPath = Path.Combine(Path.GetDirectoryName(sourceFile), outputFileName); var treeBuilder = new StringBuilder(); WriteTree(document, treeBuilder); treeBuilder.AppendLine(); treeBuilder.AppendFormat(CultureInfo.CurrentCulture, "Last Change: {0}", change); treeBuilder.AppendLine(); treeBuilder.AppendFormat(CultureInfo.CurrentCulture, "Normalized To: {0}", change.Normalize()); treeBuilder.AppendLine(); treeBuilder.AppendFormat(CultureInfo.CurrentCulture, "Partial Parse Result: {0}", result); treeBuilder.AppendLine(); if (result.HasFlag(PartialParseResult.Rejected)) { treeBuilder.AppendFormat(CultureInfo.CurrentCulture, "Tree Structure Changed: {0}", treeStructureChanged); treeBuilder.AppendLine(); } if (result.HasFlag(PartialParseResult.AutoCompleteBlock)) { treeBuilder.AppendFormat(CultureInfo.CurrentCulture, "Auto Complete Insert String: \"{0}\"", parser.GetAutoCompleteString()); treeBuilder.AppendLine(); } File.WriteAllText(outputPath, treeBuilder.ToString()); }); }
private BackgroundParseTask(RazorTemplateEngine engine, string sourceFileName, TextChange change) { Change = change; Engine = engine; SourceFileName = sourceFileName; InnerTask = new Task(() => Run(_cancelSource.Token), _cancelSource.Token); }
public void CheckForStructureChangesStartsFullReparseIfChangeOverlapsMultipleSpans() { // Arrange RazorEditorParser parser = new RazorEditorParser(CreateHost(), TestLinePragmaFileName); ITextBuffer original = new StringTextBuffer("Foo @bar Baz"); ITextBuffer changed = new StringTextBuffer("Foo @bap Daz"); TextChange change = new TextChange(7, 3, original, 3, changed); ManualResetEventSlim parseComplete = new ManualResetEventSlim(); int parseCount = 0; parser.DocumentParseComplete += (sender, args) => { Interlocked.Increment(ref parseCount); parseComplete.Set(); }; Assert.AreEqual(PartialParseResult.Rejected, parser.CheckForStructureChanges(new TextChange(0, 0, new StringTextBuffer(String.Empty), 12, original))); MiscUtils.DoWithTimeoutIfNotDebugging(parseComplete.Wait); // Wait for the parse to finish parseComplete.Reset(); // Act PartialParseResult result = parser.CheckForStructureChanges(change); // Assert Assert.AreEqual(PartialParseResult.Rejected, result); MiscUtils.DoWithTimeoutIfNotDebugging(parseComplete.Wait); Assert.AreEqual(2, parseCount); }
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 void OwnsChangeReturnsFalseIfChangeIsReplacementOrDeleteAtSpanEnd() { // Arrange Span span = new CodeSpan(new SourceLocation(42, 0, 42), "FooBarBaz"); TextChange change = new TextChange(51, 2, new StringTextBuffer("BooBarBaz"), 3, new StringTextBuffer("Foo")); // Act/Assert Assert.IsFalse(span.OwnsChange(change)); }
public void OwnsChangeReturnsTrueIfChangeIsInsertionAtSpanEndAndCanGrowIsTrue() { // Arrange Span span = new CodeSpan(new SourceLocation(42, 0, 42), "FooBarBaz"); TextChange change = new TextChange(51, 0, new StringTextBuffer("BooBarBaz"), 3, new StringTextBuffer("Foo")); // Act/Assert Assert.IsTrue(span.OwnsChange(change)); }
public void OwnsChangeReturnsFalseIfChangeStartsAfterSpanEnds() { // Arrange Span span = new CodeSpan(new SourceLocation(42, 0, 42), "FooBarBaz"); TextChange change = new TextChange(52, 3, new StringTextBuffer("BooBarBaz"), 3, new StringTextBuffer("Foo")); // Act/Assert Assert.IsFalse(span.OwnsChange(change)); }
public void OwnsChangeReturnsTrueIfSpanContentEntirelyContainsOldSpan() { // Arrange Span span = new CodeSpan(new SourceLocation(42, 0, 42), "FooBarBaz"); TextChange change = new TextChange(45, 3, new StringTextBuffer("BooBarBaz"), 3, new StringTextBuffer("Foo")); // Act/Assert Assert.IsTrue(span.OwnsChange(change)); }
public void CheckForStructureChangesRequiresNonNullBufferInChange() { TextChange change = new TextChange(); ExceptionAssert.ThrowsArgumentException(() => new RazorEditorParser(CreateHost(), "C:\\Foo.cshtml").CheckForStructureChanges(change), "change", String.Format(RazorResources.Structure_Member_CannotBeNull, "Buffer", "TextChange")); }
public void TestIsInsert() { // Arrange ITextBuffer oldBuffer = new Mock<ITextBuffer>().Object; ITextBuffer newBuffer = new Mock<ITextBuffer>().Object; TextChange change = new TextChange(0, 0, oldBuffer, 35, newBuffer); // Assert Assert.IsTrue(change.IsInsert); }
public void TestIsReplace() { // Arrange ITextBuffer oldBuffer = new Mock<ITextBuffer>().Object; ITextBuffer newBuffer = new Mock<ITextBuffer>().Object; TextChange change = new TextChange(0, 5, oldBuffer, 10, newBuffer); // Assert Assert.IsTrue(change.IsReplace); }
public void TestIsDelete() { // Arrange ITextBuffer oldBuffer = new Mock<ITextBuffer>().Object; ITextBuffer newBuffer = new Mock<ITextBuffer>().Object; TextChange change = new TextChange(0, 1, oldBuffer, 0, newBuffer); // Assert Assert.True(change.IsDelete); }
public void TestDeleteCreatesTheRightSizeChange() { // Arrange ITextBuffer oldBuffer = new Mock<ITextBuffer>().Object; ITextBuffer newBuffer = new Mock<ITextBuffer>().Object; TextChange change = new TextChange(0, 1, oldBuffer, 0, newBuffer); // Assert Assert.Equal(0, change.NewText.Length); Assert.Equal(1, change.OldText.Length); }
public void ApplyChangeReturnsRejectedIfTerminatorStringNull() { // Arrange var span = new CodeSpan(new SourceLocation(0, 0, 0), "foo"); var change = new TextChange(0, 0, new StringTextBuffer("foo"), 1, new StringTextBuffer("bfoo")); // Act PartialParseResult result = span.ApplyChange(change); // Assert Assert.AreEqual(PartialParseResult.Rejected, result); }
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 void SpanWithAcceptTrailingDotOnAcceptsIntelliSenseReplaceWhichActuallyInsertsDot() { // Arrange var span = new ImplicitExpressionSpan("abcd", CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true, acceptedCharacters: AcceptedCharacters.None); var newBuffer = new StringTextBuffer("abcd."); var oldBuffer = new StringTextBuffer("abcd"); var textChange = new TextChange(0, 4, oldBuffer, 5, newBuffer); // Act PartialParseResult result = span.ApplyChange(textChange); // Assert Assert.AreEqual(PartialParseResult.Accepted, result); }
public void SpanWithAcceptTrailingDotOffProvisionallyAcceptsEndReplacementWithTrailingDot() { // Arrange var span = new ImplicitExpressionSpan("abcd.", CSharpCodeParser.DefaultKeywords, acceptTrailingDot: false, acceptedCharacters: AcceptedCharacters.None); var newBuffer = new StringTextBuffer("abcdef."); var oldBuffer = new StringTextBuffer("abcd."); var textChange = new TextChange(0, 5, oldBuffer, 7, newBuffer); // Act PartialParseResult result = span.ApplyChange(textChange); // Assert Assert.AreEqual(PartialParseResult.Accepted | PartialParseResult.Provisional, result); }
public void ApplyChangeReturnsRejectedWithoutAutoCompleteBlockIfTerminatorStringNonNullAndEditIsNewlineInsertOnSecondLine() { // Arrange var span = new CodeSpan(new SourceLocation(0, 0, 0), "foo\r\nbar\r\nbaz") { AutoCompleteString = "}" }; var change = new TextChange(8, 0, new StringTextBuffer("foo\r\nbar\r\nbaz"), 2, new StringTextBuffer("foo\r\nb\r\nar\r\nbaz")); // Act PartialParseResult result = span.ApplyChange(change); // Assert Assert.AreEqual(PartialParseResult.Rejected, result); }
public void ConstructorInitializesProperties() { // Act ITextBuffer oldBuffer = new Mock<ITextBuffer>().Object; ITextBuffer newBuffer = new Mock<ITextBuffer>().Object; TextChange change = new TextChange(42, 24, oldBuffer, 1337, newBuffer); // Assert Assert.AreEqual(42, change.OldPosition); Assert.AreEqual(24, change.OldLength); Assert.AreEqual(1337, change.NewLength); Assert.AreSame(newBuffer, change.NewBuffer); Assert.AreSame(oldBuffer, change.OldBuffer); }
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 void LocateOwnerReturnsNullIfNoSpanReturnsTrueForOwnsSpan() { // Arrange Block block = new Block(BlockType.Markup, new SyntaxTreeNode[] { new MarkupSpan(SourceLocation.Zero, "Foo "), new StatementBlock(new SyntaxTreeNode[] { new TransitionSpan(new SourceLocation(4, 0, 4), "@"), new CodeSpan(new SourceLocation(5, 0, 5), "bar"), }), new MarkupSpan(new SourceLocation(8,0,8), " Baz") }); TextChange change = new TextChange(128, 1, new StringTextBuffer("Foo @bar Baz"), 1, new StringTextBuffer("Foo @bor Baz")); // Act Span actual = block.LocateOwner(change); // Assert Assert.IsNull(actual); }
public void LocateOwnerReturnsSpanWhichReturnsTrueForOwnsSpan() { // Arrange Span expected = new CodeSpan(new SourceLocation(5, 0, 5), "bar"); Block block = new MarkupBlock(new SyntaxTreeNode[] { new MarkupSpan(SourceLocation.Zero, "Foo "), new StatementBlock(new SyntaxTreeNode[] { new TransitionSpan(new SourceLocation(4, 0, 4), "@"), expected, }), new MarkupSpan(new SourceLocation(8,0,8), " Baz") }); TextChange change = new TextChange(6, 1, new StringTextBuffer("Foo @bar Baz"), 1, new StringTextBuffer("Foo @bor Baz")); // Act Span actual = block.LocateOwner(change); // Assert Assert.AreSame(expected, actual); }
protected override PartialParseResult CanAcceptChange(Span target, TextChange normalizedChange) { if (AcceptedCharacters == AcceptedCharacters.Any) { return PartialParseResult.Rejected; } 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; } // Only support insertions at the end of the 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; }
protected override PartialParseResult CanAcceptChange(TextChange change) { if (AcceptedCharacters == AcceptedCharacters.Any) { // TODO: Support partial parsing within brackets (with provisionally accepted "(" and "["?) return PartialParseResult.Rejected; } if (IsAcceptableReplace(change)) { return HandleReplacement(change); } else { int changeRelativePosition = change.OldPosition - Start.AbsoluteIndex; // Get the edit context char? lastChar = null; if (changeRelativePosition > 0 && Content.Length > 0) { lastChar = Content[changeRelativePosition - 1]; } // Don't support 0->1 length edits if (lastChar == null) { return PartialParseResult.Rejected; } // Only support insertions at the end of the span if (IsAcceptableInsertion(change)) { // Handle the insertion return HandleInsertion(lastChar.Value, change); } else if (IsAcceptableDeletion(change)) { return HandleDeletion(lastChar.Value, change); } } return PartialParseResult.Rejected; }
private static bool IsAcceptableReplace(Span target, TextChange change) { return IsEndReplace(target, change) || (change.IsReplace && RemainingIsWhitespace(target, change)); }
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, accept it if (ParserHelpers.IsIdentifier(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; } }