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:
authorSandy Armstrong <sandy@xamarin.com>2019-06-21 01:20:20 +0300
committerSandy Armstrong <sandy@xamarin.com>2019-06-24 17:08:06 +0300
commit914e1cd7bda1f0ca497f8c4c527aa948686b2a86 (patch)
treec45ddf481b20a935f39592a09ee203df97b9939d
parent556c2b6ac5dbf978d52a2d4750cf760f21457407 (diff)
Sync with vsec for fold commandspr-sandy-fold-commands
-rw-r--r--src/Editor/Text/Def/TextUI/Commanding/CommandSelector.cs8
-rw-r--r--src/Editor/Text/Def/TextUI/Commanding/Commands/OutliningCommandArgs.cs30
-rw-r--r--src/Editor/Text/Impl/Outlining/OutliningCommandHandler.cs434
3 files changed, 472 insertions, 0 deletions
diff --git a/src/Editor/Text/Def/TextUI/Commanding/CommandSelector.cs b/src/Editor/Text/Def/TextUI/Commanding/CommandSelector.cs
index d53e551..7e8ebd7 100644
--- a/src/Editor/Text/Def/TextUI/Commanding/CommandSelector.cs
+++ b/src/Editor/Text/Def/TextUI/Commanding/CommandSelector.cs
@@ -275,6 +275,14 @@ namespace Microsoft.VisualStudio.Text.Editor.Commanding
/// <summary>⌘L</summary>
public const string GoToLine = "goToLine:";
+ public const string ToggleOutliningEnabled = "toggleOutliningEnabled:";
+
+ public const string ToggleOutliningExpansion = "toggleOutliningExpansion:";
+
+ public const string ToggleAllOutlining = "toggleAllOutlining:";
+
+ public const string ToggleOutliningDefinitions = "toggleOutliningDefinitions:";
+
#endregion
}
diff --git a/src/Editor/Text/Def/TextUI/Commanding/Commands/OutliningCommandArgs.cs b/src/Editor/Text/Def/TextUI/Commanding/Commands/OutliningCommandArgs.cs
new file mode 100644
index 0000000..47631e2
--- /dev/null
+++ b/src/Editor/Text/Def/TextUI/Commanding/Commands/OutliningCommandArgs.cs
@@ -0,0 +1,30 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class ToggleOutliningExpansionCommandArgs : EditorCommandArgs
+ {
+ public ToggleOutliningExpansionCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+
+ public sealed class ToggleAllOutliningCommandArgs : EditorCommandArgs
+ {
+ public ToggleAllOutliningCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+
+ public sealed class ToggleOutliningDefinitionsCommandArgs : EditorCommandArgs
+ {
+ public ToggleOutliningDefinitionsCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+
+ public sealed class ToggleOutliningEnabledCommandArgs : EditorCommandArgs
+ {
+ public ToggleOutliningEnabledCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Editor/Text/Impl/Outlining/OutliningCommandHandler.cs b/src/Editor/Text/Impl/Outlining/OutliningCommandHandler.cs
new file mode 100644
index 0000000..7ccb571
--- /dev/null
+++ b/src/Editor/Text/Impl/Outlining/OutliningCommandHandler.cs
@@ -0,0 +1,434 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.Linq;
+
+using Microsoft.VisualStudio.Commanding;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Editor.Commanding.Commands;
+using Microsoft.VisualStudio.Utilities;
+
+namespace Microsoft.VisualStudio.Text.Outlining.Implementation
+{
+ /// <summary>
+ /// Commands for collapsing and expanding text
+ /// </summary>
+ [Name(nameof(OutliningCommandHandler))]
+ [ContentType("text")]
+ [Export(typeof(ICommandHandler))]
+ internal sealed class OutliningCommandHandler :
+ ICommandHandler<ToggleOutliningExpansionCommandArgs>,
+ ICommandHandler<ToggleAllOutliningCommandArgs>,
+ ICommandHandler<ToggleOutliningDefinitionsCommandArgs>,
+ ICommandHandler<ToggleOutliningEnabledCommandArgs>
+ {
+ [Import]
+ IOutliningManagerService outliningManagerService;
+
+ string INamed.DisplayName => nameof(OutliningCommandHandler);
+
+ bool ICommandHandler<ToggleOutliningExpansionCommandArgs>.ExecuteCommand(ToggleOutliningExpansionCommandArgs args, CommandExecutionContext executionContext)
+ {
+ var outliningManager = outliningManagerService.GetOutliningManager(args.TextView);
+ if (outliningManager == null || !outliningManager.Enabled)
+ {
+ return false;
+ }
+
+ var collapsibles = GetCollapsiblesForCurrentSelection(outliningManager, args.TextView);
+ if ((collapsibles != null) && (collapsibles.Any()))
+ {
+ var firstCollapsible = collapsibles.First();
+ var lastCollapsible = collapsibles.Last();
+ if ((firstCollapsible != null) && (lastCollapsible != null))
+ {
+ var snapshot = args.TextView.TextSnapshot;
+ var sortedCollapsedMatcher = new SortedCollapsedMatcher(collapsibles.Select((collapsible) => collapsible.Extent), snapshot);
+ var firstSnapshotSpan = firstCollapsible.Extent.GetSpan(snapshot);
+ var lastSnapshotSpan = ((lastCollapsible == firstCollapsible) ? firstSnapshotSpan : lastCollapsible.Extent.GetSpan(snapshot));
+ var containingSnapshotSpan = new SnapshotSpan(firstSnapshotSpan.Start, lastSnapshotSpan.End);
+
+ if (ShouldExpandToggledCollapsibles(collapsibles))
+ {
+ outliningManager.ExpandAll(containingSnapshotSpan, sortedCollapsedMatcher.Match);
+ }
+ else
+ {
+ outliningManager.CollapseAll(containingSnapshotSpan, sortedCollapsedMatcher.Match);
+ }
+
+ // Ensure caret visible
+ args.TextView.Caret.EnsureVisible();
+ }
+ }
+
+ return true;
+ }
+
+ bool ICommandHandler<ToggleAllOutliningCommandArgs>.ExecuteCommand(ToggleAllOutliningCommandArgs args, CommandExecutionContext executionContext)
+ {
+ var outliningManager = outliningManagerService.GetOutliningManager(args.TextView);
+ if (outliningManager == null || !outliningManager.Enabled)
+ {
+ return false;
+ }
+
+ var snapshot = args.TextView.TextBuffer.CurrentSnapshot;
+ var snapshotSpan = new SnapshotSpan(snapshot, 0, snapshot.Length);
+
+ if (outliningManager.GetCollapsedRegions(snapshotSpan).Any())
+ {
+ outliningManager.ExpandAll(snapshotSpan, collapsed => true);
+ }
+ else
+ {
+ outliningManager.CollapseAll(snapshotSpan, collapsed => true);
+ }
+
+ args.TextView.Caret.EnsureVisible();
+
+ return true;
+ }
+
+ bool ICommandHandler<ToggleOutliningDefinitionsCommandArgs>.ExecuteCommand(ToggleOutliningDefinitionsCommandArgs args, CommandExecutionContext executionContext)
+ {
+ // NOTE: For VSwin, this isn't something you toggle. The single related command is "collapse to definitions".
+ // It expands everthing then collapses all implementation blocks.
+ // VSmac's old editor lets you toggle, which is a little less clear-cut when other folding has occurred.
+
+ var outliningManager = outliningManagerService.GetOutliningManager(args.TextView) as IAccurateOutliningManager;
+ if (outliningManager == null)
+ {
+ return false;
+ }
+
+ // Expand non-implementation
+ var snapshot = args.TextView.TextBuffer.CurrentSnapshot;
+ var snapshotSpan = new SnapshotSpan(snapshot, 0, snapshot.Length);
+
+ // If any definitions are already collapsed, we will be expanding
+ var expandDefinitions = outliningManager.GetCollapsedRegions(snapshotSpan).Any(r => r.Tag.IsImplementation);
+
+ outliningManager.ExpandAll(snapshotSpan, (collapsible => !collapsible.Tag.IsImplementation));
+
+ if (expandDefinitions)
+ {
+ outliningManager.ExpandAll(snapshotSpan, (collapsible => collapsible.Tag.IsImplementation));
+ }
+ else
+ {
+ outliningManager.CollapseAll(snapshotSpan, (collapsible => collapsible.Tag.IsImplementation));
+ }
+
+ args.TextView.Caret.EnsureVisible();
+
+ return true;
+ }
+
+ bool ICommandHandler<ToggleOutliningEnabledCommandArgs>.ExecuteCommand(ToggleOutliningEnabledCommandArgs args, CommandExecutionContext executionContext)
+ {
+ var outliningManager = outliningManagerService.GetOutliningManager(args.TextView);
+ if (outliningManager == null)
+ {
+ return false;
+ }
+
+ outliningManager.Enabled = !outliningManager.Enabled;
+
+ // TODO: What about adhoc regions? (Windows has special logic here, but Mac does not have AdhocOutliner)
+
+ return true;
+ }
+
+ CommandState ICommandHandler<ToggleOutliningExpansionCommandArgs>.GetCommandState(ToggleOutliningExpansionCommandArgs args)
+ {
+ var outliningManager = outliningManagerService.GetOutliningManager(args.TextView);
+ if (outliningManager != null && outliningManager.Enabled)
+ {
+ var collapsibles = GetCollapsiblesForCurrentSelection(outliningManager, args.TextView);
+ if (collapsibles != null && collapsibles.Any())
+ {
+ return CommandState.Available;
+ }
+ }
+
+ return CommandState.Unavailable;
+ }
+
+ CommandState ICommandHandler<ToggleAllOutliningCommandArgs>.GetCommandState(ToggleAllOutliningCommandArgs args)
+ {
+ return outliningManagerService.GetOutliningManager(args.TextView) is IOutliningManager mgr && mgr.Enabled
+ ? CommandState.Available
+ : CommandState.Unavailable;
+ }
+
+ CommandState ICommandHandler<ToggleOutliningDefinitionsCommandArgs>.GetCommandState(ToggleOutliningDefinitionsCommandArgs args)
+ {
+ return outliningManagerService.GetOutliningManager(args.TextView) is IAccurateOutliningManager mgr && mgr.Enabled
+ ? CommandState.Available
+ : CommandState.Unavailable;
+ }
+
+ CommandState ICommandHandler<ToggleOutliningEnabledCommandArgs>.GetCommandState(ToggleOutliningEnabledCommandArgs args)
+ {
+ return CommandState.Available;
+ }
+
+ internal IEnumerable<ICollapsible> GetCollapsiblesForCurrentSelection(IOutliningManager outliningManager, ITextView textView)
+ {
+ if (outliningManager == null)
+ {
+ return null;
+ }
+
+ // Try for all collapsibles which are contained within the selection span
+ SnapshotSpan selectionSnapshotSpan = textView.Selection.StreamSelectionSpan.SnapshotSpan;
+ ITextSnapshot selectionSnapshot = selectionSnapshotSpan.Snapshot;
+ var intersectingCollapsibles = outliningManager.GetAllRegions(selectionSnapshotSpan);
+ var filteredCollapsibles = intersectingCollapsibles.Where(collapsible => selectionSnapshotSpan.Contains(collapsible.Extent.GetSpan(selectionSnapshot)));
+ var tornoffCollapsibles = GetTornoffCollapsibles(filteredCollapsibles);
+ if (tornoffCollapsibles != null)
+ {
+ return tornoffCollapsibles;
+ }
+
+ // Try for innermost collapsibles which intersect and start within the selection span
+ filteredCollapsibles = intersectingCollapsibles.Where(collapsible => selectionSnapshotSpan.Contains(collapsible.Extent.GetSpan(selectionSnapshot).Start));
+ tornoffCollapsibles = GetInnermostCollapsibles(selectionSnapshot, filteredCollapsibles);
+ if (tornoffCollapsibles != null)
+ {
+ return tornoffCollapsibles;
+ }
+
+ // Try for all collapsibles which are contained within the selection's first line
+ ITextSnapshotLine selectionStartLine = selectionSnapshot.GetLineFromPosition(selectionSnapshotSpan.Start);
+ SnapshotSpan selectionFirstLine = new SnapshotSpan(selectionSnapshot, selectionStartLine.Start, selectionStartLine.Length);
+ intersectingCollapsibles = outliningManager.GetAllRegions(selectionFirstLine);
+ filteredCollapsibles = intersectingCollapsibles.Where(collapsible => selectionFirstLine.Contains(collapsible.Extent.GetSpan(selectionSnapshot)));
+ tornoffCollapsibles = GetTornoffCollapsibles(filteredCollapsibles);
+ if (tornoffCollapsibles != null)
+ {
+ return tornoffCollapsibles;
+ }
+
+ // Try for innermost collapsibles which intersect and start within the selection's first line
+ filteredCollapsibles = intersectingCollapsibles.Where(collapsible => selectionFirstLine.Contains(collapsible.Extent.GetSpan(selectionSnapshot).Start));
+ tornoffCollapsibles = GetInnermostCollapsibles(selectionSnapshot, filteredCollapsibles);
+ if (tornoffCollapsibles != null)
+ {
+ return tornoffCollapsibles;
+ }
+
+ // Try for innermost collapsibles which simply intersect the selection's first line
+ tornoffCollapsibles = GetInnermostCollapsibles(selectionSnapshot, intersectingCollapsibles);
+ if (tornoffCollapsibles != null)
+ {
+ return tornoffCollapsibles;
+ }
+
+ return null;
+ }
+
+ internal static IEnumerable<ICollapsible> GetTornoffCollapsibles(IEnumerable<ICollapsible> collapsibles)
+ {
+ List<ICollapsible> tornoffCollapsibles = null;
+
+ if (collapsibles != null)
+ {
+ foreach (ICollapsible collapsible in collapsibles)
+ {
+ AddCollapsible(ref tornoffCollapsibles, collapsible);
+ }
+ }
+
+ return tornoffCollapsibles;
+ }
+
+ internal static IEnumerable<ICollapsible> GetInnermostCollapsibles(ITextSnapshot snapshot, IEnumerable<ICollapsible> collapsibles)
+ {
+ if (snapshot == null)
+ {
+ throw new ArgumentNullException(nameof(snapshot));
+ }
+
+ List<ICollapsible> innermostCollapsibles = null;
+
+ if (collapsibles != null)
+ {
+ // OutliningManager returns collapsibles "sorted" such that contained follow containing and preceed not contained
+ // Include collapsibles which have no following collapsibles which are contained within them
+ ICollapsible previousCollapsible = null;
+ foreach (ICollapsible collapsible in collapsibles)
+ {
+ if (previousCollapsible != null)
+ {
+ if (!previousCollapsible.Extent.GetSpan(snapshot).Contains(collapsible.Extent.GetSpan(snapshot)))
+ {
+ AddCollapsible(ref innermostCollapsibles, previousCollapsible);
+ }
+ }
+ previousCollapsible = collapsible;
+ }
+ if (previousCollapsible != null)
+ {
+ AddCollapsible(ref innermostCollapsibles, previousCollapsible);
+ }
+ }
+
+ return innermostCollapsibles;
+ }
+
+ internal static void AddCollapsible(ref List<ICollapsible> collapsibles, ICollapsible collapsible)
+ {
+ if (collapsibles == null)
+ {
+ collapsibles = new List<ICollapsible>();
+ }
+ collapsibles.Add(collapsible);
+ }
+
+ internal static bool ShouldExpandToggledCollapsibles(IEnumerable<ICollapsible> collapsibles)
+ {
+ if (collapsibles != null)
+ {
+ // Determine whether homogenous collapse state for given collapsibles
+ bool homogenousCollapseState = true;
+ ICollapsible previousCollapsible = null;
+ foreach (ICollapsible collapsible in collapsibles)
+ {
+ if (previousCollapsible != null)
+ {
+ if (previousCollapsible.IsCollapsed != collapsible.IsCollapsed)
+ {
+ homogenousCollapseState = false;
+ break;
+ }
+ }
+ previousCollapsible = collapsible;
+ }
+
+ // Check whether non empty collection of collapsibles
+ if (previousCollapsible != null)
+ {
+ // Check whether homogenous collapse state and collapsed
+ if (homogenousCollapseState && previousCollapsible.IsCollapsed)
+ {
+ // Should expand collapsibles
+ return true;
+ }
+ else
+ {
+ // Should collapse collapsibles
+ return false;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ internal static bool AreExpandable(IEnumerable<ICollapsible> collapsibles)
+ {
+ if (collapsibles != null)
+ {
+ foreach (ICollapsible collapsible in collapsibles)
+ {
+ if (collapsible.IsCollapsed)
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ internal bool AreCollapsible(IEnumerable<ICollapsible> collapsibles, ITextSnapshot snapshot)
+ {
+ if (collapsibles != null)
+ {
+ // OutliningManager returns collapsibles "sorted" such that contained follow containing and preceed not contained
+ // Return true if find uncollapsed not nested within collapsed
+ ICollapsible previousCollapsed = null;
+ foreach (ICollapsible collapsible in collapsibles)
+ {
+ bool nestedInPreviousCollapsed = ((previousCollapsed != null) && previousCollapsed.Extent.GetSpan(snapshot).Contains(collapsible.Extent.GetSpan(snapshot)));
+ if (!nestedInPreviousCollapsed)
+ {
+ if (!collapsible.IsCollapsed)
+ {
+ return true;
+ }
+ previousCollapsed = collapsible;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ internal class SortedCollapsibleMatcher
+ {
+ private IEnumerator<ITrackingSpan> _trackingSpanEnum;
+ private ITextSnapshot _textSnapshot;
+ private ITrackingSpan _currentTrackingSpan;
+ internal bool MatchReturn { get; set; }
+
+ internal SortedCollapsibleMatcher(IEnumerable<ITrackingSpan> trackingSpans, ITextSnapshot textSnapshot)
+ {
+ _trackingSpanEnum = ((trackingSpans != null) ? trackingSpans.GetEnumerator() : null);
+ _textSnapshot = textSnapshot;
+ if ((_trackingSpanEnum != null) && (_textSnapshot != null))
+ {
+ _currentTrackingSpan = (_trackingSpanEnum.MoveNext() ? _trackingSpanEnum.Current : null);
+ }
+ MatchReturn = true;
+ }
+ internal bool Match(ICollapsible givenCollapsible)
+ {
+ if (givenCollapsible != null)
+ {
+ while (_currentTrackingSpan != null)
+ {
+ SnapshotSpan currentSnapshotSpan = _currentTrackingSpan.GetSpan(_textSnapshot);
+ SnapshotSpan givenSnapshotSpan = givenCollapsible.Extent.GetSpan(_textSnapshot);
+ int comparison = ((currentSnapshotSpan.Start != givenSnapshotSpan.Start) ?
+ currentSnapshotSpan.Start.CompareTo(givenSnapshotSpan.Start) :
+ -currentSnapshotSpan.Length.CompareTo(givenSnapshotSpan.Length));
+ if (comparison <= 0)
+ {
+ // Advance current
+ _currentTrackingSpan = (_trackingSpanEnum.MoveNext() ? _trackingSpanEnum.Current : null);
+
+ if (comparison == 0)
+ {
+ // Match
+ return MatchReturn;
+ }
+
+ // Continue loop to try updated current
+ }
+ else
+ {
+ // Break loop to advance given
+ break;
+ }
+ }
+ }
+ return !MatchReturn;
+ }
+ }
+
+ internal class SortedCollapsedMatcher : SortedCollapsibleMatcher
+ {
+ internal SortedCollapsedMatcher(IEnumerable<ITrackingSpan> trackingSpans, ITextSnapshot textSnapshot) :
+ base(trackingSpans, textSnapshot)
+ {
+ }
+ internal bool Match(ICollapsed givenCollapsed)
+ {
+ return base.Match(givenCollapsed);
+ }
+ }
+ }
+}