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-04-11 05:43:01 +00:00
auto it = db - > usr_to_symbol . find ( filename ) ;
if ( it ! = db - > usr_to_symbol . end ( ) ) {
2017-04-22 07:32:29 +00:00
optional < QueryFile > & file = db - > files [ it - > second . idx ] ;
if ( file ) {
* file_id = QueryFileId ( it - > second . idx ) ;
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-04-09 02:24:32 +00:00
auto it = db - > usr_to_symbol . find ( filename ) ;
2017-04-22 07:32:29 +00:00
if ( it ! = db - > usr_to_symbol . end ( ) ) {
optional < QueryFile > & file = db - > files [ it - > second . idx ] ;
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-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 > ( ) ;
code_lens . command - > command = " superindex.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.
2017-04-19 07:32:59 +00:00
ImportOnly ,
2017-04-23 21:24:06 +00:00
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-04-21 04:09:54 +00:00
Index_DoIndex ( Type type , const std : : string & path , const optional < std : : vector < std : : string > > & args )
: type ( type ) , path ( path ) , args ( args ) { }
Type type ;
2017-03-25 19:18:25 +00:00
std : : string path ;
2017-04-21 04:06:15 +00:00
optional < std : : vector < std : : string > > args ;
2017-03-25 19:18:25 +00:00
} ;
2017-04-08 06:45:28 +00:00
struct Index_DoIdMap {
std : : unique_ptr < IndexedFile > previous ;
std : : unique_ptr < IndexedFile > current ;
explicit Index_DoIdMap ( std : : unique_ptr < IndexedFile > previous ,
2017-04-23 20:02:41 +00:00
std : : unique_ptr < IndexedFile > current )
2017-04-08 06:45:28 +00:00
: previous ( std : : move ( previous ) ) ,
2017-04-23 20:02:41 +00:00
current ( std : : move ( current ) ) { }
2017-04-08 06:45:28 +00:00
} ;
struct Index_OnIdMapped {
std : : unique_ptr < IndexedFile > previous_index ;
std : : unique_ptr < IndexedFile > current_index ;
std : : unique_ptr < IdMap > previous_id_map ;
std : : unique_ptr < IdMap > current_id_map ;
} ;
struct Index_OnIndexed {
2017-03-25 19:18:25 +00:00
IndexUpdate update ;
2017-04-08 06:45:28 +00:00
explicit Index_OnIndexed ( IndexUpdate & update ) : update ( update ) { }
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-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-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-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-21 04:50:31 +00:00
void DispatchDependencyImports ( Index_DoIndexQueue * queue_do_index ,
Index_DoIndex : : Type request_type ,
const std : : vector < std : : string > & dependencies ) {
// Import all dependencies.
for ( auto & dependency_path : dependencies ) {
std : : cerr < < " - Dispatching dependency import " < < dependency_path < < std : : endl ;
queue_do_index - > PriorityEnqueue ( Index_DoIndex ( request_type , dependency_path , nullopt ) ) ;
}
}
2017-04-16 19:02:29 +00:00
2017-04-21 04:06:15 +00:00
void ImportCachedIndex ( IndexerConfig * config ,
2017-04-08 22:54:36 +00:00
Index_DoIndexQueue * queue_do_index ,
2017-04-21 04:06:15 +00:00
Index_DoIdMapQueue * queue_do_id_map ,
const std : : string path ,
int64_t * last_modification_time ) {
* last_modification_time = 0 ;
2017-04-03 01:34:15 +00:00
2017-04-08 06:45:28 +00:00
Timer time ;
2017-04-21 06:32:18 +00:00
std : : unique_ptr < IndexedFile > cache = LoadCachedIndex ( config , path ) ;
2017-04-21 04:06:15 +00:00
time . ResetAndPrint ( " Reading cached index from disk " + path ) ;
if ( ! cache )
return ;
2017-04-20 07:53:33 +00:00
2017-04-21 04:50:31 +00:00
DispatchDependencyImports ( queue_do_index , Index_DoIndex : : Type : : ImportOnly , cache - > dependencies ) ;
2017-04-11 05:26:27 +00:00
2017-04-21 04:06:15 +00:00
* last_modification_time = cache - > last_modification_time ;
Index_DoIdMap response ( nullptr , std : : move ( cache ) ) ;
queue_do_id_map - > Enqueue ( std : : move ( response ) ) ;
}
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 ,
const std : : string & path ,
2017-04-21 04:50:31 +00:00
const optional < std : : vector < std : : string > > & args ,
std : : vector < std : : string > * opt_out_dependencies ) {
2017-04-21 04:06:15 +00:00
Timer time ;
2017-04-08 06:45:28 +00:00
2017-04-08 20:00:08 +00:00
// Parse request and send a response.
2017-04-21 06:32:18 +00:00
std : : unique_ptr < IndexedFile > cached_path_index = LoadCachedIndex ( config , path ) ;
2017-04-20 05:46:10 +00:00
2017-04-21 04:50:31 +00:00
if ( cached_path_index ) {
// Give the user dependencies if requested.
if ( opt_out_dependencies )
* opt_out_dependencies = cached_path_index - > dependencies ;
// Skip index if file modification time didn't change.
int64_t modification_time = GetLastModificationTime ( path ) ;
if ( modification_time = = cached_path_index - > last_modification_time ) {
time . ResetAndPrint ( " Skipping index update on " + path + " since file modification time has not changed " ) ;
return ;
}
else {
time . ResetAndPrint ( " Modification time on " + path + " has changed from " + std : : to_string ( cached_path_index - > last_modification_time ) + " to " + std : : to_string ( modification_time ) ) ;
}
2017-04-20 05:46:10 +00:00
}
2017-04-20 07:53:33 +00:00
std : : vector < std : : unique_ptr < IndexedFile > > indexes = Parse (
2017-04-21 04:06:15 +00:00
config , file_consumer_shared ,
path , cached_path_index ? cached_path_index - > import_file : path ,
args ? * args : cached_path_index ? cached_path_index - > args : kEmptyArgs ) ;
time . ResetAndPrint ( " Parsing/indexing " + path ) ;
for ( std : : unique_ptr < IndexedFile > & new_index : indexes ) {
std : : cerr < < " Got index for " < < new_index - > path < < std : : endl ;
// Load the cached index.
std : : unique_ptr < IndexedFile > cached_index ;
if ( new_index - > path = = path )
cached_index = std : : move ( cached_path_index ) ;
else
2017-04-21 06:32:18 +00:00
cached_index = LoadCachedIndex ( config , new_index - > path ) ;
2017-04-21 04:06:15 +00:00
time . ResetAndPrint ( " Loading cached index " ) ;
2017-03-25 19:18:25 +00:00
2017-04-21 04:06:15 +00:00
// Update dependencies on |new_index|, since they won't get reparsed if we
// have parsed them once before.
if ( cached_index )
AddRange ( & new_index - > dependencies , cached_index - > dependencies ) ;
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-04-21 04:06:15 +00:00
// Dispatch IdMap creation request, which will happen on querydb thread.
Index_DoIdMap response ( std : : move ( cached_index ) , std : : move ( new_index ) ) ;
queue_do_id_map - > Enqueue ( std : : move ( response ) ) ;
}
}
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 ) {
case Index_DoIndex : : Type : : ImportOnly : {
int64_t cache_modification_time ;
ImportCachedIndex ( config , queue_do_index , queue_do_id_map , index_request - > path , & cache_modification_time ) ;
break ;
}
2017-03-31 05:30:50 +00:00
2017-04-23 21:24:06 +00:00
case Index_DoIndex : : Type : : ImportThenParse : {
2017-04-21 04:06:15 +00:00
int64_t cache_modification_time ;
ImportCachedIndex ( config , queue_do_index , queue_do_id_map , index_request - > path , & cache_modification_time ) ;
// If the file has been updated, we need to reparse it.
if ( GetLastModificationTime ( index_request - > path ) > cache_modification_time ) {
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-21 04:50:31 +00:00
ParseFile ( config , file_consumer_shared , queue_do_id_map , index_request - > path , index_request - > args , nullptr ) ;
break ;
}
case Index_DoIndex : : Type : : Freshen : {
std : : vector < std : : string > dependencies ;
ParseFile ( config , file_consumer_shared , queue_do_id_map , index_request - > path , index_request - > args , & dependencies ) ;
DispatchDependencyImports ( queue_do_index , Index_DoIndex : : Type : : Freshen , dependencies ) ;
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-04-08 20:00:08 +00:00
Index_OnIndexed reply ( update ) ;
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-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 ( ) ) ;
if ( request - > params . rootUri ) {
std : : string project_path = request - > params . rootUri - > GetPath ( ) ;
std : : cerr < < " [stdin] Initialize in directory " < < project_path
< < " 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.
project - > Load ( project_path ) ;
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-04-23 21:24:06 +00:00
queue_do_index - > Enqueue ( Index_DoIndex ( Index_DoIndex : : Type : : ImportThenParse , entry . filename , entry . args ) ) ;
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 ;
response . result . capabilities . completionProvider - > triggerCharacters = { " . " , " :: " , " -> " } ;
2017-04-09 19:38:52 +00:00
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-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 ;
queue_do_index - > Enqueue ( Index_DoIndex ( Index_DoIndex : : Type : : Freshen , entry . filename , entry . args ) ) ;
} ) ;
2017-04-14 08:21:03 +00:00
break ;
}
2017-04-19 07:52:48 +00:00
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 ( ) ;
// Send an index update request.
queue_do_index - > Enqueue ( Index_DoIndex ( Index_DoIndex : : Type : : Parse , path , project - > FindArgsForFile ( path ) ) ) ;
2017-04-17 01:22:59 +00:00
2017-04-23 20:19:09 +00:00
// Copy current buffer content so it can be applied when index request is done.
WorkingFile * working_file = working_files - > GetFileByFilename ( path ) ;
if ( working_file )
working_file - > pending_new_index_content = working_file - > buffer_content ;
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 ) ;
timer . ResetAndPrint ( " Writing completion results " ) ;
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-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-23 20:19:09 +00:00
std : : cerr < < " File outline size is " < < file - > def . outline . size ( ) < < std : : endl ;
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 " ;
code_lens . command - > command = " superindex.goto " ;
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 ) {
std : : cerr < < " [querydb] - Query exceeded maximum number of responses ( " < < config - > maxWorkspaceSearchResults < < " ), output may not contain all results " < < std : : endl ;
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-23 20:19:09 +00:00
std : : cerr < < " [querydb] - Found " < < response . result . size ( ) < < " results for query " < < query < < std : : endl ;
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
Index_OnIdMapped response ;
2017-04-08 06:45:28 +00:00
Timer time ;
2017-04-21 06:32:18 +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 ) {
if ( working_file - > pending_new_index_content ) {
working_file - > SetIndexContent ( * working_file - > pending_new_index_content ) ;
working_file - > pending_new_index_content = nullopt ;
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-16 23:52:42 +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-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-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 :
case IpcId : : CqueryFreshenIndex : {
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 ) {
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
}
}