Difference between revisions of "Code::Completion Rewrite"

From Code::Blocks
m (add category)
 
(25 intermediate revisions by 3 users not shown)
Line 1: Line 1:
 +
[[Category:Code::Blocks Documentation]]
 +
[[Category:Developer Documentation]]
 +
Here you'll find some discussion on the Code::Completion rewrite, and some useful links to related materials online.
 +
 
== Background ==
 
== Background ==
 
The current Code::Completion plug-in has some flaws, and is currently development frozen.
 
The current Code::Completion plug-in has some flaws, and is currently development frozen.
Line 22: Line 26:
 
** Provide automatic code generation features
 
** Provide automatic code generation features
  
=== Purpose Statement ===
+
=== Code::Completion ===
==== Code::SymbolTable ====
+
==== Purpose Statement ====
==== Code::Completion ====
 
 
The current Code::Completion plugin is outdated, and needs a complete rewrite.
 
The current Code::Completion plugin is outdated, and needs a complete rewrite.
 
The purpose of the Code::Completion plugin is thus:
 
The purpose of the Code::Completion plugin is thus:
Line 32: Line 35:
 
** Parameter list
 
** Parameter list
 
** Relevant documentation
 
** Relevant documentation
 +
* Provide variable tooltips
 +
** type and modifiers
 +
** scope
 +
* Provide preprocessor tooltips
 +
** replacement value of a macro
 +
** parameter list when applicable
 
* Provide completion features for class constructors
 
* Provide completion features for class constructors
 
* Provide completion features for initializer lists
 
* Provide completion features for initializer lists
  
==== Code::SymbolOutline ====
+
==== Process ====
==== Code::Refactoring ====
+
* The user requests a completion of the current symbol.
==== Code::Documentation ====
+
** Call CC::EventSymbolHover
=== Process ===
+
*** When the mouse hovers over a symbol after a timeout
==== Code::SymbolTable ====
+
** Call CC::EventSymbolTip
==== Code::Completion ====
+
*** When the user types in any of the following: . :: ->
 +
*** When the user presses the keystroke CTRL-SPACE
 +
** Call CC::EventCallTip
 +
