77 BindingPattern ,
88 BreakOrContinueStatement ,
99 CancellationToken ,
10+ canHaveDecorators ,
1011 canUsePropertyAccess ,
1112 CaseBlock ,
1213 cast ,
@@ -44,6 +45,7 @@ import {
4445 createTextSpanFromRange ,
4546 Debug ,
4647 Declaration ,
48+ Decorator ,
4749 Diagnostics ,
4850 diagnosticToString ,
4951 displayPart ,
@@ -91,6 +93,7 @@ import {
9193 getLineAndCharacterOfPosition ,
9294 getLineStartPositionForPosition ,
9395 getLocalSymbolForExportDefault ,
96+ getModifiers ,
9497 getNameOfDeclaration ,
9598 getNameTable ,
9699 getNewLineCharacter ,
@@ -145,6 +148,7 @@ import {
145148 isConstructorDeclaration ,
146149 isContextualKeyword ,
147150 isDeclarationName ,
151+ isDecorator ,
148152 isDeprecatedDeclaration ,
149153 isEntityName ,
150154 isEnumMember ,
@@ -276,6 +280,7 @@ import {
276280 memoizeOne ,
277281 MethodDeclaration ,
278282 ModifierFlags ,
283+ ModifierLike ,
279284 modifiersToFlags ,
280285 ModifierSyntaxKind ,
281286 modifierToFlag ,
@@ -1609,6 +1614,7 @@ function createCompletionEntry(
16091614 includeSymbol : boolean
16101615) : CompletionEntry | undefined {
16111616 let insertText : string | undefined ;
1617+ let filterText : string | undefined ;
16121618 let replacementSpan = getReplacementSpanForContextToken ( replacementToken ) ;
16131619 let data : CompletionEntryData | undefined ;
16141620 let isSnippet : true | undefined ;
@@ -1682,12 +1688,16 @@ function createCompletionEntry(
16821688 completionKind === CompletionKind . MemberLike &&
16831689 isClassLikeMemberCompletion ( symbol , location , sourceFile ) ) {
16841690 let importAdder ;
1685- ( { insertText, isSnippet, importAdder, replacementSpan } =
1686- getEntryForMemberCompletion ( host , program , options , preferences , name , symbol , location , position , contextToken , formatContext ) ) ;
1687- sortText = SortText . ClassMemberSnippets ; // sortText has to be lower priority than the sortText for keywords. See #47852.
1688- if ( importAdder ?. hasFixes ( ) ) {
1689- hasAction = true ;
1690- source = CompletionSource . ClassMemberSnippet ;
1691+ const memberCompletionEntry = getEntryForMemberCompletion ( host , program , options , preferences , name , symbol , location , position , contextToken , formatContext ) ;
1692+ if ( memberCompletionEntry ) {
1693+ ( { insertText, filterText, isSnippet, importAdder } = memberCompletionEntry ) ;
1694+ if ( importAdder ?. hasFixes ( ) ) {
1695+ hasAction = true ;
1696+ source = CompletionSource . ClassMemberSnippet ;
1697+ }
1698+ }
1699+ else {
1700+ return undefined ; // Skip this entry
16911701 }
16921702 }
16931703
@@ -1755,6 +1765,7 @@ function createCompletionEntry(
17551765 hasAction : hasAction ? true : undefined ,
17561766 isRecommended : isRecommendedCompletionMatch ( symbol , recommendedCompletion , typeChecker ) || undefined ,
17571767 insertText,
1768+ filterText,
17581769 replacementSpan,
17591770 sourceDisplay,
17601771 labelDetails,
@@ -1826,15 +1837,15 @@ function getEntryForMemberCompletion(
18261837 position : number ,
18271838 contextToken : Node | undefined ,
18281839 formatContext : formatting . FormatContext | undefined ,
1829- ) : { insertText : string , isSnippet ?: true , importAdder ?: codefix . ImportAdder , replacementSpan ?: TextSpan } {
1840+ ) : { insertText : string , filterText ?: string , isSnippet ?: true , importAdder ?: codefix . ImportAdder , eraseRange ?: TextRange } | undefined {
18301841 const classLikeDeclaration = findAncestor ( location , isClassLike ) ;
18311842 if ( ! classLikeDeclaration ) {
1832- return { insertText : name } ;
1843+ return undefined ; // This should never happen.
18331844 }
18341845
18351846 let isSnippet : true | undefined ;
1836- let replacementSpan : TextSpan | undefined ;
18371847 let insertText : string = name ;
1848+ const filterText : string = name ;
18381849
18391850 const checker = program . getTypeChecker ( ) ;
18401851 const sourceFile = location . getSourceFile ( ) ;
@@ -1863,11 +1874,11 @@ function getEntryForMemberCompletion(
18631874 }
18641875
18651876 let modifiers = ModifierFlags . None ;
1877+ const { modifiers : presentModifiers , range : eraseRange , decorators : presentDecorators } = getPresentModifiers ( contextToken , sourceFile , position ) ;
18661878 // Whether the suggested member should be abstract.
18671879 // e.g. in `abstract class C { abstract | }`, we should offer abstract method signatures at position `|`.
1868- const { modifiers : presentModifiers , span : modifiersSpan } = getPresentModifiers ( contextToken , sourceFile , position ) ;
1869- const isAbstract = ! ! ( presentModifiers & ModifierFlags . Abstract ) ;
1870- const completionNodes : Node [ ] = [ ] ;
1880+ const isAbstract = presentModifiers & ModifierFlags . Abstract && classLikeDeclaration . modifierFlagsCache & ModifierFlags . Abstract ;
1881+ let completionNodes : codefix . AddNode [ ] = [ ] ;
18711882 codefix . addNewNodeForMemberSymbol (
18721883 symbol ,
18731884 classLikeDeclaration ,
@@ -1896,20 +1907,49 @@ function getEntryForMemberCompletion(
18961907 // This is needed when we have overloaded signatures,
18971908 // so this callback will be called for multiple nodes/signatures,
18981909 // and we need to make sure the modifiers are uniform for all nodes/signatures.
1899- modifiers = node . modifierFlagsCache | requiredModifiers | presentModifiers ;
1910+ modifiers = node . modifierFlagsCache | requiredModifiers ;
19001911 }
19011912 node = factory . updateModifiers ( node , modifiers ) ;
19021913 completionNodes . push ( node ) ;
19031914 } ,
19041915 body ,
19051916 codefix . PreserveOptionalFlags . Property ,
1906- isAbstract ) ;
1917+ ! ! isAbstract ) ;
19071918
19081919 if ( completionNodes . length ) {
1920+ const isMethod = symbol . flags & SymbolFlags . Method ;
1921+ let allowedModifiers = modifiers | ModifierFlags . Override | ModifierFlags . Public ;
1922+ if ( ! isMethod ) {
1923+ allowedModifiers |= ModifierFlags . Ambient | ModifierFlags . Readonly ;
1924+ }
1925+ else {
1926+ allowedModifiers |= ModifierFlags . Async ;
1927+ }
1928+ const allowedAndPresent = presentModifiers & allowedModifiers ;
1929+ if ( presentModifiers & ( ~ allowedModifiers ) ) {
1930+ return undefined ; // This completion entry will be filtered out.
1931+ }
1932+ // If the original member is protected, we allow it to change to public.
1933+ if ( modifiers & ModifierFlags . Protected && allowedAndPresent & ModifierFlags . Public ) {
1934+ modifiers &= ~ ModifierFlags . Protected ;
1935+ }
1936+ // `public` modifier is optional and can be dropped.
1937+ if ( allowedAndPresent !== ModifierFlags . None && ! ( allowedAndPresent & ModifierFlags . Public ) ) {
1938+ modifiers &= ~ ModifierFlags . Public ;
1939+ }
1940+ modifiers |= allowedAndPresent ;
1941+ completionNodes = completionNodes . map ( node => factory . updateModifiers ( node , modifiers ) ) ;
1942+ // Add back the decorators that were already present.
1943+ if ( presentDecorators ?. length ) {
1944+ const lastNode = completionNodes [ completionNodes . length - 1 ] ;
1945+ if ( canHaveDecorators ( lastNode ) ) {
1946+ completionNodes [ completionNodes . length - 1 ] = factory . updateModifierLike ( lastNode , ( presentDecorators as ModifierLike [ ] ) . concat ( getModifiers ( lastNode ) || [ ] ) ) ;
1947+ }
1948+ }
1949+
19091950 const format = ListFormat . MultiLine | ListFormat . NoTrailingNewLine ;
1910- replacementSpan = modifiersSpan ;
1911- // If we have access to formatting settings, we print the nodes using the emitter,
1912- // and then format the printed text.
1951+ // If we have access to formatting settings, we print the nodes using the emitter,
1952+ // and then format the printed text.
19131953 if ( formatContext ) {
19141954 insertText = printer . printAndFormatSnippetList (
19151955 format ,
@@ -1925,21 +1965,22 @@ function getEntryForMemberCompletion(
19251965 }
19261966 }
19271967
1928- return { insertText, isSnippet, importAdder, replacementSpan } ;
1968+ return { insertText, filterText , isSnippet, importAdder, eraseRange } ;
19291969}
19301970
19311971function getPresentModifiers (
19321972 contextToken : Node | undefined ,
19331973 sourceFile : SourceFile ,
1934- position : number ) : { modifiers : ModifierFlags , span ?: TextSpan } {
1974+ position : number ) : { modifiers : ModifierFlags , decorators ?: Decorator [ ] , range ?: TextRange } {
19351975 if ( ! contextToken ||
19361976 getLineAndCharacterOfPosition ( sourceFile , position ) . line
19371977 > getLineAndCharacterOfPosition ( sourceFile , contextToken . getEnd ( ) ) . line ) {
19381978 return { modifiers : ModifierFlags . None } ;
19391979 }
19401980 let modifiers = ModifierFlags . None ;
1941- let span ;
1981+ let decorators : Decorator [ ] | undefined ;
19421982 let contextMod ;
1983+ const range : TextRange = { pos : position , end : position } ;
19431984 /*
19441985 Cases supported:
19451986 In
@@ -1959,15 +2000,19 @@ function getPresentModifiers(
19592000 `location.parent` is property declaration ``protected override m``,
19602001 `location.parent.parent` is class declaration ``class C { ... }``.
19612002 */
1962- if ( contextMod = isModifierLike ( contextToken ) ) {
1963- modifiers |= modifierToFlag ( contextMod ) ;
1964- span = createTextSpanFromNode ( contextToken ) ;
1965- }
1966- if ( isPropertyDeclaration ( contextToken . parent ) ) {
2003+ if ( isPropertyDeclaration ( contextToken . parent ) && contextToken . parent . modifiers ) {
19672004 modifiers |= modifiersToFlags ( contextToken . parent . modifiers ) & ModifierFlags . Modifier ;
1968- span = createTextSpanFromNode ( contextToken . parent ) ;
2005+ decorators = contextToken . parent . modifiers . filter ( isDecorator ) || [ ] ;
2006+ range . pos = Math . min ( range . pos , contextToken . parent . modifiers . pos ) ;
2007+ }
2008+ if ( contextMod = isModifierLike ( contextToken ) ) {
2009+ const contextModifierFlag = modifierToFlag ( contextMod ) ;
2010+ if ( ! ( modifiers & contextModifierFlag ) ) {
2011+ modifiers |= contextModifierFlag ;
2012+ range . pos = Math . min ( range . pos , contextToken . pos ) ;
2013+ }
19692014 }
1970- return { modifiers, span } ;
2015+ return { modifiers, decorators , range : range . pos !== position ? range : undefined } ;
19712016}
19722017
19732018function isModifierLike ( node : Node ) : ModifierSyntaxKind | undefined {
@@ -2770,7 +2815,7 @@ function getCompletionEntryCodeActionsAndSourceDisplay(
27702815 }
27712816
27722817 if ( source === CompletionSource . ClassMemberSnippet ) {
2773- const { importAdder } = getEntryForMemberCompletion (
2818+ const { importAdder, eraseRange } = getEntryForMemberCompletion (
27742819 host ,
27752820 program ,
27762821 compilerOptions ,
@@ -2780,11 +2825,18 @@ function getCompletionEntryCodeActionsAndSourceDisplay(
27802825 location ,
27812826 position ,
27822827 contextToken ,
2783- formatContext ) ;
2784- if ( importAdder ) {
2828+ formatContext ) ! ;
2829+ if ( importAdder || eraseRange ) {
27852830 const changes = textChanges . ChangeTracker . with (
27862831 { host, formatContext, preferences } ,
2787- importAdder . writeFixes ) ;
2832+ tracker => {
2833+ if ( importAdder ) {
2834+ importAdder . writeFixes ( tracker ) ;
2835+ }
2836+ if ( eraseRange ) {
2837+ tracker . deleteRange ( sourceFile , eraseRange ) ;
2838+ }
2839+ } ) ;
27882840 return {
27892841 sourceDisplay : undefined ,
27902842 codeActions : [ {
0 commit comments