From cda9d126b40e82415e56fb5b020ec6a3be7e6a0a Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Mon, 3 Aug 2015 13:16:37 -0700 Subject: [PATCH] Fix Interactive window indentation The value returned from ```ISmartIndentationService.GetDesiredIndentation``` is absolute, but we were assuming it was relevant to the prompt. Fixes #4235 --- .../Editor/InteractiveWindow.cs | 12 +++- .../InteractiveWindowEditorsFactoryService.cs | 4 +- .../EditorTest/InteractiveWindowTest.csproj | 1 + .../EditorTest/InteractiveWindowTests.cs | 59 +++++++++++++++++++ .../EditorTest/TestSmartIndent.cs | 57 ++++++++++++++++++ 5 files changed, 129 insertions(+), 4 deletions(-) create mode 100644 src/InteractiveWindow/EditorTest/TestSmartIndent.cs diff --git a/src/InteractiveWindow/Editor/InteractiveWindow.cs b/src/InteractiveWindow/Editor/InteractiveWindow.cs index 6c066fe5173..429cb9b3507 100644 --- a/src/InteractiveWindow/Editor/InteractiveWindow.cs +++ b/src/InteractiveWindow/Editor/InteractiveWindow.cs @@ -527,12 +527,18 @@ private void IndentCurrentLine(SnapshotPoint caretPosition) var caretLine = caretPosition.GetContainingLine(); var indentation = _smartIndenterService.GetDesiredIndentation(_textView, caretLine); - if (indentation != null) + if (indentation.HasValue) { + var promptSpan = _projectionSpans[_promptLineMapping[_promptLineMapping.GetMappingIndexByLineNumber(caretLine.LineNumber)].Value]; + Debug.Assert(promptSpan.Kind == ReplSpanKind.Prompt || promptSpan.Kind == ReplSpanKind.SecondaryPrompt || promptSpan.Kind == ReplSpanKind.StandardInputPrompt); + int promptLength = promptSpan.Length; + Debug.Assert(promptLength == 2 || promptLength == 0); // Not required, just expected. + var adjustedIndentationValue = indentation.GetValueOrDefault() - promptLength; + if (caretPosition == caretLine.End) { // create virtual space: - _textView.Caret.MoveTo(new VirtualSnapshotPoint(caretPosition, indentation.Value)); + _textView.Caret.MoveTo(new VirtualSnapshotPoint(caretPosition, adjustedIndentationValue)); } else { @@ -544,7 +550,7 @@ private void IndentCurrentLine(SnapshotPoint caretPosition) // insert whitespace indentation: var options = _textView.Options; - string whitespace = GetWhiteSpaceForVirtualSpace(indentation.Value, options.IsConvertTabsToSpacesEnabled() ? default(int?) : options.GetTabSize()); + string whitespace = GetWhiteSpaceForVirtualSpace(adjustedIndentationValue, options.IsConvertTabsToSpacesEnabled() ? default(int?) : options.GetTabSize()); _currentLanguageBuffer.Insert(langCaret.Value, whitespace); } } diff --git a/src/InteractiveWindow/EditorTest/InteractiveWindowEditorsFactoryService.cs b/src/InteractiveWindow/EditorTest/InteractiveWindowEditorsFactoryService.cs index 3f2a12e0b46..e037f850dce 100644 --- a/src/InteractiveWindow/EditorTest/InteractiveWindowEditorsFactoryService.cs +++ b/src/InteractiveWindow/EditorTest/InteractiveWindowEditorsFactoryService.cs @@ -10,6 +10,8 @@ namespace Microsoft.VisualStudio.InteractiveWindow.UnitTests [Export(typeof(IInteractiveWindowEditorFactoryService))] internal class InteractiveWindowEditorsFactoryService : IInteractiveWindowEditorFactoryService { + public const string ContentType = "text"; + private readonly ITextBufferFactoryService _textBufferFactoryService; private readonly ITextEditorFactoryService _textEditorFactoryService; private readonly IContentTypeRegistryService _contentTypeRegistry; @@ -33,7 +35,7 @@ ITextBuffer IInteractiveWindowEditorFactoryService.CreateAndActivateBuffer(IInte IContentType contentType; if (!window.Properties.TryGetProperty(typeof(IContentType), out contentType)) { - contentType = _contentTypeRegistry.GetContentType("text"); + contentType = _contentTypeRegistry.GetContentType(ContentType); } return _textBufferFactoryService.CreateTextBuffer(contentType); diff --git a/src/InteractiveWindow/EditorTest/InteractiveWindowTest.csproj b/src/InteractiveWindow/EditorTest/InteractiveWindowTest.csproj index a470f9714fb..1933826ac24 100644 --- a/src/InteractiveWindow/EditorTest/InteractiveWindowTest.csproj +++ b/src/InteractiveWindow/EditorTest/InteractiveWindowTest.csproj @@ -88,6 +88,7 @@ + diff --git a/src/InteractiveWindow/EditorTest/InteractiveWindowTests.cs b/src/InteractiveWindow/EditorTest/InteractiveWindowTests.cs index edc507fa93c..849467ab6f4 100644 --- a/src/InteractiveWindow/EditorTest/InteractiveWindowTests.cs +++ b/src/InteractiveWindow/EditorTest/InteractiveWindowTests.cs @@ -408,5 +408,64 @@ public void CallCancelOnNonUIThread() { Task.Run(() => Window.Operations.Cancel()).PumpingWait(); } + + [WorkItem(4235, "https://github.com/dotnet/roslyn/issues/4235")] + [Fact] + public void TestIndentation1() + { + TestIndentation(indentSize: 1); + } + + [WorkItem(4235, "https://github.com/dotnet/roslyn/issues/4235")] + [Fact] + public void TestIndentation2() + { + TestIndentation(indentSize: 2); + } + + [WorkItem(4235, "https://github.com/dotnet/roslyn/issues/4235")] + [Fact] + public void TestIndentation3() + { + TestIndentation(indentSize: 3); + } + + [WorkItem(4235, "https://github.com/dotnet/roslyn/issues/4235")] + [Fact] + public void TestIndentation4() + { + TestIndentation(indentSize: 4); + } + + private void TestIndentation(int indentSize) + { + const int promptWidth = 2; + + _testHost.ExportProvider.GetExport().Value.SmartIndent = new TestSmartIndent( + promptWidth, + promptWidth + indentSize, + promptWidth + ); + + AssertCaretVirtualPosition(0, promptWidth); + Window.InsertCode("{"); + AssertCaretVirtualPosition(0, promptWidth + 1); + Window.Operations.BreakLine(); + AssertCaretVirtualPosition(1, promptWidth + indentSize); + Window.InsertCode("Console.WriteLine();"); + Window.Operations.BreakLine(); + AssertCaretVirtualPosition(2, promptWidth); + Window.InsertCode("}"); + AssertCaretVirtualPosition(2, promptWidth + 1); + } + + private void AssertCaretVirtualPosition(int expectedLine, int expectedColumn) + { + ITextSnapshotLine actualLine; + int actualColumn; + Window.TextView.Caret.Position.VirtualBufferPosition.GetLineAndColumn(out actualLine, out actualColumn); + Assert.Equal(expectedLine, actualLine.LineNumber); + Assert.Equal(expectedColumn, actualColumn); + } } } diff --git a/src/InteractiveWindow/EditorTest/TestSmartIndent.cs b/src/InteractiveWindow/EditorTest/TestSmartIndent.cs new file mode 100644 index 00000000000..6f2f6cf9205 --- /dev/null +++ b/src/InteractiveWindow/EditorTest/TestSmartIndent.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft. 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.ComponentModel.Composition; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.VisualStudio.InteractiveWindow.UnitTests +{ + internal class TestSmartIndent : ISmartIndent + { + private readonly int[] _lineToIndentMap; + + public TestSmartIndent(params int[] lineToIndentMap) + { + _lineToIndentMap = lineToIndentMap; + } + + int? ISmartIndent.GetDesiredIndentation(ITextSnapshotLine line) + { + return _lineToIndentMap[line.LineNumber]; + } + + void IDisposable.Dispose() + { + } + } + + internal class DummySmartIndent : ISmartIndent + { + public static readonly ISmartIndent Instance = new DummySmartIndent(); + + private DummySmartIndent() + { + } + + int? ISmartIndent.GetDesiredIndentation(ITextSnapshotLine line) + { + return null; + } + + void IDisposable.Dispose() + { + } + } + + [Export(typeof(TestSmartIndentProvider))] + [Export(typeof(ISmartIndentProvider))] + [ContentType(InteractiveWindowEditorsFactoryService.ContentType)] + internal class TestSmartIndentProvider : ISmartIndentProvider + { + public ISmartIndent SmartIndent; + + ISmartIndent ISmartIndentProvider.CreateSmartIndent(ITextView textView) => SmartIndent ?? DummySmartIndent.Instance; + } +} \ No newline at end of file -- GitLab