Code::Completion Rewrite: Difference between revisions
Stevenkaras (talk | contribs) |
m add category |
||
| (19 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 | ||
=== | === Code::Completion === | ||
==== | ==== Purpose Statement ==== | ||
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 31: | 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 | ||
=== Process === | ==== 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: | |||
[http://en.wikipedia.org/wiki/C%2B%2B#Parsing_and_processing_C.2B.2B_source_code On Wikipedia] | |||
==== Data Proposal ==== | |||
class symbol | class symbol | ||
{ | { | ||
string name; // name of the symbol | string name; // name of the symbol | ||
int id; // Id of the symbol, should be unique in the | int id; // Id of the symbol, should be unique in the workspace | ||
int file_id; // | int file_id; // Id of file where the symbol has been declared | ||
int filepos_begin; // Position where declaration of the symbol starts | int filepos_begin; // Position where declaration of the symbol starts | ||
int filepos_end; // Position where declaration of the symbol ends | int filepos_end; // Position where declaration of the symbol ends | ||
int type; // Type of the symbol: macro / class / typedef / variable / function | int type; // Type of the symbol: macro / class / typedef / variable / function | ||
flags modifiers; // Bitfield used to mark some extra properties of symbol | |||
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) | // 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 | int extra_type_id; // Extra type used in some cases | ||
list children; // List of child elements of this symbol (members in class etc) | list children; // List of child elements of this symbol (members in class etc) | ||
list extra_lists[3]; // | list extra_lists[3]; // See table below | ||
map extra_values; // int -> string map which can keep some extra data | 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 | |||
| | |||
| | |||
|- | |||
|- | |||
| 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 == | == More complex cases of C::C usage == | ||
| Line 67: | Line 222: | ||
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 | 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> | #include <string> | ||
| Line 78: | Line 233: | ||
} | } | ||
=== | === Template classes === | ||
template<typename T> class Template | template<typename T> class Template | ||
| Line 98: | Line 253: | ||
Template<Parameter> Object; | Template<Parameter> Object; | ||
Object.GetInstance().'''PrintfText'''(); | 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:
- function and class templates
- default arguments in some cases
- 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)
- Call CC::EventSymbolHover
- 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;
}
