2 #include <comdef.h> // For _bstr_t
10 static char THIS_FILE
[] = __FILE__
;
15 // Change directory before opening file?
16 #define CD_SOURCE 0 // Cd to source path
17 #define CD_SOURCE_PARENT 1 // Cd to parent directory of source path
18 #define CD_NONE 2 // No cd
21 static BOOL g_bEnableVim
= TRUE
; // Vim enabled
22 static BOOL g_bDevStudioEditor
= FALSE
; // Open file in Dev Studio editor simultaneously
23 static int g_ChangeDir
= CD_NONE
; // CD after file open?
25 static void VimSetEnableState (BOOL bEnableState
);
26 static BOOL
VimOpenFile (BSTR
& FileName
, long LineNr
);
27 static DISPID
VimGetDispatchId (COleAutomationControl
& VimOle
, char* Method
);
28 static void VimErrDiag (COleAutomationControl
& VimOle
);
29 static void VimChangeDir (COleAutomationControl
& VimOle
, DISPID DispatchId
, BSTR
& FileName
);
30 static void DebugMsg (char* Msg
, char* Arg
= NULL
);
33 /////////////////////////////////////////////////////////////////////////////
36 CCommands::CCommands ()
38 // m_pApplication == NULL; M$ Code generation bug!!!
39 m_pApplication
= NULL
;
40 m_pApplicationEventsObj
= NULL
;
41 m_pDebuggerEventsObj
= NULL
;
44 CCommands::~CCommands ()
46 ASSERT (m_pApplication
!= NULL
);
49 m_pApplication
->Release ();
50 m_pApplication
= NULL
;
54 void CCommands::SetApplicationObject (IApplication
* pApplication
)
56 // This function assumes pApplication has already been AddRef'd
57 // for us, which CDSAddIn did in it's QueryInterface call
58 // just before it called us.
59 m_pApplication
= pApplication
;
63 // Create Application event handlers
64 XApplicationEventsObj::CreateInstance (&m_pApplicationEventsObj
);
65 if (! m_pApplicationEventsObj
)
67 ReportInternalError ("XApplicationEventsObj::CreateInstance");
70 m_pApplicationEventsObj
->AddRef ();
71 m_pApplicationEventsObj
->Connect (m_pApplication
);
72 m_pApplicationEventsObj
->m_pCommands
= this;
75 // Create Debugger event handler
76 CComPtr
< IDispatch
> pDebugger
;
77 if (SUCCEEDED (m_pApplication
->get_Debugger (&pDebugger
))
80 XDebuggerEventsObj::CreateInstance (&m_pDebuggerEventsObj
);
81 m_pDebuggerEventsObj
->AddRef ();
82 m_pDebuggerEventsObj
->Connect (pDebugger
);
83 m_pDebuggerEventsObj
->m_pCommands
= this;
87 // Get settings from registry HKEY_CURRENT_USER\Software\Vim\VisVim
88 HKEY hAppKey
= GetAppKey ("Vim");
91 HKEY hSectionKey
= GetSectionKey (hAppKey
, "VisVim");
94 g_bEnableVim
= GetRegistryInt (hSectionKey
, "EnableVim",
96 g_bDevStudioEditor
= GetRegistryInt(hSectionKey
,"DevStudioEditor",
98 g_ChangeDir
= GetRegistryInt (hSectionKey
, "ChangeDir",
100 RegCloseKey (hSectionKey
);
102 RegCloseKey (hAppKey
);
106 void CCommands::UnadviseFromEvents ()
108 ASSERT (m_pApplicationEventsObj
!= NULL
);
109 if (m_pApplicationEventsObj
)
111 m_pApplicationEventsObj
->Disconnect (m_pApplication
);
112 m_pApplicationEventsObj
->Release ();
113 m_pApplicationEventsObj
= NULL
;
117 if (m_pDebuggerEventsObj
)
119 // Since we were able to connect to the Debugger events, we
120 // should be able to access the Debugger object again to
121 // unadvise from its events (thus the VERIFY_OK below--see
123 CComPtr
< IDispatch
> pDebugger
;
124 VERIFY_OK (m_pApplication
->get_Debugger (&pDebugger
));
125 ASSERT (pDebugger
!= NULL
);
126 m_pDebuggerEventsObj
->Disconnect (pDebugger
);
127 m_pDebuggerEventsObj
->Release ();
128 m_pDebuggerEventsObj
= NULL
;
134 /////////////////////////////////////////////////////////////////////////////
137 // Application events
139 HRESULT
CCommands::XApplicationEvents::BeforeBuildStart ()
141 AFX_MANAGE_STATE (AfxGetStaticModuleState ());
145 HRESULT
CCommands::XApplicationEvents::BuildFinish (long nNumErrors
, long nNumWarnings
)
147 AFX_MANAGE_STATE (AfxGetStaticModuleState ());
151 HRESULT
CCommands::XApplicationEvents::BeforeApplicationShutDown ()
153 AFX_MANAGE_STATE (AfxGetStaticModuleState ());
157 // The open document event handle is the place where the real interface work
159 // Vim gets called from here.
161 HRESULT
CCommands::XApplicationEvents::DocumentOpen (IDispatch
* theDocument
)
163 AFX_MANAGE_STATE (AfxGetStaticModuleState ());
166 // Vim not enabled or empty command line entered
169 // First get the current file name and line number
171 // Get the document object
172 CComQIPtr
< ITextDocument
, &IID_ITextDocument
> pDoc (theDocument
);
179 // Get the document name
180 if (FAILED (pDoc
->get_FullName (&FileName
)))
185 // Get a selection object dispatch pointer
186 if (SUCCEEDED (pDoc
->get_Selection (&pDispSel
)))
188 // Get the selection object
189 CComQIPtr
< ITextSelection
, &IID_ITextSelection
> pSel (pDispSel
);
192 // Get the selection line number
193 pSel
->get_CurrentLine (&LineNr
);
195 pDispSel
->Release ();
198 // Open the file in Vim and position to the current line
199 if (VimOpenFile (FileName
, LineNr
))
201 if (! g_bDevStudioEditor
)
203 // Close the document in developer studio
204 CComVariant vSaveChanges
= dsSaveChangesPrompt
;
207 pDoc
->Close (vSaveChanges
, &Saved
);
212 SysFreeString (FileName
);
216 HRESULT
CCommands::XApplicationEvents::BeforeDocumentClose (IDispatch
* theDocument
)
218 AFX_MANAGE_STATE (AfxGetStaticModuleState ());
222 HRESULT
CCommands::XApplicationEvents::DocumentSave (IDispatch
* theDocument
)
224 AFX_MANAGE_STATE (AfxGetStaticModuleState ());
228 HRESULT
CCommands::XApplicationEvents::NewDocument (IDispatch
* theDocument
)
230 AFX_MANAGE_STATE (AfxGetStaticModuleState ());
233 // Vim not enabled or empty command line entered
236 // First get the current file name and line number
238 CComQIPtr
< ITextDocument
, &IID_ITextDocument
> pDoc (theDocument
);
245 hr
= pDoc
->get_FullName (&FileName
);
249 // Open the file in Vim and position to the current line
250 if (VimOpenFile (FileName
, 0))
252 if (! g_bDevStudioEditor
)
254 // Close the document in developer studio
255 CComVariant vSaveChanges
= dsSaveChangesPrompt
;
258 pDoc
->Close (vSaveChanges
, &Saved
);
262 SysFreeString (FileName
);
266 HRESULT
CCommands::XApplicationEvents::WindowActivate (IDispatch
* theWindow
)
268 AFX_MANAGE_STATE (AfxGetStaticModuleState ());
272 HRESULT
CCommands::XApplicationEvents::WindowDeactivate (IDispatch
* theWindow
)
274 AFX_MANAGE_STATE (AfxGetStaticModuleState ());
278 HRESULT
CCommands::XApplicationEvents::WorkspaceOpen ()
280 AFX_MANAGE_STATE (AfxGetStaticModuleState ());
284 HRESULT
CCommands::XApplicationEvents::WorkspaceClose ()
286 AFX_MANAGE_STATE (AfxGetStaticModuleState ());
290 HRESULT
CCommands::XApplicationEvents::NewWorkspace ()
292 AFX_MANAGE_STATE (AfxGetStaticModuleState ());
298 HRESULT
CCommands::XDebuggerEvents::BreakpointHit (IDispatch
* pBreakpoint
)
300 AFX_MANAGE_STATE (AfxGetStaticModuleState ());
305 /////////////////////////////////////////////////////////////////////////////
308 class CMainDialog
: public CDialog
311 CMainDialog (CWnd
* pParent
= NULL
); // Standard constructor
313 //{{AFX_DATA(CMainDialog)
314 enum { IDD
= IDD_ADDINMAIN
};
316 BOOL m_bDevStudioEditor
;
319 //{{AFX_VIRTUAL(CMainDialog)
321 virtual void DoDataExchange (CDataExchange
* pDX
); // DDX/DDV support
325 //{{AFX_MSG(CMainDialog)
326 afx_msg
void OnEnable();
327 afx_msg
void OnDisable();
329 DECLARE_MESSAGE_MAP ()
332 CMainDialog::CMainDialog (CWnd
* pParent
/* =NULL */ )
333 : CDialog (CMainDialog::IDD
, pParent
)
335 //{{AFX_DATA_INIT(CMainDialog)
337 m_bDevStudioEditor
= FALSE
;
341 void CMainDialog::DoDataExchange (CDataExchange
* pDX
)
343 CDialog::DoDataExchange (pDX
);
344 //{{AFX_DATA_MAP(CMainDialog)
345 DDX_Radio(pDX
, IDC_CD_SOURCE_PATH
, m_ChangeDir
);
346 DDX_Check (pDX
, IDC_DEVSTUDIO_EDITOR
, m_bDevStudioEditor
);
350 BEGIN_MESSAGE_MAP (CMainDialog
, CDialog
)
351 //{{AFX_MSG_MAP(CMainDialog)
356 /////////////////////////////////////////////////////////////////////////////
359 STDMETHODIMP
CCommands::VisVimDialog ()
361 AFX_MANAGE_STATE (AfxGetStaticModuleState ());
363 // Use m_pApplication to access the Developer Studio Application
365 // and VERIFY_OK to see error strings in DEBUG builds of your add-in
368 VERIFY_OK (m_pApplication
->EnableModeless (VARIANT_FALSE
));
372 Dlg
.m_bDevStudioEditor
= g_bDevStudioEditor
;
373 Dlg
.m_ChangeDir
= g_ChangeDir
;
374 if (Dlg
.DoModal () == IDOK
)
376 g_bDevStudioEditor
= Dlg
.m_bDevStudioEditor
;
377 g_ChangeDir
= Dlg
.m_ChangeDir
;
379 // Save settings to registry HKEY_CURRENT_USER\Software\Vim\VisVim
380 HKEY hAppKey
= GetAppKey ("Vim");
383 HKEY hSectionKey
= GetSectionKey (hAppKey
, "VisVim");
386 WriteRegistryInt (hSectionKey
, "DevStudioEditor",
388 WriteRegistryInt (hSectionKey
, "ChangeDir", g_ChangeDir
);
389 RegCloseKey (hSectionKey
);
391 RegCloseKey (hAppKey
);
395 VERIFY_OK (m_pApplication
->EnableModeless (VARIANT_TRUE
));
399 STDMETHODIMP
CCommands::VisVimEnable ()
401 AFX_MANAGE_STATE (AfxGetStaticModuleState ());
402 VimSetEnableState (true);
406 STDMETHODIMP
CCommands::VisVimDisable ()
408 AFX_MANAGE_STATE (AfxGetStaticModuleState ());
409 VimSetEnableState (false);
413 STDMETHODIMP
CCommands::VisVimToggle ()
415 AFX_MANAGE_STATE (AfxGetStaticModuleState ());
416 VimSetEnableState (! g_bEnableVim
);
420 STDMETHODIMP
CCommands::VisVimLoad ()
422 AFX_MANAGE_STATE (AfxGetStaticModuleState ());
424 // Use m_pApplication to access the Developer Studio Application object,
425 // and VERIFY_OK to see error strings in DEBUG builds of your add-in
429 // Define dispatch pointers for document and selection objects
430 CComPtr
< IDispatch
> pDispDoc
, pDispSel
;
432 // Get a document object dispatch pointer
433 VERIFY_OK (m_pApplication
->get_ActiveDocument (&pDispDoc
));
440 // Get the document object
441 CComQIPtr
< ITextDocument
, &IID_ITextDocument
> pDoc (pDispDoc
);
446 // Get the document name
447 if (FAILED (pDoc
->get_FullName (&FileName
)))
450 // Get a selection object dispatch pointer
451 if (SUCCEEDED (pDoc
->get_Selection (&pDispSel
)))
453 // Get the selection object
454 CComQIPtr
< ITextSelection
, &IID_ITextSelection
> pSel (pDispSel
);
457 // Get the selection line number
458 pSel
->get_CurrentLine (&LineNr
);
461 // Open the file in Vim
462 VimOpenFile (FileName
, LineNr
);
464 SysFreeString (FileName
);
470 // Here we do the actual processing and communication with Vim
473 // Set the enable state and save to registry
475 static void VimSetEnableState (BOOL bEnableState
)
477 g_bEnableVim
= bEnableState
;
478 HKEY hAppKey
= GetAppKey ("Vim");
481 HKEY hSectionKey
= GetSectionKey (hAppKey
, "VisVim");
483 WriteRegistryInt (hSectionKey
, "EnableVim", g_bEnableVim
);
484 RegCloseKey (hAppKey
);
488 // Open the file 'FileName' in Vim and goto line 'LineNr'
489 // 'FileName' is expected to contain an absolute DOS path including the drive
491 // 'LineNr' must contain a valid line number or 0, e. g. for a new file
493 static BOOL
VimOpenFile (BSTR
& FileName
, long LineNr
)
496 // OLE automation object for com. with Vim
497 // When the object goes out of scope, it's destructor destroys the OLE
499 // This is important to avoid blocking the object
500 // (in this memory corruption would be likely when terminating Vim
501 // while still running DevStudio).
502 // So keep this object local!
503 COleAutomationControl VimOle
;
505 // :cd D:/Src2/VisVim/
507 // Get a dispatch id for the SendKeys method of Vim;
508 // enables connection to Vim if necessary
510 DispatchId
= VimGetDispatchId (VimOle
, "SendKeys");
512 // OLE error, can't obtain dispatch id
515 OLECHAR Buf
[MAX_OLE_STR
];
516 char FileNameTmp
[MAX_OLE_STR
];
517 char VimCmd
[MAX_OLE_STR
];
520 // Prepend CTRL-\ CTRL-N to exit insert mode
526 // Update the current file in Vim if it has been modified.
527 // Disabled, because it could write the file when you don't want to.
528 sprintf (VimCmd
+ 2, ":up\n");
530 if (! VimOle
.Method (DispatchId
, "s", TO_OLE_STR_BUF (VimCmd
, Buf
)))
533 // Change Vim working directory to where the file is if desired
534 if (g_ChangeDir
!= CD_NONE
)
535 VimChangeDir (VimOle
, DispatchId
, FileName
);
537 // Make Vim open the file.
538 // In the filename convert all \ to /, put a \ before a space.
539 sprintf(VimCmd
, ":drop ");
540 sprintf(FileNameTmp
, "%S", (char *)FileName
);
542 for (p
= FileNameTmp
; *p
!= '\0' && s
< FileNameTmp
+ MAX_OLE_STR
- 4;
555 if (! VimOle
.Method (DispatchId
, "s", TO_OLE_STR_BUF (VimCmd
, Buf
)))
561 sprintf (VimCmd
, ":%d\n", LineNr
);
562 if (! VimOle
.Method (DispatchId
, "s", TO_OLE_STR_BUF (VimCmd
, Buf
)))
566 // Make Vim come to the foreground
567 if (! VimOle
.Method ("SetForeground"))
574 // There was an OLE error
575 // Check if it's the "unknown class string" error
580 // Return the dispatch id for the Vim method 'Method'
581 // Create the Vim OLE object if necessary
582 // Returns a valid dispatch id or null on error
584 static DISPID
VimGetDispatchId (COleAutomationControl
& VimOle
, char* Method
)
586 // Initialize Vim OLE connection if not already done
587 if (! VimOle
.IsCreated ())
589 if (! VimOle
.CreateObject ("Vim.Application"))
593 // Get the dispatch id for the SendKeys method.
594 // By doing this, we are checking if Vim is still there...
595 DISPID DispatchId
= VimOle
.GetDispatchId ("SendKeys");
598 // We can't get a dispatch id.
599 // This means that probably Vim has been terminated.
600 // Don't issue an error message here, instead
601 // destroy the OLE object and try to connect once more
603 // In fact, this should never happen, because the OLE aut. object
604 // should not be kept long enough to allow the user to terminate Vim
605 // to avoid memory corruption (why the heck is there no system garbage
606 // collection for those damned OLE memory chunks???).
607 VimOle
.DeleteObject ();
608 if (! VimOle
.CreateObject ("Vim.Application"))
609 // If this create fails, it's time for an error msg
612 if (! (DispatchId
= VimOle
.GetDispatchId ("SendKeys")))
613 // There is something wrong...
620 // Output an error message for an OLE error
621 // Check on the classstring error, which probably means Vim wasn't registered.
623 static void VimErrDiag (COleAutomationControl
& VimOle
)
625 SCODE sc
= GetScode (VimOle
.GetResult ());
626 if (sc
== CO_E_CLASSSTRING
)
629 sprintf (Buf
, "There is no registered OLE automation server named "
630 "\"Vim.Application\".\n"
631 "Use the OLE-enabled version of Vim with VisVim and "
632 "make sure to register Vim by running \"vim -register\".");
633 MessageBox (NULL
, Buf
, "OLE Error", MB_OK
);
639 // Change directory to the directory the file 'FileName' is in or it's parent
640 // directory according to the setting of the global 'g_ChangeDir':
641 // 'FileName' is expected to contain an absolute DOS path including the drive
647 static void VimChangeDir (COleAutomationControl
& VimOle
, DISPID DispatchId
, BSTR
& FileName
)
651 // Get the path name of the file ("dir/")
652 CString StrFileName
= FileName
;
653 char Drive
[_MAX_DRIVE
];
655 char DirUnix
[_MAX_DIR
* 2];
658 _splitpath (StrFileName
, Drive
, Dir
, NULL
, NULL
);
660 // Convert to Unix path name format, escape spaces.
662 for (s
= Dir
; *s
; ++s
)
674 // Construct the cd command; append /.. if cd to parent
675 // directory and not in root directory
676 OLECHAR Buf
[MAX_OLE_STR
];
677 char VimCmd
[MAX_OLE_STR
];
679 sprintf (VimCmd
, ":cd %s%s%s\n", Drive
, DirUnix
,
680 g_ChangeDir
== CD_SOURCE_PARENT
&& DirUnix
[1] ? ".." : "");
681 VimOle
.Method (DispatchId
, "s", TO_OLE_STR_BUF (VimCmd
, Buf
));
685 // Print out a debug message
687 static void DebugMsg (char* Msg
, char* Arg
)
690 sprintf (Buf
, Msg
, Arg
);