diff --git a/Backup/Classifier.cs b/Backup/Classifier.cs new file mode 100644 index 0000000..3d883a4 --- /dev/null +++ b/Backup/Classifier.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Windows.Media; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Utilities; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Tagging; + +namespace GoToDef +{ + #region Classification type/format exports + + [Export(typeof(EditorFormatDefinition))] + [ClassificationType(ClassificationTypeNames = "UnderlineClassification")] + [Name("UnderlineClassificationFormat")] + [UserVisible(true)] + [Order(After = Priority.High)] + internal sealed class UnderlineFormatDefinition : ClassificationFormatDefinition + { + public UnderlineFormatDefinition() + { + this.DisplayName = "Underline"; + this.TextDecorations = System.Windows.TextDecorations.Underline; + this.ForegroundColor = Colors.Blue; + } + } + + #endregion + + #region Provider definition + [Export(typeof(IViewTaggerProvider))] + [ContentType("text")] + [TagType(typeof(ClassificationTag))] + internal class UnderlineClassifierProvider : IViewTaggerProvider + { + [Import] + internal IClassificationTypeRegistryService ClassificationRegistry = null; + + [Export(typeof(ClassificationTypeDefinition))] + [Name("UnderlineClassification")] + internal static ClassificationTypeDefinition underlineClassificationType = null; + + static IClassificationType UnderlineClassification; + public static UnderlineClassifier GetClassifierForView(ITextView view) + { + if (UnderlineClassification == null) + return null; + + return view.Properties.GetOrCreateSingletonProperty(() => new UnderlineClassifier(view, UnderlineClassification)); + } + + public ITagger CreateTagger(ITextView textView, ITextBuffer buffer) where T : ITag + { + if (UnderlineClassification == null) + UnderlineClassification = ClassificationRegistry.GetClassificationType("UnderlineClassification"); + + if (textView.TextBuffer != buffer) + return null; + + return GetClassifierForView(textView) as ITagger; + } + } + #endregion + + internal class UnderlineClassifier : ITagger + { + IClassificationType _classificationType; + ITextView _textView; + SnapshotSpan? _underlineSpan; + + internal UnderlineClassifier(ITextView textView, IClassificationType classificationType) + { + _textView = textView; + _classificationType = classificationType; + _underlineSpan = null; + } + + #region Private helpers + + void SendEvent(SnapshotSpan span) + { + var temp = this.TagsChanged; + if (temp != null) + temp(this, new SnapshotSpanEventArgs(span)); + } + + #endregion + + #region UnderlineClassification public members + + public SnapshotSpan? CurrentUnderlineSpan { get { return _underlineSpan; } } + + public void SetUnderlineSpan(SnapshotSpan? span) + { + var oldSpan = _underlineSpan; + _underlineSpan = span; + + if (!oldSpan.HasValue && !_underlineSpan.HasValue) + return; + + else if (oldSpan.HasValue && _underlineSpan.HasValue && oldSpan == _underlineSpan) + return; + + if (!_underlineSpan.HasValue) + { + this.SendEvent(oldSpan.Value); + } + else + { + SnapshotSpan updateSpan = _underlineSpan.Value; + if (oldSpan.HasValue) + updateSpan = new SnapshotSpan(updateSpan.Snapshot, + Span.FromBounds(Math.Min(updateSpan.Start, oldSpan.Value.Start), + Math.Max(updateSpan.End, oldSpan.Value.End))); + + this.SendEvent(updateSpan); + } + } + + #endregion + + public IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) + { + if (!_underlineSpan.HasValue || spans.Count == 0) + yield break; + + SnapshotSpan request = new SnapshotSpan(spans[0].Start, spans[spans.Count - 1].End); + SnapshotSpan underline = _underlineSpan.Value.TranslateTo(request.Snapshot, SpanTrackingMode.EdgeInclusive); + if (underline.IntersectsWith(request)) + { + yield return new TagSpan(underline, new ClassificationTag(_classificationType)); + } + } + + public event EventHandler TagsChanged; + } +} \ No newline at end of file diff --git a/Backup/GoToDef.csproj b/Backup/GoToDef.csproj new file mode 100644 index 0000000..c863444 --- /dev/null +++ b/Backup/GoToDef.csproj @@ -0,0 +1,131 @@ + + + + + Debug + AnyCPU + 10.0.20506 + 2.0 + {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {25815605-0093-48EC-A89B-44F70EBDED65} + Library + Properties + GoToDef + GoToDef + v4.6 + 512 + false + 12.0 + + + + + 4.0 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + False + + + True + False + + + False + + + True + + + + + True + + + False + + + False + + + False + + + False + + + True + + + + + + + + + + + + + + + + + + + + Designer + + + + + Always + true + + + Always + true + + + Always + true + + + + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + + \ No newline at end of file diff --git a/Backup/GoToDef.sln b/Backup/GoToDef.sln new file mode 100644 index 0000000..a0373fd --- /dev/null +++ b/Backup/GoToDef.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GoToDef", "GoToDef.csproj", "{25815605-0093-48EC-A89B-44F70EBDED65}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {25815605-0093-48EC-A89B-44F70EBDED65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {25815605-0093-48EC-A89B-44F70EBDED65}.Debug|Any CPU.Build.0 = Debug|Any CPU + {25815605-0093-48EC-A89B-44F70EBDED65}.Release|Any CPU.ActiveCfg = Release|Any CPU + {25815605-0093-48EC-A89B-44F70EBDED65}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Backup/GoToDefMouseHandler.cs b/Backup/GoToDefMouseHandler.cs new file mode 100644 index 0000000..c653ae7 --- /dev/null +++ b/Backup/GoToDefMouseHandler.cs @@ -0,0 +1,355 @@ +using System; +using System.ComponentModel.Composition; +using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Input; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Operations; +using Microsoft.VisualStudio.Utilities; +using Microsoft.VisualStudio.Shell; + +namespace GoToDef +{ + [Export(typeof(IKeyProcessorProvider))] + [TextViewRole(PredefinedTextViewRoles.Document)] + [ContentType("code")] + [Name("GotoDef")] + [Order(Before = "VisualStudioKeyboardProcessor")] + internal sealed class GoToDefKeyProcessorProvider : IKeyProcessorProvider + { + public KeyProcessor GetAssociatedProcessor(IWpfTextView view) + { + return view.Properties.GetOrCreateSingletonProperty(typeof(GoToDefKeyProcessor), + () => new GoToDefKeyProcessor(CtrlKeyState.GetStateForView(view))); + } + } + + /// + /// The state of the control key for a given view, which is kept up-to-date by a combination of the + /// key processor and the mouse process + /// + internal sealed class CtrlKeyState + { + internal static CtrlKeyState GetStateForView(ITextView view) + { + return view.Properties.GetOrCreateSingletonProperty(typeof(CtrlKeyState), () => new CtrlKeyState()); + } + + bool _enabled = false; + + internal bool Enabled + { + get + { + // Check and see if ctrl is down but we missed it somehow. + bool ctrlDown = (Keyboard.Modifiers & ModifierKeys.Control) != 0 && + (Keyboard.Modifiers & ModifierKeys.Shift) == 0; + if (ctrlDown != _enabled) + Enabled = ctrlDown; + + return _enabled; + } + set + { + bool oldVal = _enabled; + _enabled = value; + if (oldVal != _enabled) + { + var temp = CtrlKeyStateChanged; + if (temp != null) + temp(this, new EventArgs()); + } + } + } + + internal event EventHandler CtrlKeyStateChanged; + } + + /// + /// Listen for the control key being pressed or released to update the CtrlKeyStateChanged for a view. + /// + internal sealed class GoToDefKeyProcessor : KeyProcessor + { + CtrlKeyState _state; + + public GoToDefKeyProcessor(CtrlKeyState state) + { + _state = state; + } + + void UpdateState(KeyEventArgs args) + { + _state.Enabled = (args.KeyboardDevice.Modifiers & ModifierKeys.Control) != 0 && + (args.KeyboardDevice.Modifiers & ModifierKeys.Shift) == 0; + } + + public override void PreviewKeyDown(KeyEventArgs args) + { + UpdateState(args); + } + + public override void PreviewKeyUp(KeyEventArgs args) + { + UpdateState(args); + } + } + + [Export(typeof(IMouseProcessorProvider))] + [TextViewRole(PredefinedTextViewRoles.Document)] + [ContentType("code")] + [Name("GotoDef")] + [Order(Before = "WordSelection")] + internal sealed class GoToDefMouseHandlerProvider : IMouseProcessorProvider + { + [Import] + IClassifierAggregatorService AggregatorFactory = null; + + [Import] + ITextStructureNavigatorSelectorService NavigatorService = null; + + [Import] + SVsServiceProvider GlobalServiceProvider = null; + + public IMouseProcessor GetAssociatedProcessor(IWpfTextView view) + { + var buffer = view.TextBuffer; + + IOleCommandTarget shellCommandDispatcher = GetShellCommandDispatcher(view); + + if (shellCommandDispatcher == null) + return null; + + return new GoToDefMouseHandler(view, + shellCommandDispatcher, + AggregatorFactory.GetClassifier(buffer), + NavigatorService.GetTextStructureNavigator(buffer), + CtrlKeyState.GetStateForView(view)); + } + + #region Private helpers + + /// + /// Get the SUIHostCommandDispatcher from the global service provider. + /// + IOleCommandTarget GetShellCommandDispatcher(ITextView view) + { + return GlobalServiceProvider.GetService(typeof(SUIHostCommandDispatcher)) as IOleCommandTarget; + } + + #endregion + } + + /// + /// Handle ctrl+click on valid elements to send GoToDefinition to the shell. Also handle mouse moves + /// (when control is pressed) to highlight references for which GoToDefinition will (likely) be valid. + /// + internal sealed class GoToDefMouseHandler : MouseProcessorBase + { + IWpfTextView _view; + CtrlKeyState _state; + IClassifier _aggregator; + ITextStructureNavigator _navigator; + IOleCommandTarget _commandTarget; + + public GoToDefMouseHandler(IWpfTextView view, IOleCommandTarget commandTarget, IClassifier aggregator, + ITextStructureNavigator navigator, CtrlKeyState state) + { + _view = view; + _commandTarget = commandTarget; + _state = state; + _aggregator = aggregator; + _navigator = navigator; + + _state.CtrlKeyStateChanged += (sender, args) => + { + if (_state.Enabled) + this.TryHighlightItemUnderMouse(RelativeToView(Mouse.PrimaryDevice.GetPosition(_view.VisualElement))); + else + this.SetHighlightSpan(null); + }; + + // Some other points to clear the highlight span: + + _view.LostAggregateFocus += (sender, args) => this.SetHighlightSpan(null); + _view.VisualElement.MouseLeave += (sender, args) => this.SetHighlightSpan(null); + } + + #region Mouse processor overrides + + // Remember the location of the mouse on left button down, so we only handle left button up + // if the mouse has stayed in a single location. + Point? _mouseDownAnchorPoint; + + public override void PostprocessMouseLeftButtonDown(MouseButtonEventArgs e) + { + _mouseDownAnchorPoint = RelativeToView(e.GetPosition(_view.VisualElement)); + } + + public override void PreprocessMouseMove(MouseEventArgs e) + { + if (!_mouseDownAnchorPoint.HasValue && _state.Enabled && e.LeftButton == MouseButtonState.Released) + { + TryHighlightItemUnderMouse(RelativeToView(e.GetPosition(_view.VisualElement))); + } + else if (_mouseDownAnchorPoint.HasValue) + { + // Check and see if this is a drag; if so, clear out the highlight. + var currentMousePosition = RelativeToView(e.GetPosition(_view.VisualElement)); + if (InDragOperation(_mouseDownAnchorPoint.Value, currentMousePosition)) + { + _mouseDownAnchorPoint = null; + this.SetHighlightSpan(null); + } + } + } + + private bool InDragOperation(Point anchorPoint, Point currentPoint) + { + // If the mouse up is more than a drag away from the mouse down, this is a drag + return Math.Abs(anchorPoint.X - currentPoint.X) >= SystemParameters.MinimumHorizontalDragDistance || + Math.Abs(anchorPoint.Y - currentPoint.Y) >= SystemParameters.MinimumVerticalDragDistance; + } + + public override void PreprocessMouseLeave(MouseEventArgs e) + { + _mouseDownAnchorPoint = null; + } + + public override void PreprocessMouseUp(MouseButtonEventArgs e) + { + if (_mouseDownAnchorPoint.HasValue && _state.Enabled) + { + var currentMousePosition = RelativeToView(e.GetPosition(_view.VisualElement)); + + if (!InDragOperation(_mouseDownAnchorPoint.Value, currentMousePosition)) + { + _state.Enabled = false; + + this.SetHighlightSpan(null); + _view.Selection.Clear(); + this.DispatchGoToDef(); + + e.Handled = true; + } + } + + _mouseDownAnchorPoint = null; + } + + #endregion + + #region Private helpers + + Point RelativeToView(Point position) + { + return new Point(position.X + _view.ViewportLeft, position.Y + _view.ViewportTop); + } + + bool TryHighlightItemUnderMouse(Point position) + { + bool updated = false; + + try + { + var line = _view.TextViewLines.GetTextViewLineContainingYCoordinate(position.Y); + if (line == null) + return false; + + var bufferPosition = line.GetBufferPositionFromXCoordinate(position.X); + + if (!bufferPosition.HasValue) + return false; + + // Quick check - if the mouse is still inside the current underline span, we're already set + var currentSpan = CurrentUnderlineSpan; + if (currentSpan.HasValue && currentSpan.Value.Contains(bufferPosition.Value)) + { + updated = true; + return true; + } + + var extent = _navigator.GetExtentOfWord(bufferPosition.Value); + if (!extent.IsSignificant) + return false; + + // For C#, we ignore namespaces after using statements - GoToDef will fail for those + if (_view.TextBuffer.ContentType.IsOfType("csharp")) + { + string lineText = bufferPosition.Value.GetContainingLine().GetText().Trim(); + if (lineText.StartsWith("using", StringComparison.OrdinalIgnoreCase)) + return false; + } + + // Now, check for valid classification type. C# and C++ (at least) classify the things we are interested + // in as either "identifier" or "user types" (though "identifier" will yield some false positives). VB, unfortunately, + // doesn't classify identifiers. + foreach (var classification in _aggregator.GetClassificationSpans(extent.Span)) + { + var name = classification.ClassificationType.Classification.ToLower(); + if ((name.Contains("identifier") || name.Contains("user types")) && + SetHighlightSpan(classification.Span)) + { + updated = true; + return true; + } + } + + // No update occurred, so return false + return false; + } + finally + { + if (!updated) + SetHighlightSpan(null); + } + } + + SnapshotSpan? CurrentUnderlineSpan + { + get + { + var classifier = UnderlineClassifierProvider.GetClassifierForView(_view); + if (classifier != null && classifier.CurrentUnderlineSpan.HasValue) + return classifier.CurrentUnderlineSpan.Value.TranslateTo(_view.TextSnapshot, SpanTrackingMode.EdgeExclusive); + else + return null; + } + } + + bool SetHighlightSpan(SnapshotSpan? span) + { + var classifier = UnderlineClassifierProvider.GetClassifierForView(_view); + if (classifier != null) + { + if (span.HasValue) + Mouse.OverrideCursor = Cursors.Hand; + else + Mouse.OverrideCursor = null; + + classifier.SetUnderlineSpan(span); + return true; + } + + return false; + } + + bool DispatchGoToDef() + { + Guid cmdGroup = VSConstants.GUID_VSStandardCommandSet97; + int hr = _commandTarget.Exec(ref cmdGroup, + (uint)VSConstants.VSStd97CmdID.GotoDefn, + (uint)OLECMDEXECOPT.OLECMDEXECOPT_DODEFAULT, + System.IntPtr.Zero, + System.IntPtr.Zero); + return ErrorHandler.Succeeded(hr); + } + + #endregion + } +} diff --git a/Backup/GoToDefinition-icon.png b/Backup/GoToDefinition-icon.png new file mode 100644 index 0000000..0523222 Binary files /dev/null and b/Backup/GoToDefinition-icon.png differ diff --git a/Backup/GoToDefinition-screenshot.png b/Backup/GoToDefinition-screenshot.png new file mode 100644 index 0000000..a75ae12 Binary files /dev/null and b/Backup/GoToDefinition-screenshot.png differ diff --git a/Backup/ms-pl.txt b/Backup/ms-pl.txt new file mode 100644 index 0000000..da3dc93 --- /dev/null +++ b/Backup/ms-pl.txt @@ -0,0 +1,31 @@ +Microsoft Public License (Ms-PL) + +This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. + +1. Definitions + +The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. + +A "contribution" is the original software, or any additions or changes to the software. + +A "contributor" is any person that distributes its contribution under this license. + +"Licensed patents" are a contributor's patent claims that read directly on its contribution. + +2. Grant of Rights + +(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. + +(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. + +3. Conditions and Limitations + +(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. + +(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. + +(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. + +(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. + +(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. \ No newline at end of file diff --git a/Backup/source.extension.vsixmanifest b/Backup/source.extension.vsixmanifest new file mode 100644 index 0000000..65a5a54 --- /dev/null +++ b/Backup/source.extension.vsixmanifest @@ -0,0 +1,35 @@ + + + + Go To Definition + Noah Richards + 2.6 + Make ctrl+click perform a "Go To Definition" on the identifier under the cursor. + Also, when the ctrl key is held down, highlight identifiers that look like they have + definitions to navigate to. + 1033 + http://blogs.msdn.com/noahric + ms-pl.txt + GoToDefinition-icon.png + GoToDefinition-screenshot.png + + + Pro + + + Pro + + + Pro + + + Pro + + + + + + + |%CurrentProject%| + + diff --git a/GoToDef.csproj b/GoToDef.csproj index d7c0237..1af5322 100644 --- a/GoToDef.csproj +++ b/GoToDef.csproj @@ -1,5 +1,6 @@  - + + Debug AnyCPU @@ -11,9 +12,16 @@ Properties GoToDef GoToDef - v4.0 + v4.6 512 false + 14.0 + + + + + 12.0 + true @@ -23,6 +31,7 @@ DEBUG;TRACE prompt 4 + false pdbonly @@ -31,6 +40,7 @@ TRACE prompt 4 + false @@ -104,8 +114,13 @@ + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + - + +