mirror of
				https://github.com/MaskRay/ccls.git
				synced 2025-11-04 06:15:20 +00:00 
			
		
		
		
	Fix some threading bugs with code completion
This commit is contained in:
		
							parent
							
								
									4a4315d7a1
								
							
						
					
					
						commit
						1083a10a66
					
				@ -9,28 +9,37 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace {
 | 
					namespace {
 | 
				
			||||||
unsigned Flags() {
 | 
					unsigned Flags() {
 | 
				
			||||||
  /*
 | 
					 | 
				
			||||||
  return
 | 
					 | 
				
			||||||
    //CXTranslationUnit_Incomplete |
 | 
					 | 
				
			||||||
    CXTranslationUnit_PrecompiledPreamble |
 | 
					 | 
				
			||||||
    CXTranslationUnit_CacheCompletionResults |
 | 
					 | 
				
			||||||
    //CXTranslationUnit_ForSerialization |
 | 
					 | 
				
			||||||
    CXTranslationUnit_IncludeBriefCommentsInCodeCompletion |
 | 
					 | 
				
			||||||
    //CXTranslationUnit_CreatePreambleOnFirstParse |
 | 
					 | 
				
			||||||
    CXTranslationUnit_KeepGoing;
 | 
					 | 
				
			||||||
    */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return
 | 
					  return
 | 
				
			||||||
 | 
					    CXTranslationUnit_Incomplete |
 | 
				
			||||||
 | 
					    CXTranslationUnit_KeepGoing |
 | 
				
			||||||
    CXTranslationUnit_CacheCompletionResults |
 | 
					    CXTranslationUnit_CacheCompletionResults |
 | 
				
			||||||
    CXTranslationUnit_PrecompiledPreamble |
 | 
					    CXTranslationUnit_PrecompiledPreamble |
 | 
				
			||||||
    CXTranslationUnit_IncludeBriefCommentsInCodeCompletion |
 | 
					    CXTranslationUnit_IncludeBriefCommentsInCodeCompletion
 | 
				
			||||||
#if !defined(_WIN32)
 | 
					#if !defined(_WIN32)
 | 
				
			||||||
    // For whatever reason, CreatePreambleOnFirstParse causes clang to become
 | 
					    // For whatever reason, CreatePreambleOnFirstParse causes clang to become
 | 
				
			||||||
    // very crashy on windows.
 | 
					    // very crashy on windows.
 | 
				
			||||||
    // TODO: do more investigation, submit fixes to clang.
 | 
					    // TODO: do more investigation, submit fixes to clang.
 | 
				
			||||||
    CXTranslationUnit_CreatePreambleOnFirstParse |
 | 
					    | CXTranslationUnit_CreatePreambleOnFirstParse
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
    CXTranslationUnit_DetailedPreprocessingRecord;
 | 
					    ;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int GetCompletionPriority(const CXCompletionString& str, CXCursorKind result_kind, const std::string& label) {
 | 
				
			||||||
 | 
					  int priority = clang_getCompletionPriority(str);
 | 
				
			||||||
 | 
					  if (result_kind == CXCursor_Destructor) {
 | 
				
			||||||
 | 
					    priority *= 100;
 | 
				
			||||||
 | 
					    //std::cerr << "Bumping[destructor] " << ls_completion_item.label << std::endl;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (result_kind == CXCursor_ConversionFunction ||
 | 
				
			||||||
 | 
					    (result_kind  == CXCursor_CXXMethod && StartsWith(label, "operator"))) {
 | 
				
			||||||
 | 
					    //std::cerr << "Bumping[conversion] " << ls_completion_item.label << std::endl;
 | 
				
			||||||
 | 
					    priority *= 100;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (clang_getCompletionAvailability(str) != CXAvailability_Available) {
 | 
				
			||||||
 | 
					    //std::cerr << "Bumping[notavailable] " << ls_completion_item.label << std::endl;
 | 
				
			||||||
 | 
					    priority *= 100;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return priority;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
lsCompletionItemKind GetCompletionKind(CXCursorKind cursor_kind) {
 | 
					lsCompletionItemKind GetCompletionKind(CXCursorKind cursor_kind) {
 | 
				
			||||||
@ -96,7 +105,7 @@ lsCompletionItemKind GetCompletionKind(CXCursorKind cursor_kind) {
 | 
				
			|||||||
    return lsCompletionItemKind::Text;
 | 
					    return lsCompletionItemKind::Text;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  default:
 | 
					  default:
 | 
				
			||||||
    std::cerr << "Unhandled completion kind " << cursor_kind << std::endl;
 | 
					    std::cerr << "[complete] Unhandled completion kind " << cursor_kind << std::endl;
 | 
				
			||||||
    return lsCompletionItemKind::Text;
 | 
					    return lsCompletionItemKind::Text;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -201,29 +210,29 @@ std::string BuildDetailString(CXCompletionString completion_string) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void CompletionMain(CompletionManager* completion_manager) {
 | 
					void CompletionMain(CompletionManager* completion_manager) {
 | 
				
			||||||
  while (true) {
 | 
					  while (true) {
 | 
				
			||||||
 | 
					    // Fetching the completion request blocks until we have a request.
 | 
				
			||||||
    std::unique_ptr<CompletionManager::CompletionRequest> request = completion_manager->completion_request.Take();
 | 
					    std::unique_ptr<CompletionManager::CompletionRequest> request = completion_manager->completion_request.Take();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    NonElidedVector<lsCompletionItem> ls_result;
 | 
					    NonElidedVector<lsCompletionItem> ls_result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    CompletionSession* session = completion_manager->GetOrOpenSession(request->location.textDocument.uri.GetPath());
 | 
					    CompletionSession* session = completion_manager->GetOrOpenSession(request->location.textDocument.uri.GetPath());
 | 
				
			||||||
 | 
					    std::lock_guard<std::mutex> lock(session->usage_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    session->EnsureCompletionState();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    unsigned line = request->location.position.line + 1;
 | 
					    unsigned line = request->location.position.line + 1;
 | 
				
			||||||
    unsigned column = request->location.position.character + 1;
 | 
					    unsigned column = request->location.position.character + 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::cerr << std::endl;
 | 
					    std::cerr << std::endl;
 | 
				
			||||||
    std::cerr << "Completing at " << line << ":" << column << std::endl;
 | 
					    std::cerr << "[complete] Completing at " << line << ":" << column << std::endl;
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // TODO/FIXME
 | 
					 | 
				
			||||||
    // TODO/FIXME
 | 
					 | 
				
			||||||
    // TODO/FIXME NOT THREAD SAFE
 | 
					 | 
				
			||||||
    // TODO/FIXME
 | 
					 | 
				
			||||||
    // TODO/FIXME
 | 
					 | 
				
			||||||
    std::vector<CXUnsavedFile> unsaved = completion_manager->working_files->AsUnsavedFiles();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Timer timer;
 | 
					    Timer timer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::vector<CXUnsavedFile> unsaved = completion_manager->working_files->AsUnsavedFiles();
 | 
				
			||||||
 | 
					    timer.ResetAndPrint("[complete] Fetching unsaved files");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    timer.Reset();
 | 
					    timer.Reset();
 | 
				
			||||||
    CXCodeCompleteResults* cx_results = clang_codeCompleteAt(
 | 
					    CXCodeCompleteResults* cx_results = clang_codeCompleteAt(
 | 
				
			||||||
@ -232,14 +241,13 @@ void CompletionMain(CompletionManager* completion_manager) {
 | 
				
			|||||||
      unsaved.data(), unsaved.size(),
 | 
					      unsaved.data(), unsaved.size(),
 | 
				
			||||||
      CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeBriefComments);
 | 
					      CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeBriefComments);
 | 
				
			||||||
    if (!cx_results) {
 | 
					    if (!cx_results) {
 | 
				
			||||||
      std::cerr << "Code completion failed" << std::endl;
 | 
					      std::cerr << "[complete] Code completion failed" << std::endl;
 | 
				
			||||||
      request->on_complete(ls_result);
 | 
					      request->on_complete(ls_result);
 | 
				
			||||||
      continue;
 | 
					      continue;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    timer.ResetAndPrint("clangCodeCompleteAt");
 | 
					 | 
				
			||||||
    std::cerr << "Got " << cx_results->NumResults << " results" << std::endl;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // TODO: for comments we could hack the unsaved buffer and transform // into ///
 | 
					    timer.ResetAndPrint("[complete] clangCodeCompleteAt");
 | 
				
			||||||
 | 
					    std::cerr << "[complete] Got " << cx_results->NumResults << " results" << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ls_result.reserve(cx_results->NumResults);
 | 
					    ls_result.reserve(cx_results->NumResults);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -247,130 +255,58 @@ void CompletionMain(CompletionManager* completion_manager) {
 | 
				
			|||||||
    for (unsigned i = 0; i < cx_results->NumResults; ++i) {
 | 
					    for (unsigned i = 0; i < cx_results->NumResults; ++i) {
 | 
				
			||||||
      CXCompletionResult& result = cx_results->Results[i];
 | 
					      CXCompletionResult& result = cx_results->Results[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      //unsigned int is_incomplete;
 | 
					 | 
				
			||||||
      //CXCursorKind kind = clang_codeCompleteGetContainerKind(cx_results, &is_incomplete);
 | 
					 | 
				
			||||||
      //std::cerr << "clang_codeCompleteGetContainerKind kind=" << kind << " is_incomplete=" << is_incomplete << std::endl;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // CXCursor_InvalidCode fo
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      //clang_codeCompleteGetContexts
 | 
					 | 
				
			||||||
      //CXCompletionContext
 | 
					 | 
				
			||||||
      // clang_codeCompleteGetContexts
 | 
					 | 
				
			||||||
      //
 | 
					 | 
				
			||||||
      //CXCursorKind kind;
 | 
					 | 
				
			||||||
      //CXString str = clang_getCompletionParent(result.CompletionString, &kind);
 | 
					 | 
				
			||||||
      //std::cerr << "clang_getCompletionParent kind=" << kind << ", str=" << clang::ToString(str) << std::endl;
 | 
					 | 
				
			||||||
      // if global don't append now, append to end later
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // also clang_codeCompleteGetContainerKind
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // TODO: fill in more data
 | 
					      // TODO: fill in more data
 | 
				
			||||||
      lsCompletionItem ls_completion_item;
 | 
					      lsCompletionItem ls_completion_item;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // kind/label/detail/docs
 | 
					      // kind/label/detail/docs/sortText
 | 
				
			||||||
      ls_completion_item.kind = GetCompletionKind(result.CursorKind);
 | 
					      ls_completion_item.kind = GetCompletionKind(result.CursorKind);
 | 
				
			||||||
      ls_completion_item.label = BuildLabelString(result.CompletionString);
 | 
					      ls_completion_item.label = BuildLabelString(result.CompletionString);
 | 
				
			||||||
      ls_completion_item.detail = BuildDetailString(result.CompletionString);
 | 
					      ls_completion_item.detail = BuildDetailString(result.CompletionString);
 | 
				
			||||||
      ls_completion_item.documentation = clang::ToString(clang_getCompletionBriefComment(result.CompletionString));
 | 
					      ls_completion_item.documentation = clang::ToString(clang_getCompletionBriefComment(result.CompletionString));
 | 
				
			||||||
 | 
					      ls_completion_item.sortText = uint64_t(GetCompletionPriority(result.CompletionString, result.CursorKind, ls_completion_item.label));
 | 
				
			||||||
      // Priority
 | 
					 | 
				
			||||||
      int priority = clang_getCompletionPriority(result.CompletionString);
 | 
					 | 
				
			||||||
      if (result.CursorKind == CXCursor_Destructor) {
 | 
					 | 
				
			||||||
        priority *= 100;
 | 
					 | 
				
			||||||
        //std::cerr << "Bumping[destructor] " << ls_completion_item.label << std::endl;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      if (result.CursorKind == CXCursor_ConversionFunction ||
 | 
					 | 
				
			||||||
        (result.CursorKind == CXCursor_CXXMethod && StartsWith(ls_completion_item.label, "operator"))) {
 | 
					 | 
				
			||||||
        //std::cerr << "Bumping[conversion] " << ls_completion_item.label << std::endl;
 | 
					 | 
				
			||||||
        priority *= 100;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      if (clang_getCompletionAvailability(result.CompletionString) != CXAvailability_Available) {
 | 
					 | 
				
			||||||
        //std::cerr << "Bumping[notavailable] " << ls_completion_item.label << std::endl;
 | 
					 | 
				
			||||||
        priority *= 100;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      //std::cerr << "Adding kind=" << result.CursorKind << ", priority=" << ls_completion_item.priority_ << ", label=" << ls_completion_item.label << std::endl;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // TODO: we can probably remove priority_ and our sort.
 | 
					 | 
				
			||||||
      ls_completion_item.sortText = uint64_t(priority);// std::to_string(ls_completion_item.priority_);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      ls_result.push_back(ls_completion_item);
 | 
					      ls_result.push_back(ls_completion_item);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    timer.ResetAndPrint("Building " + std::to_string(ls_result.size()) + " completion results");
 | 
					    timer.ResetAndPrint("[complete] Building " + std::to_string(ls_result.size()) + " completion results");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    clang_disposeCodeCompleteResults(cx_results);
 | 
					    clang_disposeCodeCompleteResults(cx_results);
 | 
				
			||||||
    timer.ResetAndPrint("clang_disposeCodeCompleteResults ");
 | 
					    timer.ResetAndPrint("[complete] clang_disposeCodeCompleteResults ");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    request->on_complete(ls_result);
 | 
					    request->on_complete(ls_result);
 | 
				
			||||||
    continue;
 | 
					    continue;
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // we should probably main two translation units, one for
 | 
					 | 
				
			||||||
    // serving current requests, and one that is reparsing (follow qtcreator)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // todo: we need to run code completion on a separate thread from querydb
 | 
					 | 
				
			||||||
    // so thread layout looks like:
 | 
					 | 
				
			||||||
    //    - stdin                   # Reads data from stdin
 | 
					 | 
				
			||||||
    //    - stdout                  # Pushes data to stdout
 | 
					 | 
				
			||||||
    //    - querydb                 # Resolves index database queries.
 | 
					 | 
				
			||||||
    //    - complete_responder      # Handles low-latency code complete requests.
 | 
					 | 
				
			||||||
    //    - complete_parser         # Parses most recent document for future code complete requests.
 | 
					 | 
				
			||||||
    //    - indexer (many)          # Runs index jobs (for querydb updates)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // use clang_codeCompleteAt
 | 
					 | 
				
			||||||
    //CXUnsavedFile
 | 
					 | 
				
			||||||
    // we need to setup CXUnsavedFile
 | 
					 | 
				
			||||||
    // The key to doing that is via
 | 
					 | 
				
			||||||
    //  - textDocument/didOpen
 | 
					 | 
				
			||||||
    //  - textDocument/didChange
 | 
					 | 
				
			||||||
    //  - textDocument/didClose
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // probably don't need
 | 
					 | 
				
			||||||
    //  - textDocument/willSave
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace
 | 
					}  // namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CompletionSession::CompletionSession(const Project::Entry& file, WorkingFiles* working_files) : file(file) {
 | 
					CompletionSession::CompletionSession(const Project::Entry& file, WorkingFiles* working_files)
 | 
				
			||||||
  std::vector<CXUnsavedFile> unsaved = working_files->AsUnsavedFiles();
 | 
					  : file(file), working_files(working_files) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CompletionSession::~CompletionSession() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void CompletionSession::EnsureCompletionState() {
 | 
				
			||||||
 | 
					  if (active && active_index) {
 | 
				
			||||||
 | 
					    // TODO: Investigate if this helps performance. It causes crashes on windows.
 | 
				
			||||||
 | 
					    //std::vector<CXUnsavedFile> unsaved = working_files->AsUnsavedFiles();
 | 
				
			||||||
 | 
					    //active->ReparseTranslationUnit(unsaved);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  std::vector<std::string> args = file.args;
 | 
					  std::vector<std::string> args = file.args;
 | 
				
			||||||
  args.push_back("-fparse-all-comments");
 | 
					  args.push_back("-fparse-all-comments");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  std::cerr << "Creating completion session with arguments " << StringJoin(args) << std::endl;
 | 
					  std::vector<CXUnsavedFile> unsaved = working_files->AsUnsavedFiles();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // TODO: I think we crash when there are syntax errors.
 | 
					  std::cerr << "[complete] Creating completion session with arguments " << StringJoin(args) << std::endl;
 | 
				
			||||||
  active_index = MakeUnique<clang::Index>(0 /*excludeDeclarationsFromPCH*/, 0 /*displayDiagnostics*/);
 | 
					  active_index = MakeUnique<clang::Index>(0 /*excludeDeclarationsFromPCH*/, 0 /*displayDiagnostics*/);
 | 
				
			||||||
  active = MakeUnique<clang::TranslationUnit>(*active_index, file.filename, args, unsaved, Flags());
 | 
					  active = MakeUnique<clang::TranslationUnit>(*active_index, file.filename, args, unsaved, Flags());
 | 
				
			||||||
  std::cerr << "Done creating active; did_fail=" << active->did_fail << std::endl;
 | 
					  std::cerr << "[complete] Done creating active; did_fail=" << active->did_fail << std::endl;
 | 
				
			||||||
  //if (active->did_fail) {
 | 
					 | 
				
			||||||
  //  std::cerr << "Failed to create translation unit; trying again..." << std::endl;
 | 
					 | 
				
			||||||
  //  active = MakeUnique<clang::TranslationUnit>(*active_index, file.filename, args, unsaved, Flags());
 | 
					 | 
				
			||||||
  //}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Despite requesting clang create a precompiled header on the first parse in
 | 
					 | 
				
			||||||
  // Flags() via CXTranslationUnit_CreatePreambleOnFirstParse, it doesn't seem
 | 
					 | 
				
			||||||
  // to do so. Immediately reparsing will create one which reduces
 | 
					 | 
				
			||||||
  // clang_codeCompleteAt timing from 200ms to 20ms on simple files.
 | 
					 | 
				
			||||||
  // TODO: figure out why this is crashing so much
 | 
					 | 
				
			||||||
  if (!active->did_fail) {
 | 
					 | 
				
			||||||
    std::cerr << "Start reparse" << std::endl;
 | 
					 | 
				
			||||||
    active->ReparseTranslationUnit(unsaved);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    std::cerr << "Done reparse" << std::endl;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
CompletionSession::~CompletionSession() {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void CompletionSession::Refresh(std::vector<CXUnsavedFile>& unsaved) {
 | 
					 | 
				
			||||||
  // TODO: Do this off the code completion thread so we don't block completions.
 | 
					 | 
				
			||||||
  active->ReparseTranslationUnit(unsaved);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CompletionManager::CompletionManager(IndexerConfig* config, Project* project, WorkingFiles* working_files)
 | 
					CompletionManager::CompletionManager(IndexerConfig* config, Project* project, WorkingFiles* working_files)
 | 
				
			||||||
    : config(config), project(project), working_files(working_files) {
 | 
					    : config(config), project(project), working_files(working_files) {
 | 
				
			||||||
  new std::thread([&]() {
 | 
					  new std::thread([&]() {
 | 
				
			||||||
    SetCurrentThreadName("completion");
 | 
					    SetCurrentThreadName("complete");
 | 
				
			||||||
    CompletionMain(this);
 | 
					    CompletionMain(this);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -390,16 +326,27 @@ CompletionSession* CompletionManager::GetOrOpenSession(const std::string& filena
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Create new session. Note that this will block.
 | 
					  // Create new session. Note that this will block.
 | 
				
			||||||
  std::cerr << "Creating new code completion session for " << filename << std::endl;
 | 
					  std::cerr << "[complete] Creating new code completion session for " << filename << std::endl;
 | 
				
			||||||
  optional<Project::Entry> entry = project->FindCompilationEntryForFile(filename);
 | 
					  optional<Project::Entry> entry = project->FindCompilationEntryForFile(filename);
 | 
				
			||||||
  if (!entry) {
 | 
					  if (!entry) {
 | 
				
			||||||
    std::cerr << "Unable to find compilation entry" << std::endl;
 | 
					    std::cerr << "[complete] Unable to find compilation entry" << std::endl;
 | 
				
			||||||
    entry = Project::Entry();
 | 
					    entry = Project::Entry();
 | 
				
			||||||
    entry->filename = filename;
 | 
					    entry->filename = filename;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  else {
 | 
					  else {
 | 
				
			||||||
    std::cerr << "Found compilation entry" << std::endl;
 | 
					    std::cerr << "[complete] Found compilation entry" << std::endl;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  sessions.push_back(MakeUnique<CompletionSession>(*entry, working_files));
 | 
					  sessions.push_back(MakeUnique<CompletionSession>(*entry, working_files));
 | 
				
			||||||
  return sessions[sessions.size() - 1].get();
 | 
					  return sessions[sessions.size() - 1].get();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void CompletionManager::DropAllSessionsExcept(const std::string& filename) {
 | 
				
			||||||
 | 
					  for (auto& session : sessions) {
 | 
				
			||||||
 | 
					    if (session->file.filename == filename)
 | 
				
			||||||
 | 
					      continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::lock_guard<std::mutex> lock(session->usage_lock);
 | 
				
			||||||
 | 
					    session->active.reset();
 | 
				
			||||||
 | 
					    session->active_index.reset();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -8,9 +8,14 @@
 | 
				
			|||||||
#include <clang-c/Index.h>
 | 
					#include <clang-c/Index.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <functional>
 | 
					#include <functional>
 | 
				
			||||||
 | 
					#include <mutex>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct CompletionSession {
 | 
					struct CompletionSession {
 | 
				
			||||||
  Project::Entry file;
 | 
					  Project::Entry file;
 | 
				
			||||||
 | 
					  WorkingFiles* working_files;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Acquired when the session is being used.
 | 
				
			||||||
 | 
					  std::mutex usage_lock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // The active translation unit.
 | 
					  // The active translation unit.
 | 
				
			||||||
  std::unique_ptr<clang::TranslationUnit> active;
 | 
					  std::unique_ptr<clang::TranslationUnit> active;
 | 
				
			||||||
@ -18,6 +23,7 @@ struct CompletionSession {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // Updated translation unit. If |is_updated_ready| is true, then |updated|
 | 
					  // Updated translation unit. If |is_updated_ready| is true, then |updated|
 | 
				
			||||||
  // contains more recent state than |active| and the two should be swapped.
 | 
					  // contains more recent state than |active| and the two should be swapped.
 | 
				
			||||||
 | 
					  //
 | 
				
			||||||
  // TODO: implement this. Needs changes in Refresh and CodeComplete.
 | 
					  // TODO: implement this. Needs changes in Refresh and CodeComplete.
 | 
				
			||||||
  //bool is_updated_ready = false;
 | 
					  //bool is_updated_ready = false;
 | 
				
			||||||
  //std::unique_ptr<clang::TranslationUnit> updated;
 | 
					  //std::unique_ptr<clang::TranslationUnit> updated;
 | 
				
			||||||
@ -26,8 +32,8 @@ struct CompletionSession {
 | 
				
			|||||||
  CompletionSession(const Project::Entry& file, WorkingFiles* working_files);
 | 
					  CompletionSession(const Project::Entry& file, WorkingFiles* working_files);
 | 
				
			||||||
  ~CompletionSession();
 | 
					  ~CompletionSession();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Refresh file index.
 | 
					  // Validate that we have |active| and |active_index|.
 | 
				
			||||||
  void Refresh(std::vector<CXUnsavedFile>& unsaved);
 | 
					  void EnsureCompletionState();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct CompletionManager {
 | 
					struct CompletionManager {
 | 
				
			||||||
@ -41,6 +47,7 @@ struct CompletionManager {
 | 
				
			|||||||
    lsTextDocumentPositionParams location;
 | 
					    lsTextDocumentPositionParams location;
 | 
				
			||||||
    OnComplete on_complete;
 | 
					    OnComplete on_complete;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  AtomicObject<CompletionRequest> completion_request;
 | 
					  AtomicObject<CompletionRequest> completion_request;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  CompletionManager(IndexerConfig* config, Project* project, WorkingFiles* working_files);
 | 
					  CompletionManager(IndexerConfig* config, Project* project, WorkingFiles* working_files);
 | 
				
			||||||
@ -50,4 +57,5 @@ struct CompletionManager {
 | 
				
			|||||||
  void CodeComplete(const lsTextDocumentPositionParams& completion_location, const OnComplete& on_complete);
 | 
					  void CodeComplete(const lsTextDocumentPositionParams& completion_location, const OnComplete& on_complete);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  CompletionSession* GetOrOpenSession(const std::string& filename);
 | 
					  CompletionSession* GetOrOpenSession(const std::string& filename);
 | 
				
			||||||
 | 
					  void DropAllSessionsExcept(const std::string& filename);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@ -1534,6 +1534,7 @@ bool QueryDbMainLoop(
 | 
				
			|||||||
          working_file->pending_new_index_content = working_file->buffer_content;
 | 
					          working_file->pending_new_index_content = working_file->buffer_content;
 | 
				
			||||||
          queue_do_index->Enqueue(Index_DoIndex(Index_DoIndex::Type::Parse, project->FindCompilationEntryForFile(path)));
 | 
					          queue_do_index->Enqueue(Index_DoIndex(Index_DoIndex::Type::Parse, project->FindCompilationEntryForFile(path)));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        completion_manager->DropAllSessionsExcept(path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@ -1579,7 +1580,7 @@ bool QueryDbMainLoop(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
          Timer timer;
 | 
					          Timer timer;
 | 
				
			||||||
          ipc->SendOutMessageToClient(IpcId::TextDocumentCompletion, response);
 | 
					          ipc->SendOutMessageToClient(IpcId::TextDocumentCompletion, response);
 | 
				
			||||||
          timer.ResetAndPrint("Writing completion results");
 | 
					          timer.ResetAndPrint("[complete] Writing completion results");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          delete message;
 | 
					          delete message;
 | 
				
			||||||
        }, message.release(), std::placeholders::_1);
 | 
					        }, message.release(), std::placeholders::_1);
 | 
				
			||||||
 | 
				
			|||||||
@ -151,6 +151,11 @@ CXUnsavedFile WorkingFile::AsUnsavedFile() const {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
WorkingFile* WorkingFiles::GetFileByFilename(const std::string& filename) {
 | 
					WorkingFile* WorkingFiles::GetFileByFilename(const std::string& filename) {
 | 
				
			||||||
 | 
					  std::lock_guard<std::mutex> lock(files_mutex);
 | 
				
			||||||
 | 
					  return GetFileByFilenameNoLock(filename);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WorkingFile* WorkingFiles::GetFileByFilenameNoLock(const std::string& filename) {
 | 
				
			||||||
  for (auto& file : files) {
 | 
					  for (auto& file : files) {
 | 
				
			||||||
    if (file->filename == filename)
 | 
					    if (file->filename == filename)
 | 
				
			||||||
      return file.get();
 | 
					      return file.get();
 | 
				
			||||||
@ -159,11 +164,13 @@ WorkingFile* WorkingFiles::GetFileByFilename(const std::string& filename) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
WorkingFile* WorkingFiles::OnOpen(const Ipc_TextDocumentDidOpen::Params& open) {
 | 
					WorkingFile* WorkingFiles::OnOpen(const Ipc_TextDocumentDidOpen::Params& open) {
 | 
				
			||||||
 | 
					  std::lock_guard<std::mutex> lock(files_mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  std::string filename = open.textDocument.uri.GetPath();
 | 
					  std::string filename = open.textDocument.uri.GetPath();
 | 
				
			||||||
  std::string content = open.textDocument.text;
 | 
					  std::string content = open.textDocument.text;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // The file may already be open.
 | 
					  // The file may already be open.
 | 
				
			||||||
  if (WorkingFile* file = GetFileByFilename(filename)) {
 | 
					  if (WorkingFile* file = GetFileByFilenameNoLock(filename)) {
 | 
				
			||||||
    file->version = open.textDocument.version;
 | 
					    file->version = open.textDocument.version;
 | 
				
			||||||
    file->buffer_content = content;
 | 
					    file->buffer_content = content;
 | 
				
			||||||
    file->OnBufferContentUpdated();
 | 
					    file->OnBufferContentUpdated();
 | 
				
			||||||
@ -175,8 +182,10 @@ WorkingFile* WorkingFiles::OnOpen(const Ipc_TextDocumentDidOpen::Params& open) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void WorkingFiles::OnChange(const Ipc_TextDocumentDidChange::Params& change) {
 | 
					void WorkingFiles::OnChange(const Ipc_TextDocumentDidChange::Params& change) {
 | 
				
			||||||
 | 
					  std::lock_guard<std::mutex> lock(files_mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  std::string filename = change.textDocument.uri.GetPath();
 | 
					  std::string filename = change.textDocument.uri.GetPath();
 | 
				
			||||||
  WorkingFile* file = GetFileByFilename(filename);
 | 
					  WorkingFile* file = GetFileByFilenameNoLock(filename);
 | 
				
			||||||
  if (!file) {
 | 
					  if (!file) {
 | 
				
			||||||
    std::cerr << "Could not change " << filename << " because it was not open" << std::endl;
 | 
					    std::cerr << "Could not change " << filename << " because it was not open" << std::endl;
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
@ -205,6 +214,8 @@ void WorkingFiles::OnChange(const Ipc_TextDocumentDidChange::Params& change) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void WorkingFiles::OnClose(const Ipc_TextDocumentDidClose::Params& close) {
 | 
					void WorkingFiles::OnClose(const Ipc_TextDocumentDidClose::Params& close) {
 | 
				
			||||||
 | 
					  std::lock_guard<std::mutex> lock(files_mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  std::string filename = close.textDocument.uri.GetPath();
 | 
					  std::string filename = close.textDocument.uri.GetPath();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (int i = 0; i < files.size(); ++i) {
 | 
					  for (int i = 0; i < files.size(); ++i) {
 | 
				
			||||||
@ -217,7 +228,9 @@ void WorkingFiles::OnClose(const Ipc_TextDocumentDidClose::Params& close) {
 | 
				
			|||||||
  std::cerr << "Could not close " << filename << " because it was not open" << std::endl;
 | 
					  std::cerr << "Could not close " << filename << " because it was not open" << std::endl;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::vector<CXUnsavedFile> WorkingFiles::AsUnsavedFiles() const {
 | 
					std::vector<CXUnsavedFile> WorkingFiles::AsUnsavedFiles() {
 | 
				
			||||||
 | 
					  std::lock_guard<std::mutex> lock(files_mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  std::vector<CXUnsavedFile> result;
 | 
					  std::vector<CXUnsavedFile> result;
 | 
				
			||||||
  result.reserve(files.size());
 | 
					  result.reserve(files.size());
 | 
				
			||||||
  for (auto& file : files)
 | 
					  for (auto& file : files)
 | 
				
			||||||
 | 
				
			|||||||
@ -6,6 +6,7 @@
 | 
				
			|||||||
#include <clang-c/Index.h>
 | 
					#include <clang-c/Index.h>
 | 
				
			||||||
#include <optional.h>
 | 
					#include <optional.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <mutex>
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using std::experimental::optional;
 | 
					using std::experimental::optional;
 | 
				
			||||||
@ -52,15 +53,22 @@ struct WorkingFile {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct WorkingFiles {
 | 
					struct WorkingFiles {
 | 
				
			||||||
 | 
					  //
 | 
				
			||||||
 | 
					  // :: IMPORTANT :: All methods in this class are guarded by a single lock.
 | 
				
			||||||
 | 
					  //
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Find the file with the given filename.
 | 
					  // Find the file with the given filename.
 | 
				
			||||||
  WorkingFile* GetFileByFilename(const std::string& filename);
 | 
					  WorkingFile* GetFileByFilename(const std::string& filename);
 | 
				
			||||||
 | 
					  WorkingFile* GetFileByFilenameNoLock(const std::string& filename);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  WorkingFile* OnOpen(const Ipc_TextDocumentDidOpen::Params& open);
 | 
					  WorkingFile* OnOpen(const Ipc_TextDocumentDidOpen::Params& open);
 | 
				
			||||||
  void OnChange(const Ipc_TextDocumentDidChange::Params& change);
 | 
					  void OnChange(const Ipc_TextDocumentDidChange::Params& change);
 | 
				
			||||||
  void OnClose(const Ipc_TextDocumentDidClose::Params& close);
 | 
					  void OnClose(const Ipc_TextDocumentDidClose::Params& close);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  std::vector<CXUnsavedFile> AsUnsavedFiles() const;
 | 
					  std::vector<CXUnsavedFile> AsUnsavedFiles();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Use unique_ptrs so we can handout WorkingFile ptrs and not have them
 | 
					  // Use unique_ptrs so we can handout WorkingFile ptrs and not have them
 | 
				
			||||||
  // invalidated if we resize files.
 | 
					  // invalidated if we resize files.
 | 
				
			||||||
  std::vector<std::unique_ptr<WorkingFile>> files;
 | 
					  std::vector<std::unique_ptr<WorkingFile>> files;
 | 
				
			||||||
 | 
					  std::mutex files_mutex; // Protects |files|.
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user