mirror of
				https://github.com/MaskRay/ccls.git
				synced 2025-11-04 06:15:20 +00:00 
			
		
		
		
	.
This commit is contained in:
		
							parent
							
								
									da649891ae
								
							
						
					
					
						commit
						89dd4b066b
					
				
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -2,3 +2,4 @@
 | 
				
			|||||||
build
 | 
					build
 | 
				
			||||||
debug
 | 
					debug
 | 
				
			||||||
release
 | 
					release
 | 
				
			||||||
 | 
					/compile_commands.json
 | 
				
			||||||
 | 
				
			|||||||
@ -170,8 +170,6 @@ target_sources(ccls PRIVATE
 | 
				
			|||||||
               src/cache_manager.cc
 | 
					               src/cache_manager.cc
 | 
				
			||||||
               src/clang_complete.cc
 | 
					               src/clang_complete.cc
 | 
				
			||||||
               src/clang_cursor.cc
 | 
					               src/clang_cursor.cc
 | 
				
			||||||
               src/clang_format.cc
 | 
					 | 
				
			||||||
               src/clang_index.cc
 | 
					 | 
				
			||||||
               src/clang_indexer.cc
 | 
					               src/clang_indexer.cc
 | 
				
			||||||
               src/clang_translation_unit.cc
 | 
					               src/clang_translation_unit.cc
 | 
				
			||||||
               src/clang_utils.cc
 | 
					               src/clang_utils.cc
 | 
				
			||||||
@ -201,11 +199,8 @@ target_sources(ccls PRIVATE
 | 
				
			|||||||
               src/query_utils.cc
 | 
					               src/query_utils.cc
 | 
				
			||||||
               src/query.cc
 | 
					               src/query.cc
 | 
				
			||||||
               src/queue_manager.cc
 | 
					               src/queue_manager.cc
 | 
				
			||||||
               src/recorder.cc
 | 
					 | 
				
			||||||
               src/semantic_highlight_symbol_cache.cc
 | 
					 | 
				
			||||||
               src/serializer.cc
 | 
					               src/serializer.cc
 | 
				
			||||||
               src/standard_includes.cc
 | 
					               src/standard_includes.cc
 | 
				
			||||||
               src/task.cc
 | 
					 | 
				
			||||||
               src/test.cc
 | 
					               src/test.cc
 | 
				
			||||||
               src/third_party_impl.cc
 | 
					               src/third_party_impl.cc
 | 
				
			||||||
               src/timer.cc
 | 
					               src/timer.cc
 | 
				
			||||||
@ -242,9 +237,7 @@ target_sources(ccls PRIVATE
 | 
				
			|||||||
               src/messages/text_document_document_highlight.cc
 | 
					               src/messages/text_document_document_highlight.cc
 | 
				
			||||||
               src/messages/text_document_document_link.cc
 | 
					               src/messages/text_document_document_link.cc
 | 
				
			||||||
               src/messages/text_document_document_symbol.cc
 | 
					               src/messages/text_document_document_symbol.cc
 | 
				
			||||||
               src/messages/text_document_formatting.cc
 | 
					 | 
				
			||||||
               src/messages/text_document_hover.cc
 | 
					               src/messages/text_document_hover.cc
 | 
				
			||||||
               src/messages/text_document_range_formatting.cc
 | 
					 | 
				
			||||||
               src/messages/text_document_references.cc
 | 
					               src/messages/text_document_references.cc
 | 
				
			||||||
               src/messages/text_document_rename.cc
 | 
					               src/messages/text_document_rename.cc
 | 
				
			||||||
               src/messages/text_document_signature_help.cc
 | 
					               src/messages/text_document_signature_help.cc
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
# ccls
 | 
					# ccls
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ccls is a fork of cquery, a C/C++/Objective-C language server.
 | 
					ccls is a fork of cquery (originally written by Jacob Dufault),
 | 
				
			||||||
 | 
					a C/C++/Objective-C language server.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  * code completion (with both signature help and snippets)
 | 
					  * code completion (with both signature help and snippets)
 | 
				
			||||||
  * finding [definition](src/messages/text_document_definition.cc)/[references](src/messages/text_document_references.cc)
 | 
					  * finding [definition](src/messages/text_document_definition.cc)/[references](src/messages/text_document_references.cc)
 | 
				
			||||||
 | 
				
			|||||||
@ -1 +0,0 @@
 | 
				
			|||||||
build/release/compile_commands.json
 | 
					 | 
				
			||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "clang_index.h"
 | 
					#include "clang_cursor.h"
 | 
				
			||||||
#include "clang_translation_unit.h"
 | 
					#include "clang_translation_unit.h"
 | 
				
			||||||
#include "lru_cache.h"
 | 
					#include "lru_cache.h"
 | 
				
			||||||
#include "lsp_completion.h"
 | 
					#include "lsp_completion.h"
 | 
				
			||||||
 | 
				
			|||||||
@ -2,10 +2,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "clang_utils.h"
 | 
					#include "clang_utils.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <assert.h>
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <algorithm>
 | 
					#include <algorithm>
 | 
				
			||||||
#include <cassert>
 | 
					#include <mutex>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Range ResolveCXSourceRange(const CXSourceRange& range, CXFile* cx_file) {
 | 
					Range ResolveCXSourceRange(const CXSourceRange& range, CXFile* cx_file) {
 | 
				
			||||||
  CXSourceLocation start = clang_getRangeStart(range);
 | 
					  CXSourceLocation start = clang_getRangeStart(range);
 | 
				
			||||||
@ -284,3 +284,22 @@ NtString ClangCursor::get_comments() const {
 | 
				
			|||||||
std::string ClangCursor::ToString() const {
 | 
					std::string ClangCursor::ToString() const {
 | 
				
			||||||
  return ::ToString(get_kind()) + " " + get_spell_name();
 | 
					  return ::ToString(get_kind()) + " " + get_spell_name();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ClangIndex::ClangIndex() : ClangIndex(1, 0) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ClangIndex::ClangIndex(int exclude_declarations_from_pch,
 | 
				
			||||||
 | 
					                       int display_diagnostics) {
 | 
				
			||||||
 | 
					  // llvm::InitializeAllTargets (and possibly others) called by
 | 
				
			||||||
 | 
					  // clang_createIndex transtively modifies/reads lib/Support/TargetRegistry.cpp
 | 
				
			||||||
 | 
					  // FirstTarget. There will be a race condition if two threads call
 | 
				
			||||||
 | 
					  // clang_createIndex concurrently.
 | 
				
			||||||
 | 
					  static std::mutex mutex_;
 | 
				
			||||||
 | 
					  std::lock_guard<std::mutex> lock(mutex_);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  cx_index =
 | 
				
			||||||
 | 
					      clang_createIndex(exclude_declarations_from_pch, display_diagnostics);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ClangIndex::~ClangIndex() {
 | 
				
			||||||
 | 
					  clang_disposeIndex(cx_index);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -114,3 +114,14 @@ struct hash<ClangCursor> {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
}  // namespace std
 | 
					}  // namespace std
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Simple RAII wrapper about CXIndex.
 | 
				
			||||||
 | 
					// Note: building a ClangIndex instance acquires a global lock, since libclang
 | 
				
			||||||
 | 
					// API does not appear to be thread-safe here.
 | 
				
			||||||
 | 
					class ClangIndex {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  ClangIndex();
 | 
				
			||||||
 | 
					  ClangIndex(int exclude_declarations_from_pch, int display_diagnostics);
 | 
				
			||||||
 | 
					  ~ClangIndex();
 | 
				
			||||||
 | 
					  CXIndex cx_index;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -1,117 +0,0 @@
 | 
				
			|||||||
#if USE_CLANG_CXX
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "clang_format.h"
 | 
					 | 
				
			||||||
#include "working_files.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <doctest/doctest.h>
 | 
					 | 
				
			||||||
#include <loguru.hpp>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using namespace clang;
 | 
					 | 
				
			||||||
using clang::format::FormatStyle;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO Objective-C 'header/interface' files may use .h, we should get this from
 | 
					 | 
				
			||||||
// project information.
 | 
					 | 
				
			||||||
FormatStyle::LanguageKind getLanguageKindFromFilename(
 | 
					 | 
				
			||||||
    llvm::StringRef filename) {
 | 
					 | 
				
			||||||
  if (filename.endswith(".m") || filename.endswith(".mm")) {
 | 
					 | 
				
			||||||
    return FormatStyle::LK_ObjC;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return FormatStyle::LK_Cpp;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}  // namespace
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::vector<tooling::Replacement> ClangFormatDocument(
 | 
					 | 
				
			||||||
    WorkingFile* working_file,
 | 
					 | 
				
			||||||
    int start,
 | 
					 | 
				
			||||||
    int end,
 | 
					 | 
				
			||||||
    lsFormattingOptions options) {
 | 
					 | 
				
			||||||
  const auto language_kind =
 | 
					 | 
				
			||||||
      getLanguageKindFromFilename(working_file->filename);
 | 
					 | 
				
			||||||
  FormatStyle predefined_style;
 | 
					 | 
				
			||||||
  getPredefinedStyle("chromium", language_kind, &predefined_style);
 | 
					 | 
				
			||||||
  llvm::Expected<FormatStyle> style =
 | 
					 | 
				
			||||||
      format::getStyle("file", working_file->filename, "chromium");
 | 
					 | 
				
			||||||
  if (!style) {
 | 
					 | 
				
			||||||
    // If, for some reason, we cannot get a format style, use Chromium's with
 | 
					 | 
				
			||||||
    // tab configuration provided by the client editor.
 | 
					 | 
				
			||||||
    LOG_S(ERROR) << llvm::toString(style.takeError());
 | 
					 | 
				
			||||||
    predefined_style.UseTab = options.insertSpaces
 | 
					 | 
				
			||||||
                                  ? FormatStyle::UseTabStyle::UT_Never
 | 
					 | 
				
			||||||
                                  : FormatStyle::UseTabStyle::UT_Always;
 | 
					 | 
				
			||||||
    predefined_style.IndentWidth = options.tabSize;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  auto format_result = reformat(
 | 
					 | 
				
			||||||
      style ? *style : predefined_style, working_file->buffer_content,
 | 
					 | 
				
			||||||
      llvm::ArrayRef<tooling::Range>(tooling::Range(start, end - start)),
 | 
					 | 
				
			||||||
      working_file->filename);
 | 
					 | 
				
			||||||
  return std::vector<tooling::Replacement>(format_result.begin(),
 | 
					 | 
				
			||||||
                                           format_result.end());
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
TEST_SUITE("ClangFormat") {
 | 
					 | 
				
			||||||
  TEST_CASE("entireDocument") {
 | 
					 | 
				
			||||||
    const std::string sample_document = "int main() { int *i = 0; return 0; }";
 | 
					 | 
				
			||||||
    WorkingFile* file = new WorkingFile("foo.cc", sample_document);
 | 
					 | 
				
			||||||
    lsFormattingOptions formatting_options;
 | 
					 | 
				
			||||||
    formatting_options.insertSpaces = true;
 | 
					 | 
				
			||||||
    const auto replacements = ClangFormatDocument(
 | 
					 | 
				
			||||||
        file, 0, sample_document.size(), formatting_options);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // echo "int main() { int *i = 0; return 0; }" | clang-format
 | 
					 | 
				
			||||||
    // -style=Chromium -output-replacements-xml
 | 
					 | 
				
			||||||
    //
 | 
					 | 
				
			||||||
    // <?xml version='1.0'?>
 | 
					 | 
				
			||||||
    // <replacements xml:space='preserve' incomplete_format='false'>
 | 
					 | 
				
			||||||
    // <replacement offset='12' length='1'>
  </replacement>
 | 
					 | 
				
			||||||
    // <replacement offset='16' length='1'></replacement>
 | 
					 | 
				
			||||||
    // <replacement offset='18' length='0'> </replacement>
 | 
					 | 
				
			||||||
    // <replacement offset='24' length='1'>
  </replacement>
 | 
					 | 
				
			||||||
    // <replacement offset='34' length='1'>
</replacement>
 | 
					 | 
				
			||||||
    // </replacements>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    REQUIRE(replacements.size() == 5);
 | 
					 | 
				
			||||||
    REQUIRE(replacements[0].getOffset() == 12);
 | 
					 | 
				
			||||||
    REQUIRE(replacements[0].getLength() == 1);
 | 
					 | 
				
			||||||
    REQUIRE(replacements[0].getReplacementText() == "\n  ");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    REQUIRE(replacements[1].getOffset() == 16);
 | 
					 | 
				
			||||||
    REQUIRE(replacements[1].getLength() == 1);
 | 
					 | 
				
			||||||
    REQUIRE(replacements[1].getReplacementText() == "");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    REQUIRE(replacements[2].getOffset() == 18);
 | 
					 | 
				
			||||||
    REQUIRE(replacements[2].getLength() == 0);
 | 
					 | 
				
			||||||
    REQUIRE(replacements[2].getReplacementText() == " ");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    REQUIRE(replacements[3].getOffset() == 24);
 | 
					 | 
				
			||||||
    REQUIRE(replacements[3].getLength() == 1);
 | 
					 | 
				
			||||||
    REQUIRE(replacements[3].getReplacementText() == "\n  ");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    REQUIRE(replacements[4].getOffset() == 34);
 | 
					 | 
				
			||||||
    REQUIRE(replacements[4].getLength() == 1);
 | 
					 | 
				
			||||||
    REQUIRE(replacements[4].getReplacementText() == "\n");
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  TEST_CASE("range") {
 | 
					 | 
				
			||||||
    const std::string sampleDocument = "int main() { int *i = 0; return 0; }";
 | 
					 | 
				
			||||||
    WorkingFile* file = new WorkingFile("foo.cc", sampleDocument);
 | 
					 | 
				
			||||||
    lsFormattingOptions formattingOptions;
 | 
					 | 
				
			||||||
    formattingOptions.insertSpaces = true;
 | 
					 | 
				
			||||||
    const auto replacements =
 | 
					 | 
				
			||||||
        ClangFormatDocument(file, 30, sampleDocument.size(), formattingOptions);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    REQUIRE(replacements.size() == 2);
 | 
					 | 
				
			||||||
    REQUIRE(replacements[0].getOffset() == 24);
 | 
					 | 
				
			||||||
    REQUIRE(replacements[0].getLength() == 1);
 | 
					 | 
				
			||||||
    REQUIRE(replacements[0].getReplacementText() == "\n  ");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    REQUIRE(replacements[1].getOffset() == 34);
 | 
					 | 
				
			||||||
    REQUIRE(replacements[1].getLength() == 1);
 | 
					 | 
				
			||||||
    REQUIRE(replacements[1].getReplacementText() == "\n");
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,18 +0,0 @@
 | 
				
			|||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if USE_CLANG_CXX
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "lsp.h"
 | 
					 | 
				
			||||||
#include "working_files.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <clang/Format/Format.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <vector>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::vector<clang::tooling::Replacement> ClangFormatDocument(
 | 
					 | 
				
			||||||
    WorkingFile* working_file,
 | 
					 | 
				
			||||||
    int start,
 | 
					 | 
				
			||||||
    int end,
 | 
					 | 
				
			||||||
    lsFormattingOptions options);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,22 +0,0 @@
 | 
				
			|||||||
#include "clang_index.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <mutex>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ClangIndex::ClangIndex() : ClangIndex(1, 0) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ClangIndex::ClangIndex(int exclude_declarations_from_pch,
 | 
					 | 
				
			||||||
                       int display_diagnostics) {
 | 
					 | 
				
			||||||
  // llvm::InitializeAllTargets (and possibly others) called by
 | 
					 | 
				
			||||||
  // clang_createIndex transtively modifies/reads lib/Support/TargetRegistry.cpp
 | 
					 | 
				
			||||||
  // FirstTarget. There will be a race condition if two threads call
 | 
					 | 
				
			||||||
  // clang_createIndex concurrently.
 | 
					 | 
				
			||||||
  static std::mutex mutex_;
 | 
					 | 
				
			||||||
  std::lock_guard<std::mutex> lock(mutex_);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  cx_index =
 | 
					 | 
				
			||||||
      clang_createIndex(exclude_declarations_from_pch, display_diagnostics);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ClangIndex::~ClangIndex() {
 | 
					 | 
				
			||||||
  clang_disposeIndex(cx_index);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,14 +0,0 @@
 | 
				
			|||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <clang-c/Index.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Simple RAII wrapper about CXIndex.
 | 
					 | 
				
			||||||
// Note: building a ClangIndex instance acquires a global lock, since libclang
 | 
					 | 
				
			||||||
// API does not appear to be thread-safe here.
 | 
					 | 
				
			||||||
class ClangIndex {
 | 
					 | 
				
			||||||
 public:
 | 
					 | 
				
			||||||
  ClangIndex();
 | 
					 | 
				
			||||||
  ClangIndex(int exclude_declarations_from_pch, int display_diagnostics);
 | 
					 | 
				
			||||||
  ~ClangIndex();
 | 
					 | 
				
			||||||
  CXIndex cx_index;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -1555,7 +1555,6 @@ void OnIndexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) {
 | 
				
			|||||||
      SetVarDetail(var, std::string(decl->entityInfo->name), decl->cursor,
 | 
					      SetVarDetail(var, std::string(decl->entityInfo->name), decl->cursor,
 | 
				
			||||||
                   decl->semanticContainer, !decl->isRedeclaration, db, param);
 | 
					                   decl->semanticContainer, !decl->isRedeclaration, db, param);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // FIXME https://github.com/jacobdufault/ccls/issues/239
 | 
					 | 
				
			||||||
      var->def.kind = GetSymbolKind(decl->entityInfo->kind);
 | 
					      var->def.kind = GetSymbolKind(decl->entityInfo->kind);
 | 
				
			||||||
      if (var->def.kind == lsSymbolKind::Variable &&
 | 
					      if (var->def.kind == lsSymbolKind::Variable &&
 | 
				
			||||||
          decl->cursor.kind == CXCursor_ParmDecl)
 | 
					          decl->cursor.kind == CXCursor_ParmDecl)
 | 
				
			||||||
@ -1776,8 +1775,6 @@ void OnIndexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      // For Typedef/CXXTypeAlias spanning a few lines, display the declaration
 | 
					      // For Typedef/CXXTypeAlias spanning a few lines, display the declaration
 | 
				
			||||||
      // line, with spelling name replaced with qualified name.
 | 
					      // line, with spelling name replaced with qualified name.
 | 
				
			||||||
      // TODO Think how to display multi-line declaration like `typedef struct {
 | 
					 | 
				
			||||||
      // ... } foo;` https://github.com/jacobdufault/ccls/issues/29
 | 
					 | 
				
			||||||
      if (extent.end.line - extent.start.line <
 | 
					      if (extent.end.line - extent.start.line <
 | 
				
			||||||
          kMaxLinesDisplayTypeAliasDeclarations) {
 | 
					          kMaxLinesDisplayTypeAliasDeclarations) {
 | 
				
			||||||
        FileContents& fc = param->file_contents[db->path];
 | 
					        FileContents& fc = param->file_contents[db->path];
 | 
				
			||||||
@ -1924,7 +1921,6 @@ void OnIndexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// https://github.com/jacobdufault/ccls/issues/174
 | 
					 | 
				
			||||||
// Type-dependent member access expressions do not have accurate spelling
 | 
					// Type-dependent member access expressions do not have accurate spelling
 | 
				
			||||||
// ranges.
 | 
					// ranges.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
@ -2328,56 +2324,6 @@ void IndexInit() {
 | 
				
			|||||||
    clang_toggleCrashRecovery(1);
 | 
					    clang_toggleCrashRecovery(1);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ClangSanityCheck() {
 | 
					 | 
				
			||||||
  std::vector<const char*> args = {"clang", "index_tests/vars/class_member.cc"};
 | 
					 | 
				
			||||||
  unsigned opts = 0;
 | 
					 | 
				
			||||||
  CXIndex index = clang_createIndex(0, 1);
 | 
					 | 
				
			||||||
  CXTranslationUnit tu;
 | 
					 | 
				
			||||||
  clang_parseTranslationUnit2FullArgv(index, nullptr, args.data(), args.size(),
 | 
					 | 
				
			||||||
                                      nullptr, 0, opts, &tu);
 | 
					 | 
				
			||||||
  assert(tu);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  IndexerCallbacks callback = {0};
 | 
					 | 
				
			||||||
  callback.abortQuery = [](CXClientData client_data, void* reserved) {
 | 
					 | 
				
			||||||
    return 0;
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  callback.diagnostic = [](CXClientData client_data,
 | 
					 | 
				
			||||||
                           CXDiagnosticSet diagnostics, void* reserved) {};
 | 
					 | 
				
			||||||
  callback.enteredMainFile = [](CXClientData client_data, CXFile mainFile,
 | 
					 | 
				
			||||||
                                void* reserved) -> CXIdxClientFile {
 | 
					 | 
				
			||||||
    return nullptr;
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  callback.ppIncludedFile =
 | 
					 | 
				
			||||||
      [](CXClientData client_data,
 | 
					 | 
				
			||||||
         const CXIdxIncludedFileInfo* file) -> CXIdxClientFile {
 | 
					 | 
				
			||||||
    return nullptr;
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  callback.importedASTFile =
 | 
					 | 
				
			||||||
      [](CXClientData client_data,
 | 
					 | 
				
			||||||
         const CXIdxImportedASTFileInfo*) -> CXIdxClientASTFile {
 | 
					 | 
				
			||||||
    return nullptr;
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  callback.startedTranslationUnit = [](CXClientData client_data,
 | 
					 | 
				
			||||||
                                       void* reserved) -> CXIdxClientContainer {
 | 
					 | 
				
			||||||
    return nullptr;
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  callback.indexDeclaration = [](CXClientData client_data,
 | 
					 | 
				
			||||||
                                 const CXIdxDeclInfo* decl) {};
 | 
					 | 
				
			||||||
  callback.indexEntityReference = [](CXClientData client_data,
 | 
					 | 
				
			||||||
                                     const CXIdxEntityRefInfo* ref) {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const unsigned kIndexOpts = 0;
 | 
					 | 
				
			||||||
  CXIndexAction index_action = clang_IndexAction_create(index);
 | 
					 | 
				
			||||||
  int index_param = 0;
 | 
					 | 
				
			||||||
  clang_toggleCrashRecovery(0);
 | 
					 | 
				
			||||||
  clang_indexTranslationUnit(index_action, &index_param, &callback,
 | 
					 | 
				
			||||||
                             sizeof(IndexerCallbacks), kIndexOpts, tu);
 | 
					 | 
				
			||||||
  clang_IndexAction_dispose(index_action);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  clang_disposeTranslationUnit(tu);
 | 
					 | 
				
			||||||
  clang_disposeIndex(index);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::string GetClangVersion() {
 | 
					std::string GetClangVersion() {
 | 
				
			||||||
  return ToString(clang_getClangVersion());
 | 
					  return ToString(clang_getClangVersion());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,6 @@
 | 
				
			|||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "clang_cursor.h"
 | 
					#include "clang_cursor.h"
 | 
				
			||||||
#include "clang_index.h"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <clang-c/Index.h>
 | 
					#include <clang-c/Index.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2,8 +2,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "platform.h"
 | 
					#include "platform.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <doctest/doctest.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace {
 | 
					namespace {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
lsRange GetLsRangeForFixIt(const CXSourceRange& range) {
 | 
					lsRange GetLsRangeForFixIt(const CXSourceRange& range) {
 | 
				
			||||||
@ -106,31 +104,6 @@ std::optional<lsDiagnostic> BuildAndDisposeDiagnostic(CXDiagnostic diagnostic,
 | 
				
			|||||||
  return ls_diagnostic;
 | 
					  return ls_diagnostic;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if USE_CLANG_CXX
 | 
					 | 
				
			||||||
static lsPosition OffsetToRange(llvm::StringRef document, size_t offset) {
 | 
					 | 
				
			||||||
  // TODO: Support Windows line endings, etc.
 | 
					 | 
				
			||||||
  llvm::StringRef text_before = document.substr(0, offset);
 | 
					 | 
				
			||||||
  int num_line = text_before.count('\n');
 | 
					 | 
				
			||||||
  int num_column = text_before.size() - text_before.rfind('\n') - 1;
 | 
					 | 
				
			||||||
  return {num_line, num_column};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::vector<lsTextEdit> ConvertClangReplacementsIntoTextEdits(
 | 
					 | 
				
			||||||
    llvm::StringRef document,
 | 
					 | 
				
			||||||
    const std::vector<clang::tooling::Replacement>& clang_replacements) {
 | 
					 | 
				
			||||||
  std::vector<lsTextEdit> text_edits_result;
 | 
					 | 
				
			||||||
  for (const auto& replacement : clang_replacements) {
 | 
					 | 
				
			||||||
    const auto startPosition = OffsetToRange(document, replacement.getOffset());
 | 
					 | 
				
			||||||
    const auto endPosition = OffsetToRange(
 | 
					 | 
				
			||||||
        document, replacement.getOffset() + replacement.getLength());
 | 
					 | 
				
			||||||
    text_edits_result.push_back(
 | 
					 | 
				
			||||||
        {{startPosition, endPosition}, replacement.getReplacementText()});
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return text_edits_result;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::string FileName(CXFile file) {
 | 
					std::string FileName(CXFile file) {
 | 
				
			||||||
  CXString cx_name = clang_getFileName(file);
 | 
					  CXString cx_name = clang_getFileName(file);
 | 
				
			||||||
  std::string name = ToString(cx_name);
 | 
					  std::string name = ToString(cx_name);
 | 
				
			||||||
@ -172,60 +145,9 @@ const char* ClangBuiltinTypeName(CXTypeKind kind) {
 | 
				
			|||||||
    case CXType_Double: return "double";
 | 
					    case CXType_Double: return "double";
 | 
				
			||||||
    case CXType_LongDouble: return "long double";
 | 
					    case CXType_LongDouble: return "long double";
 | 
				
			||||||
    case CXType_Float128: return "__float128";
 | 
					    case CXType_Float128: return "__float128";
 | 
				
			||||||
#if CINDEX_VERSION_MINOR >= 43
 | 
					 | 
				
			||||||
    case CXType_Half: return "_Float16";
 | 
					    case CXType_Half: return "_Float16";
 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
    case CXType_NullPtr: return "nullptr";
 | 
					    case CXType_NullPtr: return "nullptr";
 | 
				
			||||||
    default: return "";
 | 
					    default: return "";
 | 
				
			||||||
      // clang-format on
 | 
					    // clang-format on
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
#if USE_CLANG_CXX
 | 
					 | 
				
			||||||
TEST_SUITE("ClangUtils") {
 | 
					 | 
				
			||||||
  TEST_CASE("replacements") {
 | 
					 | 
				
			||||||
    const std::string sample_document =
 | 
					 | 
				
			||||||
        "int \n"
 | 
					 | 
				
			||||||
        "main() { int *i = 0; return 0; \n"
 | 
					 | 
				
			||||||
        "}";
 | 
					 | 
				
			||||||
    const std::vector<clang::tooling::Replacement> clang_replacements = {
 | 
					 | 
				
			||||||
        {"foo.cc", 3, 2, " "},     {"foo.cc", 13, 1, "\n  "},
 | 
					 | 
				
			||||||
        {"foo.cc", 17, 1, ""},     {"foo.cc", 19, 0, " "},
 | 
					 | 
				
			||||||
        {"foo.cc", 25, 1, "\n  "}, {"foo.cc", 35, 2, "\n"}};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Expected format:
 | 
					 | 
				
			||||||
    //
 | 
					 | 
				
			||||||
    // int main() {
 | 
					 | 
				
			||||||
    //   int *i = 0;
 | 
					 | 
				
			||||||
    //   return 0;
 | 
					 | 
				
			||||||
    // }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const auto text_edits = ConvertClangReplacementsIntoTextEdits(
 | 
					 | 
				
			||||||
        sample_document, clang_replacements);
 | 
					 | 
				
			||||||
    REQUIRE(text_edits.size() == 6);
 | 
					 | 
				
			||||||
    REQUIRE(text_edits[0].range.start.line == 0);
 | 
					 | 
				
			||||||
    REQUIRE(text_edits[0].range.start.character == 3);
 | 
					 | 
				
			||||||
    REQUIRE(text_edits[0].newText == " ");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    REQUIRE(text_edits[1].range.start.line == 1);
 | 
					 | 
				
			||||||
    REQUIRE(text_edits[1].range.start.character == 8);
 | 
					 | 
				
			||||||
    REQUIRE(text_edits[1].newText == "\n  ");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    REQUIRE(text_edits[2].range.start.line == 1);
 | 
					 | 
				
			||||||
    REQUIRE(text_edits[2].range.start.character == 12);
 | 
					 | 
				
			||||||
    REQUIRE(text_edits[2].newText == "");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    REQUIRE(text_edits[3].range.start.line == 1);
 | 
					 | 
				
			||||||
    REQUIRE(text_edits[3].range.start.character == 14);
 | 
					 | 
				
			||||||
    REQUIRE(text_edits[3].newText == " ");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    REQUIRE(text_edits[4].range.start.line == 1);
 | 
					 | 
				
			||||||
    REQUIRE(text_edits[4].range.start.character == 20);
 | 
					 | 
				
			||||||
    REQUIRE(text_edits[4].newText == "\n  ");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    REQUIRE(text_edits[5].range.start.line == 1);
 | 
					 | 
				
			||||||
    REQUIRE(text_edits[5].range.start.character == 30);
 | 
					 | 
				
			||||||
    REQUIRE(text_edits[5].newText == "\n");
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -3,11 +3,8 @@
 | 
				
			|||||||
#include "lsp_diagnostic.h"
 | 
					#include "lsp_diagnostic.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <clang-c/Index.h>
 | 
					#include <clang-c/Index.h>
 | 
				
			||||||
#if USE_CLANG_CXX
 | 
					 | 
				
			||||||
#include <clang/Format/Format.h>
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
#include <optional>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <optional>
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::optional<lsDiagnostic> BuildAndDisposeDiagnostic(CXDiagnostic diagnostic,
 | 
					std::optional<lsDiagnostic> BuildAndDisposeDiagnostic(CXDiagnostic diagnostic,
 | 
				
			||||||
@ -21,10 +18,3 @@ std::string ToString(CXString cx_string);
 | 
				
			|||||||
std::string ToString(CXCursorKind cursor_kind);
 | 
					std::string ToString(CXCursorKind cursor_kind);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const char* ClangBuiltinTypeName(CXTypeKind);
 | 
					const char* ClangBuiltinTypeName(CXTypeKind);
 | 
				
			||||||
 | 
					 | 
				
			||||||
// Converts Clang formatting replacement operations into LSP text edits.
 | 
					 | 
				
			||||||
#if USE_CLANG_CXX
 | 
					 | 
				
			||||||
std::vector<lsTextEdit> ConvertClangReplacementsIntoTextEdits(
 | 
					 | 
				
			||||||
    llvm::StringRef document,
 | 
					 | 
				
			||||||
    const std::vector<clang::tooling::Replacement>& clang_replacements);
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -19,8 +19,6 @@
 | 
				
			|||||||
#include "query.h"
 | 
					#include "query.h"
 | 
				
			||||||
#include "query_utils.h"
 | 
					#include "query_utils.h"
 | 
				
			||||||
#include "queue_manager.h"
 | 
					#include "queue_manager.h"
 | 
				
			||||||
#include "recorder.h"
 | 
					 | 
				
			||||||
#include "semantic_highlight_symbol_cache.h"
 | 
					 | 
				
			||||||
#include "serializer.h"
 | 
					#include "serializer.h"
 | 
				
			||||||
#include "serializers/json.h"
 | 
					#include "serializers/json.h"
 | 
				
			||||||
#include "test.h"
 | 
					#include "test.h"
 | 
				
			||||||
@ -43,19 +41,10 @@
 | 
				
			|||||||
#include <unordered_map>
 | 
					#include <unordered_map>
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 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.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO: implement ThreadPool type which monitors CPU usage / number of work
 | 
					 | 
				
			||||||
// items per second completed and scales up/down number of running threads.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::string g_init_options;
 | 
					std::string g_init_options;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace {
 | 
					namespace {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::vector<std::string> kEmptyArgs;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// This function returns true if e2e timing should be displayed for the given
 | 
					// This function returns true if e2e timing should be displayed for the given
 | 
				
			||||||
// MethodId.
 | 
					// MethodId.
 | 
				
			||||||
bool ShouldDisplayMethodTiming(MethodType type) {
 | 
					bool ShouldDisplayMethodTiming(MethodType type) {
 | 
				
			||||||
@ -70,9 +59,6 @@ void PrintHelp() {
 | 
				
			|||||||
      << R"help(ccls is a low-latency C/C++/Objective-C language server.
 | 
					      << R"help(ccls is a low-latency C/C++/Objective-C language server.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Mode:
 | 
					Mode:
 | 
				
			||||||
  --clang-sanity-check
 | 
					 | 
				
			||||||
                Run a simple index test. Verifies basic clang functionality.
 | 
					 | 
				
			||||||
                Needs to be executed from the ccls root checkout directory.
 | 
					 | 
				
			||||||
  --test-unit   Run unit tests.
 | 
					  --test-unit   Run unit tests.
 | 
				
			||||||
  --test-index <opt_filter_path>
 | 
					  --test-index <opt_filter_path>
 | 
				
			||||||
                Run index tests. opt_filter_path can be used to specify which
 | 
					                Run index tests. opt_filter_path can be used to specify which
 | 
				
			||||||
@ -85,37 +71,20 @@ Other command line options:
 | 
				
			|||||||
  --init <initializationOptions>
 | 
					  --init <initializationOptions>
 | 
				
			||||||
                Override client provided initialization options
 | 
					                Override client provided initialization options
 | 
				
			||||||
         https://github.com/cquery-project/cquery/wiki/Initialization-options
 | 
					         https://github.com/cquery-project/cquery/wiki/Initialization-options
 | 
				
			||||||
  --record <path>
 | 
					 | 
				
			||||||
                Writes stdin to <path>.in and stdout to <path>.out
 | 
					 | 
				
			||||||
  --log-file <path>    Logging file for diagnostics
 | 
					  --log-file <path>    Logging file for diagnostics
 | 
				
			||||||
  --log-file-append <path>    Like --log-file, but appending
 | 
					  --log-file-append <path>    Like --log-file, but appending
 | 
				
			||||||
  --log-all-to-stderr  Write all log messages to STDERR.
 | 
					  --log-all-to-stderr  Write all log messages to STDERR.
 | 
				
			||||||
  --wait-for-input     Wait for an '[Enter]' before exiting
 | 
					 | 
				
			||||||
  --help        Print this help information.
 | 
					  --help        Print this help information.
 | 
				
			||||||
  --ci          Prevents tests from prompting the user for input. Used for
 | 
					  --ci          Prevents tests from prompting the user for input. Used for
 | 
				
			||||||
                continuous integration so it can fail faster instead of timing
 | 
					                continuous integration so it can fail faster instead of timing
 | 
				
			||||||
                out.
 | 
					                out.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
See more on https://github.com/cquery-project/cquery/wiki
 | 
					See more on https://github.com/MaskRay/ccls/wiki
 | 
				
			||||||
)help";
 | 
					)help";
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace
 | 
					}  // namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					////////////////////////////////////////////////////////////////////////////////
 | 
				
			||||||
// QUERYDB MAIN ////////////////////////////////////////////////////////////////
 | 
					// QUERYDB MAIN ////////////////////////////////////////////////////////////////
 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					////////////////////////////////////////////////////////////////////////////////
 | 
				
			||||||
@ -248,20 +217,6 @@ void RunQueryDbThread(const std::string& bin_name,
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					////////////////////////////////////////////////////////////////////////////////
 | 
				
			||||||
// STDIN MAIN //////////////////////////////////////////////////////////////////
 | 
					// STDIN MAIN //////////////////////////////////////////////////////////////////
 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					////////////////////////////////////////////////////////////////////////////////
 | 
				
			||||||
@ -333,8 +288,6 @@ void LaunchStdoutThread(std::unordered_map<MethodType, Timer>* request_times,
 | 
				
			|||||||
          time.ResetAndPrint("[e2e] Running " + std::string(message.method));
 | 
					          time.ResetAndPrint("[e2e] Running " + std::string(message.method));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        RecordOutput(message.content);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        fwrite(message.content.c_str(), message.content.size(), 1, stdout);
 | 
					        fwrite(message.content.c_str(), message.content.size(), 1, stdout);
 | 
				
			||||||
        fflush(stdout);
 | 
					        fflush(stdout);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@ -360,23 +313,6 @@ void LanguageServerMain(const std::string& bin_name,
 | 
				
			|||||||
  RunQueryDbThread(bin_name, config, querydb_waiter, indexer_waiter);
 | 
					  RunQueryDbThread(bin_name, config, querydb_waiter, indexer_waiter);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
// MAIN ////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
int main(int argc, char** argv) {
 | 
					int main(int argc, char** argv) {
 | 
				
			||||||
  TraceMe();
 | 
					  TraceMe();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -413,14 +349,6 @@ int main(int argc, char** argv) {
 | 
				
			|||||||
                     loguru::Verbosity_MAX);
 | 
					                     loguru::Verbosity_MAX);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (HasOption(options, "--record"))
 | 
					 | 
				
			||||||
    EnableRecording(options["--record"]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (HasOption(options, "--clang-sanity-check")) {
 | 
					 | 
				
			||||||
    language_server = false;
 | 
					 | 
				
			||||||
    ClangSanityCheck();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (HasOption(options, "--test-unit")) {
 | 
					  if (HasOption(options, "--test-unit")) {
 | 
				
			||||||
    language_server = false;
 | 
					    language_server = false;
 | 
				
			||||||
    doctest::Context context;
 | 
					    doctest::Context context;
 | 
				
			||||||
@ -467,10 +395,5 @@ int main(int argc, char** argv) {
 | 
				
			|||||||
                       &stdout_waiter);
 | 
					                       &stdout_waiter);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (HasOption(options, "--wait-for-input")) {
 | 
					 | 
				
			||||||
    std::cerr << std::endl << "[Enter] to exit" << std::endl;
 | 
					 | 
				
			||||||
    getchar();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return 0;
 | 
					  return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -168,8 +168,6 @@ void IncludeComplete::AddFile(const std::string& absolute_path) {
 | 
				
			|||||||
  if (is_scanning)
 | 
					  if (is_scanning)
 | 
				
			||||||
    lock.lock();
 | 
					    lock.lock();
 | 
				
			||||||
  InsertCompletionItem(absolute_path, std::move(item));
 | 
					  InsertCompletionItem(absolute_path, std::move(item));
 | 
				
			||||||
  if (lock)
 | 
					 | 
				
			||||||
    lock.unlock();
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void IncludeComplete::InsertIncludesFromDirectory(std::string directory,
 | 
					void IncludeComplete::InsertIncludesFromDirectory(std::string directory,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,6 @@
 | 
				
			|||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "clang_cursor.h"
 | 
					#include "clang_cursor.h"
 | 
				
			||||||
#include "clang_index.h"
 | 
					 | 
				
			||||||
#include "clang_translation_unit.h"
 | 
					#include "clang_translation_unit.h"
 | 
				
			||||||
#include "clang_utils.h"
 | 
					#include "clang_utils.h"
 | 
				
			||||||
#include "file_consumer.h"
 | 
					#include "file_consumer.h"
 | 
				
			||||||
@ -16,14 +15,13 @@
 | 
				
			|||||||
#include "symbol.h"
 | 
					#include "symbol.h"
 | 
				
			||||||
#include "utils.h"
 | 
					#include "utils.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <assert.h>
 | 
				
			||||||
 | 
					#include <ctype.h>
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					#include <algorithm>
 | 
				
			||||||
 | 
					#include <unordered_map>
 | 
				
			||||||
#include <optional>
 | 
					#include <optional>
 | 
				
			||||||
#include <string_view>
 | 
					#include <string_view>
 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <ctype.h>
 | 
					 | 
				
			||||||
#include <algorithm>
 | 
					 | 
				
			||||||
#include <cassert>
 | 
					 | 
				
			||||||
#include <cstdint>
 | 
					 | 
				
			||||||
#include <unordered_map>
 | 
					 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct IndexFile;
 | 
					struct IndexFile;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,5 @@
 | 
				
			|||||||
#include "lsp.h"
 | 
					#include "lsp.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "recorder.h"
 | 
					 | 
				
			||||||
#include "serializers/json.h"
 | 
					#include "serializers/json.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <doctest/doctest.h>
 | 
					#include <doctest/doctest.h>
 | 
				
			||||||
@ -67,8 +66,6 @@ std::optional<std::string> ReadJsonRpcContentFrom(
 | 
				
			|||||||
    content += *c;
 | 
					    content += *c;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  RecordInput(content);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return content;
 | 
					  return content;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,6 @@
 | 
				
			|||||||
#include "project.h"
 | 
					#include "project.h"
 | 
				
			||||||
#include "query_utils.h"
 | 
					#include "query_utils.h"
 | 
				
			||||||
#include "queue_manager.h"
 | 
					#include "queue_manager.h"
 | 
				
			||||||
#include "semantic_highlight_symbol_cache.h"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <loguru.hpp>
 | 
					#include <loguru.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -42,6 +41,77 @@ struct ScanLineEvent {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
}  // namespace
 | 
					}  // namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SemanticHighlightSymbolCache::Entry::Entry(
 | 
				
			||||||
 | 
					    SemanticHighlightSymbolCache* all_caches,
 | 
				
			||||||
 | 
					    const std::string& path)
 | 
				
			||||||
 | 
					    : all_caches_(all_caches), path(path) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::optional<int> SemanticHighlightSymbolCache::Entry::TryGetStableId(
 | 
				
			||||||
 | 
					    SymbolKind kind,
 | 
				
			||||||
 | 
					    const std::string& detailed_name) {
 | 
				
			||||||
 | 
					  TNameToId* map = GetMapForSymbol_(kind);
 | 
				
			||||||
 | 
					  auto it = map->find(detailed_name);
 | 
				
			||||||
 | 
					  if (it != map->end())
 | 
				
			||||||
 | 
					    return it->second;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return std::nullopt;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int SemanticHighlightSymbolCache::Entry::GetStableId(
 | 
				
			||||||
 | 
					    SymbolKind kind,
 | 
				
			||||||
 | 
					    const std::string& detailed_name) {
 | 
				
			||||||
 | 
					  std::optional<int> id = TryGetStableId(kind, detailed_name);
 | 
				
			||||||
 | 
					  if (id)
 | 
				
			||||||
 | 
					    return *id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Create a new id. First try to find a key in another map.
 | 
				
			||||||
 | 
					  all_caches_->cache_.IterateValues([&](const std::shared_ptr<Entry>& entry) {
 | 
				
			||||||
 | 
					    std::optional<int> other_id = entry->TryGetStableId(kind, detailed_name);
 | 
				
			||||||
 | 
					    if (other_id) {
 | 
				
			||||||
 | 
					      id = other_id;
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Create a new id.
 | 
				
			||||||
 | 
					  TNameToId* map = GetMapForSymbol_(kind);
 | 
				
			||||||
 | 
					  if (!id)
 | 
				
			||||||
 | 
					    id = all_caches_->next_stable_id_++;
 | 
				
			||||||
 | 
					  return (*map)[detailed_name] = *id;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SemanticHighlightSymbolCache::Entry::TNameToId*
 | 
				
			||||||
 | 
					SemanticHighlightSymbolCache::Entry::GetMapForSymbol_(SymbolKind kind) {
 | 
				
			||||||
 | 
					  switch (kind) {
 | 
				
			||||||
 | 
					    case SymbolKind::Type:
 | 
				
			||||||
 | 
					      return &detailed_type_name_to_stable_id;
 | 
				
			||||||
 | 
					    case SymbolKind::Func:
 | 
				
			||||||
 | 
					      return &detailed_func_name_to_stable_id;
 | 
				
			||||||
 | 
					    case SymbolKind::Var:
 | 
				
			||||||
 | 
					      return &detailed_var_name_to_stable_id;
 | 
				
			||||||
 | 
					    case SymbolKind::File:
 | 
				
			||||||
 | 
					    case SymbolKind::Invalid:
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  assert(false);
 | 
				
			||||||
 | 
					  return nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SemanticHighlightSymbolCache::SemanticHighlightSymbolCache()
 | 
				
			||||||
 | 
					    : cache_(kCacheSize) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SemanticHighlightSymbolCache::Init(Config* config) {
 | 
				
			||||||
 | 
					  match_ = std::make_unique<GroupMatch>(config->highlight.whitelist,
 | 
				
			||||||
 | 
					                                        config->highlight.blacklist);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::shared_ptr<SemanticHighlightSymbolCache::Entry>
 | 
				
			||||||
 | 
					SemanticHighlightSymbolCache::GetCacheForFile(const std::string& path) {
 | 
				
			||||||
 | 
					  return cache_.Get(
 | 
				
			||||||
 | 
					      path, [&, this]() { return std::make_shared<Entry>(this, path); });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
MessageHandler::MessageHandler() {
 | 
					MessageHandler::MessageHandler() {
 | 
				
			||||||
  // Dynamically allocate |message_handlers|, otherwise there will be static
 | 
					  // Dynamically allocate |message_handlers|, otherwise there will be static
 | 
				
			||||||
  // initialization order races.
 | 
					  // initialization order races.
 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +1,14 @@
 | 
				
			|||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "lru_cache.h"
 | 
				
			||||||
#include "lsp.h"
 | 
					#include "lsp.h"
 | 
				
			||||||
 | 
					#include "match.h"
 | 
				
			||||||
#include "method.h"
 | 
					#include "method.h"
 | 
				
			||||||
#include "query.h"
 | 
					#include "query.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <optional>
 | 
					#include <optional>
 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
 | 
					#include <unordered_map>
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ClangCompleteManager;
 | 
					struct ClangCompleteManager;
 | 
				
			||||||
@ -20,11 +22,43 @@ struct IncludeComplete;
 | 
				
			|||||||
struct MultiQueueWaiter;
 | 
					struct MultiQueueWaiter;
 | 
				
			||||||
struct Project;
 | 
					struct Project;
 | 
				
			||||||
struct QueryDatabase;
 | 
					struct QueryDatabase;
 | 
				
			||||||
struct SemanticHighlightSymbolCache;
 | 
					 | 
				
			||||||
struct TimestampManager;
 | 
					struct TimestampManager;
 | 
				
			||||||
struct WorkingFile;
 | 
					struct WorkingFile;
 | 
				
			||||||
struct WorkingFiles;
 | 
					struct WorkingFiles;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Caches symbols for a single file for semantic highlighting to provide
 | 
				
			||||||
 | 
					// relatively stable ids. Only supports xxx files at a time.
 | 
				
			||||||
 | 
					struct SemanticHighlightSymbolCache {
 | 
				
			||||||
 | 
					  struct Entry {
 | 
				
			||||||
 | 
					    SemanticHighlightSymbolCache* all_caches_ = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // The path this cache belongs to.
 | 
				
			||||||
 | 
					    std::string path;
 | 
				
			||||||
 | 
					    // Detailed symbol name to stable id.
 | 
				
			||||||
 | 
					    using TNameToId = std::unordered_map<std::string, int>;
 | 
				
			||||||
 | 
					    TNameToId detailed_type_name_to_stable_id;
 | 
				
			||||||
 | 
					    TNameToId detailed_func_name_to_stable_id;
 | 
				
			||||||
 | 
					    TNameToId detailed_var_name_to_stable_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Entry(SemanticHighlightSymbolCache* all_caches, const std::string& path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::optional<int> TryGetStableId(SymbolKind kind,
 | 
				
			||||||
 | 
					                                 const std::string& detailed_name);
 | 
				
			||||||
 | 
					    int GetStableId(SymbolKind kind, const std::string& detailed_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TNameToId* GetMapForSymbol_(SymbolKind kind);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constexpr static int kCacheSize = 10;
 | 
				
			||||||
 | 
					  LruCache<std::string, Entry> cache_;
 | 
				
			||||||
 | 
					  uint32_t next_stable_id_ = 0;
 | 
				
			||||||
 | 
					  std::unique_ptr<GroupMatch> match_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  SemanticHighlightSymbolCache();
 | 
				
			||||||
 | 
					  void Init(Config*);
 | 
				
			||||||
 | 
					  std::shared_ptr<Entry> GetCacheForFile(const std::string& path);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Out_CclsPublishSemanticHighlighting
 | 
					struct Out_CclsPublishSemanticHighlighting
 | 
				
			||||||
    : public lsOutMessage<Out_CclsPublishSemanticHighlighting> {
 | 
					    : public lsOutMessage<Out_CclsPublishSemanticHighlighting> {
 | 
				
			||||||
  struct Symbol {
 | 
					  struct Symbol {
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,6 @@
 | 
				
			|||||||
#include "platform.h"
 | 
					#include "platform.h"
 | 
				
			||||||
#include "project.h"
 | 
					#include "project.h"
 | 
				
			||||||
#include "queue_manager.h"
 | 
					#include "queue_manager.h"
 | 
				
			||||||
#include "semantic_highlight_symbol_cache.h"
 | 
					 | 
				
			||||||
#include "serializers/json.h"
 | 
					#include "serializers/json.h"
 | 
				
			||||||
#include "timer.h"
 | 
					#include "timer.h"
 | 
				
			||||||
#include "work_thread.h"
 | 
					#include "work_thread.h"
 | 
				
			||||||
@ -361,26 +360,13 @@ MAKE_REFLECT_STRUCT(lsTextDocumentClientCapabilities,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
struct lsClientCapabilities {
 | 
					struct lsClientCapabilities {
 | 
				
			||||||
  // Workspace specific client capabilities.
 | 
					  // Workspace specific client capabilities.
 | 
				
			||||||
  std::optional<lsWorkspaceClientCapabilites> workspace;
 | 
					  lsWorkspaceClientCapabilites workspace;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Text document specific client capabilities.
 | 
					  // Text document specific client capabilities.
 | 
				
			||||||
  std::optional<lsTextDocumentClientCapabilities> textDocument;
 | 
					  lsTextDocumentClientCapabilities textDocument;
 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Experimental client capabilities.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  // experimental?: any; // TODO
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
MAKE_REFLECT_STRUCT(lsClientCapabilities, workspace, textDocument);
 | 
					MAKE_REFLECT_STRUCT(lsClientCapabilities, workspace, textDocument);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
/////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
/////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
////////////////////////////// INITIALIZATION ///////////////////////////////
 | 
					 | 
				
			||||||
/////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
/////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
/////////////////////////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct lsInitializeParams {
 | 
					struct lsInitializeParams {
 | 
				
			||||||
  // The process Id of the parent process that started
 | 
					  // The process Id of the parent process that started
 | 
				
			||||||
  // the server. Is null if the process has not been started by another process.
 | 
					  // the server. Is null if the process has not been started by another process.
 | 
				
			||||||
@ -524,8 +510,8 @@ struct Handler_Initialize : BaseMessageHandler<In_InitializeRequest> {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Client capabilities
 | 
					      // Client capabilities
 | 
				
			||||||
      if (request->params.capabilities.textDocument) {
 | 
					      {
 | 
				
			||||||
        const auto& cap = *request->params.capabilities.textDocument;
 | 
					        const auto& cap = request->params.capabilities.textDocument;
 | 
				
			||||||
        if (cap.completion && cap.completion->completionItem)
 | 
					        if (cap.completion && cap.completion->completionItem)
 | 
				
			||||||
          config->client.snippetSupport =
 | 
					          config->client.snippetSupport =
 | 
				
			||||||
              cap.completion->completionItem->snippetSupport.value_or(false);
 | 
					              cap.completion->completionItem->snippetSupport.value_or(false);
 | 
				
			||||||
 | 
				
			|||||||
@ -338,7 +338,10 @@ struct Handler_TextDocumentCompletion : MessageHandler {
 | 
				
			|||||||
              include_complete->completion_items_mutex, std::defer_lock);
 | 
					              include_complete->completion_items_mutex, std::defer_lock);
 | 
				
			||||||
          if (include_complete->is_scanning)
 | 
					          if (include_complete->is_scanning)
 | 
				
			||||||
            lock.lock();
 | 
					            lock.lock();
 | 
				
			||||||
          out.result.items = include_complete->completion_items;
 | 
					          std::string quote = result.match[5];
 | 
				
			||||||
 | 
					          for (auto& item : include_complete->completion_items)
 | 
				
			||||||
 | 
					            if (quote.empty() || quote == (item.use_angle_brackets_ ? "<" : "\""))
 | 
				
			||||||
 | 
					               out.result.items.push_back(item);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        FilterAndSortCompletionResponse(&out, result.pattern,
 | 
					        FilterAndSortCompletionResponse(&out, result.pattern,
 | 
				
			||||||
                                        config->completion.filterAndSort);
 | 
					                                        config->completion.filterAndSort);
 | 
				
			||||||
 | 
				
			|||||||
@ -1,56 +0,0 @@
 | 
				
			|||||||
#include "recorder.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <loguru.hpp>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <cassert>
 | 
					 | 
				
			||||||
#include <fstream>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace {
 | 
					 | 
				
			||||||
std::ofstream* g_file_in = nullptr;
 | 
					 | 
				
			||||||
std::ofstream* g_file_out = nullptr;
 | 
					 | 
				
			||||||
}  // namespace
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void EnableRecording(std::string path) {
 | 
					 | 
				
			||||||
  if (path.empty())
 | 
					 | 
				
			||||||
    path = "ccls";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // We can only call |EnableRecording| once.
 | 
					 | 
				
			||||||
  assert(!g_file_in && !g_file_out);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Open the files.
 | 
					 | 
				
			||||||
  g_file_in = new std::ofstream(
 | 
					 | 
				
			||||||
      path + ".in", std::ios::out | std::ios::trunc | std::ios::binary);
 | 
					 | 
				
			||||||
  g_file_out = new std::ofstream(
 | 
					 | 
				
			||||||
      path + ".out", std::ios::out | std::ios::trunc | std::ios::binary);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Make sure we can write to the files.
 | 
					 | 
				
			||||||
  bool bad = false;
 | 
					 | 
				
			||||||
  if (!g_file_in->good()) {
 | 
					 | 
				
			||||||
    LOG_S(ERROR) << "record: cannot write to " << path << ".in";
 | 
					 | 
				
			||||||
    bad = true;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  if (!g_file_out->good()) {
 | 
					 | 
				
			||||||
    LOG_S(ERROR) << "record: cannot write to " << path << ".out";
 | 
					 | 
				
			||||||
    bad = true;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  if (bad) {
 | 
					 | 
				
			||||||
    delete g_file_in;
 | 
					 | 
				
			||||||
    delete g_file_out;
 | 
					 | 
				
			||||||
    g_file_in = nullptr;
 | 
					 | 
				
			||||||
    g_file_out = nullptr;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void RecordInput(std::string_view content) {
 | 
					 | 
				
			||||||
  if (!g_file_in)
 | 
					 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
  (*g_file_in) << "Content-Length: " << content.size() << "\r\n\r\n" << content;
 | 
					 | 
				
			||||||
  (*g_file_in).flush();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void RecordOutput(std::string_view content) {
 | 
					 | 
				
			||||||
  if (!g_file_out)
 | 
					 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
  (*g_file_out) << content;
 | 
					 | 
				
			||||||
  (*g_file_out).flush();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,9 +0,0 @@
 | 
				
			|||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <string_view>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <string>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void EnableRecording(std::string path);
 | 
					 | 
				
			||||||
void RecordInput(std::string_view content);
 | 
					 | 
				
			||||||
void RecordOutput(std::string_view content);
 | 
					 | 
				
			||||||
@ -1,72 +0,0 @@
 | 
				
			|||||||
#include "semantic_highlight_symbol_cache.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
SemanticHighlightSymbolCache::Entry::Entry(
 | 
					 | 
				
			||||||
    SemanticHighlightSymbolCache* all_caches,
 | 
					 | 
				
			||||||
    const std::string& path)
 | 
					 | 
				
			||||||
    : all_caches_(all_caches), path(path) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::optional<int> SemanticHighlightSymbolCache::Entry::TryGetStableId(
 | 
					 | 
				
			||||||
    SymbolKind kind,
 | 
					 | 
				
			||||||
    const std::string& detailed_name) {
 | 
					 | 
				
			||||||
  TNameToId* map = GetMapForSymbol_(kind);
 | 
					 | 
				
			||||||
  auto it = map->find(detailed_name);
 | 
					 | 
				
			||||||
  if (it != map->end())
 | 
					 | 
				
			||||||
    return it->second;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return std::nullopt;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int SemanticHighlightSymbolCache::Entry::GetStableId(
 | 
					 | 
				
			||||||
    SymbolKind kind,
 | 
					 | 
				
			||||||
    const std::string& detailed_name) {
 | 
					 | 
				
			||||||
  std::optional<int> id = TryGetStableId(kind, detailed_name);
 | 
					 | 
				
			||||||
  if (id)
 | 
					 | 
				
			||||||
    return *id;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Create a new id. First try to find a key in another map.
 | 
					 | 
				
			||||||
  all_caches_->cache_.IterateValues([&](const std::shared_ptr<Entry>& entry) {
 | 
					 | 
				
			||||||
    std::optional<int> other_id = entry->TryGetStableId(kind, detailed_name);
 | 
					 | 
				
			||||||
    if (other_id) {
 | 
					 | 
				
			||||||
      id = other_id;
 | 
					 | 
				
			||||||
      return false;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Create a new id.
 | 
					 | 
				
			||||||
  TNameToId* map = GetMapForSymbol_(kind);
 | 
					 | 
				
			||||||
  if (!id)
 | 
					 | 
				
			||||||
    id = all_caches_->next_stable_id_++;
 | 
					 | 
				
			||||||
  return (*map)[detailed_name] = *id;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
SemanticHighlightSymbolCache::Entry::TNameToId*
 | 
					 | 
				
			||||||
SemanticHighlightSymbolCache::Entry::GetMapForSymbol_(SymbolKind kind) {
 | 
					 | 
				
			||||||
  switch (kind) {
 | 
					 | 
				
			||||||
    case SymbolKind::Type:
 | 
					 | 
				
			||||||
      return &detailed_type_name_to_stable_id;
 | 
					 | 
				
			||||||
    case SymbolKind::Func:
 | 
					 | 
				
			||||||
      return &detailed_func_name_to_stable_id;
 | 
					 | 
				
			||||||
    case SymbolKind::Var:
 | 
					 | 
				
			||||||
      return &detailed_var_name_to_stable_id;
 | 
					 | 
				
			||||||
    case SymbolKind::File:
 | 
					 | 
				
			||||||
    case SymbolKind::Invalid:
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  assert(false);
 | 
					 | 
				
			||||||
  return nullptr;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
SemanticHighlightSymbolCache::SemanticHighlightSymbolCache()
 | 
					 | 
				
			||||||
    : cache_(kCacheSize) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void SemanticHighlightSymbolCache::Init(Config* config) {
 | 
					 | 
				
			||||||
  match_ = std::make_unique<GroupMatch>(config->highlight.whitelist,
 | 
					 | 
				
			||||||
                                        config->highlight.blacklist);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::shared_ptr<SemanticHighlightSymbolCache::Entry>
 | 
					 | 
				
			||||||
SemanticHighlightSymbolCache::GetCacheForFile(const std::string& path) {
 | 
					 | 
				
			||||||
  return cache_.Get(
 | 
					 | 
				
			||||||
      path, [&, this]() { return std::make_shared<Entry>(this, path); });
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,43 +0,0 @@
 | 
				
			|||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "lru_cache.h"
 | 
					 | 
				
			||||||
#include "match.h"
 | 
					 | 
				
			||||||
#include "query.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <optional>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <string>
 | 
					 | 
				
			||||||
#include <unordered_map>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Caches symbols for a single file for semantic highlighting to provide
 | 
					 | 
				
			||||||
// relatively stable ids. Only supports xxx files at a time.
 | 
					 | 
				
			||||||
struct SemanticHighlightSymbolCache {
 | 
					 | 
				
			||||||
  struct Entry {
 | 
					 | 
				
			||||||
    SemanticHighlightSymbolCache* all_caches_ = nullptr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // The path this cache belongs to.
 | 
					 | 
				
			||||||
    std::string path;
 | 
					 | 
				
			||||||
    // Detailed symbol name to stable id.
 | 
					 | 
				
			||||||
    using TNameToId = std::unordered_map<std::string, int>;
 | 
					 | 
				
			||||||
    TNameToId detailed_type_name_to_stable_id;
 | 
					 | 
				
			||||||
    TNameToId detailed_func_name_to_stable_id;
 | 
					 | 
				
			||||||
    TNameToId detailed_var_name_to_stable_id;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Entry(SemanticHighlightSymbolCache* all_caches, const std::string& path);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    std::optional<int> TryGetStableId(SymbolKind kind,
 | 
					 | 
				
			||||||
                                 const std::string& detailed_name);
 | 
					 | 
				
			||||||
    int GetStableId(SymbolKind kind, const std::string& detailed_name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    TNameToId* GetMapForSymbol_(SymbolKind kind);
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  constexpr static int kCacheSize = 10;
 | 
					 | 
				
			||||||
  LruCache<std::string, Entry> cache_;
 | 
					 | 
				
			||||||
  uint32_t next_stable_id_ = 0;
 | 
					 | 
				
			||||||
  std::unique_ptr<GroupMatch> match_;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  SemanticHighlightSymbolCache();
 | 
					 | 
				
			||||||
  void Init(Config*);
 | 
					 | 
				
			||||||
  std::shared_ptr<Entry> GetCacheForFile(const std::string& path);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
							
								
								
									
										152
									
								
								src/task.cc
									
									
									
									
									
								
							
							
						
						
									
										152
									
								
								src/task.cc
									
									
									
									
									
								
							@ -1,152 +0,0 @@
 | 
				
			|||||||
#if false
 | 
					 | 
				
			||||||
#include "task.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "utils.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <doctest/doctest.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
TaskManager::TaskManager() {
 | 
					 | 
				
			||||||
  pending_tasks_[TaskThread::Indexer] = std::make_unique<TaskQueue>();
 | 
					 | 
				
			||||||
  pending_tasks_[TaskThread::QueryDb] = std::make_unique<TaskQueue>();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void TaskManager::Post(TaskThread thread, const TTask& task) {
 | 
					 | 
				
			||||||
  TaskQueue* queue = pending_tasks_[thread].get();
 | 
					 | 
				
			||||||
  std::lock_guard<std::mutex> lock_guard(queue->tasks_mutex);
 | 
					 | 
				
			||||||
  queue->tasks.push_back(task);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void TaskManager::SetIdle(TaskThread thread, const TIdleTask& task) {
 | 
					 | 
				
			||||||
  TaskQueue* queue = pending_tasks_[thread].get();
 | 
					 | 
				
			||||||
  std::lock_guard<std::mutex> lock_guard(queue->tasks_mutex);
 | 
					 | 
				
			||||||
  assert(!queue->idle_task && "There is already an idle task");
 | 
					 | 
				
			||||||
  queue->idle_task = task;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool TaskManager::RunTasks(TaskThread thread, std::optional<std::chrono::duration<long long, std::nano>> max_time) {
 | 
					 | 
				
			||||||
  auto start = std::chrono::high_resolution_clock::now();
 | 
					 | 
				
			||||||
  TaskQueue* queue = pending_tasks_[thread].get();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  bool ran_task = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  while (true) {
 | 
					 | 
				
			||||||
    std::optional<TTask> task;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Get a task.
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      std::lock_guard<std::mutex> lock_guard(queue->tasks_mutex);
 | 
					 | 
				
			||||||
      if (queue->tasks.empty())
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
      task = std::move(queue->tasks[queue->tasks.size() - 1]);
 | 
					 | 
				
			||||||
      queue->tasks.pop_back();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    // Execute task.
 | 
					 | 
				
			||||||
    assert(task);
 | 
					 | 
				
			||||||
    (*task)();
 | 
					 | 
				
			||||||
    ran_task = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Stop if we've run past our max time. Don't run idle_task.
 | 
					 | 
				
			||||||
    auto elapsed = std::chrono::high_resolution_clock::now() - start;
 | 
					 | 
				
			||||||
    if (max_time && elapsed > *max_time)
 | 
					 | 
				
			||||||
      return ran_task;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (queue->idle_task) {
 | 
					 | 
				
			||||||
    // Even if the idle task returns false we still ran something before.
 | 
					 | 
				
			||||||
    ran_task = (*queue->idle_task)() || ran_task;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return ran_task;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
TEST_SUITE("Task");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
TEST_CASE("tasks are run as soon as they are posted") {
 | 
					 | 
				
			||||||
  TaskManager tm;
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
  // Post three tasks.
 | 
					 | 
				
			||||||
  int next = 1;
 | 
					 | 
				
			||||||
  int a = 0, b = 0, c = 0;
 | 
					 | 
				
			||||||
  tm.Post(TaskThread::QueryDb, [&] {
 | 
					 | 
				
			||||||
    a = next++;
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
  tm.Post(TaskThread::QueryDb, [&] {
 | 
					 | 
				
			||||||
    b = next++;
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
  tm.Post(TaskThread::QueryDb, [&] {
 | 
					 | 
				
			||||||
    c = next++;
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Execute all tasks.
 | 
					 | 
				
			||||||
  tm.RunTasks(TaskThread::QueryDb, std::nullopt);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Tasks are executed in reverse order.
 | 
					 | 
				
			||||||
  REQUIRE(a == 3);
 | 
					 | 
				
			||||||
  REQUIRE(b == 2);
 | 
					 | 
				
			||||||
  REQUIRE(c == 1);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
TEST_CASE("post from inside task manager") {
 | 
					 | 
				
			||||||
  TaskManager tm;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Post three tasks.
 | 
					 | 
				
			||||||
  int next = 1;
 | 
					 | 
				
			||||||
  int a = 0, b = 0, c = 0;
 | 
					 | 
				
			||||||
  tm.Post(TaskThread::QueryDb, [&] () {
 | 
					 | 
				
			||||||
    a = next++;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    tm.Post(TaskThread::QueryDb, [&] {
 | 
					 | 
				
			||||||
      b = next++;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      tm.Post(TaskThread::QueryDb, [&] {
 | 
					 | 
				
			||||||
        c = next++;
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Execute all tasks.
 | 
					 | 
				
			||||||
  tm.RunTasks(TaskThread::QueryDb, std::nullopt);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Tasks are executed in normal order because the next task is not posted
 | 
					 | 
				
			||||||
  // until the previous one is executed.
 | 
					 | 
				
			||||||
  REQUIRE(a == 1);
 | 
					 | 
				
			||||||
  REQUIRE(b == 2);
 | 
					 | 
				
			||||||
  REQUIRE(c == 3);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
TEST_CASE("idle task is run after nested tasks") {
 | 
					 | 
				
			||||||
  TaskManager tm;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  int count = 0;
 | 
					 | 
				
			||||||
  tm.SetIdle(TaskThread::QueryDb, [&]() {
 | 
					 | 
				
			||||||
    ++count;
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // No tasks posted - idle runs once.
 | 
					 | 
				
			||||||
  REQUIRE(tm.RunTasks(TaskThread::QueryDb, std::nullopt));
 | 
					 | 
				
			||||||
  REQUIRE(count == 1);
 | 
					 | 
				
			||||||
  count = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Idle runs after other posted tasks.
 | 
					 | 
				
			||||||
  bool did_run = false;
 | 
					 | 
				
			||||||
  tm.Post(TaskThread::QueryDb, [&]() {
 | 
					 | 
				
			||||||
    did_run = true;
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
  REQUIRE(tm.RunTasks(TaskThread::QueryDb, std::nullopt));
 | 
					 | 
				
			||||||
  REQUIRE(did_run);
 | 
					 | 
				
			||||||
  REQUIRE(count == 1);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
TEST_CASE("RunTasks returns false when idle task returns false and no other tasks were run") {
 | 
					 | 
				
			||||||
  TaskManager tm;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  REQUIRE(tm.RunTasks(TaskThread::QueryDb, std::nullopt) == false);
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
  tm.SetIdle(TaskThread::QueryDb, []() { return false; });
 | 
					 | 
				
			||||||
  REQUIRE(tm.RunTasks(TaskThread::QueryDb, std::nullopt) == false);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
TEST_SUITE_END();
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
							
								
								
									
										41
									
								
								src/task.h
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								src/task.h
									
									
									
									
									
								
							@ -1,41 +0,0 @@
 | 
				
			|||||||
#if false
 | 
					 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <optional>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <chrono>
 | 
					 | 
				
			||||||
#include <functional>
 | 
					 | 
				
			||||||
#include <mutex>
 | 
					 | 
				
			||||||
#include <unordered_map>
 | 
					 | 
				
			||||||
#include <vector>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
enum class TaskThread {
 | 
					 | 
				
			||||||
  Indexer,
 | 
					 | 
				
			||||||
  QueryDb,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct TaskManager {
 | 
					 | 
				
			||||||
  using TTask = std::function<void()>;
 | 
					 | 
				
			||||||
  using TIdleTask = std::function<bool()>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  TaskManager();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Run |task| at some point in the future. This will run the task as soon as possible.
 | 
					 | 
				
			||||||
  void Post(TaskThread thread, const TTask& task);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Run |task| whenever there is nothing else to run.
 | 
					 | 
				
			||||||
  void SetIdle(TaskThread thread, const TIdleTask& idle_task);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Run pending tasks for |thread|. Stop running tasks after |max_time| has
 | 
					 | 
				
			||||||
  // elapsed. Returns true if tasks were run.
 | 
					 | 
				
			||||||
  bool RunTasks(TaskThread thread, std::optional<std::chrono::duration<long long, std::nano>> max_time);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  struct TaskQueue {
 | 
					 | 
				
			||||||
    std::optional<TIdleTask> idle_task;
 | 
					 | 
				
			||||||
    std::vector<TTask> tasks;
 | 
					 | 
				
			||||||
    std::mutex tasks_mutex;
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  std::unordered_map<TaskThread, std::unique_ptr<TaskQueue>> pending_tasks_;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
							
								
								
									
										424
									
								
								wscript
									
									
									
									
									
								
							
							
						
						
									
										424
									
								
								wscript
									
									
									
									
									
								
							@ -1,424 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python
 | 
					 | 
				
			||||||
# encoding: utf-8
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
try:
 | 
					 | 
				
			||||||
    from urllib2 import urlopen  # Python 2
 | 
					 | 
				
			||||||
except ImportError:
 | 
					 | 
				
			||||||
    from urllib.request import urlopen  # Python 3
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import os.path
 | 
					 | 
				
			||||||
import string
 | 
					 | 
				
			||||||
import subprocess
 | 
					 | 
				
			||||||
import sys
 | 
					 | 
				
			||||||
import re
 | 
					 | 
				
			||||||
import ctypes
 | 
					 | 
				
			||||||
import shutil
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
VERSION = '0.0.1'
 | 
					 | 
				
			||||||
APPNAME = 'cquery'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
top = '.'
 | 
					 | 
				
			||||||
out = 'build'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
CLANG_TARBALL_NAME = None
 | 
					 | 
				
			||||||
CLANG_TARBALL_EXT = '.tar.xz'
 | 
					 | 
				
			||||||
if sys.platform == 'darwin':
 | 
					 | 
				
			||||||
  CLANG_TARBALL_NAME = 'clang+llvm-$version-x86_64-apple-darwin'
 | 
					 | 
				
			||||||
elif sys.platform.startswith('freebsd'):
 | 
					 | 
				
			||||||
  CLANG_TARBALL_NAME = 'clang+llvm-$version-amd64-unknown-freebsd-10'
 | 
					 | 
				
			||||||
# It is either 'linux2' or 'linux3' before Python 3.3
 | 
					 | 
				
			||||||
elif sys.platform.startswith('linux'):
 | 
					 | 
				
			||||||
  # These executable depend on libtinfo.so.5
 | 
					 | 
				
			||||||
  CLANG_TARBALL_NAME = 'clang+llvm-$version-x86_64-linux-gnu-ubuntu-14.04'
 | 
					 | 
				
			||||||
elif sys.platform == 'win32':
 | 
					 | 
				
			||||||
  CLANG_TARBALL_NAME = 'LLVM-$version-win64'
 | 
					 | 
				
			||||||
  CLANG_TARBALL_EXT = '.exe'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from waflib.Tools.compiler_cxx import cxx_compiler
 | 
					 | 
				
			||||||
cxx_compiler['linux'] = ['clang++', 'g++']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Creating symbolic link on Windows requires a special priviledge SeCreateSymboliclinkPrivilege,
 | 
					 | 
				
			||||||
# which an non-elevated process lacks. Starting with Windows 10 build 14972, this got relaxed
 | 
					 | 
				
			||||||
# when Developer Mode is enabled. Triggering this new behaviour requires a new flag.
 | 
					 | 
				
			||||||
SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Python 2 has no symlink function and Python 3's symlink function does not allow creation of
 | 
					 | 
				
			||||||
# symlinks without admin rights (even when Developer Mode is enabled) so we define our own
 | 
					 | 
				
			||||||
# symlink function when on Windows.
 | 
					 | 
				
			||||||
if sys.platform == 'win32':
 | 
					 | 
				
			||||||
  kdll = ctypes.windll.kernel32
 | 
					 | 
				
			||||||
  def symlink(source, link_name, target_is_directory=False):
 | 
					 | 
				
			||||||
    # SYMBOLIC_LINK_FLAG_DIRECTORY: 0x1
 | 
					 | 
				
			||||||
    flags = int(target_is_directory) | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Use unicode version (W suffix) of Windows symbolic link function and convert strings to
 | 
					 | 
				
			||||||
    # unicode if Python 2 is used (strings are unicode by default in Python 3).
 | 
					 | 
				
			||||||
    if sys.version_info < (3, 0):
 | 
					 | 
				
			||||||
      ret = kdll.CreateSymbolicLinkW(unicode(link_name), unicode(source), flags)
 | 
					 | 
				
			||||||
    else:
 | 
					 | 
				
			||||||
      ret = kdll.CreateSymbolicLinkW(link_name, source, flags)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if ret == 0:
 | 
					 | 
				
			||||||
      err = ctypes.WinError()
 | 
					 | 
				
			||||||
      raise err
 | 
					 | 
				
			||||||
  os.symlink = symlink
 | 
					 | 
				
			||||||
elif sys.version_info.major < 3:
 | 
					 | 
				
			||||||
  os_symlink_bak = os.symlink
 | 
					 | 
				
			||||||
  os.symlink = lambda src, dst, target_is_directory=False: os_symlink_bak(src, dst)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# There is a null pointer dereference issue in tools/libclang/CXIndexDataConsumer.cpp handleReference.
 | 
					 | 
				
			||||||
def patch_byte_in_libclang(filename, offset, old, new):
 | 
					 | 
				
			||||||
  with open(filename, 'rb+') as f:
 | 
					 | 
				
			||||||
    f.seek(offset)
 | 
					 | 
				
			||||||
    t = f.read(1)
 | 
					 | 
				
			||||||
    if t == old:
 | 
					 | 
				
			||||||
      f.seek(offset)
 | 
					 | 
				
			||||||
      f.write(new)
 | 
					 | 
				
			||||||
      print('Applied byte patch hack at 0x{:x}'.format(offset))
 | 
					 | 
				
			||||||
      print('This is a makeshift for indexing default arguments of template template parameters, which otherwise would crash libclang')
 | 
					 | 
				
			||||||
      print('See https://github.com/jacobdufault/cquery/issues/219 for details')
 | 
					 | 
				
			||||||
    else:
 | 
					 | 
				
			||||||
      assert t == new
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def options(opt):
 | 
					 | 
				
			||||||
  opt.load('compiler_cxx')
 | 
					 | 
				
			||||||
  grp = opt.add_option_group('Configuration options related to use of clang from the system (not recommended)')
 | 
					 | 
				
			||||||
  grp.add_option('--enable-assert', action='store_true')
 | 
					 | 
				
			||||||
  grp.add_option('--use-clang-cxx', dest='use_clang_cxx', default=False, action='store_true',
 | 
					 | 
				
			||||||
                 help='use clang C++ API')
 | 
					 | 
				
			||||||
  grp.add_option('--bundled-clang', dest='bundled_clang', default='6.0.0',
 | 
					 | 
				
			||||||
                 help='bundled clang version, downloaded from https://releases.llvm.org/ , e.g. 5.0.1 6.0.0')
 | 
					 | 
				
			||||||
  grp.add_option('--llvm-config', dest='llvm_config',
 | 
					 | 
				
			||||||
                 help='path to llvm-config to use system libclang, e.g. llvm-config llvm-config-6.0')
 | 
					 | 
				
			||||||
  grp.add_option('--clang-prefix', dest='clang_prefix',
 | 
					 | 
				
			||||||
                 help='enable fallback configuration method by specifying a clang installation prefix (e.g. /opt/llvm)')
 | 
					 | 
				
			||||||
  grp.add_option('--variant', default='release',
 | 
					 | 
				
			||||||
                 help='variant name for saving configuration and build results. Variants other than "debug" turn on -O3')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def download_and_extract(destdir, url, ext):
 | 
					 | 
				
			||||||
  dest = destdir + ext
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  # Extract the tarball.
 | 
					 | 
				
			||||||
  if not os.path.isdir(os.path.join(destdir, 'include')):
 | 
					 | 
				
			||||||
    # Download and save the compressed tarball as |compressed_file_name|.
 | 
					 | 
				
			||||||
    if not os.path.isfile(dest):
 | 
					 | 
				
			||||||
      print('Downloading tarball')
 | 
					 | 
				
			||||||
      print('   destination: {0}'.format(dest))
 | 
					 | 
				
			||||||
      print('   source:      {0}'.format(url))
 | 
					 | 
				
			||||||
      # TODO: verify checksum
 | 
					 | 
				
			||||||
      response = urlopen(url, timeout=60)
 | 
					 | 
				
			||||||
      with open(dest, 'wb') as f:
 | 
					 | 
				
			||||||
        f.write(response.read())
 | 
					 | 
				
			||||||
    else:
 | 
					 | 
				
			||||||
      print('Found tarball at {0}'.format(dest))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    print('Extracting {0}'.format(dest))
 | 
					 | 
				
			||||||
    # TODO: make portable.
 | 
					 | 
				
			||||||
    if ext == '.exe':
 | 
					 | 
				
			||||||
      subprocess.call(['7z', 'x', '-o{0}'.format(destdir), '-xr!$PLUGINSDIR', dest])
 | 
					 | 
				
			||||||
    else:
 | 
					 | 
				
			||||||
      subprocess.call(['tar', '-x', '-C', out, '-f', dest])
 | 
					 | 
				
			||||||
      # TODO Remove after migrating to a clang release newer than 5.0.1
 | 
					 | 
				
			||||||
      # For 5.0.1 Mac OS X, the directory and the tarball have different name
 | 
					 | 
				
			||||||
      if destdir == 'build/clang+llvm-5.0.1-x86_64-apple-darwin':
 | 
					 | 
				
			||||||
        os.rename(destdir.replace('5.0.1', '5.0.1-final'), destdir)
 | 
					 | 
				
			||||||
      # TODO Byte patch hack for other prebuilt binaries
 | 
					 | 
				
			||||||
      elif destdir == 'build/clang+llvm-4.0.0-x86_64-linux-gnu-ubuntu-14.04':
 | 
					 | 
				
			||||||
        patch_byte_in_libclang(os.path.join(destdir, 'lib/libclang.so.4.0'),
 | 
					 | 
				
			||||||
            0x4172b5, b'\x48', b'\x4d')
 | 
					 | 
				
			||||||
      elif destdir == 'build/clang+llvm-5.0.1-x86_64-linux-gnu-ubuntu-14.04':
 | 
					 | 
				
			||||||
        patch_byte_in_libclang(os.path.join(destdir, 'lib/libclang.so.5.0'),
 | 
					 | 
				
			||||||
            0x47aece, b'\x48', b'\x4d')
 | 
					 | 
				
			||||||
  else:
 | 
					 | 
				
			||||||
    print('Found extracted at {0}'.format(destdir))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def copy_or_symlink(src, dst):
 | 
					 | 
				
			||||||
  print('copy_or_symlink src={0}, dst={1}'.format(src, dst))
 | 
					 | 
				
			||||||
  try:
 | 
					 | 
				
			||||||
    os.makedirs(os.path.dirname(dst))
 | 
					 | 
				
			||||||
  except OSError:
 | 
					 | 
				
			||||||
    pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  try:
 | 
					 | 
				
			||||||
    os.symlink(src, dst)
 | 
					 | 
				
			||||||
  except (OSError, NotImplementedError):
 | 
					 | 
				
			||||||
    shutil.copy(src, dst)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def configure(ctx):
 | 
					 | 
				
			||||||
  ctx.resetenv(ctx.options.variant)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ctx.load('compiler_cxx')
 | 
					 | 
				
			||||||
  if ctx.env.CXX_NAME == 'msvc':
 | 
					 | 
				
			||||||
    # /Zi: -g, /WX: -Werror, /W3: roughly -Wall, there is no -std=c++11 equivalent in MSVC.
 | 
					 | 
				
			||||||
    # /wd4722: ignores warning C4722 (destructor never returns) in loguru
 | 
					 | 
				
			||||||
    # /wd4267: ignores warning C4267 (conversion from 'size_t' to 'type'), roughly -Wno-sign-compare
 | 
					 | 
				
			||||||
    # /MD: use multithread c library from DLL
 | 
					 | 
				
			||||||
    cxxflags = ['/nologo', '/FS', '/EHsc', '/Zi', '/W3', '/wd4996', '/wd4722', '/wd4267', '/wd4800', '/MD']
 | 
					 | 
				
			||||||
    ldflags = []
 | 
					 | 
				
			||||||
    if 'release' in ctx.options.variant:
 | 
					 | 
				
			||||||
      cxxflags.append('/O2') # There is no O3
 | 
					 | 
				
			||||||
    else:
 | 
					 | 
				
			||||||
      cxxflags += ['/Zi', '/FS']
 | 
					 | 
				
			||||||
      ldflags += ['/DEBUG']
 | 
					 | 
				
			||||||
  else:
 | 
					 | 
				
			||||||
    # So that dladdr() called in loguru.hpp gives symbol names in main executable
 | 
					 | 
				
			||||||
    ldflags = ['-rdynamic']
 | 
					 | 
				
			||||||
    if ctx.env.CXXFLAGS:
 | 
					 | 
				
			||||||
      cxxflags = ctx.env.CXXFLAGS
 | 
					 | 
				
			||||||
    else:
 | 
					 | 
				
			||||||
      cxxflags = ['-g', '-Wall', '-Wno-sign-compare']
 | 
					 | 
				
			||||||
      if ctx.env.CXX_NAME == 'gcc':
 | 
					 | 
				
			||||||
        cxxflags.append('-Wno-return-type')
 | 
					 | 
				
			||||||
        # Otherwise (void)write(...) => -Werror=unused-result
 | 
					 | 
				
			||||||
        cxxflags.append('-Wno-unused-result')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if all(not x.startswith('-std=') for x in ctx.env.CXXFLAGS):
 | 
					 | 
				
			||||||
      cxxflags.append('-std=c++14')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if ctx.options.use_clang_cxx:
 | 
					 | 
				
			||||||
      # include/clang/Format/Format.h error: multi-line comment
 | 
					 | 
				
			||||||
      cxxflags.append('-Wno-comment')
 | 
					 | 
				
			||||||
      # Without -fno-rtti, some Clang C++ functions may report `undefined references to typeinfo`
 | 
					 | 
				
			||||||
      cxxflags.append('-fno-rtti')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if 'asan' in ctx.options.variant:
 | 
					 | 
				
			||||||
      cxxflags.append('-fsanitize=address,undefined')
 | 
					 | 
				
			||||||
      ldflags.append('-fsanitize=address,undefined')
 | 
					 | 
				
			||||||
    if 'release' in ctx.options.variant:
 | 
					 | 
				
			||||||
      cxxflags.append('-O' if 'asan' in ctx.options.variant else '-O3')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if ctx.options.enable_assert is None:
 | 
					 | 
				
			||||||
    if 'debug' in ctx.options.variant:
 | 
					 | 
				
			||||||
      ctx.options.enable_assert = True
 | 
					 | 
				
			||||||
  if not ctx.options.enable_assert:
 | 
					 | 
				
			||||||
    ctx.define('NDEBUG', 1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if ctx.env.CXX_NAME == 'clang' and 'debug' in ctx.options.variant:
 | 
					 | 
				
			||||||
    cxxflags.append('-fno-limit-debug-info')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ctx.env.CXXFLAGS = cxxflags
 | 
					 | 
				
			||||||
  if not ctx.env.LDFLAGS:
 | 
					 | 
				
			||||||
    ctx.env.LDFLAGS = ldflags
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ctx.check(header_name='stdio.h', features='cxx cxxprogram', mandatory=True)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ctx.load('clang_compilation_database', tooldir='.')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ctx.env['use_clang_cxx'] = ctx.options.use_clang_cxx
 | 
					 | 
				
			||||||
  ctx.env['llvm_config'] = ctx.options.llvm_config
 | 
					 | 
				
			||||||
  ctx.env['bundled_clang'] = ctx.options.bundled_clang
 | 
					 | 
				
			||||||
  def libname(lib):
 | 
					 | 
				
			||||||
    # Newer MinGW and MSVC both wants full file name
 | 
					 | 
				
			||||||
    if sys.platform == 'win32':
 | 
					 | 
				
			||||||
      return 'lib' + lib
 | 
					 | 
				
			||||||
    return lib
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  # Do not use bundled clang+llvm
 | 
					 | 
				
			||||||
  if ctx.options.llvm_config is not None or ctx.options.clang_prefix is not None:
 | 
					 | 
				
			||||||
    if ctx.options.llvm_config is not None:
 | 
					 | 
				
			||||||
      # Ask llvm-config for cflags and ldflags
 | 
					 | 
				
			||||||
      ctx.find_program(ctx.options.llvm_config, msg='checking for llvm-config', var='LLVM_CONFIG', mandatory=False)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      ctx.env.rpath = [str(subprocess.check_output(
 | 
					 | 
				
			||||||
        [ctx.options.llvm_config, '--libdir'],
 | 
					 | 
				
			||||||
        stderr=subprocess.STDOUT).decode()).strip()]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if ctx.options.clang_prefix:
 | 
					 | 
				
			||||||
      ctx.start_msg('Checking for clang prefix')
 | 
					 | 
				
			||||||
      prefix = ctx.root.find_node(ctx.options.clang_prefix)
 | 
					 | 
				
			||||||
      if not prefix:
 | 
					 | 
				
			||||||
        raise ctx.errors.ConfigurationError('clang prefix not found: "%s"'%ctx.options.clang_prefix)
 | 
					 | 
				
			||||||
      ctx.end_msg(prefix)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      includes = [ n.abspath() for n in [ prefix.find_node('include') ] if n ]
 | 
					 | 
				
			||||||
      libpath  = [ n.abspath() for n in [ prefix.find_node(l) for l in ('lib', 'lib64')] if n ]
 | 
					 | 
				
			||||||
      ctx.check_cxx(msg='Checking for library clang', lib=libname('clang'), uselib_store='clang', includes=includes, libpath=libpath)
 | 
					 | 
				
			||||||
    else:
 | 
					 | 
				
			||||||
      ctx.check_cfg(msg='Checking for clang flags',
 | 
					 | 
				
			||||||
                    path=ctx.env.LLVM_CONFIG,
 | 
					 | 
				
			||||||
                    package='',
 | 
					 | 
				
			||||||
                    uselib_store='clang',
 | 
					 | 
				
			||||||
                    args='--cppflags --ldflags')
 | 
					 | 
				
			||||||
      # llvm-config does not provide the actual library we want so we check for it
 | 
					 | 
				
			||||||
      # using the provided info so far.
 | 
					 | 
				
			||||||
      ctx.check_cxx(lib=libname('clang'), uselib_store='clang', use='clang')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Use CXX set by --check-cxx-compiler if it is "clang".
 | 
					 | 
				
			||||||
    # See https://github.com/jacobdufault/cquery/issues/237
 | 
					 | 
				
			||||||
    clang = ctx.env.get_flat('CXX')
 | 
					 | 
				
			||||||
    if 'clang' not in clang:
 | 
					 | 
				
			||||||
      # Otherwise, infer the clang executable path with llvm-config --bindir
 | 
					 | 
				
			||||||
      output = str(subprocess.check_output(
 | 
					 | 
				
			||||||
          [ctx.options.llvm_config, '--bindir'],
 | 
					 | 
				
			||||||
          stderr=subprocess.STDOUT).decode()).strip()
 | 
					 | 
				
			||||||
      clang = os.path.join(output, 'clang')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Use the detected clang executable to infer resource directory
 | 
					 | 
				
			||||||
    # Use `clang -### -xc /dev/null` instead of `clang -print-resource-dir` because the option is unavailable in 4.0.0
 | 
					 | 
				
			||||||
    devnull = '/dev/null' if sys.platform != 'win32' else 'NUL'
 | 
					 | 
				
			||||||
    output = str(subprocess.check_output(
 | 
					 | 
				
			||||||
        [clang, '-###', '-xc', devnull],
 | 
					 | 
				
			||||||
        stderr=subprocess.STDOUT).decode())
 | 
					 | 
				
			||||||
    match = re.search(r'"-resource-dir" "([^"]*)"', output, re.M)
 | 
					 | 
				
			||||||
    if match:
 | 
					 | 
				
			||||||
      ctx.env.default_resource_directory = match.group(1)
 | 
					 | 
				
			||||||
    else:
 | 
					 | 
				
			||||||
      bld.fatal("Failed to found system clang resource directory.")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  else:
 | 
					 | 
				
			||||||
    global CLANG_TARBALL_NAME, CLANG_TARBALL_EXT
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if CLANG_TARBALL_NAME is None:
 | 
					 | 
				
			||||||
      sys.stderr.write('ERROR: releases.llvm.org does not provide prebuilt binary for your platform {0}\n'.format(sys.platform))
 | 
					 | 
				
			||||||
      sys.exit(1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # TODO Remove after 5.0.1 is stable
 | 
					 | 
				
			||||||
    if ctx.options.bundled_clang == '5.0.0' and sys.platform.startswith('linux'):
 | 
					 | 
				
			||||||
      CLANG_TARBALL_NAME = 'clang+llvm-$version-linux-x86_64-ubuntu14.04'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    CLANG_TARBALL_NAME = string.Template(CLANG_TARBALL_NAME).substitute(version=ctx.options.bundled_clang)
 | 
					 | 
				
			||||||
    # Directory clang has been extracted to.
 | 
					 | 
				
			||||||
    CLANG_DIRECTORY = '{0}/{1}'.format(out, CLANG_TARBALL_NAME)
 | 
					 | 
				
			||||||
    # URL of the tarball to download.
 | 
					 | 
				
			||||||
    CLANG_TARBALL_URL = 'http://releases.llvm.org/{0}/{1}{2}'.format(ctx.options.bundled_clang, CLANG_TARBALL_NAME, CLANG_TARBALL_EXT)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    print('Checking for clang')
 | 
					 | 
				
			||||||
    download_and_extract(CLANG_DIRECTORY, CLANG_TARBALL_URL, CLANG_TARBALL_EXT)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    bundled_clang_dir = os.path.join(out, ctx.options.variant, 'lib', CLANG_TARBALL_NAME)
 | 
					 | 
				
			||||||
    try:
 | 
					 | 
				
			||||||
      os.makedirs(os.path.dirname(bundled_clang_dir))
 | 
					 | 
				
			||||||
    except OSError:
 | 
					 | 
				
			||||||
      pass
 | 
					 | 
				
			||||||
    clang_dir = os.path.normpath('../../' + CLANG_TARBALL_NAME)
 | 
					 | 
				
			||||||
    try:
 | 
					 | 
				
			||||||
      if not os.path.exists(bundled_clang_dir):
 | 
					 | 
				
			||||||
        os.symlink(clang_dir, bundled_clang_dir, target_is_directory=True)
 | 
					 | 
				
			||||||
    except (OSError, NotImplementedError):
 | 
					 | 
				
			||||||
      # Copying the whole directory instead.
 | 
					 | 
				
			||||||
      print ('shutil.copytree({0}, {1})'.format(os.path.join(out, CLANG_TARBALL_NAME), bundled_clang_dir))
 | 
					 | 
				
			||||||
      try:
 | 
					 | 
				
			||||||
        shutil.copytree(os.path.join(out, CLANG_TARBALL_NAME), bundled_clang_dir)
 | 
					 | 
				
			||||||
      except Exception as e:
 | 
					 | 
				
			||||||
        print('Failed to copy tree, ', e)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    clang_node = ctx.path.find_dir(bundled_clang_dir)
 | 
					 | 
				
			||||||
    ctx.check_cxx(uselib_store='clang',
 | 
					 | 
				
			||||||
                  includes=clang_node.find_dir('include').abspath(),
 | 
					 | 
				
			||||||
                  libpath=clang_node.find_dir('lib').abspath(),
 | 
					 | 
				
			||||||
                  lib=libname('clang'))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    clang_tarball_name = os.path.basename(os.path.dirname(ctx.env['LIBPATH_clang'][0]))
 | 
					 | 
				
			||||||
    ctx.env.clang_tarball_name = clang_tarball_name
 | 
					 | 
				
			||||||
    ctx.env.default_resource_directory = '../lib/{}/lib/clang/{}'.format(clang_tarball_name, ctx.env.bundled_clang)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if sys.platform.startswith('freebsd') or sys.platform.startswith('linux'):
 | 
					 | 
				
			||||||
      ctx.env.rpath = ['$ORIGIN/../lib/' + clang_tarball_name + '/lib']
 | 
					 | 
				
			||||||
    elif sys.platform == 'darwin':
 | 
					 | 
				
			||||||
      ctx.env.rpath = ['@loader_path/../lib/' + clang_tarball_name + '/lib']
 | 
					 | 
				
			||||||
    elif sys.platform == 'win32':
 | 
					 | 
				
			||||||
      # Poor Windows users' RPATH - copy libclang.lib and libclang.dll to the build directory.
 | 
					 | 
				
			||||||
      ctx.env.rpath = [] # Unsupported
 | 
					 | 
				
			||||||
      clang_dir = os.path.dirname(ctx.env['LIBPATH_clang'][0])
 | 
					 | 
				
			||||||
      dest_dir = os.path.join(ctx.path.get_bld().abspath(), ctx.options.variant, 'bin')
 | 
					 | 
				
			||||||
      # copy_or_symlink(os.path.join(clang_dir, 'lib', 'libclang.lib'), os.path.join(dest_dir, 'libclang.lib'))
 | 
					 | 
				
			||||||
      copy_or_symlink(os.path.join(clang_dir, 'bin', 'libclang.dll'), os.path.join(dest_dir, 'libclang.dll'))
 | 
					 | 
				
			||||||
    else:
 | 
					 | 
				
			||||||
      ctx.env.rpath = ctx.env['LIBPATH_clang']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ctx.msg('Clang includes', ctx.env.INCLUDES_clang)
 | 
					 | 
				
			||||||
  ctx.msg('Clang library dir', ctx.env.LIBPATH_clang)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def build(bld):
 | 
					 | 
				
			||||||
  cc_files = bld.path.ant_glob(['src/*.cc', 'src/messages/*.cc', 'third_party/*.cc'])
 | 
					 | 
				
			||||||
  if bld.env['use_clang_cxx']:
 | 
					 | 
				
			||||||
    cc_files += bld.path.ant_glob(['src/clang_cxx/*.cc'])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  lib = []
 | 
					 | 
				
			||||||
  if sys.platform.startswith('linux'):
 | 
					 | 
				
			||||||
    # For __atomic_* when lock free instructions are unavailable
 | 
					 | 
				
			||||||
    # (either through hardware or OS support)
 | 
					 | 
				
			||||||
    lib.append('pthread')
 | 
					 | 
				
			||||||
    # loguru calls dladdr
 | 
					 | 
				
			||||||
    lib.append('dl')
 | 
					 | 
				
			||||||
  elif sys.platform.startswith('freebsd'):
 | 
					 | 
				
			||||||
    # loguru::stacktrace_as_stdstring calls backtrace_symbols
 | 
					 | 
				
			||||||
    lib.append('execinfo')
 | 
					 | 
				
			||||||
    # sparsepp/spp_memory.h uses libkvm
 | 
					 | 
				
			||||||
    lib.append('kvm')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    lib.append('pthread')
 | 
					 | 
				
			||||||
    lib.append('thr')
 | 
					 | 
				
			||||||
  elif sys.platform == 'darwin':
 | 
					 | 
				
			||||||
    lib.append('pthread')
 | 
					 | 
				
			||||||
  elif sys.platform == 'msys':
 | 
					 | 
				
			||||||
    lib.append('psapi') # GetProcessMemoryInfo
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if bld.env['use_clang_cxx']:
 | 
					 | 
				
			||||||
    # -fno-rtti is required for object files using clang/llvm C++ API
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # The order is derived by topological sorting LINK_LIBS in clang/lib/*/CMakeLists.txt
 | 
					 | 
				
			||||||
    lib.append('clangFormat')
 | 
					 | 
				
			||||||
    lib.append('clangToolingCore')
 | 
					 | 
				
			||||||
    lib.append('clangRewrite')
 | 
					 | 
				
			||||||
    lib.append('clangAST')
 | 
					 | 
				
			||||||
    lib.append('clangLex')
 | 
					 | 
				
			||||||
    lib.append('clangBasic')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # The order is derived from llvm-config --libs core
 | 
					 | 
				
			||||||
    lib.append('LLVMCore')
 | 
					 | 
				
			||||||
    lib.append('LLVMBinaryFormat')
 | 
					 | 
				
			||||||
    lib.append('LLVMSupport')
 | 
					 | 
				
			||||||
    lib.append('LLVMDemangle')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    lib.append('ncurses')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  # https://waf.io/apidocs/tools/c_aliases.html#waflib.Tools.c_aliases.program
 | 
					 | 
				
			||||||
  bld.program(
 | 
					 | 
				
			||||||
      source=cc_files,
 | 
					 | 
				
			||||||
      use=['clang'],
 | 
					 | 
				
			||||||
      includes=[
 | 
					 | 
				
			||||||
        'src/',
 | 
					 | 
				
			||||||
        'third_party/',
 | 
					 | 
				
			||||||
        'third_party/doctest/',
 | 
					 | 
				
			||||||
        'third_party/loguru/',
 | 
					 | 
				
			||||||
        'third_party/msgpack-c/include',
 | 
					 | 
				
			||||||
        'third_party/rapidjson/include/',
 | 
					 | 
				
			||||||
        'third_party/sparsepp/'] +
 | 
					 | 
				
			||||||
        (['libclang'] if bld.env['use_clang_cxx'] else []),
 | 
					 | 
				
			||||||
      defines=[
 | 
					 | 
				
			||||||
        'LOGURU_WITH_STREAMS=1',
 | 
					 | 
				
			||||||
        'LOGURU_FILENAME_WIDTH=18',
 | 
					 | 
				
			||||||
        'LOGURU_THREADNAME_WIDTH=13',
 | 
					 | 
				
			||||||
        'DEFAULT_RESOURCE_DIRECTORY="' + bld.env.get_flat('default_resource_directory') + '"'] +
 | 
					 | 
				
			||||||
        (['USE_CLANG_CXX=1', 'LOGURU_RTTI=0']
 | 
					 | 
				
			||||||
            if bld.env['use_clang_cxx']
 | 
					 | 
				
			||||||
            else []),
 | 
					 | 
				
			||||||
      lib=lib,
 | 
					 | 
				
			||||||
      rpath=bld.env.rpath,
 | 
					 | 
				
			||||||
      target='bin/cquery')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if bld.cmd == 'install' and 'clang_tarball_name' in bld.env:
 | 
					 | 
				
			||||||
    clang_tarball_name = bld.env.clang_tarball_name
 | 
					 | 
				
			||||||
    if sys.platform != 'win32':
 | 
					 | 
				
			||||||
      bld.install_files('${PREFIX}/lib/' + clang_tarball_name + '/lib', bld.path.get_bld().ant_glob('lib/' + clang_tarball_name + '/lib/libclang.(dylib|so.[4-9])', quiet=True))
 | 
					 | 
				
			||||||
      # TODO This may be cached and cannot be re-triggered. Use proper shell escape.
 | 
					 | 
				
			||||||
      bld(rule='rsync -rtR {}/./lib/{}/lib/clang/*/include {}/'.format(bld.path.get_bld(), clang_tarball_name, bld.env['PREFIX']))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def init(ctx):
 | 
					 | 
				
			||||||
  from waflib.Build import BuildContext, CleanContext, InstallContext, UninstallContext
 | 
					 | 
				
			||||||
  for y in (BuildContext, CleanContext, InstallContext, UninstallContext):
 | 
					 | 
				
			||||||
    class tmp(y):
 | 
					 | 
				
			||||||
      variant = ctx.options.variant
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  # This is needed because waf initializes the ConfigurationContext with
 | 
					 | 
				
			||||||
  # an arbitrary setenv('') which would rewrite the previous configuration
 | 
					 | 
				
			||||||
  # cache for the default variant if the configure step finishes.
 | 
					 | 
				
			||||||
  # Ideally ConfigurationContext should just let us override this at class
 | 
					 | 
				
			||||||
  # level like the other Context subclasses do with variant
 | 
					 | 
				
			||||||
  from waflib.Configure import ConfigurationContext
 | 
					 | 
				
			||||||
  class cctx(ConfigurationContext):
 | 
					 | 
				
			||||||
    def resetenv(self, name):
 | 
					 | 
				
			||||||
      self.all_envs = {}
 | 
					 | 
				
			||||||
      self.setenv(name)
 | 
					 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user