Skip to content

Commit 8deee74

Browse files
authored
Adding OptionsSchema generation (#1937)
* Adding OptionsSchema generation This is to prevent repetitive fields from being copy and pasted improperly. * Move tslint to be after compile * Address PR issues
1 parent d6aa178 commit 8deee74

File tree

7 files changed

+711
-7
lines changed

7 files changed

+711
-7
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ script:
3131
# Build and then run tests
3232
- cd Extension
3333
- npm install
34-
- npm run tslint
3534
- npm run compile
35+
- npm run tslint
3636
# pr-check needs to run before test. test modifies package.json.
3737
- npm run pr-check
3838
- npm run test

Extension/.vscodeignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ CMakeLists.txt
1111
debugAdapters/install.lock*
1212
out/src/Debugger/copyScript.js
1313
tools/**
14+
out/tools/**
1415
notices/**

Extension/gulpfile.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const env = require('gulp-env')
1010
const tslint = require('gulp-tslint');
1111
const mocha = require('gulp-mocha');
1212
const fs = require('fs');
13+
const optionsSchemaGenerator = require('./out/tools/GenerateOptionsSchema');
1314

1415
gulp.task('allTests', () => {
1516
gulp.start('unitTests');
@@ -82,4 +83,8 @@ gulp.task('pr-check', () => {
8283
console.log('Please make sure to not check in package.json that has been rewritten by the extension activation. If you intended to have changes in package.json, please only check-in your changes. If you did not, please run `git checkout -- package.json`.');
8384
process.exit(1);
8485
}
86+
});
87+
88+
gulp.task('generateOptionsSchema', () => {
89+
optionsSchemaGenerator.GenerateOptionsSchema();
8590
});

Extension/package.json

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,7 @@
533533
},
534534
"configurationAttributes": {
535535
"launch": {
536+
"type": "object",
536537
"required": [
537538
"program"
538539
],
@@ -719,8 +720,8 @@
719720
}
720721
},
721722
"logging": {
722-
"type": "object",
723723
"description": "Optional flags to determine what types of messages should be logged to the Debug Console.",
724+
"type": "object",
724725
"default": {},
725726
"properties": {
726727
"exceptions": {
@@ -759,10 +760,10 @@
759760
"description": "When present, this tells the debugger to connect to a remote computer using another executable as a pipe that will relay standard input/output between VS Code and the MI-enabled debugger backend executable (such as gdb).",
760761
"type": "object",
761762
"default": {
762-
"pipeCwd": "${workspaceRoot}",
763+
"pipeCwd": "/usr/bin",
763764
"pipeProgram": "enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'",
764765
"pipeArgs": [],
765-
"debuggerPath": "enter the path for the debugger on the target machine, for example /usr/bin/gdb"
766+
"debuggerPath": "The full path to the debugger on the target machine, for example /usr/bin/gdb."
766767
},
767768
"properties": {
768769
"pipeCwd": {
@@ -801,6 +802,7 @@
801802
}
802803
},
803804
"attach": {
805+
"type": "object",
804806
"required": [
805807
"program",
806808
"processId"
@@ -883,8 +885,8 @@
883885
}
884886
},
885887
"logging": {
886-
"type": "object",
887888
"description": "Optional flags to determine what types of messages should be logged to the Debug Console.",
889+
"type": "object",
888890
"default": {},
889891
"properties": {
890892
"exceptions": {
@@ -923,10 +925,10 @@
923925
"description": "When present, this tells the debugger to connect to a remote computer using another executable as a pipe that will relay standard input/output between VS Code and the MI-enabled debugger backend executable (such as gdb).",
924926
"type": "object",
925927
"default": {
926-
"pipeCwd": "${workspaceRoot}",
928+
"pipeCwd": "/usr/bin",
927929
"pipeProgram": "enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'",
928930
"pipeArgs": [],
929-
"debuggerPath": "enter the path for the debugger on the target machine, for example /usr/bin/gdb"
931+
"debuggerPath": "The full path to the debugger on the target machine, for example /usr/bin/gdb."
930932
},
931933
"properties": {
932934
"pipeCwd": {
@@ -1007,6 +1009,7 @@
10071009
},
10081010
"configurationAttributes": {
10091011
"launch": {
1012+
"type": "object",
10101013
"required": [
10111014
"program",
10121015
"cwd"
@@ -1109,6 +1112,7 @@
11091112
}
11101113
},
11111114
"attach": {
1115+
"type": "object",
11121116
"required": [
11131117
"processId"
11141118
],
@@ -1272,6 +1276,7 @@
12721276
},
12731277
"scripts": {
12741278
"compile": "npm run vscode:prepublish",
1279+
"generateOptionsSchema": "gulp generateOptionsSchema",
12751280
"integrationTests": "gulp integrationTests",
12761281
"postinstall": "node ./node_modules/vscode/bin/install",
12771282
"pretest": "tsc -p ./",
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import * as fs from 'fs';
7+
import * as os from 'os';
8+
9+
function AppendFieldsToObject(reference: any, obj: any) {
10+
11+
// Make sure it is an object type
12+
if (typeof obj == 'object') {
13+
for (let referenceKey in reference) {
14+
// If key exists in original object and is an object.
15+
if (obj.hasOwnProperty(referenceKey)) {
16+
obj[referenceKey] = AppendFieldsToObject(reference[referenceKey], obj[referenceKey]);
17+
} else {
18+
// Does not exist in current object context
19+
obj[referenceKey] = reference[referenceKey];
20+
}
21+
}
22+
}
23+
24+
return obj;
25+
}
26+
27+
// Combines two object's fields, giving the parentDefault a higher precedence.
28+
function MergeDefaults(parentDefault: any, childDefault: any) {
29+
let newDefault: any = {};
30+
31+
for (let attrname in childDefault) {
32+
newDefault[attrname] = childDefault[attrname];
33+
}
34+
35+
for (let attrname in parentDefault) {
36+
newDefault[attrname] = parentDefault[attrname];
37+
}
38+
39+
return newDefault;
40+
}
41+
42+
function UpdateDefaults(object: any, defaults: any) {
43+
if (defaults != null) {
44+
for (let key in object) {
45+
if (object[key].hasOwnProperty('type') && object[key].type === 'object' && object[key].properties !== null) {
46+
object[key].properties = UpdateDefaults(object[key].properties, MergeDefaults(defaults, object[key].default));
47+
} else if (key in defaults) {
48+
object[key].default = defaults[key];
49+
}
50+
}
51+
}
52+
53+
return object;
54+
}
55+
56+
function RefReplace(definitions: any, ref: any): any {
57+
// $ref is formatted as "#/definitions/ObjectName"
58+
let referenceStringArray: string[] = ref['$ref'].split('/');
59+
60+
// Getting "ObjectName"
61+
let referenceName: string = referenceStringArray[referenceStringArray.length - 1];
62+
63+
// Make sure reference has replaced its own $ref fields and hope there are no recursive references.
64+
definitions[referenceName] = ReplaceReferences(definitions, definitions[referenceName]);
65+
66+
// Retrieve ObjectName from definitions. (TODO: Does not retrieve inner objects)
67+
// Need to deep copy, there are no functions in these objects.
68+
let reference: any = JSON.parse(JSON.stringify(definitions[referenceName]));
69+
70+
ref = AppendFieldsToObject(reference, ref);
71+
72+
// Remove $ref field
73+
delete ref['$ref'];
74+
75+
return ref;
76+
}
77+
78+
function ReplaceReferences(definitions: any, objects: any) {
79+
for (let key in objects) {
80+
if (objects[key].hasOwnProperty('$ref')) {
81+
objects[key] = RefReplace(definitions, objects[key]);
82+
}
83+
84+
// Recursively replace references if this object has properties.
85+
if (objects[key].hasOwnProperty('type') && objects[key].type === 'object' && objects[key].properties !== null) {
86+
objects[key].properties = ReplaceReferences(definitions, objects[key].properties);
87+
objects[key].properties = UpdateDefaults(objects[key].properties, objects[key].default);
88+
}
89+
90+
// Recursively replace references if the array has objects in items.
91+
if (objects[key].hasOwnProperty('type') && objects[key].type === "array" && objects[key].items != null && objects[key].items.hasOwnProperty('$ref')) {
92+
objects[key].items = RefReplace(definitions, objects[key].items);
93+
}
94+
}
95+
96+
return objects;
97+
}
98+
99+
function MergeReferences(baseDefinitions: any, additionalDefinitions: any) : void {
100+
for (let key in additionalDefinitions) {
101+
if (baseDefinitions[key]) {
102+
throw `Error: '${key}' defined in multiple schema files.`;
103+
}
104+
baseDefinitions[key] = additionalDefinitions[key];
105+
}
106+
}
107+
108+
export function GenerateOptionsSchema() {
109+
let packageJSON: any = JSON.parse(fs.readFileSync('package.json').toString());
110+
let schemaJSON: any = JSON.parse(fs.readFileSync('tools/OptionsSchema.json').toString());
111+
112+
schemaJSON.definitions = ReplaceReferences(schemaJSON.definitions, schemaJSON.definitions);
113+
114+
// Hard Code adding in configurationAttributes launch and attach.
115+
// cppdbg
116+
packageJSON.contributes.debuggers[0].configurationAttributes.launch = schemaJSON.definitions.CppdbgLaunchOptions;
117+
packageJSON.contributes.debuggers[0].configurationAttributes.attach = schemaJSON.definitions.CppdbgAttachOptions;
118+
119+
// cppvsdbg
120+
packageJSON.contributes.debuggers[1].configurationAttributes.launch = schemaJSON.definitions.CppvsdbgLaunchOptions;
121+
packageJSON.contributes.debuggers[1].configurationAttributes.attach = schemaJSON.definitions.CppvsdbgAttachOptions;
122+
123+
let content = JSON.stringify(packageJSON, null, 2);
124+
if (os.platform() === 'win32') {
125+
content = content.replace(/\n/gm, "\r\n");
126+
}
127+
128+
// We use '\u200b' (unicode zero-length space character) to break VS Code's URL detection regex for URLs that are examples. This process will
129+
// convert that from the readable espace sequence, to just an invisible character. Convert it back to the visible espace sequence.
130+
content = content.replace(/\u200b/gm, "\\u200b");
131+
132+
fs.writeFileSync('package.json', content);
133+
}

0 commit comments

Comments
 (0)