diff --git a/extras/grammar/HCL.json-tmLanguage b/extras/grammar/HCL.json-tmLanguage new file mode 100755 index 0000000..1025965 --- /dev/null +++ b/extras/grammar/HCL.json-tmLanguage @@ -0,0 +1,97 @@ +{ + "fileTypes": [ + "hcl", + "hcldec" + ], + "name": "HCL", + "patterns": [ + { + "begin": "#|//", + "captures": { + "0": { + "name": "punctuation.definition.comment.hcl" + } + }, + "comment": "Comments", + "end": "$\\n?", + "name": "comment.line.hcl" + }, + { + "begin": "/\\*", + "captures": { + "0": { + "name": "punctuation.definition.comment.hcl" + } + }, + "comment": "Block comments", + "end": "\\*/", + "name": "comment.block.hcl" + }, + { + "begin": "{", + "beginCaptures": { + "0": { + "name": "punctuation.definition.block.hcl" + } + }, + "comment": "Nested Blocks", + "end": "}", + "endCaptures": { + "0": { + "name": "punctuation.definition.block.hcl" + } + }, + "name": "meta.block.hcl", + "patterns": [ + { + "include": "$self" + } + ] + }, + { + "captures": { + "1": { + "name": "string.hcl punctuation.definition.string.begin.hcl" + }, + "2": { + "name": "string.value.hcl" + }, + "3": { + "name": "string.hcl punctuation.definition.string.end.hcl" + } + }, + "comment": "Quoted Block Labels", + "match": "(\")([^\"]+)(\")" + }, + { + "begin": "(\\w+)\\s*(=)\\s*", + "beginCaptures": { + "1": { + "name": "variable.other.assignment.hcl" + }, + "2": { + "name": "keyword.operator.hcl" + } + }, + "comment": "Attribute Definitions", + "end": "$", + "name": "meta.attr.hcl", + "patterns": [ + { + "include": "source.hclexpr" + } + ] + }, + { + "captures": { + "0": { + "name": "keyword.other.hcl" + } + }, + "comment": "Keywords", + "match": "[-\\w]+" + } + ], + "scopeName": "source.hcl", + "uuid": "55e8075d-e2e3-4e44-8446-744a9860e476" +} \ No newline at end of file diff --git a/extras/grammar/HCL.tmLanguage b/extras/grammar/HCL.tmLanguage new file mode 100755 index 0000000..d65d37f --- /dev/null +++ b/extras/grammar/HCL.tmLanguage @@ -0,0 +1,157 @@ + + + + + fileTypes + + hcl + hcldec + + name + HCL + patterns + + + begin + #|// + captures + + 0 + + name + punctuation.definition.comment.hcl + + + comment + Comments + end + $\n? + name + comment.line.hcl + + + begin + /\* + captures + + 0 + + name + punctuation.definition.comment.hcl + + + comment + Block comments + end + \*/ + name + comment.block.hcl + + + begin + { + beginCaptures + + 0 + + name + punctuation.definition.block.hcl + + + comment + Nested Blocks + end + } + endCaptures + + 0 + + name + punctuation.definition.block.hcl + + + name + meta.block.hcl + patterns + + + include + $self + + + + + captures + + 1 + + name + string.hcl punctuation.definition.string.begin.hcl + + 2 + + name + string.value.hcl + + 3 + + name + string.hcl punctuation.definition.string.end.hcl + + + comment + Quoted Block Labels + match + (")([^"]+)(") + + + begin + (\w+)\s*(=)\s* + beginCaptures + + 1 + + name + variable.other.assignment.hcl + + 2 + + name + keyword.operator.hcl + + + comment + Attribute Definitions + end + $ + name + meta.attr.hcl + patterns + + + include + source.hclexpr + + + + + captures + + 0 + + name + keyword.other.hcl + + + comment + Keywords + match + [-\w]+ + + + scopeName + source.hcl + uuid + 55e8075d-e2e3-4e44-8446-744a9860e476 + + \ No newline at end of file diff --git a/extras/grammar/HCL.yaml-tmLanguage b/extras/grammar/HCL.yaml-tmLanguage new file mode 100644 index 0000000..0a23cfa --- /dev/null +++ b/extras/grammar/HCL.yaml-tmLanguage @@ -0,0 +1,53 @@ +name: HCL +scopeName: source.hcl +fileTypes: [hcl, hcldec] +uuid: 55e8075d-e2e3-4e44-8446-744a9860e476 + +patterns: + +- comment: Comments + name: comment.line.hcl + begin: '#|//' + end: $\n? + captures: + '0': {name: punctuation.definition.comment.hcl} + +- comment: Block comments + name: comment.block.hcl + begin: /\* + end: \*/ + captures: + '0': {name: punctuation.definition.comment.hcl} + +- comment: Nested Blocks + name: meta.block.hcl + begin: "{" + beginCaptures: + '0': {name: punctuation.definition.block.hcl} + end: "}" + endCaptures: + '0': {name: punctuation.definition.block.hcl} + patterns: + - include: "$self" + +- comment: Quoted Block Labels + match: '(")([^"]+)(")' + captures: + '1': {name: string.hcl punctuation.definition.string.begin.hcl} + '2': {name: string.value.hcl} + '3': {name: string.hcl punctuation.definition.string.end.hcl} + +- comment: Attribute Definitions + name: meta.attr.hcl + begin: '(\w+)\s*(=)\s*' + beginCaptures: + '1': {name: variable.other.assignment.hcl} + '2': {name: keyword.operator.hcl} + end: '$' + patterns: + - include: "source.hclexpr" + +- comment: Keywords + match: '[-\w]+' + captures: + '0': {name: keyword.other.hcl} diff --git a/extras/grammar/HCLExpression.json-tmLanguage b/extras/grammar/HCLExpression.json-tmLanguage new file mode 100755 index 0000000..91886c8 --- /dev/null +++ b/extras/grammar/HCLExpression.json-tmLanguage @@ -0,0 +1,212 @@ +{ + "fileTypes": [], + "name": "HCL Expression", + "patterns": [ + { + "begin": "#|//", + "captures": { + "0": { + "name": "punctuation.definition.comment.hcl" + } + }, + "comment": "Comments", + "end": "$\\n?", + "name": "comment.line.hcl" + }, + { + "begin": "/\\*", + "captures": { + "0": { + "name": "punctuation.definition.comment.hcl" + } + }, + "comment": "Block comments", + "end": "\\*/", + "name": "comment.block.hcl" + }, + { + "comment": "Language constants (true, false, null)", + "match": "\\b(true|false|null)\\b", + "name": "constant.language.hcl" + }, + { + "comment": "Numbers", + "match": "\\b([0-9]+)(.[0-9]+)?([eE][0-9]+)?\\b", + "name": "constant.numeric.hcl" + }, + { + "begin": "([-\\w]+)(\\()", + "beginCaptures": { + "1": { + "name": "keyword.other.function.inline.hcl" + }, + "2": { + "name": "keyword.other.section.begin.hcl" + } + }, + "comment": "Function Calls", + "end": "(\\))", + "endCaptures": { + "1": { + "name": "keyword.other.section.end.hcl" + } + }, + "patterns": [ + { + "include": "$self" + } + ] + }, + { + "captures": { + "0": { + "name": "variable.other.hcl" + } + }, + "comment": "Variables and Attribute Names", + "match": "[-\\w]+" + }, + { + "begin": "(?\u003e\\s*\u003c\u003c(\\w+))", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.hcl" + }, + "1": { + "name": "keyword.operator.heredoc.hcl" + } + }, + "comment": "Heredoc Templates", + "end": "^\\s*\\1$", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.hcl keyword.operator.heredoc.hcl" + } + }, + "patterns": [ + { + "include": "source.hcltemplate" + } + ] + }, + { + "begin": "\\\"", + "beginCaptures": { + "0": { + "name": "string.hcl punctuation.definition.string.begin.hcl" + } + }, + "comment": "String Templates", + "end": "\\\"", + "endCaptures": { + "0": { + "name": "string.hcl punctuation.definition.string.end.hcl" + } + }, + "patterns": [ + { + "include": "source.hcltemplate" + }, + { + "match": "(^\"|$\\{|%\\{)+", + "name": "string.quoted.double.hcl" + } + ] + }, + { + "captures": { + "0": { + "name": "keyword.operator.hcl" + } + }, + "comment": "Operators", + "match": "(!=|==|\u003e=|\u003c=|\u0026\u0026|\\|\\||[-+*/%\u003c\u003e!?:])" + }, + { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "meta.brace.round.hcl" + } + }, + "comment": "Parentheses", + "end": "\\)", + "endCaptures": { + "0": { + "name": "meta.brace.round.hcl" + } + }, + "patterns": [ + { + "include": "$self" + } + ] + }, + { + "begin": "\\[", + "beginCaptures": { + "0": { + "name": "meta.brace.square.hcl" + } + }, + "comment": "Tuple Constructor", + "end": "\\]", + "endCaptures": { + "0": { + "name": "meta.brace.square.hcl" + } + }, + "patterns": [ + { + "captures": { + "0": { + "name": "keyword.control.hcl" + } + }, + "match": "(for|in)" + }, + { + "include": "$self" + } + ] + }, + { + "begin": "\\{", + "beginCaptures": { + "0": { + "name": "meta.brace.curly.hcl" + } + }, + "comment": "Object Constructor", + "end": "\\}", + "endCaptures": { + "0": { + "name": "meta.brace.curly.hcl" + } + }, + "patterns": [ + { + "captures": { + "0": { + "name": "keyword.control.hcl" + } + }, + "match": "(for|in)" + }, + { + "captures": { + "0": { + "name": "keyword.operator.hcl" + } + }, + "match": "(=\u003e|\\.\\.\\.)" + }, + { + "include": "$self" + } + ] + } + ], + "scopeName": "source.hclexpr", + "uuid": "6c358551-0381-4128-9ea3-277b21943b5c" +} \ No newline at end of file diff --git a/extras/grammar/HCLExpression.tmLanguage b/extras/grammar/HCLExpression.tmLanguage new file mode 100755 index 0000000..3a5bb83 --- /dev/null +++ b/extras/grammar/HCLExpression.tmLanguage @@ -0,0 +1,336 @@ + + + + + fileTypes + + + name + HCL Expression + patterns + + + begin + #|// + captures + + 0 + + name + punctuation.definition.comment.hcl + + + comment + Comments + end + $\n? + name + comment.line.hcl + + + begin + /\* + captures + + 0 + + name + punctuation.definition.comment.hcl + + + comment + Block comments + end + \*/ + name + comment.block.hcl + + + comment + Language constants (true, false, null) + match + \b(true|false|null)\b + name + constant.language.hcl + + + comment + Numbers + match + \b([0-9]+)(.[0-9]+)?([eE][0-9]+)?\b + name + constant.numeric.hcl + + + begin + ([-\w]+)(\() + beginCaptures + + 1 + + name + keyword.other.function.inline.hcl + + 2 + + name + keyword.other.section.begin.hcl + + + comment + Function Calls + end + (\)) + endCaptures + + 1 + + name + keyword.other.section.end.hcl + + + patterns + + + include + $self + + + + + captures + + 0 + + name + variable.other.hcl + + + comment + Variables and Attribute Names + match + [-\w]+ + + + begin + (?>\s*<<(\w+)) + beginCaptures + + 0 + + name + punctuation.definition.string.begin.hcl + + 1 + + name + keyword.operator.heredoc.hcl + + + comment + Heredoc Templates + end + ^\s*\1$ + endCaptures + + 0 + + name + punctuation.definition.string.end.hcl keyword.operator.heredoc.hcl + + + patterns + + + include + source.hcltemplate + + + + + begin + \" + beginCaptures + + 0 + + name + string.hcl punctuation.definition.string.begin.hcl + + + comment + String Templates + end + \" + endCaptures + + 0 + + name + string.hcl punctuation.definition.string.end.hcl + + + patterns + + + include + source.hcltemplate + + + match + (^"|$\{|%\{)+ + name + string.quoted.double.hcl + + + + + captures + + 0 + + name + keyword.operator.hcl + + + comment + Operators + match + (!=|==|>=|<=|&&|\|\||[-+*/%<>!?:]) + + + begin + \( + beginCaptures + + 0 + + name + meta.brace.round.hcl + + + comment + Parentheses + end + \) + endCaptures + + 0 + + name + meta.brace.round.hcl + + + patterns + + + include + $self + + + + + begin + \[ + beginCaptures + + 0 + + name + meta.brace.square.hcl + + + comment + Tuple Constructor + end + \] + endCaptures + + 0 + + name + meta.brace.square.hcl + + + patterns + + + captures + + 0 + + name + keyword.control.hcl + + + match + (for|in) + + + include + $self + + + + + begin + \{ + beginCaptures + + 0 + + name + meta.brace.curly.hcl + + + comment + Object Constructor + end + \} + endCaptures + + 0 + + name + meta.brace.curly.hcl + + + patterns + + + captures + + 0 + + name + keyword.control.hcl + + + match + (for|in) + + + captures + + 0 + + name + keyword.operator.hcl + + + match + (=>|\.\.\.) + + + include + $self + + + + + scopeName + source.hclexpr + uuid + 6c358551-0381-4128-9ea3-277b21943b5c + + \ No newline at end of file diff --git a/extras/grammar/HCLExpression.yaml-tmLanguage b/extras/grammar/HCLExpression.yaml-tmLanguage new file mode 100644 index 0000000..c8ba5c1 --- /dev/null +++ b/extras/grammar/HCLExpression.yaml-tmLanguage @@ -0,0 +1,111 @@ +name: HCL Expression +scopeName: source.hclexpr +fileTypes: [] +uuid: 6c358551-0381-4128-9ea3-277b21943b5c + +patterns: + +- comment: Comments + name: comment.line.hcl + begin: '#|//' + end: $\n? + captures: + '0': {name: punctuation.definition.comment.hcl} + +- comment: Block comments + name: comment.block.hcl + begin: /\* + end: \*/ + captures: + '0': {name: punctuation.definition.comment.hcl} + +- comment: Language constants (true, false, null) + name: constant.language.hcl + match: \b(true|false|null)\b + +- comment: Numbers + name: constant.numeric.hcl + match: \b([0-9]+)(.[0-9]+)?([eE][0-9]+)?\b + +- comment: Function Calls + begin: ([-\w]+)(\() + beginCaptures: + '1': {name: keyword.other.function.inline.hcl} + '2': {name: keyword.other.section.begin.hcl} + end: (\)) + endCaptures: + '1': {name: keyword.other.section.end.hcl} + patterns: + - include: '$self' + +- comment: Variables and Attribute Names + match: '[-\w]+' + captures: + '0': {name: variable.other.hcl} + +- comment: Heredoc Templates + begin: (?>\s*<<(\w+)) + beginCaptures: + '0': {name: punctuation.definition.string.begin.hcl} + '1': {name: keyword.operator.heredoc.hcl} + end: ^\s*\1$ + endCaptures: + '0': {name: punctuation.definition.string.end.hcl keyword.operator.heredoc.hcl} + patterns: + - include: 'source.hcltemplate' + +- comment: String Templates + begin: \" + beginCaptures: + '0': {name: string.hcl punctuation.definition.string.begin.hcl} + end: \" + endCaptures: + '0': {name: string.hcl punctuation.definition.string.end.hcl} + patterns: + - include: 'source.hcltemplate' + - match: '(^"|$\{|%\{)+' + name: "string.quoted.double.hcl" + +- comment: Operators + match: '(!=|==|>=|<=|&&|\|\||[-+*/%<>!?:])' + captures: + '0': {name: keyword.operator.hcl} + +- comment: Parentheses + begin: '\(' + beginCaptures: + '0': {name: meta.brace.round.hcl} + end: '\)' + endCaptures: + '0': {name: meta.brace.round.hcl} + patterns: + - include: '$self' + +- comment: Tuple Constructor + begin: '\[' + beginCaptures: + '0': {name: meta.brace.square.hcl} + end: '\]' + endCaptures: + '0': {name: meta.brace.square.hcl} + patterns: + - match: '(for|in)' + captures: + '0': {name: keyword.control.hcl} + - include: '$self' + +- comment: Object Constructor + begin: '\{' + beginCaptures: + '0': {name: meta.brace.curly.hcl} + end: '\}' + endCaptures: + '0': {name: meta.brace.curly.hcl} + patterns: + - match: '(for|in)' + captures: + '0': {name: keyword.control.hcl} + - match: '(=>|\.\.\.)' + captures: + '0': {name: keyword.operator.hcl} + - include: '$self' diff --git a/extras/grammar/HCLTemplate.json-tmLanguage b/extras/grammar/HCLTemplate.json-tmLanguage new file mode 100755 index 0000000..6d002c1 --- /dev/null +++ b/extras/grammar/HCLTemplate.json-tmLanguage @@ -0,0 +1,107 @@ +{ + "fileTypes": [ + "tmpl" + ], + "name": "HCL Template", + "patterns": [ + { + "begin": "[^\\$]?(\\$\\{~?)", + "beginCaptures": { + "1": { + "name": "entity.tag.embedded.start.hcltemplate" + } + }, + "comment": "Interpolation Sequences", + "end": "~?}", + "endCaptures": { + "0": { + "name": "entity.tag.embedded.end.hcltemplate" + } + }, + "name": "meta.interp.hcltemplate", + "patterns": [ + { + "include": "source.hclexpr" + } + ] + }, + { + "begin": "[^\\%]?(\\%\\{~?)", + "beginCaptures": { + "1": { + "name": "entity.tag.embedded.start.hcltemplate" + } + }, + "comment": "Control Sequences", + "end": "~?}", + "endCaptures": { + "0": { + "name": "entity.tag.embedded.end.hcltemplate" + } + }, + "name": "meta.control.hcltemplate", + "patterns": [ + { + "include": "#templateif" + }, + { + "include": "#templatefor" + }, + { + "include": "#templatesimplekw" + } + ] + } + ], + "repository": { + "templatefor": { + "begin": "(for)\\s*(\\w+)\\s*(,\\s*(\\w+)\\s*)?(in)", + "beginCaptures": { + "1": { + "name": "keyword.control.hcltemplate" + }, + "2": { + "name": "variable.other.hcl" + }, + "4": { + "name": "variable.other.hcl" + }, + "5": { + "name": "keyword.control.hcltemplate" + } + }, + "end": "(?=~?\\})", + "name": "meta.templatefor.hcltemplate", + "patterns": [ + { + "include": "source.hclexpr" + } + ] + }, + "templateif": { + "begin": "(if)\\s*", + "beginCaptures": { + "1": { + "name": "keyword.control.hcltemplate" + } + }, + "end": "(?=~?\\})", + "name": "meta.templateif.hcltemplate", + "patterns": [ + { + "include": "source.hclexpr" + } + ] + }, + "templatesimplekw": { + "captures": { + "0": { + "name": "keyword.control.hcl" + } + }, + "match": "(else|endif|endfor)" + } + }, + "scopeName": "source.hcltemplate", + "uuid": "ac6be18e-d44f-4a73-bd8f-b973fd26df05" +} \ No newline at end of file diff --git a/extras/grammar/HCLTemplate.tmLanguage b/extras/grammar/HCLTemplate.tmLanguage new file mode 100755 index 0000000..6c05359 --- /dev/null +++ b/extras/grammar/HCLTemplate.tmLanguage @@ -0,0 +1,172 @@ + + + + + fileTypes + + tmpl + + name + HCL Template + patterns + + + begin + [^\$]?(\$\{~?) + beginCaptures + + 1 + + name + entity.tag.embedded.start.hcltemplate + + + comment + Interpolation Sequences + end + ~?} + endCaptures + + 0 + + name + entity.tag.embedded.end.hcltemplate + + + name + meta.interp.hcltemplate + patterns + + + include + source.hclexpr + + + + + begin + [^\%]?(\%\{~?) + beginCaptures + + 1 + + name + entity.tag.embedded.start.hcltemplate + + + comment + Control Sequences + end + ~?} + endCaptures + + 0 + + name + entity.tag.embedded.end.hcltemplate + + + name + meta.control.hcltemplate + patterns + + + include + #templateif + + + include + #templatefor + + + include + #templatesimplekw + + + + + repository + + templatefor + + begin + (for)\s*(\w+)\s*(,\s*(\w+)\s*)?(in) + beginCaptures + + 1 + + name + keyword.control.hcltemplate + + 2 + + name + variable.other.hcl + + 4 + + name + variable.other.hcl + + 5 + + name + keyword.control.hcltemplate + + + end + (?=~?\}) + name + meta.templatefor.hcltemplate + patterns + + + include + source.hclexpr + + + + templateif + + begin + (if)\s* + beginCaptures + + 1 + + name + keyword.control.hcltemplate + + + end + (?=~?\}) + name + meta.templateif.hcltemplate + patterns + + + include + source.hclexpr + + + + templatesimplekw + + captures + + 0 + + name + keyword.control.hcl + + + match + (else|endif|endfor) + + + scopeName + source.hcltemplate + uuid + ac6be18e-d44f-4a73-bd8f-b973fd26df05 + + \ No newline at end of file diff --git a/extras/grammar/HCLTemplate.yaml-tmLanguage b/extras/grammar/HCLTemplate.yaml-tmLanguage new file mode 100644 index 0000000..bd4f99f --- /dev/null +++ b/extras/grammar/HCLTemplate.yaml-tmLanguage @@ -0,0 +1,58 @@ +name: HCL Template +scopeName: source.hcltemplate +fileTypes: [tmpl] +uuid: ac6be18e-d44f-4a73-bd8f-b973fd26df05 + +patterns: + +- comment: Interpolation Sequences + name: meta.interp.hcltemplate + begin: '[^\$]?(\$\{~?)' + beginCaptures: + '1': {name: entity.tag.embedded.start.hcltemplate} + end: '~?}' + endCaptures: + '0': {name: entity.tag.embedded.end.hcltemplate} + patterns: + - include: "source.hclexpr" + +- comment: Control Sequences + name: meta.control.hcltemplate + begin: '[^\%]?(\%\{~?)' + beginCaptures: + '1': {name: entity.tag.embedded.start.hcltemplate} + end: '~?}' + endCaptures: + '0': {name: entity.tag.embedded.end.hcltemplate} + patterns: + - include: "#templateif" + - include: "#templatefor" + - include: "#templatesimplekw" + +repository: + + templateif: + name: meta.templateif.hcltemplate + begin: '(if)\s*' + beginCaptures: + '1': {name: keyword.control.hcltemplate} + end: '(?=~?\})' + patterns: + - include: "source.hclexpr" + + templatefor: + name: meta.templatefor.hcltemplate + begin: '(for)\s*(\w+)\s*(,\s*(\w+)\s*)?(in)' + beginCaptures: + '1': {name: keyword.control.hcltemplate} + '2': {name: variable.other.hcl} + '4': {name: variable.other.hcl} + '5': {name: keyword.control.hcltemplate} + end: '(?=~?\})' + patterns: + - include: "source.hclexpr" + + templatesimplekw: + match: (else|endif|endfor) + captures: + '0': {name: keyword.control.hcl} diff --git a/extras/grammar/build.go b/extras/grammar/build.go new file mode 100644 index 0000000..224550d --- /dev/null +++ b/extras/grammar/build.go @@ -0,0 +1,119 @@ +// This is a helper to transform the HCL.yaml-tmLanguage file (the source of +// record) into both HCL.json-tmLanguage and HCL.tmLanguage (in plist XML +// format). +// +// Run this after making updates to HCL.yaml-tmLanguage to generate the other +// formats. +// +// This file is intended to be run with "go run": +// +// go run ./build.go +// +// This file is also set up to run itself under "go generate": +// +// go generate . + +package main + +//go:generate go run ./build.go + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "os" + + plist "github.com/DHowett/go-plist" + yaml "gopkg.in/yaml.v2" + + multierror "github.com/hashicorp/go-multierror" +) + +func main() { + err := realMain() + if err != nil { + log.Fatal(err) + } + os.Exit(0) +} + +func realMain() error { + var err error + buildErr := build("HCL") + if buildErr != nil { + err = multierror.Append(err, fmt.Errorf("in HCL: %s", buildErr)) + } + buildErr = build("HCLTemplate") + if buildErr != nil { + err = multierror.Append(err, fmt.Errorf("in HCLTemplate: %s", buildErr)) + } + buildErr = build("HCLExpression") + if buildErr != nil { + err = multierror.Append(err, fmt.Errorf("in HCLExpression: %s", buildErr)) + } + return err +} + +func build(basename string) error { + yamlSrc, err := ioutil.ReadFile(basename + ".yaml-tmLanguage") + if err != nil { + return err + } + + var content interface{} + err = yaml.Unmarshal(yamlSrc, &content) + if err != nil { + return err + } + + // Normalize the value so it's both JSON- and plist-friendly. + content = prepare(content) + + jsonSrc, err := json.MarshalIndent(content, "", " ") + if err != nil { + return err + } + + plistSrc, err := plist.MarshalIndent(content, plist.XMLFormat, " ") + if err != nil { + return err + } + + err = ioutil.WriteFile(basename+".json-tmLanguage", jsonSrc, os.ModePerm) + if err != nil { + return err + } + + err = ioutil.WriteFile(basename+".tmLanguage", plistSrc, os.ModePerm) + if err != nil { + return err + } + + return nil +} + +func prepare(v interface{}) interface{} { + switch tv := v.(type) { + + case map[interface{}]interface{}: + var ret map[string]interface{} + if len(tv) == 0 { + return ret + } + ret = make(map[string]interface{}, len(tv)) + for k, v := range tv { + ret[k.(string)] = prepare(v) + } + return ret + + case []interface{}: + for i := range tv { + tv[i] = prepare(tv[i]) + } + return tv + + default: + return v + } +}