├── preview.gif ├── Resources ├── icon.png └── HighlightText.png ├── Shared ├── GlobalSuppressions.cs ├── JumpLabelUserControl.xaml ├── BaseOptionPage.cs ├── Shared.shproj ├── CommandExecutorService.cs ├── PeasyMotionEdAdornmentTextViewCreationListener.cs ├── PeasyMotionPackage1.cs ├── Shared.projitems ├── JumpLabelClassifierProvider.cs ├── JumpLabelUserControl.xaml.cs ├── InputListener.cs ├── PeasyMotionPackage.cs ├── HResult.cs ├── VsMethodExtensions.cs ├── BaseOptionModel.cs ├── PeasyMotionPackage.vsct ├── InfoBarService.cs ├── VsSettings.cs ├── PeasyMotionOptions.cs └── PeasyMotionActivate.cs ├── appveyor.yml ├── LICENSE.txt ├── PeasyMotion ├── Properties │ └── AssemblyInfo.cs ├── source.extension.vsixmanifest └── PeasyMotion.csproj ├── PeasyMotion2022 ├── Properties │ └── AssemblyInfo.cs ├── source.extension.vsixmanifest └── PeasyMotion2022.csproj ├── CHANGELOG.md ├── PeasyMotion.sln ├── .gitattributes ├── CONTRIBUTING.md ├── .gitignore └── README.md /preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msomeone/PeasyMotion/HEAD/preview.gif -------------------------------------------------------------------------------- /Resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msomeone/PeasyMotion/HEAD/Resources/icon.png -------------------------------------------------------------------------------- /Resources/HighlightText.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msomeone/PeasyMotion/HEAD/Resources/HighlightText.png -------------------------------------------------------------------------------- /Shared/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. 3 | // Project-level suppressions either have no target or are given 4 | // a specific target and scoped to a namespace, type, member, etc. 5 | 6 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "", Scope = "member", Target = "~M:PeasyMotion.JumpLabelUserControl.GetTrimmedLabel(System.String)~System.String")] 7 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | image: Visual Studio 2019 2 | 3 | install: 4 | - ps: (new-object Net.WebClient).DownloadString("https://raw.github.com/madskristensen/ExtensionScripts/master/AppVeyor/vsix.ps1") | iex 5 | 6 | before_build: 7 | - ps: Vsix-IncrementVsixVersion | Vsix-UpdateBuildVersion 8 | #- ps: Vsix-TokenReplacement {source.extension.cs} 'Version = "([0-9\\.]+)"' 'Version = "{version}"' 9 | 10 | build_script: 11 | - nuget restore -Verbosity quiet 12 | - msbuild /p:configuration=Release /p:DeployExtension=false /p:ZipPackageCompressionLevel=normal /v:m 13 | 14 | after_test: 15 | - ps: Vsix-PushArtifacts | Vsix-PublishToGallery 16 | -------------------------------------------------------------------------------- /Shared/JumpLabelUserControl.xaml: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /Shared/BaseOptionPage.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Shell; 2 | 3 | namespace PeasyMotion.Options 4 | { 5 | /// 6 | /// A base class for a DialogPage to show in Tools -> Options. 7 | /// 8 | internal class BaseOptionPage : DialogPage where T : BaseOptionModel, new() 9 | { 10 | public BaseOptionModel _model; 11 | 12 | public BaseOptionPage() 13 | { 14 | #pragma warning disable VSTHRD104 // Offer async methods 15 | _model = ThreadHelper.JoinableTaskFactory.Run(BaseOptionModel.CreateAsync); 16 | #pragma warning restore VSTHRD104 // Offer async methods 17 | } 18 | 19 | public override object AutomationObject => _model; 20 | 21 | public override void LoadSettingsFromStorage() 22 | { 23 | _model.Load(); 24 | } 25 | 26 | public override void SaveSettingsToStorage() 27 | { 28 | _model.Save(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Shared/Shared.shproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | fa3a3bc8-1780-40d0-af0d-087d7d92d516 5 | 14.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Maksim Vorobiev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /PeasyMotion/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("PeasyMotion")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("PeasyMotion")] 13 | [assembly: AssemblyCopyright("")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // Version information for an assembly consists of the following four values: 23 | // 24 | // Major Version 25 | // Minor Version 26 | // Build Number 27 | // Revision 28 | // 29 | // You can specify all the values or you can default the Build and Revision Numbers 30 | // by using the '*' as shown below: 31 | // [assembly: AssemblyVersion("1.0.*")] 32 | [assembly: AssemblyVersion("1.0.0.0")] 33 | [assembly: AssemblyFileVersion("1.0.0.0")] 34 | -------------------------------------------------------------------------------- /PeasyMotion2022/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("PeasyMotion2022")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("PeasyMotion2022")] 13 | [assembly: AssemblyCopyright("")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // Version information for an assembly consists of the following four values: 23 | // 24 | // Major Version 25 | // Minor Version 26 | // Build Number 27 | // Revision 28 | // 29 | // You can specify all the values or you can default the Build and Revision Numbers 30 | // by using the '*' as shown below: 31 | // [assembly: AssemblyVersion("1.0.*")] 32 | [assembly: AssemblyVersion("1.0.0.0")] 33 | [assembly: AssemblyFileVersion("1.0.0.0")] 34 | -------------------------------------------------------------------------------- /Shared/CommandExecutorService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Microsoft.VisualStudio; 7 | using Microsoft.VisualStudio.Shell; 8 | using Microsoft.VisualStudio.Shell.Interop; 9 | using EnvDTE; 10 | using System.ComponentModel.Design; 11 | using EnvDTE80; 12 | 13 | namespace PeasyMotion 14 | { 15 | public class CommandExecutorService 16 | { 17 | readonly DTE _dte; 18 | 19 | public CommandExecutorService() 20 | { 21 | ThreadHelper.ThrowIfNotOnUIThread(); 22 | _dte = Package.GetGlobalService(typeof(SDTE)) as DTE; 23 | } 24 | 25 | public bool IsCommandAvailable(string commandName) 26 | { 27 | ThreadHelper.ThrowIfNotOnUIThread(); 28 | return FindCommand(_dte.Commands, commandName) != null; 29 | } 30 | 31 | public void Execute(string commandName) 32 | { 33 | ThreadHelper.ThrowIfNotOnUIThread(); 34 | _dte.ExecuteCommand(commandName); 35 | } 36 | 37 | private static dynamic FindCommand(Commands commands, string commandName) 38 | { 39 | foreach (var command in commands) 40 | { 41 | if (((dynamic)command).Name == commandName) 42 | { 43 | return command; 44 | } 45 | } 46 | return null; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Shared/PeasyMotionEdAdornmentTextViewCreationListener.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using Microsoft.VisualStudio.Text.Editor; 3 | using Microsoft.VisualStudio.Utilities; 4 | 5 | namespace PeasyMotion 6 | { 7 | /// 8 | /// Establishes an to place the adornment on and exports the 9 | /// that instantiates the adornment on the event of a 's creation 10 | /// 11 | [Export(typeof(IWpfTextViewCreationListener))] 12 | [ContentType("text")] 13 | [TextViewRole(PredefinedTextViewRoles.Editable)] 14 | internal sealed class PeasyMotionEdAdornmentTextViewCreationListener : IWpfTextViewCreationListener 15 | { 16 | // Disable "Field is never assigned to..." and "Field is never used" compiler's warnings. Justification: the field is used by MEF. 17 | #pragma warning disable 649, 169 18 | 19 | /// 20 | /// Defines the adornment layer for the adornment. This layer is ordered 21 | /// after the selection layer in the Z-order 22 | /// 23 | [Export(typeof(AdornmentLayerDefinition))] 24 | [Name("PeasyMotionEdAdornment")] 25 | [Order(After = PredefinedAdornmentLayers.Caret)] 26 | private AdornmentLayerDefinition editorAdornmentLayer; 27 | 28 | #pragma warning restore 649, 169 29 | 30 | #region IWpfTextViewCreationListener 31 | 32 | /// 33 | /// Called when a text view having matching roles is created over a text data model having a matching content type. 34 | /// Instantiates a PeasyMotionEdAdornment manager when the textView is created. 35 | /// 36 | /// The upon which the adornment should be placed 37 | public void TextViewCreated(IWpfTextView textView) 38 | { 39 | _ = new PeasyMotionEdAdornment(); 40 | } 41 | 42 | #endregion 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /PeasyMotion/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PeasyMotion 2019 6 | Implements several vim-easymotion motions (word,line,two-char search), fast tab / document switching, fast text region selection. 7 | This extension differs from other motion/jump extensions as it assigns jump labels to all words in text viewport, without asking specific "jump" key. 8 | Such a behaviour may lead to faster motion and navigation in certain scenarios. 9 | Inspired by vim-easymotion. 10 | 11 | LICENSE.txt 12 | icon.png 13 | preview.gif 14 | Productivity, Power tools, Navigation, quick jump, Motion, EasyMotion, Jump, AceJump, CocoJumper, 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Shared/PeasyMotionPackage1.cs: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------ 2 | // 3 | // This file was generated by VSIX Synchronizer 4 | // 5 | // ------------------------------------------------------------------------------ 6 | namespace PeasyMotion 7 | { 8 | using System; 9 | 10 | /// 11 | /// Helper class that exposes all GUIDs used across VS Package. 12 | /// 13 | internal sealed partial class PackageGuids 14 | { 15 | public const string guidPeasyMotionPackageString = "4fa9da7b-5f7c-4d43-8a46-9326a6eb6eab"; 16 | public static Guid guidPeasyMotionPackage = new Guid(guidPeasyMotionPackageString); 17 | 18 | public const string guidPeasyMotionPackageCmdSetString = "921fde78-c60b-4458-af50-fbb52d4b6a63"; 19 | public static Guid guidPeasyMotionPackageCmdSet = new Guid(guidPeasyMotionPackageCmdSetString); 20 | 21 | public const string guidImagesString = "3e6d0d87-f85a-45e7-bf87-a8f3a94c781b"; 22 | public static Guid guidImages = new Guid(guidImagesString); 23 | } 24 | /// 25 | /// Helper class that encapsulates all CommandIDs uses across VS Package. 26 | /// 27 | internal sealed partial class PackageIds 28 | { 29 | public const int TopMenuGroup = 0x1020; 30 | public const int SubMenu = 0x1100; 31 | public const int SubMenuGroup = 0x1150; 32 | public const int PeasyMotionActivateId = 0x0100; 33 | public const int PeasyMotionSelectTextActivateId = 0x0101; 34 | public const int PeasyMotionLineJumpToWordBeginingId = 0x0102; 35 | public const int PeasyMotionLineJumpToWordEndingId = 0x0103; 36 | public const int PeasyMotionJumpToDocumentTab = 0x0104; 37 | public const int PeasyMotionJumpToLineBegining = 0x0105; 38 | public const int PeasyMotionTwoCharJump = 0x0106; 39 | public const int PeasyMotionOneCharJump = 0x0107; 40 | public const int bmpHighlightText = 0x0001; 41 | } 42 | } -------------------------------------------------------------------------------- /Shared/Shared.projitems: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 5 | true 6 | fa3a3bc8-1780-40d0-af0d-087d7d92d516 7 | 8 | 9 | Shared 10 | 11 | 12 | 13 | 14 | Component 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | $(MSBuildThisFileDirectory)JumpLabelUserControl.xaml 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | MSBuild:Compile 35 | Designer 36 | 37 | 38 | -------------------------------------------------------------------------------- /PeasyMotion2022/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PeasyMotion 2022 6 | Implements several vim-easymotion motions (word,line,two-char search), fast tab / document switching, fast text region selection. 7 | This extension differs from other motion/jump extensions as it assigns jump labels to all words in text viewport, without asking specific "jump" key. 8 | Such a behaviour may lead to faster motion and navigation in certain scenarios. 9 | Inspired by vim-easymotion. 10 | 11 | LICENSE.txt 12 | icon.png 13 | preview.gif 14 | Productivity, Power tools, Navigation, quick jump, Motion, EasyMotion, Jump, AceJump, CocoJumper, 15 | 16 | 17 | 18 | amd64 19 | 20 | 21 | arm64 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 1.8.1 2 | - One characted search mode has beed added. Invoke via Tools.InvokePeasyMotionOneCharJump 3 | - Jump to Word End cursor fix for non-vim mode 4 | - Caret stays in position after jumping with any text selected, text selection is cleared 5 | - move 2022 & 2019 build to master 6 | 7 | ### 1.6.1 8 | - Two characted search mode has beed added. Invoke via Tools.InvokePeasyMotionTwoCharJump 9 | - Jump to line begining fix - non-empty lines have jump label on first 'visible' character 10 | - Added status bar text showing peasy motion status 11 | 12 | ### 1.5.1 13 | - Jump to line begining added via Tools.InvokePeasyMotionJumpToLineBegining 14 | - Improved EOL convention handling 15 | - Improved close jump labels readability 16 | 17 | ### 1.4.2 18 | - Jump to open document tab via Tools.InvokePeasyMotionJumpToDocumentTa has been added 19 | - Fixed response on ';' presence in jump label 20 | - Jump label assignment algorithm fix: no more skipped candidates (i hope). 21 | - Autoload and warmup improved, leading to faster UI response on first command invocation 22 | 23 | ### 1.3.1 24 | - Moved Tools->Invoke * PeasyMotion* commands into separate Tools->PeasyMotion submenu 25 | - Crash from uncaught exception during VsSettings initialization has been fixed 26 | - Correct deactivation when wrong keys pressed 27 | 28 | ### 1.2.1 29 | - Jump to word begining or ending in current line via Tools.InvokePeasyMotionLineJumpToWordBegining or Tools.InvokePeasyMotionLineJumpToWordEnding 30 | - Jump label positioning algorithm has been improved: catches empty lines, better EOL handling, a lil bit closer to vim-easymotion behaviour 31 | 32 | ### 1.1.1 33 | - Text Selection via JumpLabel ( Tools.InvokePeasyMotionTextSelect ) 34 | - "Whats New" notification (InfoBar) added, to notify users after auto-update 35 | - Improved ViEmu support (no more caret one-char offset after jump) 36 | - Minor cleanups 37 | 38 | ### 1.0.21 39 | - Fixed wrong font color for reused jump label UI controls (color was red instead of black for some labels) 40 | 41 | ### 1.0.20 42 | - Options added (Tools -> Options -> PeasyMotion options -> ...) 43 | - Added caret position sensivity option. 44 | - Added jump label assignment algorithm option. 45 | - Shaved off 60% command execution time: (~200-400ms) -> (~50ms), slowest part is adornment addition. 46 | - JumpLabel user controls are cached now (speeds up adornments addition), 47 | - Cached VsVim commands availability, to prevent slow DTE.Commands search on PeasyMotion activation. -------------------------------------------------------------------------------- /Shared/JumpLabelClassifierProvider.cs: -------------------------------------------------------------------------------- 1 | //*************************************************************************** 2 | // 3 | // Copyright (c) Microsoft Corporation. All rights reserved. 4 | // This code is licensed under the Visual Studio SDK license terms. 5 | // THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF 6 | // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY 7 | // IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR 8 | // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. 9 | // 10 | //*************************************************************************** 11 | 12 | using System; 13 | using System.Collections.Generic; 14 | using System.Linq; 15 | using System.Text; 16 | using System.ComponentModel.Composition; 17 | using Microsoft.VisualStudio.Text.Classification; 18 | using Microsoft.VisualStudio.Utilities; 19 | using Microsoft.VisualStudio.Text.Tagging; 20 | using Microsoft.VisualStudio.Text.Editor; 21 | using Microsoft.VisualStudio.Text; 22 | using System.Windows.Media; 23 | 24 | namespace PeasyMotion 25 | { 26 | /// 27 | // Fake format definition, we do not add any classifiers. All we want to do is add label color option into Fonts And Colors TextEditor category 28 | // That is why we provide one format Definition. 29 | /// 30 | [Export(typeof(EditorFormatDefinition))] 31 | [Name("PeasyMotionJumpLabelFirstMotion")] 32 | [UserVisible(true)] 33 | internal sealed class JumpLabelFirstMotionFormatDef : ClassificationFormatDefinition 34 | { 35 | public const string FMT_NAME = "PeasyMotionJumpLabelFirstMotion"; 36 | public JumpLabelFirstMotionFormatDef() 37 | { 38 | DisplayName = "PeasyMotion First Motion Jump label color"; //human readable version of the name 39 | BackgroundOpacity = 1; 40 | BackgroundColor = System.Windows.Media.Colors.Black; 41 | ForegroundColor = System.Windows.Media.Colors.LightGray; 42 | } 43 | } 44 | 45 | [Export(typeof(EditorFormatDefinition))] 46 | [Name("PeasyMotionJumpLabelFinalMotion")] 47 | [UserVisible(true)] 48 | internal sealed class JumpLabelFinalMotionFormatDef : ClassificationFormatDefinition 49 | { 50 | public const string FMT_NAME = "PeasyMotionJumpLabelFinalMotion"; 51 | public JumpLabelFinalMotionFormatDef() 52 | { 53 | DisplayName = "PeasyMotion Final Motion Jump label color"; //human readable version of the name 54 | BackgroundOpacity = 1; 55 | BackgroundColor = System.Windows.Media.Colors.LightGray; 56 | ForegroundColor = System.Windows.Media.Colors.Red; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /PeasyMotion.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29403.142 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PeasyMotion", "PeasyMotion\PeasyMotion.csproj", "{E14D8C11-BCE9-4928-8DF9-5EDAF485F2E5}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BA5C4E4D-73D0-47EA-AF35-42B392562302}" 9 | ProjectSection(SolutionItems) = preProject 10 | .gitattributes = .gitattributes 11 | .gitignore = .gitignore 12 | appveyor.yml = appveyor.yml 13 | CHANGELOG.md = CHANGELOG.md 14 | CONTRIBUTING.md = CONTRIBUTING.md 15 | LICENSE.txt = LICENSE.txt 16 | README.md = README.md 17 | EndProjectSection 18 | EndProject 19 | Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Shared", "Shared\Shared.shproj", "{FA3A3BC8-1780-40D0-AF0D-087D7D92D516}" 20 | EndProject 21 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PeasyMotion2022", "PeasyMotion2022\PeasyMotion2022.csproj", "{DA591336-360E-41A0-B8A8-E212C12B8AAA}" 22 | EndProject 23 | Global 24 | GlobalSection(SharedMSBuildProjectFiles) = preSolution 25 | Shared\Shared.projitems*{da591336-360e-41a0-b8a8-e212c12b8aaa}*SharedItemsImports = 4 26 | Shared\Shared.projitems*{e14d8c11-bce9-4928-8df9-5edaf485f2e5}*SharedItemsImports = 4 27 | Shared\Shared.projitems*{fa3a3bc8-1780-40d0-af0d-087d7d92d516}*SharedItemsImports = 13 28 | EndGlobalSection 29 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 30 | Debug|Any CPU = Debug|Any CPU 31 | Release|Any CPU = Release|Any CPU 32 | EndGlobalSection 33 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 34 | {E14D8C11-BCE9-4928-8DF9-5EDAF485F2E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {E14D8C11-BCE9-4928-8DF9-5EDAF485F2E5}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {E14D8C11-BCE9-4928-8DF9-5EDAF485F2E5}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {E14D8C11-BCE9-4928-8DF9-5EDAF485F2E5}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {DA591336-360E-41A0-B8A8-E212C12B8AAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {DA591336-360E-41A0-B8A8-E212C12B8AAA}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {DA591336-360E-41A0-B8A8-E212C12B8AAA}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {DA591336-360E-41A0-B8A8-E212C12B8AAA}.Release|Any CPU.Build.0 = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(SolutionProperties) = preSolution 44 | HideSolutionNode = FALSE 45 | EndGlobalSection 46 | GlobalSection(ExtensibilityGlobals) = postSolution 47 | SolutionGuid = {32B4A596-794B-4EC7-A1E6-C1BBFD667128} 48 | EndGlobalSection 49 | EndGlobal 50 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /Shared/JumpLabelUserControl.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Windows; 4 | using System.Windows.Controls; 5 | using System.Windows.Media; 6 | 7 | namespace PeasyMotion 8 | { 9 | 10 | public partial class JumpLabelUserControl : UserControl 11 | { 12 | public struct CachedSetupParams { 13 | public double fontRenderingEmSize; 14 | public System.Windows.Media.Typeface typeface; 15 | public SolidColorBrush labelBg; 16 | public SolidColorBrush labelFg; 17 | public SolidColorBrush labelFinalMotionBg; 18 | public SolidColorBrush labelFinalMotionFg; 19 | public void Freeze() { 20 | labelFg.Freeze(); 21 | labelBg.Freeze(); 22 | labelFinalMotionBg.Freeze(); 23 | labelFinalMotionFg.Freeze(); 24 | } 25 | }; 26 | 27 | private static string GetTrimmedLabel(string label) // trim max to two characters 28 | { 29 | _ = label ?? throw new ArgumentNullException(nameof(label), "cannot be null"); 30 | return label.Substring(0, Math.Min(label.Length, 2)); 31 | //return label; // debug: show full jump label 32 | } 33 | public JumpLabelUserControl() 34 | { 35 | InitializeComponent(); 36 | } 37 | 38 | public void setup(string label, Rect bounds, JumpLabelUserControl.CachedSetupParams cachedParams) 39 | { 40 | var str = GetTrimmedLabel(label); 41 | this.Content = str; 42 | this.Background = cachedParams.labelBg; 43 | this.Foreground = cachedParams.labelFg; 44 | 45 | this.FontSize = cachedParams.fontRenderingEmSize; 46 | this.FontFamily = cachedParams.typeface.FontFamily; 47 | this.FontStyle = cachedParams.typeface.Style; 48 | //this.FontWeight = cachedParams.typeface.Weight; 49 | 50 | Canvas.SetLeft(this, bounds.Left - this.Padding.Left); 51 | Canvas.SetTop(this, bounds.Top - this.Padding.Top); 52 | } 53 | 54 | public void UpdateView(string alreadyPressedKeys, JumpLabelUserControl.CachedSetupParams cachedParams) 55 | { 56 | _ = alreadyPressedKeys ?? throw new ArgumentNullException(nameof(alreadyPressedKeys), "cannot be null"); 57 | 58 | var str = GetTrimmedLabel(alreadyPressedKeys); 59 | this.Content = str; 60 | if (str.Length == 1) { 61 | this.Background = cachedParams.labelFinalMotionBg; 62 | this.Foreground = cachedParams.labelFinalMotionFg; 63 | } 64 | } 65 | 66 | private static Stack cache = new Stack(1<<13); 67 | 68 | // warmup just a lil bit, dont allocate whole capacity, as it may slow down on startup. 69 | public static void WarmupCache() 70 | { 71 | for (int i = 0; i < 4096; i++) // should be enough for viewport full of short words on ~3k screen with 14pt font 72 | { 73 | cache.Push(new JumpLabelUserControl()); 74 | } 75 | } 76 | public static JumpLabelUserControl GetFreeUserControl() 77 | { 78 | JumpLabelUserControl ctrl = null; 79 | if (1 > cache.Count) 80 | { 81 | ctrl = new JumpLabelUserControl(); 82 | } else { 83 | ctrl = cache.Pop(); 84 | } 85 | return ctrl; 86 | } 87 | 88 | public static void ReleaseUserControl(JumpLabelUserControl ctrl) { 89 | cache.Push(ctrl); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Shared/InputListener.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Windows.Forms; 6 | using Microsoft.VisualStudio; 7 | using Microsoft.VisualStudio.OLE.Interop; 8 | //using Microsoft.VisualStudio.ProjectSystem; 9 | using Microsoft.VisualStudio.Shell; 10 | using Microsoft.VisualStudio.Text.Editor; 11 | using Microsoft.VisualStudio.TextManager.Interop; 12 | 13 | namespace PeasyMotion 14 | { 15 | class InputListener : IOleCommandTarget 16 | { 17 | public readonly IVsTextView vsTextView; 18 | public ITextView textView; 19 | IOleCommandTarget nextCommandHandler; 20 | 21 | public event KeyPressEventHandler KeyPressed; 22 | 23 | /// 24 | /// Add this filter to the chain of Command Filters 25 | /// 26 | internal InputListener(IVsTextView vsTextView_, ITextView textView_) 27 | { 28 | vsTextView = vsTextView_; 29 | textView = textView_; 30 | } 31 | 32 | public void AddFilter() 33 | { 34 | vsTextView.AddCommandFilter(this, out nextCommandHandler); 35 | 36 | } 37 | 38 | public void RemoveFilter() 39 | { 40 | vsTextView.RemoveCommandFilter(this); 41 | nextCommandHandler = null; 42 | } 43 | public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) 44 | { 45 | ThreadHelper.ThrowIfNotOnUIThread(); 46 | return nextCommandHandler.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText); 47 | } 48 | 49 | /// 50 | /// Get user input. 51 | /// IOleCommandTarget.Exec() function 52 | /// 53 | public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) 54 | { 55 | Debug.WriteLine($"InputListener.Exec( {pguidCmdGroup} {nCmdID} {nCmdexecopt} {pvaIn} {pvaOut} )"); 56 | ThreadHelper.ThrowIfNotOnUIThread(); 57 | int hr = VSConstants.S_OK; 58 | 59 | if ((new[] { 60 | 4, // tab 61 | 7, // left arrow 62 | 11, // up arrow 63 | 9, // right arrow 64 | 13, // down arrow 65 | 103, // escape 66 | 32, // space 67 | }).Contains((int)nCmdID)) 68 | { 69 | // send '\0' so we can abort 70 | KeyPressed?.Invoke(this,new KeyPressEventArgs('\0')); 71 | return hr; 72 | } 73 | 74 | char typedChar; 75 | if (TryGetTypedChar(pguidCmdGroup, nCmdID, pvaIn, out typedChar)) 76 | { 77 | KeyPressed?.Invoke(this, new KeyPressEventArgs(typedChar)); 78 | return hr; 79 | } 80 | 81 | ThreadHelper.ThrowIfNotOnUIThread(); 82 | hr = nextCommandHandler.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); 83 | 84 | return hr; 85 | } 86 | 87 | /// 88 | /// Try to get the keypress value. Returns 0 if attempt fails 89 | /// 90 | /// Outputs the value of the typed char 91 | /// Boolean reporting success or failure of operation 92 | bool TryGetTypedChar(Guid cmdGroup, uint nCmdID, IntPtr pvaIn, out char typedChar) 93 | { 94 | typedChar = char.MinValue; 95 | //Debug.WriteLine("InputListener.cs | TryGetTypedChar | nCmdId " + nCmdID); 96 | //Debug.WriteLine("InputListener.cs | TryGetTypedChar | pvaIn " + pvaIn); 97 | 98 | if (cmdGroup != VSConstants.VSStd2K || nCmdID != (uint)VSConstants.VSStd2KCmdID.TYPECHAR) 99 | return false; 100 | 101 | 102 | typedChar = (char)(ushort)Marshal.GetObjectForNativeVariant(pvaIn); 103 | return true; 104 | } 105 | } 106 | } -------------------------------------------------------------------------------- /Shared/PeasyMotionPackage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using System.Diagnostics; 4 | using System.Runtime.InteropServices; 5 | using System.Threading; 6 | using Microsoft.VisualStudio; 7 | using Microsoft.VisualStudio.Shell; 8 | using Microsoft.VisualStudio.Text.Operations; 9 | using Microsoft.VisualStudio.OLE.Interop; 10 | using Microsoft.VisualStudio.Shell.Interop; 11 | using Task = System.Threading.Tasks.Task; 12 | 13 | namespace PeasyMotion 14 | { 15 | /// 16 | /// This is the class that implements the package exposed by this assembly. 17 | /// 18 | /// 19 | /// 20 | /// The minimum requirement for a class to be considered a valid package for Visual Studio 21 | /// is to implement the IVsPackage interface and register itself with the shell. 22 | /// This package uses the helper classes defined inside the Managed Package Framework (MPF) 23 | /// to do it: it derives from the Package class that provides the implementation of the 24 | /// IVsPackage interface and uses the registration attributes defined in the framework to 25 | /// register itself and its components with the shell. These attributes tell the pkgdef creation 26 | /// utility what data to put into .pkgdef file. 27 | /// 28 | /// 29 | /// To get loaded into VS, the package must be referred by <Asset Type="Microsoft.VisualStudio.VsPackage" ...> in .vsixmanifest file. 30 | /// 31 | /// 32 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] 33 | [ProvideAutoLoad(VSConstants.UICONTEXT.ShellInitialized_string, PackageAutoLoadFlags.BackgroundLoad)] 34 | [Guid(PeasyMotionPackage.PackageGuidString)] 35 | [ProvideMenuResource("Menus.ctmenu", 1)] 36 | [ProvideOptionPage(typeof(DialogPageProvider.General), "PeasyMotion options", "General", 101, 106, true)] 37 | public sealed class PeasyMotionPackage : AsyncPackage 38 | { 39 | public const bool MeasureExecTime = false; 40 | /// 41 | /// PeasyMotionPackage GUID string. 42 | /// 43 | public const string PackageGuidString = "4fa9da7b-5f7c-4d43-8a46-9326a6eb6eab"; 44 | 45 | #region Package Members 46 | 47 | /// 48 | /// Initialization of the package; this method is called right after the package is sited, so this is the place 49 | /// where you can put all the initialization code that rely on services provided by VisualStudio. 50 | /// 51 | /// A cancellation token to monitor for initialization cancellation, which can occur when VS is shutting down. 52 | /// A provider for progress updates. 53 | /// A task representing the async work of package initialization, or an already completed task if there is none. Do not return null from this method. 54 | protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) 55 | { 56 | // When initialized asynchronously, the current thread may be a background thread at this point. 57 | // Do any initialization that requires the UI thread after switching to the UI thread. 58 | await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); 59 | await PeasyMotionActivate.InitializeAsync(this).ConfigureAwait(true); 60 | 61 | await base.InitializeAsync(cancellationToken, progress); 62 | } 63 | 64 | //TODO: test editor command & input field 65 | //TODO: how to DocTabNavJump from non-text editors ?!?? how to listen to kb globally? 66 | //TODO: how to cancel jump if text view was switched? | HANDLE WpfTextView change / focus change! 67 | //TODO: hot keys fucked up! 68 | //TODO: MAYBE PROVIDE AN OPTION TO CONFIGURE LABEL TEXT DECORATION??? for getDocumentTabCaptionWithLabel 69 | 70 | //TODO: HOW TO SETUP AN INDEX?!?!?!?!? (find a way to measure distance between current document and target tab) 71 | /// v-----^ <--- related issues 72 | //TODO: separate property for Tab Label assignment Algo selection??? 73 | 74 | #endregion 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Shared/HResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.VisualStudio; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace PeasyMotion 7 | { 8 | // based on code from VsVim. 9 | /* VsVim 10 | Copyright 2012 Jared Parsons 11 | 12 | Licensed under the Apache License, Version 2.0 (the "License"); 13 | you may not use this file except in compliance with the License. 14 | You may obtain a copy of the License at 15 | 16 | http://www.apache.org/licenses/LICENSE-2.0 17 | 18 | Unless required by applicable law or agreed to in writing, software 19 | distributed under the License is distributed on an "AS IS" BASIS, 20 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | See the License for the specific language governing permissions and 22 | limitations under the License. 23 | */ 24 | 25 | public readonly struct Result 26 | { 27 | private readonly bool _isSuccess; 28 | private readonly int _hresult; 29 | 30 | public bool IsSuccess 31 | { 32 | get { return _isSuccess; } 33 | } 34 | 35 | public bool IsError 36 | { 37 | get { return !_isSuccess; } 38 | } 39 | 40 | public int HResult 41 | { 42 | get 43 | { 44 | if (!IsError) 45 | { 46 | throw new InvalidOperationException(); 47 | } 48 | return _hresult; 49 | } 50 | } 51 | 52 | private Result(int hresult) 53 | { 54 | _hresult = hresult; 55 | _isSuccess = ErrorHandler.Succeeded(hresult); 56 | } 57 | 58 | public static Result Error 59 | { 60 | get { return new Result(VSConstants.E_FAIL); } 61 | } 62 | 63 | public static Result Success 64 | { 65 | get { return new Result(VSConstants.S_OK); } 66 | } 67 | 68 | public static Result CreateSuccess(T value) 69 | { 70 | return new Result(value); 71 | } 72 | 73 | public static Result CreateSuccessNonNull(T value) 74 | where T : class 75 | { 76 | if (value == null) 77 | { 78 | return Result.Error; 79 | } 80 | 81 | return new Result(value); 82 | } 83 | 84 | public static Result CreateError(int value) 85 | { 86 | return new Result(value); 87 | } 88 | 89 | public static Result CreateError(Exception ex) 90 | { 91 | return CreateError(Marshal.GetHRForException(ex)); 92 | } 93 | 94 | public static Result CreateSuccessOrError(T potentialValue, int hresult) 95 | { 96 | return ErrorHandler.Succeeded(hresult) 97 | ? CreateSuccess(potentialValue) 98 | : new Result(hresult: hresult); 99 | } 100 | } 101 | 102 | public readonly struct Result 103 | { 104 | private readonly bool _isSuccess; 105 | private readonly T _value; 106 | private readonly int _hresult; 107 | 108 | public bool IsSuccess 109 | { 110 | get { return _isSuccess; } 111 | } 112 | 113 | public bool IsError 114 | { 115 | get { return !_isSuccess; } 116 | } 117 | 118 | // TOOD: Get rid of this. Make it a method that says throws 119 | public T Value 120 | { 121 | get 122 | { 123 | if (!IsSuccess) 124 | { 125 | throw new InvalidOperationException(); 126 | } 127 | 128 | return _value; 129 | } 130 | } 131 | 132 | public int HResult 133 | { 134 | get 135 | { 136 | if (IsSuccess) 137 | { 138 | throw new InvalidOperationException(); 139 | } 140 | 141 | return _hresult; 142 | } 143 | } 144 | 145 | public Result(T value) 146 | { 147 | _value = value; 148 | _isSuccess = true; 149 | _hresult = 0; 150 | } 151 | 152 | public Result(int hresult) 153 | { 154 | _hresult = hresult; 155 | _isSuccess = false; 156 | _value = default; 157 | } 158 | 159 | public T GetValueOrDefault(T defaultValue = default) 160 | { 161 | return IsSuccess ? Value : defaultValue; 162 | } 163 | 164 | public bool TryGetValue(out T value) 165 | { 166 | if (IsSuccess) 167 | { 168 | value = Value; 169 | return true; 170 | } 171 | 172 | value = default; 173 | return false; 174 | } 175 | 176 | public static implicit operator Result(Result result) 177 | { 178 | return new Result(hresult: result.HResult); 179 | } 180 | 181 | public static implicit operator Result(T value) 182 | { 183 | return new Result(value); 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Looking to contribute something? **Here's how you can help.** 4 | 5 | Please take a moment to review this document in order to make the contribution 6 | process easy and effective for everyone involved. 7 | 8 | Following these guidelines helps to communicate that you respect the time of 9 | the developers managing and developing this open source project. In return, 10 | they should reciprocate that respect in addressing your issue or assessing 11 | patches and features. 12 | 13 | 14 | ## Using the issue tracker 15 | 16 | The issue tracker is the preferred channel for [bug reports](#bug-reports), 17 | [features requests](#feature-requests) and 18 | [submitting pull requests](#pull-requests), but please respect the 19 | following restrictions: 20 | 21 | * Please **do not** use the issue tracker for personal support requests. Stack 22 | Overflow is a better place to get help. 23 | 24 | * Please **do not** derail or troll issues. Keep the discussion on topic and 25 | respect the opinions of others. 26 | 27 | * Please **do not** open issues or pull requests which *belongs to* third party 28 | components. 29 | 30 | 31 | ## Bug reports 32 | 33 | A bug is a _demonstrable problem_ that is caused by the code in the repository. 34 | Good bug reports are extremely helpful, so thanks! 35 | 36 | Guidelines for bug reports: 37 | 38 | 1. **Use the GitHub issue search** — check if the issue has already been 39 | reported. 40 | 41 | 2. **Check if the issue has been fixed** — try to reproduce it using the 42 | latest `master` or development branch in the repository. 43 | 44 | 3. **Isolate the problem** — ideally create an 45 | [SSCCE](http://www.sscce.org/) and a live example. 46 | Uploading the project on cloud storage (OneDrive, DropBox, et el.) 47 | or creating a sample GitHub repository is also helpful. 48 | 49 | 50 | A good bug report shouldn't leave others needing to chase you up for more 51 | information. Please try to be as detailed as possible in your report. What is 52 | your environment? What steps will reproduce the issue? What browser(s) and OS 53 | experience the problem? Do other browsers show the bug differently? What 54 | would you expect to be the outcome? All these details will help people to fix 55 | any potential bugs. 56 | 57 | Example: 58 | 59 | > Short and descriptive example bug report title 60 | > 61 | > A summary of the issue and the Visual Studio, browser, OS environments 62 | > in which it occurs. If suitable, include the steps required to reproduce the bug. 63 | > 64 | > 1. This is the first step 65 | > 2. This is the second step 66 | > 3. Further steps, etc. 67 | > 68 | > `` - a link to the project/file uploaded on cloud storage or other publicly accessible medium. 69 | > 70 | > Any other information you want to share that is relevant to the issue being 71 | > reported. This might include the lines of code that you have identified as 72 | > causing the bug, and potential solutions (and your opinions on their 73 | > merits). 74 | 75 | 76 | ## Feature requests 77 | 78 | Feature requests are welcome. But take a moment to find out whether your idea 79 | fits with the scope and aims of the project. It's up to *you* to make a strong 80 | case to convince the project's developers of the merits of this feature. Please 81 | provide as much detail and context as possible. 82 | 83 | 84 | ## Pull requests 85 | 86 | Good pull requests, patches, improvements and new features are a fantastic 87 | help. They should remain focused in scope and avoid containing unrelated 88 | commits. 89 | 90 | **Please ask first** before embarking on any significant pull request (e.g. 91 | implementing features, refactoring code, porting to a different language), 92 | otherwise you risk spending a lot of time working on something that the 93 | project's developers might not want to merge into the project. 94 | 95 | Please adhere to the [coding guidelines](#code-guidelines) used throughout the 96 | project (indentation, accurate comments, etc.) and any other requirements 97 | (such as test coverage). 98 | 99 | Adhering to the following process is the best way to get your work 100 | included in the project: 101 | 102 | 1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, 103 | and configure the remotes: 104 | 105 | ```bash 106 | # Clone your fork of the repo into the current directory 107 | git clone https://github.com//.git 108 | # Navigate to the newly cloned directory 109 | cd 110 | # Assign the original repo to a remote called "upstream" 111 | git remote add upstream https://github.com/madskristensen/.git 112 | ``` 113 | 114 | 2. If you cloned a while ago, get the latest changes from upstream: 115 | 116 | ```bash 117 | git checkout master 118 | git pull upstream master 119 | ``` 120 | 121 | 3. Create a new topic branch (off the main project development branch) to 122 | contain your feature, change, or fix: 123 | 124 | ```bash 125 | git checkout -b 126 | ``` 127 | 128 | 4. Commit your changes in logical chunks. Please adhere to these [git commit 129 | message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) 130 | or your code is unlikely be merged into the main project. Use Git's 131 | [interactive rebase](https://help.github.com/articles/interactive-rebase) 132 | feature to tidy up your commits before making them public. Also, prepend name of the feature 133 | to the commit message. For instance: "SCSS: Fixes compiler results for IFileListener.\nFixes `#123`" 134 | 135 | 5. Locally merge (or rebase) the upstream development branch into your topic branch: 136 | 137 | ```bash 138 | git pull [--rebase] upstream master 139 | ``` 140 | 141 | 6. Push your topic branch up to your fork: 142 | 143 | ```bash 144 | git push origin 145 | ``` 146 | 147 | 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) 148 | with a clear title and description against the `master` branch. 149 | 150 | 151 | ## Code guidelines 152 | 153 | - Always use proper indentation. 154 | - In Visual Studio under `Tools > Options > Text Editor > C# > Advanced`, make sure 155 | `Place 'System' directives first when sorting usings` option is enabled (checked). 156 | - Before committing, organize usings for each updated C# source file. Either you can 157 | right-click editor and select `Organize Usings > Remove and sort` OR use extension 158 | like [BatchFormat](http://visualstudiogallery.msdn.microsoft.com/a7f75c34-82b4-4357-9c66-c18e32b9393e). 159 | - Before committing, run Code Analysis in `Debug` configuration and follow the guidelines 160 | to fix CA issues. Code Analysis commits can be made separately. 161 | -------------------------------------------------------------------------------- /PeasyMotion/PeasyMotion.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | true 10 | 11 | 12 | 13 | Debug 14 | AnyCPU 15 | 2.0 16 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 17 | {E14D8C11-BCE9-4928-8DF9-5EDAF485F2E5} 18 | Library 19 | Properties 20 | PeasyMotion 21 | PeasyMotion 22 | v4.8 23 | true 24 | true 25 | true 26 | false 27 | false 28 | true 29 | true 30 | Program 31 | $(DevEnvDir)devenv.exe 32 | /rootsuffix Exp 33 | 34 | 35 | true 36 | full 37 | false 38 | bin\Debug\ 39 | DEBUG;TRACE 40 | prompt 41 | 4 42 | 43 | 44 | pdbonly 45 | true 46 | bin\Release\ 47 | TRACE 48 | prompt 49 | 4 50 | 51 | 52 | 53 | 54 | 55 | 56 | CHANGELOG.md 57 | Always 58 | true 59 | 60 | 61 | CONTRIBUTING.md 62 | Always 63 | true 64 | 65 | 66 | preview.gif 67 | Always 68 | true 69 | 70 | 71 | README.md 72 | Always 73 | true 74 | 75 | 76 | HighlightText.png 77 | Always 78 | true 79 | 80 | 81 | icon.png 82 | Always 83 | true 84 | 85 | 86 | PeasyMotionPackage.vsct 87 | VsctGenerator 88 | Menus.ctmenu 89 | 90 | 91 | True 92 | True 93 | PeasyMotionPackage.vsct 94 | 95 | 96 | 97 | Designer 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | compile; build; native; contentfiles; analyzers; buildtransitive 119 | 120 | 121 | runtime; build; native; contentfiles; analyzers; buildtransitive 122 | all 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | LICENSE.txt 131 | Always 132 | true 133 | 134 | 135 | 136 | 137 | 138 | 145 | -------------------------------------------------------------------------------- /PeasyMotion2022/PeasyMotion2022.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | All 7 | 8 | 9 | 10 | true 11 | 12 | 13 | 14 | Debug 15 | AnyCPU 16 | 2.0 17 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 18 | {DA591336-360E-41A0-B8A8-E212C12B8AAA} 19 | Library 20 | Properties 21 | PeasyMotion2022 22 | PeasyMotion2022 23 | v4.8 24 | true 25 | true 26 | true 27 | false 28 | false 29 | true 30 | true 31 | Program 32 | $(DevEnvDir)devenv.exe 33 | /rootsuffix Exp 34 | 35 | 36 | true 37 | full 38 | false 39 | bin\Debug\ 40 | DEBUG;TRACE 41 | prompt 42 | 4 43 | 44 | 45 | pdbonly 46 | true 47 | bin\Release\ 48 | TRACE 49 | prompt 50 | 4 51 | false 52 | 53 | 54 | 55 | 56 | 57 | 58 | CHANGELOG.md 59 | Always 60 | true 61 | 62 | 63 | CONTRIBUTING.md 64 | Always 65 | true 66 | 67 | 68 | preview.gif 69 | Always 70 | true 71 | 72 | 73 | README.md 74 | Always 75 | true 76 | 77 | 78 | HighlightText.png 79 | Always 80 | true 81 | 82 | 83 | icon.png 84 | Always 85 | true 86 | 87 | 88 | PeasyMotionPackage.vsct 89 | VsctGenerator 90 | Menus.ctmenu 91 | 92 | 93 | True 94 | True 95 | PeasyMotionPackage.vsct 96 | 97 | 98 | 99 | Designer 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | compile; build; native; contentfiles; analyzers; buildtransitive 121 | 122 | 123 | runtime; build; native; contentfiles; analyzers; buildtransitive 124 | all 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | LICENSE.txt 133 | Always 134 | true 135 | 136 | 137 | 138 | 139 | 140 | 147 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | [Aa][Rr][Mm]/ 24 | [Aa][Rr][Mm]64/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015/2017 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # Visual Studio 2017 auto generated files 36 | Generated\ Files/ 37 | 38 | # MSTest test Results 39 | [Tt]est[Rr]esult*/ 40 | [Bb]uild[Ll]og.* 41 | 42 | # NUNIT 43 | *.VisualState.xml 44 | TestResult.xml 45 | 46 | # Build Results of an ATL Project 47 | [Dd]ebugPS/ 48 | [Rr]eleasePS/ 49 | dlldata.c 50 | 51 | # Benchmark Results 52 | BenchmarkDotNet.Artifacts/ 53 | 54 | # .NET Core 55 | project.lock.json 56 | project.fragment.lock.json 57 | artifacts/ 58 | 59 | # StyleCop 60 | StyleCopReport.xml 61 | 62 | # Files built by Visual Studio 63 | *_i.c 64 | *_p.c 65 | *_h.h 66 | *.ilk 67 | *.meta 68 | *.obj 69 | *.iobj 70 | *.pch 71 | *.pdb 72 | *.ipdb 73 | *.pgc 74 | *.pgd 75 | *.rsp 76 | *.sbr 77 | *.tlb 78 | *.tli 79 | *.tlh 80 | *.tmp 81 | *.tmp_proj 82 | *_wpftmp.csproj 83 | *.log 84 | *.vspscc 85 | *.vssscc 86 | .builds 87 | *.pidb 88 | *.svclog 89 | *.scc 90 | 91 | # Chutzpah Test files 92 | _Chutzpah* 93 | 94 | # Visual C++ cache files 95 | ipch/ 96 | *.aps 97 | *.ncb 98 | *.opendb 99 | *.opensdf 100 | *.sdf 101 | *.cachefile 102 | *.VC.db 103 | *.VC.VC.opendb 104 | 105 | # Visual Studio profiler 106 | *.psess 107 | *.vsp 108 | *.vspx 109 | *.sap 110 | 111 | # Visual Studio Trace Files 112 | *.e2e 113 | 114 | # TFS 2012 Local Workspace 115 | $tf/ 116 | 117 | # Guidance Automation Toolkit 118 | *.gpState 119 | 120 | # ReSharper is a .NET coding add-in 121 | _ReSharper*/ 122 | *.[Rr]e[Ss]harper 123 | *.DotSettings.user 124 | 125 | # JustCode is a .NET coding add-in 126 | .JustCode 127 | 128 | # TeamCity is a build add-in 129 | _TeamCity* 130 | 131 | # DotCover is a Code Coverage Tool 132 | *.dotCover 133 | 134 | # AxoCover is a Code Coverage Tool 135 | .axoCover/* 136 | !.axoCover/settings.json 137 | 138 | # Visual Studio code coverage results 139 | *.coverage 140 | *.coveragexml 141 | 142 | # NCrunch 143 | _NCrunch_* 144 | .*crunch*.local.xml 145 | nCrunchTemp_* 146 | 147 | # MightyMoose 148 | *.mm.* 149 | AutoTest.Net/ 150 | 151 | # Web workbench (sass) 152 | .sass-cache/ 153 | 154 | # Installshield output folder 155 | [Ee]xpress/ 156 | 157 | # DocProject is a documentation generator add-in 158 | DocProject/buildhelp/ 159 | DocProject/Help/*.HxT 160 | DocProject/Help/*.HxC 161 | DocProject/Help/*.hhc 162 | DocProject/Help/*.hhk 163 | DocProject/Help/*.hhp 164 | DocProject/Help/Html2 165 | DocProject/Help/html 166 | 167 | # Click-Once directory 168 | publish/ 169 | 170 | # Publish Web Output 171 | *.[Pp]ublish.xml 172 | *.azurePubxml 173 | # Note: Comment the next line if you want to checkin your web deploy settings, 174 | # but database connection strings (with potential passwords) will be unencrypted 175 | *.pubxml 176 | *.publishproj 177 | 178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 179 | # checkin your Azure Web App publish settings, but sensitive information contained 180 | # in these scripts will be unencrypted 181 | PublishScripts/ 182 | 183 | # NuGet Packages 184 | *.nupkg 185 | # The packages folder can be ignored because of Package Restore 186 | **/[Pp]ackages/* 187 | # except build/, which is used as an MSBuild target. 188 | !**/[Pp]ackages/build/ 189 | # Uncomment if necessary however generally it will be regenerated when needed 190 | #!**/[Pp]ackages/repositories.config 191 | # NuGet v3's project.json files produces more ignorable files 192 | *.nuget.props 193 | *.nuget.targets 194 | 195 | # Microsoft Azure Build Output 196 | csx/ 197 | *.build.csdef 198 | 199 | # Microsoft Azure Emulator 200 | ecf/ 201 | rcf/ 202 | 203 | # Windows Store app package directories and files 204 | AppPackages/ 205 | BundleArtifacts/ 206 | Package.StoreAssociation.xml 207 | _pkginfo.txt 208 | *.appx 209 | 210 | # Visual Studio cache files 211 | # files ending in .cache can be ignored 212 | *.[Cc]ache 213 | # but keep track of directories ending in .cache 214 | !?*.[Cc]ache/ 215 | 216 | # Others 217 | ClientBin/ 218 | ~$* 219 | *~ 220 | *.dbmdl 221 | *.dbproj.schemaview 222 | *.jfm 223 | *.pfx 224 | *.publishsettings 225 | orleans.codegen.cs 226 | 227 | # Including strong name files can present a security risk 228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 229 | #*.snk 230 | 231 | # Since there are multiple workflows, uncomment next line to ignore bower_components 232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 233 | #bower_components/ 234 | 235 | # RIA/Silverlight projects 236 | Generated_Code/ 237 | 238 | # Backup & report files from converting an old project file 239 | # to a newer Visual Studio version. Backup files are not needed, 240 | # because we have git ;-) 241 | _UpgradeReport_Files/ 242 | Backup*/ 243 | UpgradeLog*.XML 244 | UpgradeLog*.htm 245 | ServiceFabricBackup/ 246 | *.rptproj.bak 247 | 248 | # SQL Server files 249 | *.mdf 250 | *.ldf 251 | *.ndf 252 | 253 | # Business Intelligence projects 254 | *.rdl.data 255 | *.bim.layout 256 | *.bim_*.settings 257 | *.rptproj.rsuser 258 | *- Backup*.rdl 259 | 260 | # Microsoft Fakes 261 | FakesAssemblies/ 262 | 263 | # GhostDoc plugin setting file 264 | *.GhostDoc.xml 265 | 266 | # Node.js Tools for Visual Studio 267 | .ntvs_analysis.dat 268 | node_modules/ 269 | 270 | # Visual Studio 6 build log 271 | *.plg 272 | 273 | # Visual Studio 6 workspace options file 274 | *.opt 275 | 276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 277 | *.vbw 278 | 279 | # Visual Studio LightSwitch build output 280 | **/*.HTMLClient/GeneratedArtifacts 281 | **/*.DesktopClient/GeneratedArtifacts 282 | **/*.DesktopClient/ModelManifest.xml 283 | **/*.Server/GeneratedArtifacts 284 | **/*.Server/ModelManifest.xml 285 | _Pvt_Extensions 286 | 287 | # Paket dependency manager 288 | .paket/paket.exe 289 | paket-files/ 290 | 291 | # FAKE - F# Make 292 | .fake/ 293 | 294 | # JetBrains Rider 295 | .idea/ 296 | *.sln.iml 297 | 298 | # CodeRush personal settings 299 | .cr/personal 300 | 301 | # Python Tools for Visual Studio (PTVS) 302 | __pycache__/ 303 | *.pyc 304 | 305 | # Cake - Uncomment if you are using it 306 | # tools/** 307 | # !tools/packages.config 308 | 309 | # Tabs Studio 310 | *.tss 311 | 312 | # Telerik's JustMock configuration file 313 | *.jmconfig 314 | 315 | # BizTalk build output 316 | *.btp.cs 317 | *.btm.cs 318 | *.odx.cs 319 | *.xsd.cs 320 | 321 | # OpenCover UI analysis results 322 | OpenCover/ 323 | 324 | # Azure Stream Analytics local run output 325 | ASALocalRun/ 326 | 327 | # MSBuild Binary and Structured Log 328 | *.binlog 329 | 330 | # NVidia Nsight GPU debugger configuration file 331 | *.nvuser 332 | 333 | # MFractors (Xamarin productivity tool) working folder 334 | .mfractor/ 335 | 336 | # Local History for Visual Studio 337 | .localhistory/ 338 | 339 | # BeatPulse healthcheck temp database 340 | healthchecksdb -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PeasyMotion (doc tab & code navigation) 2 | === 3 | [![Build status](https://ci.appveyor.com/api/projects/status/dm1x4gin96pp9oy2/branch/master?svg=true)](https://ci.appveyor.com/project/msomeone/peasymotion/branch/master) 4 | 5 | ![Animated demonstration](preview.gif) 6 | 7 | Implements "word-motion", line motions, two char search modes ~same way as it is done in vim-easymotion. 8 | Implements fast text selection via jump label motion. 9 | Implements jumping to open document via jump label combo. 10 | This extension differs from other motion/jump extensions as it assigns jump labels to all words in text viewport, without asking specific "jump" key (in word-motion mode). 11 | Such a behaviour may lead to faster motion and navigation in certain scenarios. 12 | Inspired by original [vim-easymotion](https://github.com/easymotion/vim-easymotion) script for VIM. 13 | 14 | ## Installing 15 | At MS VS Marketplace [VS2022](https://marketplace.visualstudio.com/items?itemName=maksim-vorobiev.PeasyMotion2022newGUID) [VS2019](https://marketplace.visualstudio.com/items?itemName=maksim-vorobiev.PeasyMotion) 16 | or [VSIX Gallery](https://www.vsixgallery.com/) (search PeasyMotion) 17 | 18 | or get the [CI build](https://ci.appveyor.com/project/msomeone/peasymotion). 19 | 20 | **IMPORTANT** 21 | ![image](https://github.com/msomeone/PeasyMotion/assets/783812/8ee4c01d-616c-4f63-b5ad-ada809a30181) 22 | 23 | ## Key binding & options (for VsVim or ViEmu see 'Compatibility with other plugins' section) 24 | Assign key combination through **Tools**->**Options**->**Keyboard** 25 | commands available: 26 | * **Tools.InvokePeasyMotion** 27 | * **Tools.InvokePeasyMotionTextSelect** 28 | * **Tools.InvokePeasyMotionLineJumpToWordBegining** 29 | * **Tools.InvokePeasyMotionLineJumpToWordEnding** 30 | * **Tools.InvokePeasyMotionJumpToDocumentTab** 31 | * **Tools.InvokePeasyMotionJumpToLineBegining** 32 | * **Tools.InvokePeasyMotionTwoCharJump** 33 | * **Tools.InvokePeasyMotionOneCharJump** 34 | 35 | Two jump label assignment algorithms are available (**Tools**->**Options**->**PeasyMotion options**): 36 | * Caret relative - place labels based on proximity to caret (closer to caret -> shorter the label). 37 | * Viewport relative - labels assigned from top to bottom of visible text in viewport. 38 | 39 | In caret relative mode you can adjust 'proximity' sensitivity via "Caret position sensitivity " option. 40 | When caret sensitivity is not equal to 0, caret position is quantized into blocks of (sensitivity +1) caret positions and is treated as being located in the middle of encasing block. 41 | 42 | #### **Colors** for jump labels 43 | One can configure jump label colors, with live preview also. Color options for 'First motion' and 'Final motion' jump labels are available. 44 | Just invoke PeasyMotion and goto **Tools**->**Options** and adjust style with live preview. 45 | When 'Color source' options is not equal to **PeasyMotionJumpLabel****Motion**, one can sync label color style to other classification items from **Tools**->**Options**->**Fonts And Colors**->**Text Editor**. 46 | When 'Color source' is equal to PeasyMotionJumpLabel****Motion one can configure classification style manually trough **Tools**->**Options**->**PeasyMotion** or **Tools**->**Options**->**Fonts And Colors**->**Text Editor**->**'PeasyMotion **** Motion Jump label color'**. 47 | 48 | ## Assign allowed jump label characters 49 | One can assign allowed jump label characters through **Tools**->**Options**->**PeasyMotion options**->**Allowed jump label characters** 50 | If one observes any kind of unexpected behaviour when pressing certain key/labels during motion, there is an option to replace/remove unwanted characters from list. Beware that order of characters in this options affects ergonomics of jump-motions. Lowercase letters, digits and punctuation symbols are all valid jump label characters. 51 | 52 | ## Compatibility with other plugins 53 | VsVim and ViEmu 54 | just bind PeasyMotion command in your .vimrc (or .vsvimrc) file: 55 | ```vimscript 56 | " gS prefix is added for ViEmu, no use for VsVim AFAIK. 57 | "VsVim and ViEmu are disabled until PeasyMotion finishes 58 | 59 | "Whole viewport jump-to-word beginning mode: 60 | nnoremap gS:vsc Tools.InvokePeasyMotion 61 | 62 | "Select text from current caret position to desired jump label (fwd and reverse directions supported) 63 | nmap ;; gS:vsc Tools.InvokePeasyMotionTextSelect 64 | 65 | "Jump to word beginning in current line 66 | nmap zw gS:vsc Tools.InvokePeasyMotionLineJumpToWordBegining 67 | "Jump to word ending in current line 68 | nmap ze gS:vsc Tools.InvokePeasyMotionLineJumpToWordEnding 69 | 70 | "Jump to any open document tab 71 | nmap ;w gS:vsc Tools.InvokePeasyMotionJumpToDocumentTab 72 | 73 | "Jump to line beginning: 74 | nmap ;l gS:vsc Tools.InvokePeasyMotionJumpToLineBegining 75 | 76 | "Two char search mode: 77 | nmap ;c gS:vsc Tools.InvokePeasyMotionTwoCharJump 78 | 79 | "One char search mode: 80 | nmap ;v gS:vsc Tools.InvokePeasyMotionOneCharJump 81 | 82 | ``` 83 | ## Text selection via Tools.InvokePeasyMotionTextSelect command 84 | Invoking **Tools.InvokePeasyMotionTextSelect** command lets you to specify jump label to select in **[ current caret position -> jump label ]** range **(!)** in forward and reverse directions. 85 | 86 | ## Jump to word beginning or ending in current line 87 | Jump to word beginning or ending in current line via Tools.InvokePeasyMotionLineJumpToWordBegining or Tools.InvokePeasyMotionLineJumpToWordEnding 88 | 89 | ## Jump to document tab 90 | Jump to any open document tab via Tools.InvokePeasyMotionJumpToDocumentTab 91 | The only way to prevent UI 'jumping' when tabs caption are changed is to use monospaced font for Tools->Fonts And Colors -> show settings for Environment. This will help making tab captions keep stable. 92 | In case of non-monospaced fonts in 'Environment' - when jump labels are assigned to document tab title -> title's width can change it's width (get shorter or longer, depends on particular characters combination in caption). When document tab title changes, the re-adjustment of tab document title positions happen -> jump labels and whole doc tab title shifts either side. When user's gaze was fixed on the jump label or document tab title user will lose his 'point of focus'/ gaze point. 93 | When monospaced fonts are used in environment -> each character lower/upper of any kind occupies strictly the same space in title. This prevents any changes/adjustments to captions widths/doc title positions in combination with fact, that jump label replaces part of document name (to help with keeping document title the same width as it was). 94 | 95 | ## Jump to beginning of line 96 | Jump to beginning of any visible line via Tools.InvokePeasyMotionJumpToLineBegining 97 | 98 | ## Two char search 99 | Execute two character search for visible text portion via Tools.InvokePeasyMotionTwoCharJump. Jump labels are placed at each match. 100 | When activated, two keys are queried, search and all matched are labeled. After that PM awaits for jump label keys to execute final motion and deactivate. 101 | 102 | ## Bugreports, Feature requests and contributions 103 | PeasyMotion can be developed using Visual Studio 2017 or 2019. Contributions are welcomed. 104 | Check out the [contribution guidelines](CONTRIBUTING.md) 105 | 106 | ## License 107 | All code in this project is covered under the MIT license a copy of which 108 | is available in the same directory under the name LICENSE.txt. 109 | 110 | ## Latest Builds 111 | The build representing the latest source code can be downloaded from the 112 | [Open Vsix Gallery](http://vsixgallery.com/extension/PeasyMotion.a87d2837-6b54-4518-b014-3b29b4dcd902/). 113 | 114 | ## Building 115 | For cloning and building this project yourself, make sure 116 | to install the 117 | [Extensibility Essentials](https://marketplace.visualstudio.com/items?itemName=MadsKristensen.ExtensibilityEssentials) 118 | extension for Visual Studio which enables some features 119 | used by this project. 120 | You may also want to check awesome [Mads Kristensen guide](https://devblogs.microsoft.com/visualstudio/getting-started-writing-visual-studio-extensions/) 121 | -------------------------------------------------------------------------------- /Shared/VsMethodExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Microsoft.VisualStudio; 7 | using Microsoft.VisualStudio.Editor; 8 | using Microsoft.VisualStudio.Shell; 9 | using Microsoft.VisualStudio.Shell.Interop; 10 | using Microsoft.VisualStudio.Text; 11 | using Microsoft.VisualStudio.Text.Operations; 12 | using Microsoft.VisualStudio.Text.Editor; 13 | using Microsoft.VisualStudio.TextManager.Interop; 14 | using System.Runtime.InteropServices; 15 | 16 | namespace PeasyMotion 17 | { 18 | // based on code from VsVim. 19 | /* VsVim 20 | Copyright 2012 Jared Parsons 21 | 22 | Licensed under the Apache License, Version 2.0 (the "License"); 23 | you may not use this file except in compliance with the License. 24 | You may obtain a copy of the License at 25 | 26 | http://www.apache.org/licenses/LICENSE-2.0 27 | 28 | 29 | Unless required by applicable law or agreed to in writing, software 30 | distributed under the License is distributed on an "AS IS" BASIS, 31 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 32 | See the License for the specific language governing permissions and 33 | limitations under the License. 34 | */ 35 | 36 | public static class Extensions 37 | { 38 | // i wish we could simply do th:is: 39 | //VsShellUtilities.GetWindowObject(wf).Caption = some caption; 40 | // but its impossible. Exception is thrown - invalid operation, bla bla. 41 | 42 | public static void SetDocumentWindowFrameCaptionWithLabel(this IVsWindowFrame wf, 43 | string vanillaTabCaption, string jumpLabel) 44 | { 45 | //TODO: (check for length (what if vanilla caption is shorter than label & decor ???!!!) 46 | var newCaption = PeasyMotionEdAdornment.getDocumentTabCaptionWithLabel(vanillaTabCaption, jumpLabel); 47 | wf.SetProperty((int)VsFramePropID.OverrideCaption, newCaption); 48 | //Debug.WriteLine($"WindowFrame oldCaption={vanillaTabCaption} => newCaption={newCaption}"); 49 | } 50 | 51 | public static void RemoveJumpLabelFromDocumentWindowFrameCaption(this IVsWindowFrame wf, string vanillaTabCaption) 52 | { 53 | wf.SetProperty((int)VsFramePropID.OverrideCaption, null); 54 | } 55 | 56 | public static Result GetWindowFrame(this IVsTextView textView) 57 | { 58 | var textViewEx = textView as IVsTextViewEx; 59 | if (textViewEx == null) 60 | { 61 | return Result.Error; 62 | } 63 | 64 | return textViewEx.GetWindowFrame(); 65 | } 66 | 67 | public static Result GetWindowFrame(this IVsTextViewEx textViewEx) 68 | { 69 | if (!ErrorHandler.Succeeded(textViewEx.GetWindowFrame(out object frame))) 70 | { 71 | return Result.Error; 72 | } 73 | 74 | var vsWindowFrame = frame as IVsWindowFrame; 75 | if (vsWindowFrame == null) 76 | { 77 | return Result.Error; 78 | } 79 | 80 | return Result.CreateSuccess(vsWindowFrame); 81 | } 82 | 83 | public static Result GetCodeWindow(this IVsWindowFrame vsWindowFrame) 84 | { 85 | var iid = typeof(IVsCodeWindow).GUID; 86 | var ptr = IntPtr.Zero; 87 | try 88 | { 89 | var hr = vsWindowFrame.QueryViewInterface(ref iid, out ptr); 90 | if (ErrorHandler.Failed(hr)) 91 | { 92 | return Result.CreateError(hr); 93 | } 94 | 95 | return Result.CreateSuccess((IVsCodeWindow)Marshal.GetObjectForIUnknown(ptr)); 96 | } 97 | catch (Exception e) 98 | { 99 | // Venus will throw when querying for the code window 100 | return Result.CreateError(e); 101 | } 102 | finally 103 | { 104 | if (ptr != IntPtr.Zero) 105 | { 106 | Marshal.Release(ptr); 107 | } 108 | } 109 | } 110 | 111 | public static IVsTextView GetPrimaryTextView(this IVsWindowFrame windowFrame) 112 | { 113 | /* 114 | object docView; 115 | int hresult = windowFrame.GetProperty((int)__VSFPROPID.VSFPROPID_DocView, out docView); 116 | 117 | if (ErrorHandler.Failed(hresult)) { 118 | return null; 119 | } 120 | 121 | IVsTextView viewAdapter = docView as IVsTextView; 122 | if (viewAdapter != null) { 123 | return viewAdapter; 124 | } 125 | 126 | IVsCodeWindow codeWindow = docView as IVsCodeWindow; 127 | if (codeWindow != null) { 128 | IVsTextView codeView; 129 | if (ErrorHandler.Succeeded(codeWindow.GetPrimaryView(out codeView)) && codeView != null) { 130 | return codeView; 131 | } 132 | } 133 | 134 | return null; 135 | */ 136 | return VsShellUtilities.GetTextView(windowFrame); 137 | } 138 | 139 | public static Result GetPrimaryView(this IVsCodeWindow vsCodeWindow) 140 | { 141 | var hr = vsCodeWindow.GetPrimaryView(out IVsTextView vsTextView); 142 | if (ErrorHandler.Failed(hr)) 143 | { 144 | return Result.CreateError(hr); 145 | } 146 | 147 | return Result.CreateSuccessNonNull(vsTextView); 148 | } 149 | 150 | public static Result GetPrimaryTextView(this IVsCodeWindow codeWindow, IVsEditorAdaptersFactoryService factoryService) 151 | { 152 | var result = GetPrimaryView(codeWindow); 153 | if (result.IsError) 154 | { 155 | return Result.CreateError(result.HResult); 156 | } 157 | 158 | var textView = factoryService.GetWpfTextViewNoThrow(result.Value); 159 | return Result.CreateSuccessNonNull(textView); 160 | } 161 | 162 | public static Result> GetDocumentWindowFrames(this IVsUIShell vsShell) 163 | { 164 | var hr = vsShell.GetDocumentWindowEnum(out IEnumWindowFrames enumFrames); 165 | return ErrorHandler.Failed(hr) ? Result.CreateError(hr) : enumFrames.GetContents(); 166 | } 167 | 168 | public static Result> GetDocumentWindowFrames(this IVsUIShell4 vsShell, __WindowFrameTypeFlags flags) 169 | { 170 | var hr = vsShell.GetWindowEnum((uint)flags, out IEnumWindowFrames enumFrames); 171 | return ErrorHandler.Failed(hr) ? Result.CreateError(hr) : enumFrames.GetContents(); 172 | } 173 | 174 | public static Result> GetContents(this IEnumWindowFrames enumFrames) 175 | { 176 | var list = new List(); 177 | var array = new IVsWindowFrame[16]; 178 | while (true) 179 | { 180 | var hr = enumFrames.Next((uint)array.Length, array, out uint num); 181 | if (ErrorHandler.Failed(hr)) 182 | { 183 | return Result.CreateError(hr); 184 | } 185 | 186 | if (0 == num) 187 | { 188 | return list; 189 | } 190 | 191 | for (var i = 0; i < num; i++) 192 | { 193 | list.Add(array[i]); 194 | } 195 | } 196 | } 197 | 198 | public static IWpfTextView GetWpfTextViewNoThrow(this IVsEditorAdaptersFactoryService editorAdapter, IVsTextView vsTextView) 199 | { 200 | try { 201 | return editorAdapter.GetWpfTextView(vsTextView); 202 | } catch { 203 | return null; 204 | } 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /Shared/BaseOptionModel.cs: -------------------------------------------------------------------------------- 1 | #define DEBUG_OPTION_VALUES_LOAD_SAVE 2 | using System; 3 | using System.Diagnostics; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Runtime.Serialization.Formatters.Binary; 9 | using System.Threading.Tasks; 10 | using Microsoft; 11 | using Microsoft.VisualStudio.Settings; 12 | using Microsoft.VisualStudio.Shell; 13 | using Microsoft.VisualStudio.Shell.Interop; 14 | using Microsoft.VisualStudio.Shell.Settings; 15 | using Microsoft.VisualStudio.Threading; 16 | using Task = System.Threading.Tasks.Task; 17 | 18 | namespace PeasyMotion.Options 19 | { 20 | [AttributeUsage(AttributeTargets.Property)] 21 | public class DisableOptionSerialization : Attribute {} 22 | 23 | [AttributeUsage(AttributeTargets.Property)] 24 | public class HiddenOption : Attribute {} 25 | 26 | /// 27 | /// A base class for specifying options 28 | /// 29 | internal abstract class BaseOptionModel where T : BaseOptionModel, new() 30 | { 31 | private static AsyncLazy _liveModel = new AsyncLazy(CreateAsync, ThreadHelper.JoinableTaskFactory); 32 | private static AsyncLazy _settingsManager = new AsyncLazy(GetSettingsManagerAsync, ThreadHelper.JoinableTaskFactory); 33 | 34 | protected BaseOptionModel() 35 | { } 36 | 37 | /// 38 | /// A singleton instance of the options. MUST be called from UI thread only. 39 | /// 40 | /// 41 | /// Call instead if on a background thread or in an async context on the main thread. 42 | /// 43 | public static T Instance 44 | { 45 | get 46 | { 47 | ThreadHelper.ThrowIfNotOnUIThread(); 48 | 49 | #pragma warning disable VSTHRD104 // Offer async methods 50 | return ThreadHelper.JoinableTaskFactory.Run(GetLiveInstanceAsync); 51 | #pragma warning restore VSTHRD104 // Offer async methods 52 | } 53 | } 54 | 55 | /// 56 | /// Get the singleton instance of the options. Thread safe. 57 | /// 58 | public static Task GetLiveInstanceAsync() => _liveModel.GetValueAsync(); 59 | 60 | /// 61 | /// Creates a new instance of the options class and loads the values from the store. For internal use only 62 | /// 63 | /// 64 | public static async Task CreateAsync() 65 | { 66 | var instance = new T(); 67 | await instance.LoadAsync().ConfigureAwait(true); 68 | return instance; 69 | } 70 | 71 | /// 72 | /// The name of the options collection as stored in the registry. 73 | /// 74 | protected virtual string CollectionName { get; } = typeof(T).FullName; 75 | 76 | /// 77 | /// Hydrates the properties from the registry. 78 | /// 79 | public virtual void Load() 80 | { 81 | ThreadHelper.JoinableTaskFactory.Run(LoadAsync); 82 | } 83 | 84 | /// 85 | /// Hydrates the properties from the registry asyncronously. 86 | /// 87 | public virtual async Task LoadAsync() 88 | { 89 | ShellSettingsManager manager = await _settingsManager.GetValueAsync().ConfigureAwait(true); 90 | SettingsStore settingsStore = manager.GetReadOnlySettingsStore(SettingsScope.UserSettings); 91 | 92 | if (!settingsStore.CollectionExists(CollectionName)) 93 | { 94 | return; 95 | } 96 | 97 | #if DEBUG_OPTION_VALUES_LOAD_SAVE 98 | Debug.WriteLine($"LoadAsync<{typeof(T).Name}>()"); 99 | Debug.WriteLine($"GetPropertyCount = {settingsStore.GetPropertyCount(CollectionName)}"); 100 | Debug.WriteLine($"GetPropertyCount = {settingsStore.GetPropertyCount(CollectionName)}"); 101 | //var pnv = settingsStore.GetPropertyNamesAndValues(CollectionName); 102 | var pn = settingsStore.GetPropertyNames(CollectionName); 103 | foreach(var n in pn) { 104 | Debug.WriteLine($"Property: Name={n} Type = {settingsStore.GetPropertyType(CollectionName, n).ToString()}"); 105 | } 106 | #endif 107 | var propertiesToSerialize = GetOptionProperties(); 108 | foreach (PropertyInfo property in propertiesToSerialize) 109 | { 110 | if (!settingsStore.PropertyExists(CollectionName, property.Name)) { 111 | #if DEBUG_OPTION_VALUES_LOAD_SAVE 112 | Debug.WriteLine($"Skipping property {property.Name}. Not found in settings store"); 113 | #endif 114 | property.SetValue(this, property.GetValue(this)); 115 | continue; 116 | } 117 | try 118 | { 119 | string serializedProp = settingsStore.GetString(CollectionName, property.Name); 120 | object value = DeserializeValue(serializedProp, property.PropertyType); 121 | property.SetValue(this, value); 122 | #if DEBUG_OPTION_VALUES_LOAD_SAVE 123 | Debug.WriteLine($"{property.Name} = {property.GetValue(this)} | value = {value}"); 124 | #endif 125 | } 126 | catch (Exception ex) 127 | { 128 | System.Diagnostics.Debug.WriteLine(ex); 129 | } 130 | } 131 | #if DEBUG_OPTION_VALUES_LOAD_SAVE 132 | Debug.WriteLine($"LoadAsync<{typeof(T).Name}>() finished ==================================="); 133 | #endif 134 | } 135 | 136 | /// 137 | /// Saves the properties to the registry. 138 | /// 139 | public virtual void Save() 140 | { 141 | ThreadHelper.JoinableTaskFactory.Run(SaveAsync); 142 | ThreadHelper.JoinableTaskFactory.Run(LiveModelLoadAsync); 143 | } 144 | 145 | public virtual async Task LiveModelLoadAsync() { 146 | T liveModel = await GetLiveInstanceAsync().ConfigureAwait(true); 147 | if (this != liveModel) 148 | { 149 | await liveModel.LoadAsync().ConfigureAwait(true); 150 | } 151 | } 152 | 153 | /// 154 | /// Saves the properties to the registry asyncronously. 155 | /// 156 | public virtual async Task SaveAsync() 157 | { 158 | ShellSettingsManager manager = await _settingsManager.GetValueAsync().ConfigureAwait(true); 159 | WritableSettingsStore settingsStore = manager.GetWritableSettingsStore(SettingsScope.UserSettings); 160 | 161 | if (!settingsStore.CollectionExists(CollectionName)) 162 | { 163 | settingsStore.CreateCollection(CollectionName); 164 | } 165 | 166 | #if DEBUG_OPTION_VALUES_LOAD_SAVE 167 | Debug.WriteLine($"SaveAsync<{typeof(T).Name}>()"); 168 | #endif 169 | var propertiesToSerialize = GetOptionProperties(); 170 | foreach (PropertyInfo property in propertiesToSerialize) 171 | { 172 | string output = SerializeValue(property.GetValue(this)); 173 | #if DEBUG_OPTION_VALUES_LOAD_SAVE 174 | Debug.WriteLine($"{property.Name} = {property.GetValue(this)}"); 175 | #endif 176 | settingsStore.SetString(CollectionName, property.Name, output); 177 | } 178 | #if DEBUG_OPTION_VALUES_LOAD_SAVE 179 | Debug.WriteLine($"SaveAsync<{typeof(T).Name}>() finished ================================="); 180 | #endif 181 | } 182 | 183 | /// 184 | /// Serializes an object value to a string using the binary serializer. 185 | /// 186 | protected virtual string SerializeValue(object value) 187 | { 188 | using (var stream = new MemoryStream()) 189 | { 190 | var formatter = new BinaryFormatter(); 191 | formatter.Serialize(stream, value); 192 | stream.Flush(); 193 | return Convert.ToBase64String(stream.ToArray()); 194 | } 195 | } 196 | 197 | /// 198 | /// Deserializes a string to an object using the binary serializer. 199 | /// 200 | protected virtual object DeserializeValue(string value, Type type) 201 | { 202 | byte[] b = Convert.FromBase64String(value); 203 | 204 | using (var stream = new MemoryStream(b)) 205 | { 206 | var formatter = new BinaryFormatter(); 207 | return formatter.Deserialize(stream); 208 | } 209 | } 210 | 211 | private static async Task GetSettingsManagerAsync() 212 | { 213 | #pragma warning disable VSTHRD010 214 | // False-positive in Threading Analyzers. Bug tracked here https://github.com/Microsoft/vs-threading/issues/230 215 | var svc = await AsyncServiceProvider.GlobalProvider.GetServiceAsync(typeof(SVsSettingsManager)).ConfigureAwait(true) as IVsSettingsManager; 216 | #pragma warning restore VSTHRD010 217 | 218 | Assumes.Present(svc); 219 | 220 | return new ShellSettingsManager(svc); 221 | } 222 | 223 | private bool HasAttribute(PropertyInfo t) where ATTR_T : class { 224 | return t.GetCustomAttributes(typeof(ATTR_T), true).Length > 0; 225 | } 226 | 227 | private IEnumerable GetOptionProperties() 228 | { 229 | var a = 230 | GetType().GetProperties() 231 | .Where(p => 232 | (!HasAttribute(p)) && p.PropertyType.IsSerializable); 233 | var b = 234 | GetType().GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static) 235 | .Where(p => 236 | (!HasAttribute(p)) && 237 | p.PropertyType.IsSerializable && HasAttribute(p)); 238 | return a.Concat(b); 239 | } 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /Shared/PeasyMotionPackage.vsct: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 33 | 34 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | PeasyMotion 52 | 53 | 54 | 55 | 56 | 57 | 59 | 60 | 67 | 74 | 81 | 88 | 95 | 103 | 110 | 117 | 124 | 125 | 126 | 127 | 128 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /Shared/InfoBarService.cs: -------------------------------------------------------------------------------- 1 | // based on infobar demo (Utkarsh Shigihalli) - https://github.com/onlyutkarsh/InfoBarDemo 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Forms; 8 | using Microsoft.VisualStudio.Imaging; 9 | using Microsoft.VisualStudio.Shell; 10 | using Microsoft.VisualStudio.Shell.Interop; 11 | using System.Diagnostics; 12 | 13 | namespace PeasyMotion 14 | { 15 | abstract class InfoBarMdl { 16 | public abstract InfoBarModel getInfoBarModel(); 17 | } 18 | 19 | class WhatsNewNotification : InfoBarMdl 20 | { 21 | /* ~ 1.0.40 22 | public override InfoBarModel getInfoBarModel() { 23 | InfoBarTextSpan text = new InfoBarTextSpan( 24 | "PeasyMotion: New feature has been added! Text selection via jump. Give it a try via Tools.InvokePeasyMotionTextSelect command."); 25 | InfoBarHyperlink dismiss = new InfoBarHyperlink("Dismiss", "dismiss"); 26 | InfoBarHyperlink moreInfo = new InfoBarHyperlink("More info", 27 | "http://github.com/msomeone/PeasyMotion#text-selection-via-toolsinvokepeasymotiontextselect-command"); 28 | InfoBarTextSpan[] spans = new InfoBarTextSpan[] { text }; 29 | InfoBarActionItem[] actions = new InfoBarActionItem[] { moreInfo, dismiss }; 30 | InfoBarModel infoBarModel = new InfoBarModel(spans, actions, KnownMonikers.StatusInformation, isCloseButtonVisible: true); 31 | return infoBarModel; 32 | } 33 | */ 34 | /* ~ 1.1.42 35 | public override InfoBarModel getInfoBarModel() { 36 | InfoBarTextSpan text = new InfoBarTextSpan( 37 | "PeasyMotion: New mode has been added! In-Line word jump (begin/end). Give it a try via Tools.PeasyMotionLineJumpToWordBegining or Tools.PeasyMotionLineJumpToWordEnding command."); 38 | InfoBarHyperlink dismiss = new InfoBarHyperlink("Dismiss", "dismiss"); 39 | InfoBarHyperlink moreInfo = new InfoBarHyperlink("More info", 40 | "https://github.com/msomeone/PeasyMotion#jump-to-word-begining-or-ending-in-current-line"); 41 | InfoBarTextSpan[] spans = new InfoBarTextSpan[] { text }; 42 | InfoBarActionItem[] actions = new InfoBarActionItem[] { moreInfo, dismiss }; 43 | InfoBarModel infoBarModel = new InfoBarModel(spans, actions, KnownMonikers.StatusInformation, isCloseButtonVisible: true); 44 | return infoBarModel; 45 | } 46 | */ 47 | /* ~1.4.60 48 | public override InfoBarModel getInfoBarModel() { 49 | InfoBarTextSpan text = new InfoBarTextSpan( 50 | "PeasyMotion: New mode has been added! Jump to document tab. Give it a try via Tools.InvokePeasyMotionJumpToDocumentTab. New option: one can set allowed characters to be used in jump labels."); 51 | InfoBarHyperlink dismiss = new InfoBarHyperlink("Dismiss", "dismiss"); 52 | InfoBarHyperlink moreInfo = new InfoBarHyperlink("More info", 53 | "https://github.com/msomeone/PeasyMotion#jump-to-document-tab"); 54 | InfoBarTextSpan[] spans = new InfoBarTextSpan[] { text }; 55 | InfoBarActionItem[] actions = new InfoBarActionItem[] { moreInfo, dismiss }; 56 | InfoBarModel infoBarModel = new InfoBarModel(spans, actions, KnownMonikers.StatusInformation, isCloseButtonVisible: true); 57 | return infoBarModel; 58 | } 59 | */ 60 | /* 61 | public override InfoBarModel getInfoBarModel() { 62 | InfoBarTextSpan text = new InfoBarTextSpan( 63 | "PeasyMotion: New mode has been added! Jump to line begining. Give it a try via Tools.InvokePeasyMotionJumpToLineBegining. Several bugfixes"); 64 | InfoBarHyperlink dismiss = new InfoBarHyperlink("Dismiss", "dismiss"); 65 | InfoBarHyperlink moreInfo = new InfoBarHyperlink("More info", 66 | "https://github.com/msomeone/PeasyMotion#jump-to-begining-of-line"); 67 | InfoBarTextSpan[] spans = new InfoBarTextSpan[] { text }; 68 | InfoBarActionItem[] actions = new InfoBarActionItem[] { moreInfo, dismiss }; 69 | InfoBarModel infoBarModel = new InfoBarModel(spans, actions, KnownMonikers.StatusInformation, isCloseButtonVisible: true); 70 | return infoBarModel; 71 | } 72 | */ 73 | /* 74 | public override InfoBarModel getInfoBarModel() { 75 | InfoBarTextSpan text = new InfoBarTextSpan("PeasyMotion: Two characted search mode has beed added! Give it a try via Tools.InvokePeasyMotionTwoCharJump. LineBeginingJump bug fix."); 76 | InfoBarHyperlink dismiss = new InfoBarHyperlink("Dismiss", "dismiss"); 77 | InfoBarHyperlink moreInfo = new InfoBarHyperlink("More info", 78 | "https://github.com/msomeone/PeasyMotion#two-char-search"); 79 | InfoBarTextSpan[] spans = new InfoBarTextSpan[] { text }; 80 | InfoBarActionItem[] actions = new InfoBarActionItem[] { moreInfo, dismiss }; 81 | InfoBarModel infoBarModel = new InfoBarModel(spans, actions, KnownMonikers.StatusInformation, isCloseButtonVisible: true); 82 | return infoBarModel; 83 | }*/ 84 | /* 85 | public override InfoBarModel getInfoBarModel() { 86 | InfoBarTextSpan text = new InfoBarTextSpan("PeasyMotion: 2022 - Visual Studio 2022 support is here! Link is in 'more info' section"); 87 | InfoBarHyperlink dismiss = new InfoBarHyperlink("Dismiss", "dismiss"); 88 | InfoBarHyperlink moreInfo = new InfoBarHyperlink("More info", 89 | "https://marketplace.visualstudio.com/items?itemName=maksim-vorobiev.PeasyMotion2022"); 90 | InfoBarTextSpan[] spans = new InfoBarTextSpan[] { text }; 91 | InfoBarActionItem[] actions = new InfoBarActionItem[] { moreInfo, dismiss }; 92 | InfoBarModel infoBarModel = new InfoBarModel(spans, actions, KnownMonikers.StatusInformation, isCloseButtonVisible: true); 93 | return infoBarModel; 94 | }*/ 95 | public override InfoBarModel getInfoBarModel() { 96 | InfoBarTextSpan text = new InfoBarTextSpan("PeasyMotion: ARM64 support"); 97 | InfoBarHyperlink dismiss = new InfoBarHyperlink("Dismiss", "dismiss"); 98 | InfoBarHyperlink moreInfo = new InfoBarHyperlink("More info", 99 | "https://github.com/msomeone/PeasyMotion"); 100 | InfoBarTextSpan[] spans = new InfoBarTextSpan[] { text }; 101 | InfoBarActionItem[] actions = new InfoBarActionItem[] { moreInfo, dismiss }; 102 | InfoBarModel infoBarModel = new InfoBarModel(spans, actions, KnownMonikers.StatusInformation, isCloseButtonVisible: true); 103 | return infoBarModel; 104 | } 105 | }; 106 | 107 | class InfoBarService : IVsInfoBarUIEvents 108 | { 109 | private readonly IServiceProvider _serviceProvider; 110 | private uint _cookie; 111 | private IVsInfoBarUIElement _element = null; 112 | private Action _onInfoBarCloseOrDismissAction = null; 113 | 114 | public bool anyInfoBarActive() { 115 | return null != _element; 116 | } 117 | 118 | private InfoBarService(IServiceProvider serviceProvider) 119 | { 120 | _serviceProvider = serviceProvider; 121 | } 122 | 123 | public static InfoBarService Instance { get; private set; } 124 | 125 | public static void Initialize(IServiceProvider serviceProvider) 126 | { 127 | Instance = new InfoBarService(serviceProvider); 128 | } 129 | 130 | public void OnClosed(IVsInfoBarUIElement infoBarUIElement) 131 | { 132 | infoBarUIElement.Unadvise(_cookie); 133 | } 134 | 135 | public void OnActionItemClicked(IVsInfoBarUIElement infoBarUIElement, IVsInfoBarActionItem actionItem) 136 | { 137 | string ctxStr = actionItem.ActionContext as string; 138 | if (ctxStr != null) 139 | { 140 | if (ctxStr == "dismiss") { 141 | CloseInfoBar(); 142 | Debug.WriteLine("You clicked dismiss!"); 143 | } 144 | else if (ctxStr.StartsWith("http://") || ctxStr.StartsWith("https://")) 145 | { 146 | PeasyMotionActivate.Instance?.Deactivate(); 147 | IVsWindowFrame ppFrame; 148 | var service = Package.GetGlobalService(typeof(IVsWebBrowsingService)) as IVsWebBrowsingService; 149 | service.Navigate(ctxStr, 0, out ppFrame); 150 | } 151 | else 152 | { 153 | Debug.WriteLine("You clicked ??????"); 154 | } 155 | } 156 | } 157 | 158 | public void ShowInfoBar(InfoBarMdl ib, Action onInfoBarClosedOrDismissed) 159 | { 160 | var shell = _serviceProvider.GetService(typeof(SVsShell)) as IVsShell; 161 | if (shell != null) 162 | { 163 | shell.GetProperty((int) __VSSPROPID7.VSSPROPID_MainWindowInfoBarHost, out var obj); 164 | var host = (IVsInfoBarHost)obj; 165 | if (host == null) { 166 | return; 167 | } 168 | 169 | var factory = _serviceProvider.GetService(typeof(SVsInfoBarUIFactory)) as IVsInfoBarUIFactory; 170 | _element = factory.CreateInfoBar(ib.getInfoBarModel()); 171 | _element.Advise(this, out _cookie); 172 | host.AddInfoBar(_element); 173 | _onInfoBarCloseOrDismissAction = onInfoBarClosedOrDismissed; 174 | } 175 | } 176 | 177 | public void CloseInfoBar() 178 | { 179 | if (_element != null) 180 | { 181 | var shell = _serviceProvider.GetService(typeof(SVsShell)) as IVsShell; 182 | if (shell != null) 183 | { 184 | shell.GetProperty((int)__VSSPROPID7.VSSPROPID_MainWindowInfoBarHost, out var obj); 185 | var host = (IVsInfoBarHost)obj; 186 | if (host == null) { 187 | return; 188 | } 189 | _element.Close(); 190 | host.RemoveInfoBar(_element); 191 | _element = null; 192 | 193 | } 194 | if (_onInfoBarCloseOrDismissAction != null) { 195 | _onInfoBarCloseOrDismissAction(); 196 | } 197 | } 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /Shared/VsSettings.cs: -------------------------------------------------------------------------------- 1 | //#define DEBUG_COLOR_STYLE_OPTIONS 2 | // Based on EditorHostFactory from VsVim 3 | // And VsSettings from VsTeXCommentsExtension 4 | 5 | /* VsSettings 6 | The MIT License (MIT) 7 | 8 | Copyright (c) 2016 Hubert Kindermann 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | */ 28 | 29 | /* VsVim 30 | Copyright 2012 Jared Parsons 31 | 32 | Licensed under the Apache License, Version 2.0 (the "License"); 33 | you may not use this file except in compliance with the License. 34 | You may obtain a copy of the License at 35 | 36 | http://www.apache.org/licenses/LICENSE-2.0 37 | 38 | Unless required by applicable law or agreed to in writing, software 39 | distributed under the License is distributed on an "AS IS" BASIS, 40 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 41 | See the License for the specific language governing permissions and 42 | limitations under the License. 43 | */ 44 | 45 | using Microsoft.VisualStudio.Threading; 46 | using System.ComponentModel.Composition; 47 | using System.ComponentModel.Composition.Hosting; 48 | using System.ComponentModel.Composition.Primitives; 49 | using System.IO; 50 | using System.Linq; 51 | using System.Reflection; 52 | using System.Text; 53 | using Microsoft.VisualStudio.Text.Operations; 54 | using Microsoft.Win32; 55 | using System; 56 | using System.Drawing; 57 | using System.Collections.Generic; 58 | using System.ComponentModel; 59 | using System.Diagnostics; 60 | using System.Windows.Media; 61 | using Microsoft.VisualStudio.Editor; 62 | using Microsoft.VisualStudio.Text.Classification; 63 | using Microsoft.VisualStudio.Text.Editor; 64 | 65 | using System.Threading; 66 | using System.Windows.Threading; 67 | using wpf = System.Windows.Media; 68 | 69 | namespace PeasyMotion 70 | { 71 | public sealed partial class EditorHostFactory 72 | { 73 | /// 74 | /// Beginning in 15.0 the editor took a dependency on JoinableTaskContext. Need to provide that 75 | /// export here. 76 | /// 77 | private sealed class JoinableTaskContextExportProvider : ExportProvider 78 | { 79 | internal static string TypeFullName => typeof(JoinableTaskContext).FullName; 80 | private readonly Export _export; 81 | private readonly JoinableTaskContext _context; 82 | 83 | internal JoinableTaskContextExportProvider() 84 | { 85 | _export = new Export(TypeFullName, GetValue); 86 | _context = Microsoft.VisualStudio.Shell.ThreadHelper.JoinableTaskContext; 87 | } 88 | 89 | protected override IEnumerable GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition) 90 | { 91 | if (definition.ContractName == TypeFullName) 92 | { 93 | yield return _export; 94 | } 95 | } 96 | 97 | private object GetValue() => _context; 98 | } 99 | } 100 | 101 | public sealed class VsSettings : IDisposable, INotifyPropertyChanged 102 | { 103 | private static readonly SolidColorBrush DefaultForegroundBrush = new SolidColorBrush(wpf.Colors.Sienna); 104 | private static readonly SolidColorBrush DefaultBackgroundBrush = new SolidColorBrush(wpf.Colors.GreenYellow); 105 | private static readonly Dictionary Instances = new Dictionary(); 106 | 107 | public static bool IsInitialized { get; private set; } 108 | private static IEditorFormatMapService editorFormatMapService; 109 | private static IServiceProvider serviceProvider; 110 | 111 | private readonly IWpfTextView textView; 112 | private readonly IEditorFormatMap editorFormatMap; 113 | 114 | public event PropertyChangedEventHandler PropertyChanged; 115 | 116 | private SolidColorBrush jumpLabelFirstMotionColorFg; 117 | public SolidColorBrush JumpLabelFirstMotionForegroundColor 118 | { 119 | get { return jumpLabelFirstMotionColorFg; } 120 | private set { 121 | bool notify = JumpLabelFirstMotionForegroundColor == null || value == null || value.Color != JumpLabelFirstMotionForegroundColor.Color; 122 | jumpLabelFirstMotionColorFg = value; 123 | jumpLabelFirstMotionColorFg.Freeze(); 124 | Debug.WriteLine($"VsSettings.Property Set FG={JumpLabelFirstMotionForegroundColor.Color} notify={notify}"); 125 | if (notify) OnPropertyChanged(nameof(JumpLabelFirstMotionForegroundColor)); 126 | } 127 | } 128 | private SolidColorBrush jumpLabelFirstMotionColorBg; 129 | public SolidColorBrush JumpLabelFirstMotionBackgroundColor 130 | { 131 | get { return jumpLabelFirstMotionColorBg; } 132 | private set { 133 | bool notify = JumpLabelFirstMotionBackgroundColor == null || value == null || value.Color != JumpLabelFirstMotionBackgroundColor.Color; 134 | jumpLabelFirstMotionColorBg = value; 135 | jumpLabelFirstMotionColorBg.Freeze(); 136 | Debug.WriteLine($"VsSettings.Property Set BG={JumpLabelFirstMotionBackgroundColor.Color} notify={notify}"); 137 | if (notify) OnPropertyChanged(nameof(JumpLabelFirstMotionBackgroundColor)); 138 | } 139 | } 140 | 141 | private SolidColorBrush jumpLabelFinalMotionColorFg; 142 | public SolidColorBrush JumpLabelFinalMotionForegroundColor 143 | { 144 | get { return jumpLabelFinalMotionColorFg; } 145 | private set { 146 | bool notify = JumpLabelFinalMotionForegroundColor == null || value == null || value.Color != JumpLabelFinalMotionForegroundColor.Color; 147 | jumpLabelFinalMotionColorFg = value; 148 | jumpLabelFinalMotionColorFg.Freeze(); 149 | Debug.WriteLine($"VsSettings.Property Set FG={JumpLabelFinalMotionForegroundColor.Color} notify={notify}"); 150 | if (notify) OnPropertyChanged(nameof(JumpLabelFinalMotionForegroundColor)); 151 | } 152 | } 153 | private SolidColorBrush jumpLabelFinalMotionColorBg; 154 | public SolidColorBrush JumpLabelFinalMotionBackgroundColor 155 | { 156 | get { return jumpLabelFinalMotionColorBg; } 157 | private set { 158 | bool notify = JumpLabelFinalMotionBackgroundColor == null || value == null || value.Color != JumpLabelFinalMotionBackgroundColor.Color; 159 | jumpLabelFinalMotionColorBg = value; 160 | jumpLabelFinalMotionColorBg.Freeze(); 161 | Debug.WriteLine($"VsSettings.Property Set BG={JumpLabelFinalMotionBackgroundColor.Color} notify={notify}"); 162 | if (notify) OnPropertyChanged(nameof(JumpLabelFinalMotionBackgroundColor)); 163 | } 164 | } 165 | 166 | // https://stackoverflow.com/questions/10283206/setting-getting-the-class-properties-by-string-name 167 | public object this[string propertyName] 168 | { 169 | get{ 170 | // probably faster without reflection: 171 | // like: return Properties.Settings.Default.PropertyValues[propertyName] 172 | // instead of the following 173 | Type myType = typeof(VsSettings); 174 | PropertyInfo myPropInfo = myType.GetProperty(propertyName); 175 | return myPropInfo.GetValue(this, null); 176 | } 177 | set{ 178 | Type myType = typeof(VsSettings); 179 | PropertyInfo myPropInfo = myType.GetProperty(propertyName); 180 | myPropInfo.SetValue(this, value, null); 181 | } 182 | } 183 | 184 | public static void NotiifyInstancesFmtPropertyChanged(string propertyName, System.Windows.Media.Color value) 185 | { 186 | #if DEBUG_COLOR_STYLE_OPTIONS 187 | Debug.WriteLine($"GeneralOptions.SetColor -> VsSettings.NotiifyInstancesFmtPropertyChanged color={value}"); 188 | #endif 189 | lock (Instances) 190 | { 191 | var sb = new SolidColorBrush(value); 192 | sb.Freeze(); 193 | foreach (var i in Instances) { 194 | i.Value[propertyName] = sb; 195 | } 196 | } 197 | } 198 | 199 | public static void NotifyInstancesPropertyColorSourceChanged(string propertyName, string newColorSource) 200 | { 201 | #if DEBUG_COLOR_STYLE_OPTIONS 202 | Debug.WriteLine($"GeneralOptions.SetColor -> VsSettings.NotifyJumpLabelColorSourceChanged newColorSource={newColorSource}"); 203 | #endif 204 | lock (Instances) 205 | { 206 | foreach (var i in Instances) { 207 | i.Value.FetchColor(propertyName, newColorSource); 208 | } 209 | } 210 | } 211 | 212 | 213 | public static VsSettings GetOrCreate(IWpfTextView textView) 214 | { 215 | lock (Instances) 216 | { 217 | if (!Instances.TryGetValue(textView, out VsSettings settings)) 218 | { 219 | settings = new VsSettings(textView); 220 | Instances.Add(textView, settings); 221 | } 222 | return settings; 223 | } 224 | } 225 | 226 | public static void Initialize(IServiceProvider serviceProviderx, IEditorFormatMapService editorFormatMapService) 227 | { 228 | if (IsInitialized) 229 | throw new InvalidOperationException($"{nameof(VsSettings)} class is already initialized."); 230 | 231 | IsInitialized = true; 232 | 233 | DefaultForegroundBrush.Freeze(); 234 | DefaultBackgroundBrush.Freeze(); 235 | 236 | VsSettings.editorFormatMapService = editorFormatMapService; 237 | VsSettings.serviceProvider = serviceProviderx; 238 | GeneralOptions.Instance.LoadColors(VsSettings.serviceProvider); 239 | } 240 | 241 | public VsSettings(IWpfTextView textView) 242 | { 243 | Debug.Assert(IsInitialized); 244 | 245 | this.textView = textView; 246 | editorFormatMap = editorFormatMapService.GetEditorFormatMap(textView); 247 | ReloadColors(); 248 | 249 | editorFormatMap.FormatMappingChanged += OnFormatItemsChanged; 250 | } 251 | 252 | private void ReloadColors() 253 | { 254 | //ighContrastSelectionFg = GetBrush(editorFormatMap, "Selected Text in High Contrast", BrushType.Foreground, textView); 255 | //ighContrastSelectionBg = GetBrush(editorFormatMap, "Selected Text in High Contrast", BrushType.Background, textView); 256 | FetchColor(nameof(JumpLabelFirstMotionForegroundColor), GeneralOptions.Instance.JumplabelFirstMotionColorSource); 257 | FetchColor(nameof(JumpLabelFirstMotionBackgroundColor), GeneralOptions.Instance.JumplabelFirstMotionColorSource); 258 | Debug.WriteLine($"JUMP LABEL FG={JumpLabelFirstMotionForegroundColor.Color} BG={JumpLabelFirstMotionBackgroundColor.Color}"); 259 | FetchColor(nameof(JumpLabelFinalMotionForegroundColor), GeneralOptions.Instance.JumplabelFinalMotionColorSource); 260 | FetchColor(nameof(JumpLabelFinalMotionBackgroundColor), GeneralOptions.Instance.JumplabelFinalMotionColorSource); 261 | Debug.WriteLine($"JUMP LABEL FG={JumpLabelFinalMotionForegroundColor.Color} BG={JumpLabelFinalMotionBackgroundColor.Color}"); 262 | } 263 | 264 | private void FetchColor(string colorPropertyName, string sourceName) 265 | { 266 | BrushType brushType; 267 | if (colorPropertyName.Contains("Background")) { 268 | brushType = BrushType.Background; 269 | } else if (colorPropertyName.Contains("Foreground")) { 270 | brushType = BrushType.Foreground; 271 | } else { 272 | var msg = $"PeasyMotion: VsSettings - unable to deduce brush type from color property name={colorPropertyName}!"; 273 | Debug.Fail(msg); 274 | throw new Exception(msg); 275 | } 276 | Debug.WriteLine($"VsSettings.FetchColor settings COLOR={colorPropertyName} BRUSH_TYPE={brushType}, Source={sourceName}"); 277 | this[colorPropertyName] = GetBrush(editorFormatMap, sourceName, brushType, textView); 278 | } 279 | 280 | private void OnFormatItemsChanged(object sender, FormatItemsEventArgs args) 281 | { 282 | if (args.ChangedItems.Any(i => i == JumpLabelFirstMotionFormatDef.FMT_NAME)) 283 | { 284 | ReloadColors(); 285 | Debug.WriteLine("VsSettings.OnFormatItemsChanged, FG={JumpLabelFirstMotionForegroundColor.Color} BG={JumpLabelFirstMotionBackgroundColor}"); 286 | Debug.WriteLine("VsSettings.OnFormatItemsChanged Setting GeneralOptions FG & BG"); 287 | GeneralOptions.Instance.JumpLabelFirstMotionForegroundColor = GeneralOptions.toDrawingColor(this.JumpLabelFirstMotionForegroundColor.Color); 288 | GeneralOptions.Instance.JumpLabelFirstMotionBackgroundColor = GeneralOptions.toDrawingColor(this.JumpLabelFirstMotionBackgroundColor.Color); 289 | } 290 | else if (args.ChangedItems.Any(i => i == JumpLabelFinalMotionFormatDef.FMT_NAME)) 291 | { 292 | ReloadColors(); 293 | Debug.WriteLine("VsSettings.OnFormatItemsChanged, FG={JumpLabelFinalMotionForegroundColor.Color} BG={JumpLabelFinalMotionBackgroundColor}"); 294 | Debug.WriteLine("VsSettings.OnFormatItemsChanged Setting GeneralOptions FG & BG"); 295 | GeneralOptions.Instance.JumpLabelFinalMotionForegroundColor = GeneralOptions.toDrawingColor(this.JumpLabelFinalMotionForegroundColor.Color); 296 | GeneralOptions.Instance.JumpLabelFinalMotionBackgroundColor = GeneralOptions.toDrawingColor(this.JumpLabelFinalMotionBackgroundColor.Color); 297 | } 298 | } 299 | 300 | private static SolidColorBrush GetBrush(IEditorFormatMap editorFormatMap, string propertyName, BrushType type, IWpfTextView textView) 301 | { 302 | var props = editorFormatMap.GetProperties(propertyName); 303 | var typeText = type.ToString(); 304 | 305 | object value = null; 306 | if (props.Contains(typeText)) 307 | { 308 | value = props[typeText]; 309 | } 310 | else 311 | { 312 | typeText += "Color"; 313 | if (props.Contains(typeText)) 314 | { 315 | value = props[typeText]; 316 | if (value is wpf.Color) 317 | { 318 | var color = (wpf.Color)value; 319 | var cb = new SolidColorBrush(color); 320 | cb.Freeze(); 321 | value = cb; 322 | } 323 | } 324 | else 325 | { 326 | //Background is often not found in editorFormatMap. Don't know why :( 327 | if (type == BrushType.Background) 328 | { 329 | value = textView.Background; 330 | } 331 | } 332 | } 333 | 334 | return (value as SolidColorBrush) ?? (type == BrushType.Background ? DefaultBackgroundBrush : DefaultForegroundBrush); 335 | } 336 | 337 | private void OnPropertyChanged(string name) 338 | { 339 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); 340 | } 341 | 342 | public void Dispose() 343 | { 344 | editorFormatMap.FormatMappingChanged -= OnFormatItemsChanged; 345 | } 346 | 347 | private enum BrushType 348 | { 349 | Foreground, 350 | Background 351 | } 352 | } 353 | 354 | public sealed partial class EditorHostFactory 355 | { 356 | internal static Version VisualStudioVersion => new Version(16, 0, 0, 0); 357 | internal static Version VisualStudioThreadingVersion => new Version(16, 0, 0, 0); 358 | 359 | internal static string[] CoreEditorComponents = 360 | new[] 361 | { 362 | "Microsoft.VisualStudio.Platform.VSEditor.dll", 363 | "Microsoft.VisualStudio.Text.Internal.dll", 364 | "Microsoft.VisualStudio.Text.Logic.dll", 365 | "Microsoft.VisualStudio.Text.UI.dll", 366 | "Microsoft.VisualStudio.Text.UI.Wpf.dll", 367 | "Microsoft.VisualStudio.Language.dll", 368 | }; 369 | 370 | private readonly List _composablePartCatalogList = new List(); 371 | private readonly List _exportProviderList = new List(); 372 | 373 | public EditorHostFactory() 374 | { 375 | BuildCatalog(); 376 | } 377 | 378 | public void Add(ComposablePartCatalog composablePartCatalog) 379 | { 380 | _composablePartCatalogList.Add(composablePartCatalog); 381 | } 382 | 383 | public void Add(ExportProvider exportProvider) 384 | { 385 | _exportProviderList.Add(exportProvider); 386 | } 387 | 388 | public CompositionContainer CreateCompositionContainer() 389 | { 390 | var catalog = new AggregateCatalog(_composablePartCatalogList.ToArray()); 391 | return new CompositionContainer(catalog, _exportProviderList.ToArray()); 392 | } 393 | 394 | private void BuildCatalog() 395 | { 396 | var editorAssemblyVersion = new Version(VisualStudioVersion.Major, 0, 0, 0); 397 | AppendEditorAssemblies(editorAssemblyVersion); 398 | AppendEditorAssembly("Microsoft.VisualStudio.Threading", VisualStudioThreadingVersion); 399 | _exportProviderList.Add(new JoinableTaskContextExportProvider()); 400 | _composablePartCatalogList.Add(new AssemblyCatalog(typeof(EditorHostFactory).Assembly)); 401 | } 402 | 403 | private void AppendEditorAssemblies(Version editorAssemblyVersion) 404 | { 405 | foreach (var name in CoreEditorComponents) 406 | { 407 | var simpleName = Path.GetFileNameWithoutExtension(name); 408 | AppendEditorAssembly(simpleName, editorAssemblyVersion); 409 | } 410 | } 411 | 412 | private void AppendEditorAssembly(string name, Version version) 413 | { 414 | var assembly = GetEditorAssembly(name, version); 415 | _composablePartCatalogList.Add(new AssemblyCatalog(assembly)); 416 | } 417 | 418 | private static Assembly GetEditorAssembly(string assemblyName, Version version) 419 | { 420 | //var qualifiedName = $"{assemblyName}, Version={version}, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"; 421 | var qualifiedName = $"{assemblyName}, Version={version}, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; 422 | //var A = Assembly.GetExecutingAssembly().GetReferencedAssemblies(); 423 | //foreach(var f in A) { Debug.WriteLine(f); } 424 | //foreach(var f in Assembly.GetExecutingAssembly().GetFiles()) { Debug.WriteLine(f.Name); } 425 | return Assembly.Load(qualifiedName); 426 | } 427 | } 428 | } 429 | /* 430 | mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 431 | Microsoft.VisualStudio.OLE.Interop, Version=7.1.40304.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 432 | System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 433 | Microsoft.VisualStudio.TextManager.Interop, Version=7.1.40304.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 434 | Microsoft.VisualStudio.Text.UI, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 435 | PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 436 | System.Xaml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 437 | System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 438 | WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 439 | PresentationCore, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 440 | EnvDTE, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 441 | System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 442 | System.ComponentModel.Composition, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 443 | Microsoft.VisualStudio.Shell.15.0, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 444 | Microsoft.VisualStudio.Editor, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 445 | Microsoft.VisualStudio.Text.Logic, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 446 | Microsoft.VisualStudio.Text.UI.Wpf, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 447 | Microsoft.VisualStudio.Shell.Framework, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 448 | Microsoft.VisualStudio.Threading, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 449 | Microsoft.VisualStudio.ComponentModelHost, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 450 | Microsoft.VisualStudio.Text.Data, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 451 | Microsoft.VisualStudio.CoreUtility, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 452 | System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 453 | Microsoft.VisualStudio.Shell.Interop.8.0, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 454 | Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 455 | System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 456 | Microsoft.VisualStudio.Validation, Version=15.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 457 | */ -------------------------------------------------------------------------------- /Shared/PeasyMotionOptions.cs: -------------------------------------------------------------------------------- 1 | //#define MEASUREEXECTIME 2 | //#define DEBUG_COLOR_STYLE_OPTIONS 3 | 4 | // Options based on VSSDK Options code + some stackoverflow recipes and few classes from VsVim :} 5 | // original ColorKey, ColorInfo, LoadColor*, SaveColor from VsVim/Src/VsVimShared/Implementation/OptionPages/DefaultOptionPage.cs 6 | // 7 | /* VsVim licence: 8 | Copyright 2012 Jared Parsons 9 | 10 | Licensed under the Apache License, Version 2.0 (the "License"); 11 | you may not use this file except in compliance with the License. 12 | You may obtain a copy of the License at 13 | 14 | http://www.apache.org/licenses/LICENSE-2.0 15 | 16 | Unless required by applicable law or agreed to in writing, software 17 | distributed under the License is distributed on an "AS IS" BASIS, 18 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | See the License for the specific language governing permissions and 20 | limitations under the License. 21 | */ 22 | 23 | using System.Diagnostics; 24 | using System.ComponentModel; 25 | using Microsoft.VisualStudio.Shell; 26 | using PeasyMotion.Options; 27 | using System.Drawing; 28 | using System.Collections.Generic; 29 | using Microsoft.VisualStudio.ComponentModelHost; 30 | using System; 31 | using System.Linq; 32 | using System.Text; 33 | using Microsoft.VisualStudio.Shell.Interop; 34 | using Microsoft.VisualStudio; 35 | using Microsoft.VisualStudio.TextManager.Interop; 36 | using System.Collections.ObjectModel; 37 | using System.Globalization; 38 | using System.Collections; 39 | using System.Windows.Media; 40 | using System.Drawing.Design; 41 | using System.Runtime.CompilerServices; 42 | 43 | namespace PeasyMotion 44 | { 45 | 46 | public enum JumpLabelAssignmentAlgorithm 47 | { 48 | CaretRelative, 49 | ViewportRelative 50 | } 51 | 52 | public class Enum2StrHelper { 53 | private static List _s = new List(); 54 | private static List _e = new List(); 55 | private static void init() { 56 | foreach(T ev in(T[]) Enum.GetValues(typeof(T))) { _e.Add(ev); } 57 | foreach(String en in Enum.GetNames(typeof(T))) { _s.Add(en); } 58 | } 59 | 60 | public static List e 61 | { 62 | get { 63 | if (_e.Count == 0) { init(); } 64 | return _e; 65 | } 66 | } 67 | 68 | public static List s 69 | { 70 | get { 71 | if (_s.Count == 0) { init(); } 72 | return _s; 73 | } 74 | } 75 | // teh noob way. 76 | public static T convertToEnumVal(String str) { 77 | var i = s.IndexOf(str); 78 | return e[i != -1 ? i : 0]; 79 | } 80 | public static String convertToStr(T v) { 81 | var i = e.IndexOf(v); 82 | return s[i != -1 ? i : 0]; 83 | } 84 | } 85 | 86 | // helps to avoid pkg assembly version dependency & exceptions when (de)serializing enums 87 | public class EnumValuesListConverter : StringConverter 88 | { 89 | public override Boolean GetStandardValuesSupported(ITypeDescriptorContext context) { return true; } 90 | public override Boolean GetStandardValuesExclusive(ITypeDescriptorContext context) { return true; } 91 | public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { 92 | return new StandardValuesCollection( new List( Enum2StrHelper.s )); 93 | } 94 | } 95 | 96 | // TypeConverter workaround: if we use typeof(EnumValuesListConverter) directly in TypeConvertor attribue declaration 97 | // then somehow dropdown list stops showing and we see text edit field instead. 98 | // Using inheritance (fuck it), we'are able to workaound the issue. 99 | public class JumpAlgoEnumValuesListConverter : EnumValuesListConverter {} 100 | 101 | public class TextEditorFontsAndColorsItemsList 102 | { 103 | private static List _colorableItemsCached = new List(); 104 | public static List ColorableItemsCached 105 | { 106 | get { 107 | if (_colorableItemsCached.Count == 0) { 108 | obtainItems(); 109 | } 110 | return _colorableItemsCached; 111 | } 112 | } 113 | 114 | private static void obtainItems() { 115 | #if MEASUREEXECTIME 116 | var watch = System.Diagnostics.Stopwatch.StartNew(); 117 | #endif 118 | ThreadHelper.ThrowIfNotOnUIThread(); 119 | 120 | try { 121 | var dte = Package.GetGlobalService(typeof(SDTE)) as EnvDTE.DTE; 122 | var props = dte.Properties["FontsAndColors", "TextEditor"]; 123 | var fac = (EnvDTE.FontsAndColorsItems)props.Item("FontsAndColorsItems").Object; 124 | var enumfac = fac.GetEnumerator(); 125 | while (false != enumfac.MoveNext()) 126 | { 127 | var i = enumfac.Current; var i2 = (EnvDTE.ColorableItems)i; 128 | _colorableItemsCached.Add(i2.Name); 129 | } 130 | //var colors = ConvertDTEColor(((EnvDTE.ColorableItems)fac.Item("Plain Text")).Foreground); 131 | //FontFamily = props.Item("FontFamily").Value.ToString(); //FontSize = (float)(short)props.Item("FontSize").Value; //FontBold = colors.Bold; 132 | //ForeColor = ColorTranslator.FromOle((int)colors.Foreground); //BackColor = ColorTranslator.FromOle((int)colors.Background); 133 | //colors = (EnvDTE.ColorableItems)fac.Item("Selected Text"); 134 | //HighlightFontBold = colors.Bold; //HighlightForeColor = ColorTranslator.FromOle((int)colors.Foreground); //HighlightBackColor = ColorTranslator.FromOle((int)colors.Background); 135 | } catch (Exception ex) { 136 | Debug.WriteLine("Error loading text editor font and colors"); 137 | Debug.WriteLine(ex.ToString()); 138 | } 139 | #if MEASUREEXECTIME 140 | watch.Stop(); 141 | Debug.WriteLine($"PeasyMotion TextEditorFontsAndColorsItemsList obtaining FontsAndColorsItems took {watch.ElapsedMilliseconds} ms"); 142 | #endif 143 | } 144 | } 145 | 146 | // GitHub search helper (spent ~5 hours googling out this simple snippet T_T) 147 | // provides ComboBox DropDown for string list Property converter DialogPage List List PorpertyGrid Options Dialog ProvideOptionPage 148 | // TypeConverter ListBox blah blah any other search keywords? 149 | public class TextEditorClassificationStringConverter : StringConverter 150 | { 151 | public override Boolean GetStandardValuesSupported(ITypeDescriptorContext context) { return true; } 152 | public override Boolean GetStandardValuesExclusive(ITypeDescriptorContext context) { return true; } 153 | public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { 154 | return new StandardValuesCollection(new List(TextEditorFontsAndColorsItemsList.ColorableItemsCached)); 155 | } 156 | } 157 | 158 | readonly struct ColorKey 159 | { 160 | internal readonly string Name; 161 | internal readonly bool IsForeground; 162 | internal ColorKey(string name, bool isForeground) { Name = name; IsForeground = isForeground; } 163 | internal static ColorKey Foreground(string name) { return new ColorKey(name, isForeground: true); } 164 | internal static ColorKey Background(string name) { return new ColorKey(name, isForeground: false); } 165 | } 166 | 167 | sealed class ColorInfo 168 | { 169 | internal readonly ColorKey ColorKey; 170 | internal readonly System.Drawing.Color OriginalColor; 171 | internal bool IsValid; 172 | internal System.Drawing.Color Color; 173 | 174 | internal ColorInfo(ColorKey colorKey, System.Drawing.Color color, bool isValid = true) { ColorKey = colorKey; OriginalColor = color; Color = color; IsValid = isValid; } 175 | } 176 | 177 | internal class GeneralOptions : BaseOptionModel 178 | { 179 | internal const string _PkgVersion = "1.16.0"; 180 | public static string getCurrentVersion() { return _PkgVersion; } 181 | 182 | private static readonly ColorKey s_jumpLabelFirstMotionColorBg = ColorKey.Background(JumpLabelFirstMotionFormatDef.FMT_NAME); 183 | private static readonly ColorKey s_jumpLabelFirstMotionColorFg = ColorKey.Foreground(JumpLabelFirstMotionFormatDef.FMT_NAME); 184 | private static readonly ColorKey s_jumpLabelFinalMotionColorBg = ColorKey.Background(JumpLabelFinalMotionFormatDef.FMT_NAME); 185 | private static readonly ColorKey s_jumpLabelFinalMotionColorFg = ColorKey.Foreground(JumpLabelFinalMotionFormatDef.FMT_NAME); 186 | 187 | private static readonly ReadOnlyCollection s_colorKeyList = new ReadOnlyCollection(new[] 188 | { 189 | s_jumpLabelFirstMotionColorBg, 190 | s_jumpLabelFirstMotionColorFg, 191 | s_jumpLabelFinalMotionColorBg, 192 | s_jumpLabelFinalMotionColorFg, 193 | }); 194 | 195 | private readonly Dictionary colorMap = new Dictionary(); 196 | 197 | public GeneralOptions() 198 | { 199 | foreach (var colorKey in s_colorKeyList) { 200 | colorMap[colorKey] = new ColorInfo(colorKey, System.Drawing.Color.Black); 201 | } 202 | } 203 | 204 | private bool isJumpLabelFirstMotionColorSourceItsOwn() { 205 | return JumplabelFirstMotionColorSource == JumpLabelFirstMotionFormatDef.FMT_NAME; 206 | } 207 | 208 | [HiddenOption()] 209 | protected string installedVersion{ get; set; } = "0.0.0"; 210 | 211 | public void setInstalledVersionToCurrentPkgVersion() { installedVersion = _PkgVersion; } 212 | public string getInstalledVersionStr() { return installedVersion; } 213 | 214 | [Category("General")] 215 | [DisplayName("First motion jump label background color")] 216 | [DisableOptionSerialization()]// no need to serialize this property, as it is stored in Fonts & Colors 217 | [Description("!!! ATTENTION !!! \nChanging this color makes sense only when\n 'Fetch 'first motion' jump label colors from' is equal to " + JumpLabelFirstMotionFormatDef.FMT_NAME)] 218 | public System.Drawing.Color JumpLabelFirstMotionBackgroundColor 219 | { 220 | get { return GetColor(s_jumpLabelFirstMotionColorBg); } 221 | set { 222 | #if DEBUG_COLOR_STYLE_OPTIONS 223 | Debug.WriteLine($"GeneralOptions.JumpLabelFirstMotionBackgroundColor property set color={value}"); 224 | if (JumpLabelFirstMotionBackgroundColor != value) { 225 | Debug.WriteLine($"GeneralOptions.JumpLabelFirstMotionBackgroundColor property differs by val. Calling GeneralOptions.SetColor"); 226 | } 227 | #endif 228 | SetColor(isJumpLabelFirstMotionColorSourceItsOwn(), nameof(JumpLabelFirstMotionBackgroundColor), s_jumpLabelFirstMotionColorBg, value); 229 | } 230 | } 231 | 232 | 233 | 234 | [Category("General")] 235 | [DisplayName("First motion jump label foreground color")] 236 | [DisableOptionSerialization()]// no need to serialize this property, as it is stored in Fonts & Colors 237 | [Description("!!! ATTENTION !!! \nChanging this color makes sense only when\n 'Fetch 'first motion' jump label colors from' is equal to " + JumpLabelFirstMotionFormatDef.FMT_NAME)] 238 | public System.Drawing.Color JumpLabelFirstMotionForegroundColor 239 | { 240 | get { return GetColor(s_jumpLabelFirstMotionColorFg); } 241 | set { 242 | #if DEBUG_COLOR_STYLE_OPTIONS 243 | Debug.WriteLine($"GeneralOptions.JumpLabelFirstMotionForegroundColor property set color={value}"); 244 | if (JumpLabelFirstMotionForegroundColor != value) { 245 | Debug.WriteLine($"GeneralOptions.JumpLabelFirstMotionForegroundColor property differs by val. Calling GeneralOptions.SetColor"); 246 | } 247 | #endif 248 | SetColor(isJumpLabelFirstMotionColorSourceItsOwn(), nameof(JumpLabelFirstMotionForegroundColor), s_jumpLabelFirstMotionColorFg, value); 249 | } 250 | } 251 | 252 | 253 | 254 | private bool isJumpLabelFinalMotionColorSourceItsOwn() { 255 | return JumplabelFinalMotionColorSource == JumpLabelFinalMotionFormatDef.FMT_NAME; 256 | } 257 | 258 | [Category("General")] 259 | [DisplayName("Final motion jump label background color")] 260 | [DisableOptionSerialization()]// no need to serialize this property, as it is stored in Fonts & Colors 261 | [Description("!!! ATTENTION !!! \nChanging this color makes sense only when\n 'Fetch 'final motion' jump label colors from' is equal to " + JumpLabelFinalMotionFormatDef.FMT_NAME)] 262 | public System.Drawing.Color JumpLabelFinalMotionBackgroundColor 263 | { 264 | get { return GetColor(s_jumpLabelFinalMotionColorBg); } 265 | set { 266 | #if DEBUG_COLOR_STYLE_OPTIONS 267 | Debug.WriteLine($"GeneralOptions.JumpLabelFinalMotionBackgroundColor property set color={value}"); 268 | if (JumpLabelFinalMotionBackgroundColor != value) { 269 | Debug.WriteLine($"GeneralOptions.JumpLabelFinalMotionBackgroundColor property differs by val. Calling GeneralOptions.SetColor"); 270 | } 271 | #endif 272 | SetColor(isJumpLabelFinalMotionColorSourceItsOwn(), nameof(JumpLabelFinalMotionBackgroundColor), s_jumpLabelFinalMotionColorBg, value); 273 | } 274 | } 275 | 276 | 277 | [Category("General")] 278 | [DisplayName("Final motion jump label foreground color")] 279 | [DisableOptionSerialization()]// no need to serialize this property, as it is stored in Fonts & Colors 280 | [Description("!!! ATTENTION !!! \nChanging this color makes sense only when\n 'Fetch 'Final motion' jump label colors from' is equal to " + JumpLabelFinalMotionFormatDef.FMT_NAME)] 281 | public System.Drawing.Color JumpLabelFinalMotionForegroundColor 282 | { 283 | get { return GetColor(s_jumpLabelFinalMotionColorFg); } 284 | set { 285 | #if DEBUG_COLOR_STYLE_OPTIONS 286 | Debug.WriteLine($"GeneralOptions.JumpLabelFinalMotionForegroundColor property set color={value}"); 287 | if (JumpLabelFinalMotionForegroundColor != value) { 288 | Debug.WriteLine($"GeneralOptions.JumpLabelFinalMotionForegroundColor property differs by val. Calling GeneralOptions.SetColor"); 289 | } 290 | #endif 291 | SetColor(isJumpLabelFinalMotionColorSourceItsOwn(), nameof(JumpLabelFinalMotionForegroundColor), s_jumpLabelFinalMotionColorFg, value); 292 | } 293 | } 294 | 295 | 296 | private String _jumpLabelAssignmentAlgorithmStr = 297 | Enum2StrHelper.convertToStr(JumpLabelAssignmentAlgorithm.CaretRelative); 298 | [Category("General")] 299 | [DisplayName("Jump label assignment algorithm")] 300 | [Description( 301 | "Affects jump label placement behaviour.\n" + 302 | "- CaretRelative - place shortest jump labels closer to caret,\n" + 303 | "label positions and values may be not reproducible if cursor moved slightly.\n" + 304 | "Sensivity to caret position can be adjusted using Caret position sensivity option.\n" + 305 | "Jump labels will be reproducible for caret positions differing \u00B1sensivity chars.\n" + 306 | "- ViewportRelative - assign labels starting from text viewport top, ignoring caret position." 307 | )] 308 | [DefaultValue("CaretRelative")] 309 | [TypeConverter(typeof(JumpAlgoEnumValuesListConverter))] 310 | public String jumpLabelAssignmentAlgorithmStr4{ 311 | get { return _jumpLabelAssignmentAlgorithmStr; } 312 | set { _jumpLabelAssignmentAlgorithmStr = value; } 313 | } 314 | 315 | public JumpLabelAssignmentAlgorithm getJumpLabelAssignmentAlgorithm() { 316 | return Enum2StrHelper.convertToEnumVal(_jumpLabelAssignmentAlgorithmStr); 317 | } 318 | 319 | [Category("General")] 320 | [DisplayName("Caret position sensivity")] 321 | [Description( 322 | "Sensivity to caret position during jump label assignment.\n" + 323 | "Jumplabels will be reproducible for caret positions differing \u00B1sensivity chars.\n" 324 | )] 325 | [DefaultValue(0)] 326 | public int caretPositionSensivity { get; set; } 327 | 328 | public const string DefaultAllowedKeys = "asdghklqwertyuiopzxcvbnmfj;"; 329 | private string _allowedJumpKeys = DefaultAllowedKeys; 330 | [Category("General")] 331 | [DisplayName("Allowed jump label characters")] 332 | [Description("Default = \"asdghklqwertyuiopzxcvbnmfj;\" | " + 333 | "Set field empty to reset to defaults. Beware - characters order affects ergonomics!\n" + 334 | "Chars must be unique among list. Allowed jump label characters: " + 335 | "lowercase letters only, numbers and punctuation allowed.")] 336 | [DefaultValue(DefaultAllowedKeys)] 337 | public string AllowedJumpKeys{ 338 | get { return _allowedJumpKeys; } 339 | set{ 340 | bool AllCharsUnique(string s) { 341 | bool[] c = new bool[256]; 342 | Array.Clear(c, 0, c.Length); 343 | for (int i = 0; i < s.Length; i++) { 344 | if (c[(int)s[i]]) return false; 345 | c[(int)s[i]] = true; 346 | } 347 | return true; 348 | } 349 | bool valid = true; 350 | if (!AllCharsUnique(value)) { 351 | valid = false; 352 | } 353 | string vv = ""; 354 | if (value.Length < 2) { 355 | _allowedJumpKeys = DefaultAllowedKeys; 356 | valid = false; 357 | } 358 | for (int i = 0; i < value.Length; i++) { 359 | char c = value[i]; 360 | if (!Char.IsLetterOrDigit(c) && !Char.IsPunctuation(c)) { 361 | valid = false; break; 362 | } 363 | if (Char.IsLetter(c) && !Char.IsLower(c)) { 364 | valid = false; break; 365 | } 366 | vv += c; 367 | } 368 | if (valid) { 369 | _allowedJumpKeys = vv; 370 | } 371 | } 372 | } 373 | 374 | private String jumplabelFirstMotionColorSource = JumpLabelFirstMotionFormatDef.FMT_NAME; 375 | [Category("General")] 376 | [DisplayName("Fetch 'First motion' jump label colors from")] 377 | [Description("Live preview available!\nJust iinvoke PeasyMotion and goto Tools->Options and adjust style with live preview.\n" + 378 | "When is not equal to " + JumpLabelFirstMotionFormatDef.FMT_NAME + " one can sync label color style to other classification items from Tools->Options->Fonts And Colors->Text Editor.\n" + 379 | "When equal to " + JumpLabelFirstMotionFormatDef.FMT_NAME + " one can configure classification style manually trough Tools->Options->PeasyMotion or\nTools->Options->Fonts And Colors->Text Editor->'PeasyMotion First Motion Jump label color'.")] 380 | [DefaultValue(JumpLabelFirstMotionFormatDef.FMT_NAME)] // by default we stick with peasy motion colors 381 | [TypeConverter(typeof(TextEditorClassificationStringConverter))] 382 | public String JumplabelFirstMotionColorSource { 383 | get { return jumplabelFirstMotionColorSource; } 384 | set { 385 | if (TextEditorFontsAndColorsItemsList.ColorableItemsCached.Contains(value)) { 386 | jumplabelFirstMotionColorSource = value; 387 | } else { 388 | jumplabelFirstMotionColorSource = JumpLabelFirstMotionFormatDef.FMT_NAME; 389 | Debug.WriteLine($"Trying to set jump label color source to unexistant source value = {value}. Ignoring!"); 390 | } 391 | VsSettings.NotifyInstancesPropertyColorSourceChanged(nameof(JumpLabelFirstMotionForegroundColor), value); 392 | VsSettings.NotifyInstancesPropertyColorSourceChanged(nameof(JumpLabelFirstMotionBackgroundColor), value); 393 | } 394 | } 395 | 396 | private String jumplabelFinalMotionColorSource = JumpLabelFinalMotionFormatDef.FMT_NAME; 397 | [Category("General")] 398 | [DisplayName("Fetch 'Final motion' jump label colors from")] 399 | [Description("Live preview available!\nJust iinvoke PeasyMotion and goto Tools->Options and adjust style with live preview.\n" + 400 | "When is not equal to " + JumpLabelFinalMotionFormatDef.FMT_NAME + " one can sync label color style to other classification items from Tools->Options->Fonts And Colors->Text Editor.\n" + 401 | "When equal to " + JumpLabelFinalMotionFormatDef.FMT_NAME + " one can configure classification style manually trough Tools->Options->PeasyMotion or\nTools->Options->Fonts And Colors->Text Editor->'PeasyMotion Final Motion Jump label color'.")] 402 | [DefaultValue(JumpLabelFinalMotionFormatDef.FMT_NAME)] // by default we stick with peasy motion colors 403 | [TypeConverter(typeof(TextEditorClassificationStringConverter))] 404 | public String JumplabelFinalMotionColorSource { 405 | get { return jumplabelFinalMotionColorSource; } 406 | set { 407 | if (TextEditorFontsAndColorsItemsList.ColorableItemsCached.Contains(value)) { 408 | jumplabelFinalMotionColorSource = value; 409 | } else { 410 | jumplabelFinalMotionColorSource = JumpLabelFinalMotionFormatDef.FMT_NAME; 411 | Trace.WriteLine($"Trying to set jump label color source to unexistant source value = {value}. Ignoring!"); 412 | } 413 | VsSettings.NotifyInstancesPropertyColorSourceChanged(nameof(JumpLabelFinalMotionForegroundColor), value); 414 | VsSettings.NotifyInstancesPropertyColorSourceChanged(nameof(JumpLabelFinalMotionBackgroundColor), value); 415 | } 416 | } 417 | 418 | public static System.Windows.Media.Color fromDrawingColor(System.Drawing.Color c) { 419 | return System.Windows.Media.Color.FromArgb(c.A, c.R, c.G, c.B); 420 | } 421 | public static System.Drawing.Color toDrawingColor(System.Windows.Media.Color c) { 422 | return System.Drawing.Color.FromArgb(c.A, c.R, c.G, c.B); 423 | } 424 | 425 | private System.Drawing.Color GetColor(ColorKey colorKey) { 426 | #if DEBUG_COLOR_STYLE_OPTIONS 427 | Debug.WriteLine($"GeneralOptions.GetColor keyName={colorKey.Name}" + (colorKey.IsForeground? "FG":"BG") + $" color={colorMap[colorKey].Color}"); 428 | #endif 429 | return colorMap[colorKey].Color; 430 | } 431 | 432 | private void SetColor(bool sendNotification, string propertyName, ColorKey colorKey, System.Drawing.Color value) { 433 | #if DEBUG_COLOR_STYLE_OPTIONS 434 | Debug.WriteLine($"GeneralOptions.SetColor keyName={colorKey.Name}" + (colorKey.IsForeground? "FG":"BG") + $" color={value} Notify={sendNotification}"); 435 | #endif 436 | colorMap[colorKey].Color = value; 437 | if (sendNotification) { 438 | VsSettings.NotiifyInstancesFmtPropertyChanged(propertyName, fromDrawingColor(value)); 439 | } 440 | } 441 | 442 | public void LoadColors(IServiceProvider Site) 443 | { 444 | #if DEBUG_COLOR_STYLE_OPTIONS 445 | Debug.WriteLine($"GeneralOptions.LoadColors"); 446 | #endif 447 | ThreadHelper.ThrowIfNotOnUIThread(); 448 | try 449 | { 450 | var guid = Microsoft.VisualStudio.Editor.DefGuidList.guidTextEditorFontCategory; 451 | var flags = __FCSTORAGEFLAGS.FCSF_LOADDEFAULTS;//| __FCSTORAGEFLAGS.FCSF_NOAUTOCOLORS; 452 | var vsStorage = (IVsFontAndColorStorage)(Site.GetService(typeof(SVsFontAndColorStorage))); 453 | ErrorHandler.ThrowOnFailure(vsStorage.OpenCategory(ref guid, (uint)flags)); 454 | LoadColorsCore(Site, vsStorage); 455 | ErrorHandler.ThrowOnFailure(vsStorage.CloseCategory()); 456 | } 457 | catch (Exception ex) 458 | { 459 | Debug.WriteLine($"PeasyMotion exception in Options.LoadColors: {ex.ToString()}"); 460 | } 461 | } 462 | 463 | private void LoadColorsCore(IServiceProvider Site, IVsFontAndColorStorage vsStorage) 464 | { 465 | ThreadHelper.ThrowIfNotOnUIThread(); 466 | foreach (var colorKey in s_colorKeyList) 467 | { 468 | ColorInfo colorInfo; 469 | try 470 | { 471 | var color = LoadColor(Site, vsStorage, colorKey); 472 | colorInfo = new ColorInfo(colorKey, color); 473 | } 474 | catch (Exception ex) 475 | { 476 | Debug.WriteLine($"PeasyMotion exception in Options.LoadColorsCore: {ex.ToString()}"); 477 | colorInfo = new ColorInfo(colorKey, System.Drawing.Color.Black, isValid: false); 478 | } 479 | 480 | colorMap[colorKey] = colorInfo; 481 | } 482 | } 483 | 484 | public void SaveColors(IServiceProvider Site) 485 | { 486 | Debug.WriteLine($"GeneralOptions.SaveColors"); 487 | ThreadHelper.ThrowIfNotOnUIThread(); 488 | if (Site == null) 489 | { 490 | return; 491 | } 492 | 493 | try 494 | { 495 | var guid = Microsoft.VisualStudio.Editor.DefGuidList.guidTextEditorFontCategory; 496 | var flags = __FCSTORAGEFLAGS.FCSF_LOADDEFAULTS | __FCSTORAGEFLAGS.FCSF_PROPAGATECHANGES; 497 | var vsStorage = (IVsFontAndColorStorage)(Site.GetService(typeof(SVsFontAndColorStorage))); 498 | var vsStorageCache = (IVsFontAndColorCacheManager )(Site.GetService(typeof(SVsFontAndColorCacheManager))); 499 | 500 | ErrorHandler.ThrowOnFailure(vsStorage.OpenCategory(ref guid, (uint)flags)); 501 | foreach (var colorInfo in colorMap.Values) 502 | { 503 | SaveColor(vsStorage, colorInfo.ColorKey, colorInfo.Color); 504 | } 505 | ErrorHandler.ThrowOnFailure(vsStorage.CloseCategory()); 506 | 507 | if (vsStorageCache != null) { 508 | vsStorageCache.ClearAllCaches(); 509 | vsStorageCache.RefreshCache(Microsoft.VisualStudio.Editor.DefGuidList.guidTextEditorFontCategory); 510 | } 511 | } 512 | catch (Exception ex) 513 | { 514 | Debug.WriteLine($"PeasyMotion exception in Options.SaveColors(): {ex.ToString()}"); 515 | } 516 | } 517 | 518 | private static System.Drawing.Color LoadColor(IServiceProvider Site, IVsFontAndColorStorage vsStorage, ColorKey colorKey) 519 | { 520 | ThreadHelper.ThrowIfNotOnUIThread(); 521 | var arr = new ColorableItemInfo[1]; 522 | ErrorHandler.ThrowOnFailure(vsStorage.GetItem(colorKey.Name, arr)); 523 | 524 | var isValid = colorKey.IsForeground ? arr[0].bForegroundValid : arr[0].bBackgroundValid; 525 | if (isValid == 0) 526 | { 527 | throw new Exception(); 528 | } 529 | 530 | var colorRef = colorKey.IsForeground ? arr[0].crForeground : arr[0].crBackground; 531 | var color = FromColorRef(Site, vsStorage, colorRef); 532 | 533 | Debug.WriteLine($"GeneralOptions.LoadColoR keyName={colorKey.Name} color={color}"); 534 | return color; 535 | } 536 | 537 | private static void SaveColor(IVsFontAndColorStorage vsStorage, ColorKey colorKey, System.Drawing.Color color) 538 | { 539 | Debug.WriteLine($"GeneralOptions.SaveColoR keyName={colorKey.Name} color={color}"); 540 | ThreadHelper.ThrowIfNotOnUIThread(); 541 | ColorableItemInfo[] arr = new ColorableItemInfo[1]; 542 | ErrorHandler.ThrowOnFailure(vsStorage.GetItem(colorKey.Name, arr)); 543 | if (colorKey.IsForeground) 544 | { 545 | arr[0].bForegroundValid = 1; 546 | arr[0].crForeground = (uint)ColorTranslator.ToWin32(color); 547 | } 548 | else 549 | { 550 | arr[0].bBackgroundValid = 1; 551 | arr[0].crBackground = (uint)ColorTranslator.ToWin32(color); 552 | } 553 | ErrorHandler.ThrowOnFailure(vsStorage.SetItem(colorKey.Name, arr)); 554 | } 555 | 556 | private static System.Drawing.Color FromColorRef(IServiceProvider Site, IVsFontAndColorStorage vsStorage, uint colorValue) 557 | { 558 | ThreadHelper.ThrowIfNotOnUIThread(); 559 | var vsUtil = (IVsFontAndColorUtilities)vsStorage; 560 | ErrorHandler.ThrowOnFailure(vsUtil.GetColorType(colorValue, out int type)); 561 | switch ((__VSCOLORTYPE)type) 562 | { 563 | case __VSCOLORTYPE.CT_SYSCOLOR: 564 | case __VSCOLORTYPE.CT_RAW: 565 | return ColorTranslator.FromWin32((int)colorValue); 566 | case __VSCOLORTYPE.CT_COLORINDEX: 567 | { 568 | var array = new COLORINDEX[1]; 569 | ErrorHandler.ThrowOnFailure(vsUtil.GetEncodedIndex(colorValue, array)); 570 | ErrorHandler.ThrowOnFailure(vsUtil.GetRGBOfIndex(array[0], out uint rgb)); 571 | return ColorTranslator.FromWin32((int)rgb); 572 | }; 573 | case __VSCOLORTYPE.CT_VSCOLOR: 574 | { 575 | var vsUIShell = (IVsUIShell2)Site.GetService(typeof(SVsUIShell)); 576 | ErrorHandler.ThrowOnFailure(vsUtil.GetEncodedVSColor(colorValue, out int index)); 577 | ErrorHandler.ThrowOnFailure(vsUIShell.GetVSSysColorEx(index, out uint rgbValue)); 578 | return ColorTranslator.FromWin32((int)rgbValue); 579 | }; 580 | case __VSCOLORTYPE.CT_AUTOMATIC: 581 | case __VSCOLORTYPE.CT_TRACK_BACKGROUND: 582 | case __VSCOLORTYPE.CT_TRACK_FOREGROUND: 583 | case __VSCOLORTYPE.CT_INVALID: 584 | return System.Drawing.Color.Transparent; 585 | default: 586 | return System.Drawing.Color.Black; 587 | } 588 | } 589 | } 590 | 591 | internal class DialogPageProvider 592 | { 593 | public class General : BaseOptionPage 594 | { 595 | protected override void OnActivate(CancelEventArgs e) 596 | { 597 | Debug.WriteLine($"GeneralOptions OnActivate"); 598 | ThreadHelper.ThrowIfNotOnUIThread(); 599 | base.OnActivate(e); 600 | (base._model as GeneralOptions).LoadColors(this.Site); 601 | } 602 | 603 | protected override void OnApply(PageApplyEventArgs e) 604 | { 605 | Debug.WriteLine($"GeneralOptions OnApply"); 606 | ThreadHelper.ThrowIfNotOnUIThread(); 607 | base.OnApply(e); 608 | (base._model as GeneralOptions).SaveColors(this.Site); 609 | } 610 | } 611 | } 612 | 613 | } -------------------------------------------------------------------------------- /Shared/PeasyMotionActivate.cs: -------------------------------------------------------------------------------- 1 | //#define MEASUREEXECTIME 2 | 3 | using System; 4 | using System.Drawing; 5 | using System.Collections.Generic; 6 | using System.ComponentModel.Composition; 7 | using System.ComponentModel.Design; 8 | using System.Diagnostics; 9 | using System.Globalization; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | using System.Windows.Forms; 13 | using Microsoft.VisualStudio.Text.Classification; 14 | using Microsoft.VisualStudio.ComponentModelHost; 15 | using Microsoft.VisualStudio.Editor; 16 | using Microsoft.VisualStudio; 17 | using Microsoft.VisualStudio.Shell; 18 | using Microsoft.VisualStudio.Shell.Interop; 19 | using Microsoft.VisualStudio.Text.Editor; 20 | using Microsoft.VisualStudio.Text.Operations; 21 | using Microsoft.VisualStudio.TextManager.Interop; 22 | using Task = System.Threading.Tasks.Task; 23 | using Microsoft.VisualStudio.Text; 24 | using Microsoft.VisualStudio.Text.Formatting; 25 | using Microsoft.VisualStudio.Imaging; 26 | 27 | namespace PeasyMotion 28 | { 29 | /// 30 | /// Command handler 31 | /// 32 | [Export] 33 | internal sealed class PeasyMotionActivate 34 | { 35 | /// 36 | /// Command menu group (command set GUID). 37 | /// 38 | public static readonly Guid CommandSet = new Guid("921fde78-c60b-4458-af50-fbb52d4b6a63"); 39 | 40 | /// 41 | /// VS Package that provides this command, not null. 42 | /// 43 | private AsyncPackage pkg = null; 44 | 45 | private PeasyMotionEdAdornment adornmentMgr = null; 46 | 47 | private IVsTextManager textMgr = null; 48 | private IVsEditorAdaptersFactoryService editor = null; 49 | private OleMenuCommandService commandService = null; 50 | 51 | private InputListener inputListener = null; 52 | private string accumulatedKeyChars = null; 53 | 54 | private InputListener inputListenerUserQueryPhase = null; 55 | private string userQueryAccumulatedKeyChars = null; 56 | 57 | 58 | private const string VsVimSetDisabled = "VsVim.SetDisabled"; 59 | private const string VsVimSetEnabled = "VsVim.SetEnabled"; 60 | private static bool disableVsVimCmdAvailable = false; 61 | private static bool enableVsVimCmdAvailable = false; 62 | private CommandExecutorService cmdExec = null; 63 | public IVsUIShell iVsUiShell = null; 64 | public IVsUIShell4 iVsUiShell4 = null; 65 | 66 | private static string ViEmuEnableDisableCommand = "ViEmu.EnableDisableViEmu"; 67 | private static bool viEmuPluginPresent = false; 68 | 69 | private JumpMode activeJumpMode = JumpMode.InvalidMode; 70 | 71 | /// 72 | /// Initializes a new instance of the class. 73 | /// Adds our command handlers for menu (commands must exist in the command table file) 74 | /// 75 | private PeasyMotionActivate() 76 | { 77 | } 78 | 79 | private static System.Drawing.Color ConvertDTEColor(uint oleColor) 80 | { 81 | var sdColor = System.Drawing.ColorTranslator.FromOle((int)oleColor); 82 | return System.Drawing.Color.FromArgb(sdColor.A, sdColor.R, sdColor.G, sdColor.B); 83 | } 84 | 85 | public void Init() 86 | { 87 | { 88 | var A = new EditorHostFactory(); 89 | var B = A.CreateCompositionContainer(); 90 | IEditorFormatMapService efms = B.GetExportedValue(); 91 | if (!VsSettings.IsInitialized) { 92 | VsSettings.Initialize(this.pkg, efms); 93 | } 94 | } 95 | 96 | CreateMenu(); 97 | cmdExec = new CommandExecutorService() {}; 98 | disableVsVimCmdAvailable = cmdExec.IsCommandAvailable(VsVimSetDisabled); 99 | enableVsVimCmdAvailable = cmdExec.IsCommandAvailable(VsVimSetEnabled); 100 | viEmuPluginPresent = cmdExec.IsCommandAvailable(ViEmuEnableDisableCommand); 101 | iVsUiShell = Package.GetGlobalService(typeof(SVsUIShell)) as IVsUIShell; 102 | iVsUiShell4 = iVsUiShell as IVsUIShell4; 103 | JumpLabelUserControl.WarmupCache(); 104 | // warmup options 105 | GeneralOptions.Instance.caretPositionSensivity = GeneralOptions.Instance.caretPositionSensivity; 106 | // warmp up: 107 | #if MEASUREEXECTIME 108 | var watch2_0 = System.Diagnostics.Stopwatch.StartNew(); 109 | #endif 110 | var wfs = iVsUiShell.GetDocumentWindowFrames().GetValueOrDefault(); 111 | if (wfs.Count > 0) { 112 | Trace.WriteLine("GetDocumentWindowFrames warmed up"); 113 | foreach(var wf in wfs) { 114 | wf.GetProperty((int)VsFramePropID.Caption, out var oce); 115 | wf.SetProperty((int)VsFramePropID.Caption, (string)oce); 116 | } 117 | } 118 | #if MEASUREEXECTIME 119 | watch2_0.Stop(); 120 | Trace.WriteLine($"PeasyMotion Adornment warmup document tabs: {watch2_0.ElapsedMilliseconds} ms"); 121 | #endif 122 | } 123 | 124 | private void CreateMenu() { 125 | // one day this copy-pasta will exceed one page of code... and then! and only than we gona do smth about that :} 126 | var wordJumpMenuCommandID = new CommandID(PeasyMotion.PackageGuids.guidPeasyMotionPackageCmdSet, 127 | PeasyMotion.PackageIds.PeasyMotionActivateId); 128 | var wordJumpMenuItem = new MenuCommand(this.ExecuteWordJump, wordJumpMenuCommandID); 129 | commandService.AddCommand(wordJumpMenuItem); 130 | 131 | var selectionWordJumpMenuCommandID = new CommandID(PeasyMotion.PackageGuids.guidPeasyMotionPackageCmdSet, 132 | PeasyMotion.PackageIds.PeasyMotionSelectTextActivateId); 133 | var selectionWordJumpMenuItem = new MenuCommand(this.ExecuteSelectTextWordJump, selectionWordJumpMenuCommandID); 134 | commandService.AddCommand(selectionWordJumpMenuItem); 135 | 136 | var lineJumpToWordBeginingMenuCommandID = new CommandID(PeasyMotion.PackageGuids.guidPeasyMotionPackageCmdSet, 137 | PeasyMotion.PackageIds.PeasyMotionLineJumpToWordBeginingId); 138 | var lineJumpToWordBeginingMenuItem = new MenuCommand(this.ExecuteLineJumpToWordBegining, lineJumpToWordBeginingMenuCommandID); 139 | commandService.AddCommand(lineJumpToWordBeginingMenuItem); 140 | 141 | var lineJumpToWordEndingMenuCommandID = new CommandID(PeasyMotion.PackageGuids.guidPeasyMotionPackageCmdSet, 142 | PeasyMotion.PackageIds.PeasyMotionLineJumpToWordEndingId); 143 | var lineJumpToWordEndingMenuItem = new MenuCommand(this.ExecuteLineJumpToWordEnding, lineJumpToWordEndingMenuCommandID); 144 | commandService.AddCommand(lineJumpToWordEndingMenuItem); 145 | 146 | var jumpToDocTabMenuCommandID = new CommandID(PeasyMotion.PackageGuids.guidPeasyMotionPackageCmdSet, 147 | PeasyMotion.PackageIds.PeasyMotionJumpToDocumentTab); 148 | var jumpToDocTabMenuItem = new MenuCommand(this.ExecuteJumpToDocTab, jumpToDocTabMenuCommandID); 149 | commandService.AddCommand(jumpToDocTabMenuItem); 150 | 151 | var jumpToLineBeginingMenuCommandID = new CommandID(PeasyMotion.PackageGuids.guidPeasyMotionPackageCmdSet, 152 | PeasyMotion.PackageIds.PeasyMotionJumpToLineBegining); 153 | var jumpToLineBeginingMenuItem = new MenuCommand(this.ExecuteJumpToLineBegining, jumpToLineBeginingMenuCommandID); 154 | commandService.AddCommand(jumpToLineBeginingMenuItem); 155 | 156 | var twoCharJumpMenuCommandID = new CommandID(PeasyMotion.PackageGuids.guidPeasyMotionPackageCmdSet, 157 | PeasyMotion.PackageIds.PeasyMotionTwoCharJump); 158 | var twoCharJumphMenuItem = new MenuCommand(this.ExecuteTwoCharJump, twoCharJumpMenuCommandID); 159 | commandService.AddCommand(twoCharJumphMenuItem); 160 | 161 | var oneCharJumpMenuCommandID = new CommandID(PeasyMotion.PackageGuids.guidPeasyMotionPackageCmdSet, 162 | PeasyMotion.PackageIds.PeasyMotionOneCharJump); 163 | var oneCharJumphMenuItem = new MenuCommand(this.ExecuteOneCharJump, oneCharJumpMenuCommandID); 164 | commandService.AddCommand(oneCharJumphMenuItem); 165 | } 166 | 167 | /// 168 | /// Gets the instance of the command. 169 | /// 170 | public static PeasyMotionActivate Instance 171 | { 172 | get; 173 | private set; 174 | } 175 | 176 | /// 177 | /// Gets the service provider from the owner package. 178 | /// 179 | private Microsoft.VisualStudio.Shell.IAsyncServiceProvider ServiceProvider 180 | { 181 | get 182 | { 183 | return this.pkg; 184 | } 185 | } 186 | 187 | public ITextSearchService textSearchService { get; set; } 188 | public IVsStatusbar statusBar { get; set; } 189 | 190 | public ITextStructureNavigatorSelectorService textStructureNavigatorSelector { get; set; } 191 | 192 | 193 | /// 194 | /// Initializes the singleton instance of the command. 195 | /// 196 | /// Owner package, not null. 197 | /// 198 | private static void ThrowAndLog(string msg) 199 | { 200 | Debug.Fail(msg); 201 | throw new Exception(msg); 202 | } 203 | 204 | public static async Task InitializeAsync(AsyncPackage package) 205 | { 206 | // Switch to the main thread - the call to AddCommand in PeasyMotionActivate's constructor requires 207 | // the UI thread + we gona check if VsVim commands are available 208 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken); 209 | 210 | OleMenuCommandService commandService_ = await package.GetServiceAsync(typeof(IMenuCommandService)).ConfigureAwait(true) as OleMenuCommandService; 211 | 212 | IVsTextManager textManager = await package.GetServiceAsync(typeof(SVsTextManager), false).ConfigureAwait(true) as IVsTextManager; 213 | if (null == textManager) { 214 | ThrowAndLog(nameof(package) + ": failed to retrieve SVsTextManager"); 215 | } 216 | 217 | IComponentModel componentModel = await package.GetServiceAsync(typeof(SComponentModel)).ConfigureAwait(true) as IComponentModel; 218 | if (componentModel == null) 219 | { 220 | ThrowAndLog(nameof(package) + ": failed to retrieve SComponentModel"); 221 | } 222 | 223 | IVsEditorAdaptersFactoryService editor_ = componentModel.GetService(); 224 | if (editor_ == null) 225 | { 226 | ThrowAndLog(nameof(package) + ": failed to retrieve IVsEditorAdaptersFactoryService"); 227 | } 228 | 229 | ITextSearchService textSearchService_ = componentModel.GetService(); 230 | if (textSearchService_ == null) 231 | { 232 | ThrowAndLog(nameof(package) + ": failed to retrieve ITextSearchService"); 233 | } 234 | 235 | ITextStructureNavigatorSelectorService textStructureNavigatorSelector_ = componentModel.GetService(); 236 | if (textStructureNavigatorSelector_ == null) 237 | { 238 | ThrowAndLog(nameof(package) + ": failed to retrieve ITextStructureNavigatorSelectorService"); 239 | } 240 | 241 | 242 | IVsStatusbar statusBar = await package.GetServiceAsync(typeof(SVsStatusbar)).ConfigureAwait(true) as IVsStatusbar; 243 | 244 | InfoBarService.Initialize(package); 245 | 246 | Instance = new PeasyMotionActivate() 247 | { 248 | pkg = package, 249 | commandService = commandService_, 250 | textMgr = textManager, 251 | editor = editor_, 252 | textSearchService = textSearchService_, 253 | statusBar = statusBar, 254 | textStructureNavigatorSelector = textStructureNavigatorSelector_, 255 | }; 256 | 257 | Instance.Init(); 258 | } 259 | 260 | private void ExecuteWordJump(object o, EventArgs e) 261 | { 262 | activeJumpMode = JumpMode.WordJump; 263 | ExecuteCommonJumpCode(); 264 | } 265 | 266 | private void ExecuteSelectTextWordJump(object o, EventArgs e) 267 | { 268 | activeJumpMode = JumpMode.SelectTextJump; 269 | ExecuteCommonJumpCode(); 270 | } 271 | 272 | private void ExecuteLineJumpToWordBegining(object o, EventArgs e) 273 | { 274 | activeJumpMode = JumpMode.LineJumpToWordBegining; 275 | ExecuteCommonJumpCode(); 276 | } 277 | 278 | private void ExecuteLineJumpToWordEnding(object o, EventArgs e) 279 | { 280 | activeJumpMode = JumpMode.LineJumpToWordEnding; 281 | ExecuteCommonJumpCode(); 282 | } 283 | 284 | private void ExecuteJumpToDocTab(object o, EventArgs e) 285 | { 286 | activeJumpMode = JumpMode.VisibleDocuments; 287 | ExecuteCommonJumpCode(); 288 | } 289 | 290 | private void ExecuteJumpToLineBegining(object o, EventArgs e) 291 | { 292 | activeJumpMode = JumpMode.LineBeginingJump; 293 | ExecuteCommonJumpCode(); 294 | } 295 | 296 | private void ExecuteTwoCharJump(object o, EventArgs e) 297 | { 298 | ShowNotificationsIfAny(); 299 | DeactivateIfActive(); 300 | activeJumpMode = JumpMode.TwoCharJump; 301 | 302 | textMgr.GetActiveView(1, null, out IVsTextView vsTextView); 303 | if (vsTextView == null) { Debug.Fail("MenuItemCallback: could not retrieve current view"); return; } 304 | 305 | IWpfTextView wpfTextView = editor.GetWpfTextView(vsTextView); 306 | if (wpfTextView == null) { Debug.Fail("failed to retrieve current view"); return; } 307 | 308 | #if MEASUREEXECTIME 309 | var watch2 = System.Diagnostics.Stopwatch.StartNew(); 310 | #endif 311 | TryDisableVsVim(); 312 | #if MEASUREEXECTIME 313 | watch2.Stop(); 314 | Trace.WriteLine($"PeasyMotion ExecuteTwoCharJump - TryDisableVsVim: {watch2.ElapsedMilliseconds} ms"); 315 | #endif 316 | 317 | #if MEASUREEXECTIME 318 | var watch7 = System.Diagnostics.Stopwatch.StartNew(); 319 | #endif 320 | TryDisableViEmu(); 321 | #if MEASUREEXECTIME 322 | watch7.Stop(); 323 | Trace.WriteLine($"PeasyMotion ExecuteTwoCharJump - TryDisableViEmu: {watch7.ElapsedMilliseconds} ms"); 324 | #endif 325 | 326 | setStatusBarText("Two character jump activated. Waiting for two keys to execute search >"); 327 | 328 | ThreadHelper.ThrowIfNotOnUIThread(); 329 | CreateInputListenerUserQueryPhase(vsTextView, wpfTextView); 330 | wpfTextView.LostAggregateFocus += OnTextViewFocusLost; 331 | } 332 | 333 | private void ExecuteOneCharJump(object o, EventArgs e) 334 | { 335 | ShowNotificationsIfAny(); 336 | DeactivateIfActive(); 337 | activeJumpMode = JumpMode.OneCharJump; 338 | 339 | textMgr.GetActiveView(1, null, out IVsTextView vsTextView); 340 | if (vsTextView == null) { Debug.Fail("MenuItemCallback: could not retrieve current view"); return; } 341 | 342 | IWpfTextView wpfTextView = editor.GetWpfTextView(vsTextView); 343 | if (wpfTextView == null) { Debug.Fail("failed to retrieve current view"); return; } 344 | 345 | #if MEASUREEXECTIME 346 | var watch2 = System.Diagnostics.Stopwatch.StartNew(); 347 | #endif 348 | TryDisableVsVim(); 349 | #if MEASUREEXECTIME 350 | watch2.Stop(); 351 | Trace.WriteLine($"PeasyMotion ExecuteOneCharJump - TryDisableVsVim: {watch2.ElapsedMilliseconds} ms"); 352 | #endif 353 | 354 | #if MEASUREEXECTIME 355 | var watch7 = System.Diagnostics.Stopwatch.StartNew(); 356 | #endif 357 | TryDisableViEmu(); 358 | #if MEASUREEXECTIME 359 | watch7.Stop(); 360 | Trace.WriteLine($"PeasyMotion ExecuteOneCharJump - TryDisableViEmu: {watch7.ElapsedMilliseconds} ms"); 361 | #endif 362 | 363 | setStatusBarText("One character jump activated. Waiting for one key to execute search >"); 364 | 365 | ThreadHelper.ThrowIfNotOnUIThread(); 366 | CreateInputListenerUserQueryPhase(vsTextView, wpfTextView); 367 | wpfTextView.LostAggregateFocus += OnTextViewFocusLost; 368 | } 369 | 370 | private void unfreezeStatusBar() { 371 | int frozen; statusBar.IsFrozen(out frozen); 372 | if (frozen != 0) {// Make sure the status bar is not frozen 373 | statusBar.FreezeOutput(0); 374 | } 375 | } 376 | 377 | private void freezeStatusBar() { 378 | int frozen; statusBar.IsFrozen(out frozen); 379 | if (frozen == 0) {// Make sure the status bar is not frozen 380 | statusBar.FreezeOutput(1); 381 | } 382 | } 383 | private void setStatusBarText(string statusBarText) 384 | { 385 | unfreezeStatusBar(); 386 | statusBar.SetText($"| PeasyMotion |> {statusBarText}"); // Set the status bar text and make its display static. 387 | //statusBar.FreezeOutput(1); // Freeze the status bar. 388 | //string text; statusBar.GetText(out text); // Get the status bar text. 389 | //Debug.WriteLine($"Status bar text = {text}"); 390 | } 391 | 392 | 393 | private void ShowNotificationsIfAny() 394 | { 395 | var pkgVersion = System.Version.Parse(GeneralOptions.getCurrentVersion()); 396 | System.Version cfgPkgVersion; 397 | try { 398 | cfgPkgVersion = System.Version.Parse(GeneralOptions.Instance.getInstalledVersionStr()); 399 | } catch(Exception ex){ 400 | Trace.Write($"Failed to parse package version stored(if there was any) in options registry! Exception: {ex.ToString()}"); 401 | cfgPkgVersion = pkgVersion; //System.Version.Parse("0.0.0"); //TODO!!!! for release - change to =pkgVersion!! No notification is needed for 'whats new' 402 | } 403 | Debug.WriteLine($"cfgPkgVersion = {cfgPkgVersion} | pkgVersion = {pkgVersion}"); 404 | if (!InfoBarService.Instance.anyInfoBarActive() && (pkgVersion > cfgPkgVersion)) { 405 | InfoBarService.Instance.ShowInfoBar(new WhatsNewNotification(), 406 | new Action( () => { // in case info bar is closed propeprly, stop showing notification 407 | GeneralOptions.Instance.setInstalledVersionToCurrentPkgVersion(); 408 | GeneralOptions.Instance.Save(); 409 | }) 410 | ); 411 | } 412 | } 413 | 414 | private void ExecuteCommonJumpCode() 415 | { 416 | ShowNotificationsIfAny(); 417 | 418 | #if MEASUREEXECTIME 419 | var watch = System.Diagnostics.Stopwatch.StartNew(); 420 | #endif 421 | textMgr.GetActiveView(1, null, out IVsTextView vsTextView); 422 | if (vsTextView == null) { 423 | Debug.Fail("MenuItemCallback: could not retrieve current view"); 424 | return; 425 | } 426 | IWpfTextView wpfTextView = editor.GetWpfTextView(vsTextView); 427 | if (wpfTextView == null) { 428 | Debug.Fail("failed to retrieve current view"); 429 | return; 430 | } 431 | 432 | #if MEASUREEXECTIME 433 | var watch3 = System.Diagnostics.Stopwatch.StartNew(); 434 | #endif 435 | 436 | if (adornmentMgr != null) { 437 | Deactivate(); 438 | } 439 | 440 | #if MEASUREEXECTIME 441 | watch3.Stop(); 442 | Trace.WriteLine($"PeasyMotion Deactivate(): {watch3.ElapsedMilliseconds} ms"); 443 | #endif 444 | 445 | #if MEASUREEXECTIME 446 | var watch2 = System.Diagnostics.Stopwatch.StartNew(); 447 | #endif 448 | TryDisableVsVim(); 449 | #if MEASUREEXECTIME 450 | watch2.Stop(); 451 | Trace.WriteLine($"PeasyMotion TryDisableVsVim: {watch2.ElapsedMilliseconds} ms"); 452 | #endif 453 | 454 | #if MEASUREEXECTIME 455 | var watch7 = System.Diagnostics.Stopwatch.StartNew(); 456 | #endif 457 | TryDisableViEmu(); 458 | #if MEASUREEXECTIME 459 | watch7.Stop(); 460 | Trace.WriteLine($"PeasyMotion TryDisableViEmu: {watch7.ElapsedMilliseconds} ms"); 461 | #endif 462 | 463 | ITextStructureNavigator textStructNav = this.textStructureNavigatorSelector.GetTextStructureNavigator(wpfTextView.TextBuffer); 464 | 465 | var args = new PeasyMotionEdAdornmentCtorArgs{ 466 | vsTextView = vsTextView, 467 | wpfView = wpfTextView, 468 | textStructNav = textStructNav, 469 | jumpMode = activeJumpMode, 470 | nCharSearchJumpKeys = this.userQueryAccumulatedKeyChars?.ToLowerInvariant(), 471 | vimOrBulkyCaretPresent = viEmuPluginPresent || enableVsVimCmdAvailable 472 | }; 473 | adornmentMgr = new PeasyMotionEdAdornment(args); 474 | 475 | ThreadHelper.ThrowIfNotOnUIThread(); 476 | CreateInputListener(vsTextView, wpfTextView); 477 | wpfTextView.LostAggregateFocus += OnTextViewFocusLost; 478 | 479 | #if MEASUREEXECTIME 480 | watch.Stop(); 481 | Trace.WriteLine($"PeasyMotion FullExecTime: {watch.ElapsedMilliseconds} ms"); 482 | #endif 483 | 484 | if (!adornmentMgr.anyJumpsAvailable()) { // empty text? no jump labels 485 | Deactivate(); 486 | } 487 | } 488 | 489 | private void DeactivateIfActive() { 490 | if ((adornmentMgr != null) || (inputListenerUserQueryPhase != null)) { 491 | this.Deactivate(); 492 | } 493 | } 494 | 495 | private void OnTextViewFocusLost(object sender, EventArgs e) { 496 | DeactivateIfActive(); 497 | } 498 | 499 | private void TryDisableVsVim() 500 | { 501 | ThreadHelper.ThrowIfNotOnUIThread(); 502 | if (disableVsVimCmdAvailable) 503 | { 504 | cmdExec.Execute(VsVimSetDisabled); 505 | } 506 | } 507 | 508 | private void TryEnableVsVim() 509 | { 510 | ThreadHelper.ThrowIfNotOnUIThread(); 511 | if (enableVsVimCmdAvailable) 512 | { 513 | cmdExec.Execute(VsVimSetEnabled); 514 | } 515 | } 516 | 517 | private void TryToggleViEmu() { 518 | if (viEmuPluginPresent) { 519 | var watch = System.Diagnostics.Stopwatch.StartNew(); 520 | cmdExec.Execute(ViEmuEnableDisableCommand); 521 | watch.Stop(); 522 | Debug.WriteLine($"PeasyMotion ViEmuEnableDisableCommand exec took: {watch.ElapsedMilliseconds} ms"); 523 | } 524 | } 525 | private void TryDisableViEmu() => TryToggleViEmu(); 526 | 527 | private void TryEnableViEmu() => TryToggleViEmu(); 528 | 529 | private void CreateInputListener(IVsTextView view, IWpfTextView textView) 530 | { 531 | inputListener = new InputListener(view, textView) { }; 532 | inputListener.AddFilter(); 533 | inputListener.KeyPressed += InputListenerOnKeyPressed; 534 | accumulatedKeyChars = null; 535 | setStatusBarText($"Waiting for keys to execute jump >"); 536 | } 537 | 538 | private void CreateInputListenerUserQueryPhase(IVsTextView view, IWpfTextView textView) 539 | { 540 | inputListenerUserQueryPhase = new InputListener(view, textView) { }; 541 | inputListenerUserQueryPhase.AddFilter(); 542 | inputListenerUserQueryPhase.KeyPressed += InputListenerOnKeyPressedCharAccumulation; 543 | userQueryAccumulatedKeyChars = null; 544 | } 545 | 546 | // separate listener, used to accumulate keys for one & two char jump modes 547 | private void InputListenerOnKeyPressedCharAccumulation(object sender, KeyPressEventArgs keyPressEventArgs) 548 | { 549 | ThreadHelper.ThrowIfNotOnUIThread(); 550 | if (adornmentMgr != null) { 551 | Trace.WriteLine("PeasyMotion: InputListenerOnKeyPressedCharAccumulation - adornmentMgr is not null!!! LOGIC ERROR!!!"); 552 | } 553 | try { 554 | var ch = keyPressEventArgs.KeyChar; 555 | Debug.WriteLine("Key pressed " + ch); 556 | if (Char.IsSeparator(ch) || Char.IsPunctuation(ch) || Char.IsLetterOrDigit(ch)) 557 | { 558 | if (null == userQueryAccumulatedKeyChars) 559 | { 560 | userQueryAccumulatedKeyChars = new string(keyPressEventArgs.KeyChar, 1); 561 | } 562 | else 563 | { 564 | userQueryAccumulatedKeyChars += keyPressEventArgs.KeyChar; 565 | } 566 | setStatusBarText($"Keys pressed: {userQueryAccumulatedKeyChars}"); 567 | if ((activeJumpMode == JumpMode.TwoCharJump) || (activeJumpMode == JumpMode.OneCharJump)) 568 | { 569 | var numCharsToWaitFor = 0; 570 | if (activeJumpMode == JumpMode.TwoCharJump) 571 | { 572 | numCharsToWaitFor = 2; 573 | } 574 | else if (activeJumpMode == JumpMode.OneCharJump) 575 | { 576 | numCharsToWaitFor = 1; 577 | } 578 | 579 | if (userQueryAccumulatedKeyChars.Length == numCharsToWaitFor) { 580 | var wpfTextView = inputListenerUserQueryPhase.textView as IWpfTextView; 581 | if (null != wpfTextView) { 582 | wpfTextView.LostAggregateFocus -= OnTextViewFocusLost; 583 | } 584 | StopListening2Keyboard(); // kill user key query accumulator / listener 585 | ExecuteCommonJumpCode(); // start regular jumping code 586 | } 587 | } 588 | } else { 589 | Deactivate(); 590 | } 591 | } catch (ArgumentException ex) { // happens sometimes: "The supplied SnapshotPoint is on an incorrect snapshot." 592 | Trace.Write(ex.ToString()); 593 | System.Diagnostics.Debug.Write(ex.ToString()); 594 | } 595 | } 596 | 597 | private void InputListenerOnKeyPressed(object sender, KeyPressEventArgs keyPressEventArgs) 598 | { 599 | ThreadHelper.ThrowIfNotOnUIThread(); 600 | if (adornmentMgr == null) { 601 | //Trace.WriteLine("PeasyMotion: InputListenerOnKeyPressed - adornmentMgr is null!"); 602 | // Deactivate(); 603 | } 604 | try { 605 | //Debug.WriteLine("Key pressed " + keyPressEventArgs.KeyChar); 606 | setStatusBarText($"Keys pressed: {accumulatedKeyChars}"); 607 | if (adornmentMgr.jumpLabelKeyArray.IndexOf(keyPressEventArgs.KeyChar) != -1) 608 | { 609 | if (null == accumulatedKeyChars) 610 | { 611 | accumulatedKeyChars = new string(keyPressEventArgs.KeyChar, 1); 612 | } 613 | else 614 | { 615 | accumulatedKeyChars += keyPressEventArgs.KeyChar; 616 | } 617 | 618 | if (adornmentMgr.JumpTo(accumulatedKeyChars, out var jumpToResult)) // this was final jump char 619 | { 620 | var wpfTextView = adornmentMgr.view; 621 | var jumpMode = adornmentMgr.CurrentJumpMode; 622 | Deactivate(); 623 | if (jumpMode != JumpMode.VisibleDocuments) 624 | { 625 | //adornmentMgr.TraceLine("before labelSnapshotSpan"); 626 | var labelSnapshotSpan = new SnapshotSpan(wpfTextView.TextSnapshot, jumpToResult.jumpLabelSpan); 627 | 628 | switch (jumpMode) { 629 | case JumpMode.InvalidMode: 630 | Trace.WriteLine("PeasyMotion: OOOPS! JumpMode logic is broken!"); 631 | break; 632 | case JumpMode.WordJump: 633 | case JumpMode.LineJumpToWordBegining: 634 | case JumpMode.LineJumpToWordEnding: 635 | case JumpMode.LineBeginingJump: 636 | case JumpMode.TwoCharJump: 637 | case JumpMode.OneCharJump: 638 | { // move caret to label 639 | wpfTextView.Selection.Clear(); 640 | wpfTextView.Caret.MoveTo(labelSnapshotSpan.Start); 641 | } 642 | break; 643 | case JumpMode.SelectTextJump: 644 | { // select text, and move caret to selection end label 645 | int c = jumpToResult.currentCursorPosition; 646 | int s = jumpToResult.jumpLabelSpan.Start; 647 | int e = jumpToResult.jumpLabelSpan.End; 648 | var selectionSpan = c < s ? Span.FromBounds(c, s) : Span.FromBounds(s, c); 649 | // 1. select 650 | wpfTextView.Selection.Select(new SnapshotSpan(wpfTextView.TextSnapshot, selectionSpan), c > e); 651 | // 2. move 652 | wpfTextView.Caret.MoveTo(labelSnapshotSpan.Start); 653 | } 654 | break; 655 | } 656 | 657 | //adornmentMgr.TraceLine("after switch labelSnapshotSpan"); 658 | } 659 | else if (jumpMode == JumpMode.VisibleDocuments) { 660 | jumpToResult.windowFrame.Show(); 661 | } 662 | } else if (adornmentMgr.NoLabelsLeft()){ // in case wrong (not used in active labels) key was pressed and we ran out of labels 663 | Deactivate(); 664 | } 665 | } 666 | else 667 | { 668 | Deactivate(); 669 | } 670 | } catch (ArgumentException ex) { // happens sometimes: "The supplied SnapshotPoint is on an incorrect snapshot." 671 | Trace.Write(ex.ToString()); 672 | System.Diagnostics.Debug.Write(ex.ToString()); 673 | } 674 | } 675 | 676 | public void Deactivate() 677 | { 678 | ThreadHelper.ThrowIfNotOnUIThread(); 679 | if (adornmentMgr != null) { 680 | adornmentMgr.view.LostAggregateFocus -= OnTextViewFocusLost; 681 | } 682 | StopListening2Keyboard(); 683 | TryEnableVsVim(); 684 | TryEnableViEmu(); 685 | adornmentMgr?.Reset(); 686 | adornmentMgr = null; 687 | activeJumpMode = JumpMode.InvalidMode; 688 | statusBar.Clear(); 689 | } 690 | 691 | private void StopListening2Keyboard() 692 | { 693 | ThreadHelper.ThrowIfNotOnUIThread(); 694 | if (null != inputListener) { 695 | inputListener.KeyPressed -= InputListenerOnKeyPressed; 696 | inputListener.RemoveFilter(); 697 | inputListener = null; 698 | } 699 | if (null != inputListenerUserQueryPhase) { 700 | inputListenerUserQueryPhase.KeyPressed -= InputListenerOnKeyPressedCharAccumulation; 701 | inputListenerUserQueryPhase.RemoveFilter(); 702 | inputListenerUserQueryPhase = null; 703 | } 704 | } 705 | 706 | } 707 | } 708 | 709 | //sandbox: 710 | #if false 711 | for (int i = 0; i < 256; i++) { 712 | Debug.WriteLine("Char.IsSeparator(" + ((char)i) + " = " + Char.IsLowSurrogate((char)i)); 713 | Debug.WriteLine("Char.IsControl(" + ((char)i) + " = " + Char.IsControl((char)i)); 714 | Debug.WriteLine("Char.IsDigit(" + ((char)i) + " = " + Char.IsDigit((char)i)); 715 | Debug.WriteLine("Char.IsHighSurrogate(" + ((char)i) + " = " + Char.IsHighSurrogate((char)i)); 716 | Debug.WriteLine("Char.IsLetterOrDigit(" + ((char)i) + " = " + Char.IsLetterOrDigit((char)i)); 717 | Debug.WriteLine("Char.IsLowSurrogate(" + ((char)i) + " = " + Char.IsLowSurrogate((char)i)); 718 | Debug.WriteLine("Char.IsNumber(" + ((char)i) + " = " + Char.IsNumber((char)i)); 719 | Debug.WriteLine("Char.IsPunctuation(" + ((char)i) + " = " + Char.IsPunctuation((char)i)); 720 | Debug.WriteLine("Char.IsSeparator(" + ((char)i) + " = " + Char.IsSeparator((char)i)); 721 | Debug.WriteLine("Char.IsSymbol(" + ((char)i) + " = " + Char.IsSymbol((char)i)); 722 | Debug.WriteLine("-----"); 723 | } 724 | #endif 725 | 726 | //howtos: 727 | //var Site = PeasyMotionActivate.Instance.ServiceProvider; 728 | //var dte = Package.GetGlobalService(typeof(SDTE)) as EnvDTE80.DTE2; 729 | //wf.SetProperty((int)VsFramePropID.EditorCaption, null); 730 | //wf.SetProperty((int)VsFramePropID.OwnerCaption, newCaption); 731 | //wf.SetProperty((int)VsFramePropID.ShortCaption, newCaption); 732 | //if (VSConstants.S_OK == wf.GetProperty((int)VsFramePropID.OverrideCaption, out var c)) 733 | //wf.GetProperty((int)VsFramePropID.OverrideCaption, out var oovcap); 734 | //wf.GetProperty((int)VsFramePropID.EditorCaption, out var oecap); 735 | //wf.GetProperty((int)VsFramePropID.OwnerCaption, out var oocap); 736 | //string ecap = (string)oecap; 737 | //string ocap = (string)oocap; 738 | //string ovcap = (string)oovcap; 739 | //var _ = wf.IsOnScreen(out int isonscreen_int); // unreliable 740 | //var inOnScreen = isonscreen_int != 0; // unreliable 741 | //var isVisible = VSConstants.S_OK == wf.IsVisible(); // unreliable 742 | //int x = -9, y = -9, px = -9, py = -9; 743 | //wf.GetFramePos(null, out Guid _, out x, out y, out px, out py); 744 | //if (wf is IVsWindowFrame4 wf4) { wf4.GetWindowScreenRect(out x, out y, out px, out py); } 745 | //Trace.WriteLine($"WindowFrame[{wfi++}] Current={currentWindowFrame==wf} Caption={ce} OwnerCaption={ocap} "+ 746 | // $"EditorCaption={ecap} OverrideCaption={ovcap} "+ 747 | // $"IsOnScreen={inOnScreen} IsVisible={isVisible} {x} {y} {px} {py}"); --------------------------------------------------------------------------------