diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 02:44:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 02:44:24 +0000 |
commit | 8baab3c8d7a6f22888bd581cd5c6098fd2e4b5a8 (patch) | |
tree | 3537e168b860f2742f6029d70501b5ed7d15d345 /src/VisVim/Commands.cpp | |
parent | Initial commit. (diff) | |
download | vim-8baab3c8d7a6f22888bd581cd5c6098fd2e4b5a8.tar.xz vim-8baab3c8d7a6f22888bd581cd5c6098fd2e4b5a8.zip |
Adding upstream version 2:8.1.0875.upstream/2%8.1.0875upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VisVim/Commands.cpp')
-rw-r--r-- | src/VisVim/Commands.cpp | 710 |
1 files changed, 710 insertions, 0 deletions
diff --git a/src/VisVim/Commands.cpp b/src/VisVim/Commands.cpp new file mode 100644 index 0000000..569fbb8 --- /dev/null +++ b/src/VisVim/Commands.cpp @@ -0,0 +1,710 @@ +#include "stdafx.h" +#include <comdef.h> // For _bstr_t +#include "VisVim.h" +#include "Commands.h" +#include "OleAut.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; + +#endif + + +// Change directory before opening file? +#define CD_SOURCE 0 // Cd to source path +#define CD_SOURCE_PARENT 1 // Cd to parent directory of source path +#define CD_NONE 2 // No cd + + +static BOOL g_bEnableVim = TRUE; // Vim enabled +static BOOL g_bDevStudioEditor = FALSE; // Open file in Dev Studio editor simultaneously +static BOOL g_bNewTabs = FALSE; +static int g_ChangeDir = CD_NONE; // CD after file open? + +static void VimSetEnableState(BOOL bEnableState); +static BOOL VimOpenFile(BSTR& FileName, long LineNr); +static DISPID VimGetDispatchId(COleAutomationControl& VimOle, char* Method); +static void VimErrDiag(COleAutomationControl& VimOle); +static void VimChangeDir(COleAutomationControl& VimOle, DISPID DispatchId, BSTR& FileName); +static void DebugMsg(char* Msg, char* Arg = NULL); + + +///////////////////////////////////////////////////////////////////////////// +// CCommands + +CCommands::CCommands() +{ + // m_pApplication == NULL; M$ Code generation bug!!! + m_pApplication = NULL; + m_pApplicationEventsObj = NULL; + m_pDebuggerEventsObj = NULL; +} + +CCommands::~CCommands() +{ + ASSERT(m_pApplication != NULL); + if (m_pApplication) + { + m_pApplication->Release(); + m_pApplication = NULL; + } +} + +void CCommands::SetApplicationObject(IApplication * pApplication) +{ + // This function assumes pApplication has already been AddRef'd + // for us, which CDSAddIn did in it's QueryInterface call + // just before it called us. + m_pApplication = pApplication; + if (! m_pApplication) + return; + + // Create Application event handlers + XApplicationEventsObj::CreateInstance(&m_pApplicationEventsObj); + if (! m_pApplicationEventsObj) + { + ReportInternalError("XApplicationEventsObj::CreateInstance"); + return; + } + m_pApplicationEventsObj->AddRef(); + m_pApplicationEventsObj->Connect(m_pApplication); + m_pApplicationEventsObj->m_pCommands = this; + +#ifdef NEVER + // Create Debugger event handler + CComPtr < IDispatch > pDebugger; + if (SUCCEEDED(m_pApplication->get_Debugger(&pDebugger)) + && pDebugger != NULL) + { + XDebuggerEventsObj::CreateInstance(&m_pDebuggerEventsObj); + m_pDebuggerEventsObj->AddRef(); + m_pDebuggerEventsObj->Connect(pDebugger); + m_pDebuggerEventsObj->m_pCommands = this; + } +#endif + + // Get settings from registry HKEY_CURRENT_USER\Software\Vim\VisVim + HKEY hAppKey = GetAppKey("Vim"); + if (hAppKey) + { + HKEY hSectionKey = GetSectionKey(hAppKey, "VisVim"); + if (hSectionKey) + { + g_bEnableVim = GetRegistryInt(hSectionKey, "EnableVim", + g_bEnableVim); + g_bDevStudioEditor = GetRegistryInt(hSectionKey, + "DevStudioEditor", g_bDevStudioEditor); + g_bNewTabs = GetRegistryInt(hSectionKey, "NewTabs", + g_bNewTabs); + g_ChangeDir = GetRegistryInt(hSectionKey, "ChangeDir", + g_ChangeDir); + RegCloseKey(hSectionKey); + } + RegCloseKey(hAppKey); + } +} + +void CCommands::UnadviseFromEvents() +{ + ASSERT(m_pApplicationEventsObj != NULL); + if (m_pApplicationEventsObj) + { + m_pApplicationEventsObj->Disconnect(m_pApplication); + m_pApplicationEventsObj->Release(); + m_pApplicationEventsObj = NULL; + } + +#ifdef NEVER + if (m_pDebuggerEventsObj) + { + // Since we were able to connect to the Debugger events, we + // should be able to access the Debugger object again to + // unadvise from its events (thus the VERIFY_OK below--see + // stdafx.h). + CComPtr < IDispatch > pDebugger; + VERIFY_OK(m_pApplication->get_Debugger(&pDebugger)); + ASSERT(pDebugger != NULL); + m_pDebuggerEventsObj->Disconnect(pDebugger); + m_pDebuggerEventsObj->Release(); + m_pDebuggerEventsObj = NULL; + } +#endif +} + + +///////////////////////////////////////////////////////////////////////////// +// Event handlers + +// Application events + +HRESULT CCommands::XApplicationEvents::BeforeBuildStart() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return S_OK; +} + +HRESULT CCommands::XApplicationEvents::BuildFinish(long nNumErrors, long nNumWarnings) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return S_OK; +} + +HRESULT CCommands::XApplicationEvents::BeforeApplicationShutDown() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return S_OK; +} + +// The open document event handle is the place where the real interface work +// is done. +// Vim gets called from here. +// +HRESULT CCommands::XApplicationEvents::DocumentOpen(IDispatch * theDocument) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + if (! g_bEnableVim) + // Vim not enabled or empty command line entered + return S_OK; + + // First get the current file name and line number + + // Get the document object + CComQIPtr < ITextDocument, &IID_ITextDocument > pDoc(theDocument); + if (! pDoc) + return S_OK; + + BSTR FileName; + long LineNr = -1; + + // Get the document name + if (FAILED(pDoc->get_FullName(&FileName))) + return S_OK; + + LPDISPATCH pDispSel; + + // Get a selection object dispatch pointer + if (SUCCEEDED(pDoc->get_Selection(&pDispSel))) + { + // Get the selection object + CComQIPtr < ITextSelection, &IID_ITextSelection > pSel(pDispSel); + + if (pSel) + // Get the selection line number + pSel->get_CurrentLine(&LineNr); + + pDispSel->Release(); + } + + // Open the file in Vim and position to the current line + if (VimOpenFile(FileName, LineNr)) + { + if (! g_bDevStudioEditor) + { + // Close the document in developer studio + CComVariant vSaveChanges = dsSaveChangesPrompt; + DsSaveStatus Saved; + + pDoc->Close(vSaveChanges, &Saved); + } + } + + // We're done here + SysFreeString(FileName); + return S_OK; +} + +HRESULT CCommands::XApplicationEvents::BeforeDocumentClose(IDispatch * theDocument) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return S_OK; +} + +HRESULT CCommands::XApplicationEvents::DocumentSave(IDispatch * theDocument) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return S_OK; +} + +HRESULT CCommands::XApplicationEvents::NewDocument(IDispatch * theDocument) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + if (! g_bEnableVim) + // Vim not enabled or empty command line entered + return S_OK; + + // First get the current file name and line number + + CComQIPtr < ITextDocument, &IID_ITextDocument > pDoc(theDocument); + if (! pDoc) + return S_OK; + + BSTR FileName; + HRESULT hr; + + hr = pDoc->get_FullName(&FileName); + if (FAILED(hr)) + return S_OK; + + // Open the file in Vim and position to the current line + if (VimOpenFile(FileName, 0)) + { + if (! g_bDevStudioEditor) + { + // Close the document in developer studio + CComVariant vSaveChanges = dsSaveChangesPrompt; + DsSaveStatus Saved; + + pDoc->Close(vSaveChanges, &Saved); + } + } + + SysFreeString(FileName); + return S_OK; +} + +HRESULT CCommands::XApplicationEvents::WindowActivate(IDispatch * theWindow) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return S_OK; +} + +HRESULT CCommands::XApplicationEvents::WindowDeactivate(IDispatch * theWindow) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return S_OK; +} + +HRESULT CCommands::XApplicationEvents::WorkspaceOpen() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return S_OK; +} + +HRESULT CCommands::XApplicationEvents::WorkspaceClose() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return S_OK; +} + +HRESULT CCommands::XApplicationEvents::NewWorkspace() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return S_OK; +} + +// Debugger event + +HRESULT CCommands::XDebuggerEvents::BreakpointHit(IDispatch * pBreakpoint) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return S_OK; +} + + +///////////////////////////////////////////////////////////////////////////// +// VisVim dialog + +class CMainDialog : public CDialog +{ + public: + CMainDialog(CWnd * pParent = NULL); // Standard constructor + + //{{AFX_DATA(CMainDialog) + enum { IDD = IDD_ADDINMAIN }; + int m_ChangeDir; + BOOL m_bDevStudioEditor; + BOOL m_bNewTabs; + //}}AFX_DATA + + //{{AFX_VIRTUAL(CMainDialog) + protected: + virtual void DoDataExchange(CDataExchange * pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + protected: + //{{AFX_MSG(CMainDialog) + afx_msg void OnEnable(); + afx_msg void OnDisable(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +CMainDialog::CMainDialog(CWnd * pParent /* =NULL */ ) + : CDialog(CMainDialog::IDD, pParent) +{ + //{{AFX_DATA_INIT(CMainDialog) + m_ChangeDir = -1; + m_bDevStudioEditor = FALSE; + m_bNewTabs = FALSE; + //}}AFX_DATA_INIT +} + +void CMainDialog::DoDataExchange(CDataExchange * pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CMainDialog) + DDX_Radio(pDX, IDC_CD_SOURCE_PATH, m_ChangeDir); + DDX_Check(pDX, IDC_DEVSTUDIO_EDITOR, m_bDevStudioEditor); + DDX_Check(pDX, IDC_NEW_TABS, m_bNewTabs); + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(CMainDialog, CDialog) + //{{AFX_MSG_MAP(CMainDialog) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +///////////////////////////////////////////////////////////////////////////// +// CCommands methods + +STDMETHODIMP CCommands::VisVimDialog() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + // Use m_pApplication to access the Developer Studio Application + // object, + // and VERIFY_OK to see error strings in DEBUG builds of your add-in + // (see stdafx.h) + + VERIFY_OK(m_pApplication->EnableModeless(VARIANT_FALSE)); + + CMainDialog Dlg; + + Dlg.m_bDevStudioEditor = g_bDevStudioEditor; + Dlg.m_bNewTabs = g_bNewTabs; + Dlg.m_ChangeDir = g_ChangeDir; + if (Dlg.DoModal() == IDOK) + { + g_bDevStudioEditor = Dlg.m_bDevStudioEditor; + g_bNewTabs = Dlg.m_bNewTabs; + g_ChangeDir = Dlg.m_ChangeDir; + + // Save settings to registry HKEY_CURRENT_USER\Software\Vim\VisVim + HKEY hAppKey = GetAppKey("Vim"); + if (hAppKey) + { + HKEY hSectionKey = GetSectionKey(hAppKey, "VisVim"); + if (hSectionKey) + { + WriteRegistryInt(hSectionKey, "DevStudioEditor", + g_bDevStudioEditor); + WriteRegistryInt(hSectionKey, "NewTabs", + g_bNewTabs); + WriteRegistryInt(hSectionKey, "ChangeDir", g_ChangeDir); + RegCloseKey(hSectionKey); + } + RegCloseKey(hAppKey); + } + } + + VERIFY_OK(m_pApplication->EnableModeless(VARIANT_TRUE)); + return S_OK; +} + +STDMETHODIMP CCommands::VisVimEnable() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + VimSetEnableState(true); + return S_OK; +} + +STDMETHODIMP CCommands::VisVimDisable() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + VimSetEnableState(false); + return S_OK; +} + +STDMETHODIMP CCommands::VisVimToggle() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + VimSetEnableState(! g_bEnableVim); + return S_OK; +} + +STDMETHODIMP CCommands::VisVimLoad() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + // Use m_pApplication to access the Developer Studio Application object, + // and VERIFY_OK to see error strings in DEBUG builds of your add-in + // (see stdafx.h) + + CComBSTR bStr; + // Define dispatch pointers for document and selection objects + CComPtr < IDispatch > pDispDoc, pDispSel; + + // Get a document object dispatch pointer + VERIFY_OK(m_pApplication->get_ActiveDocument(&pDispDoc)); + if (! pDispDoc) + return S_OK; + + BSTR FileName; + long LineNr = -1; + + // Get the document object + CComQIPtr < ITextDocument, &IID_ITextDocument > pDoc(pDispDoc); + + if (! pDoc) + return S_OK; + + // Get the document name + if (FAILED(pDoc->get_FullName(&FileName))) + return S_OK; + + // Get a selection object dispatch pointer + if (SUCCEEDED(pDoc->get_Selection(&pDispSel))) + { + // Get the selection object + CComQIPtr < ITextSelection, &IID_ITextSelection > pSel(pDispSel); + + if (pSel) + // Get the selection line number + pSel->get_CurrentLine(&LineNr); + } + + // Open the file in Vim + VimOpenFile(FileName, LineNr); + + SysFreeString(FileName); + return S_OK; +} + + +// +// Here we do the actual processing and communication with Vim +// + +// Set the enable state and save to registry +// +static void VimSetEnableState(BOOL bEnableState) +{ + g_bEnableVim = bEnableState; + HKEY hAppKey = GetAppKey("Vim"); + if (hAppKey) + { + HKEY hSectionKey = GetSectionKey(hAppKey, "VisVim"); + if (hSectionKey) + WriteRegistryInt(hSectionKey, "EnableVim", g_bEnableVim); + RegCloseKey(hAppKey); + } +} + +// Open the file 'FileName' in Vim and goto line 'LineNr' +// 'FileName' is expected to contain an absolute DOS path including the drive +// letter. +// 'LineNr' must contain a valid line number or 0, e. g. for a new file +// +static BOOL VimOpenFile(BSTR& FileName, long LineNr) +{ + + // OLE automation object for com. with Vim + // When the object goes out of scope, it's destructor destroys the OLE + // connection; + // This is important to avoid blocking the object + // (in this memory corruption would be likely when terminating Vim + // while still running DevStudio). + // So keep this object local! + COleAutomationControl VimOle; + + // :cd D:/Src2/VisVim/ + // + // Get a dispatch id for the SendKeys method of Vim; + // enables connection to Vim if necessary + DISPID DispatchId; + DispatchId = VimGetDispatchId(VimOle, "SendKeys"); + if (! DispatchId) + // OLE error, can't obtain dispatch id + goto OleError; + + OLECHAR Buf[MAX_OLE_STR]; + char FileNameTmp[MAX_OLE_STR]; + char VimCmd[MAX_OLE_STR]; + char *s, *p; + + // Prepend CTRL-\ CTRL-N to exit insert mode + VimCmd[0] = 0x1c; + VimCmd[1] = 0x0e; + VimCmd[2] = 0; + +#ifdef SINGLE_WINDOW + // Update the current file in Vim if it has been modified. + // Disabled, because it could write the file when you don't want to. + sprintf(VimCmd + 2, ":up\n"); +#endif + if (! VimOle.Method(DispatchId, "s", TO_OLE_STR_BUF(VimCmd, Buf))) + goto OleError; + + // Change Vim working directory to where the file is if desired + if (g_ChangeDir != CD_NONE) + VimChangeDir(VimOle, DispatchId, FileName); + + // Make Vim open the file. + // In the filename convert all \ to /, put a \ before a space. + if (g_bNewTabs) + { + sprintf(VimCmd, ":tab drop "); + s = VimCmd + 10; + } + else + { + sprintf(VimCmd, ":drop "); + s = VimCmd + 6; + } + sprintf(FileNameTmp, "%S", (char *)FileName); + for (p = FileNameTmp; *p != '\0' && s < VimCmd + MAX_OLE_STR - 4; ++p) + if (*p == '\\') + *s++ = '/'; + else + { + if (*p == ' ') + *s++ = '\\'; + *s++ = *p; + } + *s++ = '\n'; + *s = '\0'; + + if (! VimOle.Method(DispatchId, "s", TO_OLE_STR_BUF(VimCmd, Buf))) + goto OleError; + + if (LineNr > 0) + { + // Goto line + sprintf(VimCmd, ":%ld\n", LineNr); + if (! VimOle.Method(DispatchId, "s", TO_OLE_STR_BUF(VimCmd, Buf))) + goto OleError; + } + + // Make Vim come to the foreground + if (! VimOle.Method("SetForeground")) + VimOle.ErrDiag(); + + // We're done + return true; + + OleError: + // There was an OLE error + // Check if it's the "unknown class string" error + VimErrDiag(VimOle); + return false; +} + +// Return the dispatch id for the Vim method 'Method' +// Create the Vim OLE object if necessary +// Returns a valid dispatch id or null on error +// +static DISPID VimGetDispatchId(COleAutomationControl& VimOle, char* Method) +{ + // Initialize Vim OLE connection if not already done + if (! VimOle.IsCreated()) + { + if (! VimOle.CreateObject("Vim.Application")) + return NULL; + } + + // Get the dispatch id for the SendKeys method. + // By doing this, we are checking if Vim is still there... + DISPID DispatchId = VimOle.GetDispatchId("SendKeys"); + if (! DispatchId) + { + // We can't get a dispatch id. + // This means that probably Vim has been terminated. + // Don't issue an error message here, instead + // destroy the OLE object and try to connect once more + // + // In fact, this should never happen, because the OLE aut. object + // should not be kept long enough to allow the user to terminate Vim + // to avoid memory corruption (why the heck is there no system garbage + // collection for those damned OLE memory chunks???). + VimOle.DeleteObject(); + if (! VimOle.CreateObject("Vim.Application")) + // If this create fails, it's time for an error msg + return NULL; + + if (! (DispatchId = VimOle.GetDispatchId("SendKeys"))) + // There is something wrong... + return NULL; + } + + return DispatchId; +} + +// Output an error message for an OLE error +// Check on the classstring error, which probably means Vim wasn't registered. +// +static void VimErrDiag(COleAutomationControl& VimOle) +{ + SCODE sc = GetScode(VimOle.GetResult()); + if (sc == CO_E_CLASSSTRING) + { + char Buf[256]; + sprintf(Buf, "There is no registered OLE automation server named " + "\"Vim.Application\".\n" + "Use the OLE-enabled version of Vim with VisVim and " + "make sure to register Vim by running \"vim -register\"."); + MessageBox(NULL, Buf, "OLE Error", MB_OK); + } + else + VimOle.ErrDiag(); +} + +// Change directory to the directory the file 'FileName' is in or it's parent +// directory according to the setting of the global 'g_ChangeDir': +// 'FileName' is expected to contain an absolute DOS path including the drive +// letter. +// CD_NONE +// CD_SOURCE_PATH +// CD_SOURCE_PARENT +// +static void VimChangeDir(COleAutomationControl& VimOle, DISPID DispatchId, BSTR& FileName) +{ + // Do a :cd first + + // Get the path name of the file ("dir/") + CString StrFileName = FileName; + char Drive[_MAX_DRIVE]; + char Dir[_MAX_DIR]; + char DirUnix[_MAX_DIR * 2]; + char *s, *t; + + _splitpath(StrFileName, Drive, Dir, NULL, NULL); + + // Convert to Unix path name format, escape spaces. + t = DirUnix; + for (s = Dir; *s; ++s) + if (*s == '\\') + *t++ = '/'; + else + { + if (*s == ' ') + *t++ = '\\'; + *t++ = *s; + } + *t = '\0'; + + + // Construct the cd command; append /.. if cd to parent + // directory and not in root directory + OLECHAR Buf[MAX_OLE_STR]; + char VimCmd[MAX_OLE_STR]; + + sprintf(VimCmd, ":cd %s%s%s\n", Drive, DirUnix, + g_ChangeDir == CD_SOURCE_PARENT && DirUnix[1] ? ".." : ""); + VimOle.Method(DispatchId, "s", TO_OLE_STR_BUF(VimCmd, Buf)); +} + +#ifdef _DEBUG +// Print out a debug message +// +static void DebugMsg(char* Msg, char* Arg) +{ + char Buf[400]; + sprintf(Buf, Msg, Arg); + AfxMessageBox(Buf); +} +#endif |