void PHPCodeCompletion::OnCodeComplete(clCodeCompletionEvent& e)
{
    e.Skip(true);
    if(PHPWorkspace::Get()->IsOpen()) {
        IEditor* editor = dynamic_cast<IEditor*>(e.GetEditor());
        if(editor && IsPHPFile(editor)) {
            e.Skip(false);

            // Update the settings
            TagsOptionsData d;
            clConfig ccConfig("code-completion.conf");
            ccConfig.ReadItem(&d);
            m_lookupTable.SetSizeLimit(d.GetCcNumberOfDisplayItems());

            // Check if the code completion was triggered due to user
            // typing '(', in this case, call OnFunctionCallTip()
            wxChar charAtPos = editor->GetCharAtPos(editor->GetCurrentPosition() - 1);
            if(charAtPos == '(') {
                OnFunctionCallTip(e);

            } else {
                // Perform the code completion here
                PHPExpression::Ptr_t expr(new PHPExpression(editor->GetTextRange(0, e.GetPosition())));
                bool isExprStartsWithOpenTag = expr->IsExprStartsWithOpenTag();
                PHPEntityBase::Ptr_t entity = expr->Resolve(m_lookupTable, editor->GetFileName().GetFullPath());
                if(entity) {
                    // Suggets members for the resolved entity
                    PHPEntityBase::List_t matches;
                    expr->Suggest(entity, m_lookupTable, matches);
                    if(!expr->GetFilter().IsEmpty() && (expr->GetCount() == 0)) {

                        // Word completion
                        PHPEntityBase::List_t keywords = PhpKeywords(expr->GetFilter());

                        // Preprend the keywords
                        matches.insert(matches.end(), keywords.begin(), keywords.end());

                        // Did user typed "<?ph" or "<?php" ??
                        // If so, clear the matches
                        if(isExprStartsWithOpenTag && (expr->GetFilter() == "ph" || expr->GetFilter() == "php")) {
                            matches.clear();
                        }
                    }

                    // Remove duplicates from the list
                    if(!matches.empty()) {
                        // Show the code completion box
                        DoShowCompletionBox(matches, expr);
                    }
                }
            }
        }
    }
}
void PHPCodeCompletion::GetMembers(IEditor* editor, PHPEntityBase::List_t& members, wxString& scope)
{
    members.clear();
    scope.clear();
    if(!editor) {
        return;
    }

    // Parse until the current position to get the current scope name
    {
        wxString text = editor->GetTextRange(0, editor->GetCurrentPosition());
        PHPSourceFile sourceFile(text);
        sourceFile.SetParseFunctionBody(true);
        sourceFile.SetFilename(editor->GetFileName());
        sourceFile.Parse();

        const PHPEntityClass* scopeAtPoint = sourceFile.Class()->Cast<PHPEntityClass>();
        if(!scopeAtPoint) {
            return;
        }
        scope = scopeAtPoint->GetFullName();
    }

    // Second parse: parse the entire buffer so we are not limited by the caret position
    wxString text = editor->GetTextRange(0, editor->GetLength());
    PHPSourceFile sourceFile(text);
    sourceFile.SetParseFunctionBody(true);
    sourceFile.SetFilename(editor->GetFileName());
    sourceFile.Parse();

    // Locate the scope
    PHPEntityBase::Ptr_t parentClass = sourceFile.Namespace()->FindChild(scope);
    if(!parentClass) return;

    // filter out
    const PHPEntityBase::List_t& children = parentClass->GetChildren();
    PHPEntityBase::List_t::const_iterator iter = children.begin();

    for(; iter != children.end(); ++iter) {
        PHPEntityBase::Ptr_t child = *iter;
        if(child->Is(kEntityTypeVariable) && child->Cast<PHPEntityVariable>()->IsMember() &&
            !child->Cast<PHPEntityVariable>()->IsConst() && !child->Cast<PHPEntityVariable>()->IsStatic()) {
            // a member of a class
            members.push_back(child);
        }
    }
}