diff options
Diffstat (limited to 'platform/darwin/scripts/generate-style-code.js')
-rw-r--r-- | platform/darwin/scripts/generate-style-code.js | 327 |
1 files changed, 248 insertions, 79 deletions
diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js index 6606c956f2..3998246580 100644 --- a/platform/darwin/scripts/generate-style-code.js +++ b/platform/darwin/scripts/generate-style-code.js @@ -55,17 +55,38 @@ _.forOwn(cocoaConventions, function (properties, kind) { }) }); +String.prototype.wrap = function (cols, indent) { + let wrapRe = new RegExp(`(.{1,${cols - indent}})(?: +|\n|$)`, "gm"); + return this.replace(wrapRe, "$1\n").replace(/\s+$/, "").indent(indent); +}; + +String.prototype.indent = function (cols) { + return this.replace(/^|\n/g, "$&" + " ".repeat(cols)); +}; + +global.camelize = function (str) { + return str.replace(/(?:^|-)(.)/g, function (_, x) { + return x.toUpperCase(); + }); +}; + +global.camelizeWithLeadingLowercase = function (str) { + return str.replace(/-(.)/g, function (_, x) { + return x.toUpperCase(); + }); +}; + global.objCName = function (property) { return camelizeWithLeadingLowercase(property.name); -} +}; global.objCGetter = function (property) { return camelizeWithLeadingLowercase(property.getter || property.name); -} +}; global.objCType = function (layerType, propertyName) { return `${prefix}${camelize(propertyName)}`; -} +}; global.arrayType = function (property) { return property.type === 'array' ? originalPropertyName(property).split('-').pop() : false; @@ -74,7 +95,87 @@ global.arrayType = function (property) { global.testImplementation = function (property, layerType, isFunction) { let helperMsg = testHelperMessage(property, layerType, isFunction); return `layer.${objCName(property)} = [MGLRuntimeStylingHelper ${helperMsg}];`; -} +}; + +global.objCTestValue = function (property, layerType, indent) { + let propertyName = originalPropertyName(property); + switch (property.type) { + case 'boolean': + return property.default ? '@NO' : '@YES'; + case 'number': + return '@0xff'; + case 'string': + return `@"${_.startCase(propertyName)}"`; + case 'enum': + let type = objCType(layerType, property.name); + let value = `${type}${camelize(_.last(_.keys(property.values)))}`; + return `[NSValue valueWith${type}:${value}]`; + case 'color': + return '[MGLColor redColor]'; + case 'array': + switch (arrayType(property)) { + case 'dasharray': + return '@[@1, @2]'; + case 'font': + return `@[@"${_.startCase(propertyName)}", @"${_.startCase(_.reverse(propertyName.split('')).join(''))}"]`; + case 'padding': { + let iosValue = '[NSValue valueWithUIEdgeInsets:UIEdgeInsetsMake(1, 1, 1, 1)]'.indent(indent * 4); + let macosValue = '[NSValue valueWithEdgeInsets:NSEdgeInsetsMake(1, 1, 1, 1)]'.indent(indent * 4); + return `\n#if TARGET_OS_IPHONE\n${iosValue}\n#else\n${macosValue}\n#endif\n${''.indent((indent - 1) * 4)}`; + } + case 'offset': + case 'translate': + let iosValue = '[NSValue valueWithCGVector:CGVectorMake(1, 1)]'.indent(indent * 4); + let macosValue = '[NSValue valueWithMGLVector:CGVectorMake(1, -1)]'.indent(indent * 4); + return `\n#if TARGET_OS_IPHONE\n${iosValue}\n#else\n${macosValue}\n#endif\n${''.indent((indent - 1) * 4)}`; + default: + throw new Error(`unknown array type for ${property.name}`); + } + default: + throw new Error(`unknown type for ${property.name}`); + } +}; + +global.mbglTestValue = function (property, layerType) { + let propertyName = originalPropertyName(property); + switch (property.type) { + case 'boolean': + return property.default ? 'false' : 'true'; + case 'number': + return '0xff'; + case 'string': + return `"${_.startCase(propertyName)}"`; + case 'enum': { + let type = camelize(originalPropertyName(property)); + if (/-translate-anchor$/.test(originalPropertyName(property))) { + type = 'TranslateAnchor'; + } + if (/-(rotation|pitch)-alignment$/.test(originalPropertyName(property))) { + type = 'Alignment'; + } + let value = camelize(_.last(_.keys(property.values))); + return `mbgl::style::${type}Type::${value}`; + } + case 'color': + return '{ 1, 0, 0, 1 }'; + case 'array': + switch (arrayType(property)) { + case 'dasharray': + return '{1, 2}'; + case 'font': + return `{ "${_.startCase(propertyName)}", "${_.startCase(_.reverse(propertyName.split('')).join(''))}" }`; + case 'padding': + return '{ 1, 1, 1, 1 }'; + case 'offset': + case 'translate': + return '{ 1, 1 }'; + default: + throw new Error(`unknown array type for ${property.name}`); + } + default: + throw new Error(`unknown type for ${property.name}`); + } +}; global.testGetterImplementation = function (property, layerType, isFunction) { let helperMsg = testHelperMessage(property, layerType, isFunction); @@ -87,7 +188,7 @@ global.testGetterImplementation = function (property, layerType, isFunction) { XCTAssertEqualObjects(gLayer.${objCName(property)}, ${value});`; } return `XCTAssertEqualObjects(gLayer.${objCName(property)}, ${value});`; -} +}; global.testHelperMessage = function (property, layerType, isFunction) { let fnSuffix = isFunction ? 'Function' : ''; @@ -123,7 +224,7 @@ global.testHelperMessage = function (property, layerType, isFunction) { } }; -global.propertyDoc = function (propertyName, property, layerType) { +global.propertyDoc = function (propertyName, property, layerType, kind) { // Match references to other property names & values. // Requires the format 'When `foo` is set to `bar`,'. let doc = property.doc.replace(/`([^`]+?)` is set to `([^`]+?)`/g, function (m, peerPropertyName, propertyValue, offset, str) { @@ -141,7 +242,7 @@ global.propertyDoc = function (propertyName, property, layerType) { // Requires symbols to be surrounded by backticks. doc = doc.replace(/`(.+?)`/g, function (m, symbol, offset, str) { if ('values' in property && Object.keys(property.values).indexOf(symbol) !== -1) { - let objCType = objCType(layerType, property.name); + let objCType = global.objCType(layerType, property.name); return '`' + `${objCType}${camelize(symbol)}` + '`'; } if (str.substr(offset - 4, 3) !== 'CSS') { @@ -154,11 +255,33 @@ global.propertyDoc = function (propertyName, property, layerType) { if (!property.units.match(/s$/)) { property.units += 's'; } - doc += ` - - This property is measured in ${property.units}.`; + doc += `\n\nThis property is measured in ${property.units}.`; + } + doc = doc.replace(/(p)ixel/gi, '$1oint').replace(/(\d)px\b/g, '$1pt'); + if (kind !== 'enum') { + if ('default' in property) { + doc += `\n\nThe default value of this property is ${propertyDefault(property, layerType)}.`; + if (!property.required) { + doc += ' Set this property to `nil` to reset it to the default value.'; + } + } + if ('requires' in property) { + doc += '\n\n' + propertyReqs(property, spec[`${kind}_${layerType}`], layerType); + } + if ('original' in property) { + let anchor; + switch (kind) { + case 'layout': + anchor = `layout-${layerType}-${property.original}`; + break; + case 'paint': + anchor = `paint-${property.original}`; + break; + } + doc += `\n\nThis attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#${anchor}"><code>${property.original}</code></a> layout property in the Mapbox Style Specification.`; + } } - return doc.replace(/(p)ixel/gi, '$1oint').replace(/(\d)px\b/g, '$1pt'); + return doc; }; global.propertyReqs = function (property, propertiesByName, type) { @@ -217,30 +340,32 @@ global.describeValue = function (value, property, layerType) { throw new Error(`unrecognized color format in default value of ${property.name}`); } if (color.r === 0 && color.g === 0 && color.b === 0 && color.a === 0) { - return '`MGLColor.clearColor`'; + return '`UIColor.clearColor`'; } if (color.r === 0 && color.g === 0 && color.b === 0 && color.a === 1) { - return '`MGLColor.blackColor`'; + return '`UIColor.blackColor`'; } if (color.r === 1 && color.g === 1 && color.b === 1 && color.a === 1) { - return '`MGLColor.whiteColor`'; + return '`UIColor.whiteColor`'; } - return 'an `MGLColor`' + ` object whose RGB value is ${color.r}, ${color.g}, ${color.b} and whose alpha value is ${color.a}`; + return 'a `UIColor`' + ` object whose RGB value is ${color.r}, ${color.g}, ${color.b} and whose alpha value is ${color.a}`; case 'array': let units = property.units || ''; if (units) { units = ` ${units}`.replace(/pixel/, 'point'); } - if (property.name.indexOf('padding') !== -1) { - if (value[0] === 0 && value[1] === 0 && value[2] === 0 && value[3] === 0) { - return 'an `NSValue` object containing `NSEdgeInsetsZero` or `UIEdgeInsetsZero`'; - } - return 'an `NSValue` object containing an `NSEdgeInsets` or `UIEdgeInsets` struct set to' + ` ${value[0]}${units} on the top, ${value[3]}${units} on the left, ${value[2]}${units} on the bottom, and ${value[1]}${units} on the right`; - } - if (property.name.indexOf('offset') !== -1 || property.name.indexOf('translate') !== -1) { - return 'an `NSValue` object containing a `CGVector` struct set to' + ` ${value[0]}${units} from the left and ${value[1]}${units} from the top`; + switch (arrayType(property)) { + case 'padding': + if (value[0] === 0 && value[1] === 0 && value[2] === 0 && value[3] === 0) { + return 'an `NSValue` object containing `UIEdgeInsetsZero`'; + } + return 'an `NSValue` object containing a `UIEdgeInsets` struct set to' + ` ${value[0]}${units} on the top, ${value[3]}${units} on the left, ${value[2]}${units} on the bottom, and ${value[1]}${units} on the right`; + case 'offset': + case 'translate': + return 'an `NSValue` object containing a `CGVector` struct set to' + ` ${value[0]}${units} rightward and ${value[1]}${units} downward`; + default: + return 'the array `' + value.join('`, `') + '`'; } - return 'the array `' + value.join('`, `') + '`'; default: throw new Error(`unknown type for ${property.name}`); } @@ -252,7 +377,7 @@ global.propertyDefault = function (property, layerType) { global.originalPropertyName = function (property) { return property.original || property.name; -} +}; global.propertyType = function (property) { switch (property.type) { @@ -317,100 +442,144 @@ global.valueTransformerArguments = function (property) { } }; +global.mbglType = function(property) { + switch (property.type) { + case 'boolean': + return 'bool'; + case 'number': + return 'float'; + case 'string': + return 'std::string'; + case 'enum': { + let type = camelize(originalPropertyName(property)); + if (/-translate-anchor$/.test(originalPropertyName(property))) { + type = 'TranslateAnchor'; + } + if (/-(rotation|pitch)-alignment$/.test(originalPropertyName(property))) { + type = 'Alignment'; + } + return `mbgl::style::${type}Type`; + } + case 'color': + return 'mbgl::Color'; + case 'array': + switch (arrayType(property)) { + case 'dasharray': + return 'std::vector<float>'; + case 'font': + return 'std::vector<std::string>'; + case 'padding': + return 'std::array<float, 4>'; + case 'offset': + case 'translate': + return 'std::array<float, 2>'; + default: + throw new Error(`unknown array type for ${property.name}`); + } + default: + throw new Error(`unknown type for ${property.name}`); + } +}; + global.initLayer = function (layerType) { if (layerType == "background") { return `_layer = new mbgl::style::${camelize(layerType)}Layer(identifier.UTF8String);` } else { return `_layer = new mbgl::style::${camelize(layerType)}Layer(identifier.UTF8String, source.identifier.UTF8String);` } -} +}; global.setSourceLayer = function() { - return `_layer->setSourceLayer(sourceLayer.UTF8String);` -} - -global.mbglType = function(property) { - let mbglType = camelize(originalPropertyName(property)) + 'Type'; - if (/-translate-anchor$/.test(originalPropertyName(property))) { - mbglType = 'TranslateAnchorType'; - } - if (/-(rotation|pitch)-alignment$/.test(originalPropertyName(property))) { - mbglType = 'AlignmentType'; - } - return mbglType; -} + return `_layer->setSourceLayer(sourceLayer.UTF8String);` +}; const layerH = ejs.compile(fs.readFileSync('platform/darwin/src/MGLStyleLayer.h.ejs', 'utf8'), { strict: true }); const layerM = ejs.compile(fs.readFileSync('platform/darwin/src/MGLStyleLayer.mm.ejs', 'utf8'), { strict: true}); -const testLayers = ejs.compile(fs.readFileSync('platform/darwin/test/MGLStyleLayerTests.m.ejs', 'utf8'), { strict: true}); -const categoryH = ejs.compile(fs.readFileSync('platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.h.ejs', 'utf8'), { strict: true}); -const categoryM = ejs.compile(fs.readFileSync('platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.mm.ejs', 'utf8'), { strict: true}); +const testLayers = ejs.compile(fs.readFileSync('platform/darwin/test/MGLStyleLayerTests.mm.ejs', 'utf8'), { strict: true}); +const guideMD = ejs.compile(fs.readFileSync('platform/darwin/docs/guides/For Style Authors.md.ejs', 'utf8'), { strict: true }); -const layers = Object.keys(spec.layer.type.values).map((type) => { - const layoutProperties = Object.keys(spec[`layout_${type}`]).reduce((memo, name) => { +const layers = _(spec.layer.type.values).map((value, layerType) => { + const layoutProperties = Object.keys(spec[`layout_${layerType}`]).reduce((memo, name) => { if (name !== 'visibility') { - spec[`layout_${type}`][name].name = name; - memo.push(spec[`layout_${type}`][name]); + spec[`layout_${layerType}`][name].name = name; + memo.push(spec[`layout_${layerType}`][name]); } return memo; }, []); - const paintProperties = Object.keys(spec[`paint_${type}`]).reduce((memo, name) => { - spec[`paint_${type}`][name].name = name; - memo.push(spec[`paint_${type}`][name]); + const paintProperties = Object.keys(spec[`paint_${layerType}`]).reduce((memo, name) => { + spec[`paint_${layerType}`][name].name = name; + memo.push(spec[`paint_${layerType}`][name]); return memo; }, []); return { - type: type, + doc: spec.layer.type.values[layerType].doc, + type: layerType, layoutProperties: _.sortBy(layoutProperties, ['name']), paintProperties: _.sortBy(paintProperties, ['name']), - layoutPropertiesByName: spec[`layout_${type}`], - paintPropertiesByName: spec[`paint_${type}`], }; -}); +}).sortBy(['type']).value(); function duplicatePlatformDecls(src) { - // Look for a documentation comment that contains “MGLColor” and the - // subsequent function, method, or property declaration. Try not to match - // greedily. - return src.replace(/(\/\*\*(?:\*[^\/]|[^*])*?\bMGLColor\b[\s\S]*?\*\/)(\s*.+?;)/g, + // Look for a documentation comment that contains “MGLColor” or “UIColor” + // and the subsequent function, method, or property declaration. Try not to + // match greedily. + return src.replace(/(\/\*\*(?:\*[^\/]|[^*])*?\*\/)(\s*[^;]+?\b(?:MGL|NS|UI)(?:Color|EdgeInsets(?:Zero)?)\b[^;]+?;)/g, (match, comment, decl) => { - let iosComment = comment.replace(/\bMGLColor\b/g, 'UIColor') + let macosComment = comment.replace(/\b(?:MGL|UI)(Color|EdgeInsets(?:Zero)?)\b/g, 'NS$1') // Use the correct indefinite article. - .replace(/\b(a)n(\s+`?UIColor)\b/gi, '$1$2'); - let macosComment = comment.replace(/\bMGLColor\b/g, 'NSColor'); + .replace(/\ba(\s+`?NS(?:Color|EdgeInsets))\b/gi, 'an$1'); + let iosDecl = decl.replace(/\bMGL(Color|EdgeInsets)\b/g, 'UI$1'); + let macosDecl = decl.replace(/\b(?:MGL|UI)(Color|EdgeInsets)\b/g, 'NS$1'); return `\ #if TARGET_OS_IPHONE -${iosComment}${decl} +${comment}${iosDecl} +#else +${macosComment}${macosDecl} +#endif`; + }) + // Do the same for CGVector-typed properties. + .replace(/(\/\*\*(?:\*[^\/]|[^*])*?\b(?:CGVector|UIEdgeInsets(?:Zero)?)\b[\s\S]*?\*\/)(\s*.+?;)/g, + (match, comment, decl) => { + let macosComment = comment.replace(/\bdownward\b/g, 'upward') + .replace(/\bUI(EdgeInsets(?:Zero)?)\b/g, 'NS$1'); + return `\ +#if TARGET_OS_IPHONE +${comment}${decl} #else ${macosComment}${decl} #endif`; }); } -var allLayoutProperties = []; -var allPaintProperties = []; -var allTypes = []; +var renamedPropertiesByLayerType = {}; for (var layer of layers) { - allLayoutProperties.push(layer.layoutProperties); - allPaintProperties.push(layer.paintProperties); - allTypes.push(layer.type); - const containsEnumerationProperties = _.filter(layer.layoutProperties, function(property){ return property["type"] === "enum"; }).length || _.filter(layer.paintProperties, function(property){ return property["type"] === "enum"; }).length; - layer.containsEnumerationProperties = containsEnumerationProperties; + layer.properties = _.concat(layer.layoutProperties, layer.paintProperties); + let enumProperties = _.filter(layer.properties, prop => prop.type === 'enum'); + if (enumProperties.length) { + layer.enumProperties = enumProperties; + } + + let renamedProperties = {}; + _.assign(renamedProperties, _.filter(layer.properties, prop => 'original' in prop || 'getter' in prop)); + if (!_.isEmpty(renamedProperties)) { + renamedPropertiesByLayerType[layer.type] = renamedProperties; + } - writeIfModified(`platform/darwin/src/${prefix}${camelize(layer.type)}${suffix}.h`, duplicatePlatformDecls(layerH(layer))); - writeIfModified(`platform/darwin/src/${prefix}${camelize(layer.type)}${suffix}.mm`, layerM(layer)); - writeIfModified(`platform/darwin/test/${prefix}${camelize(layer.type)}${suffix}Tests.m`, testLayers(layer)); + fs.writeFileSync(`platform/darwin/src/${prefix}${camelize(layer.type)}${suffix}.h`, duplicatePlatformDecls(layerH(layer))); + fs.writeFileSync(`platform/darwin/src/${prefix}${camelize(layer.type)}${suffix}.mm`, layerM(layer)); + fs.writeFileSync(`platform/darwin/test/${prefix}${camelize(layer.type)}${suffix}Tests.mm`, testLayers(layer)); } -fs.writeFileSync(`platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.h`, categoryH({ - layoutProperties: _.flatten(allLayoutProperties), - paintProperties: _.flatten(allPaintProperties), - types: allTypes +fs.writeFileSync(`platform/ios/docs/guides/For Style Authors.md`, guideMD({ + os: 'iOS', + renamedProperties: renamedPropertiesByLayerType, + layers: layers, })); -fs.writeFileSync(`platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.mm`, categoryM({ - layoutProperties: _.flatten(allLayoutProperties), - paintProperties: _.flatten(allPaintProperties) +fs.writeFileSync(`platform/macos/docs/guides/For Style Authors.md`, guideMD({ + os: 'macOS', + renamedProperties: renamedPropertiesByLayerType, + layers: layers, })); |