@@ -16,17 +16,20 @@ const minTypeScriptVersion = API.fromVersionString(`${VersionRequirement.major}.
1616
1717export function register ( selector : vscode . DocumentSelector , client : ITypeScriptServiceClient ) {
1818 return new VersionDependentRegistration ( client , minTypeScriptVersion , ( ) => {
19- const provider = new SemanticTokensProvider ( client ) ;
20- return vscode . languages . registerSemanticTokensProvider ( selector , provider , provider . getLegend ( ) ) ;
19+ const provider = new DocumentSemanticTokensProvider ( client ) ;
20+ return vscode . Disposable . from (
21+ vscode . languages . registerDocumentSemanticTokensProvider ( selector , provider , provider . getLegend ( ) ) ,
22+ vscode . languages . registerDocumentRangeSemanticTokensProvider ( selector , provider , provider . getLegend ( ) ) ,
23+ ) ;
2124 } ) ;
2225}
2326
2427/**
25- * Prototype of a SemanticTokensProvider , relying on the experimental `encodedSemanticClassifications-full` request from the TypeScript server.
28+ * Prototype of a DocumentSemanticTokensProvider , relying on the experimental `encodedSemanticClassifications-full` request from the TypeScript server.
2629 * As the results retured by the TypeScript server are limited, we also add a Typescript plugin (typescript-vscode-sh-plugin) to enrich the returned token.
2730 * See https://github.com/aeschli/typescript-vscode-sh-plugin.
2831 */
29- class SemanticTokensProvider implements vscode . SemanticTokensProvider {
32+ class DocumentSemanticTokensProvider implements vscode . DocumentSemanticTokensProvider , vscode . DocumentRangeSemanticTokensProvider {
3033
3134 constructor ( private readonly client : ITypeScriptServiceClient ) {
3235 }
@@ -41,68 +44,65 @@ class SemanticTokensProvider implements vscode.SemanticTokensProvider {
4144 return new vscode . SemanticTokensLegend ( tokenTypes , tokenModifiers ) ;
4245 }
4346
44- async provideSemanticTokens ( document : vscode . TextDocument , options : vscode . SemanticTokensRequestOptions , token : vscode . CancellationToken ) : Promise < vscode . SemanticTokens | null > {
47+ async provideDocumentSemanticTokens ( document : vscode . TextDocument , token : vscode . CancellationToken ) : Promise < vscode . SemanticTokens | null > {
4548 const file = this . client . toOpenedFilePath ( document ) ;
4649 if ( ! file ) {
4750 return null ;
4851 }
52+ return this . _provideSemanticTokens ( document , { file, start : 0 , length : document . getText ( ) . length } , token ) ;
53+ }
4954
50- const versionBeforeRequest = document . version ;
51-
52- const allTokenSpans : number [ ] [ ] = [ ] ;
53-
54- let requestArgs : ExperimentalProtocol . EncodedSemanticClassificationsRequestArgs [ ] = [ ] ;
55- if ( options . ranges ) {
56- requestArgs = options . ranges . map ( r => { const start = document . offsetAt ( r . start ) ; const length = document . offsetAt ( r . end ) - start ; return { file, start, length } ; } ) ;
57- requestArgs = requestArgs . sort ( ( a1 , a2 ) => a1 . start - a2 . start ) ;
58- } else {
59- requestArgs = [ { file, start : 0 , length : document . getText ( ) . length } ] ; // full document
55+ async provideDocumentRangeSemanticTokens ( document : vscode . TextDocument , range : vscode . Range , token : vscode . CancellationToken ) : Promise < vscode . SemanticTokens | null > {
56+ const file = this . client . toOpenedFilePath ( document ) ;
57+ if ( ! file ) {
58+ return null ;
6059 }
61- for ( const requestArg of requestArgs ) {
62- const response = await ( this . client as ExperimentalProtocol . IExtendedTypeScriptServiceClient ) . execute ( 'encodedSemanticClassifications-full' , requestArg , token ) ;
63- if ( response . type === 'response' && response . body ) {
64- allTokenSpans . push ( response . body . spans ) ;
65- } else {
66- return null ;
67- }
60+ const start = document . offsetAt ( range . start ) ;
61+ const length = document . offsetAt ( range . end ) - start ;
62+ return this . _provideSemanticTokens ( document , { file, start, length } , token ) ;
63+ }
64+
65+ async _provideSemanticTokens ( document : vscode . TextDocument , requestArg : ExperimentalProtocol . EncodedSemanticClassificationsRequestArgs , token : vscode . CancellationToken ) : Promise < vscode . SemanticTokens | null > {
66+ const file = this . client . toOpenedFilePath ( document ) ;
67+ if ( ! file ) {
68+ return null ;
6869 }
6970
70- const versionAfterRequest = document . version ;
71- if ( versionBeforeRequest !== versionAfterRequest ) {
72- // A new request will come in soon...
71+ const response = await ( this . client as ExperimentalProtocol . IExtendedTypeScriptServiceClient ) . execute ( 'encodedSemanticClassifications-full' , requestArg , token ) ;
72+ if ( response . type !== 'response' || ! response . body ) {
7373 return null ;
7474 }
7575
76+ const tokenSpan = response . body . spans ;
77+
7678 const builder = new vscode . SemanticTokensBuilder ( ) ;
77- for ( const tokenSpan of allTokenSpans ) {
78- let i = 0 ;
79- while ( i < tokenSpan . length ) {
80- const offset = tokenSpan [ i ++ ] ;
81- const length = tokenSpan [ i ++ ] ;
82- const tsClassification = tokenSpan [ i ++ ] ;
83-
84- let tokenModifiers = 0 ;
85- let tokenType = getTokenTypeFromClassification ( tsClassification ) ;
86- if ( tokenType !== undefined ) {
87- // it's a classification as returned by the typescript-vscode-sh-plugin
88- tokenModifiers = getTokenModifierFromClassification ( tsClassification ) ;
89- } else {
90- // typescript-vscode-sh-plugin is not present
91- tokenType = tokenTypeMap [ tsClassification ] ;
92- if ( tokenType === undefined ) {
93- continue ;
94- }
79+ let i = 0 ;
80+ while ( i < tokenSpan . length ) {
81+ const offset = tokenSpan [ i ++ ] ;
82+ const length = tokenSpan [ i ++ ] ;
83+ const tsClassification = tokenSpan [ i ++ ] ;
84+
85+ let tokenModifiers = 0 ;
86+ let tokenType = getTokenTypeFromClassification ( tsClassification ) ;
87+ if ( tokenType !== undefined ) {
88+ // it's a classification as returned by the typescript-vscode-sh-plugin
89+ tokenModifiers = getTokenModifierFromClassification ( tsClassification ) ;
90+ } else {
91+ // typescript-vscode-sh-plugin is not present
92+ tokenType = tokenTypeMap [ tsClassification ] ;
93+ if ( tokenType === undefined ) {
94+ continue ;
9595 }
96+ }
9697
97- // we can use the document's range conversion methods because the result is at the same version as the document
98- const startPos = document . positionAt ( offset ) ;
99- const endPos = document . positionAt ( offset + length ) ;
98+ // we can use the document's range conversion methods because the result is at the same version as the document
99+ const startPos = document . positionAt ( offset ) ;
100+ const endPos = document . positionAt ( offset + length ) ;
100101
101- for ( let line = startPos . line ; line <= endPos . line ; line ++ ) {
102- const startCharacter = ( line === startPos . line ? startPos . character : 0 ) ;
103- const endCharacter = ( line === endPos . line ? endPos . character : document . lineAt ( line ) . text . length ) ;
104- builder . push ( line , startCharacter , endCharacter - startCharacter , tokenType , tokenModifiers ) ;
105- }
102+ for ( let line = startPos . line ; line <= endPos . line ; line ++ ) {
103+ const startCharacter = ( line === startPos . line ? startPos . character : 0 ) ;
104+ const endCharacter = ( line === endPos . line ? endPos . character : document . lineAt ( line ) . text . length ) ;
105+ builder . push ( line , startCharacter , endCharacter - startCharacter , tokenType , tokenModifiers ) ;
106106 }
107107 }
108108 return new vscode . SemanticTokens ( builder . build ( ) ) ;
0 commit comments