*** When the user types in any of the following: ( ,
 +
*** When the user presses the keystroke CTRL-SHIFT-SPACE
 +
** Call CC::EventPreprocTip
 +
*** When the user types in any of the following: < " when in preproc context (# at the start of the line)
 +
* the C::C plugin determines the proper scope, when applicable (global, local, class)
 +
* Compare the current symbol against the symbol table of proper scope.
 +
* Provide a composite list to the user.
 +
 
 +
=== Code::SymbolTable ===
 +
There will be 3 symbol lists:
 +
* global namespace
 +
* local scope (2 options)
 +
** the local scope can be generated by smartly parsing the current file on every request
 +
** keep a running count, and add/remove symbols from the table as the file is edited
 +
* class scope
 +
 
 +
Here's a good link for further reading on the subject and the problems:
 +
[http://en.wikipedia.org/wiki/C%2B%2B#Parsing_and_processing_C.2B.2B_source_code On Wikipedia]
 +
 
 +
==== Data Proposal ====
 +
 
 +
class symbol
 +
{
 +
    string name;              // name of the symbol
 +
    int    id;                // Id of the symbol, should be unique in the workspace
 +
    int    file_id;          // Id of file where the symbol has been declared
 +
    int    filepos_begin;    // Position where declaration of the symbol starts
 +
    int    filepos_end;      // Position where declaration of the symbol ends
 +
    int    type;              // Type of the symbol: macro / class / typedef / variable / function
 +
    flags  modifiers;        // Bitfield used to mark some extra properties of symbol
 +
                              // like that it is static or inline
 +
    int    value_type_id;    // Id of symbol which represents c++ type of current symbol
 +
                              // (like type of variable or type of returned value from function)
 +
    int    extra_type_id;    // Extra type used in some cases
 +
    list  children;          // List of child elements of this symbol (members in class etc)
 +
    list  extra_lists[3];    // See table below
 +
    map    extra_values;      // int -> string map which can keep some extra data
 +
}
 +
 
 +
class list_entry
 +
{
 +
    int    symbol_id;        // ID of the symbol referenced
 +
    int    storage_class;    // Storage class of the symbol (private/protected/public)
 +
}
 +
 
 +
Explanation of symbol::extra_lists[]
 +
{| border="1" cellpadding="3" cellspacing="0" style="border: 1px solid gray; border-collapse: collapse;"
 +
|- style="background: #ececec; border: 1px solid gray;"
 +
! type
 +
! modifiers
 +
! value_type_id
 +
! extra_type
 +
! children
 +
! extra_lists[0]
 +
! extra_lists[1]
 +
! extra_lists[2]
 +
|-
 +
 
 +
|-
 +
| namespace
 +
|
 +
|
 +
|
 +
| declarations in namespace
 +
|"using" namespaces
 +
|
 +
|
 +
|-
  
# Generate a list of all valid symbols in the current scope
+
|-
## Take global list from C::SymbolTable
+
| class / struct / union
## Add in local scope parsed on the fly
+
|
# Reduce that list to what is likely
+
|
# Show that list to the user in some fashion
+
|
# Insert the proper solution on user request
+
| members of class
 +
| base classes
 +
| template args
 +
| friends of class
 +
|-
  
==== Code::SymbolOutline ====
+
|-
==== Code::Refactoring ====
+
| variable
==== Code::Documentation ====
+
| extern, static, volatile, const
 +
| type of variable
 +
|
 +
|
 +
|
 +
|
 +
|
 +
|-
 +
 
 +
|-
 +
| function
 +
| static, inline, const ...
 +
| returned value
 +
|
 +
| arguments
 +
| template arguments
 +
|
 +
|
 +
|-
 +
 
 +
|-
 +
| typedef
 +
| pointer, array, reference, pointer_to_member
 +
| base type
 +
| type of class in pointer_to_member
 +
|
 +
|
 +
|
 +
|
 +
|-
 +
 
 +
|-
 +
| enum
 +
|
 +
|
 +
|
 +
| items in enum
 +
|
 +
|
 +
|
 +
|-
 +
 
 +
|-
 +
| enum item
 +
|
 +
|
 +
| id of enum
 +
|
 +
|
 +
|
 +
|
 +
|-
 +
 
 +
|-
 +
| macro
 +
|
 +
|
 +
|
 +
| macro parts
 +
|
 +
|
 +
|
 +
|-
 +
 
 +
|-
 +
| macro part
 +
| arg_to_string, va_args
 +
| number of arg or -1
 +
|
 +
|
 +
|
 +
|
 +
|
 +
|-
 +
 
 +
|}
 +
 
 +
Comments:
 +
* Such representation would require some extra "hidden" symbols - for example when some complex type is returned from function, extra symbol of typedef representing proper value would be required.
 +
* Also in case of templates, typeid's should be threated in special way - negative value could mean to use template argument instead of some real type. Base types (the POD ones) should have some predefined type ids.
 +
 
 +
Questions:
 +
* why are we storing filepos_end? Wouldn't it be much more useful to store declaration, definition info?
  
 
== More complex cases of C::C usage ==
 
== More complex cases of C::C usage ==
  
Here we can put some more comples examples of c++ code where C::C may fail. Symbols that may be hard to find should be marked in bold
+
Here we can put some more complex examples of c++ code where C::C may fail. Symbols that may be hard to find should be marked in bold
  
=== 1: Fetching type of operator call ===
+
=== Fetching type of operator call ===
  
 
  #include <string>
 
  #include <string>
Line 66: Line 231:
 
     ( string("first") + "second" + "third" ) . '''c_str'''();
 
     ( string("first") + "second" + "third" ) . '''c_str'''();
 
     return 0;
 
     return 0;
 +
}
 +
 +
=== Template classes ===
 +
 +
template<typename T> class Template
 +
{
 +
public:
 +
    T& GetInstance() { return m_Instance; }
 +
private:
 +
    T m_Instance;
 +
};
 +
 +
class Parameter
 +
{
 +
public:
 +
    void PrintfText() { printf("Text"); }
 +
};
 +
 +
int main(int,char**)
 +
{
 +
    Template<Parameter> Object;
 +
    Object.GetInstance().'''PrintfText'''();
 +
}
 +
 +
=== Automated deduction of template arguments from passed function arguments ===
 +
 +
#include <stdio.h>
 +
 +
class Class
 +
{
 +
public:
 +
void SomeFunction()  { printf("SomeFunction\n"); }
 +
};
 +
 +
template< class T > T& Function( T& arg )
 +
{
 +
return arg;
 +
}
 +
 +
int main( int, char** )
 +
{
 +
Class f;
 +
Function(f).'''SomeFunction'''();
 +
return 0;
 +
}
 +
 +
=== Template arguments for templates ===
 +
 +
#include <stdio.h>
 +
 +
template< template<class> class InternalTemplate, typename Type > class Test
 +
{
 +
    public:
 +
 +
        InternalTemplate<Type> m_Member;
 +
};
 +
 +
template< class T > class TestInt
 +
{
 +
    public:
 +
 +
        T m_IntMember;
 +
};
 +
 +
class Class
 +
{
 +
    public:
 +
 +
        void Print() { printf("Class::Print\n"); }
 +
};
 +
 +
int main( int, char** )
 +
{
 +
    Test<TestInt,Class> Object;
 +
    Object.m_Member.'''m_IntMember'''.'''Print'''();
 +
    return 0;
 +
}
 +
 +
=== Partial specializations ===
 +
 +
#include <stdio.h>
 +
 +
template< class T > class Template
 +
{
 +
    public:
 +
        void Print() { printf("Generic specialization\n"); }
 +
};
 +
 +
template<> class Template<float>
 +
{
 +
    public:
 +
        void PrintFloat() { printf("Partial specialization\n"); }
 +
};
 +
 +
int main(int,char**)
 +
{
 +
    Template<int> TInt;
 +
    TInt.'''Print'''();        // PrintFloat() should not be available here
 +
 +
    Template<float> TFloat;
 +
    TFloat.'''PrintFloat'''();  // Print() should not be available here
 +
 +
    return 0;
 +
}
 +
 +
=== Advanced template argument deduction ===
 +
 +
#include <stdio.h>
 +
 +
// Class encapsulating function without arguments
 +
class Caller0
 +
{
 +
        void (* m_Func )( void );
 +
 +
    public:
 +
 +
        Caller0( void (* Func )( void ) ) : m_Func(Func) {}
 +
 +
        void Call0() { m_Func(); }
 +
};
 +
 +
// Template for class encapsulating function with one argument
 +
template < class T > class Caller1
 +
{
 +
        void (* m_Func )( T );
 +
 +
    public:
 +
 +
        Caller1( void (* Func )( T ) ) : m_Func(Func) {}
 +
 +
        void Call1() { m_Func( T() ); }
 +
};
 +
 +
// Template for class encapsulating function with two arguments
 +
template < class T1, class T2 > class Caller2
 +
{
 +
        void (* m_Func )( T1, T2 );
 +
 +
    public:
 +
 +
        Caller2( void (* Func )( T1, T2 ) ) : m_Func(Func) {}
 +
 +
        void Call2() { m_Func ( T1(), T2() ); }
 +
};
 +
 +
// This one will be used for functions without arguments
 +
Caller0 Invoke( void (* Func )( void ) )
 +
{
 +
    return Caller0( Func );
 +
}
 +
 +
// This one will be used for functions with one argument
 +
template < class T > Caller1<T> Invoke( void (* Func )( T ) )
 +
{
 +
    return Caller1<T>( Func );
 +
}
 +
 +
// This one will be used for functions with two arguments
 +
template < class T1, class T2 > Caller2<T1,T2> Invoke( void (* Func )( T1, T2 ) )
 +
{
 +
    return Caller2<T1,T2> ( Func );
 +
}
 +
 +
void Function0(void)
 +
{
 +
    printf("Function0\n");
 +
}
 +
 +
void Function1(float)
 +
{
 +
    printf("Function1\n");
 +
}
 +
 +
void Function2(int)
 +
{
 +
    printf("Function2\n");
 +
}
 +
 +
void Function3(int,float)
 +
{
 +
    printf("Function3\n");
 +
}
 +
 +
int main(int,char**)
 +
{
 +
    Invoke( Function0 ).'''Call0'''();
 +
    Invoke( Function1 ).'''Call1'''();
 +
    Invoke( Function2 ).'''Call1'''();
 +
    Invoke( Function3 ).'''Call2'''();
 +
 +
    Invoke( &Function0 ).'''Call0'''();
 +
    Invoke( &Function1 ).'''Call1'''();
 +
    Invoke( &Function2 ).'''Call1'''();
 +
    Invoke( &Function3 ).'''Call2'''();
 +
    return 0;
 +
}
 +
 +
=== Hidden virtual functions ===
 +
 +
class Base
 +
{
 +
public:
 +
    virtual void stam(int);
 +
};
 +
 +
class Derived : Base
 +
{
 +
public:
 +
    void stam(double, int);
 +
};
 +
 +
main()
 +
{
 +
    Base *pBase = new Base;
 +
    pBase->'''stam('''
 +
            ^ Function tip should show stam(int)
 +
    Derived * pDerived = new Derived;
 +
    pDerived->'''stam('''
 +
                ^ Function tip should show stam(double, int)
 +
                                          Base::stam(int)
 +
}
 +
 +
=== Macros hiding include files ===
 +
in header.h
 +
int x;
 +
in main.cpp
 +
#define myHeader "header.h"
 +
#include myHeader
 +
 +
main()
 +
{
 +
    int '''x''';
 
  }
 
  }

