// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using Microsoft.VisualStudio.Test; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Operations; using Moq; using Xunit; namespace Microsoft.VisualStudio.Editor.Razor { public class BraceSmartIndenterTest : BraceSmartIndenterTestBase { [Fact] public void InsertIndent_InsertsProvidedIndentIntoBuffer() { // Arrange var initialSnapshot = new StringTextSnapshot("@{ \n}"); var expectedIndentResult = "@{ anything\n}"; ITextBuffer textBuffer = null; var textView = CreateFocusedTextView(() => textBuffer); var documentTracker = CreateDocumentTracker(() => textBuffer, textView); textBuffer = CreateTextBuffer(initialSnapshot, documentTracker); // Act BraceSmartIndenter.InsertIndent(3, "anything", textBuffer); // Assert Assert.Equal(expectedIndentResult, ((StringTextSnapshot)textBuffer.CurrentSnapshot).Content); } [Fact] public void RestoreCaretTo_PlacesCursorAtProvidedPosition() { // Arrange var initialSnapshot = new StringTextSnapshot("@{ \n\n}"); var bufferPosition = new VirtualSnapshotPoint(initialSnapshot, 4); var caret = new Mock(); caret.Setup(c => c.MoveTo(It.IsAny())) .Callback(point => { Assert.Equal(3, point.Position); Assert.Same(initialSnapshot, point.Snapshot); }); ITextBuffer textBuffer = null; var textView = CreateFocusedTextView(() => textBuffer, caret.Object); var documentTracker = CreateDocumentTracker(() => textBuffer, textView); textBuffer = CreateTextBuffer(initialSnapshot, documentTracker); // Act BraceSmartIndenter.RestoreCaretTo(3, textView); // Assert caret.VerifyAll(); } [Fact] public void TriggerSmartIndent_ForcesEditorToMoveToEndOfLine() { // Arrange var textView = CreateFocusedTextView(); var editorOperations = new Mock(); editorOperations.Setup(operations => operations.MoveToEndOfLine(false)); var editorOperationsFactory = new Mock(); var documentTracker = CreateDocumentTracker(() => Mock.Of(), textView); editorOperationsFactory.Setup(factory => factory.GetEditorOperations(textView)) .Returns(editorOperations.Object); var smartIndenter = new BraceSmartIndenter(Dispatcher, documentTracker, editorOperationsFactory.Object); // Act smartIndenter.TriggerSmartIndent(textView); // Assert editorOperations.VerifyAll(); } [Fact] public void AfterClosingBrace_ContentAfterBrace_ReturnsFalse() { // Arrange var fileSnapshot = new StringTextSnapshot("@functions\n{a\n}"); var changePosition = 13; var line = fileSnapshot.GetLineFromPosition(changePosition); // Act & Assert Assert.False(BraceSmartIndenter.BeforeClosingBrace(0, line)); } [Theory] [InlineData("@functions\n{\n}")] [InlineData("@functions\n{ \n}")] [InlineData("@functions\n { \n}")] [InlineData("@functions\n\t\t{\t\t\n}")] public void AfterClosingBrace_BraceBeforePosition_ReturnsTrue(string fileContent) { // Arrange var fileSnapshot = new StringTextSnapshot(fileContent); var changePosition = fileContent.Length - 3 /* \n} */; var line = fileSnapshot.GetLineFromPosition(changePosition); // Act & Assert Assert.True(BraceSmartIndenter.AfterOpeningBrace(line.Length - 1, line)); } [Fact] public void BeforeClosingBrace_ContentPriorToBrace_ReturnsFalse() { // Arrange var fileSnapshot = new StringTextSnapshot("@functions\n{\na}"); var changePosition = 12; var line = fileSnapshot.GetLineFromPosition(changePosition + 1 /* \n */); // Act & Assert Assert.False(BraceSmartIndenter.BeforeClosingBrace(0, line)); } [Theory] [InlineData("@functions\n{\n}")] [InlineData("@functions\n{\n }")] [InlineData("@functions\n{\n } ")] [InlineData("@functions\n{\n\t\t } ")] public void BeforeClosingBrace_BraceAfterPosition_ReturnsTrue(string fileContent) { // Arrange var fileSnapshot = new StringTextSnapshot(fileContent); var changePosition = 12; var line = fileSnapshot.GetLineFromPosition(changePosition + 1 /* \n */); // Act & Assert Assert.True(BraceSmartIndenter.BeforeClosingBrace(0, line)); } [ForegroundFact] public void TextBuffer_OnChanged_NoopsIfNoChanges() { // Arrange var editorOperationsFactory = new Mock(); var changeCollection = new TestTextChangeCollection(); var textContentChangeArgs = new TestTextContentChangedEventArgs(changeCollection); var documentTracker = CreateDocumentTracker(() => Mock.Of(), Mock.Of()); var braceSmartIndenter = new BraceSmartIndenter(Dispatcher, documentTracker, editorOperationsFactory.Object); // Act & Assert braceSmartIndenter.TextBuffer_OnChanged(null, textContentChangeArgs); } [ForegroundFact] public void TextBuffer_OnChanged_NoopsIfChangesThatResultInNoChange() { // Arrange var initialSnapshot = new StringTextSnapshot("Hello World"); var textBuffer = new TestTextBuffer(initialSnapshot); var edit = new TestEdit(0, 0, initialSnapshot, 0, initialSnapshot, string.Empty); var editorOperationsFactory = new Mock(); var documentTracker = CreateDocumentTracker(() => textBuffer, Mock.Of()); var braceSmartIndenter = new BraceSmartIndenter(Dispatcher, documentTracker, editorOperationsFactory.Object); // Act & Assert textBuffer.ApplyEdits(edit, edit); } [Fact] public void TryCreateIndentationContext_ReturnsFalseIfNoFocusedTextView() { // Arrange var snapshot = new StringTextSnapshot(Environment.NewLine + "Hello World"); ITextBuffer textBuffer = null; var documentTracker = CreateDocumentTracker(() => textBuffer, focusedTextView: null); textBuffer = CreateTextBuffer(snapshot, documentTracker); // Act var result = BraceSmartIndenter.TryCreateIndentationContext(0, Environment.NewLine.Length, Environment.NewLine, documentTracker, out var context); // Assert Assert.Null(context); Assert.False(result); } [Fact] public void TryCreateIndentationContext_ReturnsFalseIfTextChangeIsNotNewline() { // Arrange var snapshot = new StringTextSnapshot("This Hello World"); ITextBuffer textBuffer = null; var focusedTextView = CreateFocusedTextView(() => textBuffer); var documentTracker = CreateDocumentTracker(() => textBuffer, focusedTextView); textBuffer = CreateTextBuffer(snapshot, documentTracker); // Act var result = BraceSmartIndenter.TryCreateIndentationContext(0, 5, "This ", documentTracker, out var context); // Assert Assert.Null(context); Assert.False(result); } [Fact] public void TryCreateIndentationContext_ReturnsFalseIfNewLineIsNotPrecededByOpenBrace_FileStart() { // Arrange var initialSnapshot = new StringTextSnapshot(Environment.NewLine + "Hello World"); ITextBuffer textBuffer = null; var focusedTextView = CreateFocusedTextView(() => textBuffer); var documentTracker = CreateDocumentTracker(() => textBuffer, focusedTextView); textBuffer = CreateTextBuffer(initialSnapshot, documentTracker); // Act var result = BraceSmartIndenter.TryCreateIndentationContext(0, Environment.NewLine.Length, Environment.NewLine, documentTracker, out var context); // Assert Assert.Null(context); Assert.False(result); } [Fact] public void TryCreateIndentationContext_ReturnsFalseIfNewLineIsNotPrecededByOpenBrace_MidFile() { // Arrange var initialSnapshot = new StringTextSnapshot("Hello\u0085World"); ITextBuffer textBuffer = null; var focusedTextView = CreateFocusedTextView(() => textBuffer); var documentTracker = CreateDocumentTracker(() => textBuffer, focusedTextView); textBuffer = CreateTextBuffer(initialSnapshot, documentTracker); // Act var result = BraceSmartIndenter.TryCreateIndentationContext(5, 1, "\u0085", documentTracker, out var context); // Assert Assert.Null(context); Assert.False(result); } [Fact] public void TryCreateIndentationContext_ReturnsFalseIfNewLineIsNotFollowedByCloseBrace() { // Arrange var initialSnapshot = new StringTextSnapshot("@{ " + Environment.NewLine + "World"); ITextBuffer textBuffer = null; var focusedTextView = CreateFocusedTextView(() => textBuffer); var documentTracker = CreateDocumentTracker(() => textBuffer, focusedTextView); textBuffer = CreateTextBuffer(initialSnapshot, documentTracker); // Act var result = BraceSmartIndenter.TryCreateIndentationContext(3, Environment.NewLine.Length, Environment.NewLine, documentTracker, out var context); // Assert Assert.Null(context); Assert.False(result); } [Fact] public void TryCreateIndentationContext_ReturnsTrueIfNewLineIsSurroundedByBraces() { // Arrange var initialSnapshot = new StringTextSnapshot("@{ \n}"); ITextBuffer textBuffer = null; var focusedTextView = CreateFocusedTextView(() => textBuffer); var documentTracker = CreateDocumentTracker(() => textBuffer, focusedTextView); textBuffer = CreateTextBuffer(initialSnapshot, documentTracker); // Act var result = BraceSmartIndenter.TryCreateIndentationContext(3, 1, "\n", documentTracker, out var context); // Assert Assert.NotNull(context); Assert.Same(focusedTextView, context.FocusedTextView); Assert.Equal(3, context.ChangePosition); Assert.True(result); } protected class TestTextContentChangedEventArgs : TextContentChangedEventArgs { public TestTextContentChangedEventArgs(INormalizedTextChangeCollection changeCollection) : base(CreateBeforeSnapshot(changeCollection), new Mock().Object, EditOptions.DefaultMinimalChange, null) { } protected static ITextSnapshot CreateBeforeSnapshot(INormalizedTextChangeCollection collection) { var version = new Mock(); version.Setup(v => v.Changes) .Returns(collection); var snapshot = new Mock(); snapshot.Setup(obj => obj.Version) .Returns(version.Object); return snapshot.Object; } } protected class TestTextChangeCollection : List, INormalizedTextChangeCollection { public bool IncludesLineChanges => throw new NotImplementedException(); } } }