Patch #3323 2012-08-23 20:15

p2rkw

Abbreviations auto-complete
Download
3323-Abbreviations.patch (10.2 KB)
Category
Plugin::FeatureAdd
Status
Accepted
Close date
2012-11-07 09:58
Assigned to
mortenmacfly
Index: src/plugins/abbreviations/abbreviations.h
===================================================================
--- src/plugins/abbreviations/abbreviations.h    (wersja 8496)
+++ src/plugins/abbreviations/abbreviations.h    (kopia robocza)
@@ -148,10 +148,16 @@
     void OnEditAutoComplete(wxCommandEvent& /*event*/);
     void OnEditMenuUpdateUI(wxUpdateUIEvent& event);
 
+public:
+  void EditorEventHook(cbEditor* editor, wxScintillaEvent& event);
+
 private:
     friend class AbbreviationsConfigPanel;
     AutoCompleteMap m_AutoCompleteMap;
 
+    int                     m_EditorHookId;
+    /// is AutoComp opened by Abbreviations
+    bool m_IsAutoCompVisible;
 private:
     DECLARE_EVENT_TABLE();
 };
Index: src/plugins/abbreviations/abbreviations.cpp
===================================================================
--- src/plugins/abbreviations/abbreviations.cpp    (wersja 8496)
+++ src/plugins/abbreviations/abbreviations.cpp    (kopia robocza)
@@ -11,6 +11,8 @@
 #include <configurationpanel.h>
 #include <cbstyledtextctrl.h>
 
+#include <editor_hooks.h>
+
 #include "abbreviations.h"
 #include "abbreviationsconfigpanel.h"
 
@@ -44,6 +46,8 @@
     {
         NotifyMissingFile(_T("abbreviations.zip"));
     }
+
+    m_IsAutoCompVisible = false;
 }
 
 // destructor
@@ -65,6 +69,10 @@
 
     LoadAutoCompleteConfig();
     RegisterScripting();
+
+    // hook to editors
+    EditorHooks::HookFunctorBase* myhook = new EditorHooks::HookFunctor<Abbreviations>(this, &Abbreviations::EditorEventHook);
+    m_EditorHookId = EditorHooks::RegisterHook(myhook);
 }
 
 void Abbreviations::OnRelease(bool appShutDown)
@@ -82,6 +90,10 @@
     {
         m_Singleton = nullptr;
     }
+
+    // unregister hook
+    // 'true' will delete the functor too
+    EditorHooks::UnregisterHook(m_EditorHookId, true);
 }
 
 void Abbreviations::RegisterScripting()
@@ -158,8 +170,38 @@
 
 void Abbreviations::OnEditAutoComplete(wxCommandEvent& /*event*/)
 {
-    cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
-    DoAutoComplete(ed);
+    cbEditor* editor = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
+    cbStyledTextCtrl* control = editor->GetControl();
+
+    const AutoCompleteMap& acm = m_AutoCompleteMap;
+
+    int curPos = control->GetCurrentPos();
+    int startPos = control->WordStartPosition(curPos, true);
+    const int endPos = control->WordEndPosition(curPos, true);
+
+    const wxString keyword = control->GetTextRange(startPos, endPos);
+    AutoCompleteMap::const_iterator it = acm.find(keyword);
+
+    if(it != acm.end() )
+    {
+        DoAutoComplete(editor);
+    }
+    else
+    {
+        wxArrayString items;
+        for( AutoCompleteMap::const_iterator it = acm.begin(); it != acm.end(); ++it )
+        {
+            if(it->first.Lower().StartsWith(keyword) )
+            {
+                items.Add(it->first);
+            }
+        }
+        items.Sort();
+        wxString itemsStr = GetStringFromArray(items, _T(" "));
+        control->AutoCompShow(endPos-startPos, itemsStr);
+        m_IsAutoCompVisible = control->AutoCompActive();
+    }
+
 }
 
 void Abbreviations::OnEditMenuUpdateUI(wxUpdateUIEvent& event)
@@ -187,83 +229,77 @@
     if (control->CallTipActive())
         control->CallTipCancel();
 
+    m_IsAutoCompVisible = false;
+
     LogManager* msgMan = Manager::Get()->GetLogManager();
     int curPos = control->GetCurrentPos();
     int wordStartPos = control->WordStartPosition(curPos, true);
