Extension to detect when a string get hovered on the document

Masa Yuilki 40 Reputation points
2024-10-18T00:52:53.9333333+00:00

I'm trying to create a Visual Studio 2022 extension to detect whenever a specific string on a .cpp or .h file get hovered, the project atm have only two files.

I have set a breakpoint on InitializeAsync of ImagePackage.cs, when i start debugging the extension it does hit and does print:

MEF components initialized successfully.

I also have set breakpoints on all functions of Image.cs but no one gets hit.

I'm missing something anywhere to make the hover detection to work?

ImagePackage.cs

using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft;
using Microsoft.VisualStudio.Shell;
using System;
using System.Runtime.InteropServices;
using System.Threading;
using Task = System.Threading.Tasks.Task;
using Microsoft.VisualStudio.Text.Classification;
using Microsoft.VisualStudio.Text.Operations;
using Microsoft.VisualStudio.Shell.Interop;
namespace Image
{
    [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
    [Guid(ImagePackage.PackageGuidString)]
    [ProvideAutoLoad(UIContextGuids.NoSolution, PackageAutoLoadFlags.BackgroundLoad)] // Ensure it loads when VS starts
    public sealed class ImagePackage : AsyncPackage
    {
        public const string PackageGuidString = "908f5230-d345-4ee5-a854-d9397bfa6ede";
        #region Package Members
        protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
        {
            await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
            // Get the IComponentModel service to initialize MEF components
            var componentModel = (IComponentModel)await GetServiceAsync(typeof(SComponentModel));
            Assumes.Present(componentModel);
            var navigatorService = componentModel.GetService<ITextStructureNavigatorSelectorService>();
            var formatMapService = componentModel.GetService<IEditorFormatMapService>();
            Assumes.Present(navigatorService);
            Assumes.Present(formatMapService);
            
            System.Diagnostics.Debug.WriteLine("MEF components initialized successfully.");
        }
        #endregion
    }
}

Image.cs

using System;
using System.ComponentModel.Composition;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Operations;
using Microsoft.VisualStudio.Utilities;
using Microsoft.VisualStudio.Text.Classification;
using Microsoft.VisualStudio.Text.Formatting;

namespace Image
{
    [Export(typeof(IMouseProcessorProvider))]
    [Name("WordHoverMouseProcessorProvider")]
    [ContentType("code")] //[ContentType("C/C++")]
    [TextViewRole(PredefinedTextViewRoles.Editable)]
    internal sealed class WordHoverMouseProcessorProvider : IMouseProcessorProvider
    {
        [Export(typeof(AdornmentLayerDefinition))]
        [Name("WordHoverAdornmentLayer")]
        [Order(After = PredefinedAdornmentLayers.Selection, Before = PredefinedAdornmentLayers.Text)]
        public AdornmentLayerDefinition WordHoverAdornmentLayerDefinition;

        [Import]
        internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }

        [Import]
        internal IEditorFormatMapService FormatMapService { get; set; }

        public IMouseProcessor GetAssociatedProcessor(IWpfTextView wpfTextView)
        {
            return wpfTextView.Properties.GetOrCreateSingletonProperty(() => 
                new WordHoverMouseProcessor(wpfTextView, NavigatorService.GetTextStructureNavigator(wpfTextView.TextBuffer), FormatMapService.GetEditorFormatMap(wpfTextView)));
        }
    }

    internal class WordHoverMouseProcessor : MouseProcessorBase
    {
        private readonly IWpfTextView _textView;
        private readonly ITextStructureNavigator _navigator;
        private readonly IEditorFormatMap _formatMap;
        private readonly IAdornmentLayer _adornmentLayer;

        public WordHoverMouseProcessor(IWpfTextView textView, ITextStructureNavigator navigator, IEditorFormatMap formatMap)
        {
            _textView = textView;
            _navigator = navigator;
            _formatMap = formatMap;
            _adornmentLayer = textView.GetAdornmentLayer("WordHoverAdornmentLayer");
        }

        public override void PostprocessMouseMove(MouseEventArgs e)
        {
            SnapshotPoint? point = GetMousePosition(e);

            if (!point.HasValue)
                return;

            TextExtent extent = _navigator.GetExtentOfWord(point.Value);
            string wordUnderMouse = extent.Span.GetText();

            if (wordUnderMouse.Contains("test123"))
            {
                HighlightWord(extent.Span);
                MessageBox.Show("Word 'test123' detected!", "Word Hover Extension");
            }
            else
            {
                ClearHighlight();
            }
        }

        private SnapshotPoint? GetMousePosition(MouseEventArgs e)
        {
            Point position = e.GetPosition(_textView.VisualElement);
            return _textView.TextViewLines.GetTextViewLineContainingYCoordinate(position.Y)
                ?.GetBufferPositionFromXCoordinate(position.X);
        }

        private void HighlightWord(SnapshotSpan span)
        {
            var properties = _formatMap.GetProperties("Selected Text");
            var brush = properties["Background"] as SolidColorBrush;

            if (brush != null)
            {
                var geometry = _textView.TextViewLines.GetMarkerGeometry(span);
                
                if (geometry != null)
                {
                    var drawing = new GeometryDrawing(brush, new Pen(Brushes.Red, 0.5), geometry);
                    drawing.Freeze();

                    var drawingImage = new DrawingImage(drawing);
                    drawingImage.Freeze();

                    var image = new System.Windows.Controls.Image
                    {
                        Source = drawingImage,
                    };

                    _adornmentLayer.AddAdornment(AdornmentPositioningBehavior.TextRelative, span, null, image, null);
                }
            }
        }

        private void ClearHighlight()
        {
            _adornmentLayer.RemoveAllAdornments();
        }
    }
}

.

Visual Studio
Visual Studio
A family of Microsoft suites of integrated development tools for building applications for Windows, the web and mobile devices.
5,307 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
11,152 questions
Visual Studio Extensions
Visual Studio Extensions
Visual Studio: A family of Microsoft suites of integrated development tools for building applications for Windows, the web and mobile devices.Extensions: A program or program module that adds functionality to or extends the effectiveness of a program.
235 questions
0 comments No comments
{count} votes

Accepted answer
  1. gekka 10,561 Reputation points MVP
    2024-10-18T03:51:51.46+00:00

    I tried your code and it worked as expected.

    Have you added MefComponent to Assets in your VSIXManifest file?

    20241018_A

    Let's modify the constructor of the class so that you can see it being loaded.

    namespace Image
    {
        [Export(typeof(IMouseProcessorProvider))]
        [Name("WordHoverMouseProcessorProvider")]
        [ContentType("code")] //[ContentType("C/C++")]
        [TextViewRole(PredefinedTextViewRoles.Editable)]
        internal sealed class WordHoverMouseProcessorProvider : IMouseProcessorProvider
        {
            static WordHoverMouseProcessorProvider()
            {
                System.Diagnostics.Debug.WriteLine("Success MEF Load");
            }
    
            public WordHoverMouseProcessorProvider()
            {
    
            }
    
    1 person found this answer helpful.
    0 comments No comments

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.