7
7
BindingPattern ,
8
8
BreakOrContinueStatement ,
9
9
CancellationToken ,
10
+ canHaveDecorators ,
10
11
canUsePropertyAccess ,
11
12
CaseBlock ,
12
13
cast ,
@@ -44,6 +45,7 @@ import {
44
45
createTextSpanFromRange ,
45
46
Debug ,
46
47
Declaration ,
48
+ Decorator ,
47
49
Diagnostics ,
48
50
diagnosticToString ,
49
51
displayPart ,
@@ -91,6 +93,7 @@ import {
91
93
getLineAndCharacterOfPosition ,
92
94
getLineStartPositionForPosition ,
93
95
getLocalSymbolForExportDefault ,
96
+ getModifiers ,
94
97
getNameOfDeclaration ,
95
98
getNameTable ,
96
99
getNewLineCharacter ,
@@ -145,6 +148,7 @@ import {
145
148
isConstructorDeclaration ,
146
149
isContextualKeyword ,
147
150
isDeclarationName ,
151
+ isDecorator ,
148
152
isDeprecatedDeclaration ,
149
153
isEntityName ,
150
154
isEnumMember ,
@@ -276,6 +280,7 @@ import {
276
280
memoizeOne ,
277
281
MethodDeclaration ,
278
282
ModifierFlags ,
283
+ ModifierLike ,
279
284
modifiersToFlags ,
280
285
ModifierSyntaxKind ,
281
286
modifierToFlag ,
@@ -1609,6 +1614,7 @@ function createCompletionEntry(
1609
1614
includeSymbol : boolean
1610
1615
) : CompletionEntry | undefined {
1611
1616
let insertText : string | undefined ;
1617
+ let filterText : string | undefined ;
1612
1618
let replacementSpan = getReplacementSpanForContextToken ( replacementToken ) ;
1613
1619
let data : CompletionEntryData | undefined ;
1614
1620
let isSnippet : true | undefined ;
@@ -1682,12 +1688,16 @@ function createCompletionEntry(
1682
1688
completionKind === CompletionKind . MemberLike &&
1683
1689
isClassLikeMemberCompletion ( symbol , location , sourceFile ) ) {
1684
1690
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
1691
1701
}
1692
1702
}
1693
1703
@@ -1755,6 +1765,7 @@ function createCompletionEntry(
1755
1765
hasAction : hasAction ? true : undefined ,
1756
1766
isRecommended : isRecommendedCompletionMatch ( symbol , recommendedCompletion , typeChecker ) || undefined ,
1757
1767
insertText,
1768
+ filterText,
1758
1769
replacementSpan,
1759
1770
sourceDisplay,
1760
1771
labelDetails,
@@ -1826,15 +1837,15 @@ function getEntryForMemberCompletion(
1826
1837
position : number ,
1827
1838
contextToken : Node | undefined ,
1828
1839
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 {
1830
1841
const classLikeDeclaration = findAncestor ( location , isClassLike ) ;
1831
1842
if ( ! classLikeDeclaration ) {
1832
- return { insertText : name } ;
1843
+ return undefined ; // This should never happen.
1833
1844
}
1834
1845
1835
1846
let isSnippet : true | undefined ;
1836
- let replacementSpan : TextSpan | undefined ;
1837
1847
let insertText : string = name ;
1848
+ const filterText : string = name ;
1838
1849
1839
1850
const checker = program . getTypeChecker ( ) ;
1840
1851
const sourceFile = location . getSourceFile ( ) ;
@@ -1863,11 +1874,11 @@ function getEntryForMemberCompletion(
1863
1874
}
1864
1875
1865
1876
let modifiers = ModifierFlags . None ;
1877
+ const { modifiers : presentModifiers , range : eraseRange , decorators : presentDecorators } = getPresentModifiers ( contextToken , sourceFile , position ) ;
1866
1878
// Whether the suggested member should be abstract.
1867
1879
// 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 [ ] = [ ] ;
1871
1882
codefix . addNewNodeForMemberSymbol (
1872
1883
symbol ,
1873
1884
classLikeDeclaration ,
@@ -1896,20 +1907,49 @@ function getEntryForMemberCompletion(
1896
1907
// This is needed when we have overloaded signatures,
1897
1908
// so this callback will be called for multiple nodes/signatures,
1898
1909
// 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 ;
1900
1911
}
1901
1912
node = factory . updateModifiers ( node , modifiers ) ;
1902
1913
completionNodes . push ( node ) ;
1903
1914
} ,
1904
1915
body ,
1905
1916
codefix . PreserveOptionalFlags . Property ,
1906
- isAbstract ) ;
1917
+ ! ! isAbstract ) ;
1907
1918
1908
1919
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
+
1909
1950
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.
1913
1953
if ( formatContext ) {
1914
1954
insertText = printer . printAndFormatSnippetList (
1915
1955
format ,
@@ -1925,21 +1965,22 @@ function getEntryForMemberCompletion(
1925
1965
}
1926
1966
}
1927
1967
1928
- return { insertText, isSnippet, importAdder, replacementSpan } ;
1968
+ return { insertText, filterText , isSnippet, importAdder, eraseRange } ;
1929
1969
}
1930
1970
1931
1971
function getPresentModifiers (
1932
1972
contextToken : Node | undefined ,
1933
1973
sourceFile : SourceFile ,
1934
- position : number ) : { modifiers : ModifierFlags , span ?: TextSpan } {
1974
+ position : number ) : { modifiers : ModifierFlags , decorators ?: Decorator [ ] , range ?: TextRange } {
1935
1975
if ( ! contextToken ||
1936
1976
getLineAndCharacterOfPosition ( sourceFile , position ) . line
1937
1977
> getLineAndCharacterOfPosition ( sourceFile , contextToken . getEnd ( ) ) . line ) {
1938
1978
return { modifiers : ModifierFlags . None } ;
1939
1979
}
1940
1980
let modifiers = ModifierFlags . None ;
1941
- let span ;
1981
+ let decorators : Decorator [ ] | undefined ;
1942
1982
let contextMod ;
1983
+ const range : TextRange = { pos : position , end : position } ;
1943
1984
/*
1944
1985
Cases supported:
1945
1986
In
@@ -1959,15 +2000,19 @@ function getPresentModifiers(
1959
2000
`location.parent` is property declaration ``protected override m``,
1960
2001
`location.parent.parent` is class declaration ``class C { ... }``.
1961
2002
*/
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 ) {
1967
2004
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
+ }
1969
2014
}
1970
- return { modifiers, span } ;
2015
+ return { modifiers, decorators , range : range . pos !== position ? range : undefined } ;
1971
2016
}
1972
2017
1973
2018
function isModifierLike ( node : Node ) : ModifierSyntaxKind | undefined {
@@ -2770,7 +2815,7 @@ function getCompletionEntryCodeActionsAndSourceDisplay(
2770
2815
}
2771
2816
2772
2817
if ( source === CompletionSource . ClassMemberSnippet ) {
2773
- const { importAdder } = getEntryForMemberCompletion (
2818
+ const { importAdder, eraseRange } = getEntryForMemberCompletion (
2774
2819
host ,
2775
2820
program ,
2776
2821
compilerOptions ,
@@ -2780,11 +2825,18 @@ function getCompletionEntryCodeActionsAndSourceDisplay(
2780
2825
location ,
2781
2826
position ,
2782
2827
contextToken ,
2783
- formatContext ) ;
2784
- if ( importAdder ) {
2828
+ formatContext ) ! ;
2829
+ if ( importAdder || eraseRange ) {
2785
2830
const changes = textChanges . ChangeTracker . with (
2786
2831
{ 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
+ } ) ;
2788
2840
return {
2789
2841
sourceDisplay : undefined ,
2790
2842
codeActions : [ {
0 commit comments