-    wxString keyword = control->GetTextRange(wordStartPos, curPos);
+    const int endPos = control->WordEndPosition(curPos, true);
+    wxString keyword = control->GetTextRange(wordStartPos, endPos);
     wxString lineIndent = ed->GetLineIndentString(control->GetCurrentLine());
     msgMan->DebugLog(_T("Auto-complete keyword: ") + keyword);
 
-    AutoCompleteMap::iterator it;
-    for (it = m_AutoCompleteMap.begin(); it != m_AutoCompleteMap.end(); ++it)
+    AutoCompleteMap::iterator it = m_AutoCompleteMap.find(keyword);
+    if (it != m_AutoCompleteMap.end() )
     {
-        if (keyword == it->first)
+        // found; auto-complete it
+        msgMan->DebugLog(_T("Auto-complete match for keyword found."));
+
+        // indent code accordingly
+        wxString code = it->second;
+        code.Replace(_T("\n"), _T('\n') + lineIndent);
+
+        // look for and replace macros
+        int macroPos = code.Find(_T("$("));
+        while (macroPos != -1)
         {
-            // found; auto-complete it
-            msgMan->DebugLog(_T("Auto-complete match for keyword found."));
+            // locate ending parenthesis
+            int macroPosEnd = macroPos + 2;
+            int len = (int)code.Length();
+            while (macroPosEnd < len && code.GetChar(macroPosEnd) != _T(')'))
+                ++macroPosEnd;
+            if (macroPosEnd == len)
+                return; // no ending parenthesis
 
-            // indent code accordingly
-            wxStr
download for full patch...
p2rkw 2012-08-26 13:08

- if keyword isn't complete abbreviation will display auto complete list with possible matches

- abbreviations now can be expandend even if cursor is inside keyword (not only at end)

topic on forum: http://forums.codeblocks.org/index.php/topic,16749.0.html

mortenmacfly 2012-10-27 21:02

One question:

In Abbreviations::EditorEventHook, why did you move the event.Skip() outof the else - tree?This changes the logic of the methods and would raise multiple autocomp boxes in case another plugin shows one, too (i.e. CC).

Was there a particular reason to do so?

p2rkw 2012-10-27 22:59
Hmm...  I don't remember right now, but you're right: that Skip() should be inside else block. Here's my current implementation of Abbreviations::EditorEventHook :

void Abbreviations::EditorEventHook(cbEditor* editor, wxScintillaEvent& event)
{
    cbStyledTextCtrl* control = editor->GetControl();

    if( !IsAttached() || !m_IsAutoCompVisible )
    {
        event.Skip();
        return;
    }

    if (event.GetEventType() == wxEVT_SCI_AUTOCOMP_SELECTION)
    {
        const wxString& itemText = event.GetText();
        int curPos = control->GetCurrentPos();
        int startPos = control->WordStartPosition(curPos, true);
        const int endPos = control->WordEndPosition(curPos, true);

        control->BeginUndoAction();
        control->SetTargetStart(startPos);
        control->SetTargetEnd(endPos);
        control->ReplaceTarget(itemText);
        control->GotoPos(startPos + itemText.size() );
        control->EndUndoAction();

        DoAutoComplete(editor);

        //prevent other plugins(CC mostly) from insertion this keyword
        event.SetText( wxEmptyString );
        event.SetEventType(wxEVT_NULL);
    }
    else  //here should be: else if( vent.GetEventType() == wxEVT_SCI_AUTOCOMP_CANCELLED)
    {	//but this event doesn't occur.
        m_IsAutoCompVisible = control->AutoCompActive();
		
        if(!m_IsAutoCompVisible)
        {
            // allow others to handle this event
            event.Skip();
        }
    }
}

As you can see I also added "event.SetEventType(wxEVT_NULL);" to prevent insertion event's text by CC.
Unfortunately, without this line CC still can handle this event, even when it's not skipped with event.Skip() call.

Boolean variable m_IsAutoCompVisible is set to true only when tooltip was opened by Abbreviations menu item or shortcut key. IMHO CC should also checks is auto complete tool-tip opened by itself. Then CC might call event.Skip() only on unwanted events( now its called on every single event). 
mortenmacfly 2012-10-28 05:38

Ok.

Do you mind to simply update the patch against trunk? I am a bit confused with what and where to update now.

p2rkw 2012-10-31 20:44

Patch updated.