mirror of
				https://github.com/MaskRay/ccls.git
				synced 2025-10-31 12:42:34 +00:00 
			
		
		
		
	Implement textDocument/references
This commit is contained in:
		
							parent
							
								
									88e0d224e3
								
							
						
					
					
						commit
						e9f0c57708
					
				| @ -8,7 +8,7 @@ struct ScopedLock { | ||||
| }; | ||||
| 
 | ||||
| // Points to a generic block of memory. Note that |data| is relocatable, ie,
 | ||||
| // multiple Buffer instantations may point to the same underlying block of
 | ||||
| // multiple Buffer instantiations may point to the same underlying block of
 | ||||
| // memory but the data pointer has different values.
 | ||||
| struct Buffer { | ||||
|   // Create a new buffer of the given capacity using process-local memory.
 | ||||
|  | ||||
| @ -55,6 +55,43 @@ QueryableVarDef* GetQueryable(QueryableDatabase* db, const QueryVarId& id) { | ||||
|   return &db->vars[id.id]; | ||||
| } | ||||
| 
 | ||||
| #if false | ||||
| optional<QueryableLocation> GetDeclarationOfSymbol(QueryableDatabase* db, const SymbolIdx& symbol) { | ||||
|   switch (symbol.kind) { | ||||
|     case SymbolKind::Type: | ||||
|       return db->types[symbol.idx].def.definition_spelling; | ||||
|     case SymbolKind::Func: | ||||
|       return db->funcs[symbol.idx].; | ||||
|     case SymbolKind::Var: | ||||
|       return db->vars[symbol.idx].uses; | ||||
|     case SymbolKind::File: | ||||
|     case SymbolKind::Invalid: { | ||||
|       assert(false && "unexpected"); | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|   return {}; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| std::vector<QueryableLocation> GetUsesOfSymbol(QueryableDatabase* db, const SymbolIdx& symbol) { | ||||
|   switch (symbol.kind) { | ||||
|     case SymbolKind::Type: | ||||
|       return db->types[symbol.idx].uses; | ||||
|     case SymbolKind::Func: | ||||
|       return db->funcs[symbol.idx].uses; | ||||
|     case SymbolKind::Var: | ||||
|       return db->vars[symbol.idx].uses; | ||||
|     case SymbolKind::File: | ||||
|     case SymbolKind::Invalid: { | ||||
|       assert(false && "unexpected"); | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|   return {}; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| optional<QueryableLocation> GetDefinitionSpellingOfSymbol(QueryableDatabase* db, const QueryTypeId& id) { | ||||
|   return GetQueryable(db, id)->def.definition_spelling; | ||||
| } | ||||
| @ -380,6 +417,7 @@ std::unique_ptr<IpcMessageQueue> BuildIpcMessageQueue(const std::string& name, s | ||||
|   RegisterId<Ipc_TextDocumentDidSave>(ipc.get()); | ||||
|   RegisterId<Ipc_TextDocumentComplete>(ipc.get()); | ||||
|   RegisterId<Ipc_TextDocumentDefinition>(ipc.get()); | ||||
|   RegisterId<Ipc_TextDocumentReferences>(ipc.get()); | ||||
|   RegisterId<Ipc_TextDocumentDocumentSymbol>(ipc.get()); | ||||
|   RegisterId<Ipc_TextDocumentCodeLens>(ipc.get()); | ||||
|   RegisterId<Ipc_CodeLensResolve>(ipc.get()); | ||||
| @ -401,6 +439,7 @@ void RegisterMessageTypes() { | ||||
|   MessageRegistry::instance()->Register<Ipc_TextDocumentDidSave>(); | ||||
|   MessageRegistry::instance()->Register<Ipc_TextDocumentComplete>(); | ||||
|   MessageRegistry::instance()->Register<Ipc_TextDocumentDefinition>(); | ||||
|   MessageRegistry::instance()->Register<Ipc_TextDocumentReferences>(); | ||||
|   MessageRegistry::instance()->Register<Ipc_TextDocumentDocumentSymbol>(); | ||||
|   MessageRegistry::instance()->Register<Ipc_TextDocumentCodeLens>(); | ||||
|   MessageRegistry::instance()->Register<Ipc_CodeLensResolve>(); | ||||
| @ -669,6 +708,39 @@ void QueryDbMainLoop( | ||||
|       Out_TextDocumentDefinition response; | ||||
|       response.id = msg->id; | ||||
| 
 | ||||
|       int target_line = msg->params.position.line + 1; | ||||
|       int target_column = msg->params.position.character + 1; | ||||
| 
 | ||||
|       for (const SymbolRef& ref : file->def.all_symbols) { | ||||
|         if (ref.loc.range.start.line >= target_line && ref.loc.range.end.line <= target_line && | ||||
|             ref.loc.range.start.column <= target_column && ref.loc.range.end.column >= target_column) { | ||||
|           // Found symbol. Return definition.
 | ||||
|           optional<QueryableLocation> location = GetDefinitionSpellingOfSymbol(db, ref.idx); | ||||
|           if (location) { | ||||
|             optional<lsLocation> ls_location = GetLsLocation(db, working_files, location.value()); | ||||
|             if (ls_location) | ||||
|               response.result.push_back(*ls_location); | ||||
|           } | ||||
|           break; | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       SendOutMessageToClient(language_client, response); | ||||
|       break; | ||||
|     } | ||||
| 
 | ||||
|     case IpcId::TextDocumentReferences: { | ||||
|       auto msg = static_cast<Ipc_TextDocumentReferences*>(message.get()); | ||||
| 
 | ||||
|       QueryableFile* file = FindFile(db, msg->params.textDocument.uri.GetPath()); | ||||
|       if (!file) { | ||||
|         std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl; | ||||
|         break; | ||||
|       } | ||||
| 
 | ||||
|       Out_TextDocumentReferences response; | ||||
|       response.id = msg->id; | ||||
| 
 | ||||
|       // TODO: Edge cases (whitespace, etc) will work a lot better
 | ||||
|       // if we store range information instead of hacking it.
 | ||||
|       int target_line = msg->params.position.line + 1; | ||||
| @ -676,10 +748,22 @@ void QueryDbMainLoop( | ||||
| 
 | ||||
|       for (const SymbolRef& ref : file->def.all_symbols) { | ||||
|         if (ref.loc.range.start.line >= target_line && ref.loc.range.end.line <= target_line && | ||||
|           ref.loc.range.start.column <= target_column && ref.loc.range.end.column >= target_column) { | ||||
|           optional<QueryableLocation> location = GetDefinitionSpellingOfSymbol(db, ref.idx); | ||||
|           if (location) { | ||||
|             optional<lsLocation> ls_location = GetLsLocation(db, working_files, location.value()); | ||||
|             ref.loc.range.start.column <= target_column && ref.loc.range.end.column >= target_column) { | ||||
| 
 | ||||
|           optional<QueryableLocation> excluded_declaration; | ||||
|           if (!msg->params.context.includeDeclaration) { | ||||
|             std::cerr << "Excluding declaration in references" << std::endl; | ||||
|             excluded_declaration = GetDefinitionSpellingOfSymbol(db, ref.idx); | ||||
|           } | ||||
| 
 | ||||
|           // Found symbol. Return references.
 | ||||
|           std::vector<QueryableLocation> uses = GetUsesOfSymbol(db, ref.idx); | ||||
|           response.result.reserve(uses.size()); | ||||
|           for (const QueryableLocation& 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); | ||||
|           } | ||||
| @ -946,9 +1030,8 @@ void LanguageServerStdinLoop(IpcMessageQueue* ipc) { | ||||
|       response.result.capabilities.codeLensProvider->resolveProvider = false; | ||||
| 
 | ||||
|       response.result.capabilities.definitionProvider = true; | ||||
| 
 | ||||
|       response.result.capabilities.referencesProvider = true; | ||||
|       response.result.capabilities.documentSymbolProvider = true; | ||||
| 
 | ||||
|       response.result.capabilities.workspaceSymbolProvider = true; | ||||
| 
 | ||||
|       //response.Write(std::cerr);
 | ||||
| @ -972,6 +1055,7 @@ void LanguageServerStdinLoop(IpcMessageQueue* ipc) { | ||||
|     case IpcId::TextDocumentDidSave: | ||||
|     case IpcId::TextDocumentCompletion: | ||||
|     case IpcId::TextDocumentDefinition: | ||||
|     case IpcId::TextDocumentReferences: | ||||
|     case IpcId::TextDocumentDocumentSymbol: | ||||
|     case IpcId::TextDocumentCodeLens: | ||||
|     case IpcId::WorkspaceSymbol: { | ||||
| @ -1080,7 +1164,7 @@ int main(int argc, char** argv) { | ||||
|     if (context.shouldExit()) | ||||
|       return res; | ||||
| 
 | ||||
|     //RunTests();
 | ||||
|     RunTests(); | ||||
|     return 0; | ||||
|   } | ||||
|   else if (options.find("--help") != options.end()) { | ||||
|  | ||||
| @ -22,6 +22,8 @@ const char* IpcIdToString(IpcId id) { | ||||
|     return "textDocument/completion"; | ||||
|   case IpcId::TextDocumentDefinition: | ||||
|     return "textDocument/definition"; | ||||
|   case IpcId::TextDocumentReferences: | ||||
|     return "textDocument/references"; | ||||
|   case IpcId::TextDocumentDocumentSymbol: | ||||
|     return "textDocument/documentSymbol"; | ||||
|   case IpcId::TextDocumentCodeLens: | ||||
|  | ||||
| @ -16,6 +16,7 @@ enum class IpcId : int { | ||||
|   TextDocumentDidSave, | ||||
|   TextDocumentCompletion, | ||||
|   TextDocumentDefinition, | ||||
|   TextDocumentReferences, | ||||
|   TextDocumentDocumentSymbol, | ||||
|   TextDocumentCodeLens, | ||||
|   CodeLensResolve, | ||||
|  | ||||
| @ -1131,6 +1131,32 @@ struct Out_TextDocumentDefinition : public lsOutMessage<Out_TextDocumentDefiniti | ||||
| }; | ||||
| MAKE_REFLECT_STRUCT(Out_TextDocumentDefinition, jsonrpc, id, result); | ||||
| 
 | ||||
| // References
 | ||||
| struct Ipc_TextDocumentReferences : public IpcMessage<Ipc_TextDocumentReferences> { | ||||
|   struct lsReferenceContext { | ||||
|     // Include the declaration of the current symbol.
 | ||||
|     bool includeDeclaration; | ||||
|   }; | ||||
|   struct lsReferenceParams : public lsTextDocumentPositionParams { | ||||
|     lsTextDocumentIdentifier textDocument; | ||||
|     lsPosition position; | ||||
|     lsReferenceContext context; | ||||
|   }; | ||||
| 
 | ||||
|   const static IpcId kIpcId = IpcId::TextDocumentReferences; | ||||
| 
 | ||||
|   lsRequestId id; | ||||
|   lsReferenceParams params; | ||||
| }; | ||||
| MAKE_REFLECT_STRUCT(Ipc_TextDocumentReferences::lsReferenceContext, includeDeclaration); | ||||
| MAKE_REFLECT_STRUCT(Ipc_TextDocumentReferences::lsReferenceParams, textDocument, position, context); | ||||
| MAKE_REFLECT_STRUCT(Ipc_TextDocumentReferences, id, params); | ||||
| struct Out_TextDocumentReferences : public lsOutMessage<Out_TextDocumentReferences> { | ||||
|   lsRequestId id; | ||||
|   NonElidedVector<lsLocation> result; | ||||
| }; | ||||
| MAKE_REFLECT_STRUCT(Out_TextDocumentReferences, jsonrpc, id, result); | ||||
| 
 | ||||
| // List symbols in a document.
 | ||||
| struct lsDocumentSymbolParams { | ||||
|   lsTextDocumentIdentifier textDocument; | ||||
|  | ||||
| @ -119,7 +119,7 @@ void RunTests() { | ||||
|     //if (path != "tests/templates/namespace_template_class_template_func_usage_folded_into_one.cc") continue;
 | ||||
|     //if (path != "tests/multi_file/header.h") continue;
 | ||||
|     //if (path != "tests/multi_file/impl.cc") continue;
 | ||||
|     //if (path != "tests/usage/func_called_from_macro_argument.cc") continue;
 | ||||
|     if (path != "tests/usage/func_called_from_template.cc") continue; | ||||
|     //if (path != "tests/templates/implicit_variable_instantiation.cc") continue;
 | ||||
|     //if (path != "tests/_empty_test.cc") continue;
 | ||||
| 
 | ||||
| @ -141,7 +141,7 @@ void RunTests() { | ||||
|         "-IC:/Users/jacob/Desktop/superindex/indexer/third_party/doctest/", | ||||
|         "-IC:/Users/jacob/Desktop/superindex/indexer/third_party/rapidjson/include", | ||||
|         "-IC:/Users/jacob/Desktop/superindex/indexer/src" | ||||
|       }, false /*dump_ast*/); | ||||
|       }, true /*dump_ast*/); | ||||
| 
 | ||||
|     for (auto& entry : all_expected_output) { | ||||
|       const std::string& expected_path = entry.first; | ||||
|  | ||||
| @ -7,7 +7,6 @@ void caller() { | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
| // TODO FIXME: called() has no callers.
 | ||||
| OUTPUT: | ||||
| { | ||||
|   "funcs": [{ | ||||
|  | ||||
							
								
								
									
										46
									
								
								tests/usage/func_called_from_template.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								tests/usage/func_called_from_template.cc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | ||||
| void called(); | ||||
| 
 | ||||
| template <typename T> | ||||
| void caller() { | ||||
|   called(); | ||||
| } | ||||
| 
 | ||||
| void foo() { | ||||
|   caller<int>(); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
| // NOTE: without caller<int>() instantation caller() is never visited so
 | ||||
| // called() is never referenced.
 | ||||
| OUTPUT: | ||||
| { | ||||
|   "funcs": [{ | ||||
|       "id": 0, | ||||
|       "usr": "c:@F@called#", | ||||
|       "short_name": "called", | ||||
|       "qualified_name": "called", | ||||
|       "declarations": ["1:6-1:12"], | ||||
|       "callers": ["1@5:3-5:9"], | ||||
|       "uses": ["1:6-1:12", "5:3-5:9"] | ||||
|     }, { | ||||
|       "id": 1, | ||||
|       "usr": "c:@FT@>1#Tcaller#v#", | ||||
|       "short_name": "caller", | ||||
|       "qualified_name": "caller", | ||||
|       "definition_spelling": "4:6-4:12", | ||||
|       "definition_extent": "4:1-6:2", | ||||
|       "callers": ["2@9:3-9:9"], | ||||
|       "callees": ["0@5:3-5:9"], | ||||
|       "uses": ["4:6-4:12", "9:3-9:9"] | ||||
|     }, { | ||||
|       "id": 2, | ||||
|       "usr": "c:@F@foo#", | ||||
|       "short_name": "foo", | ||||
|       "qualified_name": "foo", | ||||
|       "definition_spelling": "8:6-8:9", | ||||
|       "definition_extent": "8:1-10:2", | ||||
|       "callees": ["1@9:3-9:9"], | ||||
|       "uses": ["8:6-8:9"] | ||||
|     }] | ||||
| } | ||||
| */ | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user