Latest revision as of 13:15, 16 February 2009

Here you'll find some discussion on the Code::Completion rewrite, and some useful links to related materials online.

Background

The current Code::Completion plug-in has some flaws, and is currently development frozen. The current plug-in lacks full support for:

  1. function and class templates
  2. default arguments in some cases
  3. some of the more complicated c++ mucky business

Current Effort

Structure

The current C::C is a monolithic library of features, which could be de-coupled and split up for use in multiple plugins, providing extra functionality and flexibility in the future. Therefore, I propose the C::C be broken up into the following components:

  • Code::SymbolTable
    • Provide a list of valid symbols in the workspace, along with relevant scope information
  • Code::Completion
    • Provide Auto-complete features
  • Code::SymbolOutline
    • Provide Symbol browser, find symbol, function jump features
  • Code::Refactoring
    • Provide code refactoring features
  • Code::Documentation
    • Provide automatic code generation features

Code::Completion

Purpose Statement

The current Code::Completion plugin is outdated, and needs a complete rewrite. The purpose of the Code::Completion plugin is thus:

  • Provide a list of likely symbols in the current scope as possible solutions to the current symbol.
  • Provide function tooltips
    • Parameter list
    • Relevant documentation
  • Provide variable tooltips
    • type and modifiers
    • scope
  • Provide preprocessor tooltips
    • replacement value of a macro
    • parameter list when applicable
  • Provide completion features for class constructors
  • Provide completion features for initializer lists

