示例#1
0
 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));
 }
示例#2
0
        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());
            });
        }
示例#3
0
        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);
        }
示例#4
0
        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);
        }
 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);
            }
            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;
        }
示例#7
0
        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.Equal(42, change.OldPosition);
            Assert.Equal(24, change.OldLength);
            Assert.Equal(1337, change.NewLength);
            Assert.Same(newBuffer, change.NewBuffer);
            Assert.Same(oldBuffer, change.OldBuffer);
        }
示例#8
0
        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));
        }
示例#9
0
 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;
 }
示例#10
0
        public void LocateOwnerReturnsNullIfNoSpanReturnsTrueForOwnsSpan()
        {
            // Arrange
            var factory = SpanFactory.CreateCsHtml();
            var block = new MarkupBlock(
                factory.Markup("Foo "),
                new StatementBlock(
                    factory.CodeTransition(),
                    factory.Code("bar").AsStatement()),
                factory.Markup(" Baz"));
            var change = new TextChange(128, 1, new StringTextBuffer("Foo @bar Baz"), 1, new StringTextBuffer("Foo @bor Baz"));

            // Act
            var actual = block.LocateOwner(change);

            // Assert
            Assert.Null(actual);
        }
        // Accepts character insertions at the end of spans.  AKA: '@foo' -> '@fooo' or '@foo' -> '@foo   ' etc.
        private static bool IsAcceptableEndInsertion(Span target, TextChange change)
        {
            Debug.Assert(change.IsInsert);

            return IsAtEndOfSpan(target, change) ||
                   RemainingIsWhitespace(target, change);
        }
 // Acceptable insertions can occur at the end of a span or when a '.' is inserted within a span.
 private static bool IsAcceptableInsertion(Span target, TextChange change)
 {
     return change.IsInsert &&
            (IsAcceptableEndInsertion(target, change) ||
            IsAcceptableInnerInsertion(target, change));
 }
示例#13
0
 protected internal static bool IsAtEndOfSpan(Span target, TextChange change)
 {
     return (change.OldPosition + change.OldLength) == (target.Start.AbsoluteIndex + target.Length);
 }
 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 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;
     }
 }
        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.
            var oldText = GetOldText(target, change);

            var result = PartialParseResult.Rejected;
            if (EndsWithDot(oldText) && EndsWithDot(change.NewText))
            {
                result = PartialParseResult.Accepted;
                if (!AcceptTrailingDot)
                {
                    result |= PartialParseResult.Provisional;
                }
            }
            return result;
        }
示例#17
0
 protected virtual PartialParseResult CanAcceptChange(Span target, TextChange normalizedChange)
 {
     return PartialParseResult.Rejected;
 }
示例#18
0
 public virtual EditResult ApplyChange(Span target, TextChange change)
 {
     return ApplyChange(target, change, force: false);
 }
示例#19
0
 /// <summary>
 /// Returns the old text referenced by the change.
 /// </summary>
 /// <remarks>
 /// If the content has already been updated by applying the change, this data will be _invalid_
 /// </remarks>
 protected internal static string GetOldText(Span target, TextChange change)
 {
     return target.Content.Substring(change.OldPosition - target.Start.AbsoluteIndex, change.OldLength);
 }
        private static bool IsAcceptableInnerInsertion(Span target, TextChange change)
        {
            Debug.Assert(change.IsInsert);

            // Ensure that we're actually inserting in the middle of a span and not at the end.
            // This case will fail if the IsAcceptableEndInsertion does not capture an end insertion correctly.
            Debug.Assert(!IsAtEndOfSpan(target, change));

            return change.NewPosition > 0 &&
                   change.NewText == ".";
        }
 private static bool RemainingIsWhitespace(Span target, TextChange change)
 {
     var offset = (change.OldPosition - target.Start.AbsoluteIndex) + change.OldLength;
     return String.IsNullOrWhiteSpace(target.Content.Substring(offset));
 }
 // A dotless commit is the process of inserting a '.' with an intellisense selection.
 private static bool IsDotlessCommitInsertion(Span target, TextChange change)
 {
     return IsNewDotlessCommitInsertion(target, change) || IsSecondaryDotlessCommitInsertion(target, change);
 }
 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;
     }
 }
 // Completing 'DateTime' in intellisense with a '.' could result in: '@DateT' -> '@DateT.' -> '@DateTime.' which is accepted.
 private static bool IsNewDotlessCommitInsertion(Span target, TextChange change)
 {
     return !IsAtEndOfSpan(target, change) &&
            change.NewPosition > 0 &&
            change.NewLength > 0 &&
            target.Content.Last() == '.' &&
            ParserHelpers.IsIdentifier(change.NewText, requireIdentifierStart: false) &&
            (change.OldLength == 0 || ParserHelpers.IsIdentifier(change.OldText, requireIdentifierStart: false));
 }
 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
         var result = PartialParseResult.Accepted;
         if (!AcceptTrailingDot)
         {
             result |= PartialParseResult.Provisional;
         }
         return TryAcceptChange(target, change, result);
     }
     else
     {
         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)
        {
            var content = change.ApplyChange(target);
            if (StartsWithKeyword(content))
            {
                return PartialParseResult.Rejected | PartialParseResult.SpanContextChanged;
            }

            return acceptResult;
        }
 private static bool IsAcceptableDeletion(Span target, TextChange change)
 {
     return IsEndDeletion(target, change) ||
            (change.IsDelete && RemainingIsWhitespace(target, change));
 }
 // Once a dotless commit has been performed you then have something like '@DateTime.'.  This scenario is used to detect the
 // situation when you try to perform another dotless commit resulting in a textchange with '..'.  Completing 'DateTime.Now'
 // in intellisense with a '.' could result in: '@DateTime.' -> '@DateTime..' -> '@DateTime.Now.' which is accepted.
 private static bool IsSecondaryDotlessCommitInsertion(Span target, TextChange change)
 {
     // Do not need to worry about other punctuation, just looking for double '.' (after change)
     return change.NewLength == 1 &&
            !String.IsNullOrEmpty(target.Content) &&
            target.Content.Last() == '.' &&
            change.NewText == "." &&
            change.OldLength == 0;
 }
示例#30
0
 /// <summary>
 /// Returns true if the specified change is a replacement of text at the end of this span.
 /// </summary>
 protected internal static bool IsEndReplace(Span target, TextChange change)
 {
     return change.IsReplace && IsAtEndOfSpan(target, change);
 }