From: Douglas Gregor Date: Thu, 19 Nov 2009 00:01:57 +0000 (+0000) Subject: Improve code-completion results for the flags in an @property X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=54f016150acf7e0d4dab702d3d7d5e40ba1fdebf;p=clang Improve code-completion results for the flags in an @property declaration by providing patterns for "getter = " and "setter = ". As part of this, invented a new "pattern" result kind that is merely a semantic string. The "pattern" result kind should help with other kinds of code templates. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@89277 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/CodeCompleteConsumer.h b/include/clang/Sema/CodeCompleteConsumer.h index 43ff686918..f8c4c5281c 100644 --- a/include/clang/Sema/CodeCompleteConsumer.h +++ b/include/clang/Sema/CodeCompleteConsumer.h @@ -123,6 +123,9 @@ public: /// \brief Create a new current-parameter chunk. static Chunk CreateCurrentParameter(llvm::StringRef CurrentParameter); + /// \brief Clone the given chunk. + Chunk Clone() const; + /// \brief Destroy this chunk, deallocating any memory it owns. void Destroy(); }; @@ -192,15 +195,21 @@ public: /// \brief Add a new chunk. void AddChunk(Chunk C) { Chunks.push_back(C); } + /// \brief Returns the text in the TypedText chunk. + const char *getTypedText() const; + /// \brief Retrieve a string representation of the code completion string, /// which is mainly useful for debugging. std::string getAsString() const; + /// \brief Clone this code-completion string. + CodeCompletionString *Clone() const; + /// \brief Serialize this code-completion string to the given stream. void Serialize(llvm::raw_ostream &OS) const; /// \brief Deserialize a code-completion string from the given string. - static CodeCompletionString *Deserialize(llvm::StringRef &Str); + static CodeCompletionString *Deserialize(llvm::StringRef &Str); }; llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, @@ -220,7 +229,8 @@ public: enum ResultKind { RK_Declaration = 0, //< Refers to a declaration RK_Keyword, //< Refers to a keyword or symbol. - RK_Macro //< Refers to a macro + RK_Macro, //< Refers to a macro + RK_Pattern //< Refers to a precomputed pattern. }; /// \brief The kind of result stored here. @@ -235,6 +245,10 @@ public: /// or symbol's spelling. const char *Keyword; + /// \brief When Kind == RK_Pattern, the code-completion string that + /// describes the completion text to insert. + CodeCompletionString *Pattern; + /// \brief When Kind == RK_Macro, the identifier that refers to a macro. IdentifierInfo *Macro; }; @@ -277,6 +291,12 @@ public: : Kind(RK_Macro), Macro(Macro), Rank(Rank), Hidden(false), QualifierIsInformative(0), StartsNestedNameSpecifier(false), Qualifier(0) { } + + /// \brief Build a result that refers to a pattern. + Result(CodeCompletionString *Pattern, unsigned Rank) + : Kind(RK_Pattern), Pattern(Pattern), Rank(Rank), Hidden(false), + QualifierIsInformative(0), StartsNestedNameSpecifier(false), + Qualifier(0) { } /// \brief Retrieve the declaration stored in this result. NamedDecl *getDeclaration() const { @@ -293,6 +313,8 @@ public: /// \brief Create a new code-completion string that describes how to insert /// this result into a program. CodeCompletionString *CreateCodeCompletionString(Sema &S); + + void Destroy(); }; class OverloadCandidate { diff --git a/lib/Sema/CodeCompleteConsumer.cpp b/lib/Sema/CodeCompleteConsumer.cpp index 88ac4e49cf..a9d8301057 100644 --- a/lib/Sema/CodeCompleteConsumer.cpp +++ b/lib/Sema/CodeCompleteConsumer.cpp @@ -117,6 +117,33 @@ CodeCompletionString::Chunk::CreateCurrentParameter( return Chunk(CK_CurrentParameter, CurrentParameter); } +CodeCompletionString::Chunk CodeCompletionString::Chunk::Clone() const { + switch (Kind) { + case CK_TypedText: + case CK_Text: + case CK_Placeholder: + case CK_Informative: + case CK_CurrentParameter: + case CK_LeftParen: + case CK_RightParen: + case CK_LeftBracket: + case CK_RightBracket: + case CK_LeftBrace: + case CK_RightBrace: + case CK_LeftAngle: + case CK_RightAngle: + case CK_Comma: + return Chunk(Kind, Text); + + case CK_Optional: { + std::auto_ptr Opt(Optional->Clone()); + return CreateOptional(Opt); + } + } + + // Silence GCC warning. + return Chunk(); +} void CodeCompletionString::Chunk::Destroy() { @@ -168,6 +195,20 @@ std::string CodeCompletionString::getAsString() const { return Result; } +const char *CodeCompletionString::getTypedText() const { + for (iterator C = begin(), CEnd = end(); C != CEnd; ++C) + if (C->Kind == CK_TypedText) + return C->Text; + + return 0; +} + +CodeCompletionString *CodeCompletionString::Clone() const { + CodeCompletionString *Result = new CodeCompletionString; + for (iterator C = begin(), CEnd = end(); C != CEnd; ++C) + Result->AddChunk(C->Clone()); + return Result; +} namespace { // Escape a string for XML-like formatting. @@ -473,6 +514,13 @@ CodeCompletionString *CodeCompletionString::Deserialize(llvm::StringRef &Str) { return Result; } +void CodeCompleteConsumer::Result::Destroy() { + if (Kind == RK_Pattern) { + delete Pattern; + Pattern = 0; + } +} + //===----------------------------------------------------------------------===// // Code completion overload candidate implementation //===----------------------------------------------------------------------===// @@ -545,6 +593,12 @@ PrintingCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &SemaRef, OS << '\n'; break; } + + case Result::RK_Pattern: { + OS << "Pattern : " << Results[I].Rank << " : " + << Results[I].Pattern->getAsString() << '\n'; + break; + } } } @@ -627,6 +681,13 @@ CIndexCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &SemaRef, OS << '\n'; break; } + + case Result::RK_Pattern: { + OS << "Pattern:"; + Results[I].Pattern->Serialize(OS); + OS << '\n'; + break; + } } } diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index 22d2884ede..f3f7d3f405 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -1066,6 +1066,17 @@ namespace { else if (X.Rank > Y.Rank) return false; + // We use a special ordering for keywords and patterns, based on the + // typed text. + if ((X.Kind == Result::RK_Keyword || X.Kind == Result::RK_Pattern) && + (Y.Kind == Result::RK_Keyword || Y.Kind == Result::RK_Pattern)) { + const char *XStr = (X.Kind == Result::RK_Keyword)? X.Keyword + : X.Pattern->getTypedText(); + const char *YStr = (Y.Kind == Result::RK_Keyword)? Y.Keyword + : Y.Pattern->getTypedText(); + return strcmp(XStr, YStr) < 0; + } + // Result kinds are ordered by decreasing importance. if (X.Kind < Y.Kind) return true; @@ -1087,12 +1098,14 @@ namespace { return isEarlierDeclarationName(X.Declaration->getDeclName(), Y.Declaration->getDeclName()); - case Result::RK_Keyword: - return strcmp(X.Keyword, Y.Keyword) < 0; - case Result::RK_Macro: return llvm::LowercaseString(X.Macro->getName()) < llvm::LowercaseString(Y.Macro->getName()); + + case Result::RK_Keyword: + case Result::RK_Pattern: + llvm::llvm_unreachable("Result kinds handled above"); + break; } // Silence GCC warning. @@ -1120,6 +1133,9 @@ static void HandleCodeCompleteResults(Sema *S, if (CodeCompleter) CodeCompleter->ProcessCodeCompleteResults(*S, Results, NumResults); + + for (unsigned I = 0; I != NumResults; ++I) + Results[I].Destroy(); } void Sema::CodeCompleteOrdinaryName(Scope *S) { @@ -1635,10 +1651,20 @@ void Sema::CodeCompleteObjCPropertyFlags(Scope *S, ObjCDeclSpec &ODS) { Results.MaybeAddResult(CodeCompleteConsumer::Result("copy", 0)); if (!(Attributes & ObjCDeclSpec::DQ_PR_nonatomic)) Results.MaybeAddResult(CodeCompleteConsumer::Result("nonatomic", 0)); - if (!(Attributes & ObjCDeclSpec::DQ_PR_setter)) - Results.MaybeAddResult(CodeCompleteConsumer::Result("setter", 0)); - if (!(Attributes & ObjCDeclSpec::DQ_PR_getter)) - Results.MaybeAddResult(CodeCompleteConsumer::Result("getter", 0)); + if (!(Attributes & ObjCDeclSpec::DQ_PR_setter)) { + CodeCompletionString *Setter = new CodeCompletionString; + Setter->AddTypedTextChunk("setter"); + Setter->AddTextChunk(" = "); + Setter->AddPlaceholderChunk("method"); + Results.MaybeAddResult(CodeCompleteConsumer::Result(Setter, 0)); + } + if (!(Attributes & ObjCDeclSpec::DQ_PR_getter)) { + CodeCompletionString *Getter = new CodeCompletionString; + Getter->AddTypedTextChunk("getter"); + Getter->AddTextChunk(" = "); + Getter->AddPlaceholderChunk("method"); + Results.MaybeAddResult(CodeCompleteConsumer::Result(Getter, 0)); + } Results.ExitScope(); HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size()); } diff --git a/test/CodeCompletion/property.m b/test/CodeCompletion/property.m deleted file mode 100644 index 184519b408..0000000000 --- a/test/CodeCompletion/property.m +++ /dev/null @@ -1,29 +0,0 @@ -// Note: the run lines follow their respective tests, since line/column -// matter in this test. - -@interface Foo { - void *isa; -} -@property(copy) Foo *myprop; -@property(retain, nonatomic) id xx; -// RUN: clang-cc -fsyntax-only -code-completion-at=%s:7:11 %s -o - | FileCheck -check-prefix=CC1 %s -// CC1: assign -// CC1-NEXT: copy -// CC1-NEXT: getter -// CC1-NEXT: nonatomic -// CC1-NEXT: readonly -// CC1-NEXT: readwrite -// CC1-NEXT: retain -// CC1-NEXT: setter -// RUN: clang-cc -fsyntax-only -code-completion-at=%s:8:18 %s -o - | FileCheck -check-prefix=CC2 %s -// CC2: assign -// CC2-NEXT: copy -// CC2-NEXT: getter -// CC2-NEXT: nonatomic -// CC2-NEXT: readonly -// CC2-NEXT: readwrite -// CC2-NEXT: setter -@end - - - diff --git a/test/Index/complete-property-flags.m b/test/Index/complete-property-flags.m new file mode 100644 index 0000000000..65204b54f8 --- /dev/null +++ b/test/Index/complete-property-flags.m @@ -0,0 +1,29 @@ +// Note: the run lines follow their respective tests, since line/column +// matter in this test. + +@interface Foo { + void *isa; +} +@property(copy) Foo *myprop; +@property(retain, nonatomic) id xx; +// RUN: c-index-test -code-completion-at=%s:7:11 %s | FileCheck -check-prefix=CHECK-CC1 %s +// CHECK-CC1: {TypedText assign} +// CHECK-CC1-NEXT: {TypedText copy} +// CHECK-CC1-NEXT: {TypedText getter}{Text = }{Placeholder method} +// CHECK-CC1-NEXT: {TypedText nonatomic} +// CHECK-CC1-NEXT: {TypedText readonly} +// CHECK-CC1-NEXT: {TypedText readwrite} +// CHECK-CC1-NEXT: {TypedText retain} +// CHECK-CC1-NEXT: {TypedText setter}{Text = }{Placeholder method} +// RUN: c-index-test -code-completion-at=%s:8:18 %s | FileCheck -check-prefix=CHECK-CC2 %s +// CHECK-CC2: {TypedText assign} +// CHECK-CC2-NEXT: {TypedText copy} +// CHECK-CC2-NEXT: {TypedText getter}{Text = }{Placeholder method} +// CHECK-CC2-NEXT: {TypedText nonatomic} +// CHECK-CC2-NEXT: {TypedText readonly} +// CHECK-CC2-NEXT: {TypedText readwrite} +// CHECK-CC2-NEXT: {TypedText setter}{Text = }{Placeholder method} +@end + + +