-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathProjBufferToolWindow.cs
More file actions
244 lines (211 loc) · 10.8 KB
/
ProjBufferToolWindow.cs
File metadata and controls
244 lines (211 loc) · 10.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
using System;
using System.Linq;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.TextManager.Interop;
using Microsoft.VisualStudio.Editor;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.ComponentModelHost;
using System.Windows.Forms;
namespace ProjectionBufferTutorial
{
[Guid("e12a35c8-4a60-400a-af8e-5a1e4ed6cf50")]
public class ProjBufferToolWindow : ToolWindowPane, IOleCommandTarget
{
//CHANGE ME TO A FILE THAT YOU HAVE OPENED IN VISUAL STUDIO WHEN LAUNCHING THE TOOL WINDOW
//CHANGE ME TO A FILE THAT YOU HAVE OPENED IN VISUAL STUDIO WHEN LAUNCHING THE TOOL WINDOW
private string filePath = @"C:\Users\Josh\Documents\Visual Studio 2013\Projects\ConsoleApplication1\ConsoleApplication1\Program.cs";
//CHANGE ME TO A FILE THAT YOU HAVE OPENED IN VISUAL STUDIO WHEN LAUNCHING THE TOOL WINDOW
//CHANGE ME TO A FILE THAT YOU HAVE OPENED IN VISUAL STUDIO WHEN LAUNCHING THE TOOL WINDOW
IComponentModel _componentModel;
IVsInvisibleEditorManager _invisibleEditorManager;
//This adapter allows us to convert between Visual Studio 2010 editor components and
//the legacy components from Visual Studio 2008 and earlier.
IVsEditorAdaptersFactoryService _editorAdapter;
ITextEditorFactoryService _editorFactoryService;
IVsTextView _currentlyFocusedTextView;
public ProjBufferToolWindow() : base(null)
{
this.Caption = "ProjBufferToolWindow";
this.BitmapResourceID = 301;
this.BitmapIndex = 1;
_componentModel = (IComponentModel)Microsoft.VisualStudio.Shell.Package.GetGlobalService(typeof(SComponentModel));
_invisibleEditorManager = (IVsInvisibleEditorManager)Microsoft.VisualStudio.Shell.Package.GetGlobalService(typeof(SVsInvisibleEditorManager));
_editorAdapter = _componentModel.GetService<IVsEditorAdaptersFactoryService>();
_editorFactoryService = _componentModel.GetService<ITextEditorFactoryService>();
}
/// <summary>
/// Creates an invisible editor for a given filePath.
/// If you're frequently creating projection buffers, it may be worth caching
/// these editors as they're somewhat expensive to create.
/// </summary>
private IVsInvisibleEditor GetInvisibleEditor(string filePath)
{
IVsInvisibleEditor invisibleEditor;
ErrorHandler.ThrowOnFailure(this._invisibleEditorManager.RegisterInvisibleEditor(
filePath
, pProject: null
, dwFlags: (uint)_EDITORREGFLAGS.RIEF_ENABLECACHING
, pFactory: null
, ppEditor: out invisibleEditor));
RegisterDocument(filePath);
return invisibleEditor;
}
uint RegisterDocument(string targetFile)
{
//Then when creating the IVsInvisibleEditor, find and lock the document
uint itemID;
IntPtr docData;
uint docCookie;
IVsHierarchy hierarchy;
var runningDocTable = (IVsRunningDocumentTable)Microsoft.VisualStudio.Shell.Package.GetGlobalService(typeof(SVsRunningDocumentTable));
ErrorHandler.ThrowOnFailure(runningDocTable.FindAndLockDocument(
dwRDTLockType: (uint) _VSRDTFLAGS.RDT_EditLock,
pszMkDocument: targetFile,
ppHier: out hierarchy,
pitemid: out itemID,
ppunkDocData: out docData,
pdwCookie: out docCookie));
return docCookie;
}
public IWpfTextViewHost CreateEditor(string filePath, int start = 0, int end = 0, bool createProjectedEditor = false)
{
//IVsInvisibleEditors are in-memory represenations of typical Visual Studio editors.
//Language services, highlighting and error squiggles are hooked up to these editors
//for us once we convert them to WpfTextViews.
var invisibleEditor = GetInvisibleEditor(filePath);
var docDataPointer = IntPtr.Zero;
Guid guidIVsTextLines = typeof(IVsTextLines).GUID;
ErrorHandler.ThrowOnFailure(invisibleEditor.GetDocData(
fEnsureWritable: 1
, riid: ref guidIVsTextLines
, ppDocData: out docDataPointer));
IVsTextLines docData = (IVsTextLines)Marshal.GetObjectForIUnknown(docDataPointer);
// This will actually be defined as _codewindowbehaviorflags2.CWB_DISABLEDIFF once the latest version of
// Microsoft.VisualStudio.TextManager.Interop.16.0.DesignTime is published. Setting the flag will have no effect
// on releases prior to d16.0.
const _codewindowbehaviorflags CWB_DISABLEDIFF = (_codewindowbehaviorflags)0x04;
//Create a code window adapter
var codeWindow = _editorAdapter.CreateVsCodeWindowAdapter(VisualStudioServices.OLEServiceProvider);
// You need to disable the dropdown, splitter and -- for d16.0 -- diff since you are extracting the code window's TextViewHost and using it.
((IVsCodeWindowEx)codeWindow).Initialize((uint)_codewindowbehaviorflags.CWB_DISABLESPLITTER | (uint)_codewindowbehaviorflags.CWB_DISABLEDROPDOWNBAR | (uint)CWB_DISABLEDIFF,
VSUSERCONTEXTATTRIBUTEUSAGE.VSUC_Usage_Filter,
string.Empty,
string.Empty,
0,
new INITVIEW[1]);
ErrorHandler.ThrowOnFailure(codeWindow.SetBuffer(docData));
//Get a text view for our editor which we will then use to get the WPF control for that editor.
IVsTextView textView;
ErrorHandler.ThrowOnFailure(codeWindow.GetPrimaryView(out textView));
if (createProjectedEditor)
{
//We add our own role to this text view. Later this will allow us to selectively modify
//this editor without getting in the way of Visual Studio's normal editors.
var roles = _editorFactoryService.DefaultRoles.Concat(new string[] { "CustomProjectionRole" });
var vsTextBuffer = docData as IVsTextBuffer;
var textBuffer = _editorAdapter.GetDataBuffer(vsTextBuffer);
textBuffer.Properties.AddProperty("StartPosition", start);
textBuffer.Properties.AddProperty("EndPosition", end);
var guid = VSConstants.VsTextBufferUserDataGuid.VsTextViewRoles_guid;
((IVsUserData)codeWindow).SetData(ref guid, _editorFactoryService.CreateTextViewRoleSet(roles).ToString());
}
_currentlyFocusedTextView = textView;
var textViewHost = _editorAdapter.GetWpfTextViewHost(textView);
return textViewHost;
}
private IWpfTextViewHost _completeTextViewHost;
public IWpfTextViewHost CompleteTextViewHost
{
get
{
if (_completeTextViewHost == null)
{
_completeTextViewHost = CreateEditor(filePath);
}
return _completeTextViewHost;
}
}
private IWpfTextViewHost _projectedTextViewHost;
public IWpfTextViewHost ProjectedTextViewHost
{
get
{
if (_projectedTextViewHost == null)
{
_projectedTextViewHost = CreateEditor(filePath, start: 0, end: 100, createProjectedEditor: true);
}
return _projectedTextViewHost;
}
}
private ProjBufferToolWindowControl _myControl;
public override object Content
{
get
{
if (_myControl == null)
{
_myControl = new ProjBufferToolWindowControl();
_myControl.fullFile.Content = CompleteTextViewHost.HostControl; // Don't rely on the IWpfTextViewHost implementation being a FrameworkElement
_myControl.partialFile.Content = ProjectedTextViewHost.HostControl;
}
return _myControl;
}
}
public override void OnToolWindowCreated()
{
//We need to set up the tool window to respond to key bindings
//They're passed to the tool window and its buffers via Query() and Exec()
var windowFrame = (IVsWindowFrame)Frame;
var cmdUi = Microsoft.VisualStudio.VSConstants.GUID_TextEditorFactory;
windowFrame.SetGuidProperty((int)__VSFPROPID.VSFPROPID_InheritKeyBindings, ref cmdUi);
base.OnToolWindowCreated();
}
protected override bool PreProcessMessage(ref Message m)
{
if (CompleteTextViewHost != null)
{
// copy the Message into a MSG[] array, so we can pass
// it along to the active core editor's IVsWindowPane.TranslateAccelerator
var pMsg = new MSG[1];
pMsg[0].hwnd = m.HWnd;
pMsg[0].message = (uint)m.Msg;
pMsg[0].wParam = m.WParam;
pMsg[0].lParam = m.LParam;
var vsWindowPane = (IVsWindowPane)_currentlyFocusedTextView;
return vsWindowPane.TranslateAccelerator(pMsg) == 0;
}
return base.PreProcessMessage(ref m);
}
int IOleCommandTarget.Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
{
var hr =
(int)Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_NOTSUPPORTED;
if (_currentlyFocusedTextView != null)
{
var cmdTarget = (IOleCommandTarget)_currentlyFocusedTextView;
hr = cmdTarget.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut);
}
return hr;
}
int IOleCommandTarget.QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText)
{
var hr =
(int)Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_NOTSUPPORTED;
if (_currentlyFocusedTextView != null)
{
var cmdTarget = (IOleCommandTarget)_currentlyFocusedTextView;
hr = cmdTarget.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText);
}
return hr;
}
}
}