@@ -9,88 +9,135 @@ import * as https from 'https';
99import { ClientRequest } from 'http' ;
1010import * as vscode from 'vscode' ;
1111import * as fs from 'fs' ;
12- import * as util from './common' ;
1312
13+ import * as util from './common' ;
1414import * as Telemetry from './telemetry' ;
15+ import { PersistentState } from './LanguageServer/persistentState' ;
1516
1617const userBucketMax : number = 100 ;
1718const userBucketString : string = "CPP.UserBucket" ;
19+ const localConfigFile : string = "cpptools.json" ;
1820
19- export function activate ( context : vscode . ExtensionContext ) : void {
20- if ( context . globalState . get < number > ( userBucketString , - 1 ) === - 1 ) {
21- let bucket : number = Math . floor ( Math . random ( ) * userBucketMax ) + 1 ; // Range is [1, userBucketMax].
22- context . globalState . update ( userBucketString , bucket ) ;
23- }
21+ interface Settings {
22+ defaultIntelliSenseEngine ?: number ;
23+ recursiveIncludes ?: number ;
24+ }
25+
26+ export class ABTestSettings {
27+ private settings : Settings ;
28+ private intelliSenseEngineDefault : PersistentState < number > ;
29+ private recursiveIncludesDefault : PersistentState < number > ;
30+ private bucket : PersistentState < number > ;
31+
32+ constructor ( ) {
33+ this . intelliSenseEngineDefault = new PersistentState < number > ( "ABTest.1" , 100 ) ;
34+ this . recursiveIncludesDefault = new PersistentState < number > ( "ABTest.2" , 100 ) ;
35+ this . settings = {
36+ defaultIntelliSenseEngine : this . intelliSenseEngineDefault . Value ,
37+ recursiveIncludes : this . recursiveIncludesDefault . Value
38+ } ;
39+ this . bucket = new PersistentState < number > ( userBucketString , - 1 ) ;
40+ if ( this . bucket . Value === - 1 ) {
41+ this . bucket . Value = Math . floor ( Math . random ( ) * userBucketMax ) + 1 ; // Range is [1, userBucketMax].
42+ }
43+
44+ this . updateSettingsAsync ( ) . then ( ( ) => {
45+ // Redownload cpptools.json after initialization so it's not blocked.
46+ // It'll be used the next time the extension reloads.
47+ this . downloadCpptoolsJsonPkgAsync ( ) ;
48+ } ) ;
2449
25- setInterval ( ( ) => {
2650 // Redownload occasionally to prevent an extra reload during long sessions.
27- downloadCpptoolsJsonPkg ( ) ;
28- } , 30 * 60 * 1000 ) ; // 30 minutes.
29- }
51+ setInterval ( ( ) => { this . downloadCpptoolsJsonPkgAsync ( ) ; } , 30 * 60 * 1000 ) ; // 30 minutes.
52+ }
53+
54+ public get UseDefaultIntelliSenseEngine ( ) : boolean {
55+ return this . settings . defaultIntelliSenseEngine ? this . settings . defaultIntelliSenseEngine >= this . bucket . Value : true ;
56+ }
57+
58+ public get UseRecursiveIncludes ( ) : boolean {
59+ return this . settings . recursiveIncludes ? this . settings . recursiveIncludes >= this . bucket . Value : true ;
60+ }
3061
31- // NOTE: Code is copied from DownloadPackage in packageManager.ts, but with ~75% fewer lines.
32- function downloadCpptoolsJson ( urlString ) : Promise < void > {
33- return new Promise < void > ( ( resolve , reject ) => {
34- let parsedUrl : url . Url = url . parse ( urlString ) ;
35- let request : ClientRequest = https . request ( {
36- host : parsedUrl . host ,
37- path : parsedUrl . path ,
38- agent : util . getHttpsProxyAgent ( ) ,
39- rejectUnauthorized : vscode . workspace . getConfiguration ( ) . get ( "http.proxyStrictSSL" , true )
40- } , ( response ) => {
41- if ( response . statusCode === 301 || response . statusCode === 302 ) {
42- let redirectUrl : string | string [ ] ;
43- if ( typeof response . headers . location === "string" ) {
44- redirectUrl = response . headers . location ;
45- } else {
46- redirectUrl = response . headers . location [ 0 ] ;
62+ private async updateSettingsAsync ( ) : Promise < void > {
63+ const cpptoolsJsonFile : string = util . getExtensionFilePath ( localConfigFile ) ;
64+
65+ try {
66+ const exists : boolean = await util . checkFileExists ( cpptoolsJsonFile ) ;
67+ if ( exists ) {
68+ const fileContent : string = await util . readFileText ( cpptoolsJsonFile ) ;
69+ let newSettings : Settings = < Settings > JSON . parse ( fileContent ) ;
70+ if ( newSettings . defaultIntelliSenseEngine ) {
71+ this . intelliSenseEngineDefault . Value = newSettings . defaultIntelliSenseEngine ;
4772 }
48- return resolve ( downloadCpptoolsJson ( redirectUrl ) ) ; // Redirect - download from new location
49- }
50- if ( response . statusCode !== 200 ) {
51- return reject ( ) ;
73+ if ( newSettings . recursiveIncludes ) {
74+ this . recursiveIncludesDefault . Value = newSettings . recursiveIncludes ;
75+ }
76+ this . settings = {
77+ defaultIntelliSenseEngine : this . intelliSenseEngineDefault . Value ,
78+ recursiveIncludes : this . recursiveIncludesDefault . Value
79+ } ;
5280 }
53- let downloadedBytes = 0 ; // tslint:disable-line
54- let cppToolsJsonFile : fs . WriteStream = fs . createWriteStream ( util . getExtensionFilePath ( "cpptools.json" ) ) ;
55- response . on ( 'data' , ( data ) => { downloadedBytes += data . length ; } ) ;
56- response . on ( 'end' , ( ) => { cppToolsJsonFile . close ( ) ; } ) ;
57- cppToolsJsonFile . on ( 'close' , ( ) => { resolve ( ) ; } ) ;
58- response . on ( 'error' , ( error ) => { reject ( ) ; } ) ;
59- response . pipe ( cppToolsJsonFile , { end : false } ) ;
60- } ) ;
61- request . on ( 'error' , ( error ) => { reject ( ) ; } ) ;
62- request . end ( ) ;
63- } ) ;
64- }
81+ } catch ( error ) {
82+ // Ignore any cpptoolsJsonFile errors
83+ }
84+ }
6585
66- export function downloadCpptoolsJsonPkg ( ) : Promise < void > {
67- let hasError : boolean = false ;
68- let telemetryProperties : { [ key : string ] : string } = { } ;
69- return downloadCpptoolsJson ( "https://go.microsoft.com/fwlink/?linkid=852750" )
70- . catch ( ( error ) => {
71- // More specific error info is not likely to be helpful, and we get detailed download data from the initial install.
72- hasError = true ;
73- } )
74- . then ( ( ) => {
75- telemetryProperties [ 'success' ] = ( ! hasError ) . toString ( ) ;
76- Telemetry . logDebuggerEvent ( "cpptoolsJsonDownload" , telemetryProperties ) ;
86+ // NOTE: Code is copied from DownloadPackage in packageManager.ts, but with ~75% fewer lines.
87+ private downloadCpptoolsJsonAsync ( urlString ) : Promise < void > {
88+ return new Promise < void > ( ( resolve , reject ) => {
89+ let parsedUrl : url . Url = url . parse ( urlString ) ;
90+ let request : ClientRequest = https . request ( {
91+ host : parsedUrl . host ,
92+ path : parsedUrl . path ,
93+ agent : util . getHttpsProxyAgent ( ) ,
94+ rejectUnauthorized : vscode . workspace . getConfiguration ( ) . get ( "http.proxyStrictSSL" , true )
95+ } , ( response ) => {
96+ if ( response . statusCode === 301 || response . statusCode === 302 ) {
97+ let redirectUrl : string | string [ ] ;
98+ if ( typeof response . headers . location === "string" ) {
99+ redirectUrl = response . headers . location ;
100+ } else {
101+ redirectUrl = response . headers . location [ 0 ] ;
102+ }
103+ return resolve ( this . downloadCpptoolsJsonAsync ( redirectUrl ) ) ; // Redirect - download from new location
104+ }
105+ if ( response . statusCode !== 200 ) {
106+ return reject ( ) ;
107+ }
108+ let downloadedBytes = 0 ; // tslint:disable-line
109+ let cppToolsJsonFile : fs . WriteStream = fs . createWriteStream ( util . getExtensionFilePath ( localConfigFile ) ) ;
110+ response . on ( 'data' , ( data ) => { downloadedBytes += data . length ; } ) ;
111+ response . on ( 'end' , ( ) => { cppToolsJsonFile . close ( ) ; } ) ;
112+ cppToolsJsonFile . on ( 'close' , ( ) => { resolve ( ) ; this . updateSettingsAsync ( ) ; } ) ;
113+ response . on ( 'error' , ( error ) => { reject ( ) ; } ) ;
114+ response . pipe ( cppToolsJsonFile , { end : false } ) ;
115+ } ) ;
116+ request . on ( 'error' , ( error ) => { reject ( ) ; } ) ;
117+ request . end ( ) ;
77118 } ) ;
119+ }
120+
121+ private downloadCpptoolsJsonPkgAsync ( ) : Promise < void > {
122+ let hasError : boolean = false ;
123+ let telemetryProperties : { [ key : string ] : string } = { } ;
124+ return this . downloadCpptoolsJsonAsync ( "https://go.microsoft.com/fwlink/?linkid=852750" )
125+ . catch ( ( error ) => {
126+ // More specific error info is not likely to be helpful, and we get detailed download data from the initial install.
127+ hasError = true ;
128+ } )
129+ . then ( ( ) => {
130+ telemetryProperties [ 'success' ] = ( ! hasError ) . toString ( ) ;
131+ Telemetry . logDebuggerEvent ( "cpptoolsJsonDownload" , telemetryProperties ) ;
132+ } ) ;
133+ }
78134}
79135
80- export function processCpptoolsJson ( cpptoolsString : string ) : Promise < void > {
81- let cpptoolsObject : any = JSON . parse ( cpptoolsString ) ;
82- let intelliSenseEnginePercentage : number = cpptoolsObject . intelliSenseEngine_default_percentage ;
83- let packageJson : any = util . getRawPackageJson ( ) ;
136+ let settings : ABTestSettings ;
84137
85- if ( ! packageJson . extensionFolderPath . includes ( ".vscode-insiders" ) ) {
86- let prevIntelliSenseEngineDefault : any = packageJson . contributes . configuration . properties [ "C_Cpp.intelliSenseEngine" ] . default ;
87- if ( util . extensionContext . globalState . get < number > ( userBucketString , userBucketMax + 1 ) <= intelliSenseEnginePercentage ) {
88- packageJson . contributes . configuration . properties [ "C_Cpp.intelliSenseEngine" ] . default = "Default" ;
89- } else {
90- packageJson . contributes . configuration . properties [ "C_Cpp.intelliSenseEngine" ] . default = "Tag Parser" ;
91- }
92- if ( prevIntelliSenseEngineDefault !== packageJson . contributes . configuration . properties [ "C_Cpp.intelliSenseEngine" ] . default ) {
93- return util . writeFileText ( util . getPackageJsonPath ( ) , util . stringifyPackageJson ( packageJson ) ) ;
94- }
138+ export function getABTestSettings ( ) : ABTestSettings {
139+ if ( ! settings ) {
140+ settings = new ABTestSettings ( ) ;
95141 }
96- }
142+ return settings ;
143+ }
0 commit comments