2017-03-25 20:15:00 +00:00
// TODO: cleanup includes
2017-04-09 02:24:32 +00:00
# include "cache.h"
2017-03-26 21:40:34 +00:00
# include "code_completion.h"
2017-04-08 22:54:36 +00:00
# include "file_consumer.h"
2017-04-15 04:53:10 +00:00
# include "fuzzy.h"
2017-02-25 23:59:09 +00:00
# include "indexer.h"
# include "query.h"
2017-03-05 02:16:23 +00:00
# include "language_server_api.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-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-03-25 20:27:28 +00:00
# include <doctest/doctest.h>
# include <rapidjson/istreamwrapper.h>
# include <rapidjson/ostreamwrapper.h>
2017-03-31 05:30:50 +00:00
# include <fstream>
2017-04-17 01:22:59 +00:00
# include <functional>
2017-03-25 20:27:28 +00:00
# include <iostream>
# include <string>
# 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-03-25 20:27:28 +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
struct IpcManager {
2017-04-16 21:49:48 +00:00
static IpcManager * instance_ ;
static IpcManager * instance ( ) {
return instance_ ;
2017-04-16 19:02:29 +00:00
}
2017-04-23 22:45:40 +00:00
static void CreateInstance ( MultiQueueWaiter * waiter ) {
instance_ = new IpcManager ( waiter ) ;
2017-04-16 22:48:54 +00:00
}
2017-04-16 19:02:29 +00:00
2017-04-16 21:49:48 +00:00
std : : unique_ptr < ThreadedQueue < std : : unique_ptr < BaseIpcMessage > > > threaded_queue_for_client_ ;
std : : unique_ptr < ThreadedQueue < std : : unique_ptr < BaseIpcMessage > > > threaded_queue_for_server_ ;
2017-04-16 19:02:29 +00:00
2017-04-16 21:49:48 +00:00
enum class Destination {
Client , Server
} ;
ThreadedQueue < std : : unique_ptr < BaseIpcMessage > > * GetThreadedQueue ( Destination destination ) {
return destination = = Destination : : Client ? threaded_queue_for_client_ . get ( ) : threaded_queue_for_server_ . get ( ) ;
}
2017-04-16 19:02:29 +00:00
2017-04-21 07:46:51 +00:00
void SendOutMessageToClient ( IpcId id , lsBaseOutMessage & response ) {
2017-04-16 19:02:29 +00:00
std : : ostringstream sstream ;
response . Write ( sstream ) ;
2017-04-23 19:45:58 +00:00
auto out = MakeUnique < Ipc_Cout > ( ) ;
out - > content = sstream . str ( ) ;
out - > original_ipc_id = id ;
GetThreadedQueue ( Destination : : Client ) - > Enqueue ( std : : move ( out ) ) ;
2017-04-16 19:02:29 +00:00
}
2017-04-16 21:49:48 +00:00
void SendMessage ( Destination destination , std : : unique_ptr < BaseIpcMessage > message ) {
2017-04-23 19:45:58 +00:00
GetThreadedQueue ( destination ) - > Enqueue ( std : : move ( message ) ) ;
2017-04-16 19:02:29 +00:00
}
2017-04-16 20:43:30 +00:00
std : : vector < std : : unique_ptr < BaseIpcMessage > > GetMessages ( Destination destination ) {
2017-04-23 19:45:58 +00:00
return GetThreadedQueue ( destination ) - > DequeueAll ( ) ;
2017-04-16 19:02:29 +00:00
}
private :
2017-04-23 22:45:40 +00:00
IpcManager ( MultiQueueWaiter * waiter ) {
threaded_queue_for_client_ = MakeUnique < ThreadedQueue < std : : unique_ptr < BaseIpcMessage > > > ( waiter ) ;
threaded_queue_for_server_ = MakeUnique < ThreadedQueue < std : : unique_ptr < BaseIpcMessage > > > ( waiter ) ;
2017-04-16 19:02:29 +00:00
}
} ;
2017-04-16 21:49:48 +00:00
IpcManager * IpcManager : : instance_ = nullptr ;
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-04-15 05:41:35 +00:00
QueryFile * FindFile ( QueryDatabase * db , const std : : string & filename , QueryFileId * file_id ) {
2017-05-09 04:20:28 +00:00
auto it = db - > usr_to_file . find ( filename ) ;
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-09 04:20:28 +00:00
* file_id = QueryFileId ( it - > second . id ) ;
2017-04-22 07:32:29 +00:00
return & file . value ( ) ;
}
2017-04-11 05:43:01 +00:00
}
std : : cerr < < " Unable to find file " < < filename < < std : : endl ;
* file_id = QueryFileId ( - 1 ) ;
return nullptr ;
}
2017-04-15 05:41:35 +00:00
QueryFile * FindFile ( QueryDatabase * db , const std : : string & filename ) {
2017-04-13 06:01:42 +00:00
// TODO: consider calling NormalizePath here. It might add too much latency though.
2017-05-09 04:20:28 +00:00
auto it = db - > usr_to_file . find ( filename ) ;
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
std : : cerr < < " Unable to find file " < < filename < < std : : endl ;
return nullptr ;
}
2017-04-10 05:34:06 +00:00
2017-04-15 05:41:35 +00:00
optional < QueryLocation > GetDefinitionSpellingOfSymbol ( QueryDatabase * db , const QueryTypeId & id ) {
2017-04-22 07:32:29 +00:00
optional < QueryType > & type = db - > types [ id . id ] ;
if ( type )
return type - > def . definition_spelling ;
return nullopt ;
2017-04-09 02:24:32 +00:00
}
2017-04-22 07:32:29 +00:00
2017-04-15 05:41:35 +00:00
optional < QueryLocation > GetDefinitionSpellingOfSymbol ( QueryDatabase * db , const QueryFuncId & id ) {
2017-04-22 07:32:29 +00:00
optional < QueryFunc > & func = db - > funcs [ id . id ] ;
if ( func )
return func - > def . definition_spelling ;
return nullopt ;
2017-04-09 02:24:32 +00:00
}
2017-04-22 07:32:29 +00:00
2017-04-15 05:41:35 +00:00
optional < QueryLocation > GetDefinitionSpellingOfSymbol ( QueryDatabase * db , const QueryVarId & id ) {
2017-04-22 07:32:29 +00:00
optional < QueryVar > & var = db - > vars [ id . id ] ;
if ( var )
return var - > def . definition_spelling ;
return nullopt ;
2017-04-09 02:24:32 +00:00
}
2017-04-22 07:32:29 +00:00
2017-04-15 05:41:35 +00:00
optional < QueryLocation > GetDefinitionSpellingOfSymbol ( QueryDatabase * db , const SymbolIdx & symbol ) {
2017-04-09 02:24:32 +00:00
switch ( symbol . kind ) {
2017-04-22 07:32:29 +00:00
case SymbolKind : : Type : {
optional < QueryType > & type = db - > types [ symbol . idx ] ;
if ( type )
return type - > def . definition_spelling ;
break ;
}
case SymbolKind : : Func : {
optional < QueryFunc > & func = db - > funcs [ symbol . idx ] ;
if ( func )
return func - > def . definition_spelling ;
break ;
}
case SymbolKind : : Var : {
optional < QueryVar > & var = db - > vars [ symbol . idx ] ;
if ( var )
return var - > def . definition_spelling ;
break ;
}
case SymbolKind : : File :
case SymbolKind : : Invalid : {
assert ( false & & " unexpected " ) ;
break ;
}
2017-04-09 02:24:32 +00:00
}
return nullopt ;
}
2017-04-22 07:32:29 +00:00
optional < QueryLocation > GetDefinitionExtentOfSymbol ( QueryDatabase * db , const SymbolIdx & symbol ) {
switch ( symbol . kind ) {
case SymbolKind : : Type : {
optional < QueryType > & type = db - > types [ symbol . idx ] ;
if ( type )
return type - > def . definition_extent ;
break ;
}
case SymbolKind : : Func : {
optional < QueryFunc > & func = db - > funcs [ symbol . idx ] ;
if ( func )
return func - > def . definition_extent ;
break ;
}
case SymbolKind : : Var : {
optional < QueryVar > & var = db - > vars [ symbol . idx ] ;
if ( var )
return var - > def . definition_extent ;
break ;
}
case SymbolKind : : File : {
return QueryLocation ( QueryFileId ( symbol . idx ) , Range ( Position ( 1 , 1 ) , Position ( 1 , 1 ) ) ) ;
}
case SymbolKind : : Invalid : {
assert ( false & & " unexpected " ) ;
break ;
}
}
return nullopt ;
}
2017-04-13 07:47:47 +00:00
2017-04-15 05:41:35 +00:00
std : : string GetHoverForSymbol ( QueryDatabase * db , const SymbolIdx & symbol ) {
2017-04-14 05:18:02 +00:00
switch ( symbol . kind ) {
2017-04-22 07:32:29 +00:00
case SymbolKind : : Type : {
optional < QueryType > & type = db - > types [ symbol . idx ] ;
if ( type )
return type - > def . detailed_name ;
break ;
}
case SymbolKind : : Func : {
optional < QueryFunc > & func = db - > funcs [ symbol . idx ] ;
if ( func )
return func - > def . detailed_name ;
break ;
}
case SymbolKind : : Var : {
optional < QueryVar > & var = db - > vars [ symbol . idx ] ;
if ( var )
return var - > def . detailed_name ;
break ;
}
case SymbolKind : : File :
case SymbolKind : : Invalid : {
assert ( false & & " unexpected " ) ;
break ;
}
2017-04-14 05:18:02 +00:00
}
return " " ;
}
2017-04-15 05:41:35 +00:00
std : : vector < QueryLocation > ToQueryLocation ( QueryDatabase * db , const std : : vector < QueryFuncRef > & refs ) {
std : : vector < QueryLocation > locs ;
2017-04-13 07:47:47 +00:00
locs . reserve ( refs . size ( ) ) ;
for ( const QueryFuncRef & ref : refs )
locs . push_back ( ref . loc ) ;
return locs ;
}
2017-04-15 05:41:35 +00:00
std : : vector < QueryLocation > ToQueryLocation ( QueryDatabase * db , const std : : vector < QueryTypeId > & ids ) {
std : : vector < QueryLocation > locs ;
2017-04-13 07:47:47 +00:00
locs . reserve ( ids . size ( ) ) ;
for ( const QueryTypeId & id : ids ) {
2017-04-15 05:41:35 +00:00
optional < QueryLocation > loc = GetDefinitionSpellingOfSymbol ( db , id ) ;
2017-04-13 07:47:47 +00:00
if ( loc )
locs . push_back ( loc . value ( ) ) ;
}
return locs ;
}
2017-04-15 05:41:35 +00:00
std : : vector < QueryLocation > ToQueryLocation ( QueryDatabase * db , const std : : vector < QueryFuncId > & ids ) {
std : : vector < QueryLocation > locs ;
2017-04-13 07:47:47 +00:00
locs . reserve ( ids . size ( ) ) ;
for ( const QueryFuncId & id : ids ) {
2017-04-15 05:41:35 +00:00
optional < QueryLocation > loc = GetDefinitionSpellingOfSymbol ( db , id ) ;
2017-04-13 07:47:47 +00:00
if ( loc )
locs . push_back ( loc . value ( ) ) ;
}
return locs ;
}
2017-04-15 05:41:35 +00:00
std : : vector < QueryLocation > ToQueryLocation ( QueryDatabase * db , const std : : vector < QueryVarId > & ids ) {
std : : vector < QueryLocation > locs ;
2017-04-13 07:47:47 +00:00
locs . reserve ( ids . size ( ) ) ;
for ( const QueryVarId & id : ids ) {
2017-04-15 05:41:35 +00:00
optional < QueryLocation > loc = GetDefinitionSpellingOfSymbol ( db , id ) ;
2017-04-13 07:47:47 +00:00
if ( loc )
locs . push_back ( loc . value ( ) ) ;
}
return locs ;
}
2017-04-15 05:41:35 +00:00
std : : vector < QueryLocation > GetUsesOfSymbol ( QueryDatabase * db , const SymbolIdx & symbol ) {
2017-04-13 07:47:47 +00:00
switch ( symbol . kind ) {
2017-04-22 07:32:29 +00:00
case SymbolKind : : Type : {
optional < QueryType > & type = db - > types [ symbol . idx ] ;
if ( type )
return type - > uses ;
break ;
}
2017-04-13 07:47:47 +00:00
case SymbolKind : : Func : {
// TODO: the vector allocation could be avoided.
2017-04-22 07:32:29 +00:00
optional < QueryFunc > & func = db - > funcs [ symbol . idx ] ;
if ( func ) {
std : : vector < QueryLocation > result = ToQueryLocation ( db , func - > callers ) ;
AddRange ( & result , func - > declarations ) ;
if ( func - > def . definition_spelling )
result . push_back ( * func - > def . definition_spelling ) ;
return result ;
}
break ;
2017-04-13 07:47:47 +00:00
}
2017-04-22 07:32:29 +00:00
case SymbolKind : : Var : {
optional < QueryVar > & var = db - > vars [ symbol . idx ] ;
if ( var )
return var - > uses ;
2017-04-13 07:47:47 +00:00
break ;
}
2017-04-11 05:43:01 +00:00
case SymbolKind : : File :
case SymbolKind : : Invalid : {
assert ( false & & " unexpected " ) ;
break ;
}
2017-04-09 22:16:06 +00:00
}
2017-04-22 07:32:29 +00:00
return { } ;
2017-04-09 22:16:06 +00:00
}
2017-04-15 05:41:35 +00:00
std : : vector < QueryLocation > GetDeclarationsOfSymbolForGotoDefinition ( QueryDatabase * db , const SymbolIdx & symbol ) {
2017-04-11 05:43:01 +00:00
switch ( symbol . kind ) {
2017-04-11 06:02:53 +00:00
case SymbolKind : : Type : {
// Returning the definition spelling of a type is a hack (and is why the
// function has the postfix `ForGotoDefintion`, but it lets the user
// jump to the start of a type if clicking goto-definition on the same
// type from within the type definition.
2017-04-22 07:32:29 +00:00
optional < QueryType > & type = db - > types [ symbol . idx ] ;
if ( type ) {
optional < QueryLocation > declaration = type - > def . definition_spelling ;
if ( declaration )
return { * declaration } ;
}
break ;
}
case SymbolKind : : Func : {
optional < QueryFunc > & func = db - > funcs [ symbol . idx ] ;
if ( func )
return func - > declarations ;
2017-04-11 06:02:53 +00:00
break ;
}
2017-04-11 05:43:01 +00:00
case SymbolKind : : Var : {
2017-04-22 07:32:29 +00:00
optional < QueryVar > & var = db - > vars [ symbol . idx ] ;
if ( var ) {
optional < QueryLocation > declaration = var - > def . declaration ;
if ( declaration )
return { * declaration } ;
}
2017-04-11 05:43:01 +00:00
break ;
}
}
return { } ;
}
2017-04-15 05:41:35 +00:00
optional < QueryLocation > GetBaseDefinitionOrDeclarationSpelling ( QueryDatabase * db , QueryFunc & func ) {
2017-04-11 07:29:36 +00:00
if ( ! func . def . base )
return nullopt ;
2017-04-22 07:32:29 +00:00
optional < QueryFunc > & base = db - > funcs [ func . def . base - > id ] ;
if ( ! base )
return nullopt ;
auto def = base - > def . definition_spelling ;
if ( ! def & & ! base - > declarations . empty ( ) )
def = base - > declarations [ 0 ] ;
2017-04-12 06:30:31 +00:00
return def ;
2017-04-11 07:29:36 +00:00
}
2017-04-15 05:41:35 +00:00
std : : vector < QueryFuncRef > GetCallersForAllBaseFunctions ( QueryDatabase * db , QueryFunc & root ) {
2017-04-11 07:29:36 +00:00
std : : vector < QueryFuncRef > callers ;
optional < QueryFuncId > func_id = root . def . base ;
while ( func_id ) {
2017-04-22 07:32:29 +00:00
optional < QueryFunc > & func = db - > funcs [ func_id - > id ] ;
if ( ! func )
break ;
AddRange ( & callers , func - > callers ) ;
func_id = func - > def . base ;
2017-04-11 07:29:36 +00:00
}
return callers ;
}
2017-04-15 05:41:35 +00:00
std : : vector < QueryFuncRef > GetCallersForAllDerivedFunctions ( QueryDatabase * db , QueryFunc & root ) {
2017-04-11 07:29:36 +00:00
std : : vector < QueryFuncRef > callers ;
std : : queue < QueryFuncId > queue ;
PushRange ( & queue , root . derived ) ;
while ( ! queue . empty ( ) ) {
2017-04-22 07:32:29 +00:00
optional < QueryFunc > & func = db - > funcs [ queue . front ( ) . id ] ;
2017-04-11 07:29:36 +00:00
queue . pop ( ) ;
2017-04-22 07:32:29 +00:00
if ( ! func )
continue ;
2017-04-11 07:29:36 +00:00
2017-04-22 07:32:29 +00:00
PushRange ( & queue , func - > derived ) ;
AddRange ( & callers , func - > callers ) ;
2017-04-11 07:29:36 +00:00
}
return callers ;
}
2017-04-09 22:16:06 +00:00
optional < lsRange > GetLsRange ( WorkingFile * working_file , const Range & location ) {
2017-04-09 19:38:52 +00:00
if ( ! working_file ) {
return lsRange (
lsPosition ( location . start . line - 1 , location . start . column - 1 ) ,
lsPosition ( location . end . line - 1 , location . end . column - 1 ) ) ;
}
2017-04-19 07:52:48 +00:00
optional < int > start = working_file - > GetBufferLineFromIndexLine ( location . start . line ) ;
optional < int > end = working_file - > GetBufferLineFromIndexLine ( location . end . line ) ;
2017-04-16 08:09:12 +00:00
if ( ! start | | ! end )
2017-04-09 22:16:06 +00:00
return nullopt ;
2017-04-09 02:24:32 +00:00
return lsRange (
2017-04-16 08:09:12 +00:00
lsPosition ( * start - 1 , location . start . column - 1 ) ,
lsPosition ( * end - 1 , location . end . column - 1 ) ) ;
2017-04-09 19:38:52 +00:00
}
2017-04-15 05:41:35 +00:00
lsDocumentUri GetLsDocumentUri ( QueryDatabase * db , QueryFileId file_id , std : : string * path ) {
2017-04-22 07:32:29 +00:00
optional < QueryFile > & file = db - > files [ file_id . id ] ;
if ( file ) {
* path = file - > def . path ;
return lsDocumentUri : : FromPath ( * path ) ;
}
else {
* path = " " ;
return lsDocumentUri : : FromPath ( " " ) ;
}
2017-04-09 02:24:32 +00:00
}
2017-04-15 05:41:35 +00:00
lsDocumentUri GetLsDocumentUri ( QueryDatabase * db , QueryFileId file_id ) {
2017-04-22 07:32:29 +00:00
optional < QueryFile > & file = db - > files [ file_id . id ] ;
if ( file ) {
return lsDocumentUri : : FromPath ( file - > def . path ) ;
}
else {
return lsDocumentUri : : FromPath ( " " ) ;
}
2017-04-09 02:24:32 +00:00
}
2017-04-15 05:41:35 +00:00
optional < lsLocation > GetLsLocation ( QueryDatabase * db , WorkingFiles * working_files , const QueryLocation & location ) {
2017-04-09 19:38:52 +00:00
std : : string path ;
lsDocumentUri uri = GetLsDocumentUri ( db , location . path , & path ) ;
2017-04-09 22:16:06 +00:00
optional < lsRange > range = GetLsRange ( working_files - > GetFileByFilename ( path ) , location . range ) ;
if ( ! range )
return nullopt ;
return lsLocation ( uri , * range ) ;
2017-04-09 02:24:32 +00:00
}
2017-05-07 06:56:04 +00:00
NonElidedVector < lsLocation > GetLsLocations ( QueryDatabase * db , WorkingFiles * working_files , const std : : vector < QueryLocation > & locations ) {
std : : unordered_set < lsLocation > unique_locations ;
for ( const QueryLocation & query_location : locations ) {
optional < lsLocation > location = GetLsLocation ( db , working_files , query_location ) ;
if ( ! location )
continue ;
unique_locations . insert ( * location ) ;
}
NonElidedVector < lsLocation > result ;
result . reserve ( unique_locations . size ( ) ) ;
result . assign ( unique_locations . begin ( ) , unique_locations . end ( ) ) ;
return result ;
}
2017-04-09 22:16:06 +00:00
// Returns a symbol. The symbol will have *NOT* have a location assigned.
2017-04-19 06:56:37 +00:00
optional < lsSymbolInformation > GetSymbolInfo ( QueryDatabase * db , WorkingFiles * working_files , SymbolIdx symbol ) {
2017-04-22 07:32:29 +00:00
switch ( symbol . kind ) {
2017-04-09 02:48:50 +00:00
case SymbolKind : : File : {
2017-04-22 07:32:29 +00:00
optional < QueryFile > & file = db - > files [ symbol . idx ] ;
if ( ! file )
return nullopt ;
lsSymbolInformation info ;
2017-04-15 05:41:35 +00:00
info . name = file - > def . path ;
2017-04-09 02:48:50 +00:00
info . kind = lsSymbolKind : : File ;
2017-04-22 07:32:29 +00:00
return info ;
2017-04-09 02:48:50 +00:00
}
case SymbolKind : : Type : {
2017-04-22 07:32:29 +00:00
optional < QueryType > & type = db - > types [ symbol . idx ] ;
if ( ! type )
return nullopt ;
lsSymbolInformation info ;
2017-04-15 05:41:35 +00:00
info . name = type - > def . detailed_name ;
2017-04-09 02:48:50 +00:00
info . kind = lsSymbolKind : : Class ;
2017-04-22 07:32:29 +00:00
return info ;
2017-04-09 02:48:50 +00:00
}
case SymbolKind : : Func : {
2017-04-22 07:32:29 +00:00
optional < QueryFunc > & func = db - > funcs [ symbol . idx ] ;
if ( ! func )
return nullopt ;
2017-04-16 08:09:12 +00:00
2017-04-22 07:32:29 +00:00
lsSymbolInformation info ;
2017-04-15 05:41:35 +00:00
info . name = func - > def . detailed_name ;
2017-04-22 07:32:29 +00:00
info . kind = lsSymbolKind : : Function ;
2017-04-15 05:41:35 +00:00
if ( func - > def . declaring_type . has_value ( ) ) {
2017-04-22 07:32:29 +00:00
optional < QueryType > & container = db - > types [ func - > def . declaring_type - > id ] ;
if ( container ) {
info . kind = lsSymbolKind : : Method ;
info . containerName = container - > def . detailed_name ;
}
2017-04-09 02:48:50 +00:00
}
2017-04-22 07:32:29 +00:00
return info ;
2017-04-09 02:48:50 +00:00
}
case SymbolKind : : Var : {
2017-04-22 07:32:29 +00:00
optional < QueryVar > & var = db - > vars [ symbol . idx ] ;
if ( ! var )
return nullopt ;
lsSymbolInformation info ;
2017-04-15 05:41:35 +00:00
info . name + = var - > def . detailed_name ;
2017-04-09 02:48:50 +00:00
info . kind = lsSymbolKind : : Variable ;
2017-04-22 07:32:29 +00:00
return info ;
2017-04-09 02:48:50 +00:00
}
case SymbolKind : : Invalid : {
2017-04-19 06:56:37 +00:00
return nullopt ;
2017-04-09 02:48:50 +00:00
}
} ;
2017-04-22 07:32:29 +00:00
return nullopt ;
2017-04-09 02:48:50 +00:00
}
2017-04-11 07:29:36 +00:00
struct CommonCodeLensParams {
std : : vector < TCodeLens > * result ;
2017-04-15 05:41:35 +00:00
QueryDatabase * db ;
2017-04-11 07:29:36 +00:00
WorkingFiles * working_files ;
WorkingFile * working_file ;
} ;
2017-04-09 02:31:00 +00:00
void AddCodeLens (
2017-04-11 07:29:36 +00:00
CommonCodeLensParams * common ,
2017-04-15 05:41:35 +00:00
QueryLocation loc ,
const std : : vector < QueryLocation > & uses ,
2017-04-09 02:31:00 +00:00
const char * singular ,
2017-04-11 07:29:36 +00:00
const char * plural ,
2017-04-19 05:28:33 +00:00
bool exclude_loc = false ) {
2017-04-09 02:31:00 +00:00
TCodeLens code_lens ;
2017-04-11 07:29:36 +00:00
optional < lsRange > range = GetLsRange ( common - > working_file , loc . range ) ;
2017-04-09 22:16:06 +00:00
if ( ! range )
return ;
code_lens . range = * range ;
2017-04-09 02:31:00 +00:00
code_lens . command = lsCommand < lsCodeLensCommandArguments > ( ) ;
2017-05-07 06:56:04 +00:00
code_lens . command - > command = " cquery.showReferences " ;
2017-04-11 07:29:36 +00:00
code_lens . command - > arguments . uri = GetLsDocumentUri ( common - > db , loc . path ) ;
2017-04-09 02:31:00 +00:00
code_lens . command - > arguments . position = code_lens . range . start ;
// Add unique uses.
std : : unordered_set < lsLocation > unique_uses ;
2017-04-15 05:41:35 +00:00
for ( const QueryLocation & use : uses ) {
2017-04-09 02:31:00 +00:00
if ( exclude_loc & & use = = loc )
continue ;
2017-04-11 07:29:36 +00:00
optional < lsLocation > location = GetLsLocation ( common - > db , common - > working_files , use ) ;
2017-04-09 22:16:06 +00:00
if ( ! location )
continue ;
unique_uses . insert ( * location ) ;
2017-04-09 02:31:00 +00:00
}
code_lens . command - > arguments . locations . assign ( unique_uses . begin ( ) ,
unique_uses . end ( ) ) ;
// User visible label
size_t num_usages = unique_uses . size ( ) ;
code_lens . command - > title = std : : to_string ( num_usages ) + " " ;
if ( num_usages = = 1 )
code_lens . command - > title + = singular ;
else
code_lens . command - > title + = plural ;
if ( exclude_loc | | unique_uses . size ( ) > 0 )
2017-04-11 07:29:36 +00:00
common - > result - > push_back ( code_lens ) ;
2017-03-25 20:27:28 +00:00
}
2017-03-25 19:18:25 +00:00
2017-04-15 05:41:35 +00:00
lsWorkspaceEdit BuildWorkspaceEdit ( QueryDatabase * db , WorkingFiles * working_files , const std : : vector < QueryLocation > & locations , const std : : string & new_text ) {
2017-04-14 08:21:03 +00:00
std : : unordered_map < QueryFileId , lsTextDocumentEdit > path_to_edit ;
for ( auto & location : locations ) {
optional < lsLocation > ls_location = GetLsLocation ( db , working_files , location ) ;
if ( ! ls_location )
continue ;
2017-04-16 08:09:12 +00:00
2017-04-14 08:21:03 +00:00
if ( path_to_edit . find ( location . path ) = = path_to_edit . end ( ) ) {
path_to_edit [ location . path ] = lsTextDocumentEdit ( ) ;
2017-04-22 07:32:29 +00:00
optional < QueryFile > & file = db - > files [ location . path . id ] ;
if ( ! file )
continue ;
const std : : string & path = file - > def . path ;
2017-04-14 08:21:03 +00:00
path_to_edit [ location . path ] . textDocument . uri = lsDocumentUri : : FromPath ( path ) ;
2017-04-16 08:09:12 +00:00
2017-04-14 08:21:03 +00:00
WorkingFile * working_file = working_files - > GetFileByFilename ( path ) ;
if ( working_file )
path_to_edit [ location . path ] . textDocument . version = working_file - > version ;
}
lsTextEdit edit ;
edit . range = ls_location - > range ;
edit . newText = new_text ;
2017-04-21 06:56:42 +00:00
// vscode complains if we submit overlapping text edits.
auto & edits = path_to_edit [ location . path ] . edits ;
if ( std : : find ( edits . begin ( ) , edits . end ( ) , edit ) = = edits . end ( ) )
edits . push_back ( edit ) ;
2017-04-14 08:21:03 +00:00
}
lsWorkspaceEdit edit ;
for ( const auto & changes : path_to_edit )
edit . documentChanges . push_back ( changes . second ) ;
return edit ;
}
2017-04-19 07:52:48 +00:00
std : : vector < SymbolRef > FindSymbolsAtLocation ( WorkingFile * working_file , QueryFile * file , lsPosition position ) {
2017-04-15 05:14:05 +00:00
std : : vector < SymbolRef > symbols ;
symbols . reserve ( 1 ) ;
int target_line = position . line + 1 ;
int target_column = position . character + 1 ;
2017-04-19 07:52:48 +00:00
if ( working_file ) {
optional < int > index_line = working_file - > GetIndexLineFromBufferLine ( target_line ) ;
if ( index_line )
target_line = * index_line ;
}
2017-04-15 05:14:05 +00:00
for ( const SymbolRef & ref : file - > def . all_symbols ) {
if ( ref . loc . range . Contains ( target_line , target_column ) )
symbols . push_back ( ref ) ;
}
2017-04-21 00:27:21 +00:00
// Order function symbols first. This makes goto definition work better when
// used on a constructor.
std : : sort ( symbols . begin ( ) , symbols . end ( ) , [ ] ( const SymbolRef & a , const SymbolRef & b ) {
if ( a . idx . kind ! = b . idx . kind & & a . idx . kind = = SymbolKind : : Func )
return 1 ;
return 0 ;
} ) ;
2017-04-15 05:14:05 +00:00
return symbols ;
}
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-04-08 06:45:28 +00:00
struct Index_DoIndex {
2017-04-03 01:34:15 +00:00
enum class Type {
2017-04-23 21:24:06 +00:00
// ImportOnly is used internally for loading dependency caches. The main cc
// file is loaded with ImportThenParse, which will call ImportOnly on all
// of the dependencies. The main cc will then be parsed, which will include
// updates to all dependencies.
ImportThenParse ,
2017-04-21 04:06:15 +00:00
Parse ,
2017-04-21 04:50:31 +00:00
Freshen ,
2017-04-03 01:34:15 +00:00
} ;
2017-05-16 07:26:26 +00:00
Index_DoIndex ( Type type , const Project : : Entry & entry , optional < std : : string > content )
: type ( type ) , entry ( entry ) , content ( content ) { }
2017-04-21 04:09:54 +00:00
Type type ;
2017-05-07 05:36:29 +00:00
Project : : Entry entry ;
2017-05-16 07:26:26 +00:00
optional < std : : string > content ;
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 > previous ;
std : : unique_ptr < IndexFile > current ;
2017-05-16 07:26:26 +00:00
optional < std : : string > indexed_content ;
2017-04-08 06:45:28 +00:00
2017-05-16 07:26:26 +00:00
explicit Index_DoIdMap ( std : : unique_ptr < IndexFile > current ,
optional < std : : string > indexed_content )
: current ( std : : move ( current ) ) ,
indexed_content ( indexed_content ) { }
2017-04-24 01:01:51 +00:00
2017-05-12 06:08:15 +00:00
explicit Index_DoIdMap ( std : : unique_ptr < IndexFile > previous ,
2017-05-16 07:26:26 +00:00
std : : unique_ptr < IndexFile > current ,
optional < std : : string > indexed_content )
2017-04-08 06:45:28 +00:00
: previous ( std : : move ( previous ) ) ,
2017-05-16 07:26:26 +00:00
current ( std : : move ( current ) ) ,
indexed_content ( indexed_content ) { }
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 ;
std : : unique_ptr < IndexFile > current_index ;
2017-04-08 06:45:28 +00:00
std : : unique_ptr < IdMap > previous_id_map ;
std : : unique_ptr < IdMap > current_id_map ;
2017-05-16 07:26:26 +00:00
optional < std : : string > indexed_content ;
Index_OnIdMapped ( const optional < std : : string > & indexed_content )
: indexed_content ( indexed_content ) { }
2017-04-08 06:45:28 +00:00
} ;
struct Index_OnIndexed {
2017-03-25 19:18:25 +00:00
IndexUpdate update ;
2017-05-16 07:26:26 +00:00
// Map is file path to file content.
std : : unordered_map < std : : string , std : : string > indexed_content ;
explicit Index_OnIndexed ( IndexUpdate & update , const optional < std : : string > & indexed_content )
: update ( update ) {
if ( indexed_content ) {
assert ( update . files_def_update . size ( ) = = 1 ) ;
this - > indexed_content [ update . files_def_update [ 0 ] . path ] = * indexed_content ;
}
}
2017-03-25 19:18:25 +00:00
} ;
2017-04-08 20:00:08 +00:00
using Index_DoIndexQueue = ThreadedQueue < Index_DoIndex > ;
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 > ( ) ;
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-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-04-23 20:02:41 +00:00
} // namespace
2017-04-16 19:02:29 +00:00
2017-04-24 01:01:51 +00:00
bool ImportCachedIndex ( IndexerConfig * config ,
FileConsumer : : SharedState * file_consumer_shared ,
2017-04-21 04:06:15 +00:00
Index_DoIdMapQueue * queue_do_id_map ,
2017-05-16 07:26:26 +00:00
const std : : string & tu_path ,
const optional < std : : string > & indexed_content ) {
2017-04-24 01:01:51 +00:00
// TODO: only load cache if command line arguments are the same.
2017-04-03 01:34:15 +00:00
2017-04-08 06:45:28 +00:00
Timer time ;
2017-05-12 06:08:15 +00:00
std : : unique_ptr < IndexFile > cache = LoadCachedIndex ( config , tu_path ) ;
2017-04-24 01:01:51 +00:00
time . ResetAndPrint ( " Reading cached index from disk " + tu_path ) ;
2017-04-21 04:06:15 +00:00
if ( ! cache )
2017-04-24 01:01:51 +00:00
return true ;
2017-04-20 07:53:33 +00:00
2017-04-24 01:01:51 +00:00
bool needs_reparse = false ;
2017-04-11 05:26:27 +00:00
2017-04-24 01:01:51 +00:00
// Import all dependencies.
for ( auto & dependency_path : cache - > dependencies ) {
std : : cerr < < " - Got dependency " < < dependency_path < < std : : endl ;
2017-05-12 06:08:15 +00:00
std : : unique_ptr < IndexFile > cache = LoadCachedIndex ( config , dependency_path ) ;
2017-05-09 01:21:21 +00:00
if ( cache & & GetLastModificationTime ( cache - > path ) = = cache - > last_modification_time )
2017-04-24 01:01:51 +00:00
file_consumer_shared - > Mark ( cache - > path ) ;
else
needs_reparse = true ;
2017-05-09 01:21:21 +00:00
if ( cache )
2017-05-16 07:26:26 +00:00
queue_do_id_map - > Enqueue ( Index_DoIdMap ( std : : move ( cache ) , nullopt ) ) ;
2017-04-24 01:01:51 +00:00
}
// Import primary file.
if ( GetLastModificationTime ( tu_path ) = = cache - > last_modification_time )
file_consumer_shared - > Mark ( tu_path ) ;
else
needs_reparse = true ;
2017-05-16 07:26:26 +00:00
queue_do_id_map - > Enqueue ( Index_DoIdMap ( std : : move ( cache ) , indexed_content ) ) ;
2017-04-24 01:01:51 +00:00
return needs_reparse ;
2017-04-21 04:06:15 +00:00
}
2017-04-08 20:00:08 +00:00
2017-04-21 04:06:15 +00:00
void ParseFile ( IndexerConfig * config ,
FileConsumer : : SharedState * file_consumer_shared ,
Index_DoIdMapQueue * queue_do_id_map ,
2017-05-16 07:26:26 +00:00
const Project : : Entry & entry ,
const optional < std : : string > & indexed_content ) {
2017-04-21 04:06:15 +00:00
Timer time ;
2017-04-08 06:45:28 +00:00
2017-05-12 06:08:15 +00:00
std : : unique_ptr < IndexFile > cache_for_args = LoadCachedIndex ( config , entry . filename ) ;
2017-04-20 05:46:10 +00:00
2017-05-07 05:36:29 +00:00
std : : string tu_path = cache_for_args ? cache_for_args - > import_file : entry . filename ;
const std : : vector < std : : string > & tu_args = entry . args ;
2017-04-20 05:46:10 +00:00
2017-05-12 06:08:15 +00:00
std : : vector < std : : unique_ptr < IndexFile > > indexes = Parse (
2017-04-21 04:06:15 +00:00
config , file_consumer_shared ,
2017-05-16 07:38:15 +00:00
tu_path , tu_args ,
entry . filename , indexed_content ) ;
2017-04-26 01:32:45 +00:00
time . ResetAndPrint ( " Parsing/indexing " + tu_path ) ;
2017-04-21 04:06:15 +00:00
2017-05-12 06:08:15 +00:00
for ( std : : unique_ptr < IndexFile > & new_index : indexes ) {
2017-04-21 04:06:15 +00:00
std : : cerr < < " Got index for " < < new_index - > path < < std : : endl ;
// Load the cached index.
2017-05-12 06:08:15 +00:00
std : : unique_ptr < IndexFile > cached_index ;
2017-04-24 01:01:51 +00:00
if ( cache_for_args & & new_index - > path = = cache_for_args - > path )
cached_index = std : : move ( cache_for_args ) ;
2017-04-21 04:06:15 +00:00
else
2017-04-21 06:32:18 +00:00
cached_index = LoadCachedIndex ( config , new_index - > path ) ;
2017-04-24 01:01:51 +00:00
// TODO: Enable this assert when we are no longer forcibly indexing the primary file.
//assert(!cached_index || GetLastModificationTime(new_index->path) != cached_index->last_modification_time);
2017-04-21 04:06:15 +00:00
time . ResetAndPrint ( " Loading cached index " ) ;
2017-03-25 19:18:25 +00:00
2017-05-11 01:44:39 +00:00
// Publish diagnostics.
if ( ! new_index - > diagnostics . empty ( ) | | ( cached_index & & ! cached_index - > diagnostics . empty ( ) ) ) {
Out_TextDocumentPublishDiagnostics diag ;
diag . params . uri = lsDocumentUri : : FromPath ( new_index - > path ) ;
diag . params . diagnostics = new_index - > diagnostics ;
IpcManager : : instance ( ) - > SendOutMessageToClient ( IpcId : : TextDocumentPublishDiagnostics , diag ) ;
}
2017-04-24 01:01:51 +00:00
// Any any existing dependencies to |new_index| that were there before,
// because we will not reparse them if they haven't changed.
// TODO: indexer should always include dependencies. This doesn't let us remove old dependencies.
if ( cached_index ) {
for ( auto & dep : cached_index - > dependencies ) {
if ( std : : find ( new_index - > dependencies . begin ( ) , new_index - > dependencies . end ( ) , dep ) = = new_index - > dependencies . end ( ) )
new_index - > dependencies . push_back ( dep ) ;
}
}
2017-04-08 22:54:36 +00:00
2017-04-21 04:06:15 +00:00
// Cache the newly indexed file. This replaces the existing cache.
// TODO: Run this as another import pipeline stage.
WriteToCache ( config , new_index - > path , * new_index ) ;
time . ResetAndPrint ( " Cache index update to disk " ) ;
2017-04-20 07:25:38 +00:00
2017-05-16 07:26:26 +00:00
// Forward file content, but only for the primary file.
optional < std : : string > content ;
if ( new_index - > path = = entry . filename )
content = indexed_content ;
2017-04-21 04:06:15 +00:00
// Dispatch IdMap creation request, which will happen on querydb thread.
2017-05-16 07:26:26 +00:00
Index_DoIdMap response ( std : : move ( cached_index ) , std : : move ( new_index ) , content ) ;
2017-04-21 04:06:15 +00:00
queue_do_id_map - > Enqueue ( std : : move ( response ) ) ;
}
}
2017-04-24 01:01:51 +00:00
bool ResetStaleFiles ( IndexerConfig * config ,
FileConsumer : : SharedState * file_consumer_shared ,
const std : : string & tu_path ) {
Timer time ;
2017-05-12 06:08:15 +00:00
std : : unique_ptr < IndexFile > cache = LoadCachedIndex ( config , tu_path ) ;
2017-04-24 01:01:51 +00:00
time . ResetAndPrint ( " Reading cached index from disk " + tu_path ) ;
if ( ! cache ) {
std : : cerr < < " [indexer] Unable to load existing index from file when freshening (dependences will not be freshened) " < < std : : endl ;
file_consumer_shared - > Mark ( tu_path ) ;
return true ;
}
bool needs_reparse = false ;
// Check dependencies
for ( auto & dependency_path : cache - > dependencies ) {
std : : cerr < < " - Got dependency " < < dependency_path < < std : : endl ;
2017-05-12 06:08:15 +00:00
std : : unique_ptr < IndexFile > cache = LoadCachedIndex ( config , dependency_path ) ;
2017-04-24 01:01:51 +00:00
if ( GetLastModificationTime ( cache - > path ) ! = cache - > last_modification_time ) {
needs_reparse = true ;
file_consumer_shared - > Reset ( cache - > path ) ;
}
}
// Check primary file
if ( GetLastModificationTime ( tu_path ) ! = cache - > last_modification_time ) {
needs_reparse = true ;
file_consumer_shared - > Mark ( tu_path ) ;
}
return needs_reparse ;
}
2017-04-21 04:06:15 +00:00
bool IndexMain_DoIndex ( IndexerConfig * config ,
FileConsumer : : SharedState * file_consumer_shared ,
Project * project ,
Index_DoIndexQueue * queue_do_index ,
Index_DoIdMapQueue * queue_do_id_map ) {
optional < Index_DoIndex > index_request = queue_do_index - > TryDequeue ( ) ;
if ( ! index_request )
return false ;
2017-04-08 20:00:08 +00:00
2017-04-21 04:06:15 +00:00
Timer time ;
2017-04-20 08:05:19 +00:00
2017-04-21 04:06:15 +00:00
switch ( index_request - > type ) {
2017-04-23 21:24:06 +00:00
case Index_DoIndex : : Type : : ImportThenParse : {
2017-04-24 01:01:51 +00:00
// This assumes index_request->path is a cc or translation unit file (ie,
// it is in compile_commands.json).
2017-05-16 07:26:26 +00:00
bool needs_reparse = ImportCachedIndex ( config , file_consumer_shared , queue_do_id_map , index_request - > entry . filename , index_request - > content ) ;
2017-04-21 04:06:15 +00:00
// If the file has been updated, we need to reparse it.
2017-04-24 01:01:51 +00:00
if ( needs_reparse ) {
2017-04-23 21:24:06 +00:00
// Instead of parsing the file immediately, we push the request to the
2017-04-21 04:06:15 +00:00
// back of the queue so we will finish all of the Import requests
2017-04-23 21:24:06 +00:00
// before starting to run actual index jobs. This gives the user a
2017-04-21 04:06:15 +00:00
// partially-correct index potentially much sooner.
index_request - > type = Index_DoIndex : : Type : : Parse ;
queue_do_index - > Enqueue ( std : : move ( * index_request ) ) ;
}
break ;
}
2017-04-08 20:00:08 +00:00
2017-04-21 04:06:15 +00:00
case Index_DoIndex : : Type : : Parse : {
2017-04-24 01:01:51 +00:00
// index_request->path can be a cc/tu or a dependency path.
2017-05-07 05:36:29 +00:00
file_consumer_shared - > Reset ( index_request - > entry . filename ) ;
2017-05-16 07:26:26 +00:00
ParseFile ( config , file_consumer_shared , queue_do_id_map , index_request - > entry , index_request - > content ) ;
2017-04-21 04:50:31 +00:00
break ;
}
case Index_DoIndex : : Type : : Freshen : {
2017-04-24 01:01:51 +00:00
// This assumes index_request->path is a cc or translation unit file (ie,
// it is in compile_commands.json).
2017-05-07 05:36:29 +00:00
bool needs_reparse = ResetStaleFiles ( config , file_consumer_shared , index_request - > entry . filename ) ;
2017-04-24 01:01:51 +00:00
if ( needs_reparse )
2017-05-16 07:26:26 +00:00
ParseFile ( config , file_consumer_shared , queue_do_id_map , index_request - > entry , index_request - > content ) ;
2017-04-21 04:06:15 +00:00
break ;
}
2017-03-14 08:33:39 +00:00
}
2017-04-08 06:45:28 +00:00
return true ;
}
2017-04-11 05:26:27 +00:00
bool IndexMain_DoCreateIndexUpdate (
Index_OnIdMappedQueue * queue_on_id_mapped ,
Index_OnIndexedQueue * queue_on_indexed ) {
2017-04-08 20:00:08 +00:00
optional < Index_OnIdMapped > response = queue_on_id_mapped - > TryDequeue ( ) ;
if ( ! response )
2017-04-08 06:45:28 +00:00
return false ;
Timer time ;
IndexUpdate update = IndexUpdate : : CreateDelta ( response - > previous_id_map . get ( ) , response - > current_id_map . get ( ) ,
2017-04-08 20:00:08 +00:00
response - > previous_index . get ( ) , response - > current_index . get ( ) ) ;
2017-04-21 06:32:18 +00:00
time . ResetAndPrint ( " [indexer] Creating delta IndexUpdate " ) ;
2017-05-16 07:26:26 +00:00
Index_OnIndexed reply ( update , response - > indexed_content ) ;
2017-04-08 06:45:28 +00:00
queue_on_indexed - > Enqueue ( std : : move ( reply ) ) ;
2017-04-21 06:32:18 +00:00
time . ResetAndPrint ( " [indexer] Sending update to server " ) ;
2017-04-08 06:45:28 +00:00
return true ;
}
2017-04-23 22:45:40 +00:00
bool IndexMergeIndexUpdates ( Index_OnIndexedQueue * queue_on_indexed ) {
2017-04-11 05:26:27 +00:00
optional < Index_OnIndexed > root = queue_on_indexed - > TryDequeue ( ) ;
if ( ! root )
2017-04-23 22:45:40 +00:00
return false ;
2017-04-11 05:26:27 +00:00
2017-04-23 22:45:40 +00:00
bool did_merge = false ;
2017-04-11 05:26:27 +00:00
while ( true ) {
optional < Index_OnIndexed > to_join = queue_on_indexed - > TryDequeue ( ) ;
if ( ! to_join ) {
queue_on_indexed - > Enqueue ( std : : move ( * root ) ) ;
2017-04-23 22:45:40 +00:00
return did_merge ;
2017-04-11 05:26:27 +00:00
}
2017-04-23 22:45:40 +00:00
did_merge = true ;
2017-04-11 05:26:27 +00:00
Timer time ;
root - > update . Merge ( to_join - > update ) ;
2017-05-16 07:26:26 +00:00
for ( auto & & entry : to_join - > indexed_content )
root - > indexed_content . emplace ( entry ) ;
2017-04-21 06:32:18 +00:00
time . ResetAndPrint ( " [indexer] Joining two querydb updates " ) ;
2017-04-11 05:26:27 +00:00
}
}
2017-04-08 22:54:36 +00:00
void IndexMain (
2017-04-23 21:24:06 +00:00
IndexerConfig * config ,
FileConsumer : : SharedState * file_consumer_shared ,
Project * project ,
2017-04-23 22:45:40 +00:00
MultiQueueWaiter * waiter ,
2017-04-23 21:24:06 +00:00
Index_DoIndexQueue * queue_do_index ,
Index_DoIdMapQueue * queue_do_id_map ,
Index_OnIdMappedQueue * queue_on_id_mapped ,
Index_OnIndexedQueue * queue_on_indexed ) {
2017-04-08 22:54:36 +00:00
2017-04-19 00:05:14 +00:00
SetCurrentThreadName ( " indexer " ) ;
2017-04-08 06:45:28 +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-19 07:32:59 +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.
2017-04-20 05:46:10 +00:00
bool did_index = IndexMain_DoIndex ( config , file_consumer_shared , project , queue_do_index , queue_do_id_map ) ;
2017-04-19 07:32:59 +00:00
bool did_create_update = IndexMain_DoCreateIndexUpdate ( queue_on_id_mapped , queue_on_indexed ) ;
2017-04-23 22:45:40 +00:00
bool did_merge = false ;
// 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 ) ;
// We didn't do any work, so wait for a notification.
if ( ! did_index & & ! did_create_update & & ! did_merge )
waiter - > Wait ( {
queue_do_index ,
queue_on_id_mapped ,
queue_on_indexed
} ) ;
2017-04-08 06:45:28 +00:00
}
2017-03-14 08:33:39 +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-04-23 21:24:06 +00:00
IndexerConfig * config ,
QueryDatabase * db ,
2017-04-23 22:45:40 +00:00
MultiQueueWaiter * waiter ,
2017-04-23 21:24:06 +00:00
Index_DoIndexQueue * queue_do_index ,
Index_DoIdMapQueue * queue_do_id_map ,
Index_OnIdMappedQueue * queue_on_id_mapped ,
Index_OnIndexedQueue * queue_on_indexed ,
Project * project ,
FileConsumer : : SharedState * file_consumer_shared ,
WorkingFiles * working_files ,
CompletionManager * completion_manager ) {
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-04-16 21:49:48 +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-04-24 01:24:09 +00:00
std : : cerr < < " [querydb] Initialize in directory " < < project_path
2017-04-23 20:19:09 +00:00
< < " with uri " < < request - > params . rootUri - > raw_uri
< < std : : endl ;
if ( ! request - > params . initializationOptions ) {
std : : cerr < < " Initialization parameters (particularily cacheDirectory) are required " < < std : : endl ;
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-04-23 20:19:09 +00:00
// Make sure cache directory is valid.
if ( config - > cacheDirectory . empty ( ) ) {
std : : cerr < < " No cache directory " < < std : : endl ;
exit ( 1 ) ;
}
config - > cacheDirectory = NormalizePath ( config - > cacheDirectory ) ;
if ( config - > cacheDirectory [ config - > cacheDirectory . size ( ) - 1 ] ! = ' / ' )
config - > cacheDirectory + = ' / ' ;
MakeDirectoryRecursive ( config - > cacheDirectory ) ;
// Start indexer threads.
int indexer_count = std : : max < int > ( std : : thread : : hardware_concurrency ( ) , 2 ) - 1 ;
if ( config - > indexerCount > 0 )
indexer_count = config - > indexerCount ;
std : : cerr < < " [querydb] Starting " < < indexer_count < < " indexers " < < std : : endl ;
for ( int i = 0 ; i < indexer_count ; + + i ) {
new std : : thread ( [ & ] ( ) {
2017-04-23 22:45:40 +00:00
IndexMain ( config , file_consumer_shared , project , waiter , queue_do_index , 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-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-04-23 20:19:09 +00:00
std : : cerr < < " Loaded compilation entries ( " < < project - > entries . size ( ) < < " files) " < < std : : endl ;
2017-03-26 21:40:34 +00:00
2017-04-23 20:19:09 +00:00
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-04-16 23:52:42 +00:00
2017-05-16 07:26:26 +00:00
queue_do_index - > Enqueue ( Index_DoIndex ( Index_DoIndex : : Type : : ImportThenParse , entry , nullopt ) ) ;
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.
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-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-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 : {
std : : cerr < < " Freshening " < < project - > entries . size ( ) < < " files " < < std : : endl ;
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-05-16 07:26:26 +00:00
queue_do_index - > Enqueue ( Index_DoIndex ( Index_DoIndex : : Type : : Freshen , entry , nullopt ) ) ;
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-07 06:56:04 +00:00
case IpcId : : CqueryVars : {
auto msg = static_cast < Ipc_CqueryVars * > ( message . get ( ) ) ;
QueryFile * file = FindFile ( db , msg - > params . textDocument . uri . GetPath ( ) ) ;
if ( ! file ) {
std : : cerr < < " Unable to find file " < < msg - > params . textDocument . uri . GetPath ( ) < < std : : endl ;
break ;
}
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 ) ;
}
}
ipc - > SendOutMessageToClient ( IpcId : : TextDocumentReferences , response ) ;
break ;
}
case IpcId : : CqueryCallers : {
auto msg = static_cast < Ipc_CqueryCallers * > ( message . get ( ) ) ;
QueryFile * file = FindFile ( db , msg - > params . textDocument . uri . GetPath ( ) ) ;
if ( ! file ) {
std : : cerr < < " Unable to find file " < < msg - > params . textDocument . uri . GetPath ( ) < < std : : endl ;
break ;
}
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 ) ;
response . result = GetLsLocations ( db , working_files , locations ) ;
}
}
ipc - > SendOutMessageToClient ( IpcId : : TextDocumentReferences , response ) ;
break ;
}
case IpcId : : CqueryBase : {
auto msg = static_cast < Ipc_CqueryBase * > ( message . get ( ) ) ;
QueryFile * file = FindFile ( db , msg - > params . textDocument . uri . GetPath ( ) ) ;
if ( ! file ) {
std : : cerr < < " Unable to find file " < < msg - > params . textDocument . uri . GetPath ( ) < < std : : endl ;
break ;
}
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 ) ;
}
}
ipc - > SendOutMessageToClient ( IpcId : : TextDocumentReferences , response ) ;
break ;
}
case IpcId : : CqueryDerived : {
auto msg = static_cast < Ipc_CqueryDerived * > ( message . get ( ) ) ;
QueryFile * file = FindFile ( db , msg - > params . textDocument . uri . GetPath ( ) ) ;
if ( ! file ) {
std : : cerr < < " Unable to find file " < < msg - > params . textDocument . uri . GetPath ( ) < < std : : endl ;
break ;
}
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 ) ;
}
}
ipc - > SendOutMessageToClient ( IpcId : : TextDocumentReferences , response ) ;
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 ( ) ) ;
WorkingFile * working_file = working_files - > OnOpen ( msg - > params ) ;
optional < std : : string > cached_file_contents = LoadCachedFileContents ( config , msg - > params . textDocument . uri . GetPath ( ) ) ;
if ( cached_file_contents )
working_file - > SetIndexContent ( * cached_file_contents ) ;
else
working_file - > SetIndexContent ( working_file - > buffer_content ) ;
time . ResetAndPrint ( " [querydb] Loading cached index file for DidOpen " ) ;
2017-04-14 08:21:03 +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 ( ) ) ;
working_files - > OnChange ( msg - > params ) ;
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 ( ) ) ;
working_files - > OnClose ( msg - > params ) ;
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-04-23 20:19:09 +00:00
WorkingFile * working_file = working_files - > GetFileByFilename ( path ) ;
2017-05-16 07:26:26 +00:00
if ( working_file )
queue_do_index - > PriorityEnqueue ( Index_DoIndex ( Index_DoIndex : : Type : : Parse , project - > FindCompilationEntryForFile ( path ) , working_file - > buffer_content ) ) ;
2017-05-11 06:25:41 +00:00
completion_manager - > UpdateActiveSession ( 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 ;
QueryFile * file = FindFile ( db , msg - > params . textDocument . uri . GetPath ( ) , & file_id ) ;
if ( ! file ) {
std : : cerr < < " Unable to find file " < < msg - > params . textDocument . uri . GetPath ( ) < < std : : endl ;
break ;
}
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
response . Write ( std : : cerr ) ;
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 : {
auto msg = static_cast < Ipc_TextDocumentComplete * > ( message . get ( ) ) ;
lsTextDocumentPositionParams params = msg - > params ;
2017-04-15 05:14:05 +00:00
2017-04-23 20:19:09 +00:00
CompletionManager : : OnComplete callback = std : : bind ( [ ] ( BaseIpcMessage * message , const NonElidedVector < lsCompletionItem > & results ) {
auto msg = static_cast < Ipc_TextDocumentComplete * > ( message ) ;
auto ipc = IpcManager : : instance ( ) ;
2017-04-03 02:21:21 +00:00
2017-04-23 20:19:09 +00:00
Out_TextDocumentComplete response ;
response . id = msg - > id ;
response . result . isIncomplete = false ;
response . result . items = results ;
2017-04-03 02:21:21 +00:00
2017-04-23 20:19:09 +00:00
Timer timer ;
ipc - > SendOutMessageToClient ( IpcId : : TextDocumentCompletion , response ) ;
2017-05-10 04:52:15 +00:00
timer . ResetAndPrint ( " [complete] Writing completion results " ) ;
2017-04-23 20:19:09 +00:00
delete message ;
} , message . release ( ) , std : : placeholders : : _1 ) ;
completion_manager - > CodeComplete ( params , std : : move ( callback ) ) ;
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 ( ) ) ;
lsTextDocumentPositionParams params = msg - > params ;
//std::cerr << "!! SignatureHelp @ " << msg->params.position.line << ":" << msg->params.position.character << std::endl;
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 ) ;
std : : cerr < < " [completion] Changing completion position from " < < params . position . ToString ( ) < < " to " < < completion_position . ToString ( ) < < std : : endl ;
params . position = completion_position ;
}
std : : cerr < < " [completion] Returning signatures for " < < search < < std : : endl ;
if ( search . empty ( ) )
break ;
CompletionManager : : OnComplete callback = std : : bind ( [ ] ( BaseIpcMessage * message , std : : string search , int active_param , const NonElidedVector < lsCompletionItem > & results ) {
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 ( ) ) {
response . result . activeSignature = i ;
break ;
}
}
// Set signature to what we parsed from the working file.
response . result . activeParameter = active_param ;
response . Write ( std : : cerr ) ;
std : : cerr < < std : : endl ;
Timer timer ;
ipc - > SendOutMessageToClient ( IpcId : : TextDocumentSignatureHelp , response ) ;
timer . ResetAndPrint ( " [complete] Writing signature help results " ) ;
delete message ;
} , message . release ( ) , search , active_param , std : : placeholders : : _1 ) ;
completion_manager - > CodeComplete ( params , std : : move ( callback ) ) ;
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 ;
QueryFile * file = FindFile ( db , msg - > params . textDocument . uri . GetPath ( ) , & file_id ) ;
if ( ! file ) {
std : : cerr < < " Unable to find file " < < msg - > params . textDocument . uri . GetPath ( ) < < std : : endl ;
break ;
}
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-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 ;
QueryFile * file = FindFile ( db , msg - > params . textDocument . uri . GetPath ( ) , & file_id ) ;
if ( ! file ) {
std : : cerr < < " Unable to find file " < < msg - > params . textDocument . uri . GetPath ( ) < < std : : endl ;
break ;
}
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-04-23 20:19:09 +00:00
QueryFile * file = FindFile ( db , msg - > params . textDocument . uri . GetPath ( ) ) ;
if ( ! file ) {
std : : cerr < < " Unable to find file " < < msg - > params . textDocument . uri . GetPath ( ) < < std : : endl ;
break ;
2017-04-15 05:14:05 +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 ( ) ) ;
QueryFile * file = FindFile ( db , msg - > params . textDocument . uri . GetPath ( ) ) ;
if ( ! file ) {
std : : cerr < < " Unable to find file " < < msg - > params . textDocument . uri . GetPath ( ) < < std : : endl ;
break ;
}
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 ) {
std : : cerr < < " Excluding declaration in references " < < std : : endl ;
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-04-23 20:19:09 +00:00
QueryFile * file = FindFile ( db , msg - > params . textDocument . uri . GetPath ( ) ) ;
if ( ! file ) {
std : : cerr < < " Unable to find file " < < msg - > params . textDocument . uri . GetPath ( ) < < std : : endl ;
break ;
}
2017-03-25 19:18:25 +00:00
2017-04-24 01:24:09 +00:00
std : : cerr < < " [querydb] File outline size is " < < file - > def . outline . size ( ) < < std : : endl ;
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
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 ;
QueryFile * file = FindFile ( db , file_as_uri . GetPath ( ) ) ;
if ( ! file ) {
std : : cerr < < " Unable to find file " < < msg - > params . textDocument . uri . GetPath ( ) < < std : : endl ;
2017-03-29 06:33:38 +00:00
break ;
}
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 ) ;
Timer time ;
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 ;
AddCodeLens ( & common , ref . loc . OffsetStartColumn ( 0 ) , type - > uses , " ref " , " refs " ) ;
AddCodeLens ( & common , ref . loc . OffsetStartColumn ( 1 ) , ToQueryLocation ( db , type - > derived ) , " derived " , " derived " ) ;
AddCodeLens ( & common , ref . loc . OffsetStartColumn ( 2 ) , ToQueryLocation ( db , type - > instances ) , " var " , " vars " ) ;
break ;
}
case SymbolKind : : Func : {
optional < QueryFunc > & func = db - > funcs [ symbol . idx ] ;
if ( ! func )
continue ;
2017-04-11 07:29:36 +00:00
2017-04-23 20:19:09 +00:00
int 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 ( ) ) {
// set exclude_loc to true to force the code lens to show up
AddCodeLens ( & common , ref . loc . OffsetStartColumn ( offset + + ) , ToQueryLocation ( db , func - > callers ) , " call " , " calls " , true /*exclude_loc*/ ) ;
}
else {
AddCodeLens ( & common , ref . loc . OffsetStartColumn ( offset + + ) , ToQueryLocation ( db , func - > callers ) , " direct call " , " direct calls " ) ;
if ( ! base_callers . empty ( ) )
AddCodeLens ( & common , ref . loc . OffsetStartColumn ( offset + + ) , ToQueryLocation ( db , base_callers ) , " base call " , " base calls " ) ;
if ( ! derived_callers . empty ( ) )
AddCodeLens ( & common , ref . loc . OffsetStartColumn ( offset + + ) , ToQueryLocation ( db , derived_callers ) , " derived call " , " derived calls " ) ;
}
2017-04-11 07:29:36 +00:00
2017-04-23 20:19:09 +00:00
AddCodeLens ( & common , ref . loc . OffsetStartColumn ( offset + + ) , ToQueryLocation ( db , func - > derived ) , " derived " , " derived " ) ;
// "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-04-23 20:19:09 +00:00
AddCodeLens ( & common , ref . loc . OffsetStartColumn ( 0 ) , var - > uses , " ref " , " refs " , true /*exclude_loc*/ ) ;
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
time . ResetAndPrint ( " [querydb] Building code lens for " + file - > def . path ) ;
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 : {
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-04-23 20:19:09 +00:00
std : : cerr < < " [querydb] Considering " < < db - > detailed_names . size ( )
< < " candidates for query " < < msg - > params . query < < std : : endl ;
2017-03-25 19:18:25 +00:00
2017-04-23 20:19:09 +00:00
std : : string query = msg - > params . query ;
for ( int i = 0 ; i < db - > detailed_names . size ( ) ; + + i ) {
if ( response . result . size ( ) > = config - > maxWorkspaceSearchResults ) {
2017-04-24 01:24:09 +00:00
std : : cerr < < " [querydb] Query exceeded maximum number of responses ( " < < config - > maxWorkspaceSearchResults < < " ), output may not contain all results " < < std : : endl ;
2017-04-23 20:19:09 +00:00
break ;
}
2017-03-25 19:18:25 +00:00
2017-04-23 20:19:09 +00:00
if ( db - > detailed_names [ i ] . find ( query ) ! = std : : string : : npos ) {
optional < lsSymbolInformation > info = GetSymbolInfo ( db , working_files , db - > symbols [ i ] ) ;
if ( ! info )
continue ;
2017-04-07 06:38:01 +00:00
2017-04-23 20:19:09 +00:00
optional < QueryLocation > location = GetDefinitionExtentOfSymbol ( db , db - > symbols [ i ] ) ;
if ( ! location ) {
auto decls = GetDeclarationsOfSymbolForGotoDefinition ( db , db - > symbols [ i ] ) ;
if ( decls . empty ( ) )
continue ;
location = decls [ 0 ] ;
}
2017-04-19 06:56:37 +00:00
2017-04-23 20:19:09 +00:00
optional < lsLocation > ls_location = GetLsLocation ( db , working_files , * location ) ;
if ( ! ls_location )
2017-04-12 07:19:49 +00:00
continue ;
2017-04-23 20:19:09 +00:00
info - > location = * ls_location ;
response . result . push_back ( * info ) ;
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-04-24 01:24:09 +00:00
std : : cerr < < " [querydb] Found " < < response . result . size ( ) < < " results for query " < < query < < std : : endl ;
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 : {
std : : cerr < < " [querydb] Unhandled IPC message " < < IpcIdToString ( message - > method_id ) < < std : : endl ;
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-05-16 07:26:26 +00:00
Index_OnIdMapped response ( request - > indexed_content ) ;
2017-04-08 06:45:28 +00:00
Timer time ;
2017-05-16 07:26:26 +00:00
2017-04-08 06:45:28 +00:00
if ( request - > previous ) {
2017-04-08 20:00:08 +00:00
response . previous_id_map = MakeUnique < IdMap > ( db , request - > previous - > id_cache ) ;
response . previous_index = std : : move ( request - > previous ) ;
2017-04-08 06:45:28 +00:00
}
2017-04-08 20:00:08 +00:00
assert ( request - > current ) ;
response . current_id_map = MakeUnique < IdMap > ( db , request - > current - > id_cache ) ;
2017-04-21 06:32:18 +00:00
time . ResetAndPrint ( " [querydb] Create IdMap " + request - > current - > path ) ;
2017-04-08 20:00:08 +00:00
response . current_index = std : : move ( request - > current ) ;
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 ) {
// TODO: We're reading a file on querydb thread. This is slow!! If it is a
// problem in practice we need to create a file reader queue, dispatch the
// read to it, get a response, and apply the new index then.
WorkingFile * working_file = working_files - > GetFileByFilename ( updated_file . path ) ;
if ( working_file ) {
2017-05-16 07:26:26 +00:00
auto it = response - > indexed_content . find ( updated_file . path ) ;
if ( it ! = response - > indexed_content . end ( ) ) {
working_file - > SetIndexContent ( it - > second ) ;
2017-04-21 06:32:18 +00:00
time . ResetAndPrint ( " [querydb] Update WorkingFile index contents (via in-memory buffer) for " + updated_file . path ) ;
}
else {
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 ( " [querydb] Update WorkingFile index contents (via disk load) for " + updated_file . path ) ;
}
}
}
2017-03-25 19:18:25 +00:00
db - > ApplyIndexUpdate ( & response - > update ) ;
2017-04-21 06:32:18 +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-04-23 22:45:40 +00:00
void QueryDbMain ( IndexerConfig * config , MultiQueueWaiter * waiter ) {
2017-03-25 19:18:25 +00:00
// Create queues.
2017-04-23 22:45:40 +00:00
Index_DoIndexQueue queue_do_index ( waiter ) ;
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-04-18 04:06:01 +00:00
CompletionManager completion_manager ( config , & project , & working_files ) ;
2017-04-08 22:54:36 +00:00
FileConsumer : : SharedState file_consumer_shared ;
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-03-15 04:59:05 +00:00
while ( true ) {
2017-04-23 22:45:40 +00:00
bool did_work = QueryDbMainLoop ( config , & db , waiter , & queue_do_index , & queue_do_id_map , & queue_on_id_mapped , & queue_on_indexed , & project , & file_consumer_shared , & working_files , & completion_manager ) ;
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-04-21 07:46:51 +00:00
void LanguageServerStdinLoop ( IndexerConfig * 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 :
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 :
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-04-23 22:45:40 +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-04-23 22:45:40 +00:00
void LanguageServerMain ( IndexerConfig * 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 ( [ & ] ( ) {
QueryDbMain ( 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-03-25 01:28:09 +00:00
int main ( int argc , char * * argv ) {
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-04-11 05:26:27 +00:00
//std::this_thread::sleep_for(std::chrono::seconds(3));
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-04-10 05:34:06 +00:00
RunTests ( ) ;
2017-03-15 04:59:05 +00:00
return 0 ;
}
2017-04-23 19:45:58 +00:00
else if ( HasOption ( options , " --language-server " ) ) {
//std::cerr << "Running language server" << std::endl;
IndexerConfig config ;
2017-04-23 22:45:40 +00:00
LanguageServerMain ( & 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
describe those options . See | IndexerConfig | 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
}
}