Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/microsoft/vs-editor-api.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKirill Osenkov <github@osenkov.com>2018-08-15 21:13:16 +0300
committerKirill Osenkov <github@osenkov.com>2018-08-15 21:13:16 +0300
commitb407d9744a34fd4647cf3e99068a28d728fd65a2 (patch)
treee6d6cef915f32dec4f2d16b1868045143deb589f /src/Text/Impl
parentcf974b266fb428d846e2e16a25a4eeb8f635c26f (diff)
Updating to VS-Platform 15.8.519 (29d85337c7d2af248a692fd65ac37b444551e4cf).
Diffstat (limited to 'src/Text/Impl')
-rw-r--r--src/Text/Impl/BraceCompletion/BraceCompletionAggregator.cs2
-rw-r--r--src/Text/Impl/BraceCompletion/BraceCompletionAggregatorFactory.cs4
-rw-r--r--src/Text/Impl/BraceCompletion/BraceCompletionDefaultSession.cs5
-rw-r--r--src/Text/Impl/BraceCompletion/BraceCompletionEnabledOption.cs (renamed from src/Text/Impl/BraceCompletion/Options.cs)4
-rw-r--r--src/Text/Impl/BraceCompletion/BraceCompletionManager.cs7
-rw-r--r--src/Text/Impl/BraceCompletion/BraceCompletionStack.cs5
-rw-r--r--src/Text/Impl/ClassificationAggregator/ClassifierAggregator.cs14
-rw-r--r--src/Text/Impl/ClassificationAggregator/ClassifierTagger.cs2
-rw-r--r--src/Text/Impl/ClassificationAggregator/ProjectionWorkaround.cs91
-rw-r--r--src/Text/Impl/ClassificationType/ClassificationTypeImpl.cs2
-rw-r--r--src/Text/Impl/ClassificationType/ClassificationTypeRegistryService.cs12
-rw-r--r--src/Text/Impl/Commanding/EditorCommandHandlerService.cs25
-rw-r--r--src/Text/Impl/DifferenceAlgorithm/DefaultTextDifferencingService.cs12
-rw-r--r--src/Text/Impl/DifferenceAlgorithm/HierarchicalDifferenceCollection.cs6
-rw-r--r--src/Text/Impl/DifferenceAlgorithm/MaximalSubsequenceAlgorithm.cs6
-rw-r--r--src/Text/Impl/DifferenceAlgorithm/SnapshotLineList.cs4
-rw-r--r--src/Text/Impl/DifferenceAlgorithm/TFS/DiffFinder.cs11
-rw-r--r--src/Text/Impl/DifferenceAlgorithm/TFS/LCSDiff.cs20
-rw-r--r--src/Text/Impl/DifferenceAlgorithm/TokenizedStringList.cs2
-rw-r--r--src/Text/Impl/EditorOperations/AfterTextBufferChangeUndoPrimitive.cs136
-rw-r--r--src/Text/Impl/EditorOperations/BeforeTextBufferChangeUndoPrimitive.cs166
-rw-r--r--src/Text/Impl/EditorOperations/CollapsedMoveUndoPrimitive.cs6
-rw-r--r--src/Text/Impl/EditorOperations/Commands/ExpandContractSelectionCommandHandler.cs68
-rw-r--r--src/Text/Impl/EditorOperations/Commands/ExpandContractSelectionImplementation.cs134
-rw-r--r--src/Text/Impl/EditorOperations/Commands/ExpandContractSelectionOptionDefinitions.cs25
-rw-r--r--src/Text/Impl/EditorOperations/Commands/NavigateToNextIssueCommandHandler.cs156
-rw-r--r--src/Text/Impl/EditorOperations/EditorOperations.cs1042
-rw-r--r--src/Text/Impl/EditorOperations/EditorOperationsFactoryService.cs5
-rw-r--r--src/Text/Impl/EditorOperations/Strings.Designer.cs18
-rw-r--r--src/Text/Impl/EditorOperations/Strings.resx6
-rw-r--r--src/Text/Impl/EditorOperations/TextTransactionMergePolicy.cs12
-rw-r--r--src/Text/Impl/EditorOptions/EditorOptions.cs9
-rw-r--r--src/Text/Impl/EditorOptions/EditorOptionsFactoryService.cs4
-rw-r--r--src/Text/Impl/EditorPrimitives/DefaultBufferPrimitive.cs16
-rw-r--r--src/Text/Impl/EditorPrimitives/DefaultDisplayTextPointPrimitive.cs4
-rw-r--r--src/Text/Impl/EditorPrimitives/DefaultSelectionPrimitive.cs2
-rw-r--r--src/Text/Impl/EditorPrimitives/DefaultTextPointPrimitive.cs22
-rw-r--r--src/Text/Impl/EditorPrimitives/DefaultTextRangePrimitive.cs4
-rw-r--r--src/Text/Impl/EditorPrimitives/DefaultTextViewPrimitive.cs5
-rw-r--r--src/Text/Impl/EditorPrimitives/DefaultViewPrimitivesFactoryService.cs2
-rw-r--r--src/Text/Impl/EditorPrimitives/ViewPrimitives.cs4
-rw-r--r--src/Text/Impl/Navigation/TextStructureNavigatorSelectorService.cs6
-rw-r--r--src/Text/Impl/Outlining/OutliningManager.cs18
-rw-r--r--src/Text/Impl/Outlining/OutliningManagerService.cs2
-rw-r--r--src/Text/Impl/PatternMatching/AllLowerCamelCaseMatcher.cs17
-rw-r--r--src/Text/Impl/PatternMatching/ArraySlice.cs8
-rw-r--r--src/Text/Impl/PatternMatching/CamelCaseResult.cs2
-rw-r--r--src/Text/Impl/PatternMatching/ContainerPatternMatcher.cs2
-rw-r--r--src/Text/Impl/PatternMatching/EditDistance.cs7
-rw-r--r--src/Text/Impl/PatternMatching/PatternMatcher.cs8
-rw-r--r--src/Text/Impl/PatternMatching/WordSimilarityChecker.cs2
-rw-r--r--src/Text/Impl/StandaloneUndo/AutoEnclose.cs2
-rw-r--r--src/Text/Impl/StandaloneUndo/CatchOperationsFromHistoryForDelegatedPrimitive.cs2
-rw-r--r--src/Text/Impl/StandaloneUndo/DelegatedUndoPrimitiveImpl.cs4
-rw-r--r--src/Text/Impl/StandaloneUndo/UndoHistoryImpl.cs39
-rw-r--r--src/Text/Impl/StandaloneUndo/UndoHistoryRegistryImpl.cs20
-rw-r--r--src/Text/Impl/StandaloneUndo/UndoTransactionImpl.cs52
-rw-r--r--src/Text/Impl/TagAggregator/TagAggregator.cs4
-rw-r--r--src/Text/Impl/TagAggregator/TagAggregatorFactoryService.cs4
-rw-r--r--src/Text/Impl/TextBufferUndoManager/Strings.Designer.cs7
-rw-r--r--src/Text/Impl/TextBufferUndoManager/TextBufferChangeUndoPrimitive.cs53
-rw-r--r--src/Text/Impl/TextBufferUndoManager/TextBufferUndoManager.cs245
-rw-r--r--src/Text/Impl/TextBufferUndoManager/TextBufferUndoManagerProvider.cs16
-rw-r--r--src/Text/Impl/TextModel/BaseBuffer.cs38
-rw-r--r--src/Text/Impl/TextModel/BaseSnapshot.cs7
-rw-r--r--src/Text/Impl/TextModel/BufferFactoryService.cs28
-rw-r--r--src/Text/Impl/TextModel/EncodedStreamReader.cs2
-rw-r--r--src/Text/Impl/TextModel/FileNameKey.cs52
-rw-r--r--src/Text/Impl/TextModel/FileUtilities.cs25
-rw-r--r--src/Text/Impl/TextModel/ForwardFidelityCustomTrackingSpan.cs2
-rw-r--r--src/Text/Impl/TextModel/HighFidelityTrackingPoint.cs2
-rw-r--r--src/Text/Impl/TextModel/HighFidelityTrackingSpan.cs2
-rw-r--r--src/Text/Impl/TextModel/MappingPoint.cs14
-rw-r--r--src/Text/Impl/TextModel/MappingSpan.cs15
-rw-r--r--src/Text/Impl/TextModel/NativeMethods.cs28
-rw-r--r--src/Text/Impl/TextModel/NormalizedTextChangeCollection.cs13
-rw-r--r--src/Text/Impl/TextModel/PersistentSpan.cs184
-rw-r--r--src/Text/Impl/TextModel/PersistentSpanFactory.cs256
-rw-r--r--src/Text/Impl/TextModel/PersistentSpanSet.cs125
-rw-r--r--src/Text/Impl/TextModel/Projection/BaseProjectionBuffer.cs7
-rw-r--r--src/Text/Impl/TextModel/Projection/BufferGraph.cs58
-rw-r--r--src/Text/Impl/TextModel/Projection/BufferGraphFactoryService.cs2
-rw-r--r--src/Text/Impl/TextModel/Projection/ElisionBuffer.cs9
-rw-r--r--src/Text/Impl/TextModel/Projection/ElisionSnapshot.cs20
-rw-r--r--src/Text/Impl/TextModel/Projection/ProjectionBuffer.cs15
-rw-r--r--src/Text/Impl/TextModel/Projection/ProjectionSnapshot.cs25
-rw-r--r--src/Text/Impl/TextModel/Projection/ProjectionSpanToChangeConverter.cs10
-rw-r--r--src/Text/Impl/TextModel/Storage/CharStream.cs4
-rw-r--r--src/Text/Impl/TextModel/Storage/ILineBreaks.cs12
-rw-r--r--src/Text/Impl/TextModel/Storage/LineBreakManager.cs199
-rw-r--r--src/Text/Impl/TextModel/Storage/TextImageLoader.cs22
-rw-r--r--src/Text/Impl/TextModel/StringRebuilder/BinaryStringRebuilder.cs18
-rw-r--r--src/Text/Impl/TextModel/StringRebuilder/StringRebuilder.cs16
-rw-r--r--src/Text/Impl/TextModel/StringRebuilder/StringRebuilderForChars.cs2
-rw-r--r--src/Text/Impl/TextModel/StringRebuilder/StringRebuilderForCompressedChars.cs2
-rw-r--r--src/Text/Impl/TextModel/StringRebuilder/StringRebuilderForString.cs2
-rw-r--r--src/Text/Impl/TextModel/StringRebuilder/UnaryStringRebuilder.cs24
-rw-r--r--src/Text/Impl/TextModel/TextChange.cs8
-rw-r--r--src/Text/Impl/TextModel/TextDocument.cs24
-rw-r--r--src/Text/Impl/TextModel/TextDocumentFactoryService.cs12
-rw-r--r--src/Text/Impl/TextModel/TextImageVersion.cs4
-rw-r--r--src/Text/Impl/TextModel/TextVersion.cs11
-rw-r--r--src/Text/Impl/TextModel/TrackingPoint.cs10
-rw-r--r--src/Text/Impl/TextModel/TrackingSpan.cs10
-rw-r--r--src/Text/Impl/TextModel/TrivialNormalizedTextChangeCollection.cs6
-rw-r--r--src/Text/Impl/TextSearch/BackgroundSearch.cs3
-rw-r--r--src/Text/Impl/TextSearch/TextSearchNavigatorFactoryService.cs2
-rw-r--r--src/Text/Impl/TextSearch/TextSearchService.cs54
-rw-r--r--src/Text/Impl/TextSearch/TextSearchTagger.cs6
-rw-r--r--src/Text/Impl/TextSearch/TextSearchTaggerFactoryService.cs2
110 files changed, 2078 insertions, 1914 deletions
diff --git a/src/Text/Impl/BraceCompletion/BraceCompletionAggregator.cs b/src/Text/Impl/BraceCompletion/BraceCompletionAggregator.cs
index fa25233..dbf866a 100644
--- a/src/Text/Impl/BraceCompletion/BraceCompletionAggregator.cs
+++ b/src/Text/Impl/BraceCompletion/BraceCompletionAggregator.cs
@@ -249,7 +249,7 @@ namespace Microsoft.VisualStudio.Text.BraceCompletion.Implementation
/// This checks the type against all others until it finds one that it is
/// a type of. List.Sort() does not work here since most types are unrelated.
/// </summary>
- private List<IContentType> SortContentTypes(List<IContentType> contentTypes)
+ private static List<IContentType> SortContentTypes(List<IContentType> contentTypes)
{
List<IContentType> sorted = new List<IContentType>(contentTypes.Count);
diff --git a/src/Text/Impl/BraceCompletion/BraceCompletionAggregatorFactory.cs b/src/Text/Impl/BraceCompletion/BraceCompletionAggregatorFactory.cs
index b2179b3..4795373 100644
--- a/src/Text/Impl/BraceCompletion/BraceCompletionAggregatorFactory.cs
+++ b/src/Text/Impl/BraceCompletion/BraceCompletionAggregatorFactory.cs
@@ -26,7 +26,7 @@ namespace Microsoft.VisualStudio.Text.BraceCompletion.Implementation
internal IContentTypeRegistryService ContentTypeRegistryService { get; private set; }
internal ITextBufferUndoManagerProvider UndoManager { get; private set; }
internal IEditorOperationsFactoryService EditorOperationsFactoryService { get; private set; }
- internal GuardedOperations GuardedOperations { get; private set; }
+ internal IGuardedOperations GuardedOperations { get; private set; }
#endregion
@@ -40,7 +40,7 @@ namespace Microsoft.VisualStudio.Text.BraceCompletion.Implementation
IContentTypeRegistryService contentTypeRegistryService,
ITextBufferUndoManagerProvider undoManager,
IEditorOperationsFactoryService editorOperationsFactoryService,
- GuardedOperations guardedOperations)
+ IGuardedOperations guardedOperations)
{
SessionProviders = sessionProviders;
ContextProviders = contextProviders;
diff --git a/src/Text/Impl/BraceCompletion/BraceCompletionDefaultSession.cs b/src/Text/Impl/BraceCompletion/BraceCompletionDefaultSession.cs
index 6877b4e..f58b3e1 100644
--- a/src/Text/Impl/BraceCompletion/BraceCompletionDefaultSession.cs
+++ b/src/Text/Impl/BraceCompletion/BraceCompletionDefaultSession.cs
@@ -12,6 +12,7 @@ namespace Microsoft.VisualStudio.Text.BraceCompletion.Implementation
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Operations;
using System.Diagnostics;
+ using System.Globalization;
/// <summary>
/// BraceCompletionDefaultSession is a language neutral brace completion session
@@ -102,7 +103,7 @@ namespace Microsoft.VisualStudio.Text.BraceCompletion.Implementation
// insert the closing brace
using (ITextEdit edit = _subjectBuffer.CreateEdit())
{
- edit.Insert(closingSnapshotPoint, _closingBrace.ToString());
+ edit.Insert(closingSnapshotPoint, _closingBrace.ToString(CultureInfo.CurrentCulture));
if (edit.HasFailedChanges)
{
@@ -125,7 +126,7 @@ namespace Microsoft.VisualStudio.Text.BraceCompletion.Implementation
_closingPoint = SubjectBuffer.CurrentSnapshot.CreateTrackingPoint(_closingPoint.GetPoint(snapshot), PointTrackingMode.Negative);
Debug.Assert(_closingPoint.GetPoint(snapshot).Position > 0 && (new SnapshotSpan(_closingPoint.GetPoint(snapshot).Subtract(1), 1))
- .GetText().Equals(_closingBrace.ToString()), "The closing point does not match the closing brace character");
+ .GetText().Equals(_closingBrace.ToString(CultureInfo.CurrentCulture), System.StringComparison.Ordinal), "The closing point does not match the closing brace character");
// move the caret back between the braces
_textView.Caret.MoveTo(beforePoint);
diff --git a/src/Text/Impl/BraceCompletion/Options.cs b/src/Text/Impl/BraceCompletion/BraceCompletionEnabledOption.cs
index ced4d14..b36dd5f 100644
--- a/src/Text/Impl/BraceCompletion/Options.cs
+++ b/src/Text/Impl/BraceCompletion/BraceCompletionEnabledOption.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
//
@@ -7,11 +7,9 @@
//
namespace Microsoft.VisualStudio.Text.BraceCompletion.Implementation
{
- using Microsoft.VisualStudio.Text.Classification;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Utilities;
using System.ComponentModel.Composition;
- using System.Windows;
[Export(typeof(EditorOptionDefinition))]
[Name(DefaultTextViewOptions.BraceCompletionEnabledOptionName)]
diff --git a/src/Text/Impl/BraceCompletion/BraceCompletionManager.cs b/src/Text/Impl/BraceCompletion/BraceCompletionManager.cs
index 7cfe59c..b5f9689 100644
--- a/src/Text/Impl/BraceCompletion/BraceCompletionManager.cs
+++ b/src/Text/Impl/BraceCompletion/BraceCompletionManager.cs
@@ -9,6 +9,7 @@ namespace Microsoft.VisualStudio.Text.BraceCompletion.Implementation
{
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Utilities;
+ using Microsoft.VisualStudio.Utilities;
using System;
using System.Diagnostics;
@@ -24,7 +25,7 @@ namespace Microsoft.VisualStudio.Text.BraceCompletion.Implementation
private readonly IBraceCompletionAggregatorFactory _sessionFactory;
private readonly IBraceCompletionAggregator _sessionAggregator;
private readonly ITextView _textView;
- private readonly GuardedOperations _guardedOperations;
+ private readonly IGuardedOperations _guardedOperations;
private bool _braceCompletionEnabled;
@@ -36,7 +37,7 @@ namespace Microsoft.VisualStudio.Text.BraceCompletion.Implementation
#region Constructors
- internal BraceCompletionManager(ITextView textView, IBraceCompletionStack stack, IBraceCompletionAggregatorFactory sessionFactory, GuardedOperations guardedOperations)
+ internal BraceCompletionManager(ITextView textView, IBraceCompletionStack stack, IBraceCompletionAggregatorFactory sessionFactory, IGuardedOperations guardedOperations)
{
_textView = textView;
_stack = stack;
@@ -450,7 +451,7 @@ namespace Microsoft.VisualStudio.Text.BraceCompletion.Implementation
}
}
- private bool IsSingleLine(ITrackingPoint openingPoint, ITrackingPoint closingPoint)
+ private static bool IsSingleLine(ITrackingPoint openingPoint, ITrackingPoint closingPoint)
{
if (openingPoint != null && closingPoint != null)
{
diff --git a/src/Text/Impl/BraceCompletion/BraceCompletionStack.cs b/src/Text/Impl/BraceCompletion/BraceCompletionStack.cs
index 311d493..ac1ff29 100644
--- a/src/Text/Impl/BraceCompletion/BraceCompletionStack.cs
+++ b/src/Text/Impl/BraceCompletion/BraceCompletionStack.cs
@@ -11,6 +11,7 @@ namespace Microsoft.VisualStudio.Text.BraceCompletion.Implementation
using Microsoft.VisualStudio.Text.BraceCompletion;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Utilities;
+ using Microsoft.VisualStudio.Utilities;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -31,11 +32,11 @@ namespace Microsoft.VisualStudio.Text.BraceCompletion.Implementation
private IBraceCompletionAdornmentServiceFactory _adornmentServiceFactory;
private IBraceCompletionAdornmentService _adornmentService;
- private GuardedOperations _guardedOperations;
+ private IGuardedOperations _guardedOperations;
#endregion
#region Constructors
- public BraceCompletionStack(ITextView textView, IBraceCompletionAdornmentServiceFactory adornmentFactory, GuardedOperations guardedOperations)
+ public BraceCompletionStack(ITextView textView, IBraceCompletionAdornmentServiceFactory adornmentFactory, IGuardedOperations guardedOperations)
{
_adornmentServiceFactory = adornmentFactory;
_stack = new Stack<IBraceCompletionSession>();
diff --git a/src/Text/Impl/ClassificationAggregator/ClassifierAggregator.cs b/src/Text/Impl/ClassificationAggregator/ClassifierAggregator.cs
index b10eee8..95b3bdf 100644
--- a/src/Text/Impl/ClassificationAggregator/ClassifierAggregator.cs
+++ b/src/Text/Impl/ClassificationAggregator/ClassifierAggregator.cs
@@ -37,15 +37,15 @@ namespace Microsoft.VisualStudio.Text.Classification.Implementation
// Validate.
if (textBuffer == null)
{
- throw new ArgumentNullException("textBuffer");
+ throw new ArgumentNullException(nameof(textBuffer));
}
if (bufferTagAggregatorFactory == null)
{
- throw new ArgumentNullException("bufferTagAggregatorFactory");
+ throw new ArgumentNullException(nameof(bufferTagAggregatorFactory));
}
if (classificationTypeRegistry == null)
{
- throw new ArgumentNullException("classificationTypeRegistry");
+ throw new ArgumentNullException(nameof(classificationTypeRegistry));
}
_textBuffer = textBuffer;
@@ -63,15 +63,15 @@ namespace Microsoft.VisualStudio.Text.Classification.Implementation
// Validate.
if (textView == null)
{
- throw new ArgumentNullException("textView");
+ throw new ArgumentNullException(nameof(textView));
}
if (viewTagAggregatorFactory == null)
{
- throw new ArgumentNullException("viewTagAggregatorFactory");
+ throw new ArgumentNullException(nameof(viewTagAggregatorFactory));
}
if (classificationTypeRegistry == null)
{
- throw new ArgumentNullException("classificationTypeRegistry");
+ throw new ArgumentNullException(nameof(classificationTypeRegistry));
}
_textBuffer = textView.TextBuffer;
@@ -336,7 +336,7 @@ namespace Microsoft.VisualStudio.Text.Classification.Implementation
return results;
}
- private int Compare(PointData a, PointData b)
+ private static int Compare(PointData a, PointData b)
{
if (a.Position == b.Position)
return (b.IsStart.CompareTo(a.IsStart)); // startpoints go before end points when positions are tied
diff --git a/src/Text/Impl/ClassificationAggregator/ClassifierTagger.cs b/src/Text/Impl/ClassificationAggregator/ClassifierTagger.cs
index 653e768..4871c38 100644
--- a/src/Text/Impl/ClassificationAggregator/ClassifierTagger.cs
+++ b/src/Text/Impl/ClassificationAggregator/ClassifierTagger.cs
@@ -70,7 +70,9 @@ namespace Microsoft.VisualStudio.Text.Classification.Implementation
#region IDisposable members
+#pragma warning disable CA1063 // Implement IDisposable Correctly
public void Dispose()
+#pragma warning restore CA1063 // Implement IDisposable Correctly
{
foreach(var classifier in Classifiers)
{
diff --git a/src/Text/Impl/ClassificationAggregator/ProjectionWorkaround.cs b/src/Text/Impl/ClassificationAggregator/ProjectionWorkaround.cs
index 35a5835..b2a65a3 100644
--- a/src/Text/Impl/ClassificationAggregator/ProjectionWorkaround.cs
+++ b/src/Text/Impl/ClassificationAggregator/ProjectionWorkaround.cs
@@ -9,12 +9,9 @@ namespace Microsoft.VisualStudio.Text.Classification.Implementation
{
using System;
using System.Collections.Generic;
- using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
- using Microsoft.VisualStudio.Text.Differencing;
using Microsoft.VisualStudio.Text.Projection;
using Microsoft.VisualStudio.Text.Tagging;
- using Microsoft.VisualStudio.Text.Utilities;
using Microsoft.VisualStudio.Utilities;
[Export(typeof(ITaggerProvider))]
@@ -22,16 +19,13 @@ namespace Microsoft.VisualStudio.Text.Classification.Implementation
[TagType(typeof(ClassificationTag))]
internal class ProjectionWorkaroundProvider : ITaggerProvider
{
- [Import]
- internal IDifferenceService diffService { get; set; }
-
public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag
{
IProjectionBuffer projectionBuffer = buffer as IProjectionBuffer;
if (projectionBuffer == null)
return null;
- return new ProjectionWorkaroundTagger(projectionBuffer, diffService) as ITagger<T>;
+ return new ProjectionWorkaroundTagger(projectionBuffer) as ITagger<T>;
}
}
@@ -45,82 +39,69 @@ namespace Microsoft.VisualStudio.Text.Classification.Implementation
internal class ProjectionWorkaroundTagger : ITagger<ClassificationTag>
{
IProjectionBuffer ProjectionBuffer { get; set; }
- IDifferenceService diffService;
- internal ProjectionWorkaroundTagger(IProjectionBuffer projectionBuffer, IDifferenceService diffService)
+ internal ProjectionWorkaroundTagger(IProjectionBuffer projectionBuffer)
{
this.ProjectionBuffer = projectionBuffer;
- this.diffService = diffService;
this.ProjectionBuffer.SourceBuffersChanged += SourceSpansChanged;
}
#region ITagger<ClassificationTag> members
public IEnumerable<ITagSpan<ClassificationTag>> GetTags(NormalizedSnapshotSpanCollection spans)
{
- yield break;
+ return Array.Empty<ITagSpan<ClassificationTag>>();
}
public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
-
#endregion
#region Source span differencing + change event
private void SourceSpansChanged(object sender, ProjectionSourceSpansChangedEventArgs e)
{
- if (e.Changes.Count == 0)
+ var handler = TagsChanged;
+ if ((handler != null) && (e.Changes.Count == 0))
{
// If there weren't text changes, but there were span changes, then
// send out a classification changed event over the spans that changed.
- ProjectionSpanDifference difference = ProjectionSpanDiffer.DiffSourceSpans(this.diffService, e.Before, e.After);
- int pos = 0;
- int start = int.MaxValue;
- int end = int.MinValue;
- foreach (var diff in difference.DifferenceCollection)
- {
- pos += GetMatchSize(difference.DeletedSpans, diff.Before);
- start = Math.Min(start, pos);
-
- // Now, for every span added in the new snapshot that replaced
- // the deleted spans, add it to our span to raise changed events
- // over.
- for (int i = diff.Right.Start; i < diff.Right.End; i++)
- {
- pos += difference.InsertedSpans[i].Length;
- }
-
- end = Math.Max(end, pos);
- }
+ //
+ // We're raising a single event here so all we need is the start of the first changed span
+ // to the end of the last changed span (or, as we calculate it, the end of the first identical
+ // spans to the start of the last identical spans).
+ //
+ // Note that we are being generous in the span we raise. For example if I change the projection buffer
+ // from projecting (V0:[0,10)) and (V0:[10,15)) to projecting (V0:[0,5)) and (V0:[5,15)) we'll raise a snapshot changed
+ // event over the entire buffer even though neither the projected text nor the content type of its buffer
+ // changed. This case shouldn't happen very often and the cost of (falsely) raising a classification changed
+ // event is pretty small so this is a net perf win compared to doing a more expensive diff to get the actual
+ // changed span.
+ var leftSpans = e.Before.GetSourceSpans();
+ var rightSpans = e.After.GetSourceSpans();
+ var spansToCompare = Math.Min(leftSpans.Count, rightSpans.Count);
- if (start != int.MaxValue && end != int.MinValue)
+ int start = 0;
+ int identicalSpansAtStart = 0;
+ while ((identicalSpansAtStart < spansToCompare) && (leftSpans[identicalSpansAtStart] == rightSpans[identicalSpansAtStart]))
{
- RaiseTagsChangedEvent(new SnapshotSpan(e.After, Span.FromBounds(start, end)));
+ start += rightSpans[identicalSpansAtStart].Length;
+ ++identicalSpansAtStart;
}
- }
- }
- private static int GetMatchSize(ReadOnlyCollection<SnapshotSpan> spans, Match match)
- {
- int size = 0;
- if (match != null)
- {
- Span extent = match.Left;
- for (int s = extent.Start; s < extent.End; ++s)
+ if ((identicalSpansAtStart < leftSpans.Count) || (identicalSpansAtStart < rightSpans.Count))
{
- size += spans[s].Length;
- }
- }
- return size;
- }
+ // There are at least some span differences between leftSpans and rightSpans so we don't need to worry about running over.
+ spansToCompare -= identicalSpansAtStart; //No need to compare spans in the starting identical block.
+ int end = e.After.Length;
+ int identicalSpansAtEndPlus1 = 1;
+ while ((identicalSpansAtEndPlus1 <= spansToCompare) && (leftSpans[leftSpans.Count - identicalSpansAtEndPlus1] == rightSpans[rightSpans.Count - identicalSpansAtEndPlus1]))
+ {
+ end -= rightSpans[rightSpans.Count - identicalSpansAtEndPlus1].Length;
+ ++identicalSpansAtEndPlus1;
+ }
- private void RaiseTagsChangedEvent(SnapshotSpan span)
- {
- var handler = TagsChanged;
- if (handler != null)
- {
- handler(this, new SnapshotSpanEventArgs(span));
+ handler(this, new SnapshotSpanEventArgs(new SnapshotSpan(e.After, Span.FromBounds(start, end))));
+ }
}
}
-
#endregion
}
}
diff --git a/src/Text/Impl/ClassificationType/ClassificationTypeImpl.cs b/src/Text/Impl/ClassificationType/ClassificationTypeImpl.cs
index 6f3409f..5a47305 100644
--- a/src/Text/Impl/ClassificationType/ClassificationTypeImpl.cs
+++ b/src/Text/Impl/ClassificationType/ClassificationTypeImpl.cs
@@ -38,7 +38,7 @@ namespace Microsoft.VisualStudio.Text.Classification.Implementation
public bool IsOfType(string type)
{
- if (this.name == type)
+ if (string.Equals(this.name, type, System.StringComparison.Ordinal))
return true;
else if (this.baseTypes != null)
{
diff --git a/src/Text/Impl/ClassificationType/ClassificationTypeRegistryService.cs b/src/Text/Impl/ClassificationType/ClassificationTypeRegistryService.cs
index d890eb4..6f746a0 100644
--- a/src/Text/Impl/ClassificationType/ClassificationTypeRegistryService.cs
+++ b/src/Text/Impl/ClassificationType/ClassificationTypeRegistryService.cs
@@ -59,12 +59,12 @@ namespace Microsoft.VisualStudio.Text.Classification.Implementation
{
if (type == null)
{
- throw new ArgumentNullException("type");
+ throw new ArgumentNullException(nameof(type));
}
if (baseTypes == null)
{
- throw new ArgumentNullException("baseTypes");
+ throw new ArgumentNullException(nameof(baseTypes));
}
if (ClassificationTypes.ContainsKey(type))
{
@@ -94,7 +94,7 @@ namespace Microsoft.VisualStudio.Text.Classification.Implementation
// Validate
if (baseTypes == null)
{
- throw new ArgumentNullException("baseTypes");
+ throw new ArgumentNullException(nameof(baseTypes));
}
if (!baseTypes.GetEnumerator().MoveNext())
{
@@ -115,7 +115,7 @@ namespace Microsoft.VisualStudio.Text.Classification.Implementation
// Validate
if (baseTypes == null)
{
- throw new ArgumentNullException("baseTypes");
+ throw new ArgumentNullException(nameof(baseTypes));
}
if (baseTypes.Length == 0)
{
@@ -150,7 +150,7 @@ namespace Microsoft.VisualStudio.Text.Classification.Implementation
{
if (_classificationTypes == null)
{
- _classificationTypes = new Dictionary<string, ClassificationTypeImpl>(StringComparer.InvariantCultureIgnoreCase);
+ _classificationTypes = new Dictionary<string, ClassificationTypeImpl>(StringComparer.OrdinalIgnoreCase);
BuildClassificationTypes(_classificationTypes);
}
return _classificationTypes;
@@ -209,7 +209,7 @@ namespace Microsoft.VisualStudio.Text.Classification.Implementation
// Lazily init
if (_transientClassificationTypes == null)
{
- _transientClassificationTypes = new Dictionary<string, ClassificationTypeImpl>(StringComparer.InvariantCultureIgnoreCase);
+ _transientClassificationTypes = new Dictionary<string, ClassificationTypeImpl>(StringComparer.OrdinalIgnoreCase);
}
List<IClassificationType> sortedBaseTypes = new List<IClassificationType>(baseTypes);
diff --git a/src/Text/Impl/Commanding/EditorCommandHandlerService.cs b/src/Text/Impl/Commanding/EditorCommandHandlerService.cs
index 315e55f..4ced5c3 100644
--- a/src/Text/Impl/Commanding/EditorCommandHandlerService.cs
+++ b/src/Text/Impl/Commanding/EditorCommandHandlerService.cs
@@ -9,6 +9,7 @@ using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Threading;
using ICommandHandlerAndMetadata = System.Lazy<Microsoft.VisualStudio.Commanding.ICommandHandler, Microsoft.VisualStudio.UI.Text.Commanding.Implementation.ICommandHandlerMetadata>;
+using System.Runtime.CompilerServices;
namespace Microsoft.VisualStudio.UI.Text.Commanding.Implementation
{
@@ -124,20 +125,38 @@ namespace Microsoft.VisualStudio.UI.Text.Commanding.Implementation
handlerChain();
}
+ if (handler is IDynamicCommandHandler<T> dynamicCommandHandler &&
+ !dynamicCommandHandler.CanExecuteCommand(args))
+ {
+ // Skip this one as it cannot execute the command.
+ continue;
+ }
+
if (commandExecutionContext == null)
{
commandExecutionContext = CreateCommandExecutionContext();
}
- handlerChain = () => _guardedOperations.CallExtensionPoint(handler, () => handler.ExecuteCommand(args, nextHandler, commandExecutionContext));
+ handlerChain = () => _guardedOperations.CallExtensionPoint(handler,
+ () => handler.ExecuteCommand(args, nextHandler, commandExecutionContext),
+ // Do not guard against cancellation exceptions, they are handled by ExecuteCommandHandlerChain
+ exceptionGuardFilter: (e) => !IsOperationCancelledException(e));
}
ExecuteCommandHandlerChain(commandExecutionContext, handlerChain, nextCommandHandler);
}
}
- private void ExecuteCommandHandlerChain(CommandExecutionContext commandExecutionContext,
- Action handlerChain, Action nextCommandHandler)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool IsOperationCancelledException(Exception e)
+ {
+ return e is OperationCanceledException || e is AggregateException aggregate && aggregate.InnerExceptions.All(ie => ie is OperationCanceledException);
+ }
+
+ private static void ExecuteCommandHandlerChain(
+ CommandExecutionContext commandExecutionContext,
+ Action handlerChain,
+ Action nextCommandHandler)
{
try
{
diff --git a/src/Text/Impl/DifferenceAlgorithm/DefaultTextDifferencingService.cs b/src/Text/Impl/DifferenceAlgorithm/DefaultTextDifferencingService.cs
index 85f1156..4a0d84e 100644
--- a/src/Text/Impl/DifferenceAlgorithm/DefaultTextDifferencingService.cs
+++ b/src/Text/Impl/DifferenceAlgorithm/DefaultTextDifferencingService.cs
@@ -53,7 +53,7 @@ namespace Microsoft.VisualStudio.Text.Differencing.Implementation
}
else
{
- throw new ArgumentOutOfRangeException("differenceOptions");
+ throw new ArgumentOutOfRangeException(nameof(differenceOptions));
}
return DiffText(left, right, type, differenceOptions);
@@ -92,7 +92,7 @@ namespace Microsoft.VisualStudio.Text.Differencing.Implementation
}
else
{
- throw new ArgumentOutOfRangeException("differenceOptions");
+ throw new ArgumentOutOfRangeException(nameof(differenceOptions));
}
return DiffText(left, right, type, differenceOptions);
@@ -103,7 +103,7 @@ namespace Microsoft.VisualStudio.Text.Differencing.Implementation
StringDifferenceOptions nextOptions = new StringDifferenceOptions(differenceOptions);
nextOptions.DifferenceType &= ~type;
- var diffCollection = ComputeMatches(type, differenceOptions, left, right);
+ var diffCollection = ComputeMatches(differenceOptions, left, right);
return new HierarchicalDifferenceCollection(diffCollection, left, right, this, nextOptions);
}
@@ -133,13 +133,13 @@ namespace Microsoft.VisualStudio.Text.Differencing.Implementation
return line.GetTextIncludingLineBreak();
}
- static IDifferenceCollection<string> ComputeMatches(StringDifferenceTypes differenceType, StringDifferenceOptions differenceOptions,
+ static IDifferenceCollection<string> ComputeMatches(StringDifferenceOptions differenceOptions,
IList<string> leftSequence, IList<string> rightSequence)
{
- return ComputeMatches(differenceType, differenceOptions, leftSequence, rightSequence, leftSequence, rightSequence);
+ return ComputeMatches(differenceOptions, leftSequence, rightSequence, leftSequence, rightSequence);
}
- static IDifferenceCollection<string> ComputeMatches(StringDifferenceTypes differenceType, StringDifferenceOptions differenceOptions,
+ static IDifferenceCollection<string> ComputeMatches(StringDifferenceOptions differenceOptions,
IList<string> leftSequence, IList<string> rightSequence,
IList<string> originalLeftSequence, IList<string> originalRightSequence)
{
diff --git a/src/Text/Impl/DifferenceAlgorithm/HierarchicalDifferenceCollection.cs b/src/Text/Impl/DifferenceAlgorithm/HierarchicalDifferenceCollection.cs
index 33d06cb..93f5d32 100644
--- a/src/Text/Impl/DifferenceAlgorithm/HierarchicalDifferenceCollection.cs
+++ b/src/Text/Impl/DifferenceAlgorithm/HierarchicalDifferenceCollection.cs
@@ -46,11 +46,11 @@ namespace Microsoft.VisualStudio.Text.Differencing.Implementation
StringDifferenceOptions options)
{
if (differenceCollection == null)
- throw new ArgumentNullException("differenceCollection");
+ throw new ArgumentNullException(nameof(differenceCollection));
if (left == null)
- throw new ArgumentNullException("left");
+ throw new ArgumentNullException(nameof(left));
if (right == null)
- throw new ArgumentNullException("right");
+ throw new ArgumentNullException(nameof(right));
if (!object.ReferenceEquals(left, differenceCollection.LeftSequence))
throw new ArgumentException("left must equal differenceCollection.LeftSequence");
if (!object.ReferenceEquals(right, differenceCollection.RightSequence))
diff --git a/src/Text/Impl/DifferenceAlgorithm/MaximalSubsequenceAlgorithm.cs b/src/Text/Impl/DifferenceAlgorithm/MaximalSubsequenceAlgorithm.cs
index a40aa72..2f664d9 100644
--- a/src/Text/Impl/DifferenceAlgorithm/MaximalSubsequenceAlgorithm.cs
+++ b/src/Text/Impl/DifferenceAlgorithm/MaximalSubsequenceAlgorithm.cs
@@ -20,7 +20,7 @@ namespace Microsoft.VisualStudio.Text.Differencing.Implementation
internal sealed class MaximalSubsequenceAlgorithm : IDifferenceService
{
#region IDifferenceService Members
- static readonly Microsoft.TeamFoundation.Diff.Copy.IDiffChange[] Empty = new Microsoft.TeamFoundation.Diff.Copy.IDiffChange[0];
+ static readonly Microsoft.TeamFoundation.Diff.Copy.IDiffChange[] Empty = Array.Empty<TeamFoundation.Diff.Copy.IDiffChange>();
public IDifferenceCollection<T> DifferenceSequences<T>(IList<T> left, IList<T> right)
{
@@ -37,9 +37,9 @@ namespace Microsoft.VisualStudio.Text.Differencing.Implementation
internal static DifferenceCollection<T> DifferenceSequences<T>(IList<T> left, IList<T> right, IList<T> originalLeft, IList<T> originalRight, ContinueProcessingPredicate<T> continueProcessingPredicate)
{
if (left == null)
- throw new ArgumentNullException("left");
+ throw new ArgumentNullException(nameof(left));
if (right == null)
- throw new ArgumentNullException("right");
+ throw new ArgumentNullException(nameof(right));
Microsoft.TeamFoundation.Diff.Copy.IDiffChange[] changes;
if ((left.Count == 0) || (right.Count == 0))
diff --git a/src/Text/Impl/DifferenceAlgorithm/SnapshotLineList.cs b/src/Text/Impl/DifferenceAlgorithm/SnapshotLineList.cs
index 47df212..20e92c3 100644
--- a/src/Text/Impl/DifferenceAlgorithm/SnapshotLineList.cs
+++ b/src/Text/Impl/DifferenceAlgorithm/SnapshotLineList.cs
@@ -27,7 +27,7 @@ namespace Microsoft.VisualStudio.Text.Differencing.Implementation
public SnapshotLineList(SnapshotSpan snapshotSpan, Func<ITextSnapshotLine, string> getLineTextCallback, StringDifferenceOptions options)
{
if (getLineTextCallback == null)
- throw new ArgumentNullException("getLineTextCallback");
+ throw new ArgumentNullException(nameof(getLineTextCallback));
if ((options.DifferenceType & StringDifferenceTypes.Line) == 0)
throw new InvalidOperationException("This collection can only be used for line differencing");
@@ -96,7 +96,7 @@ namespace Microsoft.VisualStudio.Text.Differencing.Implementation
SnapshotSpan GetSpanOfIndex(int index)
{
if (index < 0 || index >= _lineSpan.Length)
- throw new ArgumentOutOfRangeException("index");
+ throw new ArgumentOutOfRangeException(nameof(index));
ITextSnapshotLine line = _snapshotSpan.Snapshot.GetLineFromLineNumber(_lineSpan.Start + index);
SnapshotSpan? lineSpan = line.ExtentIncludingLineBreak.Intersection(_snapshotSpan);
diff --git a/src/Text/Impl/DifferenceAlgorithm/TFS/DiffFinder.cs b/src/Text/Impl/DifferenceAlgorithm/TFS/DiffFinder.cs
index 6e2bfd3..e401345 100644
--- a/src/Text/Impl/DifferenceAlgorithm/TFS/DiffFinder.cs
+++ b/src/Text/Impl/DifferenceAlgorithm/TFS/DiffFinder.cs
@@ -515,7 +515,7 @@ namespace Microsoft.TeamFoundation.Diff.Copy
/// the constructed changes.
/// </summary>
//*************************************************************************
- internal class DiffChangeHelper : IDisposable
+ internal sealed class DiffChangeHelper : IDisposable
{
//*********************************************************************
/// <summary>
@@ -657,6 +657,7 @@ namespace Microsoft.TeamFoundation.Diff.Copy
/// A base for classes which compute the differences between two input sequences.
/// </summary>
//*************************************************************************
+#pragma warning disable CA1063 // Implement IDisposable Correctly
public abstract class DiffFinder<T> : IDisposable
{
//*************************************************************************
@@ -689,12 +690,14 @@ namespace Microsoft.TeamFoundation.Diff.Copy
get { return m_elementComparer; }
}
+
//*************************************************************************
/// <summary>
/// Disposes resources used by this DiffFinder
/// </summary>
//*************************************************************************
public virtual void Dispose()
+#pragma warning restore CA1063 // Implement IDisposable Correctly
{
if (m_originalIds != null)
{
@@ -914,7 +917,7 @@ namespace Microsoft.TeamFoundation.Diff.Copy
Debug.Assert(modifiedStart == modifiedEnd + 1, "modifiedStart should only be one more than modifiedEnd");
// Identical sequences - No differences
- changes = new IDiffChange[0];
+ changes = Array.Empty<IDiffChange>();
}
return changes;
@@ -948,7 +951,9 @@ namespace Microsoft.TeamFoundation.Diff.Copy
/// </summary>
//*************************************************************************
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Lcs")]
+#pragma warning disable CA1000 // Do not declare static members on generic types
public static DiffFinder<T> LcsDiff
+#pragma warning restore CA1000 // Do not declare static members on generic types
{
get { return new LcsDiff<T>(); }
}
@@ -971,4 +976,4 @@ namespace Microsoft.TeamFoundation.Diff.Copy
//Early termination predicate
private ContinueDifferencePredicate<T> m_predicate;
}
-} \ No newline at end of file
+}
diff --git a/src/Text/Impl/DifferenceAlgorithm/TFS/LCSDiff.cs b/src/Text/Impl/DifferenceAlgorithm/TFS/LCSDiff.cs
index c70ca9d..9fd9720 100644
--- a/src/Text/Impl/DifferenceAlgorithm/TFS/LCSDiff.cs
+++ b/src/Text/Impl/DifferenceAlgorithm/TFS/LCSDiff.cs
@@ -6,10 +6,8 @@
// Use at your own risk.
//
using System;
-using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
-using System.Text;
//*************************************************************************
// The code from this point on is a soure-port of the TFS diff algorithm, to be available
@@ -54,7 +52,6 @@ namespace Microsoft.TeamFoundation.Diff.Copy
{
m_reverseHistory = null;
}
- GC.SuppressFinalize(this);
}
//*************************************************************************
@@ -138,7 +135,7 @@ namespace Microsoft.TeamFoundation.Diff.Copy
Debug.Assert(modifiedStart == modifiedEnd + 1, "modifiedStart should only be one more than modifiedEnd");
// Identical sequences - No differences
- changes = new IDiffChange[0];
+ changes = Array.Empty<IDiffChange>();
}
return changes;
@@ -162,7 +159,7 @@ namespace Microsoft.TeamFoundation.Diff.Copy
// Second Half: (midOriginal + 1, minModified + 1) to (originalEnd, modifiedEnd)
// NOTE: ComputeDiff() is inclusive, therefore the second range starts on the next point
IDiffChange[] leftChanges = ComputeDiffRecursive(originalStart, midOriginal, modifiedStart, midModified, out quitEarly);
- IDiffChange[] rightChanges = new IDiffChange[0];
+ IDiffChange[] rightChanges = Array.Empty<IDiffChange>();
if (!quitEarly)
{
@@ -671,7 +668,7 @@ namespace Microsoft.TeamFoundation.Diff.Copy
/// <param name="right">The right changes</param>
/// <returns>The concatenated list</returns>
//*************************************************************************
- private IDiffChange[] ConcatenateChanges(IDiffChange[] left, IDiffChange[] right)
+ private static IDiffChange[] ConcatenateChanges(IDiffChange[] left, IDiffChange[] right)
{
IDiffChange mergedChange;
@@ -713,7 +710,7 @@ namespace Microsoft.TeamFoundation.Diff.Copy
/// null otherwise</param>
/// <returns>True if the two changes overlap</returns>
//*************************************************************************
- private bool ChangesOverlap(IDiffChange left, IDiffChange right, out IDiffChange mergedChange)
+ private static bool ChangesOverlap(IDiffChange left, IDiffChange right, out IDiffChange mergedChange)
{
Debug.Assert(left.OriginalStart <= right.OriginalStart, "Left change is not less than or equal to right change");
Debug.Assert(left.ModifiedStart <= right.ModifiedStart, "Left change is not less than or equal to right change");
@@ -760,10 +757,11 @@ namespace Microsoft.TeamFoundation.Diff.Copy
/// <param name="numDiagonals">The total number of diagonals.</param>
/// <returns>The clipped diagonal index.</returns>
//*************************************************************************
- private int ClipDiagonalBound(int diagonal,
- int numDifferences,
- int diagonalBaseIndex,
- int numDiagonals)
+ private static int ClipDiagonalBound(
+ int diagonal,
+ int numDifferences,
+ int diagonalBaseIndex,
+ int numDiagonals)
{
if (diagonal >= 0 && diagonal < numDiagonals)
{
diff --git a/src/Text/Impl/DifferenceAlgorithm/TokenizedStringList.cs b/src/Text/Impl/DifferenceAlgorithm/TokenizedStringList.cs
index f7afc1c..01e6895 100644
--- a/src/Text/Impl/DifferenceAlgorithm/TokenizedStringList.cs
+++ b/src/Text/Impl/DifferenceAlgorithm/TokenizedStringList.cs
@@ -42,7 +42,7 @@ namespace Microsoft.VisualStudio.Text.Differencing.Implementation
protected TokenizedStringList(string original)
{
if (original == null)
- throw new ArgumentNullException("original");
+ throw new ArgumentNullException(nameof(original));
this.original = original;
}
diff --git a/src/Text/Impl/EditorOperations/AfterTextBufferChangeUndoPrimitive.cs b/src/Text/Impl/EditorOperations/AfterTextBufferChangeUndoPrimitive.cs
index 97d1369..d19da59 100644
--- a/src/Text/Impl/EditorOperations/AfterTextBufferChangeUndoPrimitive.cs
+++ b/src/Text/Impl/EditorOperations/AfterTextBufferChangeUndoPrimitive.cs
@@ -19,10 +19,9 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
// Think twice before adding any fields here! These objects are long-lived and consume considerable space.
// Unusual cases should be handled by the GeneralAfterTextBufferChangedUndoPrimitive class below.
- protected ITextUndoHistory _undoHistory;
- protected int _newCaretIndex;
- protected byte _newCaretAffinityByte;
- protected bool _canUndo;
+ private readonly ITextUndoHistory _undoHistory;
+ public readonly SelectionState State;
+ private bool _canUndo;
/// <summary>
/// Constructs a AfterTextBufferChangeUndoPrimitive.
@@ -39,57 +38,21 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
if (textView == null)
{
- throw new ArgumentNullException("textView");
+ throw new ArgumentNullException(nameof(textView));
}
if (undoHistory == null)
{
- throw new ArgumentNullException("undoHistory");
+ throw new ArgumentNullException(nameof(undoHistory));
}
- // Store the ITextView for these changes in the ITextUndoHistory properties so we can retrieve it later.
- if (!undoHistory.Properties.ContainsProperty(typeof(ITextView)))
- {
- undoHistory.Properties[typeof(ITextView)] = textView;
- }
-
- IMapEditToData map = BeforeTextBufferChangeUndoPrimitive.GetMap(textView);
-
- CaretPosition caret = textView.Caret.Position;
- int newCaretIndex = BeforeTextBufferChangeUndoPrimitive.MapToData(map, caret.BufferPosition);
- int newCaretVirtualSpaces = caret.VirtualBufferPosition.VirtualSpaces;
-
- VirtualSnapshotPoint anchor = textView.Selection.AnchorPoint;
- int newSelectionAnchorIndex = BeforeTextBufferChangeUndoPrimitive.MapToData(map, anchor.Position);
- int newSelectionAnchorVirtualSpaces = anchor.VirtualSpaces;
-
- VirtualSnapshotPoint active = textView.Selection.ActivePoint;
- int newSelectionActiveIndex = BeforeTextBufferChangeUndoPrimitive.MapToData(map, active.Position);
- int newSelectionActiveVirtualSpaces = active.VirtualSpaces;
+ return new AfterTextBufferChangeUndoPrimitive(textView, undoHistory);
- TextSelectionMode newSelectionMode = textView.Selection.Mode;
-
- if (newCaretVirtualSpaces != 0 ||
- newSelectionAnchorIndex != newCaretIndex ||
- newSelectionAnchorVirtualSpaces != 0 ||
- newSelectionActiveIndex != newCaretIndex ||
- newSelectionActiveVirtualSpaces != 0 ||
- newSelectionMode != TextSelectionMode.Stream)
- {
- return new GeneralAfterTextBufferChangeUndoPrimitive
- (undoHistory, newCaretIndex, caret.Affinity, newCaretVirtualSpaces, newSelectionAnchorIndex,
- newSelectionAnchorVirtualSpaces, newSelectionActiveIndex, newSelectionActiveVirtualSpaces, newSelectionMode);
- }
- else
- {
- return new AfterTextBufferChangeUndoPrimitive(undoHistory, newCaretIndex, caret.Affinity);
- }
}
- protected AfterTextBufferChangeUndoPrimitive(ITextUndoHistory undoHistory, int caretIndex, PositionAffinity caretAffinity)
+ protected AfterTextBufferChangeUndoPrimitive(ITextView textView, ITextUndoHistory undoHistory)
{
_undoHistory = undoHistory;
- _newCaretIndex = caretIndex;
- _newCaretAffinityByte = (byte)caretAffinity;
+ this.State = new SelectionState(textView);
_canUndo = true;
}
@@ -106,15 +69,6 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
return view;
}
- internal int CaretIndex
- {
- get { return _newCaretIndex; }
- }
-
- internal virtual int CaretVirtualSpace
- {
- get { return 0; }
- }
#region ITextUndoPrimitive Members
@@ -151,7 +105,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
Debug.Assert(view == null || !view.IsClosed, "Attempt to undo/redo on a closed view? This shouldn't happen.");
if (view != null && !view.IsClosed)
{
- DoMoveCaretAndSelect(view, BeforeTextBufferChangeUndoPrimitive.GetMap(view));
+ this.State.Restore(view);
view.Caret.EnsureVisible();
}
@@ -159,17 +113,6 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
}
/// <summary>
- /// Move the caret and restore the selection as part of the Redo operation.
- /// </summary>
- protected virtual void DoMoveCaretAndSelect(ITextView view, IMapEditToData map)
- {
- SnapshotPoint newCaret = new SnapshotPoint(view.TextSnapshot, BeforeTextBufferChangeUndoPrimitive.MapToEdit(map, _newCaretIndex));
-
- view.Caret.MoveTo(newCaret, (PositionAffinity)_newCaretAffinityByte);
- view.Selection.Clear();
- }
-
- /// <summary>
/// Undo the action.
/// </summary>
/// <exception cref="InvalidOperationException">Operation cannot be undone.</exception>
@@ -197,65 +140,4 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
}
#endregion
}
-
- /// <summary>
- /// The UndoPrimitive to take place on the Undo stack before a text buffer change. This is the general
- /// version of the primitive that handles all cases, including those involving selections and virtual space.
- /// </summary>
- internal class GeneralAfterTextBufferChangeUndoPrimitive : AfterTextBufferChangeUndoPrimitive
- {
- private int _newCaretVirtualSpaces;
- private int _newSelectionAnchorIndex;
- private int _newSelectionAnchorVirtualSpaces;
- private int _newSelectionActiveIndex;
- private int _newSelectionActiveVirtualSpaces;
- private TextSelectionMode _newSelectionMode;
-
- public GeneralAfterTextBufferChangeUndoPrimitive(ITextUndoHistory undoHistory,
- int newCaretIndex,
- PositionAffinity newCaretAffinity,
- int newCaretVirtualSpaces,
- int newSelectionAnchorIndex,
- int newSelectionAnchorVirtualSpaces,
- int newSelectionActiveIndex,
- int newSelectionActiveVirtualSpaces,
- TextSelectionMode newSelectionMode)
- : base(undoHistory, newCaretIndex, newCaretAffinity)
- {
- _newCaretVirtualSpaces = newCaretVirtualSpaces;
- _newSelectionAnchorIndex = newSelectionAnchorIndex;
- _newSelectionAnchorVirtualSpaces = newSelectionAnchorVirtualSpaces;
- _newSelectionActiveIndex = newSelectionActiveIndex;
- _newSelectionActiveVirtualSpaces = newSelectionActiveVirtualSpaces;
- _newSelectionMode = newSelectionMode;
- }
-
- internal override int CaretVirtualSpace
- {
- get { return _newCaretVirtualSpaces; }
- }
-
- /// <summary>
- /// Move the caret and restore the selection as part of the Redo operation.
- /// </summary>
- protected override void DoMoveCaretAndSelect(ITextView view, IMapEditToData map)
- {
- SnapshotPoint newCaret = new SnapshotPoint(view.TextSnapshot, BeforeTextBufferChangeUndoPrimitive.MapToEdit(map, _newCaretIndex));
- SnapshotPoint newAnchor = new SnapshotPoint(view.TextSnapshot, BeforeTextBufferChangeUndoPrimitive.MapToEdit(map, _newSelectionAnchorIndex));
- SnapshotPoint newActive = new SnapshotPoint(view.TextSnapshot, BeforeTextBufferChangeUndoPrimitive.MapToEdit(map, _newSelectionActiveIndex));
-
- view.Caret.MoveTo(new VirtualSnapshotPoint(newCaret, _newCaretVirtualSpaces), (PositionAffinity)_newCaretAffinityByte);
-
- view.Selection.Mode = _newSelectionMode;
-
- var virtualAnchor = new VirtualSnapshotPoint(newAnchor, _newSelectionAnchorVirtualSpaces);
- var virtualActive = new VirtualSnapshotPoint(newActive, _newSelectionActiveVirtualSpaces);
-
- // Buffer may have been changed by one of the listeners on the caret move event.
- virtualAnchor = virtualAnchor.TranslateTo(view.TextSnapshot);
- virtualActive = virtualActive.TranslateTo(view.TextSnapshot);
-
- view.Selection.Select(virtualAnchor, virtualActive);
- }
- }
}
diff --git a/src/Text/Impl/EditorOperations/BeforeTextBufferChangeUndoPrimitive.cs b/src/Text/Impl/EditorOperations/BeforeTextBufferChangeUndoPrimitive.cs
index 47abd9b..baf7c70 100644
--- a/src/Text/Impl/EditorOperations/BeforeTextBufferChangeUndoPrimitive.cs
+++ b/src/Text/Impl/EditorOperations/BeforeTextBufferChangeUndoPrimitive.cs
@@ -19,10 +19,9 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
// Think twice before adding any fields here! These objects are long-lived and consume considerable space.
// Unusual cases should be handled by the GeneralAfterTextBufferChangedUndoPrimitive class below.
- protected ITextUndoHistory _undoHistory;
- protected int _oldCaretIndex;
- protected byte _oldCaretAffinityByte;
- protected bool _canUndo;
+ private readonly ITextUndoHistory _undoHistory;
+ public readonly SelectionState State;
+ private bool _canUndo;
/// <summary>
/// Constructs a BeforeTextBufferChangeUndoPrimitive.
@@ -39,86 +38,20 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
if (textView == null)
{
- throw new ArgumentNullException("textView");
+ throw new ArgumentNullException(nameof(textView));
}
if (undoHistory == null)
{
- throw new ArgumentNullException("undoHistory");
+ throw new ArgumentNullException(nameof(undoHistory));
}
- // Store the ITextView for these changes in the ITextUndoHistory properties so we can retrieve it later.
- if (!undoHistory.Properties.ContainsProperty(typeof(ITextView)))
- {
- undoHistory.Properties[typeof(ITextView)] = textView;
- }
-
- CaretPosition caret = textView.Caret.Position;
-
- IMapEditToData map = BeforeTextBufferChangeUndoPrimitive.GetMap(textView);
-
- int oldCaretIndex = BeforeTextBufferChangeUndoPrimitive.MapToData(map, caret.BufferPosition);
- int oldCaretVirtualSpaces = caret.VirtualBufferPosition.VirtualSpaces;
-
- VirtualSnapshotPoint anchor = textView.Selection.AnchorPoint;
- int oldSelectionAnchorIndex = BeforeTextBufferChangeUndoPrimitive.MapToData(map, anchor.Position);
- int oldSelectionAnchorVirtualSpaces = anchor.VirtualSpaces;
-
- VirtualSnapshotPoint active = textView.Selection.ActivePoint;
- int oldSelectionActiveIndex = BeforeTextBufferChangeUndoPrimitive.MapToData(map, active.Position);
- int oldSelectionActiveVirtualSpaces = active.VirtualSpaces;
-
- TextSelectionMode oldSelectionMode = textView.Selection.Mode;
-
- if (oldCaretVirtualSpaces != 0 ||
- oldSelectionAnchorIndex != oldCaretIndex ||
- oldSelectionAnchorVirtualSpaces != 0 ||
- oldSelectionActiveIndex != oldCaretIndex ||
- oldSelectionActiveVirtualSpaces != 0 ||
- oldSelectionMode != TextSelectionMode.Stream)
- {
- return new GeneralBeforeTextBufferChangeUndoPrimitive
- (undoHistory, oldCaretIndex, caret.Affinity, oldCaretVirtualSpaces, oldSelectionAnchorIndex,
- oldSelectionAnchorVirtualSpaces, oldSelectionActiveIndex, oldSelectionActiveVirtualSpaces, oldSelectionMode);
- }
- else
- {
- return new BeforeTextBufferChangeUndoPrimitive(undoHistory, oldCaretIndex, caret.Affinity);
- }
- }
-
- //Get the map -- if any -- used to map points in the view's edit buffer to the data buffer. The map is needed because the undo history
- //typically lives on the data buffer, but is used by the view on the edit buffer and a view (if any) on the data buffer. If there isn't
- //a contract that guarantees that the contents of the edit and databuffers are the same, undoing an action on the edit buffer view and
- //then undoing it on the data buffer view will cause cause the undo to try and restore caret/selection (in the data buffer) the coorinates
- //saved in the edit buffer. This isn't good.
- internal static IMapEditToData GetMap(ITextView view)
- {
- IMapEditToData map = null;
- if (view.TextViewModel.EditBuffer != view.TextViewModel.DataBuffer)
- {
- view.Properties.TryGetProperty(typeof(IMapEditToData), out map);
- }
-
- return map;
- }
-
- //Map point from a position in the edit buffer to a position in the data buffer (== if there is no map, otherwise ask the map).
- internal static int MapToData(IMapEditToData map, int point)
- {
- return (map != null) ? map.MapEditToData(point) : point;
- }
-
- //Map point from a position in the data buffer to a position in the edit buffer (== if there is no map, otherwise ask the map).
- internal static int MapToEdit(IMapEditToData map, int point)
- {
- return (map != null) ? map.MapDataToEdit(point) : point;
+ return new BeforeTextBufferChangeUndoPrimitive(textView, undoHistory);
}
- protected BeforeTextBufferChangeUndoPrimitive(ITextUndoHistory undoHistory, int caretIndex, PositionAffinity caretAffinity)
+ private BeforeTextBufferChangeUndoPrimitive(ITextView textView, ITextUndoHistory undoHistory)
{
_undoHistory = undoHistory;
- _oldCaretIndex = caretIndex;
- _oldCaretAffinityByte = (byte)caretAffinity;
+ this.State = new SelectionState(textView);
_canUndo = true;
}
@@ -192,34 +125,18 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
Debug.Assert(view == null || !view.IsClosed, "Attempt to undo/redo on a closed view? This shouldn't happen.");
if (view != null && !view.IsClosed)
{
- UndoMoveCaretAndSelect(view, BeforeTextBufferChangeUndoPrimitive.GetMap(view));
+ this.State.Restore(view);
view.Caret.EnsureVisible();
}
_canUndo = false;
}
- /// <summary>
- /// Move the caret and restore the selection as part of the Undo operation.
- /// </summary>
- protected virtual void UndoMoveCaretAndSelect(ITextView view, IMapEditToData map)
- {
- SnapshotPoint newCaret = new SnapshotPoint(view.TextSnapshot, MapToEdit(map, _oldCaretIndex));
-
- view.Caret.MoveTo(new VirtualSnapshotPoint(newCaret), (PositionAffinity)_oldCaretAffinityByte);
- view.Selection.Clear();
- }
-
- protected virtual int OldCaretVirtualSpaces
- {
- get { return 0; }
- }
-
public override bool CanMerge(ITextUndoPrimitive older)
{
if (older == null)
{
- throw new ArgumentNullException("older");
+ throw new ArgumentNullException(nameof(older));
}
AfterTextBufferChangeUndoPrimitive olderPrimitive = older as AfterTextBufferChangeUndoPrimitive;
@@ -229,70 +146,9 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
return false;
}
- return (olderPrimitive.CaretIndex == _oldCaretIndex) && (olderPrimitive.CaretVirtualSpace == OldCaretVirtualSpaces);
+ return olderPrimitive.State.Matches(this.State);
}
#endregion
}
-
- /// <summary>
- /// The UndoPrimitive to take place on the Undo stack before a text buffer change. This is the general
- /// version of the primitive that handles all cases, including those involving selections and virtual space.
- /// </summary>
- internal class GeneralBeforeTextBufferChangeUndoPrimitive : BeforeTextBufferChangeUndoPrimitive
- {
- private int _oldCaretVirtualSpaces;
- private int _oldSelectionAnchorIndex;
- private int _oldSelectionAnchorVirtualSpaces;
- private int _oldSelectionActiveIndex;
- private int _oldSelectionActiveVirtualSpaces;
- private TextSelectionMode _oldSelectionMode;
-
- public GeneralBeforeTextBufferChangeUndoPrimitive(ITextUndoHistory undoHistory,
- int oldCaretIndex,
- PositionAffinity oldCaretAffinity,
- int oldCaretVirtualSpaces,
- int oldSelectionAnchorIndex,
- int oldSelectionAnchorVirtualSpaces,
- int oldSelectionActiveIndex,
- int oldSelectionActiveVirtualSpaces,
- TextSelectionMode oldSelectionMode)
- : base(undoHistory, oldCaretIndex, oldCaretAffinity)
- {
- _oldCaretVirtualSpaces = oldCaretVirtualSpaces;
- _oldSelectionAnchorIndex = oldSelectionAnchorIndex;
- _oldSelectionAnchorVirtualSpaces = oldSelectionAnchorVirtualSpaces;
- _oldSelectionActiveIndex = oldSelectionActiveIndex;
- _oldSelectionActiveVirtualSpaces = oldSelectionActiveVirtualSpaces;
- _oldSelectionMode = oldSelectionMode;
- }
-
- /// <summary>
- /// Move the caret and restore the selection as part of the Undo operation.
- /// </summary>
- protected override void UndoMoveCaretAndSelect(ITextView view, IMapEditToData map)
- {
- SnapshotPoint newCaret = new SnapshotPoint(view.TextSnapshot, BeforeTextBufferChangeUndoPrimitive.MapToEdit(map, _oldCaretIndex));
- SnapshotPoint newAnchor = new SnapshotPoint(view.TextSnapshot, BeforeTextBufferChangeUndoPrimitive.MapToEdit(map, _oldSelectionAnchorIndex));
- SnapshotPoint newActive = new SnapshotPoint(view.TextSnapshot, BeforeTextBufferChangeUndoPrimitive.MapToEdit(map, _oldSelectionActiveIndex));
-
- view.Caret.MoveTo(new VirtualSnapshotPoint(newCaret, _oldCaretVirtualSpaces), (PositionAffinity)_oldCaretAffinityByte);
-
- view.Selection.Mode = _oldSelectionMode;
-
- var virtualAnchor = new VirtualSnapshotPoint(newAnchor, _oldSelectionAnchorVirtualSpaces);
- var virtualActive = new VirtualSnapshotPoint(newActive, _oldSelectionActiveVirtualSpaces);
-
- // Buffer may have been changed by one of the listeners on the caret move event.
- virtualAnchor = virtualAnchor.TranslateTo(view.TextSnapshot);
- virtualActive = virtualActive.TranslateTo(view.TextSnapshot);
-
- view.Selection.Select(virtualAnchor, virtualActive);
- }
-
- protected override int OldCaretVirtualSpaces
- {
- get { return _oldCaretVirtualSpaces; }
- }
- }
}
diff --git a/src/Text/Impl/EditorOperations/CollapsedMoveUndoPrimitive.cs b/src/Text/Impl/EditorOperations/CollapsedMoveUndoPrimitive.cs
index 44fa09e..aa065e4 100644
--- a/src/Text/Impl/EditorOperations/CollapsedMoveUndoPrimitive.cs
+++ b/src/Text/Impl/EditorOperations/CollapsedMoveUndoPrimitive.cs
@@ -126,17 +126,17 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
if (textView == null)
{
- throw new ArgumentNullException("textView");
+ throw new ArgumentNullException(nameof(textView));
}
if (outliningManager == null)
{
- throw new ArgumentNullException("outliningManager");
+ throw new ArgumentNullException(nameof(outliningManager));
}
if (collaspedSpans == null)
{
- throw new ArgumentNullException("collaspedSpans");
+ throw new ArgumentNullException(nameof(collaspedSpans));
}
_outliningManager = outliningManager;
diff --git a/src/Text/Impl/EditorOperations/Commands/ExpandContractSelectionCommandHandler.cs b/src/Text/Impl/EditorOperations/Commands/ExpandContractSelectionCommandHandler.cs
deleted file mode 100644
index c7ec9b1..0000000
--- a/src/Text/Impl/EditorOperations/Commands/ExpandContractSelectionCommandHandler.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-using System.ComponentModel.Composition;
-using Microsoft.VisualStudio.Commanding;
-using Microsoft.VisualStudio.Text.Editor;
-using Microsoft.VisualStudio.Text.Editor.Commanding.Commands;
-using Microsoft.VisualStudio.Utilities;
-
-namespace Microsoft.VisualStudio.Text.Operations.Implementation
-{
- [Export(typeof(ICommandHandler))]
- [Name(nameof(ExpandContractSelectionCommandHandler))]
- [ContentType("any")]
- [TextViewRole(PredefinedTextViewRoles.PrimaryDocument)]
- [TextViewRole(PredefinedTextViewRoles.EmbeddedPeekTextView)]
- internal sealed class ExpandContractSelectionCommandHandler
- : ICommandHandler<ExpandSelectionCommandArgs>, ICommandHandler<ContractSelectionCommandArgs>
- {
- [ImportingConstructor]
- public ExpandContractSelectionCommandHandler(
- IEditorOptionsFactoryService editorOptionsFactoryService,
- ITextStructureNavigatorSelectorService navigatorSelectorService)
- {
- this.EditorOptionsFactoryService = editorOptionsFactoryService;
- this.NavigatorSelectorService = navigatorSelectorService;
- }
-
- public IEditorOptionsFactoryService EditorOptionsFactoryService { get; }
-
- private readonly ITextStructureNavigatorSelectorService NavigatorSelectorService;
-
- public string DisplayName => Strings.ExpandContractSelectionCommandHandlerName;
-
- public CommandState GetCommandState(ExpandSelectionCommandArgs args)
- {
- var storedCommandState = ExpandContractSelectionImplementation.GetOrCreateExpandContractState(
- args.TextView,
- this.EditorOptionsFactoryService,
- this.NavigatorSelectorService);
- return storedCommandState.GetExpandCommandState(args.TextView);
- }
-
- public CommandState GetCommandState(ContractSelectionCommandArgs args)
- {
- var storedCommandState = ExpandContractSelectionImplementation.GetOrCreateExpandContractState(
- args.TextView,
- this.EditorOptionsFactoryService,
- this.NavigatorSelectorService);
- return storedCommandState.GetContractCommandState(args.TextView);
- }
-
- public bool ExecuteCommand(ExpandSelectionCommandArgs args, CommandExecutionContext context)
- {
- var storedCommandState = ExpandContractSelectionImplementation.GetOrCreateExpandContractState(
- args.TextView,
- this.EditorOptionsFactoryService,
- this.NavigatorSelectorService);
- return storedCommandState.ExpandSelection(args.TextView);
- }
-
- public bool ExecuteCommand(ContractSelectionCommandArgs args, CommandExecutionContext context)
- {
- var storedCommandState = ExpandContractSelectionImplementation.GetOrCreateExpandContractState(
- args.TextView,
- this.EditorOptionsFactoryService,
- this.NavigatorSelectorService);
- return storedCommandState.ContractSelection(args.TextView);
- }
- }
-}
diff --git a/src/Text/Impl/EditorOperations/Commands/ExpandContractSelectionImplementation.cs b/src/Text/Impl/EditorOperations/Commands/ExpandContractSelectionImplementation.cs
deleted file mode 100644
index a26c025..0000000
--- a/src/Text/Impl/EditorOperations/Commands/ExpandContractSelectionImplementation.cs
+++ /dev/null
@@ -1,134 +0,0 @@
-namespace Microsoft.VisualStudio.Text.Operations.Implementation
-{
- using System;
- using System.Collections.Generic;
- using Microsoft.VisualStudio.Commanding;
- using Microsoft.VisualStudio.Text.Editor;
-
- internal class ExpandContractSelectionImplementation
- {
- private readonly IEditorOptions editorOptions;
- private readonly ITextStructureNavigatorSelectorService navigatorSelectorService;
- private bool ignoreSelectionChangedEvent;
-
- public static ExpandContractSelectionImplementation GetOrCreateExpandContractState(
- ITextView textView,
- IEditorOptionsFactoryService editorOptionsFactoryService,
- ITextStructureNavigatorSelectorService navigator)
- {
- return textView.Properties.GetOrCreateSingletonProperty<ExpandContractSelectionImplementation>(
- typeof(ExpandContractSelectionImplementation),
- () => new ExpandContractSelectionImplementation(
- navigator,
- editorOptionsFactoryService.GetOptions(textView),
- textView));
- }
-
- private ExpandContractSelectionImplementation(
- ITextStructureNavigatorSelectorService navigatorSelectorService,
- IEditorOptions editorOptions,
- ITextView textView)
- {
- this.editorOptions = editorOptions;
- this.navigatorSelectorService = navigatorSelectorService;
- textView.Selection.SelectionChanged += this.OnSelectionChanged;
- }
-
- // Internal for testing.
- internal readonly Stack<Tuple<VirtualSnapshotSpan, TextSelectionMode>> previousExpansionsStack
- = new Stack<Tuple<VirtualSnapshotSpan, TextSelectionMode>>();
-
- public CommandState GetExpandCommandState(ITextView textView) => CommandState.Available;
-
- public CommandState GetContractCommandState(ITextView textView)
- {
- if (this.previousExpansionsStack.Count > 0)
- {
- return CommandState.Available;
- }
-
- return CommandState.Unavailable;
- }
-
- public bool ExpandSelection(ITextView textView)
- {
- try
- {
- this.ignoreSelectionChangedEvent = true;
-
- var navigator = this.GetNavigator(textView);
- VirtualSnapshotSpan currentSelection = textView.Selection.StreamSelectionSpan;
- previousExpansionsStack.Push(Tuple.Create(currentSelection, textView.Selection.Mode));
-
- SnapshotSpan newSelection;
-
- // If the current language has opt-ed out, return the span of the current word instead.
- if (this.editorOptions.GetOptionValue(ExpandContractSelectionOptions.ExpandContractSelectionEnabledKey))
- {
- // On first invocation, select the current word.
- if (currentSelection.Length == 0)
- {
- newSelection = this.GetNavigator(textView).GetExtentOfWord(currentSelection.Start.Position).Span;
- }
- else
- {
- newSelection = this.GetNavigator(textView).GetSpanOfEnclosing(currentSelection.SnapshotSpan);
- }
- }
- else
- {
- // Since the span of the current word can be left or right associative relative to the caret
- // in different contexts, to avoid different selections on subsequent invocations of Expand
- // Selection, always use the center point in the selection to compute the span of the current word.
- var centerPoint = currentSelection.Start.Position.Add(
- (currentSelection.End.Position.Position - currentSelection.Start.Position.Position) / 2);
- newSelection = navigator.GetExtentOfWord(centerPoint).Span;
- }
-
- textView.Selection.Mode = TextSelectionMode.Stream;
- textView.Selection.Select(newSelection, isReversed: false);
- }
- finally
- {
- this.ignoreSelectionChangedEvent = false;
- }
-
- return true; //return true if command is handled
- }
-
- public bool ContractSelection(ITextView textView)
- {
- try
- {
- this.ignoreSelectionChangedEvent = true;
-
- if (this.previousExpansionsStack.Count > 0)
- {
- Tuple<VirtualSnapshotSpan, TextSelectionMode> previousExpansion = this.previousExpansionsStack.Pop();
- VirtualSnapshotSpan previousExpansionSpan = previousExpansion.Item1;
- TextSelectionMode previousExpansionSelectionMode = previousExpansion.Item2;
-
- textView.Selection.Mode = previousExpansionSelectionMode;
- textView.Selection.Select(previousExpansionSpan.Start, previousExpansionSpan.End);
- }
- }
- finally
- {
- this.ignoreSelectionChangedEvent = false;
- }
-
- return true;//return true if command is handled
- }
-
- private void OnSelectionChanged(object sender, EventArgs eventArgs)
- {
- if (!this.ignoreSelectionChangedEvent)
- {
- this.previousExpansionsStack.Clear();
- }
- }
-
- private ITextStructureNavigator GetNavigator(ITextView textView)
- => this.navigatorSelectorService.GetTextStructureNavigator(textView.TextBuffer);
- }
-}
diff --git a/src/Text/Impl/EditorOperations/Commands/ExpandContractSelectionOptionDefinitions.cs b/src/Text/Impl/EditorOperations/Commands/ExpandContractSelectionOptionDefinitions.cs
deleted file mode 100644
index b39c3f8..0000000
--- a/src/Text/Impl/EditorOperations/Commands/ExpandContractSelectionOptionDefinitions.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-namespace Microsoft.VisualStudio.Text.Operations.Implementation
-{
- using System.ComponentModel.Composition;
- using Microsoft.VisualStudio.Text.Editor;
- using Microsoft.VisualStudio.Text.Operations;
- using Microsoft.VisualStudio.Utilities;
-
- /// <summary>
- /// Defines Expand Contract Selection Option.
- /// </summary>
- [Export(typeof(EditorOptionDefinition))]
- [Name(ExpandContractSelectionOptions.ExpandContractSelectionEnabledOptionId)]
- internal sealed class ExpandContractSelectionEnabled : EditorOptionDefinition<bool>
- {
- /// <summary>
- /// Gets the default value, which is <c>false</c>.
- /// </summary>
- public override bool Default => true;
-
- /// <summary>
- /// Gets the default text view host value.
- /// </summary>
- public override EditorOptionKey<bool> Key => ExpandContractSelectionOptions.ExpandContractSelectionEnabledKey;
- }
-}
diff --git a/src/Text/Impl/EditorOperations/Commands/NavigateToNextIssueCommandHandler.cs b/src/Text/Impl/EditorOperations/Commands/NavigateToNextIssueCommandHandler.cs
new file mode 100644
index 0000000..25948cf
--- /dev/null
+++ b/src/Text/Impl/EditorOperations/Commands/NavigateToNextIssueCommandHandler.cs
@@ -0,0 +1,156 @@
+namespace Microsoft.VisualStudio.Text.Operations.Implementation
+{
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel.Composition;
+ using System.Diagnostics;
+ using System.Linq;
+ using Microsoft.VisualStudio.Commanding;
+ using Microsoft.VisualStudio.Text.Editor;
+ using Microsoft.VisualStudio.Text.Editor.Commanding.Commands;
+ using Microsoft.VisualStudio.Text.Tagging;
+ using Microsoft.VisualStudio.Utilities;
+
+ [Export(typeof(ICommandHandler))]
+ [Name("default " + nameof(NavigateToNextIssueCommandHandler))]
+ [ContentType("any")]
+ [TextViewRole(PredefinedTextViewRoles.Analyzable)]
+ internal sealed class NavigateToNextIssueCommandHandler : ICommandHandler<NavigateToNextIssueInDocumentCommandArgs>, ICommandHandler<NavigateToPreviousIssueInDocumentCommandArgs>
+ {
+ [Import]
+ private Lazy<IBufferTagAggregatorFactoryService> tagAggregatorFactoryService;
+
+ public string DisplayName => Strings.NextIssue;
+
+ #region Previous Issue
+
+ public CommandState GetCommandState(NavigateToPreviousIssueInDocumentCommandArgs args) => CommandState.Available;
+
+ public bool ExecuteCommand(NavigateToPreviousIssueInDocumentCommandArgs args, CommandExecutionContext executionContext)
+ {
+ var snapshot = args.TextView.TextSnapshot;
+ var spans = this.GetTagSpansCollection(snapshot, args.ErrorTagTypeNames);
+
+ if (spans.Count == 0)
+ {
+ return true;
+ }
+
+ (int indexOfErrorSpan, bool containsPoint) = IndexOfTagSpanNearPoint(spans, args.TextView.Caret.Position.BufferPosition.Position);
+
+ int nextIndex = indexOfErrorSpan - 1;
+ if (containsPoint && (spans.Count == 1))
+ {
+ // There is only one error tag and it contains the caret. Ensure it stays put.
+ return true;
+ }
+
+ // Wrap if needed.
+ if (nextIndex < 0)
+ {
+ nextIndex = (spans.Count - 1);
+ }
+
+ args.TextView.Caret.MoveTo(new SnapshotPoint(snapshot, spans[nextIndex].Start));
+ args.TextView.Caret.EnsureVisible();
+ return true;
+ }
+
+ #endregion
+
+ #region Next Issue
+ public CommandState GetCommandState(NavigateToNextIssueInDocumentCommandArgs args) => CommandState.Available;
+
+ public bool ExecuteCommand(NavigateToNextIssueInDocumentCommandArgs args, CommandExecutionContext executionContext)
+ {
+ var snapshot = args.TextView.TextSnapshot;
+ var spans = this.GetTagSpansCollection(snapshot, args.ErrorTagTypeNames);
+
+ if (spans.Count == 0)
+ {
+ return true;
+ }
+
+ (int indexOfErrorSpan, bool containsPoint) = IndexOfTagSpanNearPoint(spans, args.TextView.Caret.Position.BufferPosition.Position);
+
+ int nextIndex = indexOfErrorSpan + 1;
+ if (containsPoint)
+ {
+ if (spans.Count == 1)
+ {
+ // There is only one error tag and it contains the caret. Ensure it stays put.
+ return true;
+ }
+ }
+ else
+ {
+ nextIndex = indexOfErrorSpan;
+ }
+
+ // Wrap if needed.
+ if ((indexOfErrorSpan == -1) || (nextIndex >= spans.Count))
+ {
+ nextIndex = 0;
+ }
+
+ args.TextView.Caret.MoveTo(new SnapshotPoint(snapshot, spans[nextIndex].Start));
+ args.TextView.Caret.EnsureVisible();
+ return true;
+ }
+
+ #endregion
+
+ private static (int index, bool containsPoint) IndexOfTagSpanNearPoint(NormalizedSpanCollection spans, int point)
+ {
+ Debug.Assert(spans.Count > 0);
+ Span? tagBefore = null;
+ Span? tagAfter = null;
+
+ for (int i = 0; i < spans.Count; i++)
+ {
+ tagBefore = tagAfter;
+ tagAfter = spans[i];
+
+ // Case 0: point falls within error tag. We use explicit comparisons instead
+ // of 'Contains' so that we match a tag even if the caret at the end of it.
+ if ((point >= tagAfter.Value.Start) && (point <= tagAfter.Value.End))
+ {
+ // Return tag containing the point.
+ return (i, true);
+ }
+
+ // Case 1: point falls between two tags.
+ if ((tagBefore != null) && (tagBefore.Value.End < point) && (tagAfter.Value.Start > point))
+ {
+ // Return tag following the point.
+ return (i, false);
+ }
+ }
+
+ // Case 2: point falls after all tags.
+ return (-1, false);
+ }
+
+ private NormalizedSpanCollection GetTagSpansCollection(ITextSnapshot snapshot, IEnumerable<string> errorTagTypeNames)
+ {
+ using (var tagger = this.tagAggregatorFactoryService.Value.CreateTagAggregator<IErrorTag>(snapshot.TextBuffer))
+ {
+ var rawTags = tagger.GetTags(new SnapshotSpan(snapshot, 0, snapshot.Length));
+ var curatedTags = (errorTagTypeNames?.Any() ?? false) ?
+ rawTags.Where(tag => errorTagTypeNames.Contains(tag.Tag.ErrorType)) :
+ rawTags;
+
+ // In this case we only grab the first span that the IMappingTagSpan maps to because we always
+ // want to place the caret at the start of the error, and so, don't care about possibly disjoint
+ // subspans after mapping to the view's buffer. NormalizedSpanCollection takes care of sorting
+ // and joining overlapping spans together for us. It's possible for a tag to map to zero spans
+ // in projection scenarios in which the tag exists entirely within a region that doesn't map to
+ // visible space.
+ return new NormalizedSpanCollection(
+ curatedTags.Select(tagSpan => tagSpan.Span.GetSpans(snapshot))
+ .Where(spanCollection => spanCollection.Count > 0)
+ .Select(spanCollection => spanCollection[0].Span));
+ }
+ }
+ }
+}
diff --git a/src/Text/Impl/EditorOperations/EditorOperations.cs b/src/Text/Impl/EditorOperations/EditorOperations.cs
index 5ac282a..369273b 100644
--- a/src/Text/Impl/EditorOperations/EditorOperations.cs
+++ b/src/Text/Impl/EditorOperations/EditorOperations.cs
@@ -16,7 +16,6 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
- using System.Threading;
using System.Windows;
using Microsoft.VisualStudio.Text;
@@ -56,7 +55,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
ClearVirtualSpace
};
- #region Private Members
+#region Private Members
ITextView _textView;
EditorOperationsFactoryService _factory;
@@ -65,6 +64,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
ITextUndoHistory _undoHistory;
IViewPrimitives _editorPrimitives;
IEditorOptions _editorOptions;
+ IMultiSelectionBroker _multiSelectionBroker;
private ITrackingSpan _immProvisionalComposition;
@@ -80,7 +80,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
/// </summary>
private const string _boxSelectionCutCopyTag = "MSDEVColumnSelect";
- #endregion // Private Members
+#endregion // Private Members
/// <summary>
/// Constructs an <see cref="EditorOperations"/> bound to a given <see cref="ITextView"/>.
@@ -93,13 +93,13 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
// Validate
if (textView == null)
- throw new ArgumentNullException("textView");
+ throw new ArgumentNullException(nameof(textView));
if (factory == null)
- throw new ArgumentNullException("factory");
+ throw new ArgumentNullException(nameof(factory));
_textView = textView;
_factory = factory;
-
+ _multiSelectionBroker = _textView.GetMultiSelectionBroker();
_editorPrimitives = factory.EditorPrimitivesProvider.GetViewPrimitives(textView);
// Get the TextStructure Navigator
_textStructureNavigator = factory.TextStructureNavigatorFactory.GetTextStructureNavigator(_textView.TextBuffer);
@@ -121,7 +121,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
}
- #region IEditorOperations2 Members
+#region IEditorOperations2 Members
public bool MoveSelectedLinesUp()
{
@@ -129,15 +129,13 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
bool success = false;
- var view = _textView as ITextView;
-
// find line start
- var startViewLine = GetLineStart(view, view.Selection.Start.Position);
+ ITextViewLine startViewLine = GetLineStart(_textView, _textView.Selection.Start.Position);
SnapshotPoint start = startViewLine.Start;
ITextSnapshotLine startLine = start.GetContainingLine();
// find the last line view
- var endViewLine = GetLineEnd(view, view.Selection.End.Position);
+ ITextViewLine endViewLine = GetLineEnd(_textView, _textView.Selection.End.Position);
SnapshotPoint end = endViewLine.EndIncludingLineBreak;
ITextSnapshotLine endLine = endViewLine.End.GetContainingLine();
@@ -146,24 +144,24 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
// Handle the case where multiple lines are selected and the caret is sitting just after the line break on the next line.
// Shortening the selection here handles the case where the last line is a collapsed region. Using endLine.End will give
// a line within the collapsed region instead of skipping it all together.
- if (GetLineEnd(view, startViewLine.Start) != endViewLine
- && view.Selection.End.Position == GetLineStart(view, view.Selection.End.Position).Start
- && !view.Selection.End.IsInVirtualSpace)
+ if (GetLineEnd(_textView, startViewLine.Start) != endViewLine
+ && _textView.Selection.End.Position == GetLineStart(_textView, _textView.Selection.End.Position).Start
+ && !_textView.Selection.End.IsInVirtualSpace)
{
endLine = snapshot.GetLineFromLineNumber(endLine.LineNumber - 1);
end = endLine.EndIncludingLineBreak;
- endViewLine = view.GetTextViewLineContainingBufferPosition(view.Selection.End.Position - 1);
+ endViewLine = _textView.GetTextViewLineContainingBufferPosition(_textView.Selection.End.Position - 1);
}
- #region Initial Asserts
+#region Initial Asserts
- Debug.Assert(view.Selection.Start.Position.Snapshot == view.TextSnapshot, "Selection is out of sync with view.");
+ Debug.Assert(_textView.Selection.Start.Position.Snapshot == _textView.TextSnapshot, "Selection is out of sync with view.");
- Debug.Assert(view.TextSnapshot == view.TextBuffer.CurrentSnapshot, "View is out of sync with text buffer.");
+ Debug.Assert(_textView.TextSnapshot == _textView.TextBuffer.CurrentSnapshot, "View is out of sync with text buffer.");
- Debug.Assert(view.TextSnapshot == snapshot, "Text view lines are out of sync with the view");
+ Debug.Assert(_textView.TextSnapshot == snapshot, "Text view lines are out of sync with the view");
- #endregion
+#endregion
// check if we are at the top of the file, or trying to move a blank line
if (startLine.LineNumber < 1 || start == end)
@@ -177,11 +175,11 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
ITextSnapshotLine prevLine = snapshot.GetLineFromLineNumber(startLine.LineNumber - 1);
// prevLineExtent is different from prevLine.Extent and avoids issues around collapsed regions
- SnapshotPoint prevLineStart = GetLineStart(view, prevLine.Start).Start;
+ SnapshotPoint prevLineStart = GetLineStart(_textView, prevLine.Start).Start;
SnapshotSpan prevLineExtent = new SnapshotSpan(prevLineStart, prevLine.End);
SnapshotSpan prevLineExtentIncludingLineBreak = new SnapshotSpan(prevLineStart, prevLine.EndIncludingLineBreak);
- using (ITextEdit edit = view.TextBuffer.CreateEdit())
+ using (ITextEdit edit = _textView.TextBuffer.CreateEdit())
{
int offset;
@@ -193,7 +191,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
bool hasCollapsedRegions = false;
IOutliningManager outliningManager = (_factory.OutliningManagerService != null)
- ? _factory.OutliningManagerService.GetOutliningManager(view)
+ ? _factory.OutliningManagerService.GetOutliningManager(_textView)
: null;
if (outliningManager != null)
@@ -208,7 +206,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
using (ITextUndoTransaction undoTransaction = _undoHistory.CreateTransaction(Strings.MoveSelLinesUp))
{
- BeforeCollapsedMoveUndoPrimitive undoPrim = new BeforeCollapsedMoveUndoPrimitive(outliningManager, view, collapsedSpansInCurLine);
+ BeforeCollapsedMoveUndoPrimitive undoPrim = new BeforeCollapsedMoveUndoPrimitive(outliningManager, _textView, collapsedSpansInCurLine);
undoTransaction.AddUndo(undoPrim);
undoTransaction.Complete();
}
@@ -237,11 +235,11 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
if (!edit.HasFailedChanges)
{
// store the position before the edit is applied
- int anchorPos = view.Selection.AnchorPoint.Position.Position;
- int anchorVirtualSpace = view.Selection.AnchorPoint.VirtualSpaces;
- int activePos = view.Selection.ActivePoint.Position.Position;
- int activeVirtualSpace = view.Selection.ActivePoint.VirtualSpaces;
- var selectionMode = view.Selection.Mode;
+ int anchorPos = _textView.Selection.AnchorPoint.Position.Position;
+ int anchorVirtualSpace = _textView.Selection.AnchorPoint.VirtualSpaces;
+ int activePos = _textView.Selection.ActivePoint.Position.Position;
+ int activeVirtualSpace = _textView.Selection.ActivePoint.VirtualSpaces;
+ var selectionMode = _textView.Selection.Mode;
// apply the edit
ITextSnapshot newSnapshot = edit.Apply();
@@ -266,8 +264,8 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
// This comes from adhocoutliner.cs in env\editor\pkg\impl\outlining and will not be available outside of VS
SimpleTagger<IOutliningRegionTag> simpleTagger =
- view.TextBuffer.Properties.GetOrCreateSingletonProperty<SimpleTagger<IOutliningRegionTag>>(
- () => new SimpleTagger<IOutliningRegionTag>(view.TextBuffer));
+ _textView.TextBuffer.Properties.GetOrCreateSingletonProperty<SimpleTagger<IOutliningRegionTag>>(
+ () => new SimpleTagger<IOutliningRegionTag>(_textView.TextBuffer));
if (simpleTagger != null)
{
@@ -316,7 +314,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
// we need to recollapse after a redo
using (ITextUndoTransaction undoTransaction = _undoHistory.CreateTransaction(Strings.MoveSelLinesUp))
{
- AfterCollapsedMoveUndoPrimitive undoPrim = new AfterCollapsedMoveUndoPrimitive(outliningManager, view, spansForUndo);
+ AfterCollapsedMoveUndoPrimitive undoPrim = new AfterCollapsedMoveUndoPrimitive(outliningManager, _textView, spansForUndo);
undoTransaction.AddUndo(undoPrim);
undoTransaction.Complete();
}
@@ -341,18 +339,15 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
Func<bool> action = () =>
{
-
bool success = false;
- var view = _textView as ITextView;
-
// find line start
- var startViewLine = GetLineStart(view, view.Selection.Start.Position);
+ ITextViewLine startViewLine = GetLineStart(_textView, _textView.Selection.Start.Position);
SnapshotPoint start = startViewLine.Start;
ITextSnapshotLine startLine = start.GetContainingLine();
// find the last line view
- var endViewLine = GetLineEnd(view, view.Selection.End.Position);
+ ITextViewLine endViewLine = GetLineEnd(_textView, _textView.Selection.End.Position);
ITextSnapshotLine endLine = endViewLine.End.GetContainingLine();
ITextSnapshot snapshot = endLine.Snapshot;
@@ -360,23 +355,23 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
// Handle the case where multiple lines are selected and the caret is sitting just after the line break on the next line.
// Shortening the selection here handles the case where the last line is a collapsed region. Using endLine.End will give
// a line within the collapsed region instead of skipping it all together.
- if (GetLineEnd(view, startViewLine.Start) != endViewLine
- && view.Selection.End.Position == GetLineStart(view, view.Selection.End.Position).Start
- && !view.Selection.End.IsInVirtualSpace)
+ if (GetLineEnd(_textView, startViewLine.Start) != endViewLine
+ && _textView.Selection.End.Position == GetLineStart(_textView, _textView.Selection.End.Position).Start
+ && !_textView.Selection.End.IsInVirtualSpace)
{
endLine = snapshot.GetLineFromLineNumber(endLine.LineNumber - 1);
- endViewLine = view.GetTextViewLineContainingBufferPosition(view.Selection.End.Position - 1);
+ endViewLine = _textView.GetTextViewLineContainingBufferPosition(_textView.Selection.End.Position - 1);
}
- #region Initial Asserts
+#region Initial Asserts
- Debug.Assert(view.Selection.Start.Position.Snapshot == view.TextSnapshot, "Selection is out of sync with view.");
+ Debug.Assert(_textView.Selection.Start.Position.Snapshot == _textView.TextSnapshot, "Selection is out of sync with view.");
- Debug.Assert(view.TextSnapshot == view.TextBuffer.CurrentSnapshot, "View is out of sync with text buffer.");
+ Debug.Assert(_textView.TextSnapshot == _textView.TextBuffer.CurrentSnapshot, "View is out of sync with text buffer.");
- Debug.Assert(view.TextSnapshot == snapshot, "Text view lines are out of sync with the view");
+ Debug.Assert(_textView.TextSnapshot == snapshot, "Text view lines are out of sync with the view");
- #endregion
+#endregion
// check if we are at the end of the file
if ((endLine.LineNumber + 1) >= snapshot.LineCount)
@@ -387,11 +382,11 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
else
{
// nextLineExtent is different from prevLine.Extent and avoids issues around collapsed regions
- var lastNextLine = GetLineEnd(view, endViewLine.EndIncludingLineBreak);
+ ITextViewLine lastNextLine = GetLineEnd(_textView, endViewLine.EndIncludingLineBreak);
SnapshotSpan nextLineExtent = new SnapshotSpan(endViewLine.EndIncludingLineBreak, lastNextLine.End);
SnapshotSpan nextLineExtentIncludingLineBreak = new SnapshotSpan(endViewLine.EndIncludingLineBreak, lastNextLine.EndIncludingLineBreak);
- using (ITextEdit edit = view.TextBuffer.CreateEdit())
+ using (ITextEdit edit = _textView.TextBuffer.CreateEdit())
{
SnapshotSpan curLineExtent = new SnapshotSpan(startViewLine.Start, endViewLine.End);
SnapshotSpan curLineExtentIncLineBreak = new SnapshotSpan(startViewLine.Start, endViewLine.EndIncludingLineBreak);
@@ -410,7 +405,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
bool hasCollapsedRegions = false;
IOutliningManager outliningManager = (_factory.OutliningManagerService != null)
- ? _factory.OutliningManagerService.GetOutliningManager(view)
+ ? _factory.OutliningManagerService.GetOutliningManager(_textView)
: null;
if (outliningManager != null)
@@ -425,7 +420,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
using (ITextUndoTransaction undoTransaction = _undoHistory.CreateTransaction(Strings.MoveSelLinesDown))
{
- BeforeCollapsedMoveUndoPrimitive undoPrim = new BeforeCollapsedMoveUndoPrimitive(outliningManager, view, collapsedSpansInCurLine);
+ BeforeCollapsedMoveUndoPrimitive undoPrim = new BeforeCollapsedMoveUndoPrimitive(outliningManager, _textView, collapsedSpansInCurLine);
undoTransaction.AddUndo(undoPrim);
undoTransaction.Complete();
}
@@ -455,11 +450,11 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
}
else
{
- int anchorPos = view.Selection.AnchorPoint.Position.Position;
- int anchorVirtualSpace = view.Selection.AnchorPoint.VirtualSpaces;
- int activePos = view.Selection.ActivePoint.Position.Position;
- int activeVirtualSpace = view.Selection.ActivePoint.VirtualSpaces;
- var selectionMode = view.Selection.Mode;
+ int anchorPos = _textView.Selection.AnchorPoint.Position.Position;
+ int anchorVirtualSpace = _textView.Selection.AnchorPoint.VirtualSpaces;
+ int activePos = _textView.Selection.ActivePoint.Position.Position;
+ int activeVirtualSpace = _textView.Selection.ActivePoint.VirtualSpaces;
+ var selectionMode = _textView.Selection.Mode;
ITextSnapshot newSnapshot = edit.Apply();
if (newSnapshot == snapshot)
@@ -483,7 +478,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
// This comes from adhocoutliner.cs in env\editor\pkg\impl\outlining and will not be available outside of VS
SimpleTagger<IOutliningRegionTag> simpleTagger =
- view.TextBuffer.Properties.GetOrCreateSingletonProperty<SimpleTagger<IOutliningRegionTag>>(() => new SimpleTagger<IOutliningRegionTag>(view.TextBuffer));
+ _textView.TextBuffer.Properties.GetOrCreateSingletonProperty<SimpleTagger<IOutliningRegionTag>>(() => new SimpleTagger<IOutliningRegionTag>(_textView.TextBuffer));
if (simpleTagger != null)
{
@@ -532,7 +527,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
// we need to recollapse after a redo
using (ITextUndoTransaction undoTransaction = _undoHistory.CreateTransaction(Strings.MoveSelLinesDown))
{
- AfterCollapsedMoveUndoPrimitive undoPrim = new AfterCollapsedMoveUndoPrimitive(outliningManager, view, spansForUndo);
+ AfterCollapsedMoveUndoPrimitive undoPrim = new AfterCollapsedMoveUndoPrimitive(outliningManager, _textView, spansForUndo);
undoTransaction.AddUndo(undoPrim);
undoTransaction.Complete();
}
@@ -555,7 +550,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
private static ITextViewLine GetLineStart(ITextView view, SnapshotPoint snapshotPoint)
{
- var line = view.GetTextViewLineContainingBufferPosition(snapshotPoint);
+ ITextViewLine line = view.GetTextViewLineContainingBufferPosition(snapshotPoint);
while (!line.IsFirstTextViewLineForSnapshotLine)
{
line = view.GetTextViewLineContainingBufferPosition(line.Start - 1);
@@ -565,7 +560,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
private static ITextViewLine GetLineEnd(ITextView view, SnapshotPoint snapshotPoint)
{
- var line = view.GetTextViewLineContainingBufferPosition(snapshotPoint);
+ ITextViewLine line = view.GetTextViewLineContainingBufferPosition(snapshotPoint);
while (!line.IsLastTextViewLineForSnapshotLine)
{
line = view.GetTextViewLineContainingBufferPosition(line.EndIncludingLineBreak);
@@ -573,10 +568,10 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
return line;
}
- #endregion
+#endregion
- #region IEditorOperations Members
+#region IEditorOperations Members
public void SelectAndMoveCaret(VirtualSnapshotPoint anchorPoint, VirtualSnapshotPoint activePoint)
{
@@ -592,41 +587,14 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
bool empty = (anchorPoint == activePoint);
- // TODO: Whenever caret/selection is updated to offer a way to set both simultaneously without either eventing before
- // the other is updated, we should update this method to use that. There are potential bugs below in how clients
- // react to things like selection moving. For example, if someone reacts to moving the selection by moving the caret,
- // the logic below will override that caret position, which may not be desirable.
-
- // The order of operations here is important:
- // 1) We need to move the selection first. Clients (like VB) who listen for caret change need the selection to be correct,
- // and we have yet to have clients that require the opposite order. See Dev10 #793198 for what happens when we do this selection-first.
- //
- // 2) Then we move the caret. This behaves differently, depending on if the new selection is empty or not (explained below).
-
- if (empty)
+ var selection = new Selection(anchorPoint, activePoint);
+ if (selectionMode == TextSelectionMode.Box)
{
- _textView.Selection.Clear();
- _textView.Selection.Mode = selectionMode;
-
- // Since the selection is empty, move the caret to the provided active point and translate that point
- // to the view's text snapshot (in case someone was listening to the selection changed event and made a text edit).
- // The empty selection will track the caret.
- // See Dev10 #785792 for an example of what happens when we get this wrong by moving the caret to the active point
- // of the selection when the selection is being cleared.
- _textView.Caret.MoveTo(activePoint.TranslateTo(_textView.TextSnapshot));
+ _multiSelectionBroker.SetBoxSelection(selection);
}
else
{
- _textView.Selection.Select(anchorPoint, activePoint);
- _textView.Selection.Mode = selectionMode;
-
- // Move the caret to the active point of the selection (don't use activePoint since someone -- on the selection changed event -- might have
- // moved the selection).
- // But if the selection is empty (it shouldn't be since anchorPoint != activePoint, but those points could be normalized to an empty span
- // or someone could have moved it), move the caret to the requested activePoint.
- _textView.Caret.MoveTo(_textView.Selection.IsEmpty
- ? activePoint.TranslateTo(_textView.TextSnapshot)
- : _textView.Selection.ActivePoint);
+ _multiSelectionBroker.SetSelection(selection);
}
// 3) If scrollOptions were provided, we're going to try and make the span visible using the provided options.
@@ -657,7 +625,8 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
/// </param>
public void MoveToNextCharacter(bool select)
{
- _editorPrimitives.Caret.MoveToNextCharacter(select);
+ _multiSelectionBroker.PerformActionOnAllSelections(select ? PredefinedSelectionTransformations.SelectToNextCaretPosition : PredefinedSelectionTransformations.MoveToNextCaretPosition);
+ _textView.Caret.EnsureVisible();
}
/// <summary>
@@ -668,18 +637,8 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
/// </param>
public void MoveToPreviousCharacter(bool select)
{
- bool isCaretAtStartOfViewLine = (!_textView.Caret.InVirtualSpace) &&
- (_textView.Caret.Position.BufferPosition == _textView.Caret.ContainingTextViewLine.Start);
-
- //Prevent the caret from moving from column 0 to the end of the previous line if either:
- // virtual space is turned on or
- // the user is extending a box selection.
- if (isCaretAtStartOfViewLine && (_editorOptions.IsVirtualSpaceEnabled() || (select && (_textView.Selection.Mode == TextSelectionMode.Box))))
- {
- return;
- }
-
- _editorPrimitives.Caret.MoveToPreviousCharacter(select);
+ _multiSelectionBroker.PerformActionOnAllSelections(select ? PredefinedSelectionTransformations.SelectToPreviousCaretPosition : PredefinedSelectionTransformations.MoveToPreviousCaretPosition);
+ _textView.Caret.EnsureVisible();
}
/// <summary>
@@ -690,7 +649,8 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
/// </param>
public void MoveToNextWord(bool select)
{
- _editorPrimitives.Caret.MoveToNextWord(select);
+ _multiSelectionBroker.PerformActionOnAllSelections(select ? PredefinedSelectionTransformations.SelectToNextWord : PredefinedSelectionTransformations.MoveToNextWord);
+ _textView.Caret.EnsureVisible();
}
/// <summary>
@@ -701,15 +661,8 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
/// </param>
public void MoveToPreviousWord(bool select)
{
- // In extending a box selection, we don't want this to jump to the previous line (if
- // we are on the beginning of a line)
- if (select && _textView.Selection.Mode == TextSelectionMode.Box && !_textView.Caret.InVirtualSpace)
- {
- if (_editorPrimitives.Caret.CurrentPosition == _editorPrimitives.Caret.StartOfViewLine)
- return;
- }
-
- _editorPrimitives.Caret.MoveToPreviousWord(select);
+ _multiSelectionBroker.PerformActionOnAllSelections(select ? PredefinedSelectionTransformations.SelectToPreviousWord : PredefinedSelectionTransformations.MoveToPreviousWord);
+ _textView.Caret.EnsureVisible();
}
/// <summary>
@@ -720,7 +673,8 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
/// </param>
public void MoveToStartOfDocument(bool select)
{
- _editorPrimitives.Caret.MoveToStartOfDocument(select);
+ _multiSelectionBroker.PerformActionOnAllSelections(select ? PredefinedSelectionTransformations.SelectToStartOfDocument : PredefinedSelectionTransformations.MoveToStartOfDocument);
+ _textView.Caret.EnsureVisible();
}
/// <summary>
@@ -731,7 +685,8 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
/// </param>
public void MoveToEndOfDocument(bool select)
{
- _editorPrimitives.Caret.MoveToEndOfDocument(select);
+ _multiSelectionBroker.PerformActionOnAllSelections(select ? PredefinedSelectionTransformations.SelectToEndOfDocument : PredefinedSelectionTransformations.MoveToEndOfDocument);
+ _textView.Caret.EnsureVisible();
}
/// <summary>
@@ -928,110 +883,267 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
/// </summary>
public bool Backspace()
{
- bool emptyBox = IsEmptyBoxSelection();
- NormalizedSnapshotSpanCollection boxDeletions = null;
+ bool success = true;
- // First, handle cases that don't require edits
- if (_textView.Selection.IsEmpty)
+ if (WillBackspaceCreateEdit())
{
- if (_textView.Caret.InVirtualSpace)
- {
- this.MoveCaretToPreviousIndentStopInVirtualSpace();
+ var selections = _multiSelectionBroker.AllSelections;
+ var boxSelection = _multiSelectionBroker.BoxSelection;
+ var primarySelection = _multiSelectionBroker.PrimarySelection;
- _textView.Caret.EnsureVisible();
- return true;
- }
- if (_textView.Caret.Position.BufferPosition.Position == 0)
+ Func<bool> action = () =>
{
- return true;
- }
+ using (_multiSelectionBroker.BeginBatchOperation())
+ {
+ if (TryBackspaceEdit(selections))
+ {
+ return TryPostBackspaceSelectionUpdate(selections, primarySelection, boxSelection);
+ }
+ }
+ return false;
+ };
+
+ success = ExecuteAction(Strings.DeleteCharToLeft, action, SelectionUpdate.Ignore, ensureVisible: false);
}
- // If the entire selection is in virtual space, clear it
- else if (_textView.Selection.VirtualSelectedSpans.All(s => s.SnapshotSpan.IsEmpty && s.IsInVirtualSpace))
+ else
{
- this.ResetVirtualSelection();
- _textView.Caret.EnsureVisible();
- return true;
+ success = TryBackspaceSelections();
}
- else if (emptyBox) // empty box selection, make sure it is valid
+
+ if (success)
{
- List<SnapshotSpan> spans = new List<SnapshotSpan>();
+ _multiSelectionBroker.TryEnsureVisible(_multiSelectionBroker.PrimarySelection, EnsureSpanVisibleOptions.MinimumScroll);
+ }
+
+ return success;
+ }
+
+ private bool TryPostBackspaceSelectionUpdate(IReadOnlyList<Selection> selections, Selection primarySelection, Selection boxSelection)
+ {
+ // Throughout this method, the parameters passed in are the OLD values, and the parameters on _multiSelectionBroker are the NEW ones
- foreach (var span in _textView.Selection.VirtualSelectedSpans.Where(s => !s.IsInVirtualSpace).Select(s => s.SnapshotSpan))
+ if (boxSelection != Selection.Invalid)
+ {
+ // If this is an empty box, we may need to capture the new active/anchor points, as points in virtual space
+ // won't track as we want them to through the edit.
+ VirtualSnapshotPoint anchorPoint = _multiSelectionBroker.BoxSelection.AnchorPoint;
+ VirtualSnapshotPoint activePoint = _multiSelectionBroker.BoxSelection.ActivePoint;
+
+ if (primarySelection.IsEmpty)
{
- var line = span.Start.GetContainingLine();
- if (span.Start > line.Start)
+ if (boxSelection.AnchorPoint.IsInVirtualSpace)
+ {
+ anchorPoint = new VirtualSnapshotPoint(_multiSelectionBroker.BoxSelection.AnchorPoint.Position, boxSelection.AnchorPoint.VirtualSpaces - 1);
+ }
+ if (boxSelection.ActivePoint.IsInVirtualSpace)
{
- spans.Add(_textView.GetTextElementSpan(span.Start - 1));
+ activePoint = new VirtualSnapshotPoint(_multiSelectionBroker.BoxSelection.ActivePoint.Position, boxSelection.ActivePoint.VirtualSpaces - 1);
}
}
+ else
+ {
+ // Just take the starting points in the first and last selections
+ activePoint = selections[boxSelection.IsReversed ? 0 : selections.Count - 1].Start;
+ anchorPoint = selections[boxSelection.IsReversed ? selections.Count - 1 : 0].Start;
+ }
- // If there is nothing to delete, clear the selection
- if (spans.Count == 0)
+ VirtualSnapshotPoint newAnchor = anchorPoint.TranslateTo(_textView.TextSnapshot);
+ VirtualSnapshotPoint newActive = activePoint.TranslateTo(_textView.TextSnapshot);
+
+ var newSelection = new Selection(insertionPoint: newActive, anchorPoint: newAnchor, activePoint: newActive, boxSelection.InsertionPointAffinity);
+ if (_multiSelectionBroker.BoxSelection != newSelection)
{
- _textView.Caret.MoveTo(_textView.Selection.Start);
- _textView.Selection.Clear();
- _textView.Caret.EnsureVisible();
- return true;
+ _multiSelectionBroker.SetBoxSelection(newSelection);
}
+ }
+ else
+ {
+ // Perf: This is actually an n^2 algorithm here, since TryPerform... also loops through all the selections. Try to avoid copying this code
+ // elsewhere. We need it here because we're actually modifying each one based on its context AND because merges can happen with backspace so we
+ // can't do anything funny like caching the transformers.
+ for (int i = 0; i < selections.Count; i++)
+ {
+ //Some could have merged away, ignore return values here intentionally.
+ _multiSelectionBroker.TryPerformActionOnSelection(selections[i], transformer =>
+ {
+ // We can't use the virtual snapshot point TranslateTo since it will remove the virtual space (because the line's line break was deleted).
+ // VirtualSnapshotPoint.TranslateTo doesn't know what to do with virtual whitespace, so we have to do this ourselves.
+ if (selections[i].IsEmpty && selections[i].InsertionPoint.IsInVirtualSpace)
+ {
+ // Move the caret back one if we have an empty selection
+ transformer.MoveTo(new VirtualSnapshotPoint(transformer.Selection.InsertionPoint.Position, selections[i].InsertionPoint.VirtualSpaces - 1),
+ select: false,
+ insertionPointAffinity: PositionAffinity.Successor);
+ }
+ else
+ {
+ //Move the caret to the start of the selection.
+ transformer.MoveTo(new VirtualSnapshotPoint(transformer.Selection.InsertionPoint.Position, selections[i].Start.VirtualSpaces),
+ select: false,
+ PositionAffinity.Successor);
+ }
+ }, out _);
+ }
+ }
+
+ return true;
+ }
- boxDeletions = new NormalizedSnapshotSpanCollection(spans);
+ private bool WillBackspaceCreateEdit()
+ {
+ if (_multiSelectionBroker.IsBoxSelection)
+ {
+ // Edits can not happen if we're a box selection at the beginning of a line
+ var primary = _multiSelectionBroker.PrimarySelection;
+ if (primary.IsEmpty && primary.Start.Position == primary.Start.Position.GetContainingLine().Start)
+ {
+ return false;
+ }
}
- // Now, handle cases that require edits
- Func<bool> action = () =>
+ var selections = _multiSelectionBroker.AllSelections;
+ for (int i = 0; i < selections.Count; i++)
{
- // 1. An empty selection mean backspace the caret
- if (_textView.Selection.IsEmpty)
- return _editorPrimitives.Caret.DeletePrevious();
+ if ((!selections[i].Extent.SnapshotSpan.IsEmpty) ||
+ (selections[i].IsEmpty
+ && !selections[i].InsertionPoint.IsInVirtualSpace
+ && selections[i].InsertionPoint.Position.Position != 0))
+ {
+ return true;
+ }
+ }
- // 2. If this is an empty box, we may need to capture the new active/anchor points, as points in virtual space
- // won't track as we want them to through the edit.
- VirtualSnapshotPoint? anchorPoint = null;
- VirtualSnapshotPoint? activePoint = null;
+ return false;
+ }
- if (emptyBox)
+ private bool TryBackspaceEdit(IReadOnlyList<Selection> selections)
+ {
+ using (var edit = _textView.TextBuffer.CreateEdit())
+ {
+ for (int i = (selections.Count - 1); i >= 0; i--)
{
- if (_textView.Selection.AnchorPoint.IsInVirtualSpace)
+ var selection = selections[i];
+
+ if (selection.IsEmpty)
{
- anchorPoint = new VirtualSnapshotPoint(_textView.Selection.AnchorPoint.Position, _textView.Selection.AnchorPoint.VirtualSpaces - 1);
+ if (selection.Extent.IsInVirtualSpace)
+ {
+ continue;
+ }
+
+ if (!TryBackspaceEmptySelection(selection, edit))
+ {
+ return false;
+ }
}
- if (_textView.Selection.ActivePoint.IsInVirtualSpace)
+ else if (!edit.Delete(selection.Extent.SnapshotSpan))
{
- activePoint = new VirtualSnapshotPoint(_textView.Selection.ActivePoint.Position, _textView.Selection.ActivePoint.VirtualSpaces - 1);
+ return false;
}
}
- // 3. The selection is non-empty, so delete the selected spans (unless this is an empty box selection: An empty box selection means treat this as a backspace on each line)
- NormalizedSnapshotSpanCollection deletion = boxDeletions ?? _textView.Selection.SelectedSpans;
+ edit.Apply();
+ return !edit.Canceled;
+ }
+ }
+
+ private bool TryBackspaceEmptySelection(Selection selection, ITextEdit edit)
+ {
+ // Assumptions:
+ // We should have already validated this before calling.
+ Debug.Assert(selection.IsEmpty);
- int selectionStartVirtualSpaces = _textView.Selection.Start.VirtualSpaces;
+ // This method is only written to perform edits on text. Virtual space operations should be performed separately and not passed here.
+ Debug.Assert(!selection.InsertionPoint.IsInVirtualSpace);
- if (!DeleteHelper(deletion))
- return false;
+ // Performing deletion:
+ // Identify what should be deleted
+ if (selection.InsertionPoint.Position.Position == 0)
+ {
+ // We're at the beginning of the document, we're done.
+ return true;
+ }
- // 5. Now, fix up the start and end points if this is an empty box
- if (emptyBox && (anchorPoint.HasValue || activePoint.HasValue))
- {
- VirtualSnapshotPoint newAnchor = (anchorPoint.HasValue) ? anchorPoint.Value.TranslateTo(_textView.TextSnapshot) : _textView.Selection.AnchorPoint;
- VirtualSnapshotPoint newActive = (activePoint.HasValue) ? activePoint.Value.TranslateTo(_textView.TextSnapshot) : _textView.Selection.ActivePoint;
+ // Get the span of the previous element
+ SnapshotSpan previousElementSpan = TextView.GetTextElementSpan(selection.InsertionPoint.Position - 1);
+
+ // Here we have some interesting decisions to make. If this is a collapsed region, we want to delete the whole thing.
+ // If this is a multi-byte character, we typically want to delete just one byte to allow for easier typing in chinese and other languages.
+ // However, if that multi-byte character is a surrogate pair or newline, we want to delete the whole thing.
+
+ // We start by looking to see if this is a collapsed region or something like one.
+ if ((previousElementSpan.Length > 0) &&
+ (_textView.TextViewModel.IsPointInVisualBuffer(selection.InsertionPoint.Position, PositionAffinity.Successor)) &&
+ (!_textView.TextViewModel.IsPointInVisualBuffer(previousElementSpan.End - 1, PositionAffinity.Successor)))
+ {
+ // Since the previous character is not visible but the current one is, delete
+ // the entire previous text element span.
+ return edit.Delete(previousElementSpan);
+ }
+ else
+ {
+ //Next we test for surrogate pairs and newline:
+ ITextSnapshot snapshot = edit.Snapshot;
+ int previousPosition = selection.InsertionPoint.Position.Position - 1;
+
+ int index = previousPosition;
+ char currentCharacter = snapshot[previousPosition];
- _textView.Caret.MoveTo(_textView.Selection.ActivePoint);
- _textView.Selection.Select(newAnchor, newActive);
+ // By default VS (and many other apps) will delete only the last character
+ // of a combining character sequence. The one exception to this rule is
+ // surrogate pais which we are handling here.
+ if (char.GetUnicodeCategory(currentCharacter) == UnicodeCategory.Surrogate)
+ {
+ index--;
}
- else if (_textView.Selection.Mode != TextSelectionMode.Box)
+
+ if ((index > 0) &&
+ (currentCharacter == '\n') &&
+ (snapshot[previousPosition - 1] == '\r'))
{
- //Move the caret to the start of the selection (this doesn't happen automatically if the caret was in virtual space).
- //But we can't use the virtual snapshot point TranslateTo since it will remove the virtual space (because the line's line break was deleted).
- _textView.Caret.MoveTo(new VirtualSnapshotPoint(_textView.Selection.Start.Position, selectionStartVirtualSpaces));
- _textView.Selection.Clear();
+ index--;
}
- _textView.Caret.EnsureVisible();
- return true;
- };
+ // With index moved back in the cases of newline and surrogate pairs, this delete should handle all other cases.
+ return edit.Delete(new Span(index, previousPosition - index + 1));
+ }
+ }
- return ExecuteAction(Strings.DeleteCharToLeft, action, SelectionUpdate.ResetUnlessEmptyBox, true);
+ private bool TryBackspaceSelections()
+ {
+ if (_multiSelectionBroker.IsBoxSelection && _multiSelectionBroker.PrimarySelection.InsertionPoint.IsInVirtualSpace)
+ {
+ _multiSelectionBroker.SetSelection(new Selection(_multiSelectionBroker.PrimarySelection.Start));
+ }
+ else if (!_multiSelectionBroker.IsBoxSelection)
+ {
+ _multiSelectionBroker.PerformActionOnAllSelections(transformer =>
+ {
+ if (transformer.Selection.IsEmpty)
+ {
+ if (transformer.Selection.InsertionPoint.IsInVirtualSpace)
+ {
+ MoveToPreviousTabStop(transformer);
+ }
+ else
+ {
+ transformer.PerformAction(PredefinedSelectionTransformations.MoveToPreviousCaretPosition);
+ }
+ }
+ else
+ {
+ transformer.MoveTo(transformer.Selection.Start, select: false, PositionAffinity.Successor);
+ }
+ });
+ }
+
+ return true;
+ }
+
+ private void MoveToPreviousTabStop(ISelectionTransformer transformer)
+ {
+ var previousStop = GetPreviousIndentStopInVirtualSpace(transformer.Selection.InsertionPoint);
+ transformer.MoveTo(previousStop, select: false, PositionAffinity.Successor);
}
private void ResetVirtualSelection()
@@ -1048,8 +1160,12 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
ITextViewLine activeLine = (_textView.Selection.IsReversed) ? startLine : endLine;
VirtualSnapshotPoint newCaret = activeLine.GetInsertionBufferPositionFromXCoordinate(leftEdge);
- _textView.Caret.MoveTo(newCaret);
- _textView.Selection.Clear();
+ _multiSelectionBroker.ClearSecondarySelections();
+ Selection unused;
+ _multiSelectionBroker.TryPerformActionOnSelection(_multiSelectionBroker.PrimarySelection, transformer =>
+ {
+ transformer.MoveTo(newCaret, select: false, PositionAffinity.Successor);
+ }, out unused);
}
public bool DeleteFullLine()
@@ -1255,7 +1371,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
if (textLine == null)
{
- throw new ArgumentNullException("textLine");
+ throw new ArgumentNullException(nameof(textLine));
}
if (extendSelection)
@@ -1288,7 +1404,8 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
/// </param>
public void MoveLineUp(bool select)
{
- _editorPrimitives.Caret.MoveToPreviousLine(select);
+ _multiSelectionBroker.PerformActionOnAllSelections(select ? PredefinedSelectionTransformations.SelectToPreviousLine : PredefinedSelectionTransformations.MoveToPreviousLine);
+ _textView.Caret.EnsureVisible();
}
/// <summary>
@@ -1299,7 +1416,8 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
/// </param>
public void MoveLineDown(bool select)
{
- _editorPrimitives.Caret.MoveToNextLine(select);
+ _multiSelectionBroker.PerformActionOnAllSelections(select ? PredefinedSelectionTransformations.SelectToNextLine : PredefinedSelectionTransformations.MoveToNextLine);
+ _textView.Caret.EnsureVisible();
}
/// <summary>
@@ -1310,7 +1428,8 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
/// </param>
public void PageUp(bool select)
{
- _editorPrimitives.Caret.MovePageUp(select);
+ _multiSelectionBroker.PerformActionOnAllSelections(select ? PredefinedSelectionTransformations.SelectPageUp : PredefinedSelectionTransformations.MovePageUp);
+
}
/// <summary>
@@ -1321,7 +1440,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
/// </param>
public void PageDown(bool select)
{
- _editorPrimitives.Caret.MovePageDown(select);
+ _multiSelectionBroker.PerformActionOnAllSelections(select ? PredefinedSelectionTransformations.SelectPageDown : PredefinedSelectionTransformations.MovePageDown);
}
/// <summary>
@@ -1332,34 +1451,14 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
/// </param>
public void MoveToEndOfLine(bool select)
{
- // If the caret is at the start of an empty line, respond by trying to position
- // the caret at the smart indent location.
- if (_textView.Caret.Position.BufferPosition.GetContainingLine().Extent.IsEmpty &&
- !_textView.Caret.InVirtualSpace)
- {
- if (PositionCaretWithSmartIndent(useOnlyVirtualSpace: true, extendSelection: select))
- {
- _editorPrimitives.Caret.EnsureVisible();
- return;
- }
- }
-
- _editorPrimitives.Caret.MoveToEndOfViewLine(select);
+ _multiSelectionBroker.PerformActionOnAllSelections(select ? PredefinedSelectionTransformations.SelectToEndOfLine : PredefinedSelectionTransformations.MoveToEndOfLine);
+ _textView.Caret.EnsureVisible();
}
public void MoveToHome(bool select)
{
- int newPosition = _editorPrimitives.Caret.GetFirstNonWhiteSpaceCharacterOnViewLine().CurrentPosition;
-
- // If the caret is already at the first non-whitespace character or
- // the line is entirely whitepsace, move to the start of the view line.
- if (newPosition == _editorPrimitives.Caret.CurrentPosition ||
- newPosition == _editorPrimitives.Caret.EndOfViewLine)
- {
- newPosition = _editorPrimitives.Caret.StartOfViewLine;
- }
-
- _editorPrimitives.Caret.MoveTo(newPosition, select);
+ _multiSelectionBroker.PerformActionOnAllSelections(select ? PredefinedSelectionTransformations.SelectToHome : PredefinedSelectionTransformations.MoveToHome);
+ _textView.Caret.EnsureVisible();
}
public void MoveToStartOfLine(bool select)
@@ -1374,117 +1473,103 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
Func<bool> action = () =>
{
- VirtualSnapshotPoint caret = _textView.Caret.Position.VirtualBufferPosition;
- ITextSnapshotLine line = caret.Position.GetContainingLine();
- ITextSnapshot snapshot = line.Snapshot;
-
- // todo: the following logic is duplicated in DefaultTextPointPrimitive.InsertNewLine()
- // didn't call that method here because it would result in two text transactions
- // ultimately everything here should probably move into primitives.
- string textToInsert = TextBufferOperationHelpers.GetNewLineCharacterToInsert(line, _editorOptions);
+ bool editSucceeded = true;
+ ITextSnapshot snapshot = _textView.TextViewModel.EditBuffer.CurrentSnapshot;
- bool succeeded = false;
- bool caretMoved = false;
- EventHandler<CaretPositionChangedEventArgs> caretWatcher = delegate (object sender, CaretPositionChangedEventArgs e)
+ using (var batchOp = _multiSelectionBroker.BeginBatchOperation())
{
- caretMoved = true;
- };
+ var toIndent = new HashSet<object>();
- // Indent unless the caret is at column 0 or the current line is empty.
- // This appears to be added as a fix for Venus; which combined with our implementation of
- // PositionCaretWithSmartIndent does not indent correctly on NewLine when Caret is at column 0.
- bool doIndent = caret.IsInVirtualSpace || (caret.Position != _textView.Caret.ContainingTextViewLine.Start)
- || (_textView.Caret.ContainingTextViewLine.Extent.Length == 0);
-
- try
- {
using (var edit = _textView.TextBuffer.CreateEdit())
{
- _textView.Caret.PositionChanged += caretWatcher;
- int searchIndexforPreviousWhitespaces = -1;
- var lineContainingTrimTrailingWhitespacesSearchindex = line; // usually is the line containing caret.
+ if (_multiSelectionBroker.IsBoxSelection)
+ {
+ _multiSelectionBroker.BreakBoxSelection();
+ }
- if (_textView.Selection.Mode == TextSelectionMode.Stream)
+ _multiSelectionBroker.PerformActionOnAllSelections(transformer =>
{
+ bool doIndent = false;
+
+ VirtualSnapshotPoint caret = transformer.Selection.InsertionPoint;
+ ITextSnapshotLine line = caret.Position.GetContainingLine();
+ ITextViewLine viewLine = _textView.GetTextViewLineContainingBufferPosition(caret.Position);
+
+ // todo: the following logic is duplicated in DefaultTextPointPrimitive.InsertNewLine()
+ // didn't call that method here because it would result in two text transactions
+ // ultimately everything here should probably move into primitives.
+ string textToInsert = TextBufferOperationHelpers.GetNewLineCharacterToInsert(line, _editorOptions);
+
+ // Indent unless the caret is at column 0 or the current line is empty.
+ // This appears to be added as a fix for Venus; which combined with our implementation of
+ // PositionCaretWithSmartIndent does not indent correctly on NewLine when Caret is at column 0.
+ doIndent = caret.IsInVirtualSpace || (caret.Position != viewLine.Extent.Start)
+ || (viewLine.Extent.Length == 0);
+
+ int searchIndexforPreviousWhitespaces = -1;
+ var lineContainingTrimTrailingWhitespacesSearchindex = line; // usually is the line containing caret.
+
// This ignores virtual space
- Span selection = _textView.Selection.StreamSelectionSpan.SnapshotSpan;
- succeeded = edit.Replace(selection, textToInsert);
+ Span selection = transformer.Selection.Extent.SnapshotSpan;
+
+ editSucceeded = editSucceeded && edit.Replace(selection, textToInsert);
// For stream selection you should always look for trimming whitespaces previous to selection.start instead of caret position
lineContainingTrimTrailingWhitespacesSearchindex = snapshot.GetLineFromPosition(selection.Start);
searchIndexforPreviousWhitespaces = selection.Start - lineContainingTrimTrailingWhitespacesSearchindex.Start.Position;
- }
- else
- {
- var isDeleteSuccessfull = true;
- searchIndexforPreviousWhitespaces = caret.Position.Position - line.Start.Position;
- foreach (var span in _textView.Selection.SelectedSpans)
+ // Trim traling whitespaces as we insert the new line as well if the editor option is set
+ if (_editorOptions.GetOptionValue<bool>(DefaultOptions.TrimTrailingWhiteSpaceOptionId))
{
- // In a box selection if the caret is forward positioned then
- //we should search for whitespaces from the start of the last span since the spans are not yet deleted
- if (span.End.Position == caret.Position.Position)
- {
- searchIndexforPreviousWhitespaces = span.Start.Position - line.Start.Position;
- }
- if (!edit.Delete(span))
- {
- isDeleteSuccessfull = false;
- }
- }
- if (!isDeleteSuccessfull)
- return false;
- succeeded = edit.Replace(new SnapshotSpan(_textView.Caret.Position.BufferPosition, 0),
- textToInsert);
- }
- // Trim traling whitespaces as we insert the new line as well if the editor option is set
- if (_editorOptions.GetOptionValue<bool>(DefaultOptions.TrimTrailingWhiteSpaceOptionId))
- {
- var previousNonWhitespaceCharacterIndex = lineContainingTrimTrailingWhitespacesSearchindex.IndexOfPreviousNonWhiteSpaceCharacter(searchIndexforPreviousWhitespaces);
+ var previousNonWhitespaceCharacterIndex = lineContainingTrimTrailingWhitespacesSearchindex.IndexOfPreviousNonWhiteSpaceCharacter(searchIndexforPreviousWhitespaces);
- // Note: If previousNonWhiteSpaceCharacter index is -1 this will automatically default to line.start.position
- var startIndexForTrailingWhitespaceSpan = lineContainingTrimTrailingWhitespacesSearchindex.Start.Position + previousNonWhitespaceCharacterIndex + 1;
- var lengthOfTrailingWhitespaceSpan = searchIndexforPreviousWhitespaces - previousNonWhitespaceCharacterIndex - 1;
+ // Note: If previousNonWhiteSpaceCharacter index is -1 this will automatically default to line.start.position
+ var startIndexForTrailingWhitespaceSpan = lineContainingTrimTrailingWhitespacesSearchindex.Start.Position + previousNonWhitespaceCharacterIndex + 1;
+ var lengthOfTrailingWhitespaceSpan = searchIndexforPreviousWhitespaces - previousNonWhitespaceCharacterIndex - 1;
- if (lengthOfTrailingWhitespaceSpan != 0) // If there are any whitespaces before the caret delete them
- edit.Delete(new Span(startIndexForTrailingWhitespaceSpan, lengthOfTrailingWhitespaceSpan));
- }
+ if (lengthOfTrailingWhitespaceSpan != 0) // If there are any whitespaces before the caret delete them
+ edit.Delete(new Span(startIndexForTrailingWhitespaceSpan, lengthOfTrailingWhitespaceSpan));
+ }
+
+ if (doIndent)
+ {
+ // WARNING: We're caching the transformers here because we are both inside a batch operation
+ // and we're inserting text, so we know that there will be no merging of selections going on.
+ // We're using them as a perf optimization so we can avoid searching through the list of selections
+ // later, since we already know what we need.
+ //
+ // When writing multiple selection-aware code, do everything you can to avoid saving transformers.
+ toIndent.Add(transformer);
+ }
+ });
// Apply all changes
- succeeded = (edit.Apply() != snapshot);
+ editSucceeded = editSucceeded && (edit.Apply() != snapshot);
}
- }
- finally
- {
- _textView.Caret.PositionChanged -= caretWatcher;
- }
- if (succeeded)
- {
- if (doIndent)
+ if (editSucceeded && toIndent.Count > 0)
{
- caret = _textView.Caret.Position.VirtualBufferPosition;
- line = caret.Position.GetContainingLine();
-
- //Only attempt to auto indent if -- after the edit above -- no one moved the caret on the buffer change
- //and the caret is at the start of its new line (no one did any funny edits to the buffer on the buffer change).
- if ((!caretMoved) && (caret.Position == line.Start))
- {
- caretMoved = PositionCaretWithSmartIndent(useOnlyVirtualSpace: false, extendSelection: false);
- if (!caretMoved && caret.IsInVirtualSpace)
- {
- //No smart indent logic so make sure the caret is not in virtual space.
- _textView.Caret.MoveTo(caret.Position);
- }
- }
+ // Need to move carets to indented location after the edit has completed, so we put them at the correct indentation in the new snapshot.
+ _multiSelectionBroker.PerformActionOnAllSelections(transformer =>
+ {
+ if (toIndent.Contains(transformer))
+ {
+ var caretMoved = PositionCaretWithSmartIndent(transformer, useOnlyVirtualSpace: false, extendSelection: false);
+ if (!caretMoved && transformer.Selection.InsertionPoint.IsInVirtualSpace)
+ {
+ //No smart indent logic so make sure the caret is not in virtual space.
+ transformer.MoveTo(new VirtualSnapshotPoint(transformer.Selection.InsertionPoint.Position), select: false, PositionAffinity.Successor);
+ }
+ transformer.PerformAction(PredefinedSelectionTransformations.ClearSelection);
+ transformer.CapturePreferredReferencePoint();
+ }
+ });
}
- ResetSelection();
}
- return succeeded;
+
+ return editSucceeded;
};
return ExecuteAction(Strings.InsertNewLine, action, SelectionUpdate.Ignore, true);
}
-
-
public bool OpenLineAbove()
{
Func<bool> action = () =>
@@ -1767,11 +1852,10 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
Debug.Assert(startLine.LineNumber <= endLine.LineNumber);
- var view = _textView as ITextView;
bool isEditMade = false;
bool success = true;
- using (ITextEdit edit = view.TextBuffer.CreateEdit())
+ using (ITextEdit edit = _textView.TextBuffer.CreateEdit())
{
var currentSnapshot = _textView.TextBuffer.CurrentSnapshot;
for (int i = startLine.LineNumber; i <= endLine.LineNumber; i++)
@@ -1793,7 +1877,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
return success;
}
- private Span? GetTrailingWhitespaceSpanToDelete(ITextSnapshotLine line)
+ private static Span? GetTrailingWhitespaceSpanToDelete(ITextSnapshotLine line)
{
int indexOfLastNonWhitespaceCharacter = -1;
@@ -1864,7 +1948,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
public void SelectLine(ITextViewLine viewLine, bool extendSelection)
{
if (viewLine == null)
- throw new ArgumentNullException("viewLine");
+ throw new ArgumentNullException(nameof(viewLine));
SnapshotPoint anchor;
SnapshotPoint active;
@@ -1917,91 +2001,205 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
/// </summary>
public bool Delete()
{
- bool emptyBox = IsEmptyBoxSelection();
- NormalizedSnapshotSpanCollection boxDeletions = null;
+ bool success = true;
- // First, handle cases that don't require edits
- if (_textView.Selection.IsEmpty)
+ if (WillDeleteCreateEdit())
{
- if (_textView.Caret.Position.BufferPosition.Position == _textView.TextSnapshot.Length)
+ var selections = _multiSelectionBroker.AllSelections;
+ var boxSelection = _multiSelectionBroker.BoxSelection;
+ var primarySelection = _multiSelectionBroker.PrimarySelection;
+
+ Func<bool> action = () =>
{
- return true;
- }
+ using (_multiSelectionBroker.BeginBatchOperation())
+ {
+ if (TryDeleteEdit(selections))
+ {
+ return TryPostDeleteSelectionUpdate(selections, primarySelection, boxSelection);
+ }
+ }
+ return false;
+ };
+
+ success = ExecuteAction(Strings.DeleteCharToRight, action, SelectionUpdate.Ignore, ensureVisible: false);
}
- // If the entire selection is empty and in virtual space, clear it
- else if (_textView.Selection.VirtualSelectedSpans.All(s => s.SnapshotSpan.IsEmpty && s.IsInVirtualSpace))
+ else
{
- this.ResetVirtualSelection();
- return true;
+ success = TryDeleteSelections();
}
- else if (emptyBox) // empty box selection, make sure it is valid
+
+ if (success)
{
- List<SnapshotSpan> spans = new List<SnapshotSpan>();
+ _multiSelectionBroker.TryEnsureVisible(_multiSelectionBroker.PrimarySelection, EnsureSpanVisibleOptions.MinimumScroll);
+ }
- foreach (var span in _textView.Selection.SelectedSpans)
+ return success;
+ }
+
+ private bool TryDeleteSelections()
+ {
+ if (_multiSelectionBroker.IsBoxSelection && _multiSelectionBroker.PrimarySelection.InsertionPoint.IsInVirtualSpace)
+ {
+ _multiSelectionBroker.SetSelection(new Selection(_multiSelectionBroker.PrimarySelection.Start));
+ }
+ else if (!_multiSelectionBroker.IsBoxSelection)
+ {
+ _multiSelectionBroker.PerformActionOnAllSelections(transformer =>
{
- var line = span.Start.GetContainingLine();
- if (span.Start < line.End)
+ if (!transformer.Selection.IsEmpty)
{
- spans.Add(_textView.GetTextElementSpan(span.Start));
+ transformer.MoveTo(transformer.Selection.Start, select: false, PositionAffinity.Successor);
}
- }
+ });
+ }
+
+ return true;
+ }
+
+ private bool TryPostDeleteSelectionUpdate(IReadOnlyList<Selection> selections, Selection primarySelection, Selection boxSelection)
+ {
+ // Throughout this method, the parameters passed in are the OLD values, and the parameters on _multiSelectionBroker are the NEW ones
+ if (boxSelection != Selection.Invalid)
+ {
+ // If this is an empty box, we may need to capture the new active/anchor points, as points in virtual space
+ // won't track as we want them to through the edit.
+ VirtualSnapshotPoint anchorPoint = _multiSelectionBroker.BoxSelection.AnchorPoint;
+ VirtualSnapshotPoint activePoint = _multiSelectionBroker.BoxSelection.ActivePoint;
- // If there is nothing to delete, clear the selection
- if (spans.Count == 0)
+ if (primarySelection.IsEmpty)
{
- _textView.Caret.MoveTo(_textView.Selection.Start);
- _textView.Selection.Clear();
- return true;
+ if (boxSelection.AnchorPoint.IsInVirtualSpace)
+ {
+ anchorPoint = new VirtualSnapshotPoint(_multiSelectionBroker.BoxSelection.AnchorPoint.Position, boxSelection.AnchorPoint.VirtualSpaces);
+ }
+ if (boxSelection.ActivePoint.IsInVirtualSpace)
+ {
+ activePoint = new VirtualSnapshotPoint(_multiSelectionBroker.BoxSelection.ActivePoint.Position, boxSelection.ActivePoint.VirtualSpaces);
+ }
}
+ else
+ {
+ // Just take the starting points in the first and last selections
+ activePoint = selections[boxSelection.IsReversed ? 0 : selections.Count - 1].Start;
+ anchorPoint = selections[boxSelection.IsReversed ? selections.Count - 1 : 0].Start;
+ }
+
+ VirtualSnapshotPoint newAnchor = anchorPoint.TranslateTo(_textView.TextSnapshot);
+ VirtualSnapshotPoint newActive = activePoint.TranslateTo(_textView.TextSnapshot);
- boxDeletions = new NormalizedSnapshotSpanCollection(spans);
+ var newSelection = new Selection(insertionPoint: newActive, anchorPoint: newAnchor, activePoint: newActive, boxSelection.InsertionPointAffinity);
+ if (_multiSelectionBroker.BoxSelection != newSelection)
+ {
+ _multiSelectionBroker.SetBoxSelection(newSelection);
+ }
+ }
+ else
+ {
+ // Perf: This is actually an n^2 algorithm here, since TryPerform... also loops through all the selections. Try to avoid copying this code
+ // elsewhere. We need it here because we're actually modifying each one based on its context AND because merges can happen with backspace so we
+ // can't do anything funny like caching the transformers.
+ for (int i = 0; i < selections.Count; i++)
+ {
+ //Some could have merged away, ignore return values here intentionally.
+ _multiSelectionBroker.TryPerformActionOnSelection(selections[i], transformer =>
+ {
+ // We can't use the virtual snapshot point TranslateTo since it will remove the virtual space (because the line's line break was deleted).
+ // VirtualSnapshotPoint.TranslateTo doesn't know what to do with virtual whitespace, so we have to do this ourselves.
+ if (selections[i].IsEmpty && selections[i].InsertionPoint.IsInVirtualSpace)
+ {
+ // Move the caret back one if we have an empty selection
+ transformer.MoveTo(new VirtualSnapshotPoint(transformer.Selection.InsertionPoint.Position, selections[i].InsertionPoint.VirtualSpaces - 1),
+ select: false,
+ insertionPointAffinity: PositionAffinity.Successor);
+ }
+ else
+ {
+ //Move the caret to the start of the selection.
+ transformer.MoveTo(new VirtualSnapshotPoint(transformer.Selection.InsertionPoint.Position, selections[i].Start.VirtualSpaces),
+ select: false,
+ PositionAffinity.Successor);
+ }
+ }, out _);
+ }
}
+ return true;
+ }
- // Now handle cases that require edits
- Func<bool> action = () =>
+ private bool TryDeleteEdit(IReadOnlyList<Selection> selections)
+ {
+ using (var edit = _textView.TextBuffer.CreateEdit())
{
- if (_textView.Selection.IsEmpty)
+ for (int i = (selections.Count - 1); i >= 0; i--)
{
- CaretPosition position = _textView.Caret.Position;
- if (position.VirtualBufferPosition.IsInVirtualSpace)
+ var selection = selections[i];
+
+ if (selection.IsEmpty)
{
- string whitespace = GetWhitespaceForVirtualSpace(position.VirtualBufferPosition);
- SnapshotSpan span = _textView.GetTextElementSpan(_textView.Caret.Position.VirtualBufferPosition.Position);
+ if (_multiSelectionBroker.IsBoxSelection)
+ {
+ var endOfLine = selection.InsertionPoint.Position.GetContainingLine().End;
+
+ if (selection.InsertionPoint.Position == endOfLine)
+ {
+ continue;
+ }
+ }
+
+ if (selection.InsertionPoint.IsInVirtualSpace)
+ {
+ var whitespace = GetWhitespaceForVirtualSpace(selection.InsertionPoint);
+ var span = _textView.GetTextElementSpan(selection.InsertionPoint.Position);
- return ReplaceHelper(span, whitespace);
+ if (!edit.Replace(span, whitespace))
+ {
+ return false;
+ }
+ }
+ else if (!edit.Delete(_textView.GetTextElementSpan(selection.InsertionPoint.Position)))
+ {
+ return false;
+ }
}
- else
+ else if (!edit.Delete(selection.Extent.SnapshotSpan))
{
- return DeleteHelper(_textView.GetTextElementSpan(position.VirtualBufferPosition.Position));
+ return false;
}
}
- else
- {
- // The selection is non-empty, so delete selected spans
- NormalizedSnapshotSpanCollection deletion = _textView.Selection.SelectedSpans;
- // Unless it is an empty box selection, so treat it as a delete on each line
- if (emptyBox && boxDeletions != null)
- deletion = boxDeletions;
+ edit.Apply();
+ return !edit.Canceled;
+ }
+ }
- int selectionStartVirtualSpaces = _textView.Selection.Start.VirtualSpaces;
- bool succeeded = DeleteHelper(deletion);
+ private bool WillDeleteCreateEdit()
+ {
+ var selections = _multiSelectionBroker.AllSelections;
- if (succeeded && (_textView.Selection.Mode != TextSelectionMode.Box))
+ if (_multiSelectionBroker.IsBoxSelection)
+ {
+ // Edits can not happen if we're a box selection at the end of every line
+ for (int i = 0; i < selections.Count; i++)
+ {
+ if (selections[i].Start.Position < selections[i].Start.Position.GetContainingLine().End)
{
- //Move the caret to the start of the selection (this doesn't happen automatically if the caret was in virtual space).
- //But we can't use the virtual snapshot point TranslateTo since it will remove the virtual space (because the line's line break was deleted).
- _textView.Caret.MoveTo(new VirtualSnapshotPoint(_textView.Selection.Start.Position, selectionStartVirtualSpaces));
- _textView.Selection.Clear();
+ return true;
}
-
- return succeeded;
}
- };
+ }
+ else
+ {
+ for (int i = 0; i < selections.Count; i++)
+ {
+ if ((!selections[i].Extent.SnapshotSpan.IsEmpty) ||
+ (selections[i].IsEmpty && selections[i].InsertionPoint.Position.Position != _multiSelectionBroker.CurrentSnapshot.Length))
+ {
+ return true;
+ }
+ }
+ }
- return ExecuteAction(Strings.DeleteText, action, SelectionUpdate.ResetUnlessEmptyBox, true);
+ return false;
}
/// <summary>
@@ -2016,7 +2214,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
// Validate
if (text == null)
{
- throw new ArgumentNullException("text");
+ throw new ArgumentNullException(nameof(text));
}
Func<bool> action = () =>
@@ -2042,7 +2240,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
// Validate
if (span.End > _textView.TextSnapshot.Length)
{
- throw new ArgumentOutOfRangeException("span");
+ throw new ArgumentOutOfRangeException(nameof(span));
}
Func<bool> action = () =>
@@ -2077,7 +2275,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
if (searchText == null)
{
- throw new ArgumentNullException("searchText");
+ throw new ArgumentNullException(nameof(searchText));
}
FindData findData = new FindData(searchText, _textView.TextSnapshot);
@@ -2341,7 +2539,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
}
string streamText = string.Join(_editorOptions.GetNewLineCharacter() + whitespace, lines);
- return this.InsertText(streamText.ToString(), true, Strings.Paste, isOverwriteModeEnabled: false);
+ return this.InsertText(streamText.ToString(CultureInfo.CurrentCulture), true, Strings.Paste, isOverwriteModeEnabled: false);
}
else
{
@@ -2438,7 +2636,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
// Validate
if (lineNumber < 0 || lineNumber > _textView.TextSnapshot.LineCount - 1)
- throw new ArgumentOutOfRangeException("lineNumber");
+ throw new ArgumentOutOfRangeException(nameof(lineNumber));
ITextSnapshotLine line = _textView.TextSnapshot.GetLineFromLineNumber(lineNumber);
@@ -2775,9 +2973,9 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
#endif
}
- #endregion // IEditorOperations Members
+#endregion // IEditorOperations Members
- #region Virtual Space to Whitespace helpers
+#region Virtual Space to Whitespace helpers
public string GetWhitespaceForVirtualSpace(VirtualSnapshotPoint point)
{
@@ -2866,9 +3064,9 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
return textToInsert;
}
- #endregion
+#endregion
- #region Text insertion helpers
+#region Text insertion helpers
private bool InsertText(string text, bool final)
{
@@ -2880,7 +3078,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
// Validate
if (text == null)
{
- throw new ArgumentNullException("text");
+ throw new ArgumentNullException(nameof(text));
}
if ((text.Length == 0) && !final)
@@ -2937,7 +3135,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
}
else
{
- replaceSpans = _textView.Selection.VirtualSelectedSpans;
+ replaceSpans = _multiSelectionBroker.VirtualSelectedSpans;
}
// The provisional composition span should be null here (the IME should
@@ -2983,18 +3181,21 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
}
else
{
- VirtualSnapshotPoint insertionPoint = _textView.Caret.Position.VirtualBufferPosition;
- if (isOverwriteModeEnabled && !insertionPoint.IsInVirtualSpace)
+ var spans = new List<VirtualSnapshotSpan>();
+ foreach (var caret in _multiSelectionBroker.GetSelectionsIntersectingSpan(new SnapshotSpan(_multiSelectionBroker.CurrentSnapshot, 0, _multiSelectionBroker.CurrentSnapshot.Length)))
{
- SnapshotPoint point = insertionPoint.Position;
- replaceSpans = new VirtualSnapshotSpan[] { new VirtualSnapshotSpan(
- new SnapshotSpan(point, _textView.GetTextElementSpan(point).End)) };
- }
- else
- {
- replaceSpans = new VirtualSnapshotSpan[] {
- new VirtualSnapshotSpan(insertionPoint, insertionPoint) };
+ var insertionPoint = caret.InsertionPoint;
+ if (isOverwriteModeEnabled && !insertionPoint.IsInVirtualSpace)
+ {
+ SnapshotPoint point = insertionPoint.Position;
+ spans.Add(new VirtualSnapshotSpan(new SnapshotSpan(point, _textView.GetTextElementSpan(point).End)));
+ }
+ else
+ {
+ spans.Add(new VirtualSnapshotSpan(insertionPoint, insertionPoint));
+ }
}
+ replaceSpans = spans;
}
ITextVersion currentVersion = _textView.TextSnapshot.Version;
@@ -3053,17 +3254,28 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
if (editSuccessful)
{
- // Get rid of virtual space if there is any,
- // since we've just made it non-virtual
- _textView.Caret.MoveTo(_textView.Caret.Position.BufferPosition);
- _textView.Selection.Select(
- new VirtualSnapshotPoint(_textView.Selection.AnchorPoint.Position),
- new VirtualSnapshotPoint(_textView.Selection.ActivePoint.Position));
+ if (_multiSelectionBroker.IsBoxSelection)
+ {
+ _textView.Caret.MoveTo(_textView.Caret.Position.BufferPosition);
+ _textView.Selection.Select(
+ new VirtualSnapshotPoint(_textView.Selection.AnchorPoint.Position),
+ new VirtualSnapshotPoint(_textView.Selection.ActivePoint.Position));
+
+ // If the selection ends up being non-empty (meaning not an empty
+ // single selection *or* an empty box), then clear it.
+ if (_textView.Selection.VirtualSelectedSpans.Any(s => !s.IsEmpty))
+ _textView.Selection.Clear();
- // If the selection ends up being non-empty (meaning not an empty
- // single selection *or* an empty box), then clear it.
- if (_textView.Selection.VirtualSelectedSpans.Any(s => !s.IsEmpty))
- _textView.Selection.Clear();
+ }
+ else
+ {
+ _multiSelectionBroker.PerformActionOnAllSelections(transformer =>
+ {
+ // We've done the edit now. We need to both remove virtual space and clear selections.
+ var newInsertion = new VirtualSnapshotPoint(transformer.Selection.InsertionPoint.Position, 0);
+ transformer.MoveTo(newInsertion, select: false, PositionAffinity.Successor);
+ });
+ }
_textView.Caret.EnsureVisible();
@@ -3118,7 +3330,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
public bool InsertTextAsBox(string text, out VirtualSnapshotPoint boxStart, out VirtualSnapshotPoint boxEnd, string undoText)
{
if (text == null)
- throw new ArgumentNullException("text");
+ throw new ArgumentNullException(nameof(text));
boxStart = boxEnd = _textView.Caret.Position.VirtualBufferPosition;
@@ -3290,9 +3502,9 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
return succeeded;
}
- #endregion
+#endregion
- #region Clipboard and RTF helpers
+#region Clipboard and RTF helpers
private Func<bool> PrepareClipboardSelectionCopy()
{
@@ -3376,7 +3588,6 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
// which causes the OS to send out two almost simultaneous clipboard open/close notification pairs
// which confuse applications that try to synchronize clipboard data between multiple machines such
// as MagicMouse or remote desktop.
-
Clipboard.SetDataObject(dataObject, false);
#endif
@@ -3392,11 +3603,6 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
private string GenerateRtf(NormalizedSnapshotSpanCollection spans)
{
#if WINDOWS
- if (_factory.RtfBuilderService == null)
- {
- return null;
- }
-
//Don't generate RTF for large spans (since it is expensive and probably not wanted).
int length = spans.Sum((span) => span.Length);
if (length < _textView.Options.GetOptionValue(MaxRtfCopyLength.OptionKey))
@@ -3426,9 +3632,9 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
return GenerateRtf(new NormalizedSnapshotSpanCollection(span));
}
- #endregion
+#endregion
- #region Horizontal whitespace helpers
+#region Horizontal whitespace helpers
private bool DeleteHorizontalWhitespace()
{
@@ -3610,9 +3816,9 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
return startPoint;
}
- #endregion
+#endregion
- #region Indent/unindent helpers
+#region Indent/unindent helpers
// Perform the given indent action (indent/unindent) on each line at the first non-whitespace
// character, skipping lines that are either empty or just whitespace.
@@ -3912,9 +4118,9 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
return new VirtualSnapshotPoint(point.Position);
}
- #endregion
+#endregion
- #region Box Selection indent/unindent helpers
+#region Box Selection indent/unindent helpers
/// <summary>
/// Given a "fix-up" anchor/active point determined before the box operation, fix up the current selection's
@@ -4011,7 +4217,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
textPoint.MoveTo(i);
string character = textPoint.GetNextCharacter();
- if (character != " " && character != "\t")
+ if (!string.Equals(character, " ", StringComparison.Ordinal) && !string.Equals(character, "\t", StringComparison.Ordinal))
break;
column = textPoint.Column;
@@ -4028,9 +4234,9 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
return maxColumnUnindent;
}
- #endregion
+#endregion
- #region Miscellaneous line helpers
+#region Miscellaneous line helpers
private DisplayTextRange GetFullLines()
{
@@ -4094,9 +4300,9 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
return firstTextColumn.CurrentPosition == displayTextPoint.EndOfViewLine;
}
- #endregion
+#endregion
- #region Tabs <-> spaces
+#region Tabs <-> spaces
private bool ConvertSpacesAndTabsHelper(bool toTabs)
{
@@ -4214,16 +4420,16 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
}
Span replaceSpan = Span.FromBounds(whiteSpaceStart, whiteSpaceEnd);
- if ((replaceSpan.Length != textToInsert.Length) || (textToInsert != textEdit.Snapshot.GetText(replaceSpan))) //performance hack: don't get the text if we know they'll be different.
+ if ((replaceSpan.Length != textToInsert.Length) || (!string.Equals(textToInsert, textEdit.Snapshot.GetText(replaceSpan), StringComparison.Ordinal))) //performance hack: don't get the text if we know they'll be different.
return textEdit.Replace(replaceSpan, textToInsert);
}
return true;
}
- #endregion
+#endregion
- #region Edit/Replace/Delete helpers
+#region Edit/Replace/Delete helpers
internal bool EditHelper(Func<ITextEdit, bool> editAction)
{
@@ -4300,7 +4506,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
});
}
- #endregion
+#endregion
internal bool IsEmptyBoxSelection()
{
@@ -4318,9 +4524,9 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
/// <param name="extendSelection">If <c>true</c>, extend the current selection, from the existing anchor point,
/// to the new caret position.</param>
/// <returns><c>true</c> if the caret was positioned in virtual space.</returns>
- private bool PositionCaretWithSmartIndent(bool useOnlyVirtualSpace = true, bool extendSelection = false)
+ private bool PositionCaretWithSmartIndent(ISelectionTransformer transformer, bool useOnlyVirtualSpace = true, bool extendSelection = false)
{
- var caretPosition = _textView.Caret.Position.VirtualBufferPosition;
+ var caretPosition = transformer.Selection.InsertionPoint;
var caretLine = caretPosition.Position.GetContainingLine();
int? indentation = _factory.SmartIndentationService.GetDesiredIndentation(_textView, caretLine);
@@ -4330,8 +4536,8 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
//Position the caret in virtual space at the appropriate indentation.
var newCaretPoint = new VirtualSnapshotPoint(caretPosition.Position, Math.Max(0, indentation.Value - caretLine.Length));
- var anchorPoint = (extendSelection) ? _textView.Selection.AnchorPoint : newCaretPoint;
- SelectAndMoveCaret(anchorPoint, newCaretPoint, selectionMode: TextSelectionMode.Stream, scrollOptions: null);
+ var anchorPoint = (extendSelection) ? transformer.Selection.AnchorPoint : newCaretPoint;
+ transformer.MoveTo(anchorPoint, newCaretPoint, newCaretPoint, PositionAffinity.Successor);
return true;
}
else if (!useOnlyVirtualSpace)
@@ -4355,7 +4561,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
/// <param name="line">Which line to evaluate</param>
/// <param name="startPosition">Position where the count starts</param>
/// <returns>Number of leading whitespace characters located after startPosition</returns>
- private int GetLeadingWhitespaceChars(ITextSnapshotLine line, SnapshotPoint startPosition)
+ private static int GetLeadingWhitespaceChars(ITextSnapshotLine line, SnapshotPoint startPosition)
{
int whitespace = 0;
for (int i = startPosition.Position; i < line.End; ++i)
@@ -4390,7 +4596,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
SnapshotSpan currentWhiteSpace = new SnapshotSpan(line.Start, firstNonWhitespaceCharacter.AdvancedTextPoint);
- if (whitespace != currentWhiteSpace.GetText())
+ if (!string.Equals(whitespace, currentWhiteSpace.GetText(), StringComparison.Ordinal))
{
if (!textEdit.Replace(currentWhiteSpace, whitespace))
return false;
@@ -4572,7 +4778,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
if (line.LineBreakLength != 0)
{
string breakText = line.GetLineBreakText();
- if (breakText != replacement)
+ if (!string.Equals(breakText, replacement, StringComparison.Ordinal))
{
if (!edit.Replace(line.End, line.LineBreakLength, replacement))
return false;
diff --git a/src/Text/Impl/EditorOperations/EditorOperationsFactoryService.cs b/src/Text/Impl/EditorOperations/EditorOperationsFactoryService.cs
index 7b1d693..a61fee1 100644
--- a/src/Text/Impl/EditorOperations/EditorOperationsFactoryService.cs
+++ b/src/Text/Impl/EditorOperations/EditorOperationsFactoryService.cs
@@ -22,6 +22,9 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
internal sealed class EditorOperationsFactoryService : IEditorOperationsFactoryService
{
[Import]
+ public IMultiSelectionBrokerFactory MultiSelectionBrokerFactory { get; set; }
+
+ [Import]
internal ITextStructureNavigatorSelectorService TextStructureNavigatorFactory { get; set; }
#if WINDOWS
@@ -76,7 +79,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
// Validate
if (textView == null)
{
- throw new ArgumentNullException("textView");
+ throw new ArgumentNullException(nameof(textView));
}
// Only one EditorOperations should be created per ITextView
diff --git a/src/Text/Impl/EditorOperations/Strings.Designer.cs b/src/Text/Impl/EditorOperations/Strings.Designer.cs
index ffebf8e..fd4a317 100644
--- a/src/Text/Impl/EditorOperations/Strings.Designer.cs
+++ b/src/Text/Impl/EditorOperations/Strings.Designer.cs
@@ -241,15 +241,6 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation {
}
/// <summary>
- /// Looks up a localized string similar to Expand/Contract Selection Command Handler.
- /// </summary>
- internal static string ExpandContractSelectionCommandHandlerName {
- get {
- return ResourceManager.GetString("ExpandContractSelectionCommandHandlerName", resourceCulture);
- }
- }
-
- /// <summary>
/// Looks up a localized string similar to Increase line indent.
/// </summary>
internal static string IncreaseLineIndent {
@@ -349,6 +340,15 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation {
}
/// <summary>
+ /// Looks up a localized string similar to Next/Previous Issue.
+ /// </summary>
+ internal static string NextIssue {
+ get {
+ return ResourceManager.GetString("NextIssue", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to Make Line Endings Consistent.
/// </summary>
internal static string NormalizeLineEndings {
diff --git a/src/Text/Impl/EditorOperations/Strings.resx b/src/Text/Impl/EditorOperations/Strings.resx
index 4792155..b53056d 100644
--- a/src/Text/Impl/EditorOperations/Strings.resx
+++ b/src/Text/Impl/EditorOperations/Strings.resx
@@ -261,10 +261,10 @@
<data name="DuplicateSelection" xml:space="preserve">
<value>Duplicate Selection</value>
</data>
- <data name="ExpandContractSelectionCommandHandlerName" xml:space="preserve">
- <value>Expand/Contract Selection Command Handler</value>
- </data>
<data name="DuplicateSelectionCommandHandlerName" xml:space="preserve">
<value>Duplicate Selection Command Handler</value>
</data>
+ <data name="NextIssue" xml:space="preserve">
+ <value>Next/Previous Issue</value>
+ </data>
</root> \ No newline at end of file
diff --git a/src/Text/Impl/EditorOperations/TextTransactionMergePolicy.cs b/src/Text/Impl/EditorOperations/TextTransactionMergePolicy.cs
index d913c0d..9c8086a 100644
--- a/src/Text/Impl/EditorOperations/TextTransactionMergePolicy.cs
+++ b/src/Text/Impl/EditorOperations/TextTransactionMergePolicy.cs
@@ -48,12 +48,12 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
// Validate
if (newTransaction == null)
{
- throw new ArgumentNullException("newTransaction");
+ throw new ArgumentNullException(nameof(newTransaction));
}
if (oldTransaction == null)
{
- throw new ArgumentNullException("oldTransaction");
+ throw new ArgumentNullException(nameof(oldTransaction));
}
TextTransactionMergePolicy oldPolicy = oldTransaction.MergePolicy as TextTransactionMergePolicy;
@@ -71,7 +71,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
}
// Only merge text transactions that have the same description
- if (newTransaction.Description != oldTransaction.Description)
+ if (!string.Equals(newTransaction.Description, oldTransaction.Description, StringComparison.Ordinal))
{
return false;
}
@@ -92,9 +92,9 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
public void PerformTransactionMerge(ITextUndoTransaction existingTransaction, ITextUndoTransaction newTransaction)
{
if (existingTransaction == null)
- throw new ArgumentNullException("existingTransaction");
+ throw new ArgumentNullException(nameof(existingTransaction));
if (newTransaction == null)
- throw new ArgumentNullException("newTransaction");
+ throw new ArgumentNullException(nameof(newTransaction));
// Remove trailing AfterTextBufferChangeUndoPrimitive from previous transaction and skip copying
// initial BeforeTextBufferChangeUndoPrimitive from newTransaction, as they are unnecessary.
@@ -128,7 +128,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
if (other == null)
{
- throw new ArgumentNullException("other");
+ throw new ArgumentNullException(nameof(other));
}
// Only merge transaction if they are both a text transaction
diff --git a/src/Text/Impl/EditorOptions/EditorOptions.cs b/src/Text/Impl/EditorOptions/EditorOptions.cs
index 59a6e55..cb97410 100644
--- a/src/Text/Impl/EditorOptions/EditorOptions.cs
+++ b/src/Text/Impl/EditorOptions/EditorOptions.cs
@@ -10,6 +10,7 @@ namespace Microsoft.VisualStudio.Text.EditorOptions.Implementation
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
+ using System.Globalization;
using System.Linq;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Utilities;
@@ -60,7 +61,7 @@ namespace Microsoft.VisualStudio.Text.EditorOptions.Implementation
throw new InvalidOperationException("Cannot change the Parent of the global options.");
if (value == null)
- throw new ArgumentNullException("value");
+ throw new ArgumentNullException(nameof(value));
if (value == this)
throw new ArgumentException("The Parent of this instance of IEditorOptions cannot be set to itself.");
@@ -124,7 +125,7 @@ namespace Microsoft.VisualStudio.Text.EditorOptions.Implementation
object value;
if (!TryGetOption(definition, out value))
- throw new ArgumentException(string.Format("The specified option is not valid in this scope: {0}", definition.Name));
+ throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "The specified option is not valid in this scope: {0}", definition.Name));
return value;
}
@@ -136,12 +137,12 @@ namespace Microsoft.VisualStudio.Text.EditorOptions.Implementation
// Make sure the type of the provided value is correct
if (!definition.ValueType.IsAssignableFrom(value.GetType()))
{
- throw new ArgumentException("Specified option value is of an invalid type", "value");
+ throw new ArgumentException("Specified option value is of an invalid type", nameof(value));
}
// Make sure the option is valid, also
else if(!definition.IsValid(ref value))
{
- throw new ArgumentException("The supplied value failed validation for the option.", "value");
+ throw new ArgumentException("The supplied value failed validation for the option.", nameof(value));
}
// Finally, set the option value locally
else
diff --git a/src/Text/Impl/EditorOptions/EditorOptionsFactoryService.cs b/src/Text/Impl/EditorOptions/EditorOptionsFactoryService.cs
index 733450b..a7f2686 100644
--- a/src/Text/Impl/EditorOptions/EditorOptionsFactoryService.cs
+++ b/src/Text/Impl/EditorOptions/EditorOptionsFactoryService.cs
@@ -35,7 +35,7 @@ namespace Microsoft.VisualStudio.Text.EditorOptions.Implementation
public IEditorOptions GetOptions(IPropertyOwner scope)
{
if (scope == null)
- throw new ArgumentNullException("scope");
+ throw new ArgumentNullException(nameof(scope));
return scope.Properties.GetOrCreateSingletonProperty<IEditorOptions>(() => new EditorOptions(this.GlobalOptions as EditorOptions, scope, this));
}
@@ -133,7 +133,7 @@ namespace Microsoft.VisualStudio.Text.EditorOptions.Implementation
{
var definition = this.GetOptionDefinition(optionId);
if (definition == null)
- throw new ArgumentException(string.Format("No EditorOptionDefinition export found for the given option name: {0}", optionId), "optionId");
+ throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "No EditorOptionDefinition export found for the given option name: {0}", optionId), nameof(optionId));
return definition;
}
diff --git a/src/Text/Impl/EditorPrimitives/DefaultBufferPrimitive.cs b/src/Text/Impl/EditorPrimitives/DefaultBufferPrimitive.cs
index 19923d5..9e383d9 100644
--- a/src/Text/Impl/EditorPrimitives/DefaultBufferPrimitive.cs
+++ b/src/Text/Impl/EditorPrimitives/DefaultBufferPrimitive.cs
@@ -30,7 +30,7 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
{
if ((position < 0) || (position > _textBuffer.CurrentSnapshot.Length))
{
- throw new ArgumentOutOfRangeException("position");
+ throw new ArgumentOutOfRangeException(nameof(position));
}
return _bufferPrimitivesFactory.CreateTextPoint(this, position);
}
@@ -39,14 +39,14 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
{
if ((line < 0) || (line > _textBuffer.CurrentSnapshot.LineCount))
{
- throw new ArgumentOutOfRangeException("line");
+ throw new ArgumentOutOfRangeException(nameof(line));
}
ITextSnapshotLine snapshotLine = _textBuffer.CurrentSnapshot.GetLineFromLineNumber(line);
if ((column < 0) || (column > snapshotLine.Length))
{
- throw new ArgumentOutOfRangeException("column");
+ throw new ArgumentOutOfRangeException(nameof(column));
}
return _bufferPrimitivesFactory.CreateTextPoint(this, snapshotLine.Start + column);
}
@@ -55,7 +55,7 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
{
if ((line < 0) || (line > _textBuffer.CurrentSnapshot.LineCount))
{
- throw new ArgumentOutOfRangeException("line");
+ throw new ArgumentOutOfRangeException(nameof(line));
}
ITextSnapshotLine snapshotLine = _textBuffer.CurrentSnapshot.GetLineFromLineNumber(line);
@@ -67,11 +67,11 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
{
if (startPoint == null)
{
- throw new ArgumentNullException("startPoint");
+ throw new ArgumentNullException(nameof(startPoint));
}
if (endPoint == null)
{
- throw new ArgumentNullException("endPoint");
+ throw new ArgumentNullException(nameof(endPoint));
}
if (!object.ReferenceEquals(startPoint.TextBuffer, this))
@@ -91,12 +91,12 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
{
if ((startPosition < 0) || (startPosition > _textBuffer.CurrentSnapshot.Length))
{
- throw new ArgumentOutOfRangeException("startPosition");
+ throw new ArgumentOutOfRangeException(nameof(startPosition));
}
if ((endPosition < 0) || (endPosition > _textBuffer.CurrentSnapshot.Length))
{
- throw new ArgumentOutOfRangeException("endPosition");
+ throw new ArgumentOutOfRangeException(nameof(endPosition));
}
TextPoint startPoint = GetTextPoint(startPosition);
diff --git a/src/Text/Impl/EditorPrimitives/DefaultDisplayTextPointPrimitive.cs b/src/Text/Impl/EditorPrimitives/DefaultDisplayTextPointPrimitive.cs
index 0b64bc4..8ca865a 100644
--- a/src/Text/Impl/EditorPrimitives/DefaultDisplayTextPointPrimitive.cs
+++ b/src/Text/Impl/EditorPrimitives/DefaultDisplayTextPointPrimitive.cs
@@ -82,7 +82,7 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
{
if (!object.ReferenceEquals(this.TextBuffer, otherPoint.TextBuffer))
{
- throw new ArgumentException("The other point must have the same TextBuffer as this one", "otherPoint");
+ throw new ArgumentException("The other point must have the same TextBuffer as this one", nameof(otherPoint));
}
return TextView.GetTextRange(this, otherPoint);
}
@@ -184,7 +184,7 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
{
if (text == null)
{
- throw new ArgumentNullException("text");
+ throw new ArgumentNullException(nameof(text));
}
return _bufferPoint.InsertText(text);
diff --git a/src/Text/Impl/EditorPrimitives/DefaultSelectionPrimitive.cs b/src/Text/Impl/EditorPrimitives/DefaultSelectionPrimitive.cs
index 35231be..d9ce555 100644
--- a/src/Text/Impl/EditorPrimitives/DefaultSelectionPrimitive.cs
+++ b/src/Text/Impl/EditorPrimitives/DefaultSelectionPrimitive.cs
@@ -18,7 +18,7 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
using Microsoft.VisualStudio.Text.Editor.OptionsExtensionMethods;
- internal sealed class DefaultSelectionPrimitive : Selection
+ internal sealed class DefaultSelectionPrimitive : Text.Editor.LegacySelection
{
private TextView _textView;
private IEditorOptions _editorOptions;
diff --git a/src/Text/Impl/EditorPrimitives/DefaultTextPointPrimitive.cs b/src/Text/Impl/EditorPrimitives/DefaultTextPointPrimitive.cs
index be6d268..69088f7 100644
--- a/src/Text/Impl/EditorPrimitives/DefaultTextPointPrimitive.cs
+++ b/src/Text/Impl/EditorPrimitives/DefaultTextPointPrimitive.cs
@@ -37,7 +37,7 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
if ((position < 0) ||
(position > textBuffer.AdvancedTextBuffer.CurrentSnapshot.Length))
{
- throw new ArgumentOutOfRangeException("position");
+ throw new ArgumentOutOfRangeException(nameof(position));
}
_textBuffer = textBuffer;
@@ -78,7 +78,7 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
for (int i = 0; i < lineTextInfo.LengthInTextElements; i++)
{
string textElement = lineTextInfo.SubstringByTextElements(i, 1);
- if (textElement == "\t")
+ if (string.Equals(textElement, "\t", StringComparison.Ordinal))
{
// If there is a tab in the text, then the column automatically jumps
// to the next tab stop.
@@ -291,7 +291,7 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
{
if (otherPoint == null)
{
- throw new ArgumentNullException("otherPoint");
+ throw new ArgumentNullException(nameof(otherPoint));
}
if (otherPoint.TextBuffer != TextBuffer)
@@ -306,7 +306,7 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
{
if ((otherPosition < 0) || (otherPosition > TextBuffer.AdvancedTextBuffer.CurrentSnapshot.Length))
{
- throw new ArgumentOutOfRangeException("otherPosition");
+ throw new ArgumentOutOfRangeException(nameof(otherPosition));
}
TextPoint otherPoint = this.Clone();
@@ -354,7 +354,7 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
{
if (text == null)
{
- throw new ArgumentNullException("text");
+ throw new ArgumentNullException(nameof(text));
}
if (text.Length > 0)
@@ -410,7 +410,7 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
{
newPoint.MoveTo(i);
string character = newPoint.GetNextCharacter();
- if (character != " " && character != "\t")
+ if (!string.Equals(character, " ", StringComparison.Ordinal) && !string.Equals(character, "\t", StringComparison.Ordinal))
{
break;
}
@@ -510,7 +510,7 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
{
if ((lineNumber < 0) || (lineNumber > _textBuffer.AdvancedTextBuffer.CurrentSnapshot.LineCount))
{
- throw new ArgumentOutOfRangeException("lineNumber");
+ throw new ArgumentOutOfRangeException(nameof(lineNumber));
}
ITextSnapshot currentSnapshot = _textBuffer.AdvancedTextBuffer.CurrentSnapshot;
@@ -737,11 +737,11 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
{
if (pattern == null)
{
- throw new ArgumentNullException("pattern");
+ throw new ArgumentNullException(nameof(pattern));
}
if (endPoint == null)
{
- throw new ArgumentNullException("endPoint");
+ throw new ArgumentNullException(nameof(endPoint));
}
if (endPoint.TextBuffer != TextBuffer)
{
@@ -769,7 +769,7 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
if ((position < 0) ||
(position > snapshot.Length))
{
- throw new ArgumentOutOfRangeException("position");
+ throw new ArgumentOutOfRangeException(nameof(position));
}
// If this is the end of the snapshot, we don't need to check anything.
@@ -872,7 +872,7 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
if ((lineNumber < 0) ||
(lineNumber > _textBuffer.AdvancedTextBuffer.CurrentSnapshot.LineCount))
{
- throw new ArgumentOutOfRangeException("lineNumber");
+ throw new ArgumentOutOfRangeException(nameof(lineNumber));
}
ITextSnapshotLine line = _textBuffer.AdvancedTextBuffer.CurrentSnapshot.GetLineFromLineNumber(lineNumber);
diff --git a/src/Text/Impl/EditorPrimitives/DefaultTextRangePrimitive.cs b/src/Text/Impl/EditorPrimitives/DefaultTextRangePrimitive.cs
index 961db4f..e0abb1b 100644
--- a/src/Text/Impl/EditorPrimitives/DefaultTextRangePrimitive.cs
+++ b/src/Text/Impl/EditorPrimitives/DefaultTextRangePrimitive.cs
@@ -167,7 +167,7 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
newChar = char.ToUpper(newChar, CultureInfo.CurrentCulture);
}
- if (!textEdit.Replace(i, 1, newChar.ToString()))
+ if (!textEdit.Replace(i, 1, newChar.ToString(CultureInfo.CurrentCulture)))
{
textEdit.Cancel();
return false; // break out early if any edit fails to reduce the time of the failure case
@@ -296,7 +296,7 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
{
if (string.IsNullOrEmpty(newText))
{
- throw new ArgumentNullException("newText");
+ throw new ArgumentNullException(nameof(newText));
}
int startPoint = _startPoint.CurrentPosition;
diff --git a/src/Text/Impl/EditorPrimitives/DefaultTextViewPrimitive.cs b/src/Text/Impl/EditorPrimitives/DefaultTextViewPrimitive.cs
index 80715de..4dca517 100644
--- a/src/Text/Impl/EditorPrimitives/DefaultTextViewPrimitive.cs
+++ b/src/Text/Impl/EditorPrimitives/DefaultTextViewPrimitive.cs
@@ -10,12 +10,13 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Formatting;
+ using LegacySelection = Microsoft.VisualStudio.Text.Editor.LegacySelection;
internal sealed class DefaultTextViewPrimitive : TextView
{
private ITextView _textView;
private Caret _caret;
- private Selection _selection;
+ private LegacySelection _selection;
private TextBuffer _textBuffer;
private IViewPrimitivesFactoryService _viewPrimitivesFactory;
@@ -144,7 +145,7 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
get { return _caret; }
}
- public override Selection Selection
+ public override LegacySelection Selection
{
get { return _selection; }
}
diff --git a/src/Text/Impl/EditorPrimitives/DefaultViewPrimitivesFactoryService.cs b/src/Text/Impl/EditorPrimitives/DefaultViewPrimitivesFactoryService.cs
index 09e9d25..99400b7 100644
--- a/src/Text/Impl/EditorPrimitives/DefaultViewPrimitivesFactoryService.cs
+++ b/src/Text/Impl/EditorPrimitives/DefaultViewPrimitivesFactoryService.cs
@@ -44,7 +44,7 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
return new DefaultDisplayTextRangePrimitive(textView, textRange);
}
- public Selection CreateSelection(TextView textView)
+ public LegacySelection CreateSelection(TextView textView)
{
if (textView.Selection == null)
{
diff --git a/src/Text/Impl/EditorPrimitives/ViewPrimitives.cs b/src/Text/Impl/EditorPrimitives/ViewPrimitives.cs
index 9b2b1dc..081fdb4 100644
--- a/src/Text/Impl/EditorPrimitives/ViewPrimitives.cs
+++ b/src/Text/Impl/EditorPrimitives/ViewPrimitives.cs
@@ -12,7 +12,7 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
internal sealed class ViewPrimitives : IViewPrimitives
{
private TextView _textView;
- private Selection _selection;
+ private LegacySelection _selection;
private Caret _caret;
private TextBuffer _textBuffer;
@@ -32,7 +32,7 @@ namespace Microsoft.VisualStudio.Text.EditorPrimitives.Implementation
get { return _textView; }
}
- public Selection Selection
+ public LegacySelection Selection
{
get { return _selection; }
}
diff --git a/src/Text/Impl/Navigation/TextStructureNavigatorSelectorService.cs b/src/Text/Impl/Navigation/TextStructureNavigatorSelectorService.cs
index e59bc8d..ae08b4f 100644
--- a/src/Text/Impl/Navigation/TextStructureNavigatorSelectorService.cs
+++ b/src/Text/Impl/Navigation/TextStructureNavigatorSelectorService.cs
@@ -32,7 +32,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
if (textBuffer == null)
{
- throw new ArgumentNullException("textBuffer");
+ throw new ArgumentNullException(nameof(textBuffer));
}
ITextStructureNavigator navigator = null;
@@ -55,11 +55,11 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
if (textBuffer == null)
{
- throw new ArgumentNullException("textBuffer");
+ throw new ArgumentNullException(nameof(textBuffer));
}
if (contentType == null)
{
- throw new ArgumentNullException("contentType");
+ throw new ArgumentNullException(nameof(contentType));
}
return CreateNavigator(textBuffer, contentType);
}
diff --git a/src/Text/Impl/Outlining/OutliningManager.cs b/src/Text/Impl/Outlining/OutliningManager.cs
index 71418ec..28decbe 100644
--- a/src/Text/Impl/Outlining/OutliningManager.cs
+++ b/src/Text/Impl/Outlining/OutliningManager.cs
@@ -244,7 +244,7 @@ namespace Microsoft.VisualStudio.Text.Outlining
if (internalCollapsed == null)
{
throw new ArgumentException("The given collapsed region was not created by this outlining manager.",
- "collapsed");
+ nameof(collapsed));
}
if (!internalCollapsed.IsValid)
@@ -272,7 +272,7 @@ namespace Microsoft.VisualStudio.Text.Outlining
internal IEnumerable<ICollapsed> InternalCollapseAll(SnapshotSpan span, Predicate<ICollapsible> match, CancellationToken? cancel)
{
if (match == null)
- throw new ArgumentNullException("match");
+ throw new ArgumentNullException(nameof(match));
EnsureValid(span);
@@ -312,7 +312,7 @@ namespace Microsoft.VisualStudio.Text.Outlining
public IEnumerable<ICollapsible> ExpandAllInternal(bool removalPending, SnapshotSpan span, Predicate<ICollapsed> match)
{
if (match == null)
- throw new ArgumentNullException("match");
+ throw new ArgumentNullException(nameof(match));
EnsureValid(span);
@@ -676,19 +676,19 @@ namespace Microsoft.VisualStudio.Text.Outlining
if (spans == null)
{
- throw new ArgumentNullException("spans");
+ throw new ArgumentNullException(nameof(spans));
}
if (spans.Count == 0)
{
- throw new ArgumentException("The given span collection is empty.", "spans");
+ throw new ArgumentException("The given span collection is empty.", nameof(spans));
}
if (spans[0].Snapshot.TextBuffer != this.editBuffer)
{
throw new ArgumentException("The given span collection is on an invalid buffer." +
"Spans must be generated against the view model's edit buffer",
- "spans");
+ nameof(spans));
}
}
@@ -705,7 +705,7 @@ namespace Microsoft.VisualStudio.Text.Outlining
{
throw new ArgumentException("The given span is on an invalid buffer." +
"Spans must be generated against the view model's edit buffer",
- "span");
+ nameof(span));
}
}
@@ -725,9 +725,9 @@ namespace Microsoft.VisualStudio.Text.Outlining
public int Compare(ICollapsible x, ICollapsible y)
{
if (x == null)
- throw new ArgumentNullException("x");
+ throw new ArgumentNullException(nameof(x));
if (y == null)
- throw new ArgumentNullException("y");
+ throw new ArgumentNullException(nameof(y));
ITextSnapshot current = SourceBuffer.CurrentSnapshot;
SnapshotSpan left = x.Extent.GetSpan(current);
diff --git a/src/Text/Impl/Outlining/OutliningManagerService.cs b/src/Text/Impl/Outlining/OutliningManagerService.cs
index 5e8a795..081672e 100644
--- a/src/Text/Impl/Outlining/OutliningManagerService.cs
+++ b/src/Text/Impl/Outlining/OutliningManagerService.cs
@@ -28,7 +28,7 @@ namespace Microsoft.VisualStudio.Text.Outlining
public IOutliningManager GetOutliningManager(ITextView textView)
{
if (textView == null)
- throw new ArgumentNullException("textView");
+ throw new ArgumentNullException(nameof(textView));
if (!textView.Roles.Contains(PredefinedTextViewRoles.Structured))
return null;
diff --git a/src/Text/Impl/PatternMatching/AllLowerCamelCaseMatcher.cs b/src/Text/Impl/PatternMatching/AllLowerCamelCaseMatcher.cs
index adfacc8..6106223 100644
--- a/src/Text/Impl/PatternMatching/AllLowerCamelCaseMatcher.cs
+++ b/src/Text/Impl/PatternMatching/AllLowerCamelCaseMatcher.cs
@@ -1,9 +1,8 @@
// 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.Collections.Generic;
using System.Collections.Immutable;
-using Microsoft.VisualStudio.Text;
+using System.Globalization;
using Microsoft.VisualStudio.Text.Utilities;
using TextSpan = Microsoft.VisualStudio.Text.Span;
@@ -66,8 +65,8 @@ namespace Microsoft.VisualStudio.Text.PatternMatching.Implementation
return GetKind(result.Value);
}
- private PatternMatchKind GetKind(CamelCaseResult result)
- => PatternMatcher.GetCamelCaseKind(result, _candidateHumps);
+ private static PatternMatchKind GetKind(CamelCaseResult result)
+ => PatternMatcher.GetCamelCaseKind(result);
private CamelCaseResult? TryMatch(
int patternIndex, int candidateHumpIndex, bool? contiguous, int chunkOffset)
@@ -99,7 +98,7 @@ namespace Microsoft.VisualStudio.Text.PatternMatching.Implementation
}
var candidateHump = _candidateHumps[humpIndex];
- if (char.ToLower(_candidate[candidateHump.Start]) == patternCharacter)
+ if (char.ToLower(_candidate[candidateHump.Start], CultureInfo.CurrentCulture) == patternCharacter)
{
// Found a hump in the candidate string that matches the current pattern
// character we're on. i.e. we matched the c in cofipro against the C in
@@ -204,7 +203,7 @@ namespace Microsoft.VisualStudio.Text.PatternMatching.Implementation
/// If 'weight' is better than 'bestWeight' and matchSpanToAdd is not null, then
/// matchSpanToAdd will be added to matchedSpansInReverse.
/// </summary>
- private bool UpdateBestResultIfBetter(
+ private static bool UpdateBestResultIfBetter(
CamelCaseResult result, ref CamelCaseResult? bestResult, TextSpan? matchSpanToAdd)
{
if (matchSpanToAdd != null)
@@ -234,7 +233,7 @@ namespace Microsoft.VisualStudio.Text.PatternMatching.Implementation
return GetKind(result) == PatternMatchKind.CamelCaseExact;
}
- private bool IsBetter(CamelCaseResult result, CamelCaseResult? currentBestResult)
+ private static bool IsBetter(CamelCaseResult result, CamelCaseResult? currentBestResult)
{
if (currentBestResult == null)
{
@@ -245,12 +244,12 @@ namespace Microsoft.VisualStudio.Text.PatternMatching.Implementation
return GetKind(result) < GetKind(currentBestResult.Value);
}
- private bool LowercaseSubstringsMatch(
+ private static bool LowercaseSubstringsMatch(
string s1, int start1, string s2, int start2, int length)
{
for (var i = 0; i < length; i++)
{
- if (char.ToLower(s1[start1 + i]) != char.ToLower(s2[start2 + i]))
+ if (char.ToLower(s1[start1 + i], CultureInfo.CurrentCulture) != char.ToLower(s2[start2 + i], CultureInfo.CurrentCulture))
{
return false;
}
diff --git a/src/Text/Impl/PatternMatching/ArraySlice.cs b/src/Text/Impl/PatternMatching/ArraySlice.cs
index 6e7455e..1da2b03 100644
--- a/src/Text/Impl/PatternMatching/ArraySlice.cs
+++ b/src/Text/Impl/PatternMatching/ArraySlice.cs
@@ -41,12 +41,12 @@ namespace Microsoft.VisualStudio.Text.PatternMatching.Implementation
{
if (start < 0)
{
- throw new ArgumentException(nameof(start), $"{start} < {0}");
+ throw new ArgumentException($"{start} < {0}", nameof(start));
}
if (start > _array.Length)
{
- throw new ArgumentException(nameof(start), $"{start} > {_array.Length}");
+ throw new ArgumentException($"{start} > {_array.Length}", nameof(start));
}
CheckLength(start, length);
@@ -59,12 +59,12 @@ namespace Microsoft.VisualStudio.Text.PatternMatching.Implementation
{
if (length < 0)
{
- throw new ArgumentException(nameof(length), $"{length} < {0}");
+ throw new ArgumentException($"{length} < {0}", nameof(length));
}
if (start + length > _array.Length)
{
- throw new ArgumentException(nameof(start), $"{start} + {length} > {_array.Length}");
+ throw new ArgumentException($"{start} + {length} > {_array.Length}", nameof(start));
}
}
diff --git a/src/Text/Impl/PatternMatching/CamelCaseResult.cs b/src/Text/Impl/PatternMatching/CamelCaseResult.cs
index f67572b..f11c61b 100644
--- a/src/Text/Impl/PatternMatching/CamelCaseResult.cs
+++ b/src/Text/Impl/PatternMatching/CamelCaseResult.cs
@@ -47,7 +47,7 @@ namespace Microsoft.VisualStudio.Text.PatternMatching.Implementation
}
}
- private static PatternMatchKind GetCamelCaseKind(CamelCaseResult result, StringBreaks candidateHumps)
+ private static PatternMatchKind GetCamelCaseKind(CamelCaseResult result)
{
/* CamelCase PatternMatchKind truth table:
* | FromStart | ToEnd | Contiguous || PatternMatchKind |
diff --git a/src/Text/Impl/PatternMatching/ContainerPatternMatcher.cs b/src/Text/Impl/PatternMatching/ContainerPatternMatcher.cs
index 6048e0f..55aa996 100644
--- a/src/Text/Impl/PatternMatching/ContainerPatternMatcher.cs
+++ b/src/Text/Impl/PatternMatching/ContainerPatternMatcher.cs
@@ -43,6 +43,7 @@ namespace Microsoft.VisualStudio.Text.PatternMatching.Implementation
_invalidPattern = _patternSegments.Length == 0 || _patternSegments.Any(s => s.IsInvalid);
}
+#pragma warning disable CA1063
public override void Dispose()
{
base.Dispose();
@@ -52,6 +53,7 @@ namespace Microsoft.VisualStudio.Text.PatternMatching.Implementation
segment.Dispose();
}
}
+#pragma warning restore CA1063
public override PatternMatch? TryMatch(string candidate)
{
diff --git a/src/Text/Impl/PatternMatching/EditDistance.cs b/src/Text/Impl/PatternMatching/EditDistance.cs
index 5eb25d2..d350be5 100644
--- a/src/Text/Impl/PatternMatching/EditDistance.cs
+++ b/src/Text/Impl/PatternMatching/EditDistance.cs
@@ -3,11 +3,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Globalization;
using System.Text;
using System.Threading;
namespace Microsoft.VisualStudio.Text.PatternMatching.Implementation
{
+#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
///<summary>
/// NOTE: Only use if you truly need an edit distance.
///
@@ -24,7 +26,7 @@ namespace Microsoft.VisualStudio.Text.PatternMatching.Implementation
/// Specifically, this implementation satisfies the following inequality: D(x, y) + D(y, z) >= D(x, z)
/// (where D is the edit distance).
///</summary>
- internal class EditDistance : IDisposable
+ internal sealed class EditDistance : IDisposable
{
// Our edit distance algorithm makes use of an 'infinite' value. A value so high that it
// could never participate in an edit distance (and effectively means the path through it
@@ -556,7 +558,7 @@ namespace Microsoft.VisualStudio.Text.PatternMatching.Implementation
for (var i = 0; i < width; i++)
{
var v = matrix[i + 2, j + 2];
- sb.Append((v == Infinity ? "∞" : v.ToString()) + " ");
+ sb.Append((v == Infinity ? "∞" : v.ToString(CultureInfo.CurrentCulture)) + " ");
}
sb.AppendLine();
}
@@ -666,4 +668,5 @@ namespace Microsoft.VisualStudio.Text.PatternMatching.Implementation
}
}
}
+#pragma warning restore CA1814 // Prefer jagged arrays over multidimensional
}
diff --git a/src/Text/Impl/PatternMatching/PatternMatcher.cs b/src/Text/Impl/PatternMatching/PatternMatcher.cs
index f781db3..793a632 100644
--- a/src/Text/Impl/PatternMatching/PatternMatcher.cs
+++ b/src/Text/Impl/PatternMatching/PatternMatcher.cs
@@ -58,7 +58,9 @@ namespace Microsoft.VisualStudio.Text.PatternMatching.Implementation
_allowSimpleSubstringMatching = allowSimpleSubstringMatching;
}
+#pragma warning disable CA1063
public virtual void Dispose()
+#pragma warning restore CA1063
{
foreach (var kvp in _stringToWordSpans)
{
@@ -138,7 +140,7 @@ namespace Microsoft.VisualStudio.Text.PatternMatching.Implementation
: NonFuzzyMatchPatternChunk(candidate, patternChunk, punctuationStripped, chunkOffset);
}
- private PatternMatch? FuzzyMatchPatternChunk(
+ private static PatternMatch? FuzzyMatchPatternChunk(
string candidate,
TextChunk patternChunk,
bool punctuationStripped)
@@ -167,7 +169,7 @@ namespace Microsoft.VisualStudio.Text.PatternMatching.Implementation
// a) Check if the part matches the candidate entirely, in an case insensitive or
// sensitive manner. If it does, return that there was an exact match.
return new PatternMatch(
- PatternMatchKind.Exact, punctuationStripped, isCaseSensitive: candidate == patternChunk.Text,
+ PatternMatchKind.Exact, punctuationStripped, isCaseSensitive: string.Equals(candidate, patternChunk.Text, StringComparison.Ordinal),
matchedSpans: GetMatchedSpans(chunkOffset, candidate.Length));
}
else
@@ -541,7 +543,7 @@ namespace Microsoft.VisualStudio.Text.PatternMatching.Implementation
matchedSpansInReverse: null,
chunkOffset: chunkOffset
);
- return GetCamelCaseKind(camelCaseResult, candidateHumps);
+ return GetCamelCaseKind(camelCaseResult);
}
else if (currentCandidateHump == candidateHumpCount)
{
diff --git a/src/Text/Impl/PatternMatching/WordSimilarityChecker.cs b/src/Text/Impl/PatternMatching/WordSimilarityChecker.cs
index d049e6b..d2ad8b6 100644
--- a/src/Text/Impl/PatternMatching/WordSimilarityChecker.cs
+++ b/src/Text/Impl/PatternMatching/WordSimilarityChecker.cs
@@ -123,7 +123,7 @@ namespace Microsoft.VisualStudio.Text.PatternMatching.Implementation
return false;
}
- if (_lastAreSimilarResult.CandidateText == candidateText)
+ if (string.Equals(_lastAreSimilarResult.CandidateText, candidateText, StringComparison.Ordinal))
{
similarityWeight = _lastAreSimilarResult.SimilarityWeight;
return _lastAreSimilarResult.AreSimilar;
diff --git a/src/Text/Impl/StandaloneUndo/AutoEnclose.cs b/src/Text/Impl/StandaloneUndo/AutoEnclose.cs
index 42af7d4..ad92cd3 100644
--- a/src/Text/Impl/StandaloneUndo/AutoEnclose.cs
+++ b/src/Text/Impl/StandaloneUndo/AutoEnclose.cs
@@ -20,7 +20,9 @@ namespace Microsoft.VisualStudio.Text.Operations.Standalone
this.end = end;
}
+#pragma warning disable CA1063 // Implement IDisposable Correctly
public void Dispose()
+#pragma warning restore CA1063 // Implement IDisposable Correctly
{
if (end != null) end();
GC.SuppressFinalize(this);
diff --git a/src/Text/Impl/StandaloneUndo/CatchOperationsFromHistoryForDelegatedPrimitive.cs b/src/Text/Impl/StandaloneUndo/CatchOperationsFromHistoryForDelegatedPrimitive.cs
index b39446d..ace7773 100644
--- a/src/Text/Impl/StandaloneUndo/CatchOperationsFromHistoryForDelegatedPrimitive.cs
+++ b/src/Text/Impl/StandaloneUndo/CatchOperationsFromHistoryForDelegatedPrimitive.cs
@@ -28,7 +28,9 @@ namespace Microsoft.VisualStudio.Text.Operations.Standalone
history.ForwardToUndoOperation(primitive);
}
+#pragma warning disable CA1063 // Implement IDisposable Correctly
public void Dispose()
+#pragma warning restore CA1063 // Implement IDisposable Correctly
{
history.EndForwardToUndoOperation(primitive);
primitive.State = DelegatedUndoPrimitiveState.Inactive;
diff --git a/src/Text/Impl/StandaloneUndo/DelegatedUndoPrimitiveImpl.cs b/src/Text/Impl/StandaloneUndo/DelegatedUndoPrimitiveImpl.cs
index 87c83f9..3e678f9 100644
--- a/src/Text/Impl/StandaloneUndo/DelegatedUndoPrimitiveImpl.cs
+++ b/src/Text/Impl/StandaloneUndo/DelegatedUndoPrimitiveImpl.cs
@@ -110,10 +110,12 @@ namespace Microsoft.VisualStudio.Text.Operations.Standalone
}
}
+#pragma warning disable CA1822 // Mark members as static
public bool MergeWithPreviousOnly
{
get { return true; }
}
+#pragma warning restore CA1822 // Mark members as static
public bool CanMerge(ITextUndoPrimitive primitive)
{
@@ -125,4 +127,4 @@ namespace Microsoft.VisualStudio.Text.Operations.Standalone
throw new InvalidOperationException("Strings.DelegatedUndoPrimitiveCannotMerge");
}
}
-} \ No newline at end of file
+}
diff --git a/src/Text/Impl/StandaloneUndo/UndoHistoryImpl.cs b/src/Text/Impl/StandaloneUndo/UndoHistoryImpl.cs
index 9f5bac5..47fec01 100644
--- a/src/Text/Impl/StandaloneUndo/UndoHistoryImpl.cs
+++ b/src/Text/Impl/StandaloneUndo/UndoHistoryImpl.cs
@@ -8,13 +8,11 @@
using System;
using System.Collections.Generic;
using System.Globalization;
-using System.Collections.ObjectModel;
-using System.ComponentModel.Composition;
using Microsoft.VisualStudio.Utilities;
namespace Microsoft.VisualStudio.Text.Operations.Standalone
{
- internal class UndoHistoryImpl : ITextUndoHistory
+ internal class UndoHistoryImpl : ITextUndoHistory2
{
public event EventHandler<TextUndoRedoEventArgs> UndoRedoHappened;
public event EventHandler<TextUndoTransactionCompletedEventArgs> UndoTransactionCompleted;
@@ -25,7 +23,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Standalone
private Stack<ITextUndoTransaction> undoStack;
private Stack<ITextUndoTransaction> redoStack;
private DelegatedUndoPrimitiveImpl activeUndoOperationPrimitive;
- private TextUndoHistoryState state;
+ internal TextUndoHistoryState state;
private PropertyCollection properties;
#endregion
@@ -188,6 +186,13 @@ namespace Microsoft.VisualStudio.Text.Operations.Standalone
get { return this.state; }
}
+ public ITextUndoTransaction CreateInvisibleTransaction(string description)
+ {
+ // Standalone undo doesn't support invisible transactions so simply return
+ // a normal transaction.
+ return this.CreateTransaction(description);
+ }
+
/// <summary>
/// Creates a new transaction, nests it in the previously current transaction, and marks it current.
/// If there is a redo stack, it gets cleared.
@@ -198,9 +203,9 @@ namespace Microsoft.VisualStudio.Text.Operations.Standalone
/// <returns></returns>
public ITextUndoTransaction CreateTransaction(string description)
{
- if (String.IsNullOrEmpty(description))
+ if (string.IsNullOrEmpty(description))
{
- throw new ArgumentNullException("description", String.Format(CultureInfo.CurrentUICulture, "Strings.ArgumentCannotBeNull", "CreateTransaction", "description"));
+ throw new ArgumentNullException(nameof(description));
}
// If there is a pending transaction that has already been completed, we should not be permitted
@@ -244,12 +249,12 @@ namespace Microsoft.VisualStudio.Text.Operations.Standalone
{
if (count <= 0)
{
- throw new ArgumentException(String.Format(CultureInfo.CurrentUICulture, "Strings.RedoAndUndoAcceptOnlyPositiveCounts", "Undo", count), "count");
+ throw new ArgumentOutOfRangeException(nameof(count));
}
if (!IsThereEnoughVisibleTransactions(this.undoStack, count))
{
- throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, "Strings.CannotUndoMoreTransactionsThanExist", "undo", count));
+ throw new InvalidOperationException("Cannot undo more transactions than exist");
}
TextUndoHistoryState originalState = this.state;
@@ -321,12 +326,12 @@ namespace Microsoft.VisualStudio.Text.Operations.Standalone
{
if (count <= 0)
{
- throw new ArgumentException(String.Format(CultureInfo.CurrentUICulture, "Strings.RedoAndUndoAcceptOnlyPositiveCounts", "Redo", count), "count");
+ throw new ArgumentOutOfRangeException(nameof(count));
}
if (!IsThereEnoughVisibleTransactions(this.redoStack, count))
{
- throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, "Strings.CannotUndoMoreTransactionsThanExist", "redo", count));
+ throw new InvalidOperationException("Cannot redo more transactions than exist");
}
TextUndoHistoryState originalState = this.state;
@@ -424,15 +429,19 @@ namespace Microsoft.VisualStudio.Text.Operations.Standalone
throw new InvalidOperationException("Strings.EndTransactionOutOfOrder");
}
+ // Note that the VS undo history actually "pops" the nested undo stack on the Complete/Cancel
+ // (instead of in the Dispose). This shouldn't affect anything but we should consider adapting
+ // this code to follow the model in VS undo.
+ this.currentTransaction = (UndoTransactionImpl)(transaction.Parent);
+
// only add completed transactions to their parents (or the stack)
- if (this.currentTransaction.State == UndoTransactionState.Completed)
+ if (transaction.State == UndoTransactionState.Completed)
{
- if (this.currentTransaction.Parent == null) // stack bottomed out!
+ if (transaction.Parent == null) // stack bottomed out!
{
- MergeOrPushToUndoStack(this.currentTransaction);
+ MergeOrPushToUndoStack((UndoTransactionImpl)transaction);
}
}
- this.currentTransaction = this.currentTransaction.Parent as UndoTransactionImpl;
}
/// <summary>
@@ -471,7 +480,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Standalone
transactionAdded = transaction;
transactionResult = TextUndoTransactionCompletionResult.TransactionAdded;
}
- RaiseUndoTransactionCompleted(transactionAdded, transactionResult);
+ RaiseUndoTransactionCompleted(transactionAdded, transactionResult);
}
public bool ValidTransactionForMarkers(ITextUndoTransaction transaction)
diff --git a/src/Text/Impl/StandaloneUndo/UndoHistoryRegistryImpl.cs b/src/Text/Impl/StandaloneUndo/UndoHistoryRegistryImpl.cs
index 2e4742d..5aef378 100644
--- a/src/Text/Impl/StandaloneUndo/UndoHistoryRegistryImpl.cs
+++ b/src/Text/Impl/StandaloneUndo/UndoHistoryRegistryImpl.cs
@@ -18,7 +18,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Standalone
internal class UndoHistoryRegistryImpl : ITextUndoHistoryRegistry
{
#region Private Fields
- private Dictionary<ITextUndoHistory, int> histories;
+ internal Dictionary<ITextUndoHistory, int> histories;
private Dictionary<WeakReferenceForDictionaryKey, ITextUndoHistory> weakContextMapping;
private Dictionary<object, ITextUndoHistory> strongContextMapping;
#endregion // Private Fields
@@ -50,7 +50,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Standalone
{
if (context == null)
{
- throw new ArgumentNullException("context", String.Format(CultureInfo.CurrentCulture, "Strings.ArgumentCannotBeNull", "RegisterHistory", "context"));
+ throw new ArgumentNullException(nameof(context));
}
return RegisterHistory(context, false);
@@ -66,7 +66,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Standalone
{
if (context == null)
{
- throw new ArgumentNullException("context", String.Format(CultureInfo.CurrentCulture, "Strings.ArgumentCannotBeNull", "RegisterHistory", "context"));
+ throw new ArgumentNullException(nameof(context));
}
ITextUndoHistory result;
@@ -118,7 +118,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Standalone
{
if (context == null)
{
- throw new ArgumentNullException("context", String.Format(CultureInfo.CurrentCulture, "Strings.ArgumentCannotBeNull", "GetHistory", "context"));
+ throw new ArgumentNullException(nameof(context));
}
ITextUndoHistory result;
@@ -149,7 +149,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Standalone
{
if (context == null)
{
- throw new ArgumentNullException("context", String.Format(CultureInfo.CurrentCulture, "Strings.ArgumentCannotBeNull", "TryGetHistory", "context"));
+ throw new ArgumentNullException(nameof(context));
}
ITextUndoHistory result = null;
@@ -176,12 +176,12 @@ namespace Microsoft.VisualStudio.Text.Operations.Standalone
{
if (context == null)
{
- throw new ArgumentNullException("context", String.Format(CultureInfo.CurrentCulture, "Strings.ArgumentCannotBeNull", "AttachHistory", "context"));
+ throw new ArgumentNullException(nameof(context));
}
if (history == null)
{
- throw new ArgumentNullException("context", String.Format(CultureInfo.CurrentCulture, "Strings.ArgumentCannotBeNull", "AttachHistory", "history"));
+ throw new ArgumentNullException(nameof(history));
}
AttachHistory(context, history, false);
@@ -197,12 +197,12 @@ namespace Microsoft.VisualStudio.Text.Operations.Standalone
{
if (context == null)
{
- throw new ArgumentNullException("context", String.Format(CultureInfo.CurrentCulture, "Strings.ArgumentCannotBeNull", "AttachHistory", "context"));
+ throw new ArgumentNullException(nameof(context));
}
if (history == null)
{
- throw new ArgumentNullException("context", String.Format(CultureInfo.CurrentCulture, "Strings.ArgumentCannotBeNull", "AttachHistory", "history"));
+ throw new ArgumentNullException(nameof(history));
}
if (strongContextMapping.ContainsKey(context) || weakContextMapping.ContainsKey(new WeakReferenceForDictionaryKey(context)))
@@ -237,7 +237,7 @@ namespace Microsoft.VisualStudio.Text.Operations.Standalone
{
if (history == null)
{
- throw new ArgumentNullException("context", String.Format(CultureInfo.CurrentCulture, "Strings.ArgumentCannotBeNull", "RemoveHistory", "history"));
+ throw new ArgumentNullException(nameof(history));
}
if (!histories.ContainsKey(history))
diff --git a/src/Text/Impl/StandaloneUndo/UndoTransactionImpl.cs b/src/Text/Impl/StandaloneUndo/UndoTransactionImpl.cs
index aae1d06..7bc7db8 100644
--- a/src/Text/Impl/StandaloneUndo/UndoTransactionImpl.cs
+++ b/src/Text/Impl/StandaloneUndo/UndoTransactionImpl.cs
@@ -17,13 +17,14 @@ namespace Microsoft.VisualStudio.Text.Operations.Standalone
{
#region Private Fields
- private readonly UndoHistoryImpl history;
+ private UndoHistoryImpl history;
private readonly UndoTransactionImpl parent;
private string description;
private UndoTransactionState state;
private List<ITextUndoPrimitive> primitives;
private IMergeTextUndoTransactionPolicy mergePolicy;
+ internal bool _isDisposed = false;
#endregion
@@ -31,12 +32,12 @@ namespace Microsoft.VisualStudio.Text.Operations.Standalone
{
if (history == null)
{
- throw new ArgumentNullException("history", String.Format(CultureInfo.CurrentUICulture, "Strings.ArgumentCannotBeNull", "UndoTransactionImpl", "history"));
+ throw new ArgumentNullException(nameof(history));
}
- if (String.IsNullOrEmpty(description))
+ if (string.IsNullOrEmpty(description))
{
- throw new ArgumentNullException("description", String.Format(CultureInfo.CurrentUICulture, "Strings.ArgumentCannotBeNull", "UndoTransactionImpl", "description"));
+ throw new ArgumentNullException(nameof(description));
}
this.history = history as UndoHistoryImpl;
@@ -363,35 +364,44 @@ namespace Microsoft.VisualStudio.Text.Operations.Standalone
{
if (value == null)
{
- throw new ArgumentNullException("value");
+ throw new ArgumentNullException(nameof(value));
}
this.mergePolicy = value;
}
}
- /// <summary>
- /// Closes a transaction and disposes it.
- /// </summary>
+#pragma warning disable CA1063 // Implement IDisposable Correctly
+ /// <summary>
+ /// Closes a transaction and disposes it.
+ /// </summary>
public void Dispose()
+#pragma warning restore CA1063 // Implement IDisposable Correctly
{
- GC.SuppressFinalize(this);
- switch (this.State)
+ if (!_isDisposed)
{
- case UndoTransactionState.Open:
- Cancel();
- break;
+ _isDisposed = true;
- case UndoTransactionState.Canceled:
- case UndoTransactionState.Completed:
- break;
+ GC.SuppressFinalize(this);
+ switch (this.State)
+ {
+ case UndoTransactionState.Open:
+ Cancel();
+ break;
+
+ case UndoTransactionState.Canceled:
+ case UndoTransactionState.Completed:
+ break;
+
+ case UndoTransactionState.Redoing:
+ case UndoTransactionState.Undoing:
+ case UndoTransactionState.Undone:
+ throw new InvalidOperationException("Strings.ClosingAnOpenTransactionThatAppearsToBeUndoneOrUndoing");
+ }
- case UndoTransactionState.Redoing:
- case UndoTransactionState.Undoing:
- case UndoTransactionState.Undone:
- throw new InvalidOperationException("Strings.ClosingAnOpenTransactionThatAppearsToBeUndoneOrUndoing");
+ this.history.EndTransaction(this);
}
- history.EndTransaction(this);
}
+
}
}
diff --git a/src/Text/Impl/TagAggregator/TagAggregator.cs b/src/Text/Impl/TagAggregator/TagAggregator.cs
index 2f84741..90005ff 100644
--- a/src/Text/Impl/TagAggregator/TagAggregator.cs
+++ b/src/Text/Impl/TagAggregator/TagAggregator.cs
@@ -128,7 +128,7 @@ namespace Microsoft.VisualStudio.Text.Tagging.Implementation
public IEnumerable<IMappingTagSpan<T>> GetTags(IMappingSpan span)
{
if (span == null)
- throw new ArgumentNullException("span");
+ throw new ArgumentNullException(nameof(span));
if (this.disposed)
throw new ObjectDisposedException("TagAggregator");
@@ -186,7 +186,7 @@ namespace Microsoft.VisualStudio.Text.Tagging.Implementation
public IEnumerable<IMappingTagSpan<T>> GetAllTags(IMappingSpan span, CancellationToken cancel)
{
if (span == null)
- throw new ArgumentNullException("span");
+ throw new ArgumentNullException(nameof(span));
if (this.disposed)
throw new ObjectDisposedException("TagAggregator");
diff --git a/src/Text/Impl/TagAggregator/TagAggregatorFactoryService.cs b/src/Text/Impl/TagAggregator/TagAggregatorFactoryService.cs
index 1b743a5..d4a62b3 100644
--- a/src/Text/Impl/TagAggregator/TagAggregatorFactoryService.cs
+++ b/src/Text/Impl/TagAggregator/TagAggregatorFactoryService.cs
@@ -57,7 +57,7 @@ namespace Microsoft.VisualStudio.Text.Tagging.Implementation
public ITagAggregator<T> CreateTagAggregator<T>(ITextBuffer textBuffer, TagAggregatorOptions options) where T : ITag
{
if (textBuffer == null)
- throw new ArgumentNullException("textBuffer");
+ throw new ArgumentNullException(nameof(textBuffer));
return new TagAggregator<T>(this, null, this.BufferGraphFactoryService.CreateBufferGraph(textBuffer), options);
@@ -75,7 +75,7 @@ namespace Microsoft.VisualStudio.Text.Tagging.Implementation
public ITagAggregator<T> CreateTagAggregator<T>(ITextView textView, TagAggregatorOptions options) where T : ITag
{
if (textView == null)
- throw new ArgumentNullException("textView");
+ throw new ArgumentNullException(nameof(textView));
return new TagAggregator<T>(this, textView, textView.BufferGraph, options);
}
diff --git a/src/Text/Impl/TextBufferUndoManager/Strings.Designer.cs b/src/Text/Impl/TextBufferUndoManager/Strings.Designer.cs
index 5d442b7..1f71b36 100644
--- a/src/Text/Impl/TextBufferUndoManager/Strings.Designer.cs
+++ b/src/Text/Impl/TextBufferUndoManager/Strings.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
+// Runtime Version:2.0.50727.1426
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -19,7 +19,7 @@ namespace Microsoft.VisualStudio.Text.BufferUndoManager.Implementation {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Strings {
@@ -39,8 +39,7 @@ namespace Microsoft.VisualStudio.Text.BufferUndoManager.Implementation {
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.VisualStudio.Text.Implementation.Text.Impl.TextBufferUndoManager.String" +
- "s", typeof(Strings).Assembly);
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.VisualStudio.Text.Implementation.Text.Impl.TextBufferUndoManager.Strings", typeof(Strings).Assembly);
resourceMan = temp;
}
return resourceMan;
diff --git a/src/Text/Impl/TextBufferUndoManager/TextBufferChangeUndoPrimitive.cs b/src/Text/Impl/TextBufferUndoManager/TextBufferChangeUndoPrimitive.cs
index 277c0a0..28b5233 100644
--- a/src/Text/Impl/TextBufferUndoManager/TextBufferChangeUndoPrimitive.cs
+++ b/src/Text/Impl/TextBufferUndoManager/TextBufferChangeUndoPrimitive.cs
@@ -8,16 +8,14 @@
namespace Microsoft.VisualStudio.Text.BufferUndoManager.Implementation
{
using System;
- using System.Text;
+ using System.Diagnostics;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Operations;
- using System.Collections.Generic;
- using System.Diagnostics;
-
+
/// <summary>
/// The UndoPrimitive for a text buffer change operation.
/// </summary>
- public class TextBufferChangeUndoPrimitive : TextUndoPrimitive
+ public class TextBufferChangeUndoPrimitive : TextUndoPrimitive, IEditOnlyTextUndoPrimitive
{
#region Private Data Members
@@ -26,9 +24,9 @@ namespace Microsoft.VisualStudio.Text.BufferUndoManager.Implementation
private readonly ITextUndoHistory _undoHistory;
private WeakReference _weakBufferReference;
- private readonly INormalizedTextChangeCollection _textChanges;
- private int? _beforeVersion;
- private int? _afterVersion;
+ public INormalizedTextChangeCollection Changes { get; }
+ public int? BeforeReiteratedVersionNumber { get; private set; }
+ public int? AfterReiteratedVersionNumber { get; private set; }
#if DEBUG
private int _bufferLengthAfterChange;
#endif
@@ -52,17 +50,17 @@ namespace Microsoft.VisualStudio.Text.BufferUndoManager.Implementation
// Verify input parameters
if (undoHistory == null)
{
- throw new ArgumentNullException("undoHistory");
+ throw new ArgumentNullException(nameof(undoHistory));
}
if (textVersion == null)
{
- throw new ArgumentNullException("textVersion");
+ throw new ArgumentNullException(nameof(textVersion));
}
- _textChanges = textVersion.Changes;
- _beforeVersion = textVersion.ReiteratedVersionNumber;
- _afterVersion = textVersion.Next.VersionNumber;
+ this.Changes = textVersion.Changes;
+ this.BeforeReiteratedVersionNumber = textVersion.ReiteratedVersionNumber;
+ this.AfterReiteratedVersionNumber = textVersion.Next.VersionNumber;
Debug.Assert(textVersion.Next.VersionNumber == textVersion.Next.ReiteratedVersionNumber,
"Creating a TextBufferChangeUndoPrimitive for a change that has previously been undone? This is probably wrong.");
@@ -124,14 +122,14 @@ namespace Microsoft.VisualStudio.Text.BufferUndoManager.Implementation
{
AttachedToNewBuffer = false;
- _beforeVersion = TextBuffer.CurrentSnapshot.Version.VersionNumber;
- _afterVersion = null;
+ this.BeforeReiteratedVersionNumber = TextBuffer.CurrentSnapshot.Version.VersionNumber;
+ this.AfterReiteratedVersionNumber = null;
}
bool editCanceled = false;
- using (ITextEdit edit = TextBuffer.CreateEdit(EditOptions.None, _afterVersion, typeof(TextBufferChangeUndoPrimitive)))
+ using (ITextEdit edit = TextBuffer.CreateEdit(EditOptions.None, this.AfterReiteratedVersionNumber, UndoTag.Tag))
{
- foreach (ITextChange textChange in _textChanges)
+ foreach (ITextChange textChange in this.Changes)
{
if (!edit.Replace(new Span(textChange.OldPosition, textChange.OldLength), textChange.NewText))
{
@@ -157,9 +155,9 @@ namespace Microsoft.VisualStudio.Text.BufferUndoManager.Implementation
throw new OperationCanceledException("Redo failed due to readonly regions or canceled edit.");
}
- if (_afterVersion == null)
+ if (this.AfterReiteratedVersionNumber == null)
{
- _afterVersion = TextBuffer.CurrentSnapshot.Version.VersionNumber;
+ this.AfterReiteratedVersionNumber = TextBuffer.CurrentSnapshot.Version.VersionNumber;
}
#if DEBUG
@@ -195,14 +193,14 @@ namespace Microsoft.VisualStudio.Text.BufferUndoManager.Implementation
{
AttachedToNewBuffer = false;
- _beforeVersion = null;
- _afterVersion = TextBuffer.CurrentSnapshot.Version.VersionNumber;
+ this.BeforeReiteratedVersionNumber = null;
+ this.AfterReiteratedVersionNumber = TextBuffer.CurrentSnapshot.Version.VersionNumber;
}
bool editCanceled = false;
- using (ITextEdit edit = TextBuffer.CreateEdit(EditOptions.None, _beforeVersion, typeof(TextBufferChangeUndoPrimitive)))
+ using (ITextEdit edit = TextBuffer.CreateEdit(EditOptions.None, this.BeforeReiteratedVersionNumber, UndoTag.Tag))
{
- foreach (ITextChange textChange in _textChanges)
+ foreach (ITextChange textChange in this.Changes)
{
if (!edit.Replace(new Span(textChange.NewPosition, textChange.NewLength), textChange.OldText))
{
@@ -228,9 +226,9 @@ namespace Microsoft.VisualStudio.Text.BufferUndoManager.Implementation
throw new OperationCanceledException("Undo failed due to readonly regions or canceled edit.");
}
- if (_beforeVersion == null)
+ if (this.BeforeReiteratedVersionNumber == null)
{
- _beforeVersion = TextBuffer.CurrentSnapshot.Version.VersionNumber;
+ this.BeforeReiteratedVersionNumber = TextBuffer.CurrentSnapshot.Version.VersionNumber;
}
_canUndo = false;
@@ -283,5 +281,10 @@ namespace Microsoft.VisualStudio.Text.BufferUndoManager.Implementation
}
}
#endregion
+
+ internal class UndoTag : IUndoEditTag
+ {
+ public static readonly UndoTag Tag = new UndoTag();
+ }
}
}
diff --git a/src/Text/Impl/TextBufferUndoManager/TextBufferUndoManager.cs b/src/Text/Impl/TextBufferUndoManager/TextBufferUndoManager.cs
index 77f526c..646a47e 100644
--- a/src/Text/Impl/TextBufferUndoManager/TextBufferUndoManager.cs
+++ b/src/Text/Impl/TextBufferUndoManager/TextBufferUndoManager.cs
@@ -8,159 +8,165 @@
namespace Microsoft.VisualStudio.Text.BufferUndoManager.Implementation
{
using System;
- using System.Collections.Generic;
- using System.Text;
+ using System.Diagnostics;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Operations;
- using System.Diagnostics;
- class TextBufferUndoManager : ITextBufferUndoManager, IDisposable
+ internal sealed class TextBufferUndoManager : ITextBufferUndoManager, IDisposable
{
#region Private Members
- ITextBuffer _textBuffer;
- ITextUndoHistoryRegistry _undoHistoryRegistry;
- ITextUndoHistory _undoHistory;
- Queue<ITextVersion> _editVersionList = new Queue<ITextVersion>();
- bool _inPostChanged;
+ private ITextBuffer _textBuffer;
+ private readonly ITextUndoHistoryRegistry _undoHistoryRegistry;
+ private ITextUndoHistory _undoHistory;
+
+ // The plan had been to add the IUndoMetadataEditTag to allow people to create simple edits
+ // that would restore carets. That is being pushed back to 16.0 (maybe) but I didn't want to
+ // abandon the work in progress.
+#if false
+ private readonly IEditorOperationsFactoryService _editorOperationsFactoryService;
- #endregion
+ IEditorOperations _initiatingOperations = null;
+#endif
+ ITextUndoTransaction _createdTransaction = null;
+#endregion
public TextBufferUndoManager(ITextBuffer textBuffer, ITextUndoHistoryRegistry undoHistoryRegistry)
{
if (textBuffer == null)
{
- throw new ArgumentNullException("textBuffer");
+ throw new ArgumentNullException(nameof(textBuffer));
}
if (undoHistoryRegistry == null)
{
- throw new ArgumentNullException("undoHistoryRegistry");
+ throw new ArgumentNullException(nameof(undoHistoryRegistry));
}
_textBuffer = textBuffer;
-
_undoHistoryRegistry = undoHistoryRegistry;
+#if false
+ if (editorOperationsFactoryService == null)
+ {
+ throw new ArgumentNullException(nameof(editorOperationsFactoryService));
+ }
+
+ _editorOperationsFactoryService = editorOperationsFactoryService;
+#endif
+
// Register the undo history
- _undoHistory = _undoHistoryRegistry.RegisterHistory(_textBuffer);
+ this.EnsureTextBufferUndoHistory();
// Listen for the buffer changed events so that we can make them undo/redo-able
+ _textBuffer.Changing += TextBufferChanging;
_textBuffer.Changed += TextBufferChanged;
_textBuffer.PostChanged += TextBufferPostChanged;
- _textBuffer.Changing += TextBufferChanging;
}
- #region Private Methods
+#region Private Methods
private void TextBufferChanged(object sender, TextContentChangedEventArgs e)
{
- Debug.Assert((e.EditTag as Type) != typeof(TextBufferChangeUndoPrimitive) ||
- (_undoHistory.State != TextUndoHistoryState.Idle),
- "We are undoing/redoing a change while UndoHistory.State is Idle. Something is wrong with the state.");
-
- // If this change didn't originate from undo, add a TextBufferChangeUndoPrimitive to our history.
- if (_undoHistory.State == TextUndoHistoryState.Idle &&
- (e.EditTag as Type) != typeof(TextBufferChangeUndoPrimitive))
+ if (!(e.EditTag is IUndoEditTag))
{
- // With projection, we sometimes get Changed events with no changes, or for "" -> "".
- // We don't want to create undo actions for these.
- bool nonNullChange = false;
- foreach (ITextChange c in e.BeforeVersion.Changes)
+ if (this.TextBufferUndoHistory.State != TextUndoHistoryState.Idle)
{
- if (c.OldLength != 0 || c.NewLength != 0)
+ Debug.Fail("We are doing a normal edit in a non-idle undo state. This is explicitly prohibited as it would corrupt the undo stack! Please fix your code.");
+ }
+ else
+ {
+ // With projection, we sometimes get Changed events with no changes, or for "" -> "".
+ // We don't want to create undo actions for these.
+ bool nonNullChange = false;
+ foreach (ITextChange c in e.BeforeVersion.Changes)
{
- nonNullChange = true;
- break;
+ if (c.OldLength != 0 || c.NewLength != 0)
+ {
+ nonNullChange = true;
+ break;
+ }
}
- }
- if (nonNullChange)
- {
- // Queue the edit, and actually add an undo primitive later (see comment on PostChanged).
- _editVersionList.Enqueue(e.BeforeVersion);
+ if (nonNullChange)
+ {
+ // If there's an open undo transaction, add our edit (turned into a primitive) to it. Otherwise, create and undo transaction.
+ var currentTransaction = _undoHistory.CurrentTransaction;
+ if (currentTransaction == null)
+ {
+ // TODO remove this
+ // Hack to allow Cascade's local undo to light up if using v15.7 but behave using the old -- non-local -- undo before if running on 15.6.
+ // Cascade should really be marking its edits with IInvisibleEditTag (and will once it can take a hard requirement of VS 15.7).
+ if ((e.EditTag is IInvisibleEditTag) || ((e.EditTag != null) && (string.Equals(e.EditTag.ToString(), "CascadeRemoteEdit", StringComparison.Ordinal))))
+ {
+ _createdTransaction = ((ITextUndoHistory2)_undoHistory).CreateInvisibleTransaction("<invisible>");
+ }
+#if false
+ else if (e.EditTag is IUndoMetadataEditTag metadata)
+ {
+ _createdTransaction = _undoHistory.CreateTransaction(metadata.Description);
+ if (_initiatingOperations == null)
+ {
+ var view = metadata.InitiatingView;
+ if (view != null)
+ {
+ _initiatingOperations = _editorOperationsFactoryService.GetEditorOperations(view);
+ _initiatingOperations.AddBeforeTextBufferChangePrimitive();
+ }
+ }
+ }
+#endif
+ else
+ {
+ _createdTransaction = _undoHistory.CreateTransaction(Strings.TextBufferChanged);
+ }
+
+ currentTransaction = _createdTransaction;
+ }
+
+ currentTransaction.AddUndo(new TextBufferChangeUndoPrimitive(_undoHistory, e.BeforeVersion));
+ }
}
}
}
- /// <remarks>
- /// Edits are queued up by our TextBufferChanged handler and then we finally add them to the
- /// undo stack here in response to PostChanged. The reason and history behind why we do this
- /// is as follows:
- ///
- /// Originally this was done for VB commit, which uses undo events (i.e. TransactionCompleted) to
- /// trigger commit. Their commit logic relies on the buffer being in a state such that applying
- /// an edit synchronously raises a Changed event (which is always the case for PostChanged, but
- /// not for Changed if there are nested edits).
- ///
- /// JaredPar made a change (CS 1182244) that allowed VB to detect that UndoTransactionCompleted
- /// was being fired from a nested edit, and therefore delay the actual commit until the following
- /// PostChanged event.
- ///
- /// So this allowed us to move TextBufferUndoManager back to adding undo actions directly
- /// from the TextBufferChanged handler (CS 1285117). This is preferable, as otherwise there's a
- /// "delay" between when the edit happens and when we record the edit on the undo stack,
- /// allowing other people to stick something on the undo stack (i.e. from
- /// their ITextBuffer.Changed handler) in between. The result is actions being "out-of-order"
- /// on the undo stack.
- ///
- /// Unfortunately, it turns out VB snippets actually rely on this "out-of-order" behavior
- /// (see Dev10 834740) and so we are forced to revert CS 1285117) and return to the model
- /// where we queue up edits and delay adding them to the undo stack until PostChanged.
- ///
- /// It would be good to revisit this at again, but we would need to work with VB
- /// to fix their snippets / undo behavior, and verify that VB commit is also unaffected.
- /// </remarks>
- private void TextBufferPostChanged(object sender, EventArgs e)
+ void TextBufferChanging(object sender, TextContentChangingEventArgs e)
{
- // Only process a top level PostChanged event. Nested events will continue to process TextChange events
- // which are added to the queue and will be processed below
- if ( _inPostChanged )
- {
- return;
- }
-
- _inPostChanged = true;
- try
+ // Note that VB explicitly forces undo edits to happen while the history is idle so we need to allow this here
+ // by always doing nothing for undo edits). This may be a bug in our code (e.g. not properly cleaning up when
+ // an undo transaction is cancelled in mid-flight) but changing that will require coordination with Roslyn.
+ if (!(e.EditTag is IUndoEditTag))
{
- // Do not do a foreach loop here. It's perfectly possible, and in fact expected, that the Complete
- // method below can trigger a series of events which leads to a nested edit and another
- // ITextBuffer::Changed. That event will add to the _editVersionList queue and hence break a
- // foreach loop
- while ( _editVersionList.Count > 0 )
+ if (this.TextBufferUndoHistory.State != TextUndoHistoryState.Idle)
{
- var cur = _editVersionList.Dequeue();
- using (ITextUndoTransaction undoTransaction = _undoHistory.CreateTransaction(Strings.TextBufferChanged))
- {
- TextBufferChangeUndoPrimitive undoPrimitive = new TextBufferChangeUndoPrimitive(_undoHistory, cur);
- undoTransaction.AddUndo(undoPrimitive);
-
- undoTransaction.Complete();
- }
+ Debug.Fail("We are doing a normal edit in a non-idle undo state. This is explicitly prohibited as it would corrupt the undo stack! Please fix your code.");
+ e.Cancel();
}
}
- finally
- {
- _editVersionList.Clear(); // Ensure we cleanup state in the face of an exception
- _inPostChanged = false;
- }
}
- void TextBufferChanging(object sender, TextContentChangingEventArgs e)
+ private void TextBufferPostChanged(object sender, EventArgs e)
{
- // See if somebody (other than us) is trying to edit the buffer during undo/redo.
- if (_undoHistory.State != TextUndoHistoryState.Idle &&
- (e.EditTag as Type) != typeof(TextBufferChangeUndoPrimitive))
+ if (_createdTransaction != null)
{
- Debug.Fail("Attempt to edit the buffer during undo/redo has been denied. This is explicitly prohibited as it would corrupt the undo stack! Please fix your code.");
- e.Cancel();
+#if false
+ if (_initiatingOperations != null)
+ {
+ _initiatingOperations.AddAfterTextBufferChangePrimitive();
+ }
+
+ _initiatingOperations = null;
+#endif
+
+ _createdTransaction.Complete();
+ _createdTransaction.Dispose();
+ _createdTransaction = null;
}
}
+#endregion
- #endregion
-
- #region ITextBufferUndoManager Members
+#region ITextBufferUndoManager Members
public ITextBuffer TextBuffer
{
@@ -175,31 +181,50 @@ namespace Microsoft.VisualStudio.Text.BufferUndoManager.Implementation
// we are robust, always register the undo history.
get
{
- _undoHistory = _undoHistoryRegistry.RegisterHistory(_textBuffer);
- return _undoHistory;
+ this.EnsureTextBufferUndoHistory();
+ return _undoHistory;
}
}
public void UnregisterUndoHistory()
{
// Unregister the undo history
- _undoHistoryRegistry.RemoveHistory(_undoHistory);
+ if (_undoHistory != null)
+ {
+ _undoHistoryRegistry.RemoveHistory(_undoHistory);
+ _undoHistory = null;
+ }
}
- #endregion
+#endregion
+
+ private void EnsureTextBufferUndoHistory()
+ {
+ if (_textBuffer == null)
+ throw new ObjectDisposedException("TextBufferUndoManager");
+
+ // Note, right now, there is no way for us to know if an ITextUndoHistory
+ // has been unregistered (ie it can be unregistered by a third party)
+ // An issue has been logged with the Undo team, but in the mean time, to ensure that
+ // we are robust, always register the undo history.
+ _undoHistory = _undoHistoryRegistry.RegisterHistory(_textBuffer);
+ }
- #region IDisposable Members
+#region IDisposable Members
public void Dispose()
{
- UnregisterUndoHistory();
- _textBuffer.Changed -= TextBufferChanged;
- _textBuffer.PostChanged -= TextBufferPostChanged;
- _textBuffer.Changing -= TextBufferChanging;
+ if (_textBuffer != null)
+ {
+ _textBuffer.PostChanged -= TextBufferPostChanged;
+ _textBuffer.Changed -= TextBufferChanged;
+ _textBuffer.Changing -= TextBufferChanging;
+ _textBuffer = null;
+ }
GC.SuppressFinalize(this);
}
- #endregion
+#endregion
}
}
diff --git a/src/Text/Impl/TextBufferUndoManager/TextBufferUndoManagerProvider.cs b/src/Text/Impl/TextBufferUndoManager/TextBufferUndoManagerProvider.cs
index 72eb736..c5a4823 100644
--- a/src/Text/Impl/TextBufferUndoManager/TextBufferUndoManagerProvider.cs
+++ b/src/Text/Impl/TextBufferUndoManager/TextBufferUndoManagerProvider.cs
@@ -8,18 +8,19 @@
namespace Microsoft.VisualStudio.Text.BufferUndoManager.Implementation
{
using System;
- using System.Collections.Generic;
+ using System.ComponentModel.Composition;
using Microsoft.VisualStudio.Text;
- using Microsoft.VisualStudio.Text.Projection;
using Microsoft.VisualStudio.Text.Operations;
- using System.ComponentModel.Composition;
[Export(typeof(ITextBufferUndoManagerProvider))]
internal sealed class TextBufferUndoManagerProvider : ITextBufferUndoManagerProvider
{
[Import]
internal ITextUndoHistoryRegistry _undoHistoryRegistry { get; set; }
-
+#if false
+ [Import]
+ internal IEditorOperationsFactoryService _editorOperationsFactoryService { get; set; }
+#endif
/// <summary>
/// Provides an <see cref="ITextBufferUndoManager"/> for the given <paramref name="textBuffer"/>.
/// </summary>
@@ -31,13 +32,16 @@ namespace Microsoft.VisualStudio.Text.BufferUndoManager.Implementation
// Validate
if (textBuffer == null)
{
- throw new ArgumentNullException("textBuffer");
+ throw new ArgumentNullException(nameof(textBuffer));
}
// See if there was already a TextBufferUndoManager created for the given textBuffer, we only ever want to create one
ITextBufferUndoManager cachedBufferUndoManager;
if (!textBuffer.Properties.TryGetProperty<ITextBufferUndoManager>(typeof(ITextBufferUndoManager), out cachedBufferUndoManager))
{
+#if false
+ cachedBufferUndoManager = new TextBufferUndoManager(textBuffer, _undoHistoryRegistry, _editorOperationsFactoryService);
+#endif
cachedBufferUndoManager = new TextBufferUndoManager(textBuffer, _undoHistoryRegistry);
textBuffer.Properties.AddProperty(typeof(ITextBufferUndoManager), cachedBufferUndoManager);
}
@@ -54,7 +58,7 @@ namespace Microsoft.VisualStudio.Text.BufferUndoManager.Implementation
// Validate
if (textBuffer == null)
{
- throw new ArgumentNullException("textBuffer");
+ throw new ArgumentNullException(nameof(textBuffer));
}
ITextBufferUndoManager cachedBufferUndoManager;
diff --git a/src/Text/Impl/TextModel/BaseBuffer.cs b/src/Text/Impl/TextModel/BaseBuffer.cs
index 4d4ed82..a318a35 100644
--- a/src/Text/Impl/TextModel/BaseBuffer.cs
+++ b/src/Text/Impl/TextModel/BaseBuffer.cs
@@ -106,7 +106,9 @@ namespace Microsoft.VisualStudio.Text.Implementation
}
}
+#pragma warning disable CA1063 // Implement IDisposable Correctly
public void Dispose()
+#pragma warning restore CA1063 // Implement IDisposable Correctly
{
if (!this.applied && !this.canceled)
{
@@ -207,11 +209,11 @@ namespace Microsoft.VisualStudio.Text.Implementation
CheckActive();
if (position < 0 || position > this.bufferLength)
{
- throw new ArgumentOutOfRangeException("position");
+ throw new ArgumentOutOfRangeException(nameof(position));
}
if (text == null)
{
- throw new ArgumentNullException("text");
+ throw new ArgumentNullException(nameof(text));
}
// Check for ReadOnly
@@ -233,19 +235,19 @@ namespace Microsoft.VisualStudio.Text.Implementation
CheckActive();
if (position < 0 || position > this.bufferLength)
{
- throw new ArgumentOutOfRangeException("position");
+ throw new ArgumentOutOfRangeException(nameof(position));
}
if (characterBuffer == null)
{
- throw new ArgumentNullException("characterBuffer");
+ throw new ArgumentNullException(nameof(characterBuffer));
}
if (startIndex < 0 || startIndex > characterBuffer.Length)
{
- throw new ArgumentOutOfRangeException("startIndex");
+ throw new ArgumentOutOfRangeException(nameof(startIndex));
}
if (length < 0 || startIndex + length > characterBuffer.Length)
{
- throw new ArgumentOutOfRangeException("length");
+ throw new ArgumentOutOfRangeException(nameof(length));
}
// Check for ReadOnly
@@ -267,15 +269,15 @@ namespace Microsoft.VisualStudio.Text.Implementation
CheckActive();
if (startPosition < 0 || startPosition > this.bufferLength)
{
- throw new ArgumentOutOfRangeException("startPosition");
+ throw new ArgumentOutOfRangeException(nameof(startPosition));
}
if (charsToReplace < 0 || startPosition + charsToReplace > this.bufferLength)
{
- throw new ArgumentOutOfRangeException("charsToReplace");
+ throw new ArgumentOutOfRangeException(nameof(charsToReplace));
}
if (replaceWith == null)
{
- throw new ArgumentNullException("replaceWith");
+ throw new ArgumentNullException(nameof(replaceWith));
}
// Check for ReadOnly
@@ -297,11 +299,11 @@ namespace Microsoft.VisualStudio.Text.Implementation
CheckActive();
if (replaceSpan.End > this.bufferLength)
{
- throw new ArgumentOutOfRangeException("replaceSpan");
+ throw new ArgumentOutOfRangeException(nameof(replaceSpan));
}
if (replaceWith == null)
{
- throw new ArgumentNullException("replaceWith");
+ throw new ArgumentNullException(nameof(replaceWith));
}
// Check for ReadOnly
@@ -323,11 +325,11 @@ namespace Microsoft.VisualStudio.Text.Implementation
CheckActive();
if (startPosition < 0 || startPosition > this.bufferLength)
{
- throw new ArgumentOutOfRangeException("startPosition");
+ throw new ArgumentOutOfRangeException(nameof(startPosition));
}
if (charsToDelete < 0 || startPosition + charsToDelete > this.bufferLength)
{
- throw new ArgumentOutOfRangeException("charsToDelete");
+ throw new ArgumentOutOfRangeException(nameof(charsToDelete));
}
// Check for ReadOnly
@@ -349,7 +351,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
CheckActive();
if (deleteSpan.End > this.bufferLength)
{
- throw new ArgumentOutOfRangeException("deleteSpan");
+ throw new ArgumentOutOfRangeException(nameof(deleteSpan));
}
// Check for ReadOnly
@@ -716,7 +718,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (newContentType == null)
{
- throw new ArgumentNullException("newContentType");
+ throw new ArgumentNullException(nameof(newContentType));
}
if (newContentType != this.contentType)
@@ -764,7 +766,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
ReadOnlyQueryThreadCheck();
if ((position < 0) || (position > this.currentSnapshot.Length))
{
- throw new ArgumentOutOfRangeException("position");
+ throw new ArgumentOutOfRangeException(nameof(position));
}
return IsReadOnlyImplementation(position, isEdit);
@@ -780,7 +782,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
ReadOnlyQueryThreadCheck();
if (span.End > this.currentSnapshot.Length)
{
- throw new ArgumentOutOfRangeException("span");
+ throw new ArgumentOutOfRangeException(nameof(span));
}
return IsReadOnlyImplementation(span, isEdit);
@@ -809,7 +811,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
ReadOnlyQueryThreadCheck();
if (span.End > this.CurrentSnapshot.Length)
{
- throw new ArgumentOutOfRangeException("span");
+ throw new ArgumentOutOfRangeException(nameof(span));
}
return GetReadOnlyExtentsImplementation(span);
}
diff --git a/src/Text/Impl/TextModel/BaseSnapshot.cs b/src/Text/Impl/TextModel/BaseSnapshot.cs
index db723d3..b099424 100644
--- a/src/Text/Impl/TextModel/BaseSnapshot.cs
+++ b/src/Text/Impl/TextModel/BaseSnapshot.cs
@@ -9,6 +9,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
using System;
using System.Collections.Generic;
+ using System.Globalization;
using System.IO;
using System.Text;
using Microsoft.VisualStudio.Utilities;
@@ -185,9 +186,11 @@ namespace Microsoft.VisualStudio.Text.Implementation
public override string ToString()
{
- return String.Format("version: {0} lines: {1} length: {2} \r\n content: {3}",
+ return string.Format(
+ CultureInfo.InvariantCulture,
+ "version: {0} lines: {1} length: {2} \r\n content: {3}",
Version.VersionNumber, LineCount, Length,
- Microsoft.VisualStudio.Text.Utilities.TextUtilities.Escape(this.GetText(0, Math.Min(40, this.Length))));
+ Utilities.TextUtilities.Escape(this.GetText(0, Math.Min(40, this.Length))));
}
#if _DEBUG
diff --git a/src/Text/Impl/TextModel/BufferFactoryService.cs b/src/Text/Impl/TextModel/BufferFactoryService.cs
index 975cbf0..5ae8b2f 100644
--- a/src/Text/Impl/TextModel/BufferFactoryService.cs
+++ b/src/Text/Impl/TextModel/BufferFactoryService.cs
@@ -149,7 +149,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (contentType == null)
{
- throw new ArgumentNullException("contentType");
+ throw new ArgumentNullException(nameof(contentType));
}
return Make(contentType, StringRebuilder.Empty, false);
}
@@ -163,7 +163,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (contentType == null)
{
- throw new ArgumentNullException("contentType");
+ throw new ArgumentNullException(nameof(contentType));
}
StringRebuilder content = StringRebuilderFromSnapshotSpan(span);
@@ -191,11 +191,11 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (text == null)
{
- throw new ArgumentNullException("text");
+ throw new ArgumentNullException(nameof(text));
}
if (contentType == null)
{
- throw new ArgumentNullException("contentType");
+ throw new ArgumentNullException(nameof(contentType));
}
return Make(contentType, StringRebuilder.Create(text), spurnGroup);
}
@@ -204,11 +204,11 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (reader == null)
{
- throw new ArgumentNullException("reader");
+ throw new ArgumentNullException(nameof(reader));
}
if (contentType == null)
{
- throw new ArgumentNullException("contentType");
+ throw new ArgumentNullException(nameof(contentType));
}
if (length > int.MaxValue)
{
@@ -217,7 +217,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
bool hasConsistentLineEndings;
int longestLineLength;
- StringRebuilder content = TextImageLoader.Load(reader, length, traceId, out hasConsistentLineEndings, out longestLineLength);
+ StringRebuilder content = TextImageLoader.Load(reader, length, out hasConsistentLineEndings, out longestLineLength);
ITextBuffer buffer = Make(contentType, content, false);
if (!hasConsistentLineEndings)
@@ -286,7 +286,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
bool hasConsistentLineEndings;
int longestLineLength;
- return CachingTextImage.Create(TextImageLoader.Load(reader, length, string.Empty, out hasConsistentLineEndings, out longestLineLength), null);
+ return CachingTextImage.Create(TextImageLoader.Load(reader, length, out hasConsistentLineEndings, out longestLineLength), null);
}
public ITextImage CreateTextImage(MemoryMappedFile source)
@@ -318,11 +318,11 @@ namespace Microsoft.VisualStudio.Text.Implementation
// projectionEditResolver is allowed to be null.
if (trackingSpans == null)
{
- throw new ArgumentNullException("trackingSpans");
+ throw new ArgumentNullException(nameof(trackingSpans));
}
if (contentType == null)
{
- throw new ArgumentNullException("contentType");
+ throw new ArgumentNullException(nameof(contentType));
}
IProjectionBuffer buffer =
new ProjectionBuffer(this, projectionEditResolver, contentType, trackingSpans, _differenceService, _textDifferencingSelectorService.DefaultTextDifferencingService, options, _guardedOperations);
@@ -337,7 +337,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
// projectionEditResolver is allowed to be null.
if (trackingSpans == null)
{
- throw new ArgumentNullException("trackingSpans");
+ throw new ArgumentNullException(nameof(trackingSpans));
}
IProjectionBuffer buffer =
@@ -354,15 +354,15 @@ namespace Microsoft.VisualStudio.Text.Implementation
// projectionEditResolver is allowed to be null.
if (exposedSpans == null)
{
- throw new ArgumentNullException("exposedSpans");
+ throw new ArgumentNullException(nameof(exposedSpans));
}
if (exposedSpans.Count == 0)
{
- throw new ArgumentOutOfRangeException("exposedSpans"); // really?
+ throw new ArgumentOutOfRangeException(nameof(exposedSpans)); // really?
}
if (contentType == null)
{
- throw new ArgumentNullException("contentType");
+ throw new ArgumentNullException(nameof(contentType));
}
if (exposedSpans[0].Snapshot != exposedSpans[0].Snapshot.TextBuffer.CurrentSnapshot)
diff --git a/src/Text/Impl/TextModel/EncodedStreamReader.cs b/src/Text/Impl/TextModel/EncodedStreamReader.cs
index b11a34b..4e30901 100644
--- a/src/Text/Impl/TextModel/EncodedStreamReader.cs
+++ b/src/Text/Impl/TextModel/EncodedStreamReader.cs
@@ -32,7 +32,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
GuardedOperations guardedOperations)
{
if (stream == null)
- throw new ArgumentNullException("stream");
+ throw new ArgumentNullException(nameof(stream));
long position = stream.Position;
diff --git a/src/Text/Impl/TextModel/FileNameKey.cs b/src/Text/Impl/TextModel/FileNameKey.cs
new file mode 100644
index 0000000..3bbd354
--- /dev/null
+++ b/src/Text/Impl/TextModel/FileNameKey.cs
@@ -0,0 +1,52 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+//
+// This file contain implementations details that are subject to change without notice.
+// Use at your own risk.
+//
+namespace Microsoft.VisualStudio.Text.Implementation
+{
+ using System;
+ using System.IO;
+ sealed class FileNameKey
+ {
+ private readonly string _fileName;
+ private readonly int _hashCode;
+
+ public FileNameKey(string fileName)
+ {
+ //Gracefully catch errors getting the full path (which can happen if the file name is on a protected share).
+ try
+ {
+ _fileName = Path.GetFullPath(fileName);
+ }
+ catch
+ {
+ //This shouldn't happen (we are generally passed names associated with documents that we are expecting to open so
+ //we should have access). If we fail, we will, at worst not get the same underlying document when people create
+ //persistent spans using unnormalized names.
+ _fileName = fileName;
+ }
+
+ _hashCode = StringComparer.OrdinalIgnoreCase.GetHashCode(_fileName);
+ }
+
+ //Override equality and hash code
+ public override int GetHashCode()
+ {
+ return _hashCode;
+ }
+
+ public override bool Equals(object obj)
+ {
+ var other = obj as FileNameKey;
+ return (other != null) && string.Equals(_fileName, other._fileName, StringComparison.OrdinalIgnoreCase);
+ }
+
+ public override string ToString()
+ {
+ return _fileName;
+ }
+ }
+}
diff --git a/src/Text/Impl/TextModel/FileUtilities.cs b/src/Text/Impl/TextModel/FileUtilities.cs
index 2c567c3..b6fbd82 100644
--- a/src/Text/Impl/TextModel/FileUtilities.cs
+++ b/src/Text/Impl/TextModel/FileUtilities.cs
@@ -185,7 +185,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
if (!(safeHandle.IsClosed || safeHandle.IsInvalid))
{
BY_HANDLE_FILE_INFORMATION fi;
- if (GetFileInformationByHandle(safeHandle, out fi))
+ if (NativeMethods.GetFileInformationByHandle(safeHandle, out fi))
{
if (fi.NumberOfLinks <= 1)
{
@@ -232,26 +232,5 @@ namespace Microsoft.VisualStudio.Text.Implementation
temporaryPath = null;
return new FileStream(filePath, fileMode, FileAccess.Write, FileShare.Read);
}
-
- [StructLayout(LayoutKind.Sequential)]
- struct BY_HANDLE_FILE_INFORMATION
- {
- public uint FileAttributes;
- public System.Runtime.InteropServices.ComTypes.FILETIME CreationTime;
- public System.Runtime.InteropServices.ComTypes.FILETIME LastAccessTime;
- public System.Runtime.InteropServices.ComTypes.FILETIME LastWriteTime;
- public uint VolumeSerialNumber;
- public uint FileSizeHigh;
- public uint FileSizeLow;
- public uint NumberOfLinks;
- public uint FileIndexHigh;
- public uint FileIndexLow;
- }
-
- [DllImport("kernel32.dll", SetLastError = true)]
- static extern bool GetFileInformationByHandle(
- Microsoft.Win32.SafeHandles.SafeFileHandle hFile,
- out BY_HANDLE_FILE_INFORMATION lpFileInformation
- );
}
-} \ No newline at end of file
+}
diff --git a/src/Text/Impl/TextModel/ForwardFidelityCustomTrackingSpan.cs b/src/Text/Impl/TextModel/ForwardFidelityCustomTrackingSpan.cs
index 4b8f0c2..938be92 100644
--- a/src/Text/Impl/TextModel/ForwardFidelityCustomTrackingSpan.cs
+++ b/src/Text/Impl/TextModel/ForwardFidelityCustomTrackingSpan.cs
@@ -19,7 +19,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (behavior == null)
{
- throw new ArgumentNullException("behavior");
+ throw new ArgumentNullException(nameof(behavior));
}
this.behavior = behavior;
this.customState = customState;
diff --git a/src/Text/Impl/TextModel/HighFidelityTrackingPoint.cs b/src/Text/Impl/TextModel/HighFidelityTrackingPoint.cs
index db784fb..5301a95 100644
--- a/src/Text/Impl/TextModel/HighFidelityTrackingPoint.cs
+++ b/src/Text/Impl/TextModel/HighFidelityTrackingPoint.cs
@@ -44,7 +44,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (fidelity != TrackingFidelityMode.UndoRedo && fidelity != TrackingFidelityMode.Backward)
{
- throw new ArgumentOutOfRangeException("fidelity");
+ throw new ArgumentOutOfRangeException(nameof(fidelity));
}
List<VersionNumberPosition> initialHistory = null;
if (fidelity == TrackingFidelityMode.UndoRedo && version.VersionNumber > 0)
diff --git a/src/Text/Impl/TextModel/HighFidelityTrackingSpan.cs b/src/Text/Impl/TextModel/HighFidelityTrackingSpan.cs
index 6eb6ca4..e889804 100644
--- a/src/Text/Impl/TextModel/HighFidelityTrackingSpan.cs
+++ b/src/Text/Impl/TextModel/HighFidelityTrackingSpan.cs
@@ -50,7 +50,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (fidelity != TrackingFidelityMode.UndoRedo && fidelity != TrackingFidelityMode.Backward)
{
- throw new ArgumentOutOfRangeException("fidelity");
+ throw new ArgumentOutOfRangeException(nameof(fidelity));
}
List<VersionNumberPosition> startHistory = null;
List<VersionNumberPosition> endHistory = null;
diff --git a/src/Text/Impl/TextModel/MappingPoint.cs b/src/Text/Impl/TextModel/MappingPoint.cs
index 46e34ec..2497607 100644
--- a/src/Text/Impl/TextModel/MappingPoint.cs
+++ b/src/Text/Impl/TextModel/MappingPoint.cs
@@ -20,15 +20,15 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (anchorPoint.Snapshot == null)
{
- throw new ArgumentNullException("anchorPoint");
+ throw new ArgumentNullException(nameof(anchorPoint));
}
if (trackingMode < PointTrackingMode.Positive || trackingMode > PointTrackingMode.Negative)
{
- throw new ArgumentOutOfRangeException("trackingMode");
+ throw new ArgumentOutOfRangeException(nameof(trackingMode));
}
if (bufferGraph == null)
{
- throw new ArgumentNullException("bufferGraph");
+ throw new ArgumentNullException(nameof(bufferGraph));
}
this.anchorPoint = anchorPoint;
this.trackingMode = trackingMode;
@@ -49,7 +49,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (targetBuffer == null)
{
- throw new ArgumentNullException("targetBuffer");
+ throw new ArgumentNullException(nameof(targetBuffer));
}
ITextBuffer anchorBuffer = this.AnchorBuffer;
SnapshotPoint currentPoint = this.anchorPoint.TranslateTo(anchorBuffer.CurrentSnapshot, this.trackingMode);
@@ -86,7 +86,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
public SnapshotPoint? GetPoint(ITextSnapshot targetSnapshot, PositionAffinity affinity)
{
if (targetSnapshot == null)
- throw new ArgumentNullException("targetSnapshot");
+ throw new ArgumentNullException(nameof(targetSnapshot));
SnapshotPoint? result = GetPoint(targetSnapshot.TextBuffer, affinity);
if (result.HasValue && (result.Value.Snapshot != targetSnapshot))
@@ -101,7 +101,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (match == null)
{
- throw new ArgumentNullException("match");
+ throw new ArgumentNullException(nameof(match));
}
ITextBuffer anchorBuffer = this.AnchorBuffer;
SnapshotPoint currentPoint = this.anchorPoint.TranslateTo(anchorBuffer.CurrentSnapshot, this.trackingMode);
@@ -143,7 +143,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
// always maps down
if (match == null)
{
- throw new ArgumentNullException("match");
+ throw new ArgumentNullException(nameof(match));
}
ITextBuffer anchorBuffer = this.AnchorBuffer;
SnapshotPoint currentPoint = this.anchorPoint.TranslateTo(anchorBuffer.CurrentSnapshot, this.trackingMode);
diff --git a/src/Text/Impl/TextModel/MappingSpan.cs b/src/Text/Impl/TextModel/MappingSpan.cs
index a42f0fe..741972c 100644
--- a/src/Text/Impl/TextModel/MappingSpan.cs
+++ b/src/Text/Impl/TextModel/MappingSpan.cs
@@ -9,6 +9,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
using System;
using System.Collections.Generic;
+ using System.Globalization;
using Microsoft.VisualStudio.Text.Projection;
using Microsoft.VisualStudio.Text.Utilities;
@@ -22,15 +23,15 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (anchorSpan.Snapshot == null)
{
- throw new ArgumentNullException("anchorSpan");
+ throw new ArgumentNullException(nameof(anchorSpan));
}
if (trackingMode < SpanTrackingMode.EdgeExclusive || trackingMode > SpanTrackingMode.EdgeNegative)
{
- throw new ArgumentOutOfRangeException("trackingMode");
+ throw new ArgumentOutOfRangeException(nameof(trackingMode));
}
if (bufferGraph == null)
{
- throw new ArgumentNullException("bufferGraph");
+ throw new ArgumentNullException(nameof(bufferGraph));
}
this.anchorSpan = anchorSpan;
this.trackingMode = trackingMode;
@@ -105,7 +106,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
public NormalizedSnapshotSpanCollection GetSpans(ITextSnapshot targetSnapshot)
{
if (targetSnapshot == null)
- throw new ArgumentNullException("targetSnapshot");
+ throw new ArgumentNullException(nameof(targetSnapshot));
NormalizedSnapshotSpanCollection results = GetSpans(targetSnapshot.TextBuffer);
if ((results.Count > 0) && (results[0].Snapshot != targetSnapshot))
@@ -126,7 +127,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (match == null)
{
- throw new ArgumentNullException("match");
+ throw new ArgumentNullException(nameof(match));
}
ITextBuffer anchorBuffer = this.AnchorBuffer;
@@ -156,7 +157,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
public override string ToString()
{
- return String.Format("MappingSpan anchored at {0}", this.anchorSpan);
+ return String.Format(CultureInfo.CurrentCulture, "MappingSpan anchored at {0}", this.anchorSpan);
}
}
-} \ No newline at end of file
+}
diff --git a/src/Text/Impl/TextModel/NativeMethods.cs b/src/Text/Impl/TextModel/NativeMethods.cs
new file mode 100644
index 0000000..78d507b
--- /dev/null
+++ b/src/Text/Impl/TextModel/NativeMethods.cs
@@ -0,0 +1,28 @@
+using System.Runtime.InteropServices;
+
+namespace Microsoft.VisualStudio.Text.Implementation
+{
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct BY_HANDLE_FILE_INFORMATION
+ {
+ public uint FileAttributes;
+ public System.Runtime.InteropServices.ComTypes.FILETIME CreationTime;
+ public System.Runtime.InteropServices.ComTypes.FILETIME LastAccessTime;
+ public System.Runtime.InteropServices.ComTypes.FILETIME LastWriteTime;
+ public uint VolumeSerialNumber;
+ public uint FileSizeHigh;
+ public uint FileSizeLow;
+ public uint NumberOfLinks;
+ public uint FileIndexHigh;
+ public uint FileIndexLow;
+ }
+
+ internal static class NativeMethods
+ {
+ [DllImport("kernel32.dll", SetLastError = true)]
+ internal static extern bool GetFileInformationByHandle(
+ Microsoft.Win32.SafeHandles.SafeFileHandle hFile,
+ out BY_HANDLE_FILE_INFORMATION lpFileInformation
+ );
+ }
+}
diff --git a/src/Text/Impl/TextModel/NormalizedTextChangeCollection.cs b/src/Text/Impl/TextModel/NormalizedTextChangeCollection.cs
index 8e3352f..5a9e03b 100644
--- a/src/Text/Impl/TextModel/NormalizedTextChangeCollection.cs
+++ b/src/Text/Impl/TextModel/NormalizedTextChangeCollection.cs
@@ -16,7 +16,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
internal partial class NormalizedTextChangeCollection : INormalizedTextChangeCollection
{
- public static readonly NormalizedTextChangeCollection Empty = new NormalizedTextChangeCollection(new TextChange[0]);
+ public static readonly NormalizedTextChangeCollection Empty = new NormalizedTextChangeCollection(Array.Empty<TextChange>());
private readonly IReadOnlyList<TextChange> _changes;
public static INormalizedTextChangeCollection Create(IReadOnlyList<TextChange> changes)
@@ -36,7 +36,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (changes == null)
{
- throw new ArgumentNullException("changes");
+ throw new ArgumentNullException(nameof(changes));
}
if (changes.Count == 0)
@@ -122,7 +122,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
}
else if (changes.Count == 0)
{
- return new TextChange[0];
+ return Array.Empty<TextChange>();
}
TextChange[] work = TextUtilities.StableSort(changes, TextChange.Compare);
@@ -215,10 +215,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
if (differenceOptions.HasValue)
{
- if (textDifferencingService == null)
- {
- throw new ArgumentNullException("stringDifferenceUtility");
- }
+ Requires.NotNull(textDifferencingService, nameof(textDifferencingService));
foreach (TextChange change in work)
{
if (change == null) continue;
@@ -259,7 +256,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
string oldText = change.OldText;
string newText = change.NewText;
- if (oldText == newText)
+ if (string.Equals(oldText, newText, StringComparison.Ordinal))
{
// This change simply evaporates. This case occurs frequently in Venus and it is much
// better to short circuit it here than to fire up the differencing engine.
diff --git a/src/Text/Impl/TextModel/PersistentSpan.cs b/src/Text/Impl/TextModel/PersistentSpan.cs
index 79f205d..6ec563a 100644
--- a/src/Text/Impl/TextModel/PersistentSpan.cs
+++ b/src/Text/Impl/TextModel/PersistentSpan.cs
@@ -7,45 +7,42 @@
//
namespace Microsoft.VisualStudio.Text.Implementation
{
- using Microsoft.VisualStudio.Text;
- using Microsoft.VisualStudio.Utilities;
using System;
- using System.Diagnostics;
internal sealed class PersistentSpan : IPersistentSpan
{
#region members
- private PersistentSpanFactory _factory;
+ public PersistentSpanSet SpanSet;
private ITrackingSpan _span; //null for spans on closed documents or disposed spans
- private ITextDocument _document; //null for spans on closed documents or disposed spans
- private string _filePath; //null for spans on opened documents or disposed spans
private int _startLine; //these parameters are valid whether or not the document is open (but _start*,_end* may be stale).
private int _startIndex;
private int _endLine;
private int _endIndex;
- private Span _nonTrackingSpan;
+ private ITextVersion _originalVersion = null;
+ private Span _originalSpan; // This is either the span when this was created or when the document was reopened.
+ // It is default(Span) if either we were created (on an unopened document) with line/column indices or after the document was closed.
private bool _useLineIndex;
private readonly SpanTrackingMode _trackingMode;
#endregion
- internal PersistentSpan(ITextDocument document, SnapshotSpan span, SpanTrackingMode trackingMode, PersistentSpanFactory factory)
+ internal PersistentSpan(SnapshotSpan span, SpanTrackingMode trackingMode, PersistentSpanSet spanSet)
{
- //Arguments verified in factory
- _document = document;
-
_span = span.Snapshot.CreateTrackingSpan(span, trackingMode);
- _trackingMode = trackingMode;
- _factory = factory;
+ _originalVersion = span.Snapshot.Version;
+ _originalSpan = span;
+
+ PersistentSpan.SnapshotPointToLineIndex(span.Start, out _startLine, out _startIndex);
+ PersistentSpan.SnapshotPointToLineIndex(span.End, out _endLine, out _endIndex);
+
+ _trackingMode = trackingMode;
+ this.SpanSet = spanSet;
}
- internal PersistentSpan(string filePath, int startLine, int startIndex, int endLine, int endIndex, SpanTrackingMode trackingMode, PersistentSpanFactory factory)
+ internal PersistentSpan(int startLine, int startIndex, int endLine, int endIndex, SpanTrackingMode trackingMode, PersistentSpanSet spanSet)
{
- //Arguments verified in factory
- _filePath = filePath;
-
_useLineIndex = true;
_startLine = startLine;
_startIndex = startIndex;
@@ -53,27 +50,22 @@ namespace Microsoft.VisualStudio.Text.Implementation
_endIndex = endIndex;
_trackingMode = trackingMode;
-
- _factory = factory;
+ this.SpanSet = spanSet;
}
- internal PersistentSpan(string filePath, Span span, SpanTrackingMode trackingMode, PersistentSpanFactory factory)
+ internal PersistentSpan(Span span, SpanTrackingMode trackingMode, PersistentSpanSet spanSet)
{
- //Arguments verified in factory
- _filePath = filePath;
-
_useLineIndex = false;
- _nonTrackingSpan = span;
+ _originalSpan = span;
_trackingMode = trackingMode;
-
- _factory = factory;
+ this.SpanSet = spanSet;
}
#region IPersistentSpan members
- public bool IsDocumentOpen { get { return _document != null; } }
+ public bool IsDocumentOpen { get { return this.SpanSet.Document != null; } }
- public ITextDocument Document { get { return _document; } }
+ public ITextDocument Document { get { return this.SpanSet.Document; } }
public ITrackingSpan Span { get { return _span; } }
@@ -81,84 +73,129 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
get
{
- return (_document != null) ? _document.FilePath : _filePath;
+ if (this.SpanSet == null)
+ throw new ObjectDisposedException("PersistentSpan");
+
+ return (this.SpanSet.Document != null) ? this.SpanSet.Document.FilePath : this.SpanSet.FileKey.ToString();
}
}
public bool TryGetStartLineIndex(out int startLine, out int startIndex)
{
- if ((_document == null) && (_filePath == null))
+ if (this.SpanSet == null)
throw new ObjectDisposedException("PersistentSpan");
if (_span != null)
- this.UpdateStartEnd();
-
- startLine = _startLine;
- startIndex = _startIndex;
+ {
+ SnapshotSpan span = _span.GetSpan(_span.TextBuffer.CurrentSnapshot);
+ PersistentSpan.SnapshotPointToLineIndex(span.Start, out startLine, out startIndex);
+ return true;
+ }
+ else if (_useLineIndex)
+ {
+ startLine = _startLine;
+ startIndex = _startIndex;
+ return true;
+ }
- return ((_span != null) || _useLineIndex);
+ startLine = startIndex = 0;
+ return false;
}
public bool TryGetEndLineIndex(out int endLine, out int endIndex)
{
- if ((_document == null) && (_filePath == null))
+ if (this.SpanSet == null)
throw new ObjectDisposedException("PersistentSpan");
if (_span != null)
- this.UpdateStartEnd();
+ {
+ SnapshotSpan span = _span.GetSpan(_span.TextBuffer.CurrentSnapshot);
+ PersistentSpan.SnapshotPointToLineIndex(span.End, out endLine, out endIndex);
+ return true;
+ }
+ else if (_useLineIndex)
+ {
+ endLine = _endLine;
+ endIndex = _endIndex;
+ return true;
+ }
- endLine = _endLine;
- endIndex = _endIndex;
- return ((_span != null) || _useLineIndex);
+ endLine = endIndex = 0;
+ return false;
}
public bool TryGetSpan(out Span span)
{
- if ((_document == null) && (_filePath == null))
+ if (this.SpanSet == null)
throw new ObjectDisposedException("PersistentSpan");
if (_span != null)
- this.UpdateStartEnd();
+ {
+ span = _span.GetSpan(_span.TextBuffer.CurrentSnapshot);
+ return true;
+ }
+ else if (!_useLineIndex)
+ {
+ span = _originalSpan;
+ return true;
+ }
- span = _nonTrackingSpan;
- return ((_span != null) || !_useLineIndex);
+ span = new Span();
+ return false;
}
#endregion
#region IDisposable members
public void Dispose()
{
- if ((_document != null) || (_filePath != null))
+ if (this.SpanSet != null)
{
- _factory.Delete(this);
-
+ this.SpanSet.Delete(this);
+ this.SpanSet = null;
+ _originalVersion = null;
_span = null;
- _document = null;
- _filePath = null;
}
}
#endregion
- #region private helpers
- internal void DocumentClosed()
+ internal void SetSpanSet(PersistentSpanSet spanSet)
{
- this.UpdateStartEnd();
+ if (this.SpanSet == null)
+ throw new ObjectDisposedException("PersistentSpan");
+
+ this.SpanSet = spanSet;
+ }
+
+ internal void DocumentClosed(ITextSnapshot savedSnapshot)
+ {
+ Assumes.NotNull(_originalVersion);
+
+ if ((savedSnapshot != null) && (savedSnapshot.Version.VersionNumber > _originalVersion.VersionNumber))
+ {
+ // The document was saved and we want to line/column indices in the saved snapshot (& not the current snapshot)
+ var savedSpan = new SnapshotSpan(savedSnapshot, Tracking.TrackSpanForwardInTime(_trackingMode, _originalSpan, _originalVersion, savedSnapshot.Version));
+
+ PersistentSpan.SnapshotPointToLineIndex(savedSpan.Start, out _startLine, out _startIndex);
+ PersistentSpan.SnapshotPointToLineIndex(savedSpan.End, out _endLine, out _endIndex);
+ }
+ else
+ {
+ // The document was never saved (or was saved before we created) so continue to use the old line/column indices.
+ // Since those are set when either the span is created (against an open document) or when the document is reopened,
+ // they don't need to be changed.
+ }
//We set this to false when the document is closed because we have an accurate line/index and that is more stable
//than a simple offset.
_useLineIndex = true;
- _nonTrackingSpan = new Span(0, 0);
-
- _filePath = _document.FilePath;
- _document = null;
+ _originalSpan = default(Span);
+ _originalVersion = null;
_span = null;
}
- internal void DocumentReopened(ITextDocument document)
+ internal void DocumentReopened()
{
- _document = document;
-
- ITextSnapshot snapshot = document.TextBuffer.CurrentSnapshot;
+ ITextSnapshot snapshot = this.SpanSet.Document.TextBuffer.CurrentSnapshot;
SnapshotPoint start;
SnapshotPoint end;
@@ -178,23 +215,27 @@ namespace Microsoft.VisualStudio.Text.Implementation
}
else
{
- start = new SnapshotPoint(snapshot, Math.Min(_nonTrackingSpan.Start, snapshot.Length));
- end = new SnapshotPoint(snapshot, Math.Min(_nonTrackingSpan.End, snapshot.Length));
+ start = new SnapshotPoint(snapshot, Math.Min(_originalSpan.Start, snapshot.Length));
+ end = new SnapshotPoint(snapshot, Math.Min(_originalSpan.End, snapshot.Length));
}
- _span = snapshot.CreateTrackingSpan(new SnapshotSpan(start, end), _trackingMode);
+ var snapshotSpan = new SnapshotSpan(start, end);
+ _span = snapshot.CreateTrackingSpan(snapshotSpan, _trackingMode);
+ _originalSpan = snapshotSpan;
- _filePath = null;
+ _originalVersion = snapshot.Version;
+ PersistentSpan.SnapshotPointToLineIndex(snapshotSpan.Start, out _startLine, out _startIndex);
+ PersistentSpan.SnapshotPointToLineIndex(snapshotSpan.End, out _endLine, out _endIndex);
}
- private void UpdateStartEnd()
+ private SnapshotSpan UpdateStartEnd()
{
SnapshotSpan span = _span.GetSpan(_span.TextBuffer.CurrentSnapshot);
- _nonTrackingSpan = span;
-
PersistentSpan.SnapshotPointToLineIndex(span.Start, out _startLine, out _startIndex);
PersistentSpan.SnapshotPointToLineIndex(span.End, out _endLine, out _endIndex);
+
+ return span;
}
private static void SnapshotPointToLineIndex(SnapshotPoint p, out int line, out int index)
@@ -207,10 +248,13 @@ namespace Microsoft.VisualStudio.Text.Implementation
internal static SnapshotPoint LineIndexToSnapshotPoint(int line, int index, ITextSnapshot snapshot)
{
- ITextSnapshotLine l = snapshot.GetLineFromLineNumber(Math.Min(line, snapshot.LineCount - 1));
+ if (line >= snapshot.LineCount)
+ {
+ return new SnapshotPoint(snapshot, snapshot.Length);
+ }
+ ITextSnapshotLine l = snapshot.GetLineFromLineNumber(line);
return l.Start + Math.Min(index, l.Length);
}
- #endregion
}
-} \ No newline at end of file
+}
diff --git a/src/Text/Impl/TextModel/PersistentSpanFactory.cs b/src/Text/Impl/TextModel/PersistentSpanFactory.cs
index 6ea3a3d..7e62ae1 100644
--- a/src/Text/Impl/TextModel/PersistentSpanFactory.cs
+++ b/src/Text/Impl/TextModel/PersistentSpanFactory.cs
@@ -7,14 +7,8 @@
//
namespace Microsoft.VisualStudio.Text.Implementation
{
- using Microsoft.VisualStudio.Text;
- using Microsoft.VisualStudio.Utilities;
- using Microsoft.VisualStudio.Text.Utilities;
- using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
- using System.Diagnostics;
- using System.IO;
[Export(typeof(IPersistentSpanFactory))]
internal class PersistentSpanFactory : IPersistentSpanFactory
@@ -22,17 +16,13 @@ namespace Microsoft.VisualStudio.Text.Implementation
[Import]
internal ITextDocumentFactoryService TextDocumentFactoryService;
- private readonly Dictionary<object, FrugalList<PersistentSpan>> _spansOnDocuments = new Dictionary<object, FrugalList<PersistentSpan>>(); //Used for lock
-
+ private readonly Dictionary<object, PersistentSpanSet> _spansOnDocuments = new Dictionary<object, PersistentSpanSet>(); //Used for lock
private bool _eventsHooked;
#region IPersistentSpanFactory members
public bool CanCreate(ITextBuffer buffer)
{
- if (buffer == null)
- {
- throw new ArgumentNullException("buffer");
- }
+ Requires.NotNull(buffer, nameof(buffer));
ITextDocument document;
return this.TextDocumentFactoryService.TryGetTextDocument(buffer, out document);
@@ -40,14 +30,16 @@ namespace Microsoft.VisualStudio.Text.Implementation
public IPersistentSpan Create(SnapshotSpan span, SpanTrackingMode trackingMode)
{
+ Requires.NotNull(span.Snapshot, nameof(span.Snapshot));
+
ITextDocument document;
if (this.TextDocumentFactoryService.TryGetTextDocument(span.Snapshot.TextBuffer, out document))
{
- PersistentSpan persistentSpan = new PersistentSpan(document, span, trackingMode, this);
-
- this.AddSpan(document, persistentSpan);
-
- return persistentSpan;
+ lock (_spansOnDocuments)
+ {
+ var spanSet = this.GetOrCreateSpanSet(null, document);
+ return spanSet.Create(span, trackingMode);
+ }
}
return null;
@@ -55,21 +47,21 @@ namespace Microsoft.VisualStudio.Text.Implementation
public IPersistentSpan Create(ITextSnapshot snapshot, int startLine, int startIndex, int endLine, int endIndex, SpanTrackingMode trackingMode)
{
+ Requires.NotNull(snapshot, nameof(snapshot));
+ Requires.Argument(startLine >= 0, nameof(startLine), "Must be non-negative.");
+ Requires.Argument(startIndex >= 0, nameof(startIndex), "Must be non-negative.");
+ Requires.Argument(endLine >= startLine, nameof(endLine), "Must be >= startLine.");
+ Requires.Argument((endIndex >= 0) && ((startLine != endLine) || (endIndex >= startIndex)), nameof(endIndex), "Must be non-negative and (endLine,endIndex) may not be before (startLine,startIndex).");
+ Requires.Range(((int)trackingMode >= (int)SpanTrackingMode.EdgeExclusive) || ((int)trackingMode <= (int)(SpanTrackingMode.EdgeNegative)), nameof(trackingMode));
+
ITextDocument document;
if (this.TextDocumentFactoryService.TryGetTextDocument(snapshot.TextBuffer, out document))
{
- var start = PersistentSpan.LineIndexToSnapshotPoint(startLine, startIndex, snapshot);
- var end = PersistentSpan.LineIndexToSnapshotPoint(endLine, endIndex, snapshot);
- if (end < start)
+ lock (_spansOnDocuments)
{
- end = start;
+ var spanSet = this.GetOrCreateSpanSet(null, document);
+ return spanSet.Create(snapshot, startLine, startIndex, endLine, endIndex, trackingMode);
}
-
- PersistentSpan persistentSpan = new PersistentSpan(document, new SnapshotSpan(start, end), trackingMode, this);
-
- this.AddSpan(document, persistentSpan);
-
- return persistentSpan;
}
return null;
@@ -77,218 +69,134 @@ namespace Microsoft.VisualStudio.Text.Implementation
public IPersistentSpan Create(string filePath, int startLine, int startIndex, int endLine, int endIndex, SpanTrackingMode trackingMode)
{
- if (string.IsNullOrEmpty(filePath))
- {
- throw new ArgumentException("filePath");
- }
- if (startLine < 0)
- {
- throw new ArgumentOutOfRangeException("startLine", "Must be non-negative.");
- }
- if (startIndex < 0)
- {
- throw new ArgumentOutOfRangeException("startIndex", "Must be non-negative.");
- }
- if (endLine < startLine)
- {
- throw new ArgumentOutOfRangeException("endLine", "Must be >= startLine.");
- }
- if ((endIndex < 0) || ((startLine == endLine) && (endIndex < startIndex)))
- {
- throw new ArgumentOutOfRangeException("endIndex", "Must be non-negative and (endLine,endIndex) may not be before (startLine,startIndex).");
- }
- if (((int)trackingMode < (int)SpanTrackingMode.EdgeExclusive) || ((int)trackingMode > (int)(SpanTrackingMode.EdgeNegative)))
+ Requires.NotNullOrEmpty(filePath, nameof(filePath));
+ Requires.Argument(startLine >= 0, nameof(startLine), "Must be non-negative.");
+ Requires.Argument(startIndex >= 0, nameof(startIndex), "Must be non-negative.");
+ Requires.Argument(endLine >= startLine, nameof(endLine), "Must be >= startLine.");
+ Requires.Argument((endIndex >= 0) && ((startLine != endLine) || (endIndex >= startIndex)), nameof(endIndex), "Must be non-negative and (endLine,endIndex) may not be before (startLine,startIndex).");
+ Requires.Range(((int)trackingMode >= (int)SpanTrackingMode.EdgeExclusive) || ((int)trackingMode <= (int)(SpanTrackingMode.EdgeNegative)), nameof(trackingMode));
+
+ var key = new FileNameKey(filePath);
+ lock (_spansOnDocuments)
{
- throw new ArgumentOutOfRangeException("trackingMode");
+ var spanSet = this.GetOrCreateSpanSet(key, null);
+ return spanSet.Create(startLine, startIndex, endLine, endIndex, trackingMode);
}
-
- PersistentSpan persistentSpan = new PersistentSpan(filePath, startLine, startIndex, endLine, endIndex, trackingMode, this);
-
- this.AddSpan(new FileNameKey(filePath), persistentSpan);
-
- return persistentSpan;
}
public IPersistentSpan Create(string filePath, Span span, SpanTrackingMode trackingMode)
{
- if (string.IsNullOrEmpty(filePath))
- {
- throw new ArgumentException("filePath");
- }
- if (((int)trackingMode < (int)SpanTrackingMode.EdgeExclusive) || ((int)trackingMode > (int)(SpanTrackingMode.EdgeNegative)))
+ Requires.NotNullOrEmpty(filePath, nameof(filePath));
+ Requires.Range(((int)trackingMode >= (int)SpanTrackingMode.EdgeExclusive) || ((int)trackingMode <= (int)(SpanTrackingMode.EdgeNegative)), nameof(trackingMode));
+
+ var key = new FileNameKey(filePath);
+ lock (_spansOnDocuments)
{
- throw new ArgumentOutOfRangeException("trackingMode");
+ var spanSet = this.GetOrCreateSpanSet(key, null);
+ return spanSet.Create(span, trackingMode);
}
-
- PersistentSpan persistentSpan = new PersistentSpan(filePath, span, trackingMode, this);
-
- this.AddSpan(new FileNameKey(filePath), persistentSpan);
-
- return persistentSpan;
}
#endregion
- internal bool IsEmpty { get { return _spansOnDocuments.Count == 0; } } //For unit tests
+ internal bool IsEmpty { get { return _spansOnDocuments.Count == 0; } } //For unit tests
- private void AddSpan(object key, PersistentSpan persistentSpan)
+ private PersistentSpanSet GetOrCreateSpanSet(FileNameKey filePath, ITextDocument document)
{
- lock (_spansOnDocuments)
+ object key = ((object)document) ?? filePath;
+ if (!_spansOnDocuments.TryGetValue(key, out PersistentSpanSet spanSet))
{
- FrugalList<PersistentSpan> spans;
- if (!_spansOnDocuments.TryGetValue(key, out spans))
+ if (!_eventsHooked)
{
- this.EnsureEventsHooked();
+ _eventsHooked = true;
- spans = new FrugalList<PersistentSpan>();
- _spansOnDocuments.Add(key, spans);
+ this.TextDocumentFactoryService.TextDocumentCreated += OnTextDocumentCreated;
+ this.TextDocumentFactoryService.TextDocumentDisposed += OnTextDocumentDisposed;
}
- spans.Add(persistentSpan);
+ spanSet = new PersistentSpanSet(filePath, document, this);
+ _spansOnDocuments.Add(key, spanSet);
}
- }
-
- private void EnsureEventsHooked()
- {
- if (!_eventsHooked)
- {
- _eventsHooked = true;
- this.TextDocumentFactoryService.TextDocumentCreated += OnTextDocumentCreated;
- this.TextDocumentFactoryService.TextDocumentDisposed += OnTextDocumentDisposed;
- }
+ return spanSet;
}
private void OnTextDocumentCreated(object sender, TextDocumentEventArgs e)
{
var path = new FileNameKey(e.TextDocument.FilePath);
- FrugalList<PersistentSpan> spans;
lock (_spansOnDocuments)
{
- if (_spansOnDocuments.TryGetValue(path, out spans))
+ if (_spansOnDocuments.TryGetValue(path, out PersistentSpanSet spanSet))
{
- foreach (var span in spans)
- {
- span.DocumentReopened(e.TextDocument);
- }
+ spanSet.DocumentReopened(e.TextDocument);
_spansOnDocuments.Remove(path);
- _spansOnDocuments.Add(e.TextDocument, spans);
+ _spansOnDocuments.Add(e.TextDocument, spanSet);
}
}
}
private void OnTextDocumentDisposed(object sender, TextDocumentEventArgs e)
{
- FrugalList<PersistentSpan> spans;
lock (_spansOnDocuments)
{
- if (_spansOnDocuments.TryGetValue(e.TextDocument, out spans))
+ if (_spansOnDocuments.TryGetValue(e.TextDocument, out PersistentSpanSet spanSet))
{
- foreach (var span in spans)
- {
- span.DocumentClosed();
- }
-
+ spanSet.DocumentClosed();
_spansOnDocuments.Remove(e.TextDocument);
- var path = new FileNameKey(e.TextDocument.FilePath);
- FrugalList<PersistentSpan> existingSpansOnPath;
- if (_spansOnDocuments.TryGetValue(path, out existingSpansOnPath))
+ if (_spansOnDocuments.TryGetValue(spanSet.FileKey, out PersistentSpanSet existingSpansOnPath))
{
- //Handle (badly) the case where a document is renamed to an existing closed document & then closed.
- existingSpansOnPath.AddRange(spans);
+ // Handle (badly) the case where a document is renamed to an existing closed document & then closed.
+ // We should only end up in this case if we had spans on two open documents that were both renamed
+ // to the same file name & then closed.
+ foreach (var s in spanSet.Spans)
+ {
+ s.SetSpanSet(existingSpansOnPath);
+ existingSpansOnPath.Spans.Add(s);
+ }
+
+ spanSet.Spans.Clear();
+ spanSet.Dispose();
}
else
{
- _spansOnDocuments.Add(path, spans);
+ _spansOnDocuments.Add(spanSet.FileKey, spanSet);
}
}
}
}
- internal void Delete(PersistentSpan span)
+ internal void DocumentRenamed(PersistentSpanSet spanSet)
{
lock (_spansOnDocuments)
{
- ITextDocument document = span.Document;
- if (document != null)
+ if (_spansOnDocuments.TryGetValue(spanSet.FileKey, out PersistentSpanSet existingSpansOnPath))
{
- FrugalList<PersistentSpan> spans;
- if (_spansOnDocuments.TryGetValue(document, out spans))
+ // There were spans on a closed document with the same name as this one. Move all of those spans to this one
+ // and "open" them (note that this will probably do bad things to their positions but it is the best we
+ // can do).
+ foreach (var s in existingSpansOnPath.Spans)
{
- spans.Remove(span);
+ s.SetSpanSet(spanSet);
+ spanSet.Spans.Add(s);
- if (spans.Count == 0)
- {
- //Last one ... remove all references to document.
- _spansOnDocuments.Remove(document);
- }
+ s.DocumentReopened();
}
- else
- {
- Debug.Fail("There should have been an entry in SpanOnDocuments.");
- }
- }
- else
- {
- var path = new FileNameKey(span.FilePath);
- FrugalList<PersistentSpan> spans;
- if (_spansOnDocuments.TryGetValue(path, out spans))
- {
- spans.Remove(span);
- if (spans.Count == 0)
- {
- //Last one ... remove all references to path.
- _spansOnDocuments.Remove(path);
- }
- }
- else
- {
- Debug.Fail("There should have been an entry in SpanOnDocuments.");
- }
+ existingSpansOnPath.Spans.Clear();
+ existingSpansOnPath.Dispose();
}
}
}
-
- private class FileNameKey
+ internal void Delete(PersistentSpanSet spanSet, PersistentSpan span)
{
- private readonly string _fileName;
- private readonly int _hashCode;
-
- public FileNameKey(string fileName)
+ lock (_spansOnDocuments)
{
- //Gracefully catch errors getting the full path (which can happen if the file name is on a protected share).
- try
+ if (spanSet.Spans.Remove(span) && (spanSet.Spans.Count == 0))
{
- _fileName = Path.GetFullPath(fileName);
+ _spansOnDocuments.Remove(((object)(spanSet.Document)) ?? spanSet.FileKey);
+ spanSet.Dispose();
}
- catch
- {
- //This shouldn't happen (we are generally passed names associated with documents that we are expecting to open so
- //we should have access). If we fail, we will, at worst not get the same underlying document when people create
- //persistent spans using unnormalized names.
- _fileName = fileName;
- }
-
- _hashCode = StringComparer.OrdinalIgnoreCase.GetHashCode(_fileName);
- }
-
- //Override equality and hash code
- public override int GetHashCode()
- {
- return _hashCode;
- }
-
- public override bool Equals(object obj)
- {
- var other = obj as FileNameKey;
- return (other != null) && string.Equals(_fileName, other._fileName, StringComparison.OrdinalIgnoreCase);
- }
-
- public override string ToString()
- {
- return _fileName;
}
}
}
diff --git a/src/Text/Impl/TextModel/PersistentSpanSet.cs b/src/Text/Impl/TextModel/PersistentSpanSet.cs
new file mode 100644
index 0000000..8370eeb
--- /dev/null
+++ b/src/Text/Impl/TextModel/PersistentSpanSet.cs
@@ -0,0 +1,125 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+//
+// This file contain implementations details that are subject to change without notice.
+// Use at your own risk.
+//
+namespace Microsoft.VisualStudio.Text.Implementation
+{
+ using System;
+ using System.Collections.Generic;
+
+ sealed class PersistentSpanSet : IDisposable
+ {
+ internal FileNameKey FileKey;
+ internal ITextDocument Document;
+ internal readonly HashSet<PersistentSpan> Spans = new HashSet<PersistentSpan>();
+ private readonly PersistentSpanFactory Factory;
+
+ private ITextSnapshot _savedSnapshot = null;
+
+ internal PersistentSpanSet(FileNameKey filePath, ITextDocument document, PersistentSpanFactory factory)
+ {
+ this.FileKey = filePath;
+ this.Document = document;
+ this.Factory = factory;
+
+ if (document != null)
+ {
+ document.FileActionOccurred += this.OnFileActionOccurred;
+ }
+ }
+
+ public void Dispose()
+ {
+ Assumes.True(this.Spans.Count == 0);
+
+ if (this.Document != null)
+ {
+ this.Document.FileActionOccurred -= this.OnFileActionOccurred;
+ this.Document = null;
+ }
+ }
+
+ internal PersistentSpan Create(int startLine, int startIndex, int endLine, int endIndex, SpanTrackingMode trackingMode)
+ {
+ PersistentSpan persistentSpan = new PersistentSpan(startLine, startIndex, endLine, endIndex, trackingMode, this);
+ this.Spans.Add(persistentSpan);
+ return persistentSpan;
+ }
+
+ internal PersistentSpan Create(Span span, SpanTrackingMode trackingMode)
+ {
+ var persistentSpan = new PersistentSpan(span, trackingMode, this);
+ this.Spans.Add(persistentSpan);
+ return persistentSpan;
+ }
+
+ internal PersistentSpan Create(ITextSnapshot snapshot, int startLine, int startIndex, int endLine, int endIndex, SpanTrackingMode trackingMode)
+ {
+ var start = PersistentSpan.LineIndexToSnapshotPoint(startLine, startIndex, snapshot);
+ var end = PersistentSpan.LineIndexToSnapshotPoint(endLine, endIndex, snapshot);
+ if (end < start)
+ {
+ end = start;
+ }
+
+ return this.Create(new SnapshotSpan(start, end), trackingMode);
+ }
+
+ internal PersistentSpan Create(SnapshotSpan span, SpanTrackingMode trackingMode)
+ {
+ var persistentSpan = new PersistentSpan(span, trackingMode, this);
+ this.Spans.Add(persistentSpan);
+ return persistentSpan;
+ }
+
+ internal void Delete(PersistentSpan span)
+ {
+ this.Factory.Delete(this, span);
+ }
+
+ internal void DocumentReopened(ITextDocument document)
+ {
+ Requires.NotNull(document, nameof(document));
+ Assumes.Null(this.Document);
+
+ this.Document = document;
+ document.FileActionOccurred += this.OnFileActionOccurred;
+
+ foreach (var s in this.Spans)
+ {
+ s.DocumentReopened();
+ }
+ }
+
+ internal void DocumentClosed()
+ {
+ Assumes.NotNull(this.Document);
+
+ this.FileKey = new FileNameKey(this.Document.FilePath);
+
+ foreach (var s in this.Spans)
+ {
+ s.DocumentClosed(_savedSnapshot);
+ }
+
+ this.Document.FileActionOccurred -= this.OnFileActionOccurred;
+ this.Document = null;
+ }
+
+ private void OnFileActionOccurred(object sender, TextDocumentFileActionEventArgs e)
+ {
+ if (e.FileActionType == FileActionTypes.ContentSavedToDisk)
+ {
+ _savedSnapshot = this.Document.TextBuffer.CurrentSnapshot;
+ }
+ else if (e.FileActionType == FileActionTypes.DocumentRenamed)
+ {
+ this.FileKey = new FileNameKey(this.Document.FilePath);
+ this.Factory.DocumentRenamed(this);
+ }
+ }
+ }
+}
diff --git a/src/Text/Impl/TextModel/Projection/BaseProjectionBuffer.cs b/src/Text/Impl/TextModel/Projection/BaseProjectionBuffer.cs
index 5dbdfc3..4a3572c 100644
--- a/src/Text/Impl/TextModel/Projection/BaseProjectionBuffer.cs
+++ b/src/Text/Impl/TextModel/Projection/BaseProjectionBuffer.cs
@@ -17,6 +17,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
using Microsoft.VisualStudio.Text.Differencing;
using Microsoft.VisualStudio.Text.Utilities;
using System.Collections.ObjectModel;
+ using System.Globalization;
internal abstract class BaseProjectionBuffer : BaseBuffer, IProjectionBufferBase
{
@@ -184,7 +185,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
#endregion
#region Debug support
- [Conditional("_DEBUG")]
+ [Conditional("DEBUG")]
protected void DumpPendingChanges(List<Tuple<ITextBuffer, List<TextChange>>> pendingSourceChanges)
{
if (BufferGroup.Tracing)
@@ -203,7 +204,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
}
}
- [Conditional("_DEBUG")]
+ [Conditional("DEBUG")]
protected void DumpPendingContentChangedEventArgs()
{
if (BufferGroup.Tracing)
@@ -213,7 +214,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
sb.Append(TextUtilities.GetTag(args.Before.TextBuffer));
sb.Append(" V");
- sb.AppendLine(args.After.Version.VersionNumber.ToString());
+ sb.AppendLine(args.After.Version.VersionNumber.ToString(CultureInfo.InvariantCulture));
foreach (var change in args.Changes)
{
sb.AppendLine(change.ToString());
diff --git a/src/Text/Impl/TextModel/Projection/BufferGraph.cs b/src/Text/Impl/TextModel/Projection/BufferGraph.cs
index 87f42e8..da4d4eb 100644
--- a/src/Text/Impl/TextModel/Projection/BufferGraph.cs
+++ b/src/Text/Impl/TextModel/Projection/BufferGraph.cs
@@ -29,11 +29,11 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (topBuffer == null)
{
- throw new ArgumentNullException("topBuffer");
+ throw new ArgumentNullException(nameof(topBuffer));
}
if (guardedOperations == null)
{
- throw new ArgumentNullException("guardedOperations");
+ throw new ArgumentNullException(nameof(guardedOperations));
}
this.topBuffer = topBuffer;
@@ -69,7 +69,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (match == null)
{
- throw new ArgumentNullException("match");
+ throw new ArgumentNullException(nameof(match));
}
FrugalList<ITextBuffer> buffers = new FrugalList<ITextBuffer>();
foreach (ITextBuffer buffer in this.importingProjectionBufferMap.Keys)
@@ -100,19 +100,19 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (position.Snapshot == null)
{
- throw new ArgumentNullException("position");
+ throw new ArgumentNullException(nameof(position));
}
if (trackingMode < PointTrackingMode.Positive || trackingMode > PointTrackingMode.Negative)
{
- throw new ArgumentOutOfRangeException("trackingMode");
+ throw new ArgumentOutOfRangeException(nameof(trackingMode));
}
if (match == null)
{
- throw new ArgumentNullException("match");
+ throw new ArgumentNullException(nameof(match));
}
if (affinity < PositionAffinity.Predecessor || affinity > PositionAffinity.Successor)
{
- throw new ArgumentOutOfRangeException("affinity");
+ throw new ArgumentOutOfRangeException(nameof(affinity));
}
if (!this.importingProjectionBufferMap.ContainsKey(position.Snapshot.TextBuffer))
{
@@ -146,15 +146,15 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (position.Snapshot == null)
{
- throw new ArgumentNullException("position");
+ throw new ArgumentNullException(nameof(position));
}
if (trackingMode < PointTrackingMode.Positive || trackingMode > PointTrackingMode.Negative)
{
- throw new ArgumentOutOfRangeException("trackingMode");
+ throw new ArgumentOutOfRangeException(nameof(trackingMode));
}
if (match == null)
{
- throw new ArgumentNullException("match");
+ throw new ArgumentNullException(nameof(match));
}
ITextBuffer currentBuffer = position.Snapshot.TextBuffer;
@@ -184,19 +184,19 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (position.Snapshot == null)
{
- throw new ArgumentNullException("position");
+ throw new ArgumentNullException(nameof(position));
}
if (trackingMode < PointTrackingMode.Positive || trackingMode > PointTrackingMode.Negative)
{
- throw new ArgumentOutOfRangeException("trackingMode");
+ throw new ArgumentOutOfRangeException(nameof(trackingMode));
}
if (targetBuffer == null)
{
- throw new ArgumentNullException("targetBuffer");
+ throw new ArgumentNullException(nameof(targetBuffer));
}
if (affinity < PositionAffinity.Predecessor || affinity > PositionAffinity.Successor)
{
- throw new ArgumentOutOfRangeException("affinity");
+ throw new ArgumentOutOfRangeException(nameof(affinity));
}
ITextBuffer currentBuffer = position.Snapshot.TextBuffer;
@@ -228,7 +228,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (targetSnapshot == null)
{
- throw new ArgumentNullException("targetSnapshot");
+ throw new ArgumentNullException(nameof(targetSnapshot));
}
SnapshotPoint? result = MapDownToBuffer(position, trackingMode, targetSnapshot.TextBuffer, affinity);
@@ -250,7 +250,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (targetSnapshot == null)
{
- throw new ArgumentNullException("targetSnapshot");
+ throw new ArgumentNullException(nameof(targetSnapshot));
}
SnapshotPoint? result = MapUpToBuffer(position, trackingMode, affinity, targetSnapshot.TextBuffer);
@@ -266,7 +266,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (match == null)
{
- throw new ArgumentNullException("match");
+ throw new ArgumentNullException(nameof(match));
}
return CheckedMapUpToBuffer(point, trackingMode, match, affinity);
}
@@ -275,15 +275,15 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (point.Snapshot == null)
{
- throw new ArgumentNullException("point");
+ throw new ArgumentNullException(nameof(point));
}
if (trackingMode < PointTrackingMode.Positive || trackingMode > PointTrackingMode.Negative)
{
- throw new ArgumentOutOfRangeException("trackingMode");
+ throw new ArgumentOutOfRangeException(nameof(trackingMode));
}
if (affinity < PositionAffinity.Predecessor || affinity > PositionAffinity.Successor)
{
- throw new ArgumentOutOfRangeException("affinity");
+ throw new ArgumentOutOfRangeException(nameof(affinity));
}
if (!this.importingProjectionBufferMap.ContainsKey(point.Snapshot.TextBuffer))
@@ -328,15 +328,15 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (span.Snapshot == null)
{
- throw new ArgumentNullException("span");
+ throw new ArgumentNullException(nameof(span));
}
if (trackingMode < SpanTrackingMode.EdgeExclusive || trackingMode > SpanTrackingMode.EdgeNegative)
{
- throw new ArgumentOutOfRangeException("trackingMode");
+ throw new ArgumentOutOfRangeException(nameof(trackingMode));
}
if (match == null)
{
- throw new ArgumentNullException("match");
+ throw new ArgumentNullException(nameof(match));
}
if (!this.importingProjectionBufferMap.ContainsKey(span.Snapshot.TextBuffer))
@@ -372,7 +372,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (targetBuffer == null)
{
- throw new ArgumentNullException("targetBuffer");
+ throw new ArgumentNullException(nameof(targetBuffer));
}
if (!this.importingProjectionBufferMap.ContainsKey(targetBuffer))
@@ -389,7 +389,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (targetSnapshot == null)
{
- throw new ArgumentNullException("targetSnapshot");
+ throw new ArgumentNullException(nameof(targetSnapshot));
}
NormalizedSnapshotSpanCollection results = MapDownToBuffer(span, trackingMode, targetSnapshot.TextBuffer);
@@ -411,7 +411,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (targetSnapshot == null)
{
- throw new ArgumentNullException("targetSnapshot");
+ throw new ArgumentNullException(nameof(targetSnapshot));
}
NormalizedSnapshotSpanCollection results = MapUpToBuffer(span, trackingMode, targetSnapshot.TextBuffer);
@@ -482,7 +482,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (match == null)
{
- throw new ArgumentNullException("match");
+ throw new ArgumentNullException(nameof(match));
}
return CheckedMapUpToBuffer(span, trackingMode, match);
}
@@ -491,11 +491,11 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (span.Snapshot == null)
{
- throw new ArgumentNullException("span");
+ throw new ArgumentNullException(nameof(span));
}
if (trackingMode < SpanTrackingMode.EdgeExclusive || trackingMode > SpanTrackingMode.EdgeNegative)
{
- throw new ArgumentOutOfRangeException("trackingMode");
+ throw new ArgumentOutOfRangeException(nameof(trackingMode));
}
ITextBuffer buffer = span.Snapshot.TextBuffer;
if (!this.importingProjectionBufferMap.ContainsKey(buffer))
diff --git a/src/Text/Impl/TextModel/Projection/BufferGraphFactoryService.cs b/src/Text/Impl/TextModel/Projection/BufferGraphFactoryService.cs
index 7c47c06..f4b1cc1 100644
--- a/src/Text/Impl/TextModel/Projection/BufferGraphFactoryService.cs
+++ b/src/Text/Impl/TextModel/Projection/BufferGraphFactoryService.cs
@@ -21,7 +21,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (textBuffer == null)
{
- throw new ArgumentNullException("textBuffer");
+ throw new ArgumentNullException(nameof(textBuffer));
}
return textBuffer.Properties.GetOrCreateSingletonProperty<BufferGraph>(() => (new BufferGraph(textBuffer, GuardedOperations)));
}
diff --git a/src/Text/Impl/TextModel/Projection/ElisionBuffer.cs b/src/Text/Impl/TextModel/Projection/ElisionBuffer.cs
index f47748e..6d30821 100644
--- a/src/Text/Impl/TextModel/Projection/ElisionBuffer.cs
+++ b/src/Text/Impl/TextModel/Projection/ElisionBuffer.cs
@@ -123,6 +123,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
this.sourceBuffer = sourceBuffer;
this.sourceSnapshot = sourceBuffer.CurrentSnapshot;
+ Debug.Assert(sourceBuffer is BaseBuffer);
BaseBuffer baseSourceBuffer = (BaseBuffer)sourceBuffer;
this.eventHook = new WeakEventHook(this, baseSourceBuffer);
@@ -219,11 +220,11 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if ((spansToElide.Count > 0) && (spansToElide[spansToElide.Count - 1].End > this.elBuffer.sourceSnapshot.Length))
{
- throw new ArgumentOutOfRangeException("spansToElide");
+ throw new ArgumentOutOfRangeException(nameof(spansToElide));
}
if ((spansToExpand.Count > 0) && (spansToExpand[spansToExpand.Count - 1].End > this.elBuffer.sourceSnapshot.Length))
{
- throw new ArgumentOutOfRangeException("spansToExpand");
+ throw new ArgumentOutOfRangeException(nameof(spansToExpand));
}
ElisionSourceSpansChangedEventArgs args = this.elBuffer.ApplySpanChanges(spansToElide, spansToExpand);
if (args != null)
@@ -251,7 +252,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (spansToElide == null)
{
- throw new ArgumentNullException("spansToElide");
+ throw new ArgumentNullException(nameof(spansToElide));
}
return ModifySpans(spansToElide, null);
}
@@ -260,7 +261,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (spansToExpand == null)
{
- throw new ArgumentNullException("spansToExpand");
+ throw new ArgumentNullException(nameof(spansToExpand));
}
return ModifySpans(null, spansToExpand);
}
diff --git a/src/Text/Impl/TextModel/Projection/ElisionSnapshot.cs b/src/Text/Impl/TextModel/Projection/ElisionSnapshot.cs
index 36b0779..8c70f24 100644
--- a/src/Text/Impl/TextModel/Projection/ElisionSnapshot.cs
+++ b/src/Text/Impl/TextModel/Projection/ElisionSnapshot.cs
@@ -91,7 +91,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (textBuffer == null)
{
- throw new ArgumentNullException("textBuffer");
+ throw new ArgumentNullException(nameof(textBuffer));
}
return this.sourceSnapshot.TextBuffer == textBuffer ? this.sourceSnapshot : null;
}
@@ -100,7 +100,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (textBuffer == null)
{
- throw new ArgumentNullException("textBuffer");
+ throw new ArgumentNullException(nameof(textBuffer));
}
if (this.sourceSnapshot.TextBuffer == textBuffer)
@@ -121,7 +121,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (match == null)
{
- throw new ArgumentNullException("match");
+ throw new ArgumentNullException(nameof(match));
}
if (match(this.sourceSnapshot.TextBuffer))
@@ -142,11 +142,11 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (startSpanIndex < 0)
{
- throw new ArgumentOutOfRangeException("startSpanIndex");
+ throw new ArgumentOutOfRangeException(nameof(startSpanIndex));
}
if (count < 0 || startSpanIndex + count > SpanCount)
{
- throw new ArgumentOutOfRangeException("count");
+ throw new ArgumentOutOfRangeException(nameof(count));
}
return new ReadOnlyCollection<SnapshotSpan>(this.content.GetSourceSpans(this.sourceSnapshot, startSpanIndex, count));
}
@@ -162,7 +162,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (position < 0 || position > this.totalLength)
{
- throw new ArgumentOutOfRangeException("position");
+ throw new ArgumentOutOfRangeException(nameof(position));
}
FrugalList<SnapshotPoint> points = this.content.MapInsertionPointToSourceSnapshots(this, position);
if (points.Count == 1)
@@ -183,11 +183,11 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (position < 0 || position > this.totalLength)
{
- throw new ArgumentOutOfRangeException("position");
+ throw new ArgumentOutOfRangeException(nameof(position));
}
if (affinity < PositionAffinity.Predecessor || affinity > PositionAffinity.Successor)
{
- throw new ArgumentOutOfRangeException("affinity");
+ throw new ArgumentOutOfRangeException(nameof(affinity));
}
return this.content.MapToSourceSnapshot(this.sourceSnapshot, position, affinity);
}
@@ -200,7 +200,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
}
if (affinity < PositionAffinity.Predecessor || affinity > PositionAffinity.Successor)
{
- throw new ArgumentOutOfRangeException("affinity");
+ throw new ArgumentOutOfRangeException(nameof(affinity));
}
return this.content.MapFromSourceSnapshot(this, point.Position);
}
@@ -209,7 +209,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (span.End > this.totalLength)
{
- throw new ArgumentOutOfRangeException("span");
+ throw new ArgumentOutOfRangeException(nameof(span));
}
FrugalList<SnapshotSpan> result = new FrugalList<SnapshotSpan>();
if (fillIn)
diff --git a/src/Text/Impl/TextModel/Projection/ProjectionBuffer.cs b/src/Text/Impl/TextModel/Projection/ProjectionBuffer.cs
index 67b747e..368db2f 100644
--- a/src/Text/Impl/TextModel/Projection/ProjectionBuffer.cs
+++ b/src/Text/Impl/TextModel/Projection/ProjectionBuffer.cs
@@ -554,10 +554,8 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
foreach (object spanToInsert in this.RawSpansToInsert)
{
- if (spanToInsert == null)
- {
- throw new ArgumentNullException("spansToInsert");
- }
+ Requires.NotNull(spanToInsert, nameof(spanToInsert));
+
ITrackingSpan trackingSpanToInsert = spanToInsert as ITrackingSpan;
if (trackingSpanToInsert != null)
{
@@ -731,15 +729,15 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (position < 0 || position > this.projBuffer.sourceSpans.Count)
{
- throw new ArgumentOutOfRangeException("position");
+ throw new ArgumentOutOfRangeException(nameof(position));
}
if (spansToReplace < 0 || position + spansToReplace > this.projBuffer.sourceSpans.Count)
{
- throw new ArgumentOutOfRangeException("spansToReplace");
+ throw new ArgumentOutOfRangeException(nameof(spansToReplace));
}
if (spansToInsert == null)
{
- throw new ArgumentNullException("spansToInsert");
+ throw new ArgumentNullException(nameof(spansToInsert));
}
this.spanManager = new SpanManager(this.projBuffer, position, spansToReplace, spansToInsert, true, (this.projBuffer.bufferOptions & ProjectionBufferOptions.WritableLiteralSpans) != 0);
@@ -1596,12 +1594,15 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
}
}
+#pragma warning disable CA1801 // Review unused parameters
private StringRebuilder InsertionLiesInCustomSpan(ITextSnapshot afterSourceSnapshot,
int spanPosition,
ITextChange incomingChange,
HashSet<SnapshotPoint> urPoints,
int accumulatedDelta)
{
+#pragma warning disable CA1801 // Review unused parameters
+
// just evaluate the new span and see if it overlaps the insertion.
ITrackingSpan sourceTrackingSpan = this.sourceSpans[spanPosition];
SnapshotSpan afterSpan = sourceTrackingSpan.GetSpan(afterSourceSnapshot);
diff --git a/src/Text/Impl/TextModel/Projection/ProjectionSnapshot.cs b/src/Text/Impl/TextModel/Projection/ProjectionSnapshot.cs
index 5414af1..67a4473 100644
--- a/src/Text/Impl/TextModel/Projection/ProjectionSnapshot.cs
+++ b/src/Text/Impl/TextModel/Projection/ProjectionSnapshot.cs
@@ -17,6 +17,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
using Microsoft.VisualStudio.Text.Utilities;
using Strings = Microsoft.VisualStudio.Text.Implementation.Strings;
+ using System.Globalization;
internal partial class ProjectionSnapshot : BaseProjectionSnapshot, IProjectionSnapshot
{
@@ -192,7 +193,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (textBuffer == null)
{
- throw new ArgumentNullException("textBuffer");
+ throw new ArgumentNullException(nameof(textBuffer));
}
foreach (ITextSnapshot snappy in this.sourceSnapshotMap.Keys)
{
@@ -208,7 +209,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (textBuffer == null)
{
- throw new ArgumentNullException("textBuffer");
+ throw new ArgumentNullException(nameof(textBuffer));
}
foreach (ITextSnapshot snappy in this.sourceSnapshotMap.Keys)
{
@@ -233,7 +234,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (match == null)
{
- throw new ArgumentNullException("match");
+ throw new ArgumentNullException(nameof(match));
}
foreach (ITextSnapshot snappy in this.sourceSnapshotMap.Keys)
{
@@ -263,11 +264,11 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (startSpanIndex < 0 || startSpanIndex > this.SpanCount)
{
- throw new ArgumentOutOfRangeException("startSpanIndex");
+ throw new ArgumentOutOfRangeException(nameof(startSpanIndex));
}
if (count < 0 || startSpanIndex + count > this.SpanCount)
{
- throw new ArgumentOutOfRangeException("count");
+ throw new ArgumentOutOfRangeException(nameof(count));
}
// better using iterator or explicit successor func eventually
@@ -290,7 +291,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (span.End > this.Length)
{
- throw new ArgumentOutOfRangeException("span");
+ throw new ArgumentOutOfRangeException(nameof(span));
}
FrugalList<SnapshotSpan> mappedSpans = new FrugalList<SnapshotSpan>();
@@ -464,7 +465,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (position < 0 || position > this.totalLength)
{
- throw new ArgumentOutOfRangeException("position");
+ throw new ArgumentOutOfRangeException(nameof(position));
}
ReadOnlyCollection<SnapshotPoint> points = this.MapInsertionPointToSourceSnapshots(position, this.projectionBuffer.literalBuffer); // should this be conditional on writable literal buffer?
if (points.Count == 1)
@@ -485,11 +486,11 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (position < 0 || position > this.Length)
{
- throw new ArgumentOutOfRangeException("position");
+ throw new ArgumentOutOfRangeException(nameof(position));
}
if (affinity < PositionAffinity.Predecessor || affinity > PositionAffinity.Successor)
{
- throw new ArgumentOutOfRangeException("affinity");
+ throw new ArgumentOutOfRangeException(nameof(affinity));
}
int rover = affinity == PositionAffinity.Predecessor ? FindLowestSpanIndexOfPosition(position)
@@ -508,7 +509,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (affinity < PositionAffinity.Predecessor || affinity > PositionAffinity.Successor)
{
- throw new ArgumentOutOfRangeException("affinity");
+ throw new ArgumentOutOfRangeException(nameof(affinity));
}
List<InvertedSource> orderedSources;
@@ -576,7 +577,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
{
if (position < 0 || position > this.Length)
{
- throw new ArgumentOutOfRangeException("position");
+ throw new ArgumentOutOfRangeException(nameof(position));
}
int rover = FindLowestSpanIndexOfPosition(position);
@@ -740,7 +741,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
"{0,12} {1,10} {2,4} {3,12} {4}\r\n",
new Span(cumulativeLength, sourceSpan.Length),
TextUtilities.GetTagOrContentType(sourceSpan.Snapshot.TextBuffer),
- "V" + sourceSpan.Snapshot.Version.VersionNumber.ToString(),
+ "V" + sourceSpan.Snapshot.Version.VersionNumber.ToString(CultureInfo.InvariantCulture),
sourceSpan.Span,
TextUtilities.Escape(sourceSpan.GetText()));
cumulativeLength += sourceSpan.Length;
diff --git a/src/Text/Impl/TextModel/Projection/ProjectionSpanToChangeConverter.cs b/src/Text/Impl/TextModel/Projection/ProjectionSpanToChangeConverter.cs
index 06b0306..9e62620 100644
--- a/src/Text/Impl/TextModel/Projection/ProjectionSpanToChangeConverter.cs
+++ b/src/Text/Impl/TextModel/Projection/ProjectionSpanToChangeConverter.cs
@@ -48,7 +48,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
private void ConstructChanges()
{
- IDifferenceCollection<SnapshotSpan> diffs = differ.GetDifferences();
+ var diffs = differ.GetDifferences();
List<TextChange> changes = new List<TextChange>();
int pos = this.textPosition;
@@ -56,10 +56,10 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
// each difference generates a text change
foreach (Difference diff in diffs)
{
- pos += GetMatchSize(differ.DeletedSpans, diff.Before);
+ pos += GetMatchSize(diffs.LeftSequence, diff.Before);
TextChange change = TextChange.Create(pos,
- BufferFactoryService.StringRebuilderFromSnapshotSpans(differ.DeletedSpans, diff.Left),
- BufferFactoryService.StringRebuilderFromSnapshotSpans(differ.InsertedSpans, diff.Right),
+ BufferFactoryService.StringRebuilderFromSnapshotSpans(diffs.LeftSequence, diff.Left),
+ BufferFactoryService.StringRebuilderFromSnapshotSpans(diffs.RightSequence, diff.Right),
this.currentSnapshot);
changes.Add(change);
pos += change.OldLength;
@@ -67,7 +67,7 @@ namespace Microsoft.VisualStudio.Text.Projection.Implementation
this.normalizedChanges = NormalizedTextChangeCollection.Create(changes);
}
- private static int GetMatchSize(ReadOnlyCollection<SnapshotSpan> spans, Match match)
+ private static int GetMatchSize(IList<SnapshotSpan> spans, Match match)
{
int size = 0;
if (match != null)
diff --git a/src/Text/Impl/TextModel/Storage/CharStream.cs b/src/Text/Impl/TextModel/Storage/CharStream.cs
index 2179e9d..9a8e0f8 100644
--- a/src/Text/Impl/TextModel/Storage/CharStream.cs
+++ b/src/Text/Impl/TextModel/Storage/CharStream.cs
@@ -119,12 +119,12 @@ namespace Microsoft.VisualStudio.Text.Implementation
}
}
- private char Make(byte hi, byte lo)
+ private static char Make(byte hi, byte lo)
{
return (char)((hi << 8) | lo);
}
- private void Split(char c, out byte hi, out byte lo)
+ private static void Split(char c, out byte hi, out byte lo)
{
hi = (byte)(c >> 8);
lo = (byte)(c & 255);
diff --git a/src/Text/Impl/TextModel/Storage/ILineBreaks.cs b/src/Text/Impl/TextModel/Storage/ILineBreaks.cs
index ccf1358..2100860 100644
--- a/src/Text/Impl/TextModel/Storage/ILineBreaks.cs
+++ b/src/Text/Impl/TextModel/Storage/ILineBreaks.cs
@@ -32,4 +32,16 @@ namespace Microsoft.VisualStudio.Text.Implementation
/// </summary>
void Add(int start, int length);
}
+
+ public interface IPooledLineBreaksEditor : ILineBreaksEditor
+ {
+ /// <summary>
+ /// If the internal list of line breaks has excess capacity, copy it to a correctly sized list and return the oversized
+ /// list to a pool that can be reused.
+ /// </summary>
+ /// <remarks>
+ /// This method should be called when using calling <see cref="LineBreakManager.CreatePooledLineBreakEditor(int)"/>.
+ /// </remarks>
+ void ReleasePooledLineBreaks();
+ }
}
diff --git a/src/Text/Impl/TextModel/Storage/LineBreakManager.cs b/src/Text/Impl/TextModel/Storage/LineBreakManager.cs
index 2d3071b..f12a9a4 100644
--- a/src/Text/Impl/TextModel/Storage/LineBreakManager.cs
+++ b/src/Text/Impl/TextModel/Storage/LineBreakManager.cs
@@ -1,23 +1,39 @@
using System;
-using System.Collections.Generic;
+using System.Threading;
using Microsoft.VisualStudio.Text.Utilities;
namespace Microsoft.VisualStudio.Text.Implementation
{
public static class LineBreakManager
{
- public readonly static ILineBreaks Empty = new ShortLineBreaksEditor(0);
+ public readonly static ILineBreaks Empty = new ShortLineBreaksEditor(Array.Empty<ushort>());
+
+ /// <summary>
+ /// Create a line break editor using the pooled line break lists (which should have excess capacity).
+ /// </summary>
+ /// <remarks>
+ /// <para>ILineBreakEditor.ReleasePooledLineBreaks() should be called on the returne editors once all line breaks have been added.</para>
+ /// <para>Note that this method is thread-safe. If multiple PooledLineBreakEditor are created simultaneously on different threads then
+ /// only one will use the pooled line breaks (and the others will get freshly allocated line breaks).</para>
+ /// </remarks>
+ public static IPooledLineBreaksEditor CreatePooledLineBreakEditor(int maxLength)
+ {
+ return (maxLength <= short.MaxValue)
+ ? (IPooledLineBreaksEditor)(new ShortLineBreaksEditor(LineBreakListManager<ushort>.AcquireLineBreaks(ShortLineBreaksEditor.ExpectedNumberOfLines)))
+ : (IPooledLineBreaksEditor)(new IntLineBreaksEditor(LineBreakListManager<int>.AcquireLineBreaks(IntLineBreaksEditor.ExpectedNumberOfLines)));
+ }
- public static ILineBreaksEditor CreateLineBreakEditor(int maxLength, int initialCapacity = 0)
+ // Create a line break editor using an allocated list (which should be sized to hold all the expected line breaks without reallocations),
+ public static ILineBreaksEditor CreateLineBreakEditor(int maxLength, int initialCapacity)
{
return (maxLength < short.MaxValue)
- ? (ILineBreaksEditor)(new ShortLineBreaksEditor(initialCapacity))
- : (ILineBreaksEditor)(new IntLineBreaksEditor(initialCapacity));
+ ? (ILineBreaksEditor)(new ShortLineBreaksEditor(new ushort[initialCapacity]))
+ : (ILineBreaksEditor)(new IntLineBreaksEditor(new int[initialCapacity]));
}
public static ILineBreaks CreateLineBreaks(string source)
{
- ILineBreaksEditor lineBreaks = null;
+ IPooledLineBreaksEditor lineBreaks = null;
int index = 0;
while (index < source.Length)
@@ -30,112 +46,167 @@ namespace Microsoft.VisualStudio.Text.Implementation
else
{
if (lineBreaks == null)
- lineBreaks = LineBreakManager.CreateLineBreakEditor(source.Length);
+ lineBreaks = LineBreakManager.CreatePooledLineBreakEditor(source.Length);
lineBreaks.Add(index, breakLength);
index += breakLength;
}
}
- return lineBreaks ?? Empty;
+ if (lineBreaks != null)
+ {
+ lineBreaks.ReleasePooledLineBreaks();
+ return lineBreaks;
+ }
+
+ return Empty;
}
- private class ShortLineBreaksEditor : ILineBreaksEditor
+ internal abstract class LineBreakListManager<T> : IPooledLineBreaksEditor
{
- private const ushort MaskForPosition = 0x7fff;
- private const ushort MaskForLength = 0x8000;
+ internal static T[] _pooledLineBreaks = null;
+
+ internal protected T[] LineBreaks;
+
+ private int _length;
- private readonly static List<ushort> Empty = new List<ushort>(0);
- private List<ushort> _lineBreaks = Empty;
+ public int Length => _length;
- public ShortLineBreaksEditor(int initialCapacity)
+ public LineBreakListManager(T[] lineBreaks)
{
- if (initialCapacity > 0)
- _lineBreaks = new List<ushort>(initialCapacity);
+ this.LineBreaks = lineBreaks;
}
- public int Length => _lineBreaks.Count;
+ protected void Add(T value)
+ {
+ if (_length == this.LineBreaks.Length)
+ {
+ // Simulate a List.Add()
+ var newLineBreaks = new T[_length * 2];
+ Array.Copy(this.LineBreaks, newLineBreaks, _length);
- public int LengthOfLineBreak(int index)
+ this.LineBreaks = newLineBreaks;
+ }
+
+ this.LineBreaks[_length++] = value;
+ }
+
+ // In single threaded operations, we'll always be getting/reusing the same list of line breaks. We, however, need to handle
+ // the case of a file being simultaneously read on multiple threads (at which point one thread will get the pooled list,
+ // the other threads will allocate, and the largest list will end up back in the shared pool).
+ internal static T[] AcquireLineBreaks(int size)
{
- return ((_lineBreaks[index] & MaskForLength) != 0 ? 2 : 1);
+ T[] buffer = Volatile.Read(ref _pooledLineBreaks);
+ if (buffer != null && buffer.Length >= size)
+ {
+ if (buffer == Interlocked.CompareExchange(ref _pooledLineBreaks, null, buffer))
+ {
+ return buffer;
+ }
+ }
+
+ return new T[size];
}
- public int StartOfLineBreak(int index)
+ public void ReleasePooledLineBreaks()
{
- return (int)(_lineBreaks[index] & MaskForPosition);
+ if (this.LineBreaks.Length != _length)
+ {
+ // We have excess capacity, so make an accurately sized copy of this.LineBreaks and return it to the pool.
+ T[] newLineBreaks;
+ if (_length > 0)
+ {
+ newLineBreaks = new T[_length];
+ Array.Copy(this.LineBreaks, newLineBreaks, _length);
+ }
+ else
+ {
+ newLineBreaks = Array.Empty<T>();
+ }
+
+ T[] buffer = Volatile.Read(ref _pooledLineBreaks);
+ if ((buffer == null) || (buffer.Length < this.LineBreaks.Length))
+ {
+ // We're done with this.LineBreaks and either there is nothing in the pool or
+ // this.LineBreaks are larger than the array in the pool so replace it with
+ // this.LineBreaks.
+ Interlocked.CompareExchange(ref _pooledLineBreaks, this.LineBreaks, buffer);
+ }
+
+ this.LineBreaks = newLineBreaks;
+ }
}
- public int EndOfLineBreak(int index)
+
+ public abstract void Add(int start, int length);
+ public abstract int StartOfLineBreak(int index);
+ public abstract int EndOfLineBreak(int index);
+ }
+
+ private class ShortLineBreaksEditor : LineBreakListManager<ushort>
+ {
+ public const int ExpectedNumberOfLines = 500; // Guestimate on how many lines will be in a typical 16k block.
+
+ private const ushort MaskForPosition = 0x7fff;
+ private const ushort MaskForLength = 0x8000;
+
+ public ShortLineBreaksEditor(ushort[] lineBreaks)
+ : base(lineBreaks)
+ { }
+
+ public override int StartOfLineBreak(int index)
+ {
+ return (int)(this.LineBreaks[index] & MaskForPosition);
+ }
+
+ public override int EndOfLineBreak(int index)
{
- int lineBreak = _lineBreaks[index];
- return (lineBreak & MaskForPosition) +
+ int lineBreak = this.LineBreaks[index];
+ return (lineBreak & MaskForPosition) +
(((lineBreak & MaskForLength) != 0) ? 2 : 1);
}
- public void Add(int start, int length)
+ public override void Add(int start, int length)
{
if ((start < 0) || (start > short.MaxValue))
throw new ArgumentOutOfRangeException(nameof(start));
if ((length < 1) || (length > 2))
throw new ArgumentOutOfRangeException(nameof(length));
- if (_lineBreaks == Empty)
- _lineBreaks = new List<ushort>();
-
- if (length == 1)
- _lineBreaks.Add((ushort)start);
- else if (length == 2)
- _lineBreaks.Add((ushort)(start | MaskForLength));
+ this.Add((length == 1) ? (ushort)start : (ushort)(start | MaskForLength));
}
}
- private class IntLineBreaksEditor : ILineBreaksEditor
+ private class IntLineBreaksEditor : LineBreakListManager<int>
{
- private const uint MaskForPosition = 0x7fffffff;
- private const uint MaskForLength = 0x80000000;
-
- private readonly static List<uint> Empty = new List<uint>(0);
- private List<uint> _lineBreaks = Empty;
-
- public IntLineBreaksEditor(int initialCapacity)
- {
- if (initialCapacity > 0)
- _lineBreaks = new List<uint>(initialCapacity);
- }
+ public const int ExpectedNumberOfLines = 32000; // Guestimate on how many lines will be in a typical 1MB block.
- public int Length => _lineBreaks.Count;
+ private const int MaskForPosition = int.MaxValue; //0x7fffffff
+ private const int MaskForLength = int.MinValue; //0x80000000 in an int-friendly way
- public int LengthOfLineBreak(int index)
- {
- return (_lineBreaks[index] & MaskForLength) != 0 ? 2 : 1;
- }
+ public IntLineBreaksEditor(int[] lineBreaks)
+ : base(lineBreaks)
+ { }
- public int StartOfLineBreak(int index)
+ public override int StartOfLineBreak(int index)
{
- return (int)(_lineBreaks[index] & MaskForPosition);
+ return (int)(this.LineBreaks[index] & MaskForPosition);
}
- public int EndOfLineBreak(int index)
+ public override int EndOfLineBreak(int index)
{
- uint lineBreak = _lineBreaks[index];
- return (int)((lineBreak & MaskForPosition) +
- (((lineBreak & MaskForLength) != 0) ? 2 : 1));
+ int lineBreak = this.LineBreaks[index];
+ return (lineBreak & MaskForPosition) +
+ (((lineBreak & MaskForLength) != 0) ? 2 : 1);
}
- public void Add(int start, int length)
+ public override void Add(int start, int length)
{
- if ((start < 0) || (start > int.MaxValue))
+ if (start < 0)
throw new ArgumentOutOfRangeException(nameof(start));
if ((length < 1) || (length > 2))
throw new ArgumentOutOfRangeException(nameof(length));
- if (_lineBreaks == Empty)
- _lineBreaks = new List<uint>();
-
- if (length == 1)
- _lineBreaks.Add((uint)start);
- else if (length == 2)
- _lineBreaks.Add((uint)(start | MaskForLength));
+ this.Add((length == 1) ? start : (start | MaskForLength));
}
}
}
diff --git a/src/Text/Impl/TextModel/Storage/TextImageLoader.cs b/src/Text/Impl/TextModel/Storage/TextImageLoader.cs
index 428d29c..da7646d 100644
--- a/src/Text/Impl/TextModel/Storage/TextImageLoader.cs
+++ b/src/Text/Impl/TextModel/Storage/TextImageLoader.cs
@@ -6,7 +6,6 @@
// Use at your own risk.
//
using System;
-using System.Collections.Generic;
using System.IO;
using System.Threading;
using Microsoft.VisualStudio.Text.Utilities;
@@ -17,7 +16,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
public const int BlockSize = 16384;
- internal static StringRebuilder Load(TextReader reader, long fileSize, string id,
+ internal static StringRebuilder Load(TextReader reader, long fileSize,
out bool hasConsistentLineEndings, out int longestLineLength,
int blockSize = 0,
int minCompressedBlockSize = TextImageLoader.BlockSize) // Exposed for unit tests
@@ -52,8 +51,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
if (read == 0)
break;
- var lineBreaks = LineBreakManager.CreateLineBreakEditor(read);
- TextImageLoader.ParseBlock(buffer, read, lineBreaks, ref lineEnding, ref currentLineLength, ref longestLineLength);
+ var lineBreaks = TextImageLoader.ParseBlock(buffer, read, ref lineEnding, ref currentLineLength, ref longestLineLength);
char[] bufferForStringBuilder = buffer;
if (read < (buffer.Length / 2))
@@ -64,7 +62,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
}
else
{
- // We're using most of bufferForStringRebuilder so allocate a new block for the next chunk.
+ // We're using most of buffer so allocate a new block for the next chunk.
buffer = new char[blockSize];
}
@@ -76,7 +74,6 @@ namespace Microsoft.VisualStudio.Text.Implementation
}
longestLineLength = Math.Max(longestLineLength, currentLineLength);
- hasConsistentLineEndings = lineEnding != LineEndingState.Inconsistent;
}
finally
{
@@ -86,6 +83,8 @@ namespace Microsoft.VisualStudio.Text.Implementation
}
}
+ hasConsistentLineEndings = lineEnding != LineEndingState.Inconsistent;
+
return content;
}
@@ -110,9 +109,12 @@ namespace Microsoft.VisualStudio.Text.Implementation
return read;
}
- private static void ParseBlock(char[] buffer, int length, ILineBreaksEditor lineBreaks,
- ref LineEndingState lineEnding, ref int currentLineLength, ref int longestLineLength)
+ private static ILineBreaks ParseBlock(char[] buffer, int length,
+ ref LineEndingState lineEnding, ref int currentLineLength, ref int longestLineLength)
{
+ // Note that the lineBreaks created here will (internally) use the pooled list of line breaks.
+ IPooledLineBreaksEditor lineBreaks = LineBreakManager.CreatePooledLineBreakEditor(length);
+
int index = 0;
while (index < length)
{
@@ -161,6 +163,10 @@ namespace Microsoft.VisualStudio.Text.Implementation
index += breakLength;
}
}
+
+ lineBreaks.ReleasePooledLineBreaks();
+
+ return lineBreaks;
}
internal enum LineEndingState
diff --git a/src/Text/Impl/TextModel/StringRebuilder/BinaryStringRebuilder.cs b/src/Text/Impl/TextModel/StringRebuilder/BinaryStringRebuilder.cs
index e3e3581..26139fc 100644
--- a/src/Text/Impl/TextModel/StringRebuilder/BinaryStringRebuilder.cs
+++ b/src/Text/Impl/TextModel/StringRebuilder/BinaryStringRebuilder.cs
@@ -164,9 +164,9 @@ namespace Microsoft.VisualStudio.Text.Implementation
public static StringRebuilder Create(StringRebuilder left, StringRebuilder right)
{
if (left == null)
- throw new ArgumentNullException("left");
+ throw new ArgumentNullException(nameof(left));
if (right == null)
- throw new ArgumentNullException("right");
+ throw new ArgumentNullException(nameof(right));
if (left.Length == 0)
return right;
@@ -203,7 +203,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
public override int GetLineNumberFromPosition(int position)
{
if ((position < 0) || (position > this.Length))
- throw new ArgumentOutOfRangeException("position");
+ throw new ArgumentOutOfRangeException(nameof(position));
return (position <= _left.Length)
? _left.GetLineNumberFromPosition(position)
@@ -214,7 +214,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
public override void GetLineFromLineNumber(int lineNumber, out Span extent, out int lineBreakLength)
{
if ((lineNumber < 0) || (lineNumber > this.LineBreakCount))
- throw new ArgumentOutOfRangeException("lineNumber");
+ throw new ArgumentOutOfRangeException(nameof(lineNumber));
if (lineNumber < _left.LineBreakCount)
{
@@ -273,7 +273,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
get
{
if ((index < 0) || (index >= this.Length))
- throw new ArgumentOutOfRangeException("index");
+ throw new ArgumentOutOfRangeException(nameof(index));
return (index < _left.Length)
? _left[index]
@@ -284,7 +284,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
public override string GetText(Span span)
{
if (span.End > this.Length)
- throw new ArgumentOutOfRangeException("span");
+ throw new ArgumentOutOfRangeException(nameof(span));
if (span.End <= _left.Length)
return _left.GetText(span);
@@ -338,9 +338,9 @@ namespace Microsoft.VisualStudio.Text.Implementation
public override void Write(TextWriter writer, Span span)
{
if (writer == null)
- throw new ArgumentNullException("writer");
+ throw new ArgumentNullException(nameof(writer));
if (span.End > this.Length)
- throw new ArgumentOutOfRangeException("span");
+ throw new ArgumentOutOfRangeException(nameof(span));
if (span.Start >= _left.Length)
_right.Write(writer, new Span(span.Start - _left.Length, span.Length));
@@ -356,7 +356,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
public override StringRebuilder GetSubText(Span span)
{
if (span.End > this.Length)
- throw new ArgumentOutOfRangeException("span");
+ throw new ArgumentOutOfRangeException(nameof(span));
if (span.Length == this.Length)
return this;
diff --git a/src/Text/Impl/TextModel/StringRebuilder/StringRebuilder.cs b/src/Text/Impl/TextModel/StringRebuilder/StringRebuilder.cs
index 1fe8b63..9134e56 100644
--- a/src/Text/Impl/TextModel/StringRebuilder/StringRebuilder.cs
+++ b/src/Text/Impl/TextModel/StringRebuilder/StringRebuilder.cs
@@ -34,7 +34,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
public static StringRebuilder Create(string text)
{
if (text == null)
- throw new ArgumentNullException("text");
+ throw new ArgumentNullException(nameof(text));
#if DEBUG
Interlocked.Add(ref _totalCharactersScanned, text.Length);
#endif
@@ -258,10 +258,10 @@ namespace Microsoft.VisualStudio.Text.Implementation
public char[] ToCharArray(int startIndex, int length)
{
if (startIndex < 0)
- throw new ArgumentOutOfRangeException("startIndex");
+ throw new ArgumentOutOfRangeException(nameof(startIndex));
if ((length < 0) || (startIndex + length > this.Length) || (startIndex + length < 0))
- throw new ArgumentOutOfRangeException("length");
+ throw new ArgumentOutOfRangeException(nameof(length));
char[] copy = new char[length];
this.CopyTo(startIndex, copy, 0, length);
@@ -331,9 +331,9 @@ namespace Microsoft.VisualStudio.Text.Implementation
public StringRebuilder Insert(int position, StringRebuilder text)
{
if ((position < 0) || (position > this.Length))
- throw new ArgumentOutOfRangeException("position");
+ throw new ArgumentOutOfRangeException(nameof(position));
if (text == null)
- throw new ArgumentNullException("text");
+ throw new ArgumentNullException(nameof(text));
return this.Assemble(Span.FromBounds(0, position), text, Span.FromBounds(position, this.Length));
}
@@ -351,7 +351,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
public StringRebuilder Delete(Span span)
{
if (span.End > this.Length)
- throw new ArgumentOutOfRangeException("span");
+ throw new ArgumentOutOfRangeException(nameof(span));
return this.Assemble(Span.FromBounds(0, span.Start), Span.FromBounds(span.End, this.Length));
}
@@ -402,9 +402,9 @@ namespace Microsoft.VisualStudio.Text.Implementation
public StringRebuilder Replace(Span span, StringRebuilder text)
{
if (span.End > this.Length)
- throw new ArgumentOutOfRangeException("span");
+ throw new ArgumentOutOfRangeException(nameof(span));
if (text == null)
- throw new ArgumentNullException("text");
+ throw new ArgumentNullException(nameof(text));
return this.Assemble(Span.FromBounds(0, span.Start), text, Span.FromBounds(span.End, this.Length));
}
diff --git a/src/Text/Impl/TextModel/StringRebuilder/StringRebuilderForChars.cs b/src/Text/Impl/TextModel/StringRebuilder/StringRebuilderForChars.cs
index a4f4293..70e932a 100644
--- a/src/Text/Impl/TextModel/StringRebuilder/StringRebuilderForChars.cs
+++ b/src/Text/Impl/TextModel/StringRebuilder/StringRebuilderForChars.cs
@@ -71,7 +71,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
public override StringRebuilder GetSubText(Span span)
{
if (span.End > this.Length)
- throw new ArgumentOutOfRangeException("span");
+ throw new ArgumentOutOfRangeException(nameof(span));
if (span.Length == 0)
return StringRebuilder.Empty;
diff --git a/src/Text/Impl/TextModel/StringRebuilder/StringRebuilderForCompressedChars.cs b/src/Text/Impl/TextModel/StringRebuilder/StringRebuilderForCompressedChars.cs
index ca80b09..59dd801 100644
--- a/src/Text/Impl/TextModel/StringRebuilder/StringRebuilderForCompressedChars.cs
+++ b/src/Text/Impl/TextModel/StringRebuilder/StringRebuilderForCompressedChars.cs
@@ -62,7 +62,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
public override StringRebuilder GetSubText(Span span)
{
if (span.End > this.Length)
- throw new ArgumentOutOfRangeException("span");
+ throw new ArgumentOutOfRangeException(nameof(span));
if (span.Length == this.Length)
return this;
diff --git a/src/Text/Impl/TextModel/StringRebuilder/StringRebuilderForString.cs b/src/Text/Impl/TextModel/StringRebuilder/StringRebuilderForString.cs
index 56e5c8a..75838f1 100644
--- a/src/Text/Impl/TextModel/StringRebuilder/StringRebuilderForString.cs
+++ b/src/Text/Impl/TextModel/StringRebuilder/StringRebuilderForString.cs
@@ -96,7 +96,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
public override StringRebuilder GetSubText(Span span)
{
if (span.End > this.Length)
- throw new ArgumentOutOfRangeException("span");
+ throw new ArgumentOutOfRangeException(nameof(span));
if (span.Length == 0)
return StringRebuilder.Empty;
diff --git a/src/Text/Impl/TextModel/StringRebuilder/UnaryStringRebuilder.cs b/src/Text/Impl/TextModel/StringRebuilder/UnaryStringRebuilder.cs
index 3f53283..41f11fb 100644
--- a/src/Text/Impl/TextModel/StringRebuilder/UnaryStringRebuilder.cs
+++ b/src/Text/Impl/TextModel/StringRebuilder/UnaryStringRebuilder.cs
@@ -57,7 +57,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
public override int GetLineNumberFromPosition(int position)
{
if ((position < 0) || (position > this.Length))
- throw new ArgumentOutOfRangeException("position");
+ throw new ArgumentOutOfRangeException(nameof(position));
//Convert position to a position relative to the start of _text.
if (position == this.Length)
@@ -87,7 +87,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
public override void GetLineFromLineNumber(int lineNumber, out Span extent, out int lineBreakLength)
{
if ((lineNumber < 0) || (lineNumber > this.LineBreakCount))
- throw new ArgumentOutOfRangeException("lineNumber");
+ throw new ArgumentOutOfRangeException(nameof(lineNumber));
int absoluteLineNumber = _lineBreakSpanStart + lineNumber;
@@ -122,7 +122,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
protected char GetChar(char[] content, int index)
{
if ((index < 0) || (index >= this.Length))
- throw new ArgumentOutOfRangeException("index");
+ throw new ArgumentOutOfRangeException(nameof(index));
#if DEBUG
Interlocked.Increment(ref _totalCharactersReturned);
@@ -134,7 +134,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
protected string GetText(char[] content, Span span)
{
if (span.End > this.Length)
- throw new ArgumentOutOfRangeException("span");
+ throw new ArgumentOutOfRangeException(nameof(span));
#if DEBUG
Interlocked.Add(ref _totalCharactersReturned, span.Length);
@@ -146,19 +146,19 @@ namespace Microsoft.VisualStudio.Text.Implementation
protected void CopyTo(char[] content, int sourceIndex, char[] destination, int destinationIndex, int count)
{
if (sourceIndex < 0)
- throw new ArgumentOutOfRangeException("sourceIndex");
+ throw new ArgumentOutOfRangeException(nameof(sourceIndex));
if (destination == null)
- throw new ArgumentNullException("destination");
+ throw new ArgumentNullException(nameof(destination));
if (destinationIndex < 0)
- throw new ArgumentOutOfRangeException("destinationIndex");
+ throw new ArgumentOutOfRangeException(nameof(destinationIndex));
if (count < 0)
- throw new ArgumentOutOfRangeException("count");
+ throw new ArgumentOutOfRangeException(nameof(count));
if ((sourceIndex + count > this.Length) || (sourceIndex + count < 0))
- throw new ArgumentOutOfRangeException("count");
+ throw new ArgumentOutOfRangeException(nameof(count));
if ((destinationIndex + count > destination.Length) || (destinationIndex + count < 0))
- throw new ArgumentOutOfRangeException("count");
+ throw new ArgumentOutOfRangeException(nameof(count));
#if DEBUG
Interlocked.Add(ref _totalCharactersCopied, count);
@@ -170,9 +170,9 @@ namespace Microsoft.VisualStudio.Text.Implementation
protected void Write(char[] content, TextWriter writer, Span span)
{
if (writer == null)
- throw new ArgumentNullException("writer");
+ throw new ArgumentNullException(nameof(writer));
if (span.End > this.Length)
- throw new ArgumentOutOfRangeException("span");
+ throw new ArgumentOutOfRangeException(nameof(span));
writer.Write(content, span.Start + _textSpanStart, span.Length);
}
diff --git a/src/Text/Impl/TextModel/TextChange.cs b/src/Text/Impl/TextModel/TextChange.cs
index 972854e..b1f3383 100644
--- a/src/Text/Impl/TextModel/TextChange.cs
+++ b/src/Text/Impl/TextModel/TextChange.cs
@@ -53,7 +53,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (oldPosition < 0)
{
- throw new ArgumentOutOfRangeException("oldPosition");
+ throw new ArgumentOutOfRangeException(nameof(oldPosition));
}
_oldPosition = oldPosition;
@@ -106,7 +106,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (value < 0)
{
- throw new ArgumentOutOfRangeException("value");
+ throw new ArgumentOutOfRangeException(nameof(value));
}
_oldPosition = value;
}
@@ -119,7 +119,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (value < 0)
{
- throw new ArgumentOutOfRangeException("value");
+ throw new ArgumentOutOfRangeException(nameof(value));
}
_newPosition = value;
}
@@ -226,7 +226,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
internal void RecordMasterChangeOffset(int masterChangeOffset)
{
if (masterChangeOffset < 0)
- throw new ArgumentOutOfRangeException("masterChangeOffset", "MasterChangeOffset should be non-negative.");
+ throw new ArgumentOutOfRangeException(nameof(masterChangeOffset), "MasterChangeOffset should be non-negative.");
if (_masterChangeOffset != -1)
throw new InvalidOperationException("MasterChangeOffset has already been set.");
diff --git a/src/Text/Impl/TextModel/TextDocument.cs b/src/Text/Impl/TextModel/TextDocument.cs
index e546e97..62d0f5a 100644
--- a/src/Text/Impl/TextModel/TextDocument.cs
+++ b/src/Text/Impl/TextModel/TextDocument.cs
@@ -15,7 +15,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
using Microsoft.VisualStudio.Utilities;
using Microsoft.VisualStudio.Text.Editor;
- internal partial class TextDocument : ITextDocument
+ internal sealed partial class TextDocument : ITextDocument
{
#region Private Members
@@ -50,19 +50,19 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (textBuffer == null)
{
- throw new ArgumentNullException("textBuffer");
+ throw new ArgumentNullException(nameof(textBuffer));
}
if (filePath == null)
{
- throw new ArgumentNullException("filePath");
+ throw new ArgumentNullException(nameof(filePath));
}
if (textDocumentFactoryService == null)
{
- throw new ArgumentNullException("textDocumentFactoryService");
+ throw new ArgumentNullException(nameof(textDocumentFactoryService));
}
if (encoding == null)
{
- throw new ArgumentNullException("encoding");
+ throw new ArgumentNullException(nameof(encoding));
}
_textBuffer = textBuffer;
@@ -125,7 +125,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
}
if (newFilePath == null)
{
- throw new ArgumentNullException("newFilePath");
+ throw new ArgumentNullException(nameof(newFilePath));
}
_filePath = newFilePath;
@@ -147,7 +147,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
bool hasConsistentLineEndings;
int longestLineLength;
- StringRebuilder newContent = TextImageLoader.Load(streamReader, fileSize, _filePath, out hasConsistentLineEndings, out longestLineLength);
+ StringRebuilder newContent = TextImageLoader.Load(streamReader, fileSize, out hasConsistentLineEndings, out longestLineLength);
if (!hasConsistentLineEndings)
{
@@ -361,11 +361,11 @@ namespace Microsoft.VisualStudio.Text.Implementation
}
if (filePath == null)
{
- throw new ArgumentNullException("filePath");
+ throw new ArgumentNullException(nameof(filePath));
}
PerformSave(overwrite ? FileMode.Create : FileMode.CreateNew, filePath, createFolder);
- UpdateSaveStatus(filePath, _filePath != filePath);
+ UpdateSaveStatus(filePath, !string.Equals(_filePath, filePath, StringComparison.Ordinal));
// file path won't be updated if the save fails (in which case PerformSave will throw an exception)
@@ -376,7 +376,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (newContentType == null)
{
- throw new ArgumentNullException("newContentType");
+ throw new ArgumentNullException(nameof(newContentType));
}
SaveAs(filePath, overwrite, createFolder);
// content type won't be changed if the save fails (in which case SaveAs will throw an exception)
@@ -391,7 +391,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
}
if (filePath == null)
{
- throw new ArgumentNullException("filePath");
+ throw new ArgumentNullException(nameof(filePath));
}
PerformSave(overwrite ? FileMode.Create : FileMode.CreateNew, filePath, createFolder);
@@ -453,7 +453,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (value == null)
{
- throw new ArgumentNullException("value");
+ throw new ArgumentNullException(nameof(value));
}
Encoding oldEncoding = _encoding;
diff --git a/src/Text/Impl/TextModel/TextDocumentFactoryService.cs b/src/Text/Impl/TextModel/TextDocumentFactoryService.cs
index ff59ae3..67c75f9 100644
--- a/src/Text/Impl/TextModel/TextDocumentFactoryService.cs
+++ b/src/Text/Impl/TextModel/TextDocumentFactoryService.cs
@@ -48,17 +48,17 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (filePath == null)
{
- throw new ArgumentNullException("filePath");
+ throw new ArgumentNullException(nameof(filePath));
}
if (contentType == null)
{
- throw new ArgumentNullException("contentType");
+ throw new ArgumentNullException(nameof(contentType));
}
if (encoding == null)
{
- throw new ArgumentNullException("encoding");
+ throw new ArgumentNullException(nameof(encoding));
}
var fallbackDetector = new FallbackDetector(encoding.DecoderFallback);
@@ -191,12 +191,12 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (textBuffer == null)
{
- throw new ArgumentNullException("textBuffer");
+ throw new ArgumentNullException(nameof(textBuffer));
}
if (filePath == null)
{
- throw new ArgumentNullException("filePath");
+ throw new ArgumentNullException(nameof(filePath));
}
TextDocument textDocument = new TextDocument(textBuffer, filePath, DateTime.UtcNow, this, Encoding.UTF8);
@@ -209,7 +209,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (textBuffer == null)
{
- throw new ArgumentNullException("textBuffer");
+ throw new ArgumentNullException(nameof(textBuffer));
}
textDocument = null;
diff --git a/src/Text/Impl/TextModel/TextImageVersion.cs b/src/Text/Impl/TextModel/TextImageVersion.cs
index ef3e44a..783a32a 100644
--- a/src/Text/Impl/TextModel/TextImageVersion.cs
+++ b/src/Text/Impl/TextModel/TextImageVersion.cs
@@ -98,7 +98,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
public int TrackTo(VersionedPosition other, PointTrackingMode mode)
{
if (other.Version == null)
- throw new ArgumentException(nameof(other));
+ throw new ArgumentException(nameof(other) + " version cannot be null");
if (other.Version.VersionNumber == this.VersionNumber)
return other.Position;
@@ -112,7 +112,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
public Span TrackTo(VersionedSpan span, SpanTrackingMode mode)
{
if (span.Version == null)
- throw new ArgumentException(nameof(span));
+ throw new ArgumentException(nameof(span) + " version cannot be null");
if (span.Version.VersionNumber == this.VersionNumber)
return span.Span;
diff --git a/src/Text/Impl/TextModel/TextVersion.cs b/src/Text/Impl/TextModel/TextVersion.cs
index 55727c5..3e16443 100644
--- a/src/Text/Impl/TextModel/TextVersion.cs
+++ b/src/Text/Impl/TextModel/TextVersion.cs
@@ -8,6 +8,7 @@
namespace Microsoft.VisualStudio.Text.Implementation
{
using System;
+ using System.Globalization;
/// <summary>
/// An internal implementation of ITextVersion
@@ -131,7 +132,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
// Forward fidelity is implicit
if (trackingMode == SpanTrackingMode.Custom)
{
- throw new ArgumentOutOfRangeException("trackingMode");
+ throw new ArgumentOutOfRangeException(nameof(trackingMode));
}
return new ForwardFidelityTrackingSpan(this, new Span(start, length), trackingMode);
}
@@ -146,7 +147,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
// Forward fidelity is implicit
if (trackingMode == SpanTrackingMode.Custom)
{
- throw new ArgumentOutOfRangeException("trackingMode");
+ throw new ArgumentOutOfRangeException(nameof(trackingMode));
}
return new ForwardFidelityTrackingSpan(this, span, trackingMode);
}
@@ -155,7 +156,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (trackingMode == SpanTrackingMode.Custom)
{
- throw new ArgumentOutOfRangeException("trackingMode");
+ throw new ArgumentOutOfRangeException(nameof(trackingMode));
}
if (trackingFidelity == TrackingFidelityMode.Forward)
{
@@ -171,7 +172,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (behavior == null)
{
- throw new ArgumentNullException("behavior");
+ throw new ArgumentNullException(nameof(behavior));
}
if (trackingFidelity != TrackingFidelityMode.Forward)
{
@@ -183,7 +184,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
public override string ToString()
{
- return String.Format("V{0} (r{1})", VersionNumber, ReiteratedVersionNumber);
+ return String.Format(CultureInfo.CurrentCulture, "V{0} (r{1})", this.VersionNumber, ReiteratedVersionNumber);
}
}
}
diff --git a/src/Text/Impl/TextModel/TrackingPoint.cs b/src/Text/Impl/TextModel/TrackingPoint.cs
index 02750f3..3549d03 100644
--- a/src/Text/Impl/TextModel/TrackingPoint.cs
+++ b/src/Text/Impl/TextModel/TrackingPoint.cs
@@ -21,15 +21,15 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (version == null)
{
- throw new ArgumentNullException("version");
+ throw new ArgumentNullException(nameof(version));
}
if (position < 0 | position > version.Length)
{
- throw new ArgumentOutOfRangeException("position");
+ throw new ArgumentOutOfRangeException(nameof(position));
}
if (trackingMode < PointTrackingMode.Positive || trackingMode > PointTrackingMode.Negative)
{
- throw new ArgumentOutOfRangeException("trackingMode");
+ throw new ArgumentOutOfRangeException(nameof(trackingMode));
}
this.trackingMode = trackingMode;
@@ -50,7 +50,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (version == null)
{
- throw new ArgumentNullException("version");
+ throw new ArgumentNullException(nameof(version));
}
if (version.TextBuffer != this.TextBuffer)
{
@@ -63,7 +63,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (snapshot == null)
{
- throw new ArgumentNullException("snapshot");
+ throw new ArgumentNullException(nameof(snapshot));
}
if (snapshot.TextBuffer != this.TextBuffer)
{
diff --git a/src/Text/Impl/TextModel/TrackingSpan.cs b/src/Text/Impl/TextModel/TrackingSpan.cs
index 8ffb5fe..82d5d55 100644
--- a/src/Text/Impl/TextModel/TrackingSpan.cs
+++ b/src/Text/Impl/TextModel/TrackingSpan.cs
@@ -21,15 +21,15 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (version == null)
{
- throw new ArgumentNullException("version");
+ throw new ArgumentNullException(nameof(version));
}
if (span.End > version.Length)
{
- throw new ArgumentOutOfRangeException("span");
+ throw new ArgumentOutOfRangeException(nameof(span));
}
if (trackingMode < SpanTrackingMode.EdgeExclusive || trackingMode > SpanTrackingMode.Custom)
{
- throw new ArgumentOutOfRangeException("trackingMode");
+ throw new ArgumentOutOfRangeException(nameof(trackingMode));
}
this.trackingMode = trackingMode;
@@ -50,7 +50,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (version == null)
{
- throw new ArgumentNullException("version");
+ throw new ArgumentNullException(nameof(version));
}
if (version.TextBuffer != this.TextBuffer)
{
@@ -63,7 +63,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (snapshot == null)
{
- throw new ArgumentNullException("snapshot");
+ throw new ArgumentNullException(nameof(snapshot));
}
if (snapshot.TextBuffer != this.TextBuffer)
{
diff --git a/src/Text/Impl/TextModel/TrivialNormalizedTextChangeCollection.cs b/src/Text/Impl/TextModel/TrivialNormalizedTextChangeCollection.cs
index bb70ebd..e92dbf9 100644
--- a/src/Text/Impl/TextModel/TrivialNormalizedTextChangeCollection.cs
+++ b/src/Text/Impl/TextModel/TrivialNormalizedTextChangeCollection.cs
@@ -39,7 +39,7 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (index != 0)
{
- throw new ArgumentOutOfRangeException("index");
+ throw new ArgumentOutOfRangeException(nameof(index));
}
return this;
}
@@ -95,11 +95,11 @@ namespace Microsoft.VisualStudio.Text.Implementation
{
if (array == null)
{
- throw new ArgumentNullException("array");
+ throw new ArgumentNullException(nameof(array));
}
if (arrayIndex < 0)
{
- throw new ArgumentOutOfRangeException("arrayIndex");
+ throw new ArgumentOutOfRangeException(nameof(arrayIndex));
}
if (array.Rank > 1 || arrayIndex >= array.Length)
{
diff --git a/src/Text/Impl/TextSearch/BackgroundSearch.cs b/src/Text/Impl/TextSearch/BackgroundSearch.cs
index f0e6db9..3bbe3ec 100644
--- a/src/Text/Impl/TextSearch/BackgroundSearch.cs
+++ b/src/Text/Impl/TextSearch/BackgroundSearch.cs
@@ -25,7 +25,7 @@ namespace Microsoft.VisualStudio.Text.Find.Implementation
/// Once we've searched a section of the buffer we don't search it again unless it is modified.
/// Even if we get multiple, nearly simultaneous requests to search a section of the buffer, we only search it once.
/// </remarks>
- internal class BackgroundSearch<T> : IDisposable where T : ITag
+ internal sealed class BackgroundSearch<T> : IDisposable where T : ITag
{
private ITextBuffer _buffer;
private readonly ITextSearchService2 _textSearchService;
@@ -429,6 +429,7 @@ namespace Microsoft.VisualStudio.Text.Find.Implementation
public void Dispose()
{
_isDisposed = true;
+ GC.SuppressFinalize(this);
}
#endregion
diff --git a/src/Text/Impl/TextSearch/TextSearchNavigatorFactoryService.cs b/src/Text/Impl/TextSearch/TextSearchNavigatorFactoryService.cs
index 278532f..a555f9b 100644
--- a/src/Text/Impl/TextSearch/TextSearchNavigatorFactoryService.cs
+++ b/src/Text/Impl/TextSearch/TextSearchNavigatorFactoryService.cs
@@ -25,7 +25,7 @@ namespace Microsoft.VisualStudio.Text.Find.Implementation
{
if (buffer == null)
{
- throw new ArgumentNullException("buffer");
+ throw new ArgumentNullException(nameof(buffer));
}
// Don't return a singleton since it's allowed to have multiple search navigators on the same buffer
diff --git a/src/Text/Impl/TextSearch/TextSearchService.cs b/src/Text/Impl/TextSearch/TextSearchService.cs
index 71c44e1..5146f4f 100644
--- a/src/Text/Impl/TextSearch/TextSearchService.cs
+++ b/src/Text/Impl/TextSearch/TextSearchService.cs
@@ -8,16 +8,15 @@
namespace Microsoft.VisualStudio.Text.Find.Implementation
{
using System;
- using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.Diagnostics;
+ using System.Linq;
using System.Text.RegularExpressions;
+ using System.Threading;
using Microsoft.VisualStudio.Text.Operations;
using Microsoft.VisualStudio.Text.Utilities;
- using System.Linq;
- using System.Threading;
[Export(typeof(ITextSearchService))]
[Export(typeof(ITextSearchService2))]
@@ -30,18 +29,12 @@ namespace Microsoft.VisualStudio.Text.Find.Implementation
// Cache of recently used Regex expressions to save on construction
// of Regex objects.
- static IDictionary<string, WeakReference> _cachedRegexEngines;
- static ReaderWriterLockSlim _regexCacheLock;
+ static IDictionary<string, WeakReference> _cachedRegexEngines = new Dictionary<string, WeakReference>(_maxCachedRegexEngines);
+ static ReaderWriterLockSlim _regexCacheLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
// Maximum number of Regex engines to cache
const int _maxCachedRegexEngines = 10;
- static TextSearchService()
- {
- _cachedRegexEngines = new Dictionary<string, WeakReference>(_maxCachedRegexEngines);
- _regexCacheLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
- }
-
#region ITextSearchService Members
public SnapshotSpan? FindNext(int startIndex, bool wraparound, FindData findData)
@@ -49,12 +42,12 @@ namespace Microsoft.VisualStudio.Text.Find.Implementation
// We allow startIndex to be at the end of the buffer
if ((startIndex < 0) || (startIndex > findData.TextSnapshotToSearch.Length))
{
- throw new ArgumentOutOfRangeException("startIndex");
+ throw new ArgumentOutOfRangeException(nameof(startIndex));
}
if (string.IsNullOrEmpty(findData.SearchString))
{
- throw new ArgumentException("Search pattern can't be empty or null", "findData");
+ throw new ArgumentException("Search pattern can't be empty or null", nameof(findData));
}
FindOptions options = findData.FindOptions;
@@ -102,7 +95,7 @@ namespace Microsoft.VisualStudio.Text.Find.Implementation
{
if (string.IsNullOrEmpty(findData.SearchString))
{
- throw new ArgumentException("Search pattern can't be empty or null", "findData");
+ throw new ArgumentException("Search pattern can't be empty or null", nameof(findData));
}
FindOptions options = findData.FindOptions;
@@ -140,7 +133,7 @@ namespace Microsoft.VisualStudio.Text.Find.Implementation
{
if (string.IsNullOrEmpty(searchPattern))
{
- throw new ArgumentException("Pattern can't be empty or null", "searchPattern");
+ throw new ArgumentException("Pattern can't be empty or null", nameof(searchPattern));
}
return Find(startingPosition, new SnapshotSpan(startingPosition.Snapshot, Span.FromBounds(0, startingPosition.Snapshot.Length)), searchPattern, options);
@@ -150,7 +143,7 @@ namespace Microsoft.VisualStudio.Text.Find.Implementation
{
if (string.IsNullOrEmpty(searchPattern))
{
- throw new ArgumentException("Pattern can't be empty or null", "searchPattern");
+ throw new ArgumentException("Pattern can't be empty or null", nameof(searchPattern));
}
if (searchRange.Snapshot != startingPosition.Snapshot)
@@ -170,13 +163,10 @@ namespace Microsoft.VisualStudio.Text.Find.Implementation
{
if (string.IsNullOrEmpty(searchPattern))
{
- throw new ArgumentException("Pattern can't be empty or null", "searchPattern");
+ throw new ArgumentException("Pattern can't be empty or null", nameof(searchPattern));
}
- if (replacePattern == null)
- {
- throw new ArgumentNullException("Replace pattern can't be null.", "replacePattern");
- }
+ Requires.NotNull(replacePattern, nameof(replacePattern));
return FindForReplace(startingPosition, new SnapshotSpan(startingPosition.Snapshot, Span.FromBounds(0, startingPosition.Snapshot.Length)),
searchPattern, replacePattern, options, out expandedReplacePattern);
@@ -186,13 +176,10 @@ namespace Microsoft.VisualStudio.Text.Find.Implementation
{
if (string.IsNullOrEmpty(searchPattern))
{
- throw new ArgumentException("Pattern can't be empty or null", "searchPattern");
+ throw new ArgumentException("Pattern can't be empty or null", nameof(searchPattern));
}
- if (replacePattern == null)
- {
- throw new ArgumentNullException("Replace pattern can't be null.", "replacePattern");
- }
+ Requires.NotNull(replacePattern, nameof(replacePattern));
return FindForReplace(((options & FindOptions.SearchReverse) != FindOptions.SearchReverse) ? searchRange.Start : searchRange.End, searchRange, searchPattern, replacePattern, options, out expandedReplacePattern);
}
@@ -201,12 +188,12 @@ namespace Microsoft.VisualStudio.Text.Find.Implementation
{
if (string.IsNullOrEmpty(searchPattern))
{
- throw new ArgumentException("Pattern can't be empty or null", "searchPattern");
+ throw new ArgumentException("Pattern can't be empty or null", nameof(searchPattern));
}
if (searchRange.Length == 0)
{
- return new SnapshotSpan[] { };
+ return Array.Empty<SnapshotSpan>();
}
return FindAllForReplace(searchRange.Start, searchRange, searchPattern, null, options).Select(r => r.Item1);
@@ -216,12 +203,12 @@ namespace Microsoft.VisualStudio.Text.Find.Implementation
{
if (string.IsNullOrEmpty(searchPattern))
{
- throw new ArgumentException("Pattern can't be empty or null", "searchPattern");
+ throw new ArgumentException("Pattern can't be empty or null", nameof(searchPattern));
}
if (searchRange.Length == 0)
{
- return new SnapshotSpan[] { };
+ return Array.Empty<SnapshotSpan>();
}
if (searchRange.Snapshot != startingPosition.Snapshot)
@@ -241,13 +228,10 @@ namespace Microsoft.VisualStudio.Text.Find.Implementation
{
if (string.IsNullOrEmpty(searchPattern))
{
- throw new ArgumentException("Search pattern can't be null or empty.", "searchPattern");
+ throw new ArgumentException("Search pattern can't be null or empty.", nameof(searchPattern));
}
- if (replacePattern == null)
- {
- throw new ArgumentNullException("Replace pattern can't be null.", "replacePattern");
- }
+ Requires.NotNull(replacePattern, nameof(replacePattern));
return FindAllForReplace(searchRange.Start, searchRange, searchPattern, replacePattern, options);
}
diff --git a/src/Text/Impl/TextSearch/TextSearchTagger.cs b/src/Text/Impl/TextSearch/TextSearchTagger.cs
index 6a8852b..150a67b 100644
--- a/src/Text/Impl/TextSearch/TextSearchTagger.cs
+++ b/src/Text/Impl/TextSearch/TextSearchTagger.cs
@@ -19,7 +19,7 @@ namespace Microsoft.VisualStudio.Text.Find.Implementation
/// <remarks>
/// This tagger -- like most others -- will not raise a TagsChanged event when the buffer changes.
/// </remarks>
- class TextSearchTagger<T> : ITextSearchTagger<T> where T : ITag
+ internal sealed class TextSearchTagger<T> : ITextSearchTagger<T> where T : ITag
{
// search service to use for doing the real search
ITextSearchService2 _searchService;
@@ -103,12 +103,12 @@ namespace Microsoft.VisualStudio.Text.Find.Implementation
{
if ((searchOptions & FindOptions.SearchReverse) == FindOptions.SearchReverse)
{
- throw new ArgumentException("FindOptions.SearchReverse is invalid as searches are performed forwards to ensure all matches in a requested search span are found.", "searchOptions");
+ throw new ArgumentException("FindOptions.SearchReverse is invalid as searches are performed forwards to ensure all matches in a requested search span are found.", nameof(searchOptions));
}
if ((searchOptions & FindOptions.Wrap) == FindOptions.Wrap)
{
- throw new ArgumentException("FindOptions.Wrap is invalid as searches are performed forwards with no wrapping to ensure all matches in a requested span are found.", "searchOptions");
+ throw new ArgumentException("FindOptions.Wrap is invalid as searches are performed forwards with no wrapping to ensure all matches in a requested span are found.", nameof(searchOptions));
}
_searchTerms.Add(new BackgroundSearch<T>(_searchService, _buffer, searchTerm, searchOptions, tagFactory, this.ResultsCalculated));
diff --git a/src/Text/Impl/TextSearch/TextSearchTaggerFactoryService.cs b/src/Text/Impl/TextSearch/TextSearchTaggerFactoryService.cs
index fca8282..711fda8 100644
--- a/src/Text/Impl/TextSearch/TextSearchTaggerFactoryService.cs
+++ b/src/Text/Impl/TextSearch/TextSearchTaggerFactoryService.cs
@@ -25,7 +25,7 @@ namespace Microsoft.VisualStudio.Text.Find.Implementation
{
if (buffer == null)
{
- throw new ArgumentNullException("buffer");
+ throw new ArgumentNullException(nameof(buffer));
}
// Don't return singleton instances since multiple taggers can exist per buffer