mirror of
				https://github.com/MaskRay/ccls.git
				synced 2025-11-04 06:15:20 +00:00 
			
		
		
		
	Fix completion of headers.
This commit is contained in:
		
							parent
							
								
									5d88eb959d
								
							
						
					
					
						commit
						699d2f3655
					
				@ -101,7 +101,8 @@ struct Config {
 | 
			
		||||
  // An example value is { ".h", ".hpp" }
 | 
			
		||||
  //
 | 
			
		||||
  // This is significantly faster than using a regex.
 | 
			
		||||
  std::vector<std::string> includeCompletionWhitelistLiteralEnding;
 | 
			
		||||
  std::vector<std::string> includeCompletionWhitelistLiteralEnding = {
 | 
			
		||||
      ".h", ".hpp", ".hh"};
 | 
			
		||||
  // Regex patterns to match include completion candidates against. They
 | 
			
		||||
  // receive the absolute file path.
 | 
			
		||||
  //
 | 
			
		||||
 | 
			
		||||
@ -78,30 +78,21 @@ bool TrimPath(Project* project,
 | 
			
		||||
 | 
			
		||||
lsCompletionItem BuildCompletionItem(Config* config,
 | 
			
		||||
                                     const std::string& path,
 | 
			
		||||
                                     bool use_angle_brackets,
 | 
			
		||||
                                     bool /*use_angle_brackets*/,
 | 
			
		||||
                                     bool is_stl) {
 | 
			
		||||
  lsCompletionItem item;
 | 
			
		||||
  if (use_angle_brackets)
 | 
			
		||||
    item.label = "#include <" + ElideLongPath(config, path) + ">";
 | 
			
		||||
  else
 | 
			
		||||
    item.label = "#include \"" + ElideLongPath(config, path) + "\"";
 | 
			
		||||
 | 
			
		||||
  item.label = ElideLongPath(config, path);
 | 
			
		||||
  item.detail = path;
 | 
			
		||||
 | 
			
		||||
  // Replace the entire existing content.
 | 
			
		||||
  // NOTE: When submitting completion items, textEdit->range must be updated.
 | 
			
		||||
  item.textEdit = lsTextEdit();
 | 
			
		||||
  if (use_angle_brackets)
 | 
			
		||||
    item.textEdit->newText = "#include <" + path + ">";
 | 
			
		||||
  else
 | 
			
		||||
    item.textEdit->newText = "#include \"" + path + "\"";
 | 
			
		||||
 | 
			
		||||
  item.textEdit->newText = path;
 | 
			
		||||
  item.insertTextFormat = lsInsertTextFormat::PlainText;
 | 
			
		||||
  if (is_stl)
 | 
			
		||||
  if (is_stl) {
 | 
			
		||||
    item.kind = lsCompletionItemKind::Module;
 | 
			
		||||
  else
 | 
			
		||||
    item.priority_ = 2;
 | 
			
		||||
  } else {
 | 
			
		||||
    item.kind = lsCompletionItemKind::File;
 | 
			
		||||
 | 
			
		||||
    item.priority_ = 1;
 | 
			
		||||
  }
 | 
			
		||||
  return item;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -127,8 +118,8 @@ void IncludeComplete::Rescan() {
 | 
			
		||||
    Timer timer;
 | 
			
		||||
 | 
			
		||||
    InsertStlIncludes();
 | 
			
		||||
    InsertIncludesFromDirectory(config_->projectRoot,
 | 
			
		||||
                                false /*use_angle_brackets*/);
 | 
			
		||||
    // InsertIncludesFromDirectory(config_->projectRoot,
 | 
			
		||||
    //                             false /*use_angle_brackets*/);
 | 
			
		||||
    for (const std::string& dir : project_->quote_include_directories)
 | 
			
		||||
      InsertIncludesFromDirectory(dir, false /*use_angle_brackets*/);
 | 
			
		||||
    for (const std::string& dir : project_->angle_include_directories)
 | 
			
		||||
 | 
			
		||||
@ -45,11 +45,38 @@ lsPosition CharPos(const std::string& search,
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ShouldRunIncludeCompletion(const std::string& line) {
 | 
			
		||||
std::tuple<bool, std::string, std::string> ShouldRunIncludeCompletion(
 | 
			
		||||
    const std::string& line) {
 | 
			
		||||
  size_t start = 0;
 | 
			
		||||
  while (start < line.size() && isspace(line[start]))
 | 
			
		||||
    ++start;
 | 
			
		||||
  return start < line.size() && line[start] == '#';
 | 
			
		||||
  // Must start with '#'.
 | 
			
		||||
  if (start >= line.size() || line[start] != '#')
 | 
			
		||||
    return std::make_tuple(false, "", "");
 | 
			
		||||
  ++start;
 | 
			
		||||
  // Ingore "include" and following spaces.
 | 
			
		||||
  if (line.compare(start, 7, "include") == 0) {
 | 
			
		||||
    start += 7;
 | 
			
		||||
    while (start < line.size() && isspace(line[start]))
 | 
			
		||||
      ++start;
 | 
			
		||||
  }
 | 
			
		||||
  // Determine the surrounding characters.
 | 
			
		||||
  std::string surround;
 | 
			
		||||
  if (line[start] == '"') {
 | 
			
		||||
    surround = "\"\"";
 | 
			
		||||
    ++start;
 | 
			
		||||
  } else if (line[start] == '<') {
 | 
			
		||||
    surround = "<>";
 | 
			
		||||
    ++start;
 | 
			
		||||
  } else
 | 
			
		||||
    surround = "<>";
 | 
			
		||||
  // Fix the prefix for completion.
 | 
			
		||||
  size_t end = start;
 | 
			
		||||
  while (end < line.size() && line[end] != '\"' && line[end] != '>')
 | 
			
		||||
    ++end;
 | 
			
		||||
  std::string prefix = line.substr(start, end - start);
 | 
			
		||||
 | 
			
		||||
  return std::make_tuple(true, surround, prefix);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: eliminate |line_number| param.
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,7 @@
 | 
			
		||||
#include "language_server_api.h"
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <tuple>
 | 
			
		||||
 | 
			
		||||
// Utility method to map |position| to an offset inside of |content|.
 | 
			
		||||
int GetOffsetForPosition(lsPosition position, const std::string& content);
 | 
			
		||||
@ -11,7 +12,8 @@ lsPosition CharPos(const std::string& search,
 | 
			
		||||
                   char character,
 | 
			
		||||
                   int character_offset = 0);
 | 
			
		||||
 | 
			
		||||
bool ShouldRunIncludeCompletion(const std::string& line);
 | 
			
		||||
std::tuple<bool, std::string, std::string> ShouldRunIncludeCompletion(
 | 
			
		||||
    const std::string& line);
 | 
			
		||||
 | 
			
		||||
// TODO: eliminate |line_number| param.
 | 
			
		||||
optional<lsRange> ExtractQuotedRange(int line_number, const std::string& line);
 | 
			
		||||
 | 
			
		||||
@ -38,7 +38,7 @@ struct lsCompletionOptions {
 | 
			
		||||
  // for
 | 
			
		||||
  // '::' and '>' for '->'. See
 | 
			
		||||
  // https://github.com/Microsoft/language-server-protocol/issues/138.
 | 
			
		||||
  std::vector<std::string> triggerCharacters = {".", ":", ">", "#"};
 | 
			
		||||
  std::vector<std::string> triggerCharacters = {".", ":", ">", "#", "<", "\""};
 | 
			
		||||
};
 | 
			
		||||
MAKE_REFLECT_STRUCT(lsCompletionOptions, resolveProvider, triggerCharacters);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -256,9 +256,17 @@ struct TextDocumentCompletionHandler : MessageHandler {
 | 
			
		||||
      std::string character = *request->params.context->triggerCharacter;
 | 
			
		||||
      char preceding_index = request->params.position.character - 2;
 | 
			
		||||
 | 
			
		||||
      // If the character is '"' or '<', make sure the line is start with '#'.
 | 
			
		||||
      if (character == "\"" || character == "<") {
 | 
			
		||||
        size_t i = 0;
 | 
			
		||||
        while (i < buffer_line.size() && isspace(buffer_line[i]))
 | 
			
		||||
          ++i;
 | 
			
		||||
        if (i >= buffer_line.size() || buffer_line[i] != '#')
 | 
			
		||||
          did_fail_check = true;
 | 
			
		||||
      }
 | 
			
		||||
      // If the character is > or : and we are at the start of the line, do not
 | 
			
		||||
      // show completion results.
 | 
			
		||||
      if ((character == ">" || character == ":") && preceding_index < 0) {
 | 
			
		||||
      else if ((character == ">" || character == ":") && preceding_index < 0) {
 | 
			
		||||
        did_fail_check = true;
 | 
			
		||||
      }
 | 
			
		||||
      // If the character is > but - does not preced it, or if it is : and :
 | 
			
		||||
@ -283,7 +291,11 @@ struct TextDocumentCompletionHandler : MessageHandler {
 | 
			
		||||
          &existing_completion);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ShouldRunIncludeCompletion(buffer_line)) {
 | 
			
		||||
    bool yes;
 | 
			
		||||
    std::string surround, prefix;
 | 
			
		||||
    std::tie(yes, surround, prefix) = ShouldRunIncludeCompletion(buffer_line);
 | 
			
		||||
 | 
			
		||||
    if (yes) {
 | 
			
		||||
      Out_TextDocumentComplete out;
 | 
			
		||||
      out.id = request->id;
 | 
			
		||||
 | 
			
		||||
@ -296,19 +308,26 @@ struct TextDocumentCompletionHandler : MessageHandler {
 | 
			
		||||
                                include_complete->completion_items.end());
 | 
			
		||||
        if (lock)
 | 
			
		||||
          lock.unlock();
 | 
			
		||||
 | 
			
		||||
        // Update textEdit params.
 | 
			
		||||
        for (lsCompletionItem& item : out.result.items) {
 | 
			
		||||
          item.textEdit->range.start.line = request->params.position.line;
 | 
			
		||||
          item.textEdit->range.start.character = 0;
 | 
			
		||||
          item.textEdit->range.end.line = request->params.position.line;
 | 
			
		||||
          item.textEdit->range.end.character = (int)buffer_line.size();
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      TrimInPlace(buffer_line);
 | 
			
		||||
      FilterAndSortCompletionResponse(&out, buffer_line,
 | 
			
		||||
      FilterAndSortCompletionResponse(&out, prefix,
 | 
			
		||||
                                      config->completion.filterAndSort);
 | 
			
		||||
 | 
			
		||||
      auto decorator = [&](std::string& text) {
 | 
			
		||||
        std::string result = "#include ";
 | 
			
		||||
        result += surround[0] + text + surround[1];
 | 
			
		||||
        text = result;
 | 
			
		||||
      };
 | 
			
		||||
      LOG_S(INFO) << "DEBUG prefix " << prefix;
 | 
			
		||||
      for (lsCompletionItem& item : out.result.items) {
 | 
			
		||||
        item.textEdit->range.start.line = request->params.position.line;
 | 
			
		||||
        item.textEdit->range.start.character = 0;
 | 
			
		||||
        item.textEdit->range.end.line = request->params.position.line;
 | 
			
		||||
        item.textEdit->range.end.character = (int)buffer_line.size();
 | 
			
		||||
        decorator(item.textEdit->newText);
 | 
			
		||||
        decorator(item.label);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      QueueManager::WriteStdout(IpcId::TextDocumentCompletion, out);
 | 
			
		||||
    } else {
 | 
			
		||||
      // If existing completion is empty, dont return clang-based completion
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user