Difference between revisions of "Code::Completion Rewrite"
m (add category) |
|||
(26 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 7: | Line 11: | ||
== Current Effort == | == 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: |
− | Also to | + | |
+ | * 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: | ||
+ | [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 | ||
+ | | | ||
+ | | | ||
+ | |- | ||
+ | |||
+ | |- | ||
+ | | 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 == | ||
− | Here we can put some more | + | 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 25: | 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:
- 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; }