Process

  • The user requests a completion of the current symbol.
    • Call CC::EventSymbolHover
      • When the mouse hovers over a symbol after a timeout
    • Call CC::EventSymbolTip
      • When the user types in any of the following: . :: ->
      • When the user presses the keystroke CTRL-SPACE
    • Call CC::EventCallTip
      • When the user types in any of the following: ( ,
      • When the user presses the keystroke CTRL-SHIFT-SPACE
    • Call CC::EventPreprocTip
      • When the user types in any of the following: < " when in preproc context (# at the start of the line)
  • the C::C plugin determines the proper scope, when applicable (global, local, class)
  • Compare the current symbol against the symbol table of proper scope.
  • Provide a composite list to the user.

Code::SymbolTable

There will be 3 symbol lists:

  • global namespace
  • local scope (2 options)
    • the local scope can be generated by smartly parsing the current file on every request
    • keep a running count, and add/remove symbols from the table as the file is edited
  • class scope

Here's a good link for further reading on the subject and the problems: On Wikipedia

Data Proposal

class symbol
{
    string name;              // name of the symbol
    int    id;                // Id of the symbol, should be unique in the workspace
    int    file_id;           // Id of file where the symbol has been declared
    int    filepos_begin;     // Position where declaration of the symbol starts
    int    filepos_end;       // Position where declaration of the symbol ends
    int    type;              // Type of the symbol: macro / class / typedef / variable / function
    flags  modifiers;         // Bitfield used to mark some extra properties of symbol
                              // like that it is static or inline
    int    value_type_id;     // Id of symbol which represents c++ type of current symbol
                              // (like type of variable or type of returned value from function)
    int    extra_type_id;     // Extra type used in some cases
    list   children;          // List of child elements of this symbol (members in class etc)
    list   extra_lists[3];    // See table below
    map    extra_values;      // int -> string map which can keep some extra data
}
class list_entry
{
    int    symbol_id;         // ID of the symbol referenced
    int    storage_class;     // Storage class of the symbol (private/protected/public)
}

Explanation of symbol::extra_lists[]

type modifiers value_type_id extra_type children extra_lists[0] extra_lists[1] extra_lists[2]
namespace declarations in namespace "using" namespaces
class / struct / union members of class base classes template args friends of class
variable extern, static, volatile, const type of variable
function static, inline, const ... returned value arguments template arguments
typedef pointer, array, reference, pointer_to_member base type type of class in pointer_to_member
enum items in enum
enum item id of enum
macro macro parts
macro part arg_to_string, va_args number of arg or -1

Comments:

  • Such representation would require some extra "hidden" symbols - for example when some complex type is returned from function, extra symbol of typedef representing proper value would be required.
  • Also in case of templates, typeid's should be threated in special way - negative value could mean to use template argument instead of some real type. Base types (the POD ones) should have some predefined type ids.

Questions:

  • why are we storing filepos_end? Wouldn't it be much more useful to store declaration, definition info?

More complex cases of C::C usage

Here we can put some more complex examples of c++ code where C::C may fail. Symbols that may be hard to find should be marked in bold

Fetching type of operator call

#include <string>
using namespace std;

int main(int,char**)
{
    ( string("first") + "second" + "third" ) . c_str();
    return 0;
}

Template classes

template<typename T> class Template
{
public:
    T& GetInstance() { return m_Instance; }
private:
    T m_Instance;
};

class Parameter
{
public:
    void PrintfText() { printf("Text"); }
};

int main(int,char**)
{
    Template<Parameter> Object;
    Object.GetInstance().PrintfText();
}

Automated deduction of template arguments from passed function arguments

#include <stdio.h>

class Class
{
	public:
		void SomeFunction()  { printf("SomeFunction\n"); }
};

template< class T > T& Function( T& arg )
{
	return arg;
}

int main( int, char** )
{
	Class f;
	Function(f).SomeFunction();
	return 0;
}

Template arguments for templates

#include <stdio.h>

template< template<class> class InternalTemplate, typename Type > class Test
{
    public:

        InternalTemplate<Type> m_Member;
};

template< class T > class TestInt
{
    public:

        T m_IntMember;
};

class Class
{
    public:

        void Print() { printf("Class::Print\n"); }
};

int main( int, char** )
{
    Test<TestInt,Class> Object;
    Object.m_Member.m_IntMember.Print();
    return 0;
}

Partial specializations

#include <stdio.h>

template< class T > class Template
{
    public:
        void Print() { printf("Generic specialization\n"); }
};

template<> class Template<float>
{
    public:
        void PrintFloat() { printf("Partial specialization\n"); }
};

int main(int,char**)
{
    Template<int> TInt;
    TInt.Print();         // PrintFloat() should not be available here

    Template<float> TFloat;
    TFloat.PrintFloat();  // Print() should not be available here

    return 0;
}

Advanced template argument deduction

#include <stdio.h>

// Class encapsulating function without arguments
class Caller0
{
        void (* m_Func )( void );

    public:

        Caller0( void (* Func )( void ) ) : m_Func(Func) {}

        void Call0() { m_Func(); }
};

// Template for class encapsulating function with one argument
template < class T > class Caller1
{
        void (* m_Func )( T );

    public:

        Caller1( void (* Func )( T ) ) : m_Func(Func) {}

        void Call1() { m_Func( T() ); }
};

// Template for class encapsulating function with two arguments
template < class T1, class T2 > class Caller2
{
        void (* m_Func )( T1, T2 );

    public:

        Caller2( void (* Func )( T1, T2 ) ) : m_Func(Func) {}

        void Call2() { m_Func ( T1(), T2() ); }
};

// This one will be used for functions without arguments
Caller0 Invoke( void (* Func )( void ) )
{
    return Caller0( Func );
}

// This one will be used for functions with one argument
template < class T > Caller1<T> Invoke( void (* Func )( T ) )
{
    return Caller1<T>( Func );
}

// This one will be used for functions with two arguments
template < class T1, class T2 > Caller2<T1,T2> Invoke( void (* Func )( T1, T2 ) )
{
    return Caller2<T1,T2> ( Func );
}

void Function0(void)
{
    printf("Function0\n");
}

void Function1(float)
{
    printf("Function1\n");
}

void Function2(int)
{
    printf("Function2\n");
}

void Function3(int,float)
{
    printf("Function3\n");
}

int main(int,char**)
{
    Invoke( Function0 ).Call0();
    Invoke( Function1 ).Call1();
    Invoke( Function2 ).Call1();
    Invoke( Function3 ).Call2();

    Invoke( &Function0 ).Call0();
    Invoke( &Function1 ).Call1();
    Invoke( &Function2 ).Call1();
    Invoke( &Function3 ).Call2();
    return 0;
}

Hidden virtual functions

class Base
{
public:
    virtual void stam(int);
};

class Derived : Base
{
public:
    void stam(double, int);
};

main()
{
    Base *pBase = new Base;
    pBase->stam(
            ^ Function tip should show stam(int)
    Derived * pDerived = new Derived;
    pDerived->stam(
               ^ Function tip should show stam(double, int)
                                          Base::stam(int)
}

Macros hiding include files

in header.h

int x;

in main.cpp

#define myHeader "header.h"
#include myHeader

main()
{
    int x;
}