2017-03-25 20:15:00 +00:00
// TODO: cleanup includes
2017-07-30 04:24:02 +00:00
# include "buffer.h"
2017-04-09 02:24:32 +00:00
# include "cache.h"
2017-05-27 04:21:00 +00:00
# include "clang_complete.h"
2017-04-08 22:54:36 +00:00
# include "file_consumer.h"
2017-05-21 19:51:15 +00:00
# include "match.h"
2017-05-27 04:21:00 +00:00
# include "include_complete.h"
2017-05-22 05:54:27 +00:00
# include "ipc_manager.h"
2017-02-25 23:59:09 +00:00
# include "indexer.h"
2017-07-30 04:24:02 +00:00
# include "message_queue.h"
2017-02-25 23:59:09 +00:00
# include "query.h"
2017-06-15 05:32:23 +00:00
# include "query_utils.h"
2017-03-05 02:16:23 +00:00
# include "language_server_api.h"
2017-06-15 05:32:23 +00:00
# include "lex_utils.h"
2017-04-09 02:24:32 +00:00
# include "options.h"
2017-03-26 21:40:34 +00:00
# include "project.h"
2017-03-25 20:27:28 +00:00
# include "platform.h"
2017-07-30 04:24:02 +00:00
# include "serializer.h"
2017-05-21 07:37:53 +00:00
# include "standard_includes.h"
2017-03-10 07:06:01 +00:00
# include "test.h"
2017-03-25 20:15:00 +00:00
# include "timer.h"
# include "threaded_queue.h"
2017-03-26 21:40:34 +00:00
# include "working_files.h"
2017-03-05 02:16:23 +00:00
2017-07-28 02:14:33 +00:00
# include <loguru.hpp>
2017-07-30 04:24:02 +00:00
# include "tiny-process-library/process.hpp"
2017-03-25 20:27:28 +00:00
# include <doctest/doctest.h>
# include <rapidjson/istreamwrapper.h>
# include <rapidjson/ostreamwrapper.h>
2017-06-10 01:02:48 +00:00
# include <climits>
2017-03-31 05:30:50 +00:00
# include <fstream>
2017-07-30 04:24:02 +00:00
# include <future>
2017-04-17 01:22:59 +00:00
# include <functional>
2017-03-25 20:27:28 +00:00
# include <iostream>
2017-05-21 07:37:53 +00:00
# include <iterator>
2017-03-25 20:27:28 +00:00
# include <string>
2017-05-17 07:08:45 +00:00
# include <sstream>
2017-03-25 20:27:28 +00:00
# include <unordered_map>
# include <thread>
# include <vector>
2017-02-25 23:59:09 +00:00
2017-04-11 07:29:36 +00:00
// TODO: provide a feature like 'https://github.com/goldsborough/clang-expand',
// ie, a fully linear view of a function with inline function calls expanded.
// We can probably use vscode decorators to achieve it.
2017-05-20 19:31:07 +00:00
// TODO: implement ThreadPool type which monitors CPU usage / number of work items
// per second completed and scales up/down number of running threads.
2017-07-30 04:24:02 +00:00
//namespace {
2017-04-09 02:24:32 +00:00
2017-04-21 04:06:15 +00:00
std : : vector < std : : string > kEmptyArgs ;
2017-04-16 19:02:29 +00:00
2017-05-20 19:31:07 +00:00
// Expected client version. We show an error if this doesn't match.
2017-06-14 07:13:26 +00:00
const int kExpectedClientVersion = 3 ;
2017-04-16 19:02:29 +00:00
2017-05-20 08:07:29 +00:00
// Cached completion information, so we can give fast completion results when
// the user erases a character. vscode will resend the completion request if
// that happens.
struct CodeCompleteCache {
2017-06-30 06:51:22 +00:00
// NOTE: Make sure to access these variables under |WithLock|.
optional < std : : string > cached_path_ ;
optional < lsPosition > cached_completion_position_ ;
NonElidedVector < lsCompletionItem > cached_results_ ;
2017-05-21 23:48:21 +00:00
2017-06-30 06:51:22 +00:00
std : : mutex mutex_ ;
void WithLock ( std : : function < void ( ) > action ) {
std : : lock_guard < std : : mutex > lock ( mutex_ ) ;
action ( ) ;
}
bool IsCacheValid ( lsTextDocumentPositionParams position ) {
std : : lock_guard < std : : mutex > lock ( mutex_ ) ;
return cached_path_ = = position . textDocument . uri . GetPath ( ) & &
cached_completion_position_ = = position . position ;
2017-05-21 23:48:21 +00:00
}
2017-05-20 08:07:29 +00:00
} ;
2017-04-16 19:02:29 +00:00
2017-05-20 08:07:29 +00:00
// This function returns true if e2e timing should be displayed for the given IpcId.
bool ShouldDisplayIpcTiming ( IpcId id ) {
switch ( id ) {
case IpcId : : TextDocumentPublishDiagnostics :
2017-05-20 21:45:46 +00:00
case IpcId : : CqueryPublishInactiveRegions :
2017-05-20 08:07:29 +00:00
return false ;
default :
return true ;
}
}
2017-04-16 19:02:29 +00:00
2017-05-21 07:37:53 +00:00
2017-04-16 19:02:29 +00:00
2017-04-09 02:24:32 +00:00
2017-04-12 06:41:19 +00:00
void PushBack ( NonElidedVector < lsLocation > * result , optional < lsLocation > location ) {
if ( location )
result - > push_back ( * location ) ;
}
2017-05-27 07:10:21 +00:00
bool FindFileOrFail ( QueryDatabase * db , lsRequestId id , const std : : string & absolute_path , QueryFile * * out_query_file , QueryFileId * out_file_id = nullptr ) {
auto it = db - > usr_to_file . find ( LowerPathIfCaseInsensitive ( absolute_path ) ) ;
2017-05-09 04:20:28 +00:00
if ( it ! = db - > usr_to_file . end ( ) ) {
optional < QueryFile > & file = db - > files [ it - > second . id ] ;
2017-04-22 07:32:29 +00:00
if ( file ) {
2017-05-27 07:10:21 +00:00
* out_query_file = & file . value ( ) ;
if ( out_file_id )
* out_file_id = QueryFileId ( it - > second . id ) ;
return true ;
2017-04-22 07:32:29 +00:00
}
2017-04-11 05:43:01 +00:00
}
2017-05-27 07:10:21 +00:00
if ( out_file_id )
* out_file_id = QueryFileId ( ( size_t ) - 1 ) ;
2017-07-28 02:14:33 +00:00
LOG_S ( INFO ) < < " Unable to find file " < < absolute_path ;
2017-05-27 07:10:21 +00:00
Out_Error out ;
out . id = id ;
out . error . code = lsErrorCodes : : InternalError ;
out . error . message = " Unable to find file " + absolute_path ;
IpcManager : : instance ( ) - > SendOutMessageToClient ( IpcId : : Cout , out ) ;
return false ;
2017-04-11 05:43:01 +00:00
}
2017-05-27 07:10:21 +00:00
QueryFile * FindFile ( QueryDatabase * db , const std : : string & absolute_path ) {
auto it = db - > usr_to_file . find ( LowerPathIfCaseInsensitive ( absolute_path ) ) ;
2017-05-09 04:20:28 +00:00
if ( it ! = db - > usr_to_file . end ( ) ) {
optional < QueryFile > & file = db - > files [ it - > second . id ] ;
2017-04-22 07:32:29 +00:00
if ( file )
return & file . value ( ) ;
}
2017-04-09 02:24:32 +00:00
return nullptr ;
}
2017-04-15 05:14:05 +00:00
2017-05-20 21:45:46 +00:00
void PublishInactiveLines ( WorkingFile * working_file , const std : : vector < Range > & inactive ) {
Out_CquerySetInactiveRegion out ;
out . params . uri = lsDocumentUri : : FromPath ( working_file - > filename ) ;
for ( Range skipped : inactive ) {
optional < lsRange > ls_skipped = GetLsRange ( working_file , skipped ) ;
if ( ls_skipped )
out . params . inactiveRegions . push_back ( * ls_skipped ) ;
}
IpcManager : : instance ( ) - > SendOutMessageToClient ( IpcId : : CqueryPublishInactiveRegions , out ) ;
}
2017-04-15 05:14:05 +00:00
2017-05-29 23:57:19 +00:00
optional < int > FindIncludeLine ( const std : : vector < std : : string > & lines , const std : : string & full_include_line ) {
//
// This returns an include line. For example,
//
// #include <a> // 0
// #include <c> // 1
//
// Given #include <b>, this will return '1', which means that the
// #include <b> text should be inserted at the start of line 1. Inserting
// at the start of a line allows insertion at both the top and bottom of the
// document.
//
// If the include line is already in the document this returns nullopt.
//
optional < int > last_include_line ;
optional < int > best_include_line ;
// 1 => include line is gt content (ie, it should go after)
// -1 => include line is lt content (ie, it should go before)
int last_line_compare = 1 ;
for ( int line = 0 ; line < ( int ) lines . size ( ) ; + + line ) {
if ( ! StartsWith ( lines [ line ] , " #include " ) ) {
last_line_compare = 1 ;
continue ;
}
last_include_line = line ;
int current_line_compare = full_include_line . compare ( lines [ line ] ) ;
if ( current_line_compare = = 0 )
return nullopt ;
if ( last_line_compare = = 1 & & current_line_compare = = - 1 )
best_include_line = line ;
last_line_compare = current_line_compare ;
}
if ( best_include_line )
return * best_include_line ;
// If |best_include_line| didn't match that means we likely didn't find an
// include which was lt the new one, so put it at the end of the last include
// list.
if ( last_include_line )
return * last_include_line + 1 ;
// No includes, use top of document.
return 0 ;
}
2017-06-29 04:40:30 +00:00
optional < QueryFileId > GetImplementationFile ( QueryDatabase * db , QueryFileId file_id , QueryFile * file ) {
2017-05-29 21:18:35 +00:00
for ( SymbolRef sym : file - > def . outline ) {
switch ( sym . idx . kind ) {
case SymbolKind : : Func : {
optional < QueryFunc > & func = db - > funcs [ sym . idx . idx ] ;
2017-06-29 04:40:30 +00:00
// Note: we ignore the definition if it is in the same file (ie, possibly a header).
if ( func & & func - > def . definition_extent & & func - > def . definition_extent - > path ! = file_id )
2017-05-29 21:18:35 +00:00
return func - > def . definition_extent - > path ;
break ;
}
case SymbolKind : : Var : {
optional < QueryVar > & var = db - > vars [ sym . idx . idx ] ;
2017-06-29 04:40:30 +00:00
// Note: we ignore the definition if it is in the same file (ie, possibly a header).
if ( var & & var - > def . definition_extent & & var - > def . definition_extent - > path ! = file_id )
2017-05-29 21:18:35 +00:00
return db - > vars [ sym . idx . idx ] - > def . definition_extent - > path ;
break ;
}
default :
break ;
}
}
// No associated definition, scan the project for a file in the same
// directory with the same base-name.
std : : string original_path = LowerPathIfCaseInsensitive ( file - > def . path ) ;
std : : string target_path = original_path ;
size_t last = target_path . find_last_of ( ' . ' ) ;
if ( last ! = std : : string : : npos ) {
target_path = target_path . substr ( 0 , last ) ;
}
2017-07-28 02:14:33 +00:00
LOG_S ( INFO ) < < " !! Looking for impl file that starts with " < < target_path ;
2017-06-29 04:40:30 +00:00
2017-05-29 21:18:35 +00:00
for ( auto & entry : db - > usr_to_file ) {
Usr path = entry . first ;
// Do not consider header files for implementation files.
// TODO: make file extensions configurable.
if ( EndsWith ( path , " .h " ) | | EndsWith ( path , " .hpp " ) )
continue ;
if ( StartsWith ( path , target_path ) & & path ! = original_path ) {
return entry . second ;
}
}
return nullopt ;
}
void EnsureImplFile ( QueryDatabase * db , QueryFileId file_id , optional < lsDocumentUri > & impl_uri , optional < QueryFileId > & impl_file_id ) {
if ( ! impl_uri . has_value ( ) ) {
optional < QueryFile > & file = db - > files [ file_id . id ] ;
assert ( file ) ;
2017-06-29 04:40:30 +00:00
impl_file_id = GetImplementationFile ( db , file_id , & file . value ( ) ) ;
2017-05-29 21:18:35 +00:00
if ( ! impl_file_id . has_value ( ) )
impl_file_id = file_id ;
optional < QueryFile > & impl_file = db - > files [ impl_file_id - > id ] ;
if ( impl_file )
impl_uri = lsDocumentUri : : FromPath ( impl_file - > def . path ) ;
else
impl_uri = lsDocumentUri : : FromPath ( file - > def . path ) ;
}
}
optional < lsTextEdit > BuildAutoImplementForFunction ( QueryDatabase * db , WorkingFiles * working_files , WorkingFile * working_file , int default_line , QueryFileId decl_file_id , QueryFileId impl_file_id , QueryFunc & func ) {
for ( const QueryLocation & decl : func . declarations ) {
if ( decl . path ! = decl_file_id )
continue ;
optional < lsRange > ls_decl = GetLsRange ( working_file , decl . range ) ;
if ( ! ls_decl )
continue ;
optional < std : : string > type_name ;
optional < lsPosition > same_file_insert_end ;
if ( func . def . declaring_type ) {
optional < QueryType > & declaring_type = db - > types [ func . def . declaring_type - > id ] ;
if ( declaring_type ) {
type_name = declaring_type - > def . short_name ;
optional < lsRange > ls_type_def_extent = GetLsRange ( working_file , declaring_type - > def . definition_extent - > range ) ;
if ( ls_type_def_extent ) {
same_file_insert_end = ls_type_def_extent - > end ;
same_file_insert_end - > character + = 1 ; // move past semicolon.
}
}
}
std : : string insert_text ;
int newlines_after_name = 0 ;
LexFunctionDeclaration ( working_file - > buffer_content , ls_decl - > start , type_name , & insert_text , & newlines_after_name ) ;
if ( ! same_file_insert_end ) {
same_file_insert_end = ls_decl - > end ;
same_file_insert_end - > line + = newlines_after_name ;
same_file_insert_end - > character = 1000 ;
}
lsTextEdit edit ;
if ( decl_file_id = = impl_file_id ) {
edit . range . start = * same_file_insert_end ;
edit . range . end = * same_file_insert_end ;
edit . newText = " \n \n " + insert_text ;
}
else {
lsPosition best_pos ;
best_pos . line = default_line ;
int best_dist = INT_MAX ;
optional < QueryFile > & file = db - > files [ impl_file_id . id ] ;
assert ( file ) ;
for ( SymbolRef sym : file - > def . outline ) {
switch ( sym . idx . kind ) {
case SymbolKind : : Func : {
optional < QueryFunc > & sym_func = db - > funcs [ sym . idx . idx ] ;
if ( ! sym_func | | ! sym_func - > def . definition_extent )
break ;
for ( QueryLocation & func_decl : sym_func - > declarations ) {
if ( func_decl . path = = decl_file_id ) {
int dist = func_decl . range . start . line - decl . range . start . line ;
if ( abs ( dist ) < abs ( best_dist ) ) {
optional < lsLocation > def_loc = GetLsLocation ( db , working_files , * sym_func - > def . definition_extent ) ;
if ( ! def_loc )
continue ;
best_dist = dist ;
if ( dist > 0 )
best_pos = def_loc - > range . start ;
else
best_pos = def_loc - > range . end ;
}
}
}
break ;
}
case SymbolKind : : Var : {
// TODO: handle vars.
//optional<QueryVar>& var = db->vars[sym.idx.idx];
//if (!var || !var->def.definition_extent)
// continue;
break ;
}
2017-06-17 05:40:03 +00:00
case SymbolKind : : Invalid :
case SymbolKind : : File :
case SymbolKind : : Type :
2017-07-28 02:14:33 +00:00
LOG_S ( WARNING ) < < " Unexpected SymbolKind "
< < static_cast < int > ( sym . idx . kind ) ;
2017-06-17 05:40:03 +00:00
break ;
2017-05-29 21:18:35 +00:00
}
}
edit . range . start = best_pos ;
edit . range . end = best_pos ;
if ( best_dist < 0 )
edit . newText = " \n \n " + insert_text ;
else
edit . newText = insert_text + " \n \n " ;
}
return edit ;
}
return nullopt ;
}
2017-06-10 04:13:16 +00:00
void EmitDiagnostics ( WorkingFiles * working_files , std : : string path , NonElidedVector < lsDiagnostic > diagnostics ) {
// Emit diagnostics.
Out_TextDocumentPublishDiagnostics diagnostic_response ;
diagnostic_response . params . uri = lsDocumentUri : : FromPath ( path ) ;
diagnostic_response . params . diagnostics = diagnostics ;
IpcManager : : instance ( ) - > SendOutMessageToClient ( IpcId : : TextDocumentPublishDiagnostics , diagnostic_response ) ;
// Cache diagnostics so we can show fixits.
2017-06-14 06:29:41 +00:00
working_files - > DoActionOnFile ( path , [ & ] ( WorkingFile * working_file ) {
if ( working_file )
working_file - > diagnostics_ = diagnostics ;
} ) ;
2017-06-10 04:13:16 +00:00
}
2017-04-15 05:14:05 +00:00
2017-06-16 02:28:49 +00:00
// Pre-filters completion responses before sending to vscode. This results in a
// significantly snappier completion experience as vscode is easily overloaded
// when given 1000+ completion items.
void FilterCompletionResponse ( Out_TextDocumentComplete * complete_response ,
const std : : string & complete_text ) {
// Used to inject more completions.
# if false
const size_t kNumIterations = 250 ;
size_t size = complete_response - > result . items . size ( ) ;
complete_response - > result . items . reserve ( size * ( kNumIterations + 1 ) ) ;
for ( size_t iteration = 0 ; iteration < kNumIterations ; + + iteration ) {
for ( size_t i = 0 ; i < size ; + + i ) {
auto item = complete_response - > result . items [ i ] ;
item . label + = " # " + std : : to_string ( iteration ) ;
complete_response - > result . items . push_back ( item ) ;
}
}
# endif
2017-04-15 05:14:05 +00:00
2017-06-16 02:28:49 +00:00
const size_t kMaxResultSize = 100u ;
if ( complete_response - > result . items . size ( ) > kMaxResultSize ) {
//std::cerr << "!!! Filtering " << complete_response->result.items.size() << " results using " << complete_text << std::endl;
2017-04-15 05:14:05 +00:00
2017-06-16 02:28:49 +00:00
complete_response - > result . isIncomplete = true ;
2017-04-15 05:14:05 +00:00
2017-06-16 02:28:49 +00:00
if ( complete_text . empty ( ) ) {
complete_response - > result . items . resize ( kMaxResultSize ) ;
}
else {
NonElidedVector < lsCompletionItem > filtered_result ;
filtered_result . reserve ( kMaxResultSize ) ;
2017-06-29 04:33:52 +00:00
2017-06-29 06:59:38 +00:00
std : : unordered_set < std : : string > inserted ;
inserted . reserve ( kMaxResultSize ) ;
2017-06-16 02:28:49 +00:00
for ( const lsCompletionItem & item : complete_response - > result . items ) {
2017-06-29 04:33:52 +00:00
if ( item . label . find ( complete_text ) ! = std : : string : : npos ) {
2017-06-29 06:59:38 +00:00
// Don't insert the same completion entry.
2017-07-28 02:14:33 +00:00
if ( ! inserted . insert ( item . InsertedContent ( ) ) . second )
2017-06-29 06:59:38 +00:00
continue ;
2017-06-16 02:28:49 +00:00
filtered_result . push_back ( item ) ;
if ( filtered_result . size ( ) > = kMaxResultSize )
break ;
}
}
2017-04-16 19:02:29 +00:00
2017-06-29 04:33:52 +00:00
if ( filtered_result . size ( ) < kMaxResultSize ) {
for ( const lsCompletionItem & item : complete_response - > result . items ) {
if ( SubstringMatch ( complete_text , item . label ) ) {
2017-06-29 06:59:38 +00:00
// Don't insert the same completion entry.
if ( ! inserted . insert ( item . InsertedContent ( ) ) . second )
continue ;
2017-06-29 04:33:52 +00:00
filtered_result . push_back ( item ) ;
if ( filtered_result . size ( ) > = kMaxResultSize )
break ;
}
}
}
2017-06-16 02:28:49 +00:00
complete_response - > result . items = filtered_result ;
}
//std::cerr << "!! Filtering resulted in " << complete_response->result.items.size() << " entries" << std::endl;
}
}
2017-04-16 19:02:29 +00:00
2017-04-15 05:14:05 +00:00
2017-04-09 19:38:52 +00:00
2017-03-25 19:18:25 +00:00
2017-04-08 06:45:28 +00:00
struct Index_DoIdMap {
2017-05-12 06:08:15 +00:00
std : : unique_ptr < IndexFile > current ;
2017-05-17 07:08:45 +00:00
PerformanceImportFile perf ;
2017-05-19 07:02:01 +00:00
bool is_interactive ;
2017-04-08 06:45:28 +00:00
2017-07-30 04:24:02 +00:00
explicit Index_DoIdMap (
std : : unique_ptr < IndexFile > current ,
PerformanceImportFile perf ,
bool is_interactive )
2017-05-16 07:26:26 +00:00
: current ( std : : move ( current ) ) ,
2017-05-19 07:02:01 +00:00
perf ( perf ) ,
is_interactive ( is_interactive ) { }
2017-04-08 06:45:28 +00:00
} ;
struct Index_OnIdMapped {
2017-05-12 06:08:15 +00:00
std : : unique_ptr < IndexFile > previous_index ;
2017-07-30 04:24:02 +00:00
IndexFile * current_index ;
2017-04-08 06:45:28 +00:00
std : : unique_ptr < IdMap > previous_id_map ;
2017-07-30 04:24:02 +00:00
IdMap * current_id_map ;
2017-05-17 07:08:45 +00:00
PerformanceImportFile perf ;
2017-05-19 07:02:01 +00:00
bool is_interactive ;
2017-05-16 07:26:26 +00:00
2017-07-30 04:24:02 +00:00
Index_OnIdMapped (
PerformanceImportFile perf ,
bool is_interactive )
: perf ( perf ) ,
2017-05-19 07:02:01 +00:00
is_interactive ( is_interactive ) { }
2017-04-08 06:45:28 +00:00
} ;
struct Index_OnIndexed {
2017-03-25 19:18:25 +00:00
IndexUpdate update ;
2017-05-17 07:08:45 +00:00
PerformanceImportFile perf ;
2017-05-16 07:26:26 +00:00
2017-05-17 07:08:45 +00:00
explicit Index_OnIndexed (
IndexUpdate & update ,
PerformanceImportFile perf )
2017-07-30 04:24:02 +00:00
: update ( update ) , perf ( perf ) { }
} ;
// IndexRequest is all messages that can be sent from the querydb process to
// the indexer process.
struct IndexProcess_Request {
enum class Type {
kInvalid = 0 ,
kInitialize = 1 ,
kQuit = 2 ,
kIndex = 3
} ;
Type type = Type : : kInvalid ;
optional < Config > initialize_args ;
struct IndexArgs {
std : : string path ;
std : : vector < std : : string > args ; // TODO: make this a string that is parsed lazily.
} ;
optional < IndexArgs > index_args ;
static IndexProcess_Request CreateInitialize ( const Config & config ) {
IndexProcess_Request m ;
m . type = Type : : kInitialize ;
m . initialize_args = config ;
return m ;
}
static IndexProcess_Request CreateQuit ( ) {
IndexProcess_Request m ;
m . type = Type : : kQuit ;
return m ;
}
static IndexProcess_Request CreateIndex ( const std : : string & path , const std : : vector < std : : string > & args ) {
IndexProcess_Request m ;
m . type = Type : : kIndex ;
m . index_args = IndexArgs ( ) ;
m . index_args - > path = path ;
m . index_args - > args = args ;
return m ;
}
} ;
MAKE_REFLECT_TYPE_PROXY ( IndexProcess_Request : : Type , int ) ;
MAKE_REFLECT_STRUCT ( IndexProcess_Request : : IndexArgs , path , args ) ;
MAKE_REFLECT_STRUCT ( IndexProcess_Request , type , initialize_args , index_args ) ;
using Index_IndexProcess_Request_IndexQueue = ThreadedQueue < IndexProcess_Request : : IndexArgs > ;
// IndexResponse is all messages that can be sent from the indexer process to
// the querydb process.
struct IndexProcess_Response {
enum class Type {
kInvalid = 0 ,
kShutdown = 1 ,
kIndexResult = 2
} ;
Type type ;
struct IndexResultArgs {
std : : string file_path ;
PerformanceImportFile perf ;
} ;
optional < IndexResultArgs > index_result_args ;
static IndexProcess_Response CreateShutdown ( ) {
IndexProcess_Response response ;
response . type = Type : : kShutdown ;
return response ;
}
static IndexProcess_Response CreateIndexResult ( const std : : string & file_path , const PerformanceImportFile & perf ) {
IndexProcess_Response response ;
response . type = Type : : kIndexResult ;
response . index_result_args = IndexResultArgs ( ) ;
response . index_result_args - > file_path = file_path ;
response . index_result_args - > perf = perf ;
return response ;
2017-05-16 07:26:26 +00:00
}
2017-03-25 19:18:25 +00:00
} ;
2017-07-30 04:24:02 +00:00
MAKE_REFLECT_TYPE_PROXY ( IndexProcess_Response : : Type , int ) ;
MAKE_REFLECT_STRUCT ( IndexProcess_Response : : IndexResultArgs , file_path , perf ) ;
MAKE_REFLECT_STRUCT ( IndexProcess_Response , type , index_result_args ) ;
2017-03-25 19:18:25 +00:00
2017-07-30 04:24:02 +00:00
using IndexProcess_ResponseQueue = ThreadedQueue < IndexProcess_Response : : IndexResultArgs > ;
2017-04-08 20:00:08 +00:00
using Index_DoIdMapQueue = ThreadedQueue < Index_DoIdMap > ;
using Index_OnIdMappedQueue = ThreadedQueue < Index_OnIdMapped > ;
using Index_OnIndexedQueue = ThreadedQueue < Index_OnIndexed > ;
2017-03-25 19:18:25 +00:00
void RegisterMessageTypes ( ) {
2017-03-25 21:02:45 +00:00
MessageRegistry : : instance ( ) - > Register < Ipc_CancelRequest > ( ) ;
MessageRegistry : : instance ( ) - > Register < Ipc_InitializeRequest > ( ) ;
MessageRegistry : : instance ( ) - > Register < Ipc_InitializedNotification > ( ) ;
2017-05-03 06:45:10 +00:00
MessageRegistry : : instance ( ) - > Register < Ipc_Exit > ( ) ;
2017-03-26 06:47:59 +00:00
MessageRegistry : : instance ( ) - > Register < Ipc_TextDocumentDidOpen > ( ) ;
MessageRegistry : : instance ( ) - > Register < Ipc_TextDocumentDidChange > ( ) ;
MessageRegistry : : instance ( ) - > Register < Ipc_TextDocumentDidClose > ( ) ;
2017-04-10 00:08:54 +00:00
MessageRegistry : : instance ( ) - > Register < Ipc_TextDocumentDidSave > ( ) ;
2017-04-14 08:21:03 +00:00
MessageRegistry : : instance ( ) - > Register < Ipc_TextDocumentRename > ( ) ;
2017-03-26 06:47:59 +00:00
MessageRegistry : : instance ( ) - > Register < Ipc_TextDocumentComplete > ( ) ;
2017-05-15 07:28:53 +00:00
MessageRegistry : : instance ( ) - > Register < Ipc_TextDocumentSignatureHelp > ( ) ;
2017-04-03 02:21:21 +00:00
MessageRegistry : : instance ( ) - > Register < Ipc_TextDocumentDefinition > ( ) ;
2017-04-14 06:43:50 +00:00
MessageRegistry : : instance ( ) - > Register < Ipc_TextDocumentDocumentHighlight > ( ) ;
2017-04-14 05:18:02 +00:00
MessageRegistry : : instance ( ) - > Register < Ipc_TextDocumentHover > ( ) ;
2017-04-10 05:34:06 +00:00
MessageRegistry : : instance ( ) - > Register < Ipc_TextDocumentReferences > ( ) ;
2017-03-25 21:02:45 +00:00
MessageRegistry : : instance ( ) - > Register < Ipc_TextDocumentDocumentSymbol > ( ) ;
2017-05-21 04:30:59 +00:00
MessageRegistry : : instance ( ) - > Register < Ipc_TextDocumentDocumentLink > ( ) ;
2017-05-20 19:31:07 +00:00
MessageRegistry : : instance ( ) - > Register < Ipc_TextDocumentCodeAction > ( ) ;
2017-03-25 21:02:45 +00:00
MessageRegistry : : instance ( ) - > Register < Ipc_TextDocumentCodeLens > ( ) ;
MessageRegistry : : instance ( ) - > Register < Ipc_CodeLensResolve > ( ) ;
MessageRegistry : : instance ( ) - > Register < Ipc_WorkspaceSymbol > ( ) ;
2017-04-21 04:50:31 +00:00
MessageRegistry : : instance ( ) - > Register < Ipc_CqueryFreshenIndex > ( ) ;
2017-05-24 07:17:29 +00:00
MessageRegistry : : instance ( ) - > Register < Ipc_CqueryTypeHierarchyTree > ( ) ;
MessageRegistry : : instance ( ) - > Register < Ipc_CqueryCallTreeInitial > ( ) ;
MessageRegistry : : instance ( ) - > Register < Ipc_CqueryCallTreeExpand > ( ) ;
2017-05-07 06:56:04 +00:00
MessageRegistry : : instance ( ) - > Register < Ipc_CqueryVars > ( ) ;
MessageRegistry : : instance ( ) - > Register < Ipc_CqueryCallers > ( ) ;
MessageRegistry : : instance ( ) - > Register < Ipc_CqueryBase > ( ) ;
MessageRegistry : : instance ( ) - > Register < Ipc_CqueryDerived > ( ) ;
2017-03-12 00:36:00 +00:00
}
2017-03-02 09:28:07 +00:00
2017-04-16 19:02:29 +00:00
2017-07-30 04:24:02 +00:00
# if false
TODO : re - enable
void PriorityEnqueueFileForIndex ( QueryDatabase * db , Project * project , Index_DoIndexQueue * queue_do_index , WorkingFile * working_file , const std : : string & path ) {
// Only do a delta update (Type::Parse) if we've already imported the
// file. If the user saves a file not loaded by the project we don't
// want the initial import to be a delta-update.
Index_DoIndex : : Type index_type = Index_DoIndex : : Type : : Parse ;
// TODO/FIXME: this is racy. we need to check if the file is already in the import pipeline. So we should change PriorityEnqueue to look at existing contents before appending. That's not a full fix tho.
QueryFile * file = FindFile ( db , path ) ;
if ( ! file )
index_type = Index_DoIndex : : Type : : ImportThenParse ;
queue_do_index - > PriorityEnqueue ( Index_DoIndex ( index_type , project - > FindCompilationEntryForFile ( path ) , working_file - > buffer_content , true /*is_interactive*/ ) ) ;
}
# endif
2017-04-23 20:02:41 +00:00
2017-06-17 02:42:14 +00:00
void InsertSymbolIntoResult ( QueryDatabase * db , WorkingFiles * working_files , SymbolIdx symbol , std : : vector < lsSymbolInformation > * result ) {
optional < lsSymbolInformation > info = GetSymbolInfo ( db , working_files , symbol ) ;
if ( ! info )
return ;
optional < QueryLocation > location = GetDefinitionExtentOfSymbol ( db , symbol ) ;
if ( ! location ) {
auto decls = GetDeclarationsOfSymbolForGotoDefinition ( db , symbol ) ;
if ( decls . empty ( ) )
return ;
location = decls [ 0 ] ;
}
2017-04-23 20:02:41 +00:00
2017-06-17 02:42:14 +00:00
optional < lsLocation > ls_location = GetLsLocation ( db , working_files , * location ) ;
if ( ! ls_location )
return ;
info - > location = * ls_location ;
result - > push_back ( * info ) ;
}
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
// Manages loading caches from file paths for the indexer process.
struct CacheLoader {
explicit CacheLoader ( Config * config ) : config_ ( config ) { }
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
IndexFile * TryLoad ( const std : : string & path ) {
auto it = caches . find ( path ) ;
if ( it ! = caches . end ( ) )
return it - > second . get ( ) ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
std : : unique_ptr < IndexFile > cache = LoadCachedIndex ( config_ , path ) ;
if ( ! cache )
return nullptr ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
caches [ path ] = std : : move ( cache ) ;
return caches [ path ] . get ( ) ;
}
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
std : : unordered_map < std : : string , std : : unique_ptr < IndexFile > > caches ;
Config * config_ ;
} ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
// Maintains the currently indexed file cache for the querydb process. This is
// needed for delta index updates.
struct CacheManager {
struct Entry {
std : : unique_ptr < IndexFile > file ;
std : : unique_ptr < IdMap > ids ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
Entry ( std : : unique_ptr < IndexFile > file , std : : unique_ptr < IdMap > ids )
: file ( std : : move ( file ) ) , ids ( std : : move ( ids ) ) { }
} ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
Entry * TryGet ( const std : : string & path ) {
auto it = files_ . find ( path ) ;
if ( it ! = files_ . end ( ) )
return it - > second . get ( ) ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
return nullptr ;
}
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
std : : unique_ptr < Entry > UpdateAndReturnOldFile ( std : : unique_ptr < Entry > entry ) {
auto & it = files_ . find ( entry - > file - > path ) ;
if ( it ! = files_ . end ( ) ) {
std : : unique_ptr < Entry > old = std : : move ( it - > second ) ;
it - > second = std : : move ( entry ) ;
return old ;
}
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
files_ [ entry - > file - > path ] = std : : move ( entry ) ;
return nullptr ;
}
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
Config * config_ ;
std : : unordered_map < std : : string , std : : unique_ptr < Entry > > files_ ;
} ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
struct IndexManager {
std : : unordered_set < std : : string > files_being_indexed_ ;
std : : mutex mutex_ ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
// Marks a file as being indexed. Returns true if the file is not already
// being indexed.
bool MarkIndex ( const std : : string & path ) {
std : : lock_guard < std : : mutex > lock ( mutex_ ) ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
return files_being_indexed_ . insert ( path ) . second ;
}
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
// Unmarks a file as being indexed, so it can get indexed again in the
// future.
void ClearIndex ( const std : : string & path ) {
std : : lock_guard < std : : mutex > lock ( mutex_ ) ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
auto it = files_being_indexed_ . find ( path ) ;
assert ( it ! = files_being_indexed_ . end ( ) ) ;
files_being_indexed_ . erase ( it ) ;
}
} ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
//} // namespace
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
bool IndexMain_DoIndex ( Config * config ,
FileConsumer : : SharedState * file_consumer_shared ,
Project * project ,
WorkingFiles * working_files ,
clang : : Index * index ,
IndexProcess_ResponseQueue * queue_index_response ,
Index_DoIdMapQueue * queue_do_id_map ) {
optional < IndexProcess_Response : : IndexResultArgs > request =
queue_index_response - > TryDequeue ( ) ;
if ( ! request )
return false ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
std : : unique_ptr < IndexFile > current_index =
LoadCachedIndex ( config , request - > file_path ) ;
if ( ! current_index ) {
std : : cerr < < " !!! Failed to load index for " + request - > file_path + " \n " ;
return false ;
}
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
assert ( current_index ) ;
// assert(previous_index);
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
// TODO: get real value for is_interactive
Index_DoIdMap response ( std : : move ( current_index ) , request - > perf ,
false /*is_interactive*/ ) ;
queue_do_id_map - > Enqueue ( std : : move ( response ) ) ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
return true ;
}
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
bool IndexMain_DoCreateIndexUpdate (
Index_OnIdMappedQueue * queue_on_id_mapped ,
Index_OnIndexedQueue * queue_on_indexed ) {
optional < Index_OnIdMapped > response = queue_on_id_mapped - > TryDequeue ( ) ;
if ( ! response )
return false ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
Timer time ;
IndexUpdate update = IndexUpdate : : CreateDelta ( response - > previous_id_map . get ( ) , response - > current_id_map ,
response - > previous_index . get ( ) , response - > current_index ) ;
response - > perf . index_make_delta = time . ElapsedMicrosecondsAndReset ( ) ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
# if false
# define PRINT_SECTION(name) \
if ( response - > perf . name ) { \
total + = response - > perf . name ; \
output < < " " < < # name < < " : " < < FormatMicroseconds ( response - > perf . name ) ; \
}
std : : stringstream output ;
long long total = 0 ;
output < < " [perf] " ;
PRINT_SECTION ( index_parse ) ;
PRINT_SECTION ( index_build ) ;
PRINT_SECTION ( index_save_to_disk ) ;
PRINT_SECTION ( index_load_cached ) ;
PRINT_SECTION ( querydb_id_map ) ;
PRINT_SECTION ( index_make_delta ) ;
output < < " \n total: " < < FormatMicroseconds ( total ) ;
output < < " path: " < < response - > current_index - > path ;
output < < std : : endl ;
std : : cerr < < output . rdbuf ( ) ;
# undef PRINT_SECTION
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
if ( response - > is_interactive )
std : : cerr < < " Applying IndexUpdate " < < std : : endl < < update . ToString ( ) < < std : : endl ;
# endif
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
Index_OnIndexed reply ( update , response - > perf ) ;
queue_on_indexed - > Enqueue ( std : : move ( reply ) ) ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
return true ;
}
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
bool IndexMergeIndexUpdates ( Index_OnIndexedQueue * queue_on_indexed ) {
optional < Index_OnIndexed > root = queue_on_indexed - > TryDequeue ( ) ;
if ( ! root )
return false ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
bool did_merge = false ;
while ( true ) {
optional < Index_OnIndexed > to_join = queue_on_indexed - > TryDequeue ( ) ;
if ( ! to_join ) {
queue_on_indexed - > Enqueue ( std : : move ( * root ) ) ;
return did_merge ;
}
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
did_merge = true ;
//Timer time;
root - > update . Merge ( to_join - > update ) ;
//time.ResetAndPrint("[indexer] Joining two querydb updates");
}
}
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
void IndexMain ( Config * config ,
FileConsumer : : SharedState * file_consumer_shared ,
Project * project ,
WorkingFiles * working_files ,
MultiQueueWaiter * waiter ,
IndexProcess_ResponseQueue * queue_index_response ,
Index_DoIdMapQueue * queue_do_id_map ,
Index_OnIdMappedQueue * queue_on_id_mapped ,
Index_OnIndexedQueue * queue_on_indexed ) {
SetCurrentThreadName ( " indexer " ) ;
// TODO: dispose of index after it is not used for a while.
clang : : Index index ( 1 , 0 ) ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
while ( true ) {
// TODO: process all off IndexMain_DoIndex before calling
// IndexMain_DoCreateIndexUpdate for
// better icache behavior. We need to have some threads spinning on
// both though
// otherwise memory usage will get bad.
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
// We need to make sure to run both IndexMain_DoIndex and
// IndexMain_DoCreateIndexUpdate so we don't starve querydb from doing any
// work. Running both also lets the user query the partially constructed
// index.
bool did_index =
IndexMain_DoIndex ( config , file_consumer_shared , project , working_files ,
& index , queue_index_response , queue_do_id_map ) ;
bool did_create_update =
IndexMain_DoCreateIndexUpdate ( queue_on_id_mapped , queue_on_indexed ) ;
bool did_merge = false ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
// Nothing to index and no index updates to create, so join some already
// created index updates to reduce work on querydb thread.
if ( ! did_index & & ! did_create_update )
did_merge = IndexMergeIndexUpdates ( queue_on_indexed ) ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
// We didn't do any work, so wait for a notification.
if ( ! did_index & & ! did_create_update & & ! did_merge )
waiter - > Wait (
{ queue_index_response , queue_on_id_mapped , queue_on_indexed } ) ;
}
}
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
struct IQueryDbResponder {
virtual void Write ( IndexProcess_Response response ) = 0 ;
} ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
struct OutOfProcessQueryDbResponder : IQueryDbResponder {
void Write ( IndexProcess_Response response ) override {
rapidjson : : StringBuffer output ;
rapidjson : : PrettyWriter < rapidjson : : StringBuffer > writer ( output ) ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
Reflect ( writer , response ) ;
std : : cerr < < " !! Wrote to querydb " + std : : string ( output . GetString ( ) ) + " \n " ;
std : : cout < < output . GetSize ( ) < < " \n \n " < < output . GetString ( ) ;
}
} ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
struct InProcessQueryDbResponder : IQueryDbResponder {
IndexProcess_ResponseQueue * queue_ ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
InProcessQueryDbResponder ( IndexProcess_ResponseQueue * queue )
: queue_ ( queue ) { }
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
void Write ( IndexProcess_Response response ) override {
if ( response . index_result_args )
queue_ - > Enqueue ( std : : move ( * response . index_result_args ) ) ;
}
} ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
optional < std : : string > ReadContentFromSource ( std : : function < optional < char > ( ) > read ) {
// Read the content length. It is terminated by two \n\n characters.
std : : string stringified_content_length ;
char last = 0 ;
while ( true ) {
optional < char > opt_c = read ( ) ;
if ( ! opt_c )
return nullopt ;
char c = * opt_c ;
if ( last = = ' \n ' & & c = = ' \n ' )
break ;
last = c ;
stringified_content_length + = c ;
}
int content_length = atoi ( stringified_content_length . c_str ( ) ) ;
// Read content.
std : : string content ;
content . reserve ( content_length ) ;
for ( size_t i = 0 ; i < content_length ; + + i ) {
char c ;
std : : cin . read ( & c , 1 ) ;
content + = c ;
}
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
return content ;
}
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
void PumpIndexThreadStdioReaderMain ( ThreadedQueue < IndexProcess_Request > * messages ) {
// Read content.
optional < std : : string > content = ReadContentFromSource ( [ ] ( ) {
// Bad stdin means parent process has probably exited. Either way, indexer
// process can no longer be communicated with so exit.
if ( ! std : : cin . good ( ) )
exit ( 0 ) ;
char c = 0 ;
std : : cin . read ( & c , 1 ) ;
return c ;
} ) ;
assert ( content ) ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
// Parse content.
rapidjson : : Document document ;
document . Parse ( content - > c_str ( ) , content - > size ( ) ) ;
assert ( ! document . HasParseError ( ) ) ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
// Deserialize content.
IndexProcess_Request message ;
Reflect ( document , message ) ;
assert ( message . type ! = IndexProcess_Request : : Type : : kInvalid ) ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
// Push message to queue.
messages - > Enqueue ( std : : move ( message ) ) ;
}
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
std : : vector < IndexProcess_Response > DoParseFile (
Config * config ,
clang : : Index * index ,
FileConsumer : : SharedState * file_consumer_shared ,
CacheLoader * cache_loader ,
const std : : string & path ,
const std : : vector < std : : string > & args ) {
std : : vector < IndexProcess_Response > result ;
IndexFile * previous_index = cache_loader - > TryLoad ( path ) ;
if ( previous_index ) {
// If none of the dependencies have changed, skip parsing and just load from cache.
auto file_needs_parse = [ & ] ( const std : : string & path ) {
int64_t modification_timestamp = GetLastModificationTime ( path ) ;
// PERF: We don't need to fully deserialize the file from cache here; we
// need to load it into memory but after that we can just check the
// timestamp. We may want to actually introduce a third file, so we have
// foo.cc, foo.cc.index.json, and foo.cc.meta.json and store the
// timestamp in foo.cc.meta.json. We may also want to begin writing
// to separate folders so things are not all in one folder (ie, take the
// first 5 chars as the folder name).
IndexFile * index = cache_loader - > TryLoad ( path ) ;
if ( ! index | | modification_timestamp ! = index - > last_modification_time ) {
file_consumer_shared - > Reset ( path ) ;
return true ;
}
return false ;
} ;
// Check timestamps and update |file_consumer_shared|.
bool needs_reparse = file_needs_parse ( path ) ;
for ( const std : : string & dependency : previous_index - > dependencies ) {
if ( file_needs_parse ( dependency ) ) {
LOG_S ( INFO ) < < " Timestamp has changed for " < < dependency ;
needs_reparse = true ;
// SUBTLE: Do not break here, as |file_consumer_shared| is updated
// inside of |file_needs_parse|.
}
}
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
// No timestamps changed - load directly from cache.
if ( ! needs_reparse ) {
LOG_S ( INFO ) < < " Skipping parse; no timestamp change for " < < path ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
// TODO/FIXME: real perf
PerformanceImportFile perf ;
result . push_back ( IndexProcess_Response : : CreateIndexResult ( path , perf ) ) ;
for ( const std : : string & dependency : previous_index - > dependencies ) {
LOG_S ( INFO ) < < " Emitting index result for " < < dependency ;
result . push_back ( IndexProcess_Response : : CreateIndexResult ( dependency , perf ) ) ;
}
return result ;
}
}
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
LOG_S ( INFO ) < < " Parsing " < < path ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
// Load file contents for all dependencies into memory. If the dependencies
// for the file changed we may not end up using all of the files we
// preloaded. If a new dependency was added the indexer will grab the file
// contents as soon as possible.
//
// We do this to minimize the race between indexing a file and capturing the
// file contents.
std : : vector < FileContents > file_contents ;
for ( const auto & it : cache_loader - > caches ) {
const std : : unique_ptr < IndexFile > & index = it . second ;
assert ( index ) ;
optional < std : : string > index_content = ReadContent ( index - > path ) ;
if ( ! index_content ) {
LOG_S ( ERROR ) < < " Failed to preload index content for " < < index - > path ;
continue ;
}
file_contents . push_back ( FileContents ( index - > path , * index_content ) ) ;
}
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
PerformanceImportFile perf ;
std : : vector < std : : unique_ptr < IndexFile > > indexes = Parse (
config , file_consumer_shared ,
path , args , file_contents ,
& perf , index ) ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
for ( std : : unique_ptr < IndexFile > & new_index : indexes ) {
Timer time ;
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
// TODO: don't load cached index. We don't need to do this when indexer always exports dependency tree.
// Sanity check that verifies we did not generate a new index for a file whose timestamp did not change.
//{
// IndexFile* previous_index = cache_loader->TryLoad(new_index->path);
// assert(!previous_index || GetLastModificationTime(new_index->path) != previous_index->last_modification_time);
//}
2017-04-23 20:02:41 +00:00
2017-07-30 04:24:02 +00:00
// Note: we are reusing the parent perf.
perf . index_load_cached = time . ElapsedMicrosecondsAndReset ( ) ;
2017-04-16 19:02:29 +00:00
2017-07-30 04:24:02 +00:00
// Write new cache to disk. Add file to list of paths that need to be reimported.
time . Reset ( ) ;
WriteToCache ( config , new_index - > path , * new_index , new_index - > file_contents_ ) ;
perf . index_save_to_disk = time . ElapsedMicrosecondsAndReset ( ) ;
2017-04-16 19:02:29 +00:00
2017-07-30 04:24:02 +00:00
LOG_S ( INFO ) < < " Emitting index result for " < < new_index - > path ;
result . push_back ( IndexProcess_Response : : CreateIndexResult ( new_index - > path , perf ) ) ;
}
2017-04-16 19:02:29 +00:00
2017-07-30 04:24:02 +00:00
return result ;
}
2017-04-16 19:02:29 +00:00
2017-07-30 04:24:02 +00:00
std : : vector < IndexProcess_Response > ParseFile (
Config * config ,
clang : : Index * index ,
FileConsumer : : SharedState * file_consumer_shared ,
const Project : : Entry & entry ) {
2017-04-16 19:02:29 +00:00
2017-07-30 04:24:02 +00:00
CacheLoader cache_loader ( config ) ;
2017-04-16 19:02:29 +00:00
2017-07-30 04:24:02 +00:00
// Try to determine the original import file by loading the file from cache.
// This lets the user request an index on a header file, which clang will
// complain about if indexed by itself.
IndexFile * entry_cache = cache_loader . TryLoad ( entry . filename ) ;
std : : string tu_path = entry_cache ? entry_cache - > import_file : entry . filename ;
return DoParseFile ( config , index , file_consumer_shared , & cache_loader , tu_path , entry . args ) ;
}
2017-04-16 19:02:29 +00:00
2017-07-30 04:24:02 +00:00
void IndexThreadMain ( Config * config , IQueryDbResponder * responder , Index_IndexProcess_Request_IndexQueue * queue , std : : atomic < int > * busy , FileConsumer : : SharedState * file_consumer_shared ) {
while ( true ) {
IndexProcess_Request : : IndexArgs request = queue - > DequeuePlusAction ( [ & ] ( ) {
+ + ( * busy ) ;
} ) ;
2017-04-16 19:02:29 +00:00
2017-07-30 04:24:02 +00:00
clang : : Index index ( 0 , 0 ) ;
Project : : Entry entry ;
entry . filename = request . path ;
entry . args = request . args ;
std : : vector < IndexProcess_Response > responses = ParseFile ( config , & index , file_consumer_shared , entry ) ;
2017-04-16 19:02:29 +00:00
2017-07-30 04:24:02 +00:00
for ( const auto & response : responses )
responder - > Write ( response ) ;
2017-04-16 19:02:29 +00:00
2017-07-30 04:24:02 +00:00
- - ( * busy ) ;
}
}
2017-04-16 19:02:29 +00:00
2017-07-30 04:24:02 +00:00
struct IIndexerProcess {
virtual void Restart ( ) = 0 ;
virtual void EnableAutoRestart ( ) = 0 ;
virtual void SetConfig ( const Config & config ) = 0 ;
virtual void SendMessage ( IndexProcess_Request message ) = 0 ;
} ;
2017-04-16 19:02:29 +00:00
2017-07-30 04:24:02 +00:00
struct InProcessIndexer : IIndexerProcess {
ThreadedQueue < IndexProcess_Request > * messages_ ;
Index_IndexProcess_Request_IndexQueue queue_ ;
std : : vector < std : : thread > indexer_threads_ ;
std : : atomic < int > num_busy_indexers_ = 0 ;
Config config_ ;
IQueryDbResponder * responder_ ;
// TODO: Remove FileConsumer::SharedState support from indexer. Indexer
// always generates every index since it doesn't take a huge amount of time.
// Then the querydb process decides if the new index is worth importing. At
// some point we can "blacklist" certain files we are definately not
// interested in indexing.
FileConsumer : : SharedState file_consumer_shared_ ;
explicit InProcessIndexer ( IQueryDbResponder * responder , ThreadedQueue < IndexProcess_Request > * messages )
: messages_ ( messages ) , responder_ ( responder ) { }
void Restart ( ) override { } // no-op
void EnableAutoRestart ( ) override { } // no-op
void SetConfig ( const Config & config ) override {
SendMessage ( IndexProcess_Request : : CreateInitialize ( config ) ) ;
}
2017-04-16 19:02:29 +00:00
2017-07-30 04:24:02 +00:00
void SendMessage ( IndexProcess_Request message ) override {
switch ( message . type ) {
case IndexProcess_Request : : Type : : kInitialize : {
config_ = * message . initialize_args ;
for ( int i = 0 ; i < config_ . indexerCount ; + + i ) {
indexer_threads_ . push_back ( std : : thread ( [ & , i ] ( ) {
SetCurrentThreadName ( " indexer " + std : : to_string ( i ) ) ;
IndexThreadMain ( & config_ , responder_ , & queue_ , & num_busy_indexers_ , & file_consumer_shared_ ) ;
} ) ) ;
}
break ;
}
case IndexProcess_Request : : Type : : kIndex : {
// Dispatch the request so one of the indexers will pick it up.
queue_ . Enqueue ( std : : move ( * message . index_args ) ) ;
break ;
}
}
}
2017-04-16 19:02:29 +00:00
2017-07-30 04:24:02 +00:00
bool TryWaitUntilIdle ( ) {
// Wait until all indexers are done running and we have finished all of our work.
while ( num_busy_indexers_ ! = 0 | | ! queue_ . IsEmpty ( ) | | ! messages_ - > IsEmpty ( ) ) {
// There are other messages that need to be processed; start the loop over.
if ( ! messages_ - > IsEmpty ( ) )
return false ;
std : : cerr < < " !! Trying to exit indexer; there are still " + std : : to_string ( num_busy_indexers_ ) + " indexers running \n " ;
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 100 ) ) ;
}
return true ;
}
} ;
2017-04-16 19:02:29 +00:00
2017-07-30 04:24:02 +00:00
struct OutOfProcessIndexer : IIndexerProcess {
struct ProcessState {
std : : unique_ptr < TinyProcessLib : : Process > process ;
std : : string unhandled_output ;
} ;
std : : recursive_mutex processes_mutex_ ;
std : : unordered_map < int , ProcessState > processes_ ;
int next_process_id_ = 0 ;
2017-04-16 19:02:29 +00:00
2017-07-30 04:24:02 +00:00
optional < Config > config_ ;
2017-04-16 19:02:29 +00:00
2017-07-30 04:24:02 +00:00
std : : string bin_name_ ;
IndexProcess_ResponseQueue * response_queue_ ;
2017-04-16 19:02:29 +00:00
2017-07-30 04:24:02 +00:00
const int kMaxIndexRequestsUntilRestart = 25 ;
bool enable_auto_restart_ = false ;
int number_of_index_requests_since_last_restart_ = 0 ;
2017-04-08 06:45:28 +00:00
2017-07-30 04:24:02 +00:00
OutOfProcessIndexer ( const std : : string & bin_name ,
IndexProcess_ResponseQueue * response_queue )
: bin_name_ ( bin_name ) , response_queue_ ( response_queue ) {
CreateIndexProcess ( ) ;
}
2017-04-20 07:53:33 +00:00
2017-07-30 04:24:02 +00:00
void Restart ( ) override {
SendMessage ( IndexProcess_Request : : CreateQuit ( ) ) ;
CreateIndexProcess ( ) ;
number_of_index_requests_since_last_restart_ = 0 ;
}
2017-04-11 05:26:27 +00:00
2017-07-30 04:24:02 +00:00
void EnableAutoRestart ( ) override {
enable_auto_restart_ = true ;
2017-04-24 01:01:51 +00:00
}
2017-07-30 04:24:02 +00:00
void SetConfig ( const Config & config ) override {
assert ( ! config_ ) ;
config_ = config ;
2017-04-24 01:01:51 +00:00
2017-07-30 04:24:02 +00:00
//std::lock_guard<std::recursive_mutex> processes_lock(processes_mutex_);
for ( auto & process : processes_ )
SendMessage ( IndexProcess_Request : : CreateInitialize ( * config_ ) ) ;
}
2017-04-08 20:00:08 +00:00
2017-07-30 04:24:02 +00:00
void SendMessage ( IndexProcess_Request message ) override {
if ( message . type = = IndexProcess_Request : : Type : : kIndex & &
number_of_index_requests_since_last_restart_ + + > kMaxIndexRequestsUntilRestart ) {
Restart ( ) ;
}
2017-04-08 06:45:28 +00:00
2017-07-30 04:24:02 +00:00
std : : string content ;
2017-04-20 05:46:10 +00:00
2017-07-30 04:24:02 +00:00
rapidjson : : StringBuffer output ;
rapidjson : : PrettyWriter < rapidjson : : StringBuffer > writer ( output ) ;
2017-04-20 05:46:10 +00:00
2017-07-30 04:24:02 +00:00
Reflect ( writer , message ) ;
std : : cerr < < " !!! WRITING TO INDEXER: " < < output . GetString ( ) < < std : : endl ;
2017-05-17 07:08:45 +00:00
2017-07-30 04:24:02 +00:00
//std::lock_guard<std::recursive_mutex> processes_lock(processes_mutex_);
processes_ [ next_process_id_ - 1 ] . process - > write ( std : : to_string ( output . GetSize ( ) ) + " \n \n " ) ;
processes_ [ next_process_id_ - 1 ] . process - > write ( output . GetString ( ) , output . GetSize ( ) ) ;
}
2017-04-21 04:06:15 +00:00
2017-07-30 04:24:02 +00:00
void CreateIndexProcess ( ) {
int process_id = next_process_id_ + + ;
ProcessState state ;
state . process = MakeUnique < TinyProcessLib : : Process > (
bin_name_ + std : : string ( " --indexer " ) , " . " ,
[ this , process_id ] ( const char * bytes , size_t n ) { OnStdOut ( process_id , bytes , n ) ; } ,
[ this ] ( const char * bytes , size_t n ) { OnStdErr ( bytes , n ) ; } ,
true /*open_stdin*/ ) ;
2017-04-21 04:06:15 +00:00
2017-07-30 04:24:02 +00:00
//std::lock_guard<std::recursive_mutex> processes_lock(processes_mutex_);
processes_ [ process_id ] = std : : move ( state ) ;
2017-04-24 01:01:51 +00:00
2017-07-30 04:24:02 +00:00
if ( config_ )
SendMessage ( IndexProcess_Request : : CreateInitialize ( * config_ ) ) ;
}
2017-03-25 19:18:25 +00:00
2017-07-30 04:24:02 +00:00
void ParseOutput ( int process_id ) {
//std::lock_guard<std::recursive_mutex> processes_lock(processes_mutex_);
while ( true ) {
size_t next_idx = 0 ;
optional < std : : string > content = ReadContentFromSource ( [ & ] ( ) - > optional < char > {
if ( next_idx > = processes_ [ process_id ] . unhandled_output . size ( ) )
return nullopt ;
return processes_ [ process_id ] . unhandled_output [ next_idx + + ] ;
} ) ;
std : : cerr < < " !! querydb failed to read input; next_idx= " < < next_idx < < std : : endl ;
if ( ! content )
return ;
processes_ [ process_id ] . unhandled_output = processes_ [ process_id ] . unhandled_output . substr ( next_idx ) ;
std : : cerr < < " !! querydb got input " < < * content < < " from process " < < process_id < < std : : endl ;
// Parse content.
rapidjson : : Document document ;
document . Parse ( content - > c_str ( ) , content - > size ( ) ) ;
assert ( ! document . HasParseError ( ) ) ;
// Deserialize content.
IndexProcess_Response message ;
Reflect ( document , message ) ;
// Cleanup state from our side if the process exits.
if ( message . type = = IndexProcess_Response : : Type : : kShutdown ) {
std : : cerr < < " !!! Got process shutdown message !!! \n " ;
// Delete the process on a separate thread, since a thread cannot destroy itself.
//std::async([&, process_id]() {
// std::lock_guard<std::recursive_mutex> processes_lock(processes_mutex_);
// processes_.erase(processes_.find(process_id));
//});
return ;
2017-05-20 21:45:46 +00:00
}
2017-05-11 01:44:39 +00:00
2017-07-30 04:24:02 +00:00
// Push message to queue.
response_queue_ - > Enqueue ( std : : move ( * message . index_result_args ) ) ;
2017-06-20 01:52:25 +00:00
}
2017-07-30 04:24:02 +00:00
}
2017-06-20 01:52:25 +00:00
2017-07-30 04:24:02 +00:00
void OnStdOut ( int process_id , const char * bytes , size_t n ) {
std : : string content ;
for ( size_t i = 0 ; i < n ; + + i )
content + = bytes [ i ] ;
std : : cerr < < " !!! ON STDOUT for process " + std : : to_string ( process_id ) + " with content " + content + " \n " ;
2017-04-08 22:54:36 +00:00
2017-07-30 04:24:02 +00:00
{
//std::lock_guard<std::recursive_mutex> processes_lock(processes_mutex_);
for ( size_t i = 0 ; i < n ; + + i )
processes_ [ process_id ] . unhandled_output + = bytes [ i ] ;
}
2017-05-16 07:26:26 +00:00
2017-07-30 04:24:02 +00:00
std : : cerr < < " !&&! Begin ParseOutput \n " ;
ParseOutput ( process_id ) ;
std : : cerr < < " !&&! End ParseOutput \n " ;
}
2017-05-17 06:01:51 +00:00
2017-07-30 04:24:02 +00:00
void OnStdErr ( const char * bytes , size_t n ) {
std : : string content = " OOP [indexer]: " ;
for ( size_t i = 0 ; i < n ; + + i )
content + = bytes [ i ] ;
std : : cerr < < content ;
2017-04-21 04:06:15 +00:00
}
2017-07-30 04:24:02 +00:00
} ;
2017-04-21 04:06:15 +00:00
2017-07-30 04:24:02 +00:00
constexpr const char * kIpcBufferName = " CqueryIpc " ;
constexpr size_t kIpcBufferSize = 1024 * 8 ;
2017-04-21 04:06:15 +00:00
2017-07-30 04:24:02 +00:00
// Main function for the out-of-process indexer.
void IndexProcessMain ( ) {
// TODO
// querydb process is responsible for owning the buffer.
//MessageQueue queue(Buffer::CreateSharedBuffer(kIpcBufferName, kIpcBufferSize), true /*buffer_has_data*/);
2017-05-17 07:08:45 +00:00
2017-07-30 04:24:02 +00:00
std : : cerr < < " Indexer process starting \n " ;
2017-04-24 01:01:51 +00:00
2017-07-30 04:24:02 +00:00
OutOfProcessQueryDbResponder responder ;
ThreadedQueue < IndexProcess_Request > messages ;
InProcessIndexer indexer ( & responder , & messages ) ;
2017-04-24 01:01:51 +00:00
2017-07-30 04:24:02 +00:00
std : : thread stdin_reader ( [ & ] ( ) {
SetCurrentThreadName ( " IndexStdinReader " ) ;
while ( true )
PumpIndexThreadStdioReaderMain ( & messages ) ;
} ) ;
SetCurrentThreadName ( " IndexMain " ) ;
while ( true ) {
IndexProcess_Request message = messages . Dequeue ( ) ;
std : : cerr < < " !! Got message.type= " + std : : to_string ( ( int ) message . type ) + " \n " ;
if ( message . type = = IndexProcess_Request : : Type : : kInitialize )
indexer . SetConfig ( * message . initialize_args ) ;
if ( message . type = = IndexProcess_Request : : Type : : kQuit ) {
if ( ! indexer . TryWaitUntilIdle ( ) ) {
// Process other messages.
assert ( ! messages . IsEmpty ( ) ) ;
messages . Enqueue ( std : : move ( message ) ) ;
continue ;
}
responder . Write ( IndexProcess_Response : : CreateShutdown ( ) ) ;
assert ( messages . IsEmpty ( ) ) ;
exit ( 0 ) ;
2017-04-24 01:01:51 +00:00
}
2017-07-30 04:24:02 +00:00
indexer . SendMessage ( message ) ;
2017-04-24 01:01:51 +00:00
}
}
2017-04-20 08:05:19 +00:00
2017-04-08 20:00:08 +00:00
2017-04-21 04:50:31 +00:00
2017-04-24 01:01:51 +00:00
2017-04-08 06:45:28 +00:00
2017-05-17 07:08:45 +00:00
2017-05-19 07:02:01 +00:00
2017-04-08 06:45:28 +00:00
2017-04-11 05:26:27 +00:00
2017-04-08 22:54:36 +00:00
2017-05-25 02:04:19 +00:00
2017-04-08 06:45:28 +00:00
2017-04-23 22:45:40 +00:00
2017-03-05 19:48:05 +00:00
2017-04-08 07:52:57 +00:00
2017-03-15 04:59:05 +00:00
2017-04-08 07:52:57 +00:00
2017-04-16 19:02:29 +00:00
2017-04-08 07:52:57 +00:00
2017-03-16 07:36:49 +00:00
2017-04-23 22:45:40 +00:00
bool QueryDbMainLoop (
2017-05-21 19:51:15 +00:00
Config * config ,
2017-04-23 21:24:06 +00:00
QueryDatabase * db ,
2017-07-30 04:24:02 +00:00
CacheManager * db_cache ,
2017-04-23 22:45:40 +00:00
MultiQueueWaiter * waiter ,
2017-07-30 04:24:02 +00:00
IndexProcess_ResponseQueue * queue_index_response ,
2017-04-23 21:24:06 +00:00
Index_DoIdMapQueue * queue_do_id_map ,
Index_OnIdMappedQueue * queue_on_id_mapped ,
Index_OnIndexedQueue * queue_on_indexed ,
2017-07-30 04:24:02 +00:00
IIndexerProcess * indexer_process ,
2017-04-23 21:24:06 +00:00
Project * project ,
FileConsumer : : SharedState * file_consumer_shared ,
WorkingFiles * working_files ,
2017-05-27 04:21:00 +00:00
ClangCompleteManager * clang_complete ,
IncludeComplete * include_complete ,
2017-05-26 07:10:55 +00:00
CodeCompleteCache * global_code_complete_cache ,
CodeCompleteCache * non_global_code_complete_cache ,
2017-05-20 08:20:37 +00:00
CodeCompleteCache * signature_cache ) {
2017-04-16 21:49:48 +00:00
IpcManager * ipc = IpcManager : : instance ( ) ;
2017-03-26 21:40:34 +00:00
2017-04-23 22:45:40 +00:00
bool did_work = false ;
2017-04-16 21:49:48 +00:00
std : : vector < std : : unique_ptr < BaseIpcMessage > > messages = ipc - > GetMessages ( IpcManager : : Destination : : Server ) ;
2017-03-05 19:48:05 +00:00
for ( auto & message : messages ) {
2017-04-23 22:45:40 +00:00
did_work = true ;
2017-05-19 05:44:23 +00:00
//std::cerr << "[querydb] Processing message " << IpcIdToString(message->method_id) << std::endl;
2017-03-05 19:48:05 +00:00
2017-03-25 19:18:25 +00:00
switch ( message - > method_id ) {
2017-04-23 20:19:09 +00:00
case IpcId : : Initialize : {
auto request = static_cast < Ipc_InitializeRequest * > ( message . get ( ) ) ;
2017-04-26 01:32:45 +00:00
// Log initialization parameters.
rapidjson : : StringBuffer output ;
Writer writer ( output ) ;
Reflect ( writer , request - > params . initializationOptions ) ;
std : : cerr < < output . GetString ( ) < < std : : endl ;
2017-04-23 20:19:09 +00:00
if ( request - > params . rootUri ) {
std : : string project_path = request - > params . rootUri - > GetPath ( ) ;
2017-07-28 02:14:33 +00:00
LOG_S ( INFO ) < < " [querydb] Initialize in directory " < < project_path
< < " with uri " < < request - > params . rootUri - > raw_uri ;
2017-04-23 20:19:09 +00:00
if ( ! request - > params . initializationOptions ) {
2017-07-28 02:14:33 +00:00
LOG_S ( INFO ) < < " Initialization parameters (particularily cacheDirectory) are required " ;
2017-04-23 20:19:09 +00:00
exit ( 1 ) ;
}
2017-03-05 19:48:05 +00:00
2017-04-23 20:19:09 +00:00
* config = * request - > params . initializationOptions ;
2017-03-15 07:14:44 +00:00
2017-05-20 19:31:07 +00:00
// Check client version.
if ( config - > clientVersion ! = kExpectedClientVersion ) {
Out_ShowLogMessage out ;
out . display_type = Out_ShowLogMessage : : DisplayType : : Show ;
out . params . type = lsMessageType : : Error ;
out . params . message = " cquery client (v " + std : : to_string ( config - > clientVersion ) + " ) and server (v " + std : : to_string ( kExpectedClientVersion ) + " ) version mismatch. Please update " ;
if ( config - > clientVersion > kExpectedClientVersion )
out . params . message + = " the cquery binary. " ;
else
2017-06-14 07:13:26 +00:00
out . params . message + = " your extension client (VSIX file). Make sure to uninstall the cquery extension and restart vscode before reinstalling. " ;
2017-05-20 19:31:07 +00:00
out . Write ( std : : cout ) ;
}
2017-04-23 20:19:09 +00:00
// Make sure cache directory is valid.
if ( config - > cacheDirectory . empty ( ) ) {
2017-07-28 02:14:33 +00:00
LOG_S ( ERROR ) < < " No cache directory " ;
2017-04-23 20:19:09 +00:00
exit ( 1 ) ;
}
config - > cacheDirectory = NormalizePath ( config - > cacheDirectory ) ;
2017-05-21 19:51:15 +00:00
EnsureEndsInSlash ( config - > cacheDirectory ) ;
2017-04-23 20:19:09 +00:00
MakeDirectoryRecursive ( config - > cacheDirectory ) ;
2017-05-21 07:37:53 +00:00
// Set project root.
config - > projectRoot = NormalizePath ( request - > params . rootUri - > GetPath ( ) ) ;
2017-05-21 19:51:15 +00:00
EnsureEndsInSlash ( config - > projectRoot ) ;
2017-05-21 07:37:53 +00:00
2017-04-23 20:19:09 +00:00
// Start indexer threads.
2017-07-30 04:24:02 +00:00
// Set default indexer count if not specified.
if ( config - > indexerCount = = 0 ) {
config - > indexerCount = std : : max < int > ( std : : thread : : hardware_concurrency ( ) , 2 ) - 1 ;
}
std : : cerr < < " [querydb] Starting " < < config - > indexerCount < < " indexers " < < std : : endl ;
for ( int i = 0 ; i < config - > indexerCount ; + + i ) {
2017-04-23 20:19:09 +00:00
new std : : thread ( [ & ] ( ) {
2017-07-30 04:24:02 +00:00
IndexMain ( config , file_consumer_shared , project , working_files , waiter , queue_index_response , queue_do_id_map , queue_on_id_mapped , queue_on_indexed ) ;
2017-04-23 20:19:09 +00:00
} ) ;
}
2017-03-25 19:18:25 +00:00
2017-07-30 04:24:02 +00:00
// Send config to indexer process.
indexer_process - > SetConfig ( * config ) ;
2017-05-21 19:51:15 +00:00
Timer time ;
2017-04-23 20:19:09 +00:00
// Open up / load the project.
2017-04-26 04:03:22 +00:00
project - > Load ( config - > extraClangArguments , project_path ) ;
2017-05-21 19:51:15 +00:00
time . ResetAndPrint ( " [perf] Loaded compilation entries ( " + std : : to_string ( project - > entries . size ( ) ) + " files) " ) ;
2017-03-26 21:40:34 +00:00
2017-05-21 19:51:15 +00:00
// Start scanning include directories before dispatching project files, because that takes a long time.
2017-05-27 04:21:00 +00:00
include_complete - > Rescan ( ) ;
2017-04-16 23:52:42 +00:00
2017-05-21 19:51:15 +00:00
time . Reset ( ) ;
project - > ForAllFilteredFiles ( config , [ & ] ( int i , const Project : : Entry & entry ) {
//std::cerr << "[" << i << "/" << (project->entries.size() - 1)
// << "] Dispatching index request for file " << entry.filename
// << std::endl;
2017-07-30 04:24:02 +00:00
indexer_process - > SendMessage ( IndexProcess_Request : : CreateIndex ( entry . filename , entry . args ) ) ;
2017-04-23 20:19:09 +00:00
} ) ;
2017-07-30 04:24:02 +00:00
indexer_process - > Restart ( ) ;
indexer_process - > EnableAutoRestart ( ) ;
// We need to support multiple concurrent index processes.
2017-05-21 19:51:15 +00:00
time . ResetAndPrint ( " [perf] Dispatched initial index requests " ) ;
2017-04-23 20:19:09 +00:00
}
2017-04-16 23:52:42 +00:00
2017-04-23 20:19:09 +00:00
// TODO: query request->params.capabilities.textDocument and support only things
// the client supports.
2017-04-16 23:52:42 +00:00
2017-04-23 20:19:09 +00:00
auto response = Out_InitializeResponse ( ) ;
response . id = request - > id ;
2017-03-15 07:14:44 +00:00
2017-04-23 20:19:09 +00:00
//response.result.capabilities.textDocumentSync = lsTextDocumentSyncOptions();
//response.result.capabilities.textDocumentSync->openClose = true;
//response.result.capabilities.textDocumentSync->change = lsTextDocumentSyncKind::Full;
//response.result.capabilities.textDocumentSync->willSave = true;
//response.result.capabilities.textDocumentSync->willSaveWaitUntil = true;
response . result . capabilities . textDocumentSync = lsTextDocumentSyncKind : : Incremental ;
2017-04-22 07:32:29 +00:00
2017-04-23 20:19:09 +00:00
response . result . capabilities . renameProvider = true ;
2017-04-21 06:32:18 +00:00
2017-04-23 20:19:09 +00:00
response . result . capabilities . completionProvider = lsCompletionOptions ( ) ;
response . result . capabilities . completionProvider - > resolveProvider = false ;
2017-04-24 01:52:38 +00:00
// vscode doesn't support trigger character sequences, so we use ':' for '::' and '>' for '->'.
// See https://github.com/Microsoft/language-server-protocol/issues/138.
2017-05-21 07:37:53 +00:00
response . result . capabilities . completionProvider - > triggerCharacters = { " . " , " : " , " > " , " # " } ;
2017-04-09 19:38:52 +00:00
2017-05-15 07:28:53 +00:00
response . result . capabilities . signatureHelpProvider = lsSignatureHelpOptions ( ) ;
// NOTE: If updating signature help tokens make sure to also update
// WorkingFile::FindClosestCallNameInBuffer.
response . result . capabilities . signatureHelpProvider - > triggerCharacters = { " ( " , " , " } ;
2017-04-23 20:19:09 +00:00
response . result . capabilities . codeLensProvider = lsCodeLensOptions ( ) ;
response . result . capabilities . codeLensProvider - > resolveProvider = false ;
2017-04-09 19:38:52 +00:00
2017-04-23 20:19:09 +00:00
response . result . capabilities . definitionProvider = true ;
response . result . capabilities . documentHighlightProvider = true ;
response . result . capabilities . hoverProvider = true ;
response . result . capabilities . referencesProvider = true ;
2017-04-21 04:09:54 +00:00
2017-05-20 19:31:07 +00:00
response . result . capabilities . codeActionProvider = true ;
2017-04-23 20:19:09 +00:00
response . result . capabilities . documentSymbolProvider = true ;
response . result . capabilities . workspaceSymbolProvider = true ;
2017-04-10 00:17:49 +00:00
2017-05-21 04:30:59 +00:00
response . result . capabilities . documentLinkProvider = lsDocumentLinkOptions ( ) ;
response . result . capabilities . documentLinkProvider - > resolveProvider = false ;
2017-04-23 20:19:09 +00:00
ipc - > SendOutMessageToClient ( IpcId : : Initialize , response ) ;
break ;
}
2017-04-14 08:21:03 +00:00
2017-05-03 06:45:10 +00:00
case IpcId : : Exit : {
exit ( 0 ) ;
break ;
}
2017-04-23 20:19:09 +00:00
case IpcId : : CqueryFreshenIndex : {
2017-07-28 02:14:33 +00:00
LOG_S ( INFO ) < < " Freshening " < < project - > entries . size ( ) < < " files " ;
2017-04-23 20:19:09 +00:00
project - > ForAllFilteredFiles ( config , [ & ] ( int i , const Project : : Entry & entry ) {
2017-07-28 02:14:33 +00:00
LOG_S ( INFO ) < < " [ " < < i < < " / " < < ( project - > entries . size ( ) - 1 )
< < " ] Dispatching index request for file " < < entry . filename ;
2017-07-30 04:24:02 +00:00
indexer_process - > SendMessage ( IndexProcess_Request : : CreateIndex ( entry . filename , entry . args ) ) ;
2017-04-23 20:19:09 +00:00
} ) ;
2017-04-14 08:21:03 +00:00
break ;
}
2017-04-19 07:52:48 +00:00
2017-05-24 07:17:29 +00:00
case IpcId : : CqueryTypeHierarchyTree : {
auto msg = static_cast < Ipc_CqueryTypeHierarchyTree * > ( message . get ( ) ) ;
2017-05-27 07:10:21 +00:00
QueryFile * file ;
if ( ! FindFileOrFail ( db , msg - > id , msg - > params . textDocument . uri . GetPath ( ) , & file ) )
2017-05-24 07:17:29 +00:00
break ;
2017-05-27 07:10:21 +00:00
2017-05-24 07:17:29 +00:00
WorkingFile * working_file = working_files - > GetFileByFilename ( file - > def . path ) ;
Out_CqueryTypeHierarchyTree response ;
response . id = msg - > id ;
for ( const SymbolRef & ref : FindSymbolsAtLocation ( working_file , file , msg - > params . position ) ) {
if ( ref . idx . kind = = SymbolKind : : Type ) {
2017-07-21 02:53:08 +00:00
response . result = BuildInheritanceHierarchyForType ( db , working_files , QueryTypeId ( ref . idx . idx ) ) ;
break ;
}
if ( ref . idx . kind = = SymbolKind : : Func ) {
response . result = BuildInheritanceHierarchyForFunc ( db , working_files , QueryFuncId ( ref . idx . idx ) ) ;
2017-05-24 07:17:29 +00:00
break ;
}
}
ipc - > SendOutMessageToClient ( IpcId : : CqueryTypeHierarchyTree , response ) ;
break ;
}
case IpcId : : CqueryCallTreeInitial : {
auto msg = static_cast < Ipc_CqueryCallTreeInitial * > ( message . get ( ) ) ;
2017-05-27 07:10:21 +00:00
QueryFile * file ;
if ( ! FindFileOrFail ( db , msg - > id , msg - > params . textDocument . uri . GetPath ( ) , & file ) )
2017-05-24 07:17:29 +00:00
break ;
2017-05-27 07:10:21 +00:00
2017-05-24 07:17:29 +00:00
WorkingFile * working_file = working_files - > GetFileByFilename ( file - > def . path ) ;
Out_CqueryCallTree response ;
response . id = msg - > id ;
for ( const SymbolRef & ref : FindSymbolsAtLocation ( working_file , file , msg - > params . position ) ) {
if ( ref . idx . kind = = SymbolKind : : Func ) {
response . result = BuildInitialCallTree ( db , working_files , QueryFuncId ( ref . idx . idx ) ) ;
break ;
}
}
ipc - > SendOutMessageToClient ( IpcId : : CqueryCallTreeInitial , response ) ;
break ;
}
case IpcId : : CqueryCallTreeExpand : {
auto msg = static_cast < Ipc_CqueryCallTreeExpand * > ( message . get ( ) ) ;
Out_CqueryCallTree response ;
response . id = msg - > id ;
auto func_id = db - > usr_to_func . find ( msg - > params . usr ) ;
if ( func_id ! = db - > usr_to_func . end ( ) )
response . result = BuildExpandCallTree ( db , working_files , func_id - > second ) ;
ipc - > SendOutMessageToClient ( IpcId : : CqueryCallTreeExpand , response ) ;
break ;
}
2017-05-07 06:56:04 +00:00
case IpcId : : CqueryVars : {
auto msg = static_cast < Ipc_CqueryVars * > ( message . get ( ) ) ;
2017-05-27 07:10:21 +00:00
QueryFile * file ;
if ( ! FindFileOrFail ( db , msg - > id , msg - > params . textDocument . uri . GetPath ( ) , & file ) )
2017-05-07 06:56:04 +00:00
break ;
2017-05-27 07:10:21 +00:00
2017-05-07 06:56:04 +00:00
WorkingFile * working_file = working_files - > GetFileByFilename ( file - > def . path ) ;
Out_LocationList response ;
response . id = msg - > id ;
for ( const SymbolRef & ref : FindSymbolsAtLocation ( working_file , file , msg - > params . position ) ) {
if ( ref . idx . kind = = SymbolKind : : Type ) {
optional < QueryType > & type = db - > types [ ref . idx . idx ] ;
if ( ! type ) continue ;
std : : vector < QueryLocation > locations = ToQueryLocation ( db , type - > instances ) ;
response . result = GetLsLocations ( db , working_files , locations ) ;
}
}
2017-05-24 07:17:29 +00:00
ipc - > SendOutMessageToClient ( IpcId : : CqueryVars , response ) ;
2017-05-07 06:56:04 +00:00
break ;
}
case IpcId : : CqueryCallers : {
auto msg = static_cast < Ipc_CqueryCallers * > ( message . get ( ) ) ;
2017-05-27 07:10:21 +00:00
QueryFile * file ;
if ( ! FindFileOrFail ( db , msg - > id , msg - > params . textDocument . uri . GetPath ( ) , & file ) )
2017-05-07 06:56:04 +00:00
break ;
2017-05-27 07:10:21 +00:00
2017-05-07 06:56:04 +00:00
WorkingFile * working_file = working_files - > GetFileByFilename ( file - > def . path ) ;
Out_LocationList response ;
response . id = msg - > id ;
for ( const SymbolRef & ref : FindSymbolsAtLocation ( working_file , file , msg - > params . position ) ) {
if ( ref . idx . kind = = SymbolKind : : Func ) {
optional < QueryFunc > & func = db - > funcs [ ref . idx . idx ] ;
if ( ! func ) continue ;
std : : vector < QueryLocation > locations = ToQueryLocation ( db , func - > callers ) ;
2017-07-19 07:11:16 +00:00
for ( QueryFuncRef func_ref : GetCallersForAllBaseFunctions ( db , * func ) )
locations . push_back ( func_ref . loc ) ;
for ( QueryFuncRef func_ref : GetCallersForAllDerivedFunctions ( db , * func ) )
locations . push_back ( func_ref . loc ) ;
2017-05-07 06:56:04 +00:00
response . result = GetLsLocations ( db , working_files , locations ) ;
}
}
2017-05-24 07:17:29 +00:00
ipc - > SendOutMessageToClient ( IpcId : : CqueryCallers , response ) ;
2017-05-07 06:56:04 +00:00
break ;
}
case IpcId : : CqueryBase : {
auto msg = static_cast < Ipc_CqueryBase * > ( message . get ( ) ) ;
2017-05-27 07:10:21 +00:00
QueryFile * file ;
if ( ! FindFileOrFail ( db , msg - > id , msg - > params . textDocument . uri . GetPath ( ) , & file ) )
2017-05-07 06:56:04 +00:00
break ;
2017-05-27 07:10:21 +00:00
2017-05-07 06:56:04 +00:00
WorkingFile * working_file = working_files - > GetFileByFilename ( file - > def . path ) ;
Out_LocationList response ;
response . id = msg - > id ;
for ( const SymbolRef & ref : FindSymbolsAtLocation ( working_file , file , msg - > params . position ) ) {
if ( ref . idx . kind = = SymbolKind : : Type ) {
optional < QueryType > & type = db - > types [ ref . idx . idx ] ;
if ( ! type ) continue ;
std : : vector < QueryLocation > locations = ToQueryLocation ( db , type - > def . parents ) ;
response . result = GetLsLocations ( db , working_files , locations ) ;
}
else if ( ref . idx . kind = = SymbolKind : : Func ) {
optional < QueryFunc > & func = db - > funcs [ ref . idx . idx ] ;
if ( ! func ) continue ;
optional < QueryLocation > location = GetBaseDefinitionOrDeclarationSpelling ( db , * func ) ;
if ( ! location ) continue ;
optional < lsLocation > ls_loc = GetLsLocation ( db , working_files , * location ) ;
if ( ! ls_loc ) continue ;
response . result . push_back ( * ls_loc ) ;
}
}
2017-05-24 07:17:29 +00:00
ipc - > SendOutMessageToClient ( IpcId : : CqueryBase , response ) ;
2017-05-07 06:56:04 +00:00
break ;
}
case IpcId : : CqueryDerived : {
auto msg = static_cast < Ipc_CqueryDerived * > ( message . get ( ) ) ;
2017-05-27 07:10:21 +00:00
QueryFile * file ;
if ( ! FindFileOrFail ( db , msg - > id , msg - > params . textDocument . uri . GetPath ( ) , & file ) )
2017-05-07 06:56:04 +00:00
break ;
2017-05-27 07:10:21 +00:00
2017-05-07 06:56:04 +00:00
WorkingFile * working_file = working_files - > GetFileByFilename ( file - > def . path ) ;
Out_LocationList response ;
response . id = msg - > id ;
for ( const SymbolRef & ref : FindSymbolsAtLocation ( working_file , file , msg - > params . position ) ) {
if ( ref . idx . kind = = SymbolKind : : Type ) {
optional < QueryType > & type = db - > types [ ref . idx . idx ] ;
if ( ! type ) continue ;
std : : vector < QueryLocation > locations = ToQueryLocation ( db , type - > derived ) ;
response . result = GetLsLocations ( db , working_files , locations ) ;
}
else if ( ref . idx . kind = = SymbolKind : : Func ) {
optional < QueryFunc > & func = db - > funcs [ ref . idx . idx ] ;
if ( ! func ) continue ;
std : : vector < QueryLocation > locations = ToQueryLocation ( db , func - > derived ) ;
response . result = GetLsLocations ( db , working_files , locations ) ;
}
}
2017-05-24 07:17:29 +00:00
ipc - > SendOutMessageToClient ( IpcId : : CqueryDerived , response ) ;
2017-05-07 06:56:04 +00:00
break ;
}
2017-04-23 20:19:09 +00:00
case IpcId : : TextDocumentDidOpen : {
// NOTE: This function blocks code lens. If it starts taking a long time
// we will need to find a way to unblock the code lens request.
Timer time ;
auto msg = static_cast < Ipc_TextDocumentDidOpen * > ( message . get ( ) ) ;
2017-05-26 06:40:38 +00:00
std : : string path = msg - > params . textDocument . uri . GetPath ( ) ;
2017-04-23 20:19:09 +00:00
WorkingFile * working_file = working_files - > OnOpen ( msg - > params ) ;
2017-05-26 06:40:38 +00:00
optional < std : : string > cached_file_contents = LoadCachedFileContents ( config , path ) ;
2017-04-23 20:19:09 +00:00
if ( cached_file_contents )
working_file - > SetIndexContent ( * cached_file_contents ) ;
else
working_file - > SetIndexContent ( working_file - > buffer_content ) ;
2017-05-20 21:45:46 +00:00
2017-05-26 06:40:38 +00:00
std : : unique_ptr < IndexFile > cache = LoadCachedIndex ( config , path ) ;
2017-05-20 21:45:46 +00:00
if ( cache & & ! cache - > skipped_by_preprocessor . empty ( ) )
PublishInactiveLines ( working_file , cache - > skipped_by_preprocessor ) ;
time . ResetAndPrint ( " [querydb] Loading cached index file for DidOpen (blocks CodeLens) " ) ;
2017-04-14 08:21:03 +00:00
2017-05-27 04:21:00 +00:00
include_complete - > AddFile ( working_file - > filename ) ;
clang_complete - > NotifyView ( path ) ;
2017-07-30 04:24:02 +00:00
// TODO/FIXME
//PriorityEnqueueFileForIndex(db, project, queue_do_index, working_file, path);
2017-05-21 19:51:15 +00:00
2017-04-23 20:19:09 +00:00
break ;
}
2017-04-23 21:24:06 +00:00
2017-04-23 20:19:09 +00:00
case IpcId : : TextDocumentDidChange : {
auto msg = static_cast < Ipc_TextDocumentDidChange * > ( message . get ( ) ) ;
2017-05-26 06:40:38 +00:00
std : : string path = msg - > params . textDocument . uri . GetPath ( ) ;
2017-04-23 20:19:09 +00:00
working_files - > OnChange ( msg - > params ) ;
2017-05-27 04:21:00 +00:00
clang_complete - > NotifyEdit ( path ) ;
2017-04-23 20:19:09 +00:00
break ;
}
2017-04-23 21:24:06 +00:00
2017-04-23 20:19:09 +00:00
case IpcId : : TextDocumentDidClose : {
auto msg = static_cast < Ipc_TextDocumentDidClose * > ( message . get ( ) ) ;
2017-05-29 23:33:57 +00:00
// Clear any diagnostics for the file.
Out_TextDocumentPublishDiagnostics diag ;
diag . params . uri = msg - > params . textDocument . uri ;
IpcManager : : instance ( ) - > SendOutMessageToClient ( IpcId : : TextDocumentPublishDiagnostics , diag ) ;
// Remove internal state.
2017-04-23 20:19:09 +00:00
working_files - > OnClose ( msg - > params ) ;
2017-05-29 23:33:57 +00:00
2017-04-15 05:14:05 +00:00
break ;
2017-04-14 08:21:03 +00:00
}
2017-04-23 20:19:09 +00:00
case IpcId : : TextDocumentDidSave : {
auto msg = static_cast < Ipc_TextDocumentDidSave * > ( message . get ( ) ) ;
2017-04-17 01:22:59 +00:00
2017-04-23 20:19:09 +00:00
std : : string path = msg - > params . textDocument . uri . GetPath ( ) ;
2017-05-09 05:09:57 +00:00
// Send out an index request, and copy the current buffer state so we
// can update the cached index contents when the index is done.
//
// We also do not index if there is already an index request.
//
// TODO: Cancel outgoing index request. Might be tricky to make
// efficient since we have to cancel.
// - we could have an |atomic<int> active_cancellations| variable
// that all of the indexers check before accepting an index. if
// zero we don't slow down fast-path. if non-zero we acquire
// mutex and check to see if we should skip the current request.
// if so, ignore that index response.
2017-07-30 04:24:02 +00:00
// TODO: send as priority request
Project : : Entry entry = project - > FindCompilationEntryForFile ( path ) ;
indexer_process - > SendMessage ( IndexProcess_Request : : CreateIndex ( entry . filename , entry . args ) ) ;
2017-05-26 06:40:38 +00:00
2017-05-27 04:21:00 +00:00
clang_complete - > NotifySave ( path ) ;
2017-04-17 01:22:59 +00:00
2017-04-23 20:19:09 +00:00
break ;
}
2017-04-17 01:22:59 +00:00
2017-04-23 20:19:09 +00:00
case IpcId : : TextDocumentRename : {
auto msg = static_cast < Ipc_TextDocumentRename * > ( message . get ( ) ) ;
2017-04-17 01:22:59 +00:00
2017-04-23 20:19:09 +00:00
QueryFileId file_id ;
2017-05-27 07:10:21 +00:00
QueryFile * file ;
if ( ! FindFileOrFail ( db , msg - > id , msg - > params . textDocument . uri . GetPath ( ) , & file , & file_id ) )
2017-04-23 20:19:09 +00:00
break ;
2017-05-27 07:10:21 +00:00
2017-04-23 20:19:09 +00:00
WorkingFile * working_file = working_files - > GetFileByFilename ( file - > def . path ) ;
2017-03-26 06:47:59 +00:00
2017-04-23 20:19:09 +00:00
Out_TextDocumentRename response ;
response . id = msg - > id ;
2017-03-26 06:47:59 +00:00
2017-04-23 20:19:09 +00:00
for ( const SymbolRef & ref : FindSymbolsAtLocation ( working_file , file , msg - > params . position ) ) {
// Found symbol. Return references to rename.
std : : vector < QueryLocation > uses = GetUsesOfSymbol ( db , ref . idx ) ;
response . result = BuildWorkspaceEdit ( db , working_files , uses , msg - > params . newName ) ;
break ;
}
2017-04-03 02:21:21 +00:00
2017-04-23 20:19:09 +00:00
ipc - > SendOutMessageToClient ( IpcId : : TextDocumentRename , response ) ;
2017-04-03 02:21:21 +00:00
break ;
}
2017-04-15 05:14:05 +00:00
2017-04-23 20:19:09 +00:00
case IpcId : : TextDocumentCompletion : {
2017-06-29 04:59:16 +00:00
auto msg = std : : shared_ptr < Ipc_TextDocumentComplete > (
static_cast < Ipc_TextDocumentComplete * > ( message . release ( ) ) ) ;
2017-04-15 05:14:05 +00:00
2017-05-27 04:21:00 +00:00
std : : string path = msg - > params . textDocument . uri . GetPath ( ) ;
2017-05-26 07:10:55 +00:00
WorkingFile * file = working_files - > GetFileByFilename ( path ) ;
2017-05-20 08:07:29 +00:00
2017-05-27 06:51:32 +00:00
// It shouldn't be possible, but sometimes vscode will send queries out
// of order, ie, we get completion request before buffer content update.
std : : string buffer_line ;
if ( msg - > params . position . line > = 0 & & msg - > params . position . line < file - > all_buffer_lines . size ( ) )
buffer_line = file - > all_buffer_lines [ msg - > params . position . line ] ;
2017-05-21 07:37:53 +00:00
if ( ShouldRunIncludeCompletion ( buffer_line ) ) {
2017-05-20 06:35:14 +00:00
Out_TextDocumentComplete complete_response ;
complete_response . id = msg - > id ;
complete_response . result . isIncomplete = false ;
2017-05-21 21:01:52 +00:00
2017-05-21 19:51:15 +00:00
{
2017-05-27 04:21:00 +00:00
std : : unique_lock < std : : mutex > lock ( include_complete - > completion_items_mutex , std : : defer_lock ) ;
if ( include_complete - > is_scanning )
2017-05-21 19:51:15 +00:00
lock . lock ( ) ;
complete_response . result . items . assign (
2017-05-27 04:21:00 +00:00
include_complete - > completion_items . begin ( ) ,
include_complete - > completion_items . end ( ) ) ;
2017-05-21 19:51:15 +00:00
if ( lock )
lock . unlock ( ) ;
// Update textEdit params.
for ( lsCompletionItem & item : complete_response . result . items ) {
2017-05-27 04:21:00 +00:00
item . textEdit - > range . start . line = msg - > params . position . line ;
2017-05-21 19:51:15 +00:00
item . textEdit - > range . start . character = 0 ;
2017-05-27 04:21:00 +00:00
item . textEdit - > range . end . line = msg - > params . position . line ;
2017-05-21 19:51:15 +00:00
item . textEdit - > range . end . character = ( int ) buffer_line . size ( ) ;
2017-05-21 07:37:53 +00:00
}
}
2017-07-28 02:14:33 +00:00
LOG_S ( INFO ) < < " [complete] Returning " < < complete_response . result . items . size ( ) < < " include completions " ;
2017-06-16 02:28:49 +00:00
FilterCompletionResponse ( & complete_response , buffer_line ) ;
2017-05-21 07:37:53 +00:00
ipc - > SendOutMessageToClient ( IpcId : : TextDocumentCompletion , complete_response ) ;
2017-05-20 08:07:29 +00:00
}
else {
2017-05-26 07:10:55 +00:00
bool is_global_completion = false ;
2017-06-16 02:28:49 +00:00
std : : string existing_completion ;
if ( file ) {
msg - > params . position = file - > FindStableCompletionSource ( msg - > params . position , & is_global_completion , & existing_completion ) ;
}
2017-07-28 02:14:33 +00:00
LOG_S ( INFO ) < < " [complete] Got existing completion " < < existing_completion ;
2017-05-21 07:37:53 +00:00
2017-06-09 07:08:06 +00:00
ClangCompleteManager : : OnComplete callback = std : : bind (
2017-06-30 06:51:22 +00:00
[ working_files , global_code_complete_cache , non_global_code_complete_cache , is_global_completion , existing_completion , msg ]
( const NonElidedVector < lsCompletionItem > & results , bool is_cached_result ) {
2017-05-21 07:37:53 +00:00
Out_TextDocumentComplete complete_response ;
complete_response . id = msg - > id ;
complete_response . result . isIncomplete = false ;
complete_response . result . items = results ;
// Emit completion results.
2017-06-16 02:28:49 +00:00
FilterCompletionResponse ( & complete_response , existing_completion ) ;
2017-06-10 04:13:16 +00:00
IpcManager : : instance ( ) - > SendOutMessageToClient ( IpcId : : TextDocumentCompletion , complete_response ) ;
2017-05-21 07:37:53 +00:00
2017-05-26 07:10:55 +00:00
// Cache completion results.
2017-06-30 06:51:22 +00:00
if ( ! is_cached_result ) {
std : : string path = msg - > params . textDocument . uri . GetPath ( ) ;
if ( is_global_completion ) {
global_code_complete_cache - > WithLock ( [ & ] ( ) {
global_code_complete_cache - > cached_path_ = path ;
2017-07-28 02:14:33 +00:00
LOG_S ( INFO ) < < " [complete] Updating global_code_complete_cache->cached_results [0] " ;
2017-06-30 06:51:22 +00:00
global_code_complete_cache - > cached_results_ = results ;
2017-07-28 02:14:33 +00:00
LOG_S ( INFO ) < < " [complete] DONE Updating global_code_complete_cache->cached_results [0] " ;
2017-06-30 06:51:22 +00:00
} ) ;
}
else {
non_global_code_complete_cache - > WithLock ( [ & ] ( ) {
non_global_code_complete_cache - > cached_path_ = path ;
non_global_code_complete_cache - > cached_completion_position_ = msg - > params . position ;
2017-07-28 02:14:33 +00:00
LOG_S ( INFO ) < < " [complete] Updating non_global_code_complete_cache->cached_results [1] " ;
2017-06-30 06:51:22 +00:00
non_global_code_complete_cache - > cached_results_ = results ;
2017-07-28 02:14:33 +00:00
LOG_S ( INFO ) < < " [complete] DONE Updating non_global_code_complete_cache->cached_results [1] " ;
2017-06-30 06:51:22 +00:00
} ) ;
}
2017-05-26 07:10:55 +00:00
}
2017-06-30 06:51:22 +00:00
} , std : : placeholders : : _1 , std : : placeholders : : _2 ) ;
2017-05-21 07:37:53 +00:00
2017-06-30 06:51:22 +00:00
bool is_cache_match = false ;
global_code_complete_cache - > WithLock ( [ & ] ( ) {
is_cache_match = is_global_completion & & global_code_complete_cache - > cached_path_ = = path & & ! global_code_complete_cache - > cached_results_ . empty ( ) ;
} ) ;
if ( is_cache_match ) {
2017-07-28 02:14:33 +00:00
LOG_S ( INFO ) < < " [complete] Early-returning cached global completion results at " < < msg - > params . position . ToString ( ) ;
2017-05-26 07:10:55 +00:00
2017-06-10 04:13:16 +00:00
ClangCompleteManager : : OnComplete freshen_global =
[ global_code_complete_cache ]
2017-06-30 06:51:22 +00:00
( NonElidedVector < lsCompletionItem > results , bool is_cached_result ) {
assert ( ! is_cached_result ) ;
2017-06-10 04:13:16 +00:00
2017-07-28 02:14:33 +00:00
LOG_S ( INFO ) < < " [complete] Updating global_code_complete_cache->cached_results [2] " ;
2017-05-26 07:10:55 +00:00
// note: path is updated in the normal completion handler.
2017-06-30 06:51:22 +00:00
global_code_complete_cache - > WithLock ( [ & ] ( ) {
global_code_complete_cache - > cached_results_ = results ;
} ) ;
2017-07-28 02:14:33 +00:00
LOG_S ( INFO ) < < " [complete] DONE Updating global_code_complete_cache->cached_results [2] " ;
2017-05-27 04:21:00 +00:00
} ;
2017-05-26 07:10:55 +00:00
2017-06-30 06:51:22 +00:00
global_code_complete_cache - > WithLock ( [ & ] ( ) {
callback ( global_code_complete_cache - > cached_results_ , true /*is_cached_result*/ ) ;
} ) ;
clang_complete - > CodeComplete ( msg - > params , freshen_global ) ;
2017-05-26 07:10:55 +00:00
}
2017-05-27 04:21:00 +00:00
else if ( non_global_code_complete_cache - > IsCacheValid ( msg - > params ) ) {
2017-07-28 02:14:33 +00:00
LOG_S ( INFO ) < < " [complete] Using cached completion results at " < < msg - > params . position . ToString ( ) ;
2017-06-30 06:51:22 +00:00
non_global_code_complete_cache - > WithLock ( [ & ] ( ) {
callback ( non_global_code_complete_cache - > cached_results_ , true /*is_cached_result*/ ) ;
} ) ;
2017-05-21 07:37:53 +00:00
}
else {
2017-06-30 06:51:22 +00:00
clang_complete - > CodeComplete ( msg - > params , callback ) ;
2017-05-21 07:37:53 +00:00
}
2017-05-20 08:07:29 +00:00
}
2017-04-14 06:43:50 +00:00
break ;
}
2017-04-19 07:52:48 +00:00
2017-05-15 07:28:53 +00:00
case IpcId : : TextDocumentSignatureHelp : {
auto msg = static_cast < Ipc_TextDocumentSignatureHelp * > ( message . get ( ) ) ;
2017-05-20 08:20:37 +00:00
lsTextDocumentPositionParams & params = msg - > params ;
2017-05-15 07:28:53 +00:00
WorkingFile * file = working_files - > GetFileByFilename ( params . textDocument . uri . GetPath ( ) ) ;
std : : string search ;
int active_param = 0 ;
if ( file ) {
lsPosition completion_position ;
search = file - > FindClosestCallNameInBuffer ( params . position , & active_param , & completion_position ) ;
params . position = completion_position ;
}
2017-07-28 02:14:33 +00:00
//LOG_S(INFO) << "[completion] Returning signatures for " << search;
2017-05-15 07:28:53 +00:00
if ( search . empty ( ) )
break ;
2017-06-30 06:51:22 +00:00
ClangCompleteManager : : OnComplete callback = std : : bind (
[ signature_cache ]
( BaseIpcMessage * message , std : : string search , int active_param , const NonElidedVector < lsCompletionItem > & results , bool is_cached_result ) {
2017-05-15 07:28:53 +00:00
auto msg = static_cast < Ipc_TextDocumentSignatureHelp * > ( message ) ;
auto ipc = IpcManager : : instance ( ) ;
Out_TextDocumentSignatureHelp response ;
response . id = msg - > id ;
for ( auto & result : results ) {
if ( result . label ! = search )
continue ;
lsSignatureInformation signature ;
signature . label = result . detail ;
for ( auto & parameter : result . parameters_ ) {
lsParameterInformation ls_param ;
ls_param . label = parameter ;
signature . parameters . push_back ( ls_param ) ;
}
response . result . signatures . push_back ( signature ) ;
}
// Guess the signature the user wants based on available parameter
// count.
response . result . activeSignature = 0 ;
for ( size_t i = 0 ; i < response . result . signatures . size ( ) ; + + i ) {
if ( active_param < response . result . signatures . size ( ) ) {
2017-05-17 07:08:45 +00:00
response . result . activeSignature = ( int ) i ;
2017-05-15 07:28:53 +00:00
break ;
}
}
// Set signature to what we parsed from the working file.
response . result . activeParameter = active_param ;
Timer timer ;
ipc - > SendOutMessageToClient ( IpcId : : TextDocumentSignatureHelp , response ) ;
timer . ResetAndPrint ( " [complete] Writing signature help results " ) ;
2017-06-30 06:51:22 +00:00
if ( ! is_cached_result ) {
signature_cache - > WithLock ( [ & ] ( ) {
signature_cache - > cached_path_ = msg - > params . textDocument . uri . GetPath ( ) ;
signature_cache - > cached_completion_position_ = msg - > params . position ;
2017-07-28 02:14:33 +00:00
LOG_S ( INFO ) < < " [complete] Updating signature_cache->cached_results [3] " ;
2017-06-30 06:51:22 +00:00
signature_cache - > cached_results_ = results ;
} ) ;
}
2017-05-20 08:20:37 +00:00
2017-05-15 07:28:53 +00:00
delete message ;
2017-06-30 06:51:22 +00:00
} , message . release ( ) , search , active_param , std : : placeholders : : _1 , std : : placeholders : : _2 ) ;
2017-05-15 07:28:53 +00:00
2017-05-20 08:23:01 +00:00
if ( signature_cache - > IsCacheValid ( params ) ) {
2017-07-28 02:14:33 +00:00
LOG_S ( INFO ) < < " [complete] Using cached completion results at " < < params . position . ToString ( ) ;
2017-06-30 06:51:22 +00:00
signature_cache - > WithLock ( [ & ] ( ) {
callback ( signature_cache - > cached_results_ , true /*is_cached_result*/ ) ;
} ) ;
2017-05-20 08:20:37 +00:00
}
else {
2017-05-27 04:21:00 +00:00
clang_complete - > CodeComplete ( params , std : : move ( callback ) ) ;
2017-05-20 08:20:37 +00:00
}
2017-05-15 07:28:53 +00:00
break ;
}
2017-04-23 20:19:09 +00:00
case IpcId : : TextDocumentDefinition : {
auto msg = static_cast < Ipc_TextDocumentDefinition * > ( message . get ( ) ) ;
2017-04-14 06:43:50 +00:00
2017-04-23 20:19:09 +00:00
QueryFileId file_id ;
2017-05-27 07:10:21 +00:00
QueryFile * file ;
if ( ! FindFileOrFail ( db , msg - > id , msg - > params . textDocument . uri . GetPath ( ) , & file , & file_id ) )
2017-04-23 20:19:09 +00:00
break ;
2017-05-27 07:10:21 +00:00
2017-04-23 20:19:09 +00:00
WorkingFile * working_file = working_files - > GetFileByFilename ( file - > def . path ) ;
2017-04-15 05:14:05 +00:00
2017-04-23 20:19:09 +00:00
Out_TextDocumentDefinition response ;
response . id = msg - > id ;
2017-04-15 05:14:05 +00:00
2017-04-23 20:19:09 +00:00
int target_line = msg - > params . position . line + 1 ;
int target_column = msg - > params . position . character + 1 ;
for ( const SymbolRef & ref : FindSymbolsAtLocation ( working_file , file , msg - > params . position ) ) {
// Found symbol. Return definition.
// Special cases which are handled:
// - symbol has declaration but no definition (ie, pure virtual)
// - start at spelling but end at extent for better mouse tooltip
// - goto declaration while in definition of recursive type
optional < QueryLocation > def_loc = GetDefinitionSpellingOfSymbol ( db , ref . idx ) ;
// We use spelling start and extent end because this causes vscode to
// highlight the entire definition when previewing / hoving with the
// mouse.
optional < QueryLocation > def_extent = GetDefinitionExtentOfSymbol ( db , ref . idx ) ;
if ( def_loc & & def_extent )
def_loc - > range . end = def_extent - > range . end ;
// If the cursor is currently at or in the definition we should goto
// the declaration if possible. We also want to use declarations if
// we're pointing to, ie, a pure virtual function which has no
// definition.
if ( ! def_loc | | ( def_loc - > path = = file_id & &
def_loc - > range . Contains ( target_line , target_column ) ) ) {
// Goto declaration.
std : : vector < QueryLocation > declarations = GetDeclarationsOfSymbolForGotoDefinition ( db , ref . idx ) ;
for ( auto declaration : declarations ) {
optional < lsLocation > ls_declaration = GetLsLocation ( db , working_files , declaration ) ;
if ( ls_declaration )
response . result . push_back ( * ls_declaration ) ;
}
// We found some declarations. Break so we don't add the definition location.
if ( ! response . result . empty ( ) )
break ;
}
2017-04-14 06:43:50 +00:00
2017-04-23 20:19:09 +00:00
if ( def_loc )
PushBack ( & response . result , GetLsLocation ( db , working_files , * def_loc ) ) ;
2017-04-14 06:43:50 +00:00
2017-04-23 20:19:09 +00:00
if ( ! response . result . empty ( ) )
break ;
}
2017-04-14 05:18:02 +00:00
2017-05-21 03:46:15 +00:00
// No symbols - check for includes.
if ( response . result . empty ( ) ) {
for ( const IndexInclude & include : file - > def . includes ) {
if ( include . line = = target_line ) {
lsLocation result ;
result . uri = lsDocumentUri : : FromPath ( include . resolved_path ) ;
response . result . push_back ( result ) ;
break ;
}
}
}
2017-04-23 20:19:09 +00:00
ipc - > SendOutMessageToClient ( IpcId : : TextDocumentDefinition , response ) ;
2017-04-14 05:18:02 +00:00
break ;
}
2017-04-19 07:52:48 +00:00
2017-04-23 20:19:09 +00:00
case IpcId : : TextDocumentDocumentHighlight : {
auto msg = static_cast < Ipc_TextDocumentDocumentHighlight * > ( message . get ( ) ) ;
2017-04-14 05:18:02 +00:00
2017-04-23 20:19:09 +00:00
QueryFileId file_id ;
2017-05-27 07:10:21 +00:00
QueryFile * file ;
if ( ! FindFileOrFail ( db , msg - > id , msg - > params . textDocument . uri . GetPath ( ) , & file , & file_id ) )
2017-04-23 20:19:09 +00:00
break ;
2017-05-27 07:10:21 +00:00
2017-04-23 20:19:09 +00:00
WorkingFile * working_file = working_files - > GetFileByFilename ( file - > def . path ) ;
2017-04-14 05:18:02 +00:00
2017-04-23 20:19:09 +00:00
Out_TextDocumentDocumentHighlight response ;
response . id = msg - > id ;
2017-04-14 05:18:02 +00:00
2017-04-23 20:19:09 +00:00
for ( const SymbolRef & ref : FindSymbolsAtLocation ( working_file , file , msg - > params . position ) ) {
// Found symbol. Return references to highlight.
std : : vector < QueryLocation > uses = GetUsesOfSymbol ( db , ref . idx ) ;
response . result . reserve ( uses . size ( ) ) ;
for ( const QueryLocation & use : uses ) {
if ( use . path ! = file_id )
continue ;
optional < lsLocation > ls_location = GetLsLocation ( db , working_files , use ) ;
if ( ! ls_location )
continue ;
2017-04-14 05:18:02 +00:00
2017-04-23 20:19:09 +00:00
lsDocumentHighlight highlight ;
highlight . kind = lsDocumentHighlightKind : : Text ;
highlight . range = ls_location - > range ;
response . result . push_back ( highlight ) ;
}
break ;
}
2017-04-10 05:34:06 +00:00
2017-04-23 20:19:09 +00:00
ipc - > SendOutMessageToClient ( IpcId : : TextDocumentDocumentHighlight , response ) ;
2017-04-10 05:34:06 +00:00
break ;
}
2017-04-23 20:19:09 +00:00
case IpcId : : TextDocumentHover : {
auto msg = static_cast < Ipc_TextDocumentHover * > ( message . get ( ) ) ;
2017-04-10 05:34:06 +00:00
2017-05-27 07:10:21 +00:00
QueryFile * file ;
if ( ! FindFileOrFail ( db , msg - > id , msg - > params . textDocument . uri . GetPath ( ) , & file ) )
2017-04-23 20:19:09 +00:00
break ;
2017-05-27 07:10:21 +00:00
2017-04-23 20:19:09 +00:00
WorkingFile * working_file = working_files - > GetFileByFilename ( file - > def . path ) ;
Out_TextDocumentHover response ;
response . id = msg - > id ;
2017-04-10 05:34:06 +00:00
2017-04-23 20:19:09 +00:00
for ( const SymbolRef & ref : FindSymbolsAtLocation ( working_file , file , msg - > params . position ) ) {
// Found symbol. Return hover.
optional < lsRange > ls_range = GetLsRange ( working_files - > GetFileByFilename ( file - > def . path ) , ref . loc . range ) ;
if ( ! ls_range )
2017-04-15 05:14:05 +00:00
continue ;
2017-04-10 05:34:06 +00:00
2017-04-23 20:19:09 +00:00
response . result . contents = GetHoverForSymbol ( db , ref . idx ) ;
response . result . range = * ls_range ;
break ;
2017-04-10 05:34:06 +00:00
}
2017-04-23 20:19:09 +00:00
ipc - > SendOutMessageToClient ( IpcId : : TextDocumentHover , response ) ;
2017-04-15 05:14:05 +00:00
break ;
2017-04-10 05:34:06 +00:00
}
2017-04-23 20:19:09 +00:00
case IpcId : : TextDocumentReferences : {
auto msg = static_cast < Ipc_TextDocumentReferences * > ( message . get ( ) ) ;
2017-05-27 07:10:21 +00:00
QueryFile * file ;
if ( ! FindFileOrFail ( db , msg - > id , msg - > params . textDocument . uri . GetPath ( ) , & file ) )
2017-04-23 20:19:09 +00:00
break ;
2017-05-27 07:10:21 +00:00
2017-04-23 20:19:09 +00:00
WorkingFile * working_file = working_files - > GetFileByFilename ( file - > def . path ) ;
2017-04-10 05:34:06 +00:00
2017-04-23 20:19:09 +00:00
Out_TextDocumentReferences response ;
response . id = msg - > id ;
2017-03-25 19:18:25 +00:00
2017-04-23 20:19:09 +00:00
for ( const SymbolRef & ref : FindSymbolsAtLocation ( working_file , file , msg - > params . position ) ) {
optional < QueryLocation > excluded_declaration ;
if ( ! msg - > params . context . includeDeclaration ) {
2017-07-28 02:14:33 +00:00
LOG_S ( INFO ) < < " Excluding declaration in references " ;
2017-04-23 20:19:09 +00:00
excluded_declaration = GetDefinitionSpellingOfSymbol ( db , ref . idx ) ;
}
2017-03-25 19:18:25 +00:00
2017-04-23 20:19:09 +00:00
// Found symbol. Return references.
std : : vector < QueryLocation > uses = GetUsesOfSymbol ( db , ref . idx ) ;
response . result . reserve ( uses . size ( ) ) ;
for ( const QueryLocation & use : uses ) {
if ( excluded_declaration . has_value ( ) & & use = = * excluded_declaration )
continue ;
optional < lsLocation > ls_location = GetLsLocation ( db , working_files , use ) ;
if ( ls_location )
response . result . push_back ( * ls_location ) ;
}
break ;
}
ipc - > SendOutMessageToClient ( IpcId : : TextDocumentReferences , response ) ;
2017-03-29 06:33:38 +00:00
break ;
}
2017-03-25 19:18:25 +00:00
2017-04-23 20:19:09 +00:00
case IpcId : : TextDocumentDocumentSymbol : {
auto msg = static_cast < Ipc_TextDocumentDocumentSymbol * > ( message . get ( ) ) ;
2017-03-06 08:48:51 +00:00
2017-04-23 20:19:09 +00:00
Out_TextDocumentDocumentSymbol response ;
response . id = msg - > id ;
2017-03-25 19:18:25 +00:00
2017-05-27 07:10:21 +00:00
QueryFile * file ;
if ( ! FindFileOrFail ( db , msg - > id , msg - > params . textDocument . uri . GetPath ( ) , & file ) )
2017-04-23 20:19:09 +00:00
break ;
2017-05-27 07:10:21 +00:00
2017-03-25 19:18:25 +00:00
2017-04-23 20:19:09 +00:00
for ( SymbolRef ref : file - > def . outline ) {
optional < lsSymbolInformation > info = GetSymbolInfo ( db , working_files , ref . idx ) ;
if ( ! info )
continue ;
2017-03-25 19:18:25 +00:00
2017-04-23 20:19:09 +00:00
optional < lsLocation > location = GetLsLocation ( db , working_files , ref . loc ) ;
if ( ! location )
continue ;
info - > location = * location ;
response . result . push_back ( * info ) ;
}
2017-03-25 19:18:25 +00:00
2017-04-23 20:19:09 +00:00
ipc - > SendOutMessageToClient ( IpcId : : TextDocumentDocumentSymbol , response ) ;
2017-03-29 06:33:38 +00:00
break ;
}
2017-04-23 20:19:09 +00:00
2017-05-21 04:30:59 +00:00
case IpcId : : TextDocumentDocumentLink : {
auto msg = static_cast < Ipc_TextDocumentDocumentLink * > ( message . get ( ) ) ;
Out_TextDocumentDocumentLink response ;
response . id = msg - > id ;
if ( config - > showDocumentLinksOnIncludes ) {
2017-05-27 07:10:21 +00:00
QueryFile * file ;
if ( ! FindFileOrFail ( db , msg - > id , msg - > params . textDocument . uri . GetPath ( ) , & file ) )
2017-05-21 04:30:59 +00:00
break ;
WorkingFile * working_file = working_files - > GetFileByFilename ( msg - > params . textDocument . uri . GetPath ( ) ) ;
if ( ! working_file ) {
2017-07-28 02:14:33 +00:00
LOG_S ( INFO ) < < " Unable to find working file " < < msg - > params . textDocument . uri . GetPath ( ) ;
2017-05-21 04:30:59 +00:00
break ;
}
for ( const IndexInclude & include : file - > def . includes ) {
optional < int > buffer_line ;
optional < std : : string > buffer_line_content = working_file - > GetBufferLineContentFromIndexLine ( include . line , & buffer_line ) ;
if ( ! buffer_line | | ! buffer_line_content )
continue ;
2017-05-26 04:26:35 +00:00
// Subtract 1 from line because querydb stores 1-based lines but
// vscode expects 0-based lines.
optional < lsRange > between_quotes = ExtractQuotedRange ( * buffer_line - 1 , * buffer_line_content ) ;
if ( ! between_quotes )
2017-05-21 04:30:59 +00:00
continue ;
lsDocumentLink link ;
link . target = lsDocumentUri : : FromPath ( include . resolved_path ) ;
2017-05-26 04:26:35 +00:00
link . range = * between_quotes ;
2017-05-21 04:30:59 +00:00
response . result . push_back ( link ) ;
}
}
ipc - > SendOutMessageToClient ( IpcId : : TextDocumentDocumentLink , response ) ;
break ;
}
2017-05-20 19:31:07 +00:00
case IpcId : : TextDocumentCodeAction : {
// NOTE: This code snippet will generate some FixIts for testing:
//
// struct origin { int x, int y };
// void foo() {
// point origin = {
// x: 0.0,
// y: 0.0
// };
// }
//
auto msg = static_cast < Ipc_TextDocumentCodeAction * > ( message . get ( ) ) ;
2017-05-29 21:18:35 +00:00
QueryFileId file_id ;
QueryFile * file ;
if ( ! FindFileOrFail ( db , msg - > id , msg - > params . textDocument . uri . GetPath ( ) , & file , & file_id ) )
break ;
2017-05-20 19:31:07 +00:00
WorkingFile * working_file = working_files - > GetFileByFilename ( msg - > params . textDocument . uri . GetPath ( ) ) ;
if ( ! working_file ) {
// TODO: send error response.
2017-07-28 02:14:33 +00:00
LOG_S ( INFO ) < < " [error] textDocument/codeAction could not find working file " ;
2017-05-20 19:31:07 +00:00
break ;
}
Out_TextDocumentCodeAction response ;
response . id = msg - > id ;
2017-05-29 21:18:35 +00:00
// TODO: auto-insert namespace?
int default_line = ( int ) working_file - > all_buffer_lines . size ( ) ;
// Make sure to call EnsureImplFile before using these. We lazy load
// them because computing the values could involve an entire project
// scan.
optional < lsDocumentUri > impl_uri ;
optional < QueryFileId > impl_file_id ;
std : : vector < SymbolRef > syms = FindSymbolsAtLocation ( working_file , file , msg - > params . range . start ) ;
for ( SymbolRef sym : syms ) {
switch ( sym . idx . kind ) {
case SymbolKind : : Type : {
optional < QueryType > & type = db - > types [ sym . idx . idx ] ;
if ( ! type )
break ;
int num_edits = 0 ;
// Get implementation file.
Out_TextDocumentCodeAction : : Command command ;
for ( QueryFuncId func_id : type - > def . funcs ) {
optional < QueryFunc > & func_def = db - > funcs [ func_id . id ] ;
if ( ! func_def | | func_def - > def . definition_extent )
continue ;
EnsureImplFile ( db , file_id , impl_uri /*out*/ , impl_file_id /*out*/ ) ;
optional < lsTextEdit > edit = BuildAutoImplementForFunction ( db , working_files , working_file , default_line , file_id , * impl_file_id , * func_def ) ;
if ( ! edit )
continue ;
+ + num_edits ;
// Merge edits together if they are on the same line.
// TODO: be smarter about newline merging? ie, don't end up
// with foo()\n\n\n\nfoo(), we want foo()\n\nfoo()\n\n
//
if ( ! command . arguments . edits . empty ( ) & &
command . arguments . edits [ command . arguments . edits . size ( ) - 1 ] . range . end . line = = edit - > range . start . line ) {
command . arguments . edits [ command . arguments . edits . size ( ) - 1 ] . newText + = edit - > newText ;
}
else {
command . arguments . edits . push_back ( * edit ) ;
}
}
if ( command . arguments . edits . empty ( ) )
break ;
// If we're inserting at the end of the document, put a newline before the insertion.
if ( command . arguments . edits [ 0 ] . range . start . line > = default_line )
command . arguments . edits [ 0 ] . newText . insert ( 0 , " \n " ) ;
command . arguments . textDocumentUri = * impl_uri ;
command . title = " Auto-Implement " + std : : to_string ( num_edits ) + " methods on " + type - > def . short_name ;
command . command = " cquery._autoImplement " ;
response . result . push_back ( command ) ;
break ;
}
case SymbolKind : : Func : {
optional < QueryFunc > & func = db - > funcs [ sym . idx . idx ] ;
if ( ! func | | func - > def . definition_extent )
break ;
EnsureImplFile ( db , file_id , impl_uri /*out*/ , impl_file_id /*out*/ ) ;
// Get implementation file.
Out_TextDocumentCodeAction : : Command command ;
command . title = " Auto-Implement " + func - > def . short_name ;
command . command = " cquery._autoImplement " ;
command . arguments . textDocumentUri = * impl_uri ;
optional < lsTextEdit > edit = BuildAutoImplementForFunction ( db , working_files , working_file , default_line , file_id , * impl_file_id , * func ) ;
if ( ! edit )
break ;
// If we're inserting at the end of the document, put a newline before the insertion.
if ( edit - > range . start . line > = default_line )
edit - > newText . insert ( 0 , " \n " ) ;
command . arguments . edits . push_back ( * edit ) ;
response . result . push_back ( command ) ;
break ;
}
default :
break ;
}
// Only show one auto-impl section.
if ( ! response . result . empty ( ) )
break ;
}
2017-06-14 06:29:41 +00:00
std : : vector < lsDiagnostic > diagnostics ;
working_files - > DoAction ( [ & ] ( ) {
diagnostics = working_file - > diagnostics_ ;
} ) ;
for ( lsDiagnostic & diag : diagnostics ) {
2017-05-29 23:57:19 +00:00
if ( diag . range . start . line ! = msg - > params . range . start . line )
continue ;
// For error diagnostics, provide an action to resolve an include.
// TODO: find a way to index diagnostic contents so line numbers
// don't get mismatched when actively editing a file.
std : : string include_query = LexWordAroundPos ( diag . range . start , working_file - > buffer_content ) ;
if ( diag . severity = = lsDiagnosticSeverity : : Error & & ! include_query . empty ( ) ) {
const size_t kMaxResults = 20 ;
std : : unordered_set < std : : string > include_absolute_paths ;
// Find include candidate strings.
for ( int i = 0 ; i < db - > detailed_names . size ( ) ; + + i ) {
if ( include_absolute_paths . size ( ) > kMaxResults )
break ;
if ( db - > detailed_names [ i ] . find ( include_query ) = = std : : string : : npos )
continue ;
optional < QueryFileId > decl_file_id = GetDeclarationFileForSymbol ( db , db - > symbols [ i ] ) ;
if ( ! decl_file_id )
continue ;
optional < QueryFile > & decl_file = db - > files [ decl_file_id - > id ] ;
if ( ! decl_file )
continue ;
include_absolute_paths . insert ( decl_file - > def . path ) ;
}
// Build include strings.
2017-06-14 06:09:25 +00:00
std : : unordered_set < std : : string > include_insert_strings ;
2017-05-29 23:57:19 +00:00
include_insert_strings . reserve ( include_absolute_paths . size ( ) ) ;
for ( const std : : string & path : include_absolute_paths ) {
optional < lsCompletionItem > item = include_complete - > FindCompletionItemForAbsolutePath ( path ) ;
if ( ! item )
continue ;
if ( item - > textEdit )
2017-06-14 06:09:25 +00:00
include_insert_strings . insert ( item - > textEdit - > newText ) ;
2017-05-29 23:57:19 +00:00
else if ( ! item - > insertText . empty ( ) )
2017-06-14 06:09:25 +00:00
include_insert_strings . insert ( item - > insertText ) ;
2017-05-29 23:57:19 +00:00
else
assert ( false & & " unable to determine insert string for include completion item " ) ;
}
// Build code action.
if ( ! include_insert_strings . empty ( ) ) {
Out_TextDocumentCodeAction : : Command command ;
// Build edits.
for ( const std : : string & include_insert_string : include_insert_strings ) {
lsTextEdit edit ;
optional < int > include_line = FindIncludeLine ( working_file - > all_buffer_lines , include_insert_string ) ;
if ( ! include_line )
continue ;
edit . range . start . line = * include_line ;
edit . range . end . line = * include_line ;
edit . newText = include_insert_string + " \n " ;
command . arguments . edits . push_back ( edit ) ;
}
// Setup metadata and send to client.
if ( include_insert_strings . size ( ) = = 1 )
2017-06-14 06:09:25 +00:00
command . title = " Insert " + * include_insert_strings . begin ( ) ;
2017-05-29 23:57:19 +00:00
else
2017-06-29 04:40:30 +00:00
command . title = " Pick one of " + std : : to_string ( command . arguments . edits . size ( ) ) + " includes to insert " ;
2017-05-29 23:57:19 +00:00
command . command = " cquery._insertInclude " ;
command . arguments . textDocumentUri = msg - > params . textDocument . uri ;
response . result . push_back ( command ) ;
}
}
// clang does not provide accurate enough column reporting for
2017-05-20 19:31:07 +00:00
// diagnostics to do good column filtering, so report all
// diagnostics on the line.
2017-05-29 23:57:19 +00:00
if ( ! diag . fixits_ . empty ( ) ) {
2017-05-20 19:31:07 +00:00
Out_TextDocumentCodeAction : : Command command ;
command . title = " FixIt: " + diag . message ;
command . command = " cquery._applyFixIt " ;
command . arguments . textDocumentUri = msg - > params . textDocument . uri ;
command . arguments . edits = diag . fixits_ ;
response . result . push_back ( command ) ;
}
}
ipc - > SendOutMessageToClient ( IpcId : : TextDocumentCodeAction , response ) ;
break ;
}
2017-04-23 20:19:09 +00:00
case IpcId : : TextDocumentCodeLens : {
auto msg = static_cast < Ipc_TextDocumentCodeLens * > ( message . get ( ) ) ;
Out_TextDocumentCodeLens response ;
response . id = msg - > id ;
lsDocumentUri file_as_uri = msg - > params . textDocument . uri ;
2017-05-26 06:40:38 +00:00
std : : string path = file_as_uri . GetPath ( ) ;
2017-05-27 04:21:00 +00:00
clang_complete - > NotifyView ( path ) ;
2017-04-23 20:19:09 +00:00
2017-05-27 07:10:21 +00:00
QueryFile * file ;
if ( ! FindFileOrFail ( db , msg - > id , msg - > params . textDocument . uri . GetPath ( ) , & file ) )
2017-03-29 06:33:38 +00:00
break ;
2017-05-27 07:10:21 +00:00
2017-04-23 20:19:09 +00:00
CommonCodeLensParams common ;
common . result = & response . result ;
common . db = db ;
common . working_files = working_files ;
common . working_file = working_files - > GetFileByFilename ( file - > def . path ) ;
for ( SymbolRef ref : file - > def . outline ) {
// NOTE: We OffsetColumn so that the code lens always show up in a
// predictable order. Otherwise, the client may randomize it.
SymbolIdx symbol = ref . idx ;
switch ( symbol . kind ) {
case SymbolKind : : Type : {
optional < QueryType > & type = db - > types [ symbol . idx ] ;
if ( ! type )
continue ;
2017-05-21 01:26:50 +00:00
AddCodeLens ( " ref " , " refs " , & common , ref . loc . OffsetStartColumn ( 0 ) , type - > uses , type - > def . definition_spelling , true /*force_display*/ ) ;
AddCodeLens ( " derived " , " derived " , & common , ref . loc . OffsetStartColumn ( 1 ) , ToQueryLocation ( db , type - > derived ) , nullopt , false /*force_display*/ ) ;
AddCodeLens ( " var " , " vars " , & common , ref . loc . OffsetStartColumn ( 2 ) , ToQueryLocation ( db , type - > instances ) , nullopt , false /*force_display*/ ) ;
2017-04-23 20:19:09 +00:00
break ;
}
case SymbolKind : : Func : {
optional < QueryFunc > & func = db - > funcs [ symbol . idx ] ;
if ( ! func )
continue ;
2017-04-11 07:29:36 +00:00
2017-05-21 23:22:00 +00:00
int16_t offset = 0 ;
2017-04-11 07:29:36 +00:00
2017-04-23 20:19:09 +00:00
std : : vector < QueryFuncRef > base_callers = GetCallersForAllBaseFunctions ( db , * func ) ;
std : : vector < QueryFuncRef > derived_callers = GetCallersForAllDerivedFunctions ( db , * func ) ;
if ( base_callers . empty ( ) & & derived_callers . empty ( ) ) {
2017-05-21 01:26:50 +00:00
AddCodeLens ( " call " , " calls " , & common , ref . loc . OffsetStartColumn ( offset + + ) , ToQueryLocation ( db , func - > callers ) , nullopt , true /*force_display*/ ) ;
2017-04-23 20:19:09 +00:00
}
else {
2017-05-21 01:26:50 +00:00
AddCodeLens ( " direct call " , " direct calls " , & common , ref . loc . OffsetStartColumn ( offset + + ) , ToQueryLocation ( db , func - > callers ) , nullopt , false /*force_display*/ ) ;
2017-04-23 20:19:09 +00:00
if ( ! base_callers . empty ( ) )
2017-05-21 01:26:50 +00:00
AddCodeLens ( " base call " , " base calls " , & common , ref . loc . OffsetStartColumn ( offset + + ) , ToQueryLocation ( db , base_callers ) , nullopt , false /*force_display*/ ) ;
2017-04-23 20:19:09 +00:00
if ( ! derived_callers . empty ( ) )
2017-05-21 01:26:50 +00:00
AddCodeLens ( " derived call " , " derived calls " , & common , ref . loc . OffsetStartColumn ( offset + + ) , ToQueryLocation ( db , derived_callers ) , nullopt , false /*force_display*/ ) ;
2017-04-23 20:19:09 +00:00
}
2017-04-11 07:29:36 +00:00
2017-05-21 01:26:50 +00:00
AddCodeLens ( " derived " , " derived " , & common , ref . loc . OffsetStartColumn ( offset + + ) , ToQueryLocation ( db , func - > derived ) , nullopt , false /*force_display*/ ) ;
2017-04-23 20:19:09 +00:00
// "Base"
optional < QueryLocation > base_loc = GetBaseDefinitionOrDeclarationSpelling ( db , * func ) ;
if ( base_loc ) {
optional < lsLocation > ls_base = GetLsLocation ( db , working_files , * base_loc ) ;
if ( ls_base ) {
optional < lsRange > range = GetLsRange ( common . working_file , ref . loc . range ) ;
if ( range ) {
TCodeLens code_lens ;
code_lens . range = * range ;
code_lens . range . start . character + = offset + + ;
code_lens . command = lsCommand < lsCodeLensCommandArguments > ( ) ;
code_lens . command - > title = " Base " ;
2017-05-07 06:56:04 +00:00
code_lens . command - > command = " cquery.goto " ;
2017-04-23 20:19:09 +00:00
code_lens . command - > arguments . uri = ls_base - > uri ;
code_lens . command - > arguments . position = ls_base - > range . start ;
response . result . push_back ( code_lens ) ;
}
2017-04-11 08:43:35 +00:00
}
}
2017-04-23 20:19:09 +00:00
break ;
2017-04-11 08:43:35 +00:00
}
2017-04-23 20:19:09 +00:00
case SymbolKind : : Var : {
optional < QueryVar > & var = db - > vars [ symbol . idx ] ;
if ( ! var )
continue ;
2017-04-11 08:43:35 +00:00
2017-05-21 01:26:50 +00:00
if ( var - > def . is_local & & ! config - > codeLensOnLocalVariables )
continue ;
2017-06-29 06:34:04 +00:00
bool force_display = true ;
// Do not show 0 refs on macro with no uses, as it is most likely a
// header guard.
if ( var - > def . is_macro )
force_display = false ;
AddCodeLens ( " ref " , " refs " , & common , ref . loc . OffsetStartColumn ( 0 ) , var - > uses , var - > def . definition_spelling , force_display ) ;
2017-04-23 20:19:09 +00:00
break ;
}
case SymbolKind : : File :
case SymbolKind : : Invalid : {
assert ( false & & " unexpected " ) ;
break ;
}
} ;
2017-03-29 06:33:38 +00:00
}
2017-04-22 07:32:29 +00:00
2017-04-23 20:19:09 +00:00
ipc - > SendOutMessageToClient ( IpcId : : TextDocumentCodeLens , response ) ;
break ;
2017-03-17 07:58:41 +00:00
}
2017-03-06 08:48:51 +00:00
2017-04-23 20:19:09 +00:00
case IpcId : : WorkspaceSymbol : {
2017-05-21 19:51:15 +00:00
// TODO: implement fuzzy search, see https://github.com/junegunn/fzf/blob/master/src/matcher.go for inspiration
2017-04-23 20:19:09 +00:00
auto msg = static_cast < Ipc_WorkspaceSymbol * > ( message . get ( ) ) ;
2017-03-25 19:18:25 +00:00
2017-04-23 20:19:09 +00:00
Out_WorkspaceSymbol response ;
response . id = msg - > id ;
2017-03-25 19:18:25 +00:00
2017-07-28 02:14:33 +00:00
LOG_S ( INFO ) < < " [querydb] Considering " < < db - > detailed_names . size ( )
< < " candidates for query " < < msg - > params . query ;
2017-03-25 19:18:25 +00:00
2017-04-23 20:19:09 +00:00
std : : string query = msg - > params . query ;
2017-06-29 06:59:38 +00:00
std : : unordered_set < std : : string > inserted_results ;
inserted_results . reserve ( config - > maxWorkspaceSearchResults ) ;
2017-04-23 20:19:09 +00:00
for ( int i = 0 ; i < db - > detailed_names . size ( ) ; + + i ) {
2017-06-17 02:42:14 +00:00
if ( db - > detailed_names [ i ] . find ( query ) ! = std : : string : : npos ) {
2017-06-29 06:59:38 +00:00
// Do not show the same entry twice.
if ( ! inserted_results . insert ( db - > detailed_names [ i ] ) . second )
continue ;
2017-06-17 02:42:14 +00:00
InsertSymbolIntoResult ( db , working_files , db - > symbols [ i ] , & response . result ) ;
if ( response . result . size ( ) > = config - > maxWorkspaceSearchResults )
break ;
2017-04-23 20:19:09 +00:00
}
2017-06-17 02:42:14 +00:00
}
2017-03-25 19:18:25 +00:00
2017-06-17 02:42:14 +00:00
if ( response . result . size ( ) < config - > maxWorkspaceSearchResults ) {
for ( int i = 0 ; i < db - > detailed_names . size ( ) ; + + i ) {
if ( SubstringMatch ( query , db - > detailed_names [ i ] ) ) {
2017-06-29 06:59:38 +00:00
// Do not show the same entry twice.
if ( ! inserted_results . insert ( db - > detailed_names [ i ] ) . second )
continue ;
2017-06-17 02:42:14 +00:00
InsertSymbolIntoResult ( db , working_files , db - > symbols [ i ] , & response . result ) ;
if ( response . result . size ( ) > = config - > maxWorkspaceSearchResults )
break ;
2017-04-23 20:19:09 +00:00
}
2017-04-12 07:19:49 +00:00
}
2017-04-09 22:16:06 +00:00
}
2017-03-25 19:18:25 +00:00
2017-07-28 02:14:33 +00:00
LOG_S ( INFO ) < < " [querydb] Found " < < response . result . size ( ) < < " results for query " < < query ;
2017-04-23 20:19:09 +00:00
ipc - > SendOutMessageToClient ( IpcId : : WorkspaceSymbol , response ) ;
break ;
}
2017-03-25 19:18:25 +00:00
2017-04-23 20:19:09 +00:00
default : {
2017-07-28 02:14:33 +00:00
LOG_S ( INFO ) < < " [querydb] Unhandled IPC message " < < IpcIdToString ( message - > method_id ) ;
2017-04-23 20:19:09 +00:00
exit ( 1 ) ;
}
2017-03-15 04:59:05 +00:00
}
}
2017-03-25 19:18:25 +00:00
// TODO: consider rate-limiting and checking for IPC messages so we don't block
// requests / we can serve partial requests.
2017-04-08 06:45:28 +00:00
2017-03-25 19:18:25 +00:00
while ( true ) {
2017-04-08 20:00:08 +00:00
optional < Index_DoIdMap > request = queue_do_id_map - > TryDequeue ( ) ;
if ( ! request )
2017-03-25 19:18:25 +00:00
break ;
2017-04-08 06:45:28 +00:00
2017-04-23 22:45:40 +00:00
did_work = true ;
2017-04-08 20:00:08 +00:00
2017-07-30 04:24:02 +00:00
Index_OnIdMapped response ( request - > perf , request - > is_interactive ) ;
2017-04-08 06:45:28 +00:00
Timer time ;
2017-05-20 08:07:54 +00:00
2017-07-30 04:24:02 +00:00
assert ( request - > current ) ;
std : : unique_ptr < CacheManager : : Entry > previous ;
CacheManager : : Entry * current = nullptr ;
{
auto id_map = MakeUnique < IdMap > ( db , request - > current - > id_cache ) ;
std : : unique_ptr < CacheManager : : Entry > current0 =
MakeUnique < CacheManager : : Entry > ( std : : move ( request - > current ) ,
std : : move ( id_map ) ) ;
current = current0 . get ( ) ;
previous = db_cache - > UpdateAndReturnOldFile ( std : : move ( current0 ) ) ;
2017-04-08 06:45:28 +00:00
}
2017-04-08 20:00:08 +00:00
2017-07-30 04:24:02 +00:00
if ( previous ) {
response . previous_id_map = std : : move ( previous - > ids ) ;
response . previous_index = std : : move ( previous - > file ) ;
}
response . current_id_map = current - > ids . get ( ) ;
response . current_index = current - > file . get ( ) ;
2017-05-17 07:08:45 +00:00
response . perf . querydb_id_map = time . ElapsedMicrosecondsAndReset ( ) ;
2017-04-08 06:45:28 +00:00
queue_on_id_mapped - > Enqueue ( std : : move ( response ) ) ;
}
while ( true ) {
2017-04-08 20:00:08 +00:00
optional < Index_OnIndexed > response = queue_on_indexed - > TryDequeue ( ) ;
if ( ! response )
2017-04-08 06:45:28 +00:00
break ;
2017-03-15 04:59:05 +00:00
2017-04-23 22:45:40 +00:00
did_work = true ;
2017-03-25 19:18:25 +00:00
Timer time ;
2017-04-21 06:32:18 +00:00
for ( auto & updated_file : response - > update . files_def_update ) {
2017-07-30 04:24:02 +00:00
// TODO: We're reading a file on querydb thread. This is slow!! If this
// a real problem in practice we can load the file in a previous stage.
// It should be fine though because we only do it if the user has the
// file open.
2017-04-21 06:32:18 +00:00
WorkingFile * working_file = working_files - > GetFileByFilename ( updated_file . path ) ;
if ( working_file ) {
2017-07-30 04:24:02 +00:00
optional < std : : string > cached_file_contents = LoadCachedFileContents ( config , updated_file . path ) ;
if ( cached_file_contents )
working_file - > SetIndexContent ( * cached_file_contents ) ;
else
working_file - > SetIndexContent ( working_file - > buffer_content ) ;
time . ResetAndPrint ( " Update WorkingFile index contents (via disk load) for " + updated_file . path ) ;
2017-04-21 06:32:18 +00:00
}
}
2017-03-25 19:18:25 +00:00
db - > ApplyIndexUpdate ( & response - > update ) ;
2017-05-17 07:08:45 +00:00
//time.ResetAndPrint("[querydb] Applying index update");
2017-03-05 02:16:23 +00:00
}
2017-04-23 22:45:40 +00:00
return did_work ;
2017-03-05 02:16:23 +00:00
}
2017-03-15 04:59:05 +00:00
2017-07-30 04:24:02 +00:00
void QueryDbMain ( const std : : string & bin_name , Config * config , MultiQueueWaiter * waiter ) {
2017-03-25 19:18:25 +00:00
// Create queues.
2017-07-30 04:24:02 +00:00
IndexProcess_ResponseQueue queue_process_response ( waiter ) ;
2017-04-23 22:45:40 +00:00
Index_DoIdMapQueue queue_do_id_map ( waiter ) ;
Index_OnIdMappedQueue queue_on_id_mapped ( waiter ) ;
Index_OnIndexedQueue queue_on_indexed ( waiter ) ;
2017-04-08 06:45:28 +00:00
2017-03-26 21:40:34 +00:00
Project project ;
WorkingFiles working_files ;
2017-06-10 04:13:16 +00:00
ClangCompleteManager clang_complete (
2017-07-30 04:24:02 +00:00
config , & project , & working_files ,
std : : bind ( & EmitDiagnostics , & working_files , std : : placeholders : : _1 , std : : placeholders : : _2 ) ) ;
2017-05-27 04:21:00 +00:00
IncludeComplete include_complete ( config , & project ) ;
2017-06-30 06:51:22 +00:00
auto global_code_complete_cache = MakeUnique < CodeCompleteCache > ( ) ;
auto non_global_code_complete_cache = MakeUnique < CodeCompleteCache > ( ) ;
auto signature_cache = MakeUnique < CodeCompleteCache > ( ) ;
2017-04-08 22:54:36 +00:00
FileConsumer : : SharedState file_consumer_shared ;
2017-03-25 19:18:25 +00:00
2017-07-30 04:24:02 +00:00
InProcessQueryDbResponder responder ( & queue_process_response ) ;
ThreadedQueue < IndexProcess_Request > queue_process_request ;
auto indexer = MakeUnique < InProcessIndexer > ( & responder , & queue_process_request ) ;
//auto indexer = MakeUnique<OutOfProcessIndexer>(bin_name, &queue_process_response);
2017-03-25 19:18:25 +00:00
// Run query db main loop.
2017-04-19 00:05:14 +00:00
SetCurrentThreadName ( " querydb " ) ;
2017-04-15 05:41:35 +00:00
QueryDatabase db ;
2017-07-30 04:24:02 +00:00
CacheManager db_cache ;
2017-03-15 04:59:05 +00:00
while ( true ) {
2017-05-21 19:51:15 +00:00
bool did_work = QueryDbMainLoop (
2017-07-30 04:24:02 +00:00
config , & db , & db_cache , waiter , & queue_process_response , & queue_do_id_map , & queue_on_id_mapped , & queue_on_indexed ,
indexer . get ( ) ,
& project , & file_consumer_shared , & working_files ,
& clang_complete , & include_complete , global_code_complete_cache . get ( ) , non_global_code_complete_cache . get ( ) , signature_cache . get ( ) ) ;
2017-04-23 22:45:40 +00:00
if ( ! did_work ) {
IpcManager * ipc = IpcManager : : instance ( ) ;
waiter - > Wait ( {
ipc - > threaded_queue_for_server_ . get ( ) ,
& queue_do_id_map ,
& queue_on_indexed
} ) ;
}
2017-03-15 04:59:05 +00:00
}
}
2017-04-16 19:02:29 +00:00
2017-03-05 19:48:05 +00:00
// TODO: global lock on stderr output.
// Separate thread whose only job is to read from stdin and
// dispatch read commands to the actual indexer program. This
// cannot be done on the main thread because reading from std::cin
// blocks.
//
// |ipc| is connected to a server.
2017-05-21 19:51:15 +00:00
void LanguageServerStdinLoop ( Config * config , std : : unordered_map < IpcId , Timer > * request_times ) {
2017-04-16 21:49:48 +00:00
IpcManager * ipc = IpcManager : : instance ( ) ;
2017-04-19 00:05:14 +00:00
SetCurrentThreadName ( " stdin " ) ;
2017-03-05 02:16:23 +00:00
while ( true ) {
2017-03-25 21:57:06 +00:00
std : : unique_ptr < BaseIpcMessage > message = MessageRegistry : : instance ( ) - > ReadMessageFromStdin ( ) ;
2017-03-05 02:16:23 +00:00
// Message parsing can fail if we don't recognize the method.
if ( ! message )
continue ;
2017-04-21 07:46:51 +00:00
( * request_times ) [ message - > method_id ] = Timer ( ) ;
2017-04-24 01:24:09 +00:00
//std::cerr << "[stdin] Got message " << IpcIdToString(message->method_id) << std::endl;
2017-03-05 02:16:23 +00:00
switch ( message - > method_id ) {
2017-03-26 06:47:59 +00:00
case IpcId : : Initialized : {
// TODO: don't send output until we get this notification
break ;
}
case IpcId : : CancelRequest : {
// TODO: support cancellation
break ;
}
2017-04-23 20:19:09 +00:00
case IpcId : : Initialize :
2017-05-03 06:45:10 +00:00
case IpcId : : Exit :
2017-03-26 21:40:34 +00:00
case IpcId : : TextDocumentDidOpen :
case IpcId : : TextDocumentDidChange :
2017-04-10 00:08:54 +00:00
case IpcId : : TextDocumentDidClose :
case IpcId : : TextDocumentDidSave :
2017-04-14 08:21:03 +00:00
case IpcId : : TextDocumentRename :
2017-03-26 06:47:59 +00:00
case IpcId : : TextDocumentCompletion :
2017-05-15 07:28:53 +00:00
case IpcId : : TextDocumentSignatureHelp :
2017-04-03 02:21:21 +00:00
case IpcId : : TextDocumentDefinition :
2017-04-14 06:43:50 +00:00
case IpcId : : TextDocumentDocumentHighlight :
2017-04-14 05:18:02 +00:00
case IpcId : : TextDocumentHover :
2017-04-10 05:34:06 +00:00
case IpcId : : TextDocumentReferences :
2017-03-25 21:57:06 +00:00
case IpcId : : TextDocumentDocumentSymbol :
2017-05-21 04:30:59 +00:00
case IpcId : : TextDocumentDocumentLink :
2017-05-20 19:31:07 +00:00
case IpcId : : TextDocumentCodeAction :
2017-03-25 21:57:06 +00:00
case IpcId : : TextDocumentCodeLens :
2017-04-21 04:50:31 +00:00
case IpcId : : WorkspaceSymbol :
2017-05-07 06:56:04 +00:00
case IpcId : : CqueryFreshenIndex :
2017-05-24 07:17:29 +00:00
case IpcId : : CqueryTypeHierarchyTree :
case IpcId : : CqueryCallTreeInitial :
case IpcId : : CqueryCallTreeExpand :
2017-05-07 06:56:04 +00:00
case IpcId : : CqueryVars :
case IpcId : : CqueryCallers :
case IpcId : : CqueryBase :
case IpcId : : CqueryDerived : {
2017-04-16 21:49:48 +00:00
ipc - > SendMessage ( IpcManager : : Destination : : Server , std : : move ( message ) ) ;
2017-03-25 19:18:25 +00:00
break ;
}
2017-03-26 06:47:59 +00:00
default : {
2017-04-16 21:49:48 +00:00
std : : cerr < < " [stdin] Unhandled IPC message " < < IpcIdToString ( message - > method_id ) < < std : : endl ;
2017-03-26 06:47:59 +00:00
exit ( 1 ) ;
}
2017-03-03 08:12:11 +00:00
}
}
}
2017-04-16 21:49:48 +00:00
2017-03-06 08:48:51 +00:00
2017-03-05 19:48:05 +00:00
2017-04-16 19:02:29 +00:00
2017-04-23 22:45:40 +00:00
void StdoutMain ( std : : unordered_map < IpcId , Timer > * request_times , MultiQueueWaiter * waiter ) {
SetCurrentThreadName ( " stdout " ) ;
2017-04-16 23:57:31 +00:00
IpcManager * ipc = IpcManager : : instance ( ) ;
2017-04-16 19:02:29 +00:00
2017-04-23 22:45:40 +00:00
while ( true ) {
std : : vector < std : : unique_ptr < BaseIpcMessage > > messages = ipc - > GetMessages ( IpcManager : : Destination : : Client ) ;
if ( messages . empty ( ) ) {
waiter - > Wait ( { ipc - > threaded_queue_for_client_ . get ( ) } ) ;
continue ;
}
2017-04-16 19:02:29 +00:00
2017-04-23 22:45:40 +00:00
for ( auto & message : messages ) {
2017-04-24 01:24:09 +00:00
//std::cerr << "[stdout] Processing message " << IpcIdToString(message->method_id) << std::endl;
2017-04-22 06:15:46 +00:00
2017-04-23 22:45:40 +00:00
switch ( message - > method_id ) {
case IpcId : : Cout : {
auto msg = static_cast < Ipc_Cout * > ( message . get ( ) ) ;
2017-04-21 07:46:51 +00:00
2017-06-14 06:59:40 +00:00
if ( ShouldDisplayIpcTiming ( msg - > original_ipc_id ) ) {
2017-05-20 08:07:29 +00:00
Timer time = ( * request_times ) [ msg - > original_ipc_id ] ;
time . ResetAndPrint ( " [e2e] Running " + std : : string ( IpcIdToString ( msg - > original_ipc_id ) ) ) ;
}
2017-04-16 19:02:29 +00:00
2017-04-23 22:45:40 +00:00
std : : cout < < msg - > content ;
std : : cout . flush ( ) ;
break ;
}
default : {
std : : cerr < < " [stdout] Unhandled IPC message " < < IpcIdToString ( message - > method_id ) < < std : : endl ;
exit ( 1 ) ;
}
2017-04-16 23:57:31 +00:00
}
}
}
}
2017-04-16 19:02:29 +00:00
2017-07-30 04:24:02 +00:00
void LanguageServerMain ( const std : : string & bin_name , Config * config , MultiQueueWaiter * waiter ) {
2017-04-21 07:46:51 +00:00
std : : unordered_map < IpcId , Timer > request_times ;
2017-04-23 20:19:09 +00:00
// Start stdin reader. Reading from stdin is a blocking operation so this
// needs a dedicated thread.
2017-04-21 07:46:51 +00:00
new std : : thread ( [ & ] ( ) {
LanguageServerStdinLoop ( config , & request_times ) ;
2017-04-18 02:59:48 +00:00
} ) ;
2017-04-19 00:05:14 +00:00
2017-04-23 20:19:09 +00:00
// Start querydb thread. querydb will start indexer threads as needed.
2017-04-23 22:45:40 +00:00
new std : : thread ( [ & ] ( ) {
2017-07-30 04:24:02 +00:00
QueryDbMain ( bin_name , config , waiter ) ;
2017-04-23 20:19:09 +00:00
} ) ;
// We run a dedicated thread for writing to stdout because there can be an
// unknown number of delays when output information.
2017-04-23 22:45:40 +00:00
StdoutMain ( & request_times , waiter ) ;
2017-03-16 07:36:49 +00:00
}
2017-03-03 08:12:11 +00:00
2017-04-16 19:02:29 +00:00
2017-07-30 04:24:02 +00:00
// What's the plan?
//
// - start up a separate process for indexing
// - send file paths to it to index raw compile_commands.json, it can reparse as needed
// -
2017-04-16 19:02:29 +00:00
2017-03-25 01:28:09 +00:00
int main ( int argc , char * * argv ) {
2017-07-28 02:14:33 +00:00
loguru : : init ( argc , argv ) ;
loguru : : g_flush_interval_ms = 0 ;
2017-04-23 22:45:40 +00:00
MultiQueueWaiter waiter ;
IpcManager : : CreateInstance ( & waiter ) ;
2017-04-16 22:48:54 +00:00
2017-04-03 01:34:15 +00:00
//bool loop = true;
//while (loop)
// std::this_thread::sleep_for(std::chrono::milliseconds(10));
2017-06-09 07:08:06 +00:00
//std::this_thread::sleep_for(std::chrono::seconds(10));
2017-03-03 08:12:11 +00:00
2017-03-25 20:27:28 +00:00
PlatformInit ( ) ;
2017-04-16 21:51:47 +00:00
IndexInit ( ) ;
2017-03-25 19:18:25 +00:00
RegisterMessageTypes ( ) ;
2017-03-15 04:59:05 +00:00
2017-03-25 19:18:25 +00:00
std : : unordered_map < std : : string , std : : string > options =
ParseOptions ( argc , argv ) ;
2017-04-23 19:45:58 +00:00
if ( HasOption ( options , " --test " ) ) {
2017-03-17 23:45:10 +00:00
doctest : : Context context ;
context . applyCommandLine ( argc , argv ) ;
int res = context . run ( ) ;
if ( context . shouldExit ( ) )
2017-03-25 19:18:25 +00:00
return res ;
2017-03-17 23:45:10 +00:00
2017-05-25 03:06:05 +00:00
for ( int i = 0 ; i < 1 ; + + i )
2017-05-25 02:04:19 +00:00
RunTests ( ) ;
2017-05-27 07:47:03 +00:00
/*
2017-05-25 03:06:05 +00:00
for ( int i = 0 ; i < 1 ; + + i ) {
2017-05-25 02:04:19 +00:00
std : : this_thread : : sleep_for ( std : : chrono : : seconds ( 5 ) ) ;
2017-05-25 03:06:05 +00:00
std : : cerr < < " [POST] Memory usage: " < < GetProcessMemoryUsedInMb ( ) < < " mb " < < std : : endl ;
2017-05-25 02:04:19 +00:00
}
2017-05-27 07:47:03 +00:00
*/
2017-05-25 02:04:19 +00:00
std : : cerr < < std : : endl < < " [Enter] to exit " < < std : : endl ;
std : : cin . get ( ) ;
2017-03-15 04:59:05 +00:00
return 0 ;
}
2017-07-30 04:24:02 +00:00
else if ( HasOption ( options , " --indexer " ) ) {
IndexProcessMain ( ) ;
return 0 ;
}
2017-04-23 19:45:58 +00:00
else if ( HasOption ( options , " --language-server " ) ) {
//std::cerr << "Running language server" << std::endl;
2017-05-21 19:51:15 +00:00
Config config ;
2017-07-30 04:24:02 +00:00
LanguageServerMain ( argv [ 0 ] , & config , & waiter ) ;
2017-04-23 19:45:58 +00:00
return 0 ;
}
else {
std : : cout < < R " help(cquery help:
2017-03-25 19:18:25 +00:00
2017-04-23 19:45:58 +00:00
cquery is a low - latency C + + language server .
2017-03-25 19:18:25 +00:00
General :
- - help Print this help information .
- - language - server
2017-04-23 19:45:58 +00:00
Run as a language server . This implements the language
server spec over STDIN and STDOUT .
2017-03-25 19:18:25 +00:00
- - test Run tests . Does nothing if test support is not compiled in .
Configuration :
2017-04-23 19:45:58 +00:00
When opening up a directory , cquery will look for a compile_commands . json
file emitted by your preferred build system . If not present , cquery will
use a recursive directory listing instead . Command line flags can be
provided by adding a " clang_args " file in the top - level directory . Each
line in that file is a separate argument .
There are also a number of configuration options available when
initializing the language server - your editor should have tooling to
2017-05-21 19:51:15 +00:00
describe those options . See | Config | in this source code for a detailed
list of all currently supported options .
2017-03-25 19:18:25 +00:00
) help " ;
2017-03-05 19:48:05 +00:00
return 0 ;
2017-02-25 23:59:09 +00:00
}
}
2017-05-29 21:18:35 +00:00
TEST_SUITE ( " LexFunctionDeclaration " ) ;
TEST_CASE ( " simple " ) {
std : : string buffer_content = " void Foo(); " ;
lsPosition declaration = CharPos ( buffer_content , ' F ' ) ;
std : : string insert_text ;
int newlines_after_name = 0 ;
LexFunctionDeclaration ( buffer_content , declaration , nullopt , & insert_text , & newlines_after_name ) ;
REQUIRE ( insert_text = = " void Foo() { \n } " ) ;
REQUIRE ( newlines_after_name = = 0 ) ;
LexFunctionDeclaration ( buffer_content , declaration , std : : string ( " Type " ) , & insert_text , & newlines_after_name ) ;
REQUIRE ( insert_text = = " void Type::Foo() { \n } " ) ;
REQUIRE ( newlines_after_name = = 0 ) ;
}
TEST_CASE ( " ctor " ) {
std : : string buffer_content = " Foo(); " ;
lsPosition declaration = CharPos ( buffer_content , ' F ' ) ;
std : : string insert_text ;
int newlines_after_name = 0 ;
LexFunctionDeclaration ( buffer_content , declaration , std : : string ( " Foo " ) , & insert_text , & newlines_after_name ) ;
REQUIRE ( insert_text = = " Foo::Foo() { \n } " ) ;
REQUIRE ( newlines_after_name = = 0 ) ;
}
TEST_CASE ( " dtor " ) {
std : : string buffer_content = " ~Foo(); " ;
lsPosition declaration = CharPos ( buffer_content , ' ~ ' ) ;
std : : string insert_text ;
int newlines_after_name = 0 ;
LexFunctionDeclaration ( buffer_content , declaration , std : : string ( " Foo " ) , & insert_text , & newlines_after_name ) ;
REQUIRE ( insert_text = = " Foo::~Foo() { \n } " ) ;
REQUIRE ( newlines_after_name = = 0 ) ;
}
TEST_CASE ( " complex return type " ) {
std : : string buffer_content = " std::vector<int> Foo(); " ;
lsPosition declaration = CharPos ( buffer_content , ' F ' ) ;
std : : string insert_text ;
int newlines_after_name = 0 ;
LexFunctionDeclaration ( buffer_content , declaration , nullopt , & insert_text , & newlines_after_name ) ;
REQUIRE ( insert_text = = " std::vector<int> Foo() { \n } " ) ;
REQUIRE ( newlines_after_name = = 0 ) ;
LexFunctionDeclaration ( buffer_content , declaration , std : : string ( " Type " ) , & insert_text , & newlines_after_name ) ;
REQUIRE ( insert_text = = " std::vector<int> Type::Foo() { \n } " ) ;
REQUIRE ( newlines_after_name = = 0 ) ;
}
TEST_CASE ( " extra complex return type " ) {
std : : string buffer_content = " std::function < int() > \n Foo(); " ;
lsPosition declaration = CharPos ( buffer_content , ' F ' ) ;
std : : string insert_text ;
int newlines_after_name = 0 ;
LexFunctionDeclaration ( buffer_content , declaration , nullopt , & insert_text , & newlines_after_name ) ;
REQUIRE ( insert_text = = " std::function < int() > \n Foo() { \n } " ) ;
REQUIRE ( newlines_after_name = = 0 ) ;
LexFunctionDeclaration ( buffer_content , declaration , std : : string ( " Type " ) , & insert_text , & newlines_after_name ) ;
REQUIRE ( insert_text = = " std::function < int() > \n Type::Foo() { \n } " ) ;
REQUIRE ( newlines_after_name = = 0 ) ;
}
TEST_CASE ( " parameters " ) {
std : : string buffer_content = " void Foo(int a, \n \n int b); " ;
lsPosition declaration = CharPos ( buffer_content , ' F ' ) ;
std : : string insert_text ;
int newlines_after_name = 0 ;
LexFunctionDeclaration ( buffer_content , declaration , nullopt , & insert_text , & newlines_after_name ) ;
REQUIRE ( insert_text = = " void Foo(int a, \n \n int b) { \n } " ) ;
REQUIRE ( newlines_after_name = = 2 ) ;
LexFunctionDeclaration ( buffer_content , declaration , std : : string ( " Type " ) , & insert_text , & newlines_after_name ) ;
REQUIRE ( insert_text = = " void Type::Foo(int a, \n \n int b) { \n } " ) ;
REQUIRE ( newlines_after_name = = 2 ) ;
}
2017-05-29 23:57:19 +00:00
TEST_SUITE_END ( ) ;
TEST_SUITE ( " LexWordAroundPos " ) ;
TEST_CASE ( " edges " ) {
std : : string content = " Foobar " ;
REQUIRE ( LexWordAroundPos ( CharPos ( content , ' F ' ) , content ) = = " Foobar " ) ;
REQUIRE ( LexWordAroundPos ( CharPos ( content , ' o ' ) , content ) = = " Foobar " ) ;
REQUIRE ( LexWordAroundPos ( CharPos ( content , ' b ' ) , content ) = = " Foobar " ) ;
REQUIRE ( LexWordAroundPos ( CharPos ( content , ' a ' ) , content ) = = " Foobar " ) ;
REQUIRE ( LexWordAroundPos ( CharPos ( content , ' r ' ) , content ) = = " Foobar " ) ;
}
TEST_CASE ( " simple " ) {
std : : string content = " Foobar " ;
REQUIRE ( LexWordAroundPos ( CharPos ( content , ' F ' ) , content ) = = " Foobar " ) ;
REQUIRE ( LexWordAroundPos ( CharPos ( content , ' o ' ) , content ) = = " Foobar " ) ;
REQUIRE ( LexWordAroundPos ( CharPos ( content , ' b ' ) , content ) = = " Foobar " ) ;
REQUIRE ( LexWordAroundPos ( CharPos ( content , ' a ' ) , content ) = = " Foobar " ) ;
REQUIRE ( LexWordAroundPos ( CharPos ( content , ' r ' ) , content ) = = " Foobar " ) ;
}
TEST_CASE ( " underscores and numbers " ) {
std : : string content = " _my_t5ype7 " ;
REQUIRE ( LexWordAroundPos ( CharPos ( content , ' _ ' ) , content ) = = " _my_t5ype7 " ) ;
REQUIRE ( LexWordAroundPos ( CharPos ( content , ' 5 ' ) , content ) = = " _my_t5ype7 " ) ;
REQUIRE ( LexWordAroundPos ( CharPos ( content , ' e ' ) , content ) = = " _my_t5ype7 " ) ;
REQUIRE ( LexWordAroundPos ( CharPos ( content , ' 7 ' ) , content ) = = " _my_t5ype7 " ) ;
}
TEST_CASE ( " dot, dash, colon are skipped " ) {
std : : string content = " 1. 2- 3: " ;
REQUIRE ( LexWordAroundPos ( CharPos ( content , ' 1 ' ) , content ) = = " 1 " ) ;
REQUIRE ( LexWordAroundPos ( CharPos ( content , ' 2 ' ) , content ) = = " 2 " ) ;
REQUIRE ( LexWordAroundPos ( CharPos ( content , ' 3 ' ) , content ) = = " 3 " ) ;
}
TEST_SUITE_END ( ) ;
TEST_SUITE ( " FindIncludeLine " ) ;
TEST_CASE ( " in document " ) {
std : : vector < std : : string > lines = {
" #include <bbb> " , // 0
" #include <ddd> " // 1
} ;
REQUIRE ( FindIncludeLine ( lines , " #include <bbb> " ) = = nullopt ) ;
}
TEST_CASE ( " insert before " ) {
std : : vector < std : : string > lines = {
" #include <bbb> " , // 0
" #include <ddd> " // 1
} ;
REQUIRE ( FindIncludeLine ( lines , " #include <aaa> " ) = = 0 ) ;
}
TEST_CASE ( " insert middle " ) {
std : : vector < std : : string > lines = {
" #include <bbb> " , // 0
" #include <ddd> " // 1
} ;
REQUIRE ( FindIncludeLine ( lines , " #include <ccc> " ) = = 1 ) ;
}
TEST_CASE ( " insert after " ) {
std : : vector < std : : string > lines = {
" #include <bbb> " , // 0
" #include <ddd> " , // 1
" " , // 2
} ;
REQUIRE ( FindIncludeLine ( lines , " #include <eee> " ) = = 2 ) ;
}
TEST_CASE ( " ignore header " ) {
std : : vector < std : : string > lines = {
" // FOOBAR " , // 0
" // FOOBAR " , // 1
" // FOOBAR " , // 2
" // FOOBAR " , // 3
" " , // 4
" #include <bbb> " , // 5
" #include <ddd> " , // 6
" " , // 7
} ;
REQUIRE ( FindIncludeLine ( lines , " #include <a> " ) = = 5 ) ;
REQUIRE ( FindIncludeLine ( lines , " #include <c> " ) = = 6 ) ;
REQUIRE ( FindIncludeLine ( lines , " #include <e> " ) = = 7 ) ;
}
TEST_SUITE_END ( ) ;