# vs插件等号对齐 **Repository Path**: JJbox/JoinBoxAlignPlug ## Basic Information - **Project Name**: vs插件等号对齐 - **Description**: 用于vs保存时候进行等号对齐,阿惊的学习笔记,内容不一定正确. - **Primary Language**: C# - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 2 - **Created**: 2021-06-08 - **Last Updated**: 2023-04-08 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 本插件功能 1. 保存事件 - [x] 发送格式化 - [x] 删除行尾空格 - [x] 对齐等号 - [x] 对齐注释 2. 制作工具条 - [x] 启用或者禁用插件 3. 模仿vscode的 - [ ] alt+鼠标右键到行尾 4. 后缀补全 - [x] 利用\`if将本行代码加入到if(括号内),同理\`foreach也可以...其他就没做了 因为不知道vs的语义切割是怎么做的,看教程是用CodeElement,我又急迫的想做出来,所以用的字符串切割.甚至没有用一条正则.. 我是判断**大括号内连续**,连续条件是去掉注释和空行之后,括号上下行有=号. **枚举类型**豁免,全部为连续. 如果是 { int a=1; list.clear(); } 这样就不为连续.也可以给cpp用. # vs2019安装拓展程序开发环境 ![](/图片/1285775-20210603224739619-1294570872.png) ![](/图片/1.png) ![](/图片/2.png) # 创建VSIX项目 ![](/图片/1285775-20210603224912759-119432246.png) ![](/图片/1285775-20210603225131337-912485906.png) *Package.cs 文件是程序入口,相当于Main: (在VS2017+是继承自AsyncPackage,采用异步方式加载,2010中继承自Package,同步加载方式,AsyncPackage也继承自Package类) 继承自AsyncPackage的Package的核心类,由于是根据Visual Studio启动的外部插件,加载该插件时会实例化该类; ## 配置.vsixmanifest文件 1. 打开source.extension.vsixmanifest文件, 填写基本信息 ![](/图片/1285775-20210603225353513-457798831.png) 2. 选择Install Targets, 配置插件适用的VS版本 ![](/图片/1285775-20210603225434898-226010855.png) 配置插件适用的VS版本 > VS版本: > VS2019=>16.0 > VS2017=>15.0 > VS2015=>14.0 > VS2013=>12.0 部分摘录自 https://www.jianshu.com/p/ac33559cd8c0 # 新建Editor Viewport Adornment ![](/图片/1285775-20210603225510862-1372047957.png) 新建完成之后就可以进行F5调试了,打开了一个文档之后,你会发现右上角有个实验案例的小框框. 解释每个内容需要阅读此文: https://www.cnblogs.com/stg609/p/3711443.html 他主要讲解了,Editor几种工程的不同, 还有:IWpfTextViewCreationListener 接口,当出现文档窗口(写代码的窗口)时候就会激发它下面的TextViewCreated函数. 而其中修改的核心内容,背后是调用了vs的语言服务器的代码规则: ```c# // 在激活文档中获取选中的文本 var Selection = (TextSelection)_dte.ActiveDocument.Selection; // 获取代码块也就是大括号{} CodeFunction func = Selection.ActivePoint.CodeElement[vsCMElement.vsCMElementFunction] as CodeFunction; if (func != null) { if (true)//然后移动光标到大括号前后 { Selection.MoveToPoint(func.StartPoint); } else { Selection.MoveToPoint(func.EndPoint); } } ``` 阅读完之后,你应该知道操作Visual Studio是靠DTE或者DTE2(比较新) ```c# public TskComment(IWpfTextView view, DTE2 dte) //在包接口获取DTE对象之后,传入其他对象. ``` 而有写地方是异步处理的,因此有两种方式掌握: 同步方法 ```c# //成员处 [Import] internal SVsServiceProvider ServiceProvider = null; //函数内 var _dTE = ServiceProvider.GetService(typeof(DTE)) as DTE; ``` 异步方法 ```c# //函数内 //这样DTE获取的东西都不会有警告了 //切换到主线程异步 https://github.com/Microsoft/vs-threading/blob/main/doc/analyzers/VSTHRD010.md await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); var _dTE = await GetServiceAsync(typeof(DTE)) as DTE2;//初始化获取DTE对象,是用AsyncPackage拿的.异步 // package获取 // _dTE = await package.GetServiceAsync(typeof(DTE)) as DTE2; // 命令内获取dte方法.异步 ``` ## 新建命令 ![](/图片/1285775-20210603234544907-720201955.png) 文件名是 Command1.cs ## 通过新建命令来添加.vsct失败 JoinBoxAlignPlugPackage.cs内,如果有这句的话,就会发生这样的事情: ```c# [ProvideMenuResource("Menus.ctmenu", 1)] ``` 而添加过一次命令的话,这句会自动加入到此处,是vs用来防止多次覆盖.vsct用的. ## 设置快捷键1_组合键 因为文件名的原因,它的命令按钮id自动设置为 Command1Id, 它很重要 ,因为快捷键绑定需要指定. 打开.vsct文件 ```xml ..省略其他项.. ``` 然后就可以进入调试看看效果了... ![](/图片/1285775-20210603225547066-1794223225.png) 微软参考 https://docs.microsoft.com/zh-cn/visualstudio/extensibility/binding-keyboard-shortcuts-to-menu-items?view=vs-2019 其他参考 https://www.cnblogs.com/forevertime/p/9358994.html ## 设置快捷键2_拦截自带的ctrl+s 上面的方法并不适用在冲突的快捷键上面使用, 所以我的想法是在TextViewCreated函数中挂一个**钩子拦截**ctrl+s. ```c# internal sealed class TskCommentTextViewCreationListener : IWpfTextViewCreationListener //文档窗口侦听器 { //省略展示中间部分 public void TextViewCreated(IWpfTextView textView) { ThreadHelper.ThrowIfNotOnUIThread(); _dTE = ServiceProvider.GetService(typeof(DTE)) as DTE2; //<-- 获取DTE对象 // 因为每次打开文档就会执行 TextViewCreated 函数,所以需要设置成单例,用钩子拦截 if (_keyboardHook == null) { _keyboardHook = new KeyboardHook(); _keyboardHook.KeyDownEvent += KeyboardHook_KeyDownEvent; } new TskComment(textView, _dTE); //<-- 把dte传给 view } } ``` 不过此方法会拦截并触发系统所有的ctrl+s,若通过当前鼠标获取句柄Win32API.GetForegroundWindow,则由于vs是WPF做的,难以判断文档窗口句柄, 尤其是文档窗口被拖拽出去之后,它会新建一个窗口出现,并且不是vsDTE2.Application.MainWindow的子窗口. 因此需要利用**文档保存事件**. ## 文档保存事件 参考 https://stackoverflow.com/questions/41620241/can-i-detect-document-saved-not-changed-with-visual-studio-workspace-in-a-vsix ```c# internal sealed class EditorViewTextViewCreationListener : IWpfTextViewCreationListener { ///////省略中间部分 [Import] internal SVsServiceProvider vsService = null; //<-- 通过这句代码,同步获取 Visual Studio 的产物 public static DTE2 vsDTE2 { get; set; } public Events2 _events2 { get; private set; } public DocumentEvents _documentEvents { get; private set; } public void TextViewCreated(IWpfTextView textView) { ThreadHelper.ThrowIfNotOnUIThread(); vsDTE2 = vsService.GetService(typeof(DTE)) as DTE2; //<-- 获取DTE对象 //文档保存的事件: //成员不能移除,否则GC回收 this._events2 = (Events2)vsDTE2.Events; this._documentEvents = this._events2.DocumentEvents; this._documentEvents.DocumentSaved += DocumentEvents_DocumentSaved; } bool _Save = true; private void DocumentEvents_DocumentSaved(Document Document) { ThreadHelper.ThrowIfNotOnUIThread(); // 防止重复触发保存事件 if (!_Save) return; JoinBoxCommand.CmdExecute(vsDTE2, Document); if (_Save && !Document.Saved) { _Save = false; Document.Save();//那这个时候怎么保存呢? _Save = true; } } } ``` # DTE使用 ## 格式化代码 方法1:编辑器 ```c# _dTE.ExecuteCommand("Edit.FormatDocument");// 发送命令:格式化代码,绕开选择文本造成滚动条改变(好用) ``` 方法2:编辑器 ```c# var doc = _dTE?.ActiveDocument; var selection = (TextSelection)doc.Selection; // 获取当前文档所有文本 selection.SelectAll();//选择会跳动滚动条 selection.SmartFormat();//格式化 ``` 方法3:缓冲区 ```c# var doc = _dTE?.ActiveDocument; var objTD = (TextDocument)doc.Object(); objTD.Selection.SelectAll();//选择会跳动滚动条 objTD.Selection.SmartFormat();// 格式化 ``` ## 获取编辑器文本 获取当前文档所有文本,但是要注意此方法会导致上下滚动条移动..不想的话要用 [CreateEditPoint](#CreateEditPoint优) ```c# var doc = _dTE?.ActiveDocument; var selection = (TextSelection)doc.Selection;// 文本选择器 selection.SelectAll(); var str1 = selection.Text; ``` ## CreateEditPoint优 微软帮助 https://docs.microsoft.com/zh-cn/dotnet/api/envdte.editpoint?view=visualstudiosdk-2019#properties ```c# var doc = _dTE?.ActiveDocument; var objTD = (TextDocument)doc.Object(); var objEP = objTD.StartPoint.CreateEditPoint(); var str1 = objEP.GetText(objTD.EndPoint);//获取所有文本 string contents = ChangTextContents(str1, out int row);//修改文本 objEP.ReplaceText(objTD.EndPoint, contents, (int)vsEPReplaceTextOptions.vsEPReplaceTextKeepMarkers);//编辑点替换方式 ``` ## 替换文本多种方法 ```c# objEP.ReplaceText(objTD.EndPoint, contents, (int)vsEPReplaceTextOptions.vsEPReplaceTextKeepMarkers);//编辑点替换方式 //doc.ReplaceText(str1, contents);//可 //selection2.ReplaceText(str1, contents);//可 //selection.ReplacePattern(str1, contents);//可 //selection.DestructiveInsert(contents);//单纯插入,不可 //以下不好 //selection.Text = contents;//这样会卡死 //selection.Insert(contents, 4);//设置了会选中所有文本.. ``` ## 移动光标 ```c# var doc = _dTE?.ActiveDocument; var selection = (TextSelection)doc.Selection;// 文本选择器 selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstColumn);//移动到行头 selection.EndOfLine();//移动到行尾 selection.OutlineSection();//上一行 selection.LineDown();//设置光标移动下一行 //记录行和列 var lStartLine = selection.ActivePoint.Line; var lStartColumn = selection.ActivePoint.LineCharOffset; selection.MoveToLineAndOffset(lStartLine, lStartColumn);//移动到指定行和列 ``` # 界面 ## 菜单栏 在.vsct文件中是利用guid="guidSHLMainMenu"此方式加入vs的菜单中... ```xml 省略后面 ``` 而加入到什么地方,需要看id=xxx,[VisualStudio菜单的GUID和ID](https://docs.microsoft.com/en-us/visualstudio/extensibility/internals/guids-and-ids-of-visual-studio-menus) ## 工具栏(条) 工具栏的定义方式和菜单栏类似,就是type类型不同 参考链接: [添加工具栏 - Visual Studio | Microsoft Docs](https://docs.microsoft.com/zh-cn/visualstudio/extensibility/adding-a-toolbar?view=vs-2019) 1. 在符号集Symbols中添加节点,添加001为我添加的部分 ```xml 注意value的冲突? ``` 2. 在菜单集Menus节点添加menu,类型为toolbar, ```xml DefaultDocked 惊惊盒子工具栏 惊惊盒子工具栏 ``` 3. 在组集Groups节点中添加, ```xml ``` 4. 在按钮集Buttons节点中添加 ```xml ``` 5. 调试启动,在工具栏行右键鼠标,点击它显示出来. ![aa](/图片/aa.png) 6. 共享多个命令,修改组名称就可以了 ![bb](/图片/bb.png) ## 解决方案管理器 ![img](/图片/cc.png) [动态添加菜单项](https://docs.microsoft.com/zh-cn/visualstudio/extensibility/dynamically-adding-menu-items?view=vs-2019) # 卸载 调试期间的vs是一个实验vs,也是当成插件安装了, 需要在vs调试的时候到插件管理器去卸载. # 30个学习案例 [VisualStudioSDK-处理文件保存事件](https://stackoverflow.com/questions/9844900/visual-studio-sdk-handle-file-save-event) 注意,您需要保留对`Events`和`DocumentEvents`才能真正发挥作用。以下是有关这方面的一些信息:[Http://social.msdn.microsoft.com/Forums/br/vsx/thread/0857a868-e650-42ed-b9cc-2975dc46e994](http://social.msdn.microsoft.com/Forums/br/vsx/thread/0857a868-e650-42ed-b9cc-2975dc46e994) 下面是一个指向30个示例项目的链接,这些项目演示了VisualStudio外接程序的各种功能: [Http://code.msdn.microsoft.com/Visual-Studio-2010-SDK-ddfe1372](http://code.msdn.microsoft.com/Visual-Studio-2010-SDK-ddfe1372) 你可以在这里找到一些开始的信息:[微软连接](http://msdn.microsoft.com/en-us/vstudio/ff677564.aspx)