rwols updated this revision to Diff 112609.
rwols added a comment.

[clangd] [WIP] Add support for snippet completions

- Restore the sortText logic
- Return CompletionItem instead of modifying a ref param
- Make all helper methods const
- Only set to Snippet once we encounter CK_Placeholder/CK_CurrentParameter
- More clear annotation handling
- Escape any $, } and \ for a snippet


https://reviews.llvm.org/D37101

Files:
  clangd/ClangdUnit.cpp
  clangd/Protocol.cpp

Index: clangd/Protocol.cpp
===================================================================
--- clangd/Protocol.cpp
+++ clangd/Protocol.cpp
@@ -724,8 +724,8 @@
   if (!CI.insertText.empty())
     Os << R"("insertText":")" << llvm::yaml::escape(CI.insertText) << R"(",)";
   if (CI.insertTextFormat != InsertTextFormat::Missing) {
-    Os << R"("insertTextFormat":")" << static_cast<int>(CI.insertTextFormat)
-       << R"(",)";
+    Os << R"("insertTextFormat":)" << static_cast<int>(CI.insertTextFormat)
+       << R"(,)";
   }
   if (CI.textEdit)
     Os << R"("textEdit":)" << TextEdit::unparse(*CI.textEdit) << ',';
Index: clangd/ClangdUnit.cpp
===================================================================
--- clangd/ClangdUnit.cpp
+++ clangd/ClangdUnit.cpp
@@ -288,45 +288,189 @@
   void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
                                   CodeCompletionResult *Results,
                                   unsigned NumResults) override {
-    for (unsigned I = 0; I != NumResults; ++I) {
-      CodeCompletionResult &Result = Results[I];
-      CodeCompletionString *CCS = Result.CreateCodeCompletionString(
+    assert(Items && "We need a non-null items container here");
+    Items->reserve(NumResults);
+    for (unsigned I = 0; I < NumResults; ++I) {
+      auto &Result = Results[I];
+      const auto *CCS = Result.CreateCodeCompletionString(
           S, Context, *Allocator, CCTUInfo,
           CodeCompleteOpts.IncludeBriefComments);
-      if (CCS) {
-        CompletionItem Item;
-        for (CodeCompletionString::Chunk C : *CCS) {
-          switch (C.Kind) {
-          case CodeCompletionString::CK_ResultType:
-            Item.detail = C.Text;
-            break;
-          case CodeCompletionString::CK_Optional:
-            break;
-          default:
-            Item.label += C.Text;
-            break;
-          }
-        }
-        assert(CCS->getTypedText());
-        Item.kind = getKind(Result.CursorKind);
-        // Priority is a 16-bit integer, hence at most 5 digits.
-        assert(CCS->getPriority() < 99999 && "Expecting code completion result "
-                                             "priority to have at most "
-                                             "5-digits");
-        llvm::raw_string_ostream(Item.sortText)
-            << llvm::format("%05d%s", CCS->getPriority(), CCS->getTypedText());
-        Item.insertText = Item.filterText = CCS->getTypedText();
-        if (CCS->getBriefComment())
-          Item.documentation = CCS->getBriefComment();
-        Items->push_back(std::move(Item));
-      }
+      assert(CCS && "Expected the CodeCompletionString to be non-null");
+      Items->push_back(ProcessCodeCompleteResult(Result, *CCS));
     }
   }
 
   GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; }
 
   CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
-};
+
+private:
+  CompletionItem
+  ProcessCodeCompleteResult(const CodeCompletionResult &Result,
+                            const CodeCompletionString &CCS) const {
+    // The object that we'll return.
+    CompletionItem Item;
+
+    // By default InsertTextFormat::PlainText. When we encounter a
+    // CK_Placeholder or CK_CurrentParameter, this will be modified to
+    // InsertTextFormat::PlainText.
+    Item.insertTextFormat = InsertTextFormat::PlainText;
+
+    // Things like __attribute__((nonnull(1,3))) and [[noreturn]]. Present this
+    // information in the documentation field.
+    if (CCS.getAnnotationCount() > 0) {
+      Item.documentation += "Annotation";
+      if (CCS.getAnnotationCount() == 1) {
+        Item.documentation += ": ";
+      } else /* CCS.getAnnotationCount() > 1 */ {
+        Item.documentation += "s: ";
+      }
+      for (unsigned j = 0; j < CCS.getAnnotationCount(); ++j) {
+        Item.documentation += CCS.getAnnotation(j);
+        Item.documentation += j == CCS.getAnnotationCount() - 1 ? "\n\n" : " ";
+      }
+    }
+
+    // Fill in the label, detail, documentation and insertText fields of the
+    // CompletionItem. The informative chunks are put into the documentation
+    // field.
+    ProcessChunks(CCS, Item);
+
+    // Add brief documentation (if there is any).
+    if (CCS.getBriefComment() != nullptr) {
+      Item.documentation += CCS.getBriefComment();
+    }
+
+    // Fill in the kind field of the CompletionItem.
+    Item.kind = getKind(Result.CursorKind);
+
+    // Fill in the sortText of the CompletionItem.
+    assert(CCS.getPriority() < 99999 && "Expecting code completion result "
+                                        "priority to have at most 5-digits");
+    llvm::raw_string_ostream(Item.sortText)
+        << llvm::format("%05d%s", CCS.getPriority(), CCS.getTypedText());
+
+    return Item;
+  }
+
+  void ProcessChunks(const CodeCompletionString &CCS,
+                     CompletionItem &Item) const {
+    unsigned ArgCount = 0;
+    for (const auto &Chunk : CCS) {
+      switch (Chunk.Kind) {
+      case CodeCompletionString::CK_TypedText:
+        // The piece of text that the user is expected to type to match
+        // the code-completion string, typically a keyword or the name of
+        // a declarator or macro.
+        Item.label += Chunk.Text;
+        Item.insertText += Chunk.Text;
+        break;
+      case CodeCompletionString::CK_Text:
+        // A piece of text that should be placed in the buffer,
+        // e.g., parentheses or a comma in a function call.
+        Item.label += Chunk.Text;
+        Item.insertText += Chunk.Text;
+        break;
+      case CodeCompletionString::CK_Optional:
+        // A code completion string that is entirely optional.
+        // For example, an optional code completion string that
+        // describes the default arguments in a function call.
+
+        // TODO: Maybe add an option to allow presenting the optional chunks?
+        break;
+      case CodeCompletionString::CK_Placeholder:
+        // A string that acts as a placeholder for, e.g., a function call
+        // argument.
+        AddSnippetParameter(Chunk.Text, ArgCount, Item);
+        break;
+      case CodeCompletionString::CK_Informative:
+        // A piece of text that describes something about the result
+        // but should not be inserted into the buffer.
+        // For example, the word "const" for a const method, or the name of the
+        // base class for methods that are part of the base class.
+
+        // Terrible hack.
+        // TODO: See SemaCodeComplete.cpp:2598
+        if (Chunk.Text[0] == ' ')
+          Item.documentation += "[" + std::string(Chunk.Text + 1) + "] ";
+        else
+          Item.documentation += "[" + std::string(Chunk.Text) + "] ";
+        break;
+      case CodeCompletionString::CK_ResultType:
+        // A piece of text that describes the type of an entity or,
+        // for functions and methods, the return type.
+        Item.detail += Chunk.Text;
+        break;
+      case CodeCompletionString::CK_CurrentParameter:
+        // TODO(rwols): I'm still not sure what the difference is between
+        // CK_CurrentParameter and CK_Placeholder. From skimming over the code
+        // in SemaCodeComplete.cpp they seem to be used interchangeably.
+        //
+        // A piece of text that describes the parameter that corresponds to the
+        // code-completion location within a function call, message send, macro
+        // invocation, etc.
+        AddSnippetParameter(Chunk.Text, ArgCount, Item);
+        break;
+      case CodeCompletionString::CK_LeftParen:
+        // A left parenthesis ('(').
+      case CodeCompletionString::CK_RightParen:
+        // A right parenthesis (')').
+      case CodeCompletionString::CK_LeftBracket:
+        // A left bracket ('[').
+      case CodeCompletionString::CK_RightBracket:
+        // A right bracket (']').
+      case CodeCompletionString::CK_LeftBrace:
+        // A left brace ('{').
+      case CodeCompletionString::CK_RightBrace:
+        // A right brace ('}').
+      case CodeCompletionString::CK_LeftAngle:
+        // A left angle bracket ('<').
+      case CodeCompletionString::CK_RightAngle:
+        // A right angle bracket ('>').
+      case CodeCompletionString::CK_Comma:
+        // A comma separator (',').
+      case CodeCompletionString::CK_Colon:
+        // A colon (':').
+      case CodeCompletionString::CK_SemiColon:
+        // A semicolon (';').
+      case CodeCompletionString::CK_Equal:
+        // An '=' sign.
+      case CodeCompletionString::CK_HorizontalSpace:
+        // Horizontal whitespace (' ').
+      case CodeCompletionString::CK_VerticalSpace:
+        // Vertical whitespace ('\n' or '\r\n', depending on the
+        // platform).
+        Item.insertText += Chunk.Text;
+        Item.label += Chunk.Text;
+        break;
+      default:
+        llvm_unreachable("Unexpected CodeCompletionString chunk type!");
+      }
+    }
+  }
+
+  void AddSnippetParameter(const char *Text, unsigned &ArgCount,
+                           CompletionItem &Item) const {
+    ++ArgCount;
+    Item.insertText +=
+        "${" + std::to_string(ArgCount) + ":" + SnippetEscape(Text) + "}";
+    Item.label += Text;
+    Item.insertTextFormat = InsertTextFormat::Snippet;
+  }
+
+  std::string SnippetEscape(const char *Text) const {
+    std::string Result;
+    const auto Length = std::strlen(Text);
+    Result.reserve(Length); // Assume a '$', '}' and '\\' are rare.
+    for (const auto *S = Text; S != Text + Length; ++S) {
+      if (*S == '$' || *S == '}' || *S == '\\')
+        Result += '\\';
+      Result += *S;
+    }
+    return Result;
+  }
+
+}; // CompletionItemsCollector
 } // namespace
 
 std::vector<CompletionItem>
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to