diff --git a/grammars/puppet.cson b/grammars/puppet.cson index 702b01f..b2182c3 100644 --- a/grammars/puppet.cson +++ b/grammars/puppet.cson @@ -7,166 +7,32 @@ 'name': 'Puppet' 'patterns': [ { - 'include': '#line_comment' + 'include': '#puppet' } { - 'include': '#constants' + 'include': '#definitions' } - { - 'begin': '^\\s*/\\*' - 'end': '\\*/' - 'name': 'comment.block.puppet' - } - { - 'begin': '^\\s*(node|class)\\s+((?:[-_A-Za-z0-9"\'.]+::)*[-_A-Za-z0-9"\'.]+)\\s*' - 'captures': - '1': - 'name': 'storage.type.puppet' - '2': - 'name': 'entity.name.type.class.puppet' - 'end': '(?={)' - 'name': 'meta.definition.class.puppet' - 'patterns': [ - { - 'begin': '\\b(inherits)\\b\\s+' - 'captures': - '1': - 'name': 'storage.modifier.puppet' - 'end': '(?={)' - 'name': 'meta.definition.class.inherits.puppet' - 'patterns': [ - { - 'match': '\\b((?:[-_A-Za-z0-9".]+::)*[-_A-Za-z0-9".]+)\\b' - 'name': 'support.type.puppet' - } - ] - } - { - 'captures': - '1': - 'name': 'variable.other.puppet' - '2': - 'name': 'punctuation.definition.variable.puppet' - 'match': '((\\$+)[a-zA-Z_][a-zA-Z0-9_]*)\\s*(?=,|\\))' - 'name': 'meta.function.argument.no-default.puppet' - } - { - 'begin': '((\\$+)[a-zA-Z_][a-zA-Z0-9_]*)(?:\\s*(=)\\s*)\\s*' - 'captures': - '1': - 'name': 'variable.other.puppet' - '2': - 'name': 'punctuation.definition.variable.puppet' - '3': - 'name': 'keyword.operator.assignment.puppet' - 'end': '(?=,|\\))' - 'name': 'meta.function.argument.default.puppet' - 'patterns': [ - { - 'include': '#parameter-default-types' - } - ] - } - ] - } - { - 'begin': '^\\s*(define)\\s+([a-zA-Z0-9_:]+)\\s*(\\()' +] +'repository': + 'functions': + 'begin': '(?:^|\\s|\\W|\\D)(\\w+)\\s*(\\()' 'beginCaptures': '1': - 'name': 'storage.type.function.puppet' + 'name': 'support.function.puppet' '2': - 'name': 'entity.name.function.puppet' - '3': - 'name': 'punctuation.definition.parameters.begin.puppet' - 'contentName': 'meta.function.arguments.puppet' + 'name': 'punctuation.definition.function.begin.puppet' 'end': '\\)' 'endCaptures': - '1': - 'name': 'punctuation.definition.parameters.end.puppet' + '0': + 'name': 'punctuation.definition.function.end.puppet' 'name': 'meta.function.puppet' 'patterns': [ { - 'captures': - '1': - 'name': 'variable.other.puppet' - '2': - 'name': 'punctuation.definition.variable.puppet' - 'match': '((\\$+)[a-zA-Z_][a-zA-Z0-9_]*)\\s*(?=,|\\))' - 'name': 'meta.function.argument.no-default.puppet' + 'match': ',' + 'name': 'punctuation.separator.parameter.puppet' } { - 'begin': '((\\$+)[a-zA-Z_][a-zA-Z0-9_]*)(?:\\s*(=)\\s*)\\s*' - 'captures': - '1': - 'name': 'variable.other.puppet' - '2': - 'name': 'punctuation.definition.variable.puppet' - '3': - 'name': 'keyword.operator.assignment.puppet' - 'end': '(?=,|\\))' - 'name': 'meta.function.argument.default.puppet' - 'patterns': [ - { - 'include': '#parameter-default-types' - } - ] - } - ] - } - { - 'captures': - '1': - 'name': 'storage.type.puppet' - '2': - 'name': 'entity.name.section.puppet' - 'match': '^\\s*(\\w+)\\s*{\\s*([\'"].+[\'"]):' - 'name': 'meta.definition.resource.puppet' - } - { - 'match': '\\b(case|if|else|elsif)(?!::)\\b' - 'name': 'keyword.control.puppet' - } - { - 'include': '#strings' - } - { - 'match': '((\\$?)"?[a-zA-Z_\\x{7f}-\\x{ff}][a-zA-Z0-9_\\x{7f}-\\x{ff}]*"?):(?=\\s+|$)' - 'name': 'entity.name.section.puppet' - } - { - 'include': '#variable' - } - { - 'begin': '(?i)\\b(import|include)\\b\\s*' - 'beginCaptures': - '1': - 'name': 'keyword.control.import.include.puppet' - 'end': '(?=\\s|$)' - 'name': 'meta.include.puppet' - } - { - 'match': '\\b\\w+\\s*(?==>)\\s*' - 'name': 'constant.other.key.puppet' - } - { - 'match': '(?<={)\\s*\\w+\\s*(?=})' - 'name': 'constant.other.bareword.puppet' - } - { - 'match': '(?i)\\b(alert|contain|crit|debug|defined|emerg|err|escape|fail|failed|file|generate|gsub|include|info|notice|package|realize|search|tag|tagged|template|warning)\\b' - 'name': 'support.function.puppet' - } - { - 'match': '=>' - 'name': 'punctuation.separator.key-value.puppet' - } -] -'repository': - 'constants': - 'patterns': [ - { - 'match': '(?i)\\b(absent|directory|false|file|present|running|stopped|true)\\b' - 'name': 'constant.language.puppet' + 'include': '#parameter-default-types' } ] 'double-quoted-string': @@ -184,7 +50,7 @@ 'include': '#escaped_char' } { - 'include': '#variable' + 'include': '#variables' } ] 'escaped_char': @@ -234,7 +100,7 @@ 'include': '#escaped_char' } { - 'include': '#variable' + 'include': '#variables' } { 'include': '#nested_braces_interpolated' @@ -265,7 +131,7 @@ 'include': '#escaped_char' } { - 'include': '#variable' + 'include': '#variables' } { 'include': '#nested_brackets_interpolated' @@ -296,7 +162,7 @@ 'include': '#escaped_char' } { - 'include': '#variable' + 'include': '#variables' } { 'include': '#nested_parens_interpolated' @@ -320,14 +186,7 @@ 'include': '#array' } { - 'begin': '([a-zA-Z_][a-zA-Z0-9_]*)(\\()' - 'end': '(\\))' - 'name': 'meta.function.puppet' - 'patterns': [ - { - 'include': '#parameter-default-types' - } - ] + 'include': "#functions" } { 'include': '#constants' @@ -352,19 +211,22 @@ } 'hash': { - 'begin': '\\{' + 'begin': '{' 'beginCaptures': '0': 'name': 'punctuation.definition.hash.begin.puppet' - 'end': '\\}' + 'end': '}' 'endCaptures': '0': 'name': 'punctuation.definition.hash.end.puppet' 'name': 'meta.hash.puppet' 'patterns': [ { - 'match': '\\b\\w+\\s*(?==>)\\s*' - 'name': 'constant.other.key.puppet' + 'captures': + '0': + 'name': 'punctuation.separator.key-value.puppet' + 'match': '=>' + 'name': 'meta.punctuation.hash.puppet' } { 'include': '#parameter-default-types' @@ -394,23 +256,252 @@ { 'include': '#single-quoted-string' } + { + 'include': '#bareword' + } ] - 'variable': + 'bareword': + 'match': '(?<={)\\s*\\w+\\s*(?=})' + 'name': 'constant.other.bareword.puppet' + 'variables': 'patterns': [ { 'captures': '1': 'name': 'punctuation.definition.variable.puppet' - 'match': '(\\$)([a-zA-Zx7f-xff\\$]|::)([a-zA-Z0-9_x7f-xff\\$]|::)*\\b' + 'match': '(\\${*)([a-zA-Zx7f-xff\\$]|::)([a-zA-Z0-9_x7f-xff\\$]|::)*\\b' 'name': 'variable.other.readwrite.global.puppet' } { + 'match': '(\\$\\{)(?:[a-zA-Zx7f-xff\\$]|::)(?:[a-zA-Z0-9_x7f-xff\\$]|::)*(\\})' 'captures': '1': 'name': 'punctuation.definition.variable.puppet' '2': 'name': 'punctuation.definition.variable.puppet' - 'match': '(\\$\\{)(?:[a-zA-Zx7f-xff\\$]|::)(?:[a-zA-Z0-9_x7f-xff\\$]|::)*(\\})' 'name': 'variable.other.readwrite.global.puppet' } ] + 'constants': + 'begin': '((?:[-_A-Za-z0-9"\'.]+::)*[-_A-Za-z0-9"\'.]+)\\[' + 'beginCaptures': + '0': + 'name': 'constant.support.puppet' + 'end': '\\]' + 'endCaptures': + '0': + 'name': 'constant.support.puppet' + 'patterns': [ + { + 'include': '#strings' + } + { + 'include': '#variables' + } + ] + 'conditionals': + 'patterns': [ + { + 'include': '#comparisons' + } + { + 'include': '#cases' + } + ] + 'cases': + 'begin': '(case)\\s+((\\${*)([a-zA-Zx7f-xff\\$]|::)([a-zA-Z0-9_x7f-xff\\$]|::)*\\b)\\s*({)' + 'beginCaptures': + '1': + 'name': 'keyword.control.puppet' + '2': + 'name': 'variable.other.readwrite.global.puppet' + '3': + 'name': 'punctuation.conditional.case.begin.puppet' + 'end': '}' + 'endCaptures': + '0': + 'name': 'punctuation.conditional.case.end.puppet' + 'name': 'meta.conditional.case.puppet' + 'patterns': [ + { + 'include': '#case-parameters' + } + ] + 'case-parameters': + 'patterns': [ + { + 'begin': '(=>)\\s*({)' + 'beginCaptures': + '0': + 'name': 'punctuation.separator.key-value.puppet' + '2': + 'name': 'punctuation.parameter.begin.puppet' + 'end': '(})(?:,|$)' + 'endCaptures': + '1': + 'name': 'punctuation.parameter.end.puppet' + 'patterns': [ + { + 'include': '#puppet' + } + ] + } + { + 'include': '#variables' + } + { + 'include': '#strings' + } + ] + 'comparisons': + 'begin': '(^|\\s)\\s*(if|else|unless|elsif)\\s+' + 'beginCaptures': + '2': + 'name': 'keyword.control.puppet' + 'end': '{' + 'patterns': [ + { + 'include': '#parameter-default-types' + } + { + 'include': '#comparison-operators' + } + { + 'include': '#negation-operators' + } + ] + 'comparison-operators': + 'match': '(?:\\s|^)(={2}|!=|>|<|>=|<=|=~|!~|in)(?:\\s|^)' + 'name': 'keyword.control.puppet' + 'negation-operators': + 'captures': + '1': + 'name': 'negation.keyword.control.puppet' + 'match': '(?:\\s|^)(not|!)(?:\\s|^)' + 'name': 'meta.keyword.control.puppet' + 'numbers': + 'captures': + '1': + 'name': 'constant.numeric.puppet' + 'match': '(?:(?:\\D&\\W)|\\s|^|\\[|\\{|,|;|\\()(\\d+([.]\\d+)?)(?!\\w)' + 'name': 'meta.control.puppet' + 'define-parameters': + 'begin': '\\(' + 'beginCaptures': + '0': + 'name': 'punctuation.definition.classparameter.begin.puppet' + 'end': '\\)' + 'endCaptures': + '0': + 'name': 'punctuation.definition.classparameter.end.puppet' + 'name': 'meta.classparameter.language.puppet' + 'patterns': [ + { + 'include': '#parameter-default-types' + } + ] + + 'definitions': + 'begin': '^\\s*(node|define|class|application)\\s+((?:[-_A-Za-z0-9"\'.]+::)*[-_A-Za-z0-9"\'.]+)\\s*' + 'beginCaptures': + '1': + 'name': 'storage.type.puppet' + '2': + 'name': 'entity.name.type.class.puppet' + 'end': '{' + 'endCaptures': + '0': + 'name': 'punctuation.definition.class.begin.puppet' + 'name': 'meta.definition.class.puppet' + 'patterns': [ + { + 'begin': '\\b(inherits)\\b\\s+' + 'beginCaptures': + '1': + 'name': 'storage.modifier.puppet' + 'end': '{' + 'endCaptures': + + 'name': 'punctuation.definition.class.begin.puppet' + 'name': 'meta.definition.class.inherits.puppet' + 'patterns': [ + { + 'match': '\\b((?:[-_A-Za-z0-9".]+::)*[-_A-Za-z0-9".]+)\\b' + 'name': 'entity.other.inherited-class.puppet' + } + ] + } + { + 'include': '#define-parameters' + } + ] + 'resources': + 'begin': '((?:[-_A-Za-z0-9".]+::)*[-_A-Za-z0-9".]+)\\s*{' + 'beginCaptures': + '1': + 'name': 'storage.type.puppet' + 'name': 'meta.definition.resource.puppet' + 'end': '}' + 'patterns': [ + { + 'match': ':' + 'captures': + '0': + 'name': 'punctuation.classtitle.puppet' + 'name': 'meta.title.puppet' + } + { + 'include': '#line_comment' + } + { + 'include': '#strings' + } + { + 'include': '#variables' + } + { + 'include': '#metaparameters' + } + { + 'beginCaptures': + '1': + 'name': 'name.parameter.resource.puppet' + '2': + 'name': 'punctuation.separator.key-value.puppet' + 'begin': '(\\w+)\\s*(=>)' + 'name': 'meta.parameter.resource.puppet' + 'end': ',|\\n|;' + 'patterns': [ + { + 'include': '#parameter-default-types' + } + ] + } + ] + 'puppet': + 'patterns': [ + { + 'include': '#parameter-default-types' + } + { + 'include': '#resources' + } + { + 'include': '#conditionals' + } + { + 'match': '((\\$?)"?[a-zA-Z_\\x{7f}-\\x{ff}][a-zA-Z0-9_\\x{7f}-\\x{ff}]*"?):(?=\\s+|$)' + 'name': 'entity.name.section.puppet' + } + { + 'begin': '(?i)\\b(import|include)\\b\\s*' + 'beginCaptures': + '1': + 'name': 'keyword.control.import.include.puppet' + 'end': '(?=\\s|$)' + 'name': 'meta.include.puppet' + } + { + 'include': '#functions' + } + ] diff --git a/spec/puppet-spec.coffee b/spec/puppet-spec.coffee index 5f6f791..d0a6d10 100644 --- a/spec/puppet-spec.coffee +++ b/spec/puppet-spec.coffee @@ -12,16 +12,78 @@ describe "Puppet grammar", -> expect(grammar).toBeTruthy() expect(grammar.scopeName).toBe "source.puppet" - describe "separators", -> - it "tokenizes attribute separator", -> - {tokens} = grammar.tokenizeLine('ensure => present') - expect(tokens[1]).toEqual value: '=>', scopes: ['source.puppet', 'punctuation.separator.key-value.puppet'] + describe "resources", -> + manifest = "type { title: parameter1 => 'stringvalue', inlineparameter => Resource['reference']; }" - it "tokenizes attribute separator with string values", -> - {tokens} = grammar.tokenizeLine('ensure => "present"') - expect(tokens[1]).toEqual value: '=>', scopes: ['source.puppet', 'punctuation.separator.key-value.puppet'] + it 'tokenizes resource types', -> + {tokens} = grammar.tokenizeLine(manifest) + expect(tokens[0]).toEqual value: 'type', scopes: ['source.puppet', 'meta.definition.resource.puppet', 'storage.type.puppet'] - describe "blocks", -> + it 'tokenizes defined type resources', -> + manifest = "classname::type { title: parameter1 => 'stringvalue', inlineparameter => Resource['reference']; }" + {tokens} = grammar.tokenizeLine(manifest) + expect(tokens[0]).toEqual value: 'classname::type', scopes: ['source.puppet', 'meta.definition.resource.puppet', 'storage.type.puppet'] + + it 'tokenizes resource title punctuation', -> + {tokens} = grammar.tokenizeLine(manifest) + expect(tokens[3]).toEqual value: ':', scopes: ['source.puppet', 'meta.definition.resource.puppet', 'meta.title.puppet', 'punctuation.classtitle.puppet'] + + it 'tokenizes resource parameter', -> + {tokens} = grammar.tokenizeLine(manifest) + expect(tokens[5]).toEqual value: 'parameter1', scopes: ['source.puppet', 'meta.definition.resource.puppet', 'meta.parameter.resource.puppet', 'name.parameter.resource.puppet'] + + it 'tokenizes resource parameter separators', -> + {tokens} = grammar.tokenizeLine(manifest) + expect(tokens[7]).toEqual value: '=>', scopes: ['source.puppet', 'meta.definition.resource.puppet', 'meta.parameter.resource.puppet', 'punctuation.separator.key-value.puppet'] + + describe "classes", -> + it 'should tokenize a class without parameters', -> + {tokens} = grammar.tokenizeLine("class classname { }") + expect(tokens[0]).toEqual value: 'class', scopes: ['source.puppet', 'meta.definition.class.puppet', 'storage.type.puppet'] + expect(tokens[2]).toEqual value: 'classname', scopes: ['source.puppet', 'meta.definition.class.puppet', 'entity.name.type.class.puppet'] + expect(tokens[4]).toEqual value: '{', scopes: [ 'source.puppet', 'meta.definition.class.puppet', 'punctuation.definition.class.begin.puppet' ] + + it 'should tokenize a class with parameters', -> + {tokens} = grammar.tokenizeLine("class classname ( $parameter1, $parameter2 = 'value', $parameter3 = $classname::params) { }") + expect(tokens[4]).toEqual value: '(', scopes: ['source.puppet', 'meta.definition.class.puppet', 'meta.classparameter.language.puppet', 'punctuation.definition.classparameter.begin.puppet'] + expect(tokens[21]).toEqual value: ')', scopes: ['source.puppet', 'meta.definition.class.puppet', 'meta.classparameter.language.puppet', 'punctuation.definition.classparameter.end.puppet'] + + it 'should tokenize a class with ineritence', -> + {tokens} = grammar.tokenizeLine("class classname ( $parameter1, $parameter2 = 'value', $parameter3 = $classname::params) inherits another::class { }") + expect(tokens[23]).toEqual value: 'inherits', scopes: ['source.puppet', 'meta.definition.class.puppet', 'meta.definition.class.inherits.puppet', 'storage.modifier.puppet'] + expect(tokens[25]).toEqual value: 'another::class', scopes: ['source.puppet', 'meta.definition.class.puppet', 'meta.definition.class.inherits.puppet', 'entity.other.inherited-class.puppet'] + + describe "applications", -> + it 'should tokenize an application without parameters', -> + {tokens} = grammar.tokenizeLine("application appname { }") + expect(tokens[0]).toEqual value: 'application', scopes: ['source.puppet', 'meta.definition.class.puppet', 'storage.type.puppet'] + expect(tokens[2]).toEqual value: 'appname', scopes: ['source.puppet', 'meta.definition.class.puppet', 'entity.name.type.class.puppet'] + expect(tokens[4]).toEqual value: '{', scopes: [ 'source.puppet', 'meta.definition.class.puppet', 'punctuation.definition.class.begin.puppet' ] + + it 'should tokenize an application with parameters', -> + {tokens} = grammar.tokenizeLine("application appname ( $parameter1, $parameter2 = 'value', $parameter3 = $classname::params) { }") + expect(tokens[4]).toEqual value: '(', scopes: ['source.puppet', 'meta.definition.class.puppet', 'meta.classparameter.language.puppet', 'punctuation.definition.classparameter.begin.puppet'] + expect(tokens[21]).toEqual value: ')', scopes: ['source.puppet', 'meta.definition.class.puppet', 'meta.classparameter.language.puppet', 'punctuation.definition.classparameter.end.puppet'] + + describe "defined types", -> + it 'should tokenize a defined type without parameters', -> + {tokens} = grammar.tokenizeLine("define typename { }") + expect(tokens[0]).toEqual value: 'define', scopes: ['source.puppet', 'meta.definition.class.puppet', 'storage.type.puppet'] + expect(tokens[2]).toEqual value: 'typename', scopes: ['source.puppet', 'meta.definition.class.puppet', 'entity.name.type.class.puppet'] + expect(tokens[4]).toEqual value: '{', scopes: [ 'source.puppet', 'meta.definition.class.puppet', 'punctuation.definition.class.begin.puppet' ] + + it 'should tokenize a defined type with parameters', -> + {tokens} = grammar.tokenizeLine("define typename ( $parameter1, $parameter2 = 'value', $parameter3 = $classname::params) { }") + expect(tokens[4]).toEqual value: '(', scopes: ['source.puppet', 'meta.definition.class.puppet', 'meta.classparameter.language.puppet', 'punctuation.definition.classparameter.begin.puppet'] + expect(tokens[21]).toEqual value: ')', scopes: ['source.puppet', 'meta.definition.class.puppet', 'meta.classparameter.language.puppet', 'punctuation.definition.classparameter.end.puppet'] + + describe "constants", -> + it 'should tokenize a resource reference', -> + {tokens} = grammar.tokenizeLine("Resource::Reference[title]") + expect(tokens[0]).toEqual value: "Resource::Reference[", scopes: ['source.puppet', 'constant.support.puppet'] + expect(tokens[2]).toEqual value: "]", scopes: ['source.puppet', 'constant.support.puppet'] + + describe "nodes", -> it "tokenizes single quoted node", -> {tokens} = grammar.tokenizeLine("node 'hostname' {") expect(tokens[0]).toEqual value: 'node', scopes: ['source.puppet', 'meta.definition.class.puppet', 'storage.type.puppet'] @@ -29,3 +91,20 @@ describe "Puppet grammar", -> it "tokenizes double quoted node", -> {tokens} = grammar.tokenizeLine('node "hostname" {') expect(tokens[0]).toEqual value: 'node', scopes: ['source.puppet', 'meta.definition.class.puppet', 'storage.type.puppet'] + + describe "functions", -> + it 'should tokenize a function with no space between name and (', -> + {tokens} = grammar.tokenizeLine('function(parameter, parameter2)') + expect(tokens[0]).toEqual value: 'function', scopes: [ 'source.puppet', 'meta.function.puppet', 'support.function.puppet' ] + expect(tokens[1]).toEqual value: '(', scopes: [ 'source.puppet', 'meta.function.puppet', 'punctuation.definition.function.begin.puppet' ] + expect(tokens[3]).toEqual value: ',', scopes: [ 'source.puppet', 'meta.function.puppet', 'punctuation.separator.parameter.puppet' ] + expect(tokens[5]).toEqual value: ')', scopes: [ 'source.puppet', 'meta.function.puppet', 'punctuation.definition.function.end.puppet' ] + + describe "numbers", -> + it "should tokenize floats", -> + {tokens} = grammar.tokenizeLine('883.111999') + expect(tokens[0]).toEqual value: '883.111999', scopes: [ 'source.puppet', 'meta.control.puppet', 'constant.numeric.puppet'] + + it "should tokenize integers", -> + {tokens} = grammar.tokenizeLine('883') + expect(tokens[0]).toEqual value: '883', scopes: [ 'source.puppet', 'meta.control.puppet', 'constant.numeric.puppet']