Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Directory formatting intentionally applies formatting only. SQLovely Extras are

SQLovely Extras are optional file-updating features beyond pure formatting.

The current extra inserts or updates a metadata header for procedures, functions and triggers:
The current extra inserts or updates a metadata header for procedures, functions and triggers. Generated metadata headers also receive dedicated syntax scopes so themes can distinguish the markers, field names, versions, dates, TODO placeholders and history entries:

```sql
CREATE PROCEDURE dbo.my_proc()
Expand Down
2 changes: 1 addition & 1 deletion docs/SQL_IMPLEMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ The grammar highlights useful lexical regions in `.sql` files.
| Transactions and savepoints | yes | keyword casing | not object-relevant |
| MSSQL batches with `GO` | yes | root-level keyword handling | not object-relevant |
| MSSQL routines | yes | rudimentary formatting | procedure/function/trigger supported |
| Metadata headers | comments highlighted | inserted/updated through extras | supported for one primary object per file |
| Metadata headers | section markers, field names, versions, dates, TODO placeholders and history entries | inserted/updated through extras | supported for one primary object per file |

## Design boundaries

Expand Down
283 changes: 271 additions & 12 deletions syntaxes/sqlovely.tmLanguage.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,22 +49,89 @@
"metadataHeader": {
"patterns": [
{
"name": "meta.header.sqlovely.sql",
"match": "^\\s*--\\s*(SQLovely-Metadata-(?:Start|End))\\b.*$",
"captures": {
"1": {
"name": "entity.name.tag.metadata.sqlovely.sql"
"name": "comment.block.metadata.sqlovely.sql",
"begin": "^(\\s*)(--)(\\s*)(METADATA)\\s*$",
"beginCaptures": {
"2": {
"name": "punctuation.definition.comment.sql.sqlovely"
},
"4": {
"name": "entity.name.section.metadata.begin.sqlovely.sql"
}
}
},
"end": "^(\\s*)(--)(\\s*)(METADATA END)\\s*$",
"endCaptures": {
"2": {
"name": "punctuation.definition.comment.sql.sqlovely"
},
"4": {
"name": "entity.name.section.metadata.end.sqlovely.sql"
}
},
"patterns": [
{
"include": "#metadataHeaderSeparators"
},
{
"include": "#metadataHeaderDescription"
},
{
"include": "#metadataHeaderVersion"
},
{
"include": "#metadataHeaderAuthor"
},
{
"include": "#metadataHeaderDates"
},
{
"include": "#metadataHeaderHistory"
},
{
"include": "#metadataHeaderGenericFields"
}
]
},
{
"name": "meta.header.field.sqlovely.sql",
"match": "^\\s*--\\s*(Object Type|Object Name|Dialect|Author|Created|Updated|Description)\\s*:",
"captures": {
"1": {
"name": "variable.other.metadata-field.sqlovely.sql"
"name": "comment.block.metadata.legacy.sqlovely.sql",
"begin": "^(\\s*)(--)(\\s*)(SQLovely-Metadata-Start)\\b.*$",
"beginCaptures": {
"2": {
"name": "punctuation.definition.comment.sql.sqlovely"
},
"4": {
"name": "entity.name.section.metadata.begin.sqlovely.sql"
}
}
},
"end": "^(\\s*)(--)(\\s*)(SQLovely-Metadata-End)\\b.*$",
"endCaptures": {
"2": {
"name": "punctuation.definition.comment.sql.sqlovely"
},
"4": {
"name": "entity.name.section.metadata.end.sqlovely.sql"
}
},
"patterns": [
{
"include": "#metadataHeaderDescription"
},
{
"include": "#metadataHeaderVersion"
},
{
"include": "#metadataHeaderAuthor"
},
{
"include": "#metadataHeaderDates"
},
{
"include": "#metadataHeaderHistory"
},
{
"include": "#metadataHeaderGenericFields"
}
]
}
]
},
Expand Down Expand Up @@ -373,6 +440,198 @@
"match": "\\."
}
]
},
"metadataHeaderSeparators": {
"patterns": [
{
"name": "meta.separator.metadata.sqlovely.sql",
"match": "^(\\s*)(--)(\\s*)$",
"captures": {
"2": {
"name": "punctuation.definition.comment.sql.sqlovely"
}
}
}
]
},
"metadataHeaderDescription": {
"patterns": [
{
"name": "meta.field.description.metadata.sqlovely.sql",
"begin": "^(\\s*)(--)(\\s*)(Description)(\\s*)(:)(\\s*)",
"beginCaptures": {
"2": {
"name": "punctuation.definition.comment.sql.sqlovely"
},
"4": {
"name": "variable.other.property.metadata-field.sqlovely.sql"
},
"6": {
"name": "punctuation.separator.key-value.metadata.sqlovely.sql"
}
},
"end": "$",
"patterns": [
{
"name": "comment.line.todo.metadata.placeholder.sqlovely.sql",
"match": "<TODO>"
},
{
"name": "string.unquoted.metadata.description.sqlovely.sql",
"match": "[^\\r\\n]+"
}
]
}
]
},
"metadataHeaderVersion": {
"patterns": [
{
"name": "meta.field.version.metadata.sqlovely.sql",
"begin": "^(\\s*)(--)(\\s*)(Version)(\\s*)(:)(\\s*)",
"beginCaptures": {
"2": {
"name": "punctuation.definition.comment.sql.sqlovely"
},
"4": {
"name": "variable.other.property.metadata-field.sqlovely.sql"
},
"6": {
"name": "punctuation.separator.key-value.metadata.sqlovely.sql"
}
},
"end": "$",
"patterns": [
{
"name": "constant.numeric.version.metadata.sqlovely.sql",
"match": "\\bv?\\d+(?:\\.\\d+)*(?:[-+][A-Za-z0-9.-]+)?\\b"
}
]
}
]
},
"metadataHeaderAuthor": {
"patterns": [
{
"name": "meta.field.author.metadata.sqlovely.sql",
"begin": "^(\\s*)(--)(\\s*)(Author)(\\s*)(:)(\\s*)",
"beginCaptures": {
"2": {
"name": "punctuation.definition.comment.sql.sqlovely"
},
"4": {
"name": "variable.other.property.metadata-field.sqlovely.sql"
},
"6": {
"name": "punctuation.separator.key-value.metadata.sqlovely.sql"
}
},
"end": "$",
"patterns": [
{
"name": "entity.name.other.author.metadata.sqlovely.sql",
"match": "[^\\r\\n]+"
}
]
}
]
},
"metadataHeaderDates": {
"patterns": [
{
"name": "meta.field.date.metadata.sqlovely.sql",
"begin": "^(\\s*)(--)(\\s*)(Created|Updated)(\\s*)(:)(\\s*)",
"beginCaptures": {
"2": {
"name": "punctuation.definition.comment.sql.sqlovely"
},
"4": {
"name": "variable.other.property.metadata-field.sqlovely.sql"
},
"6": {
"name": "punctuation.separator.key-value.metadata.sqlovely.sql"
}
},
"end": "$",
"patterns": [
{
"name": "constant.other.date.metadata.sqlovely.sql",
"match": "\\b\\d{4}-\\d{2}-\\d{2}\\b"
}
]
}
]
},
"metadataHeaderHistory": {
"patterns": [
{
"name": "meta.field.history.metadata.sqlovely.sql",
"match": "^(\\s*)(--)(\\s*)(History)(\\s*)(:)(\\s*)$",
"captures": {
"2": {
"name": "punctuation.definition.comment.sql.sqlovely"
},
"4": {
"name": "variable.other.property.metadata-field.sqlovely.sql"
},
"6": {
"name": "punctuation.separator.key-value.metadata.sqlovely.sql"
}
}
},
{
"name": "meta.history-entry.metadata.sqlovely.sql",
"begin": "^(\\s*)(--)(\\s*)(v\\d+(?:\\.\\d+)*)(\\s*)(:)(\\s*)",
"beginCaptures": {
"2": {
"name": "punctuation.definition.comment.sql.sqlovely"
},
"4": {
"name": "constant.numeric.version.metadata.sqlovely.sql"
},
"6": {
"name": "punctuation.separator.key-value.metadata.sqlovely.sql"
}
},
"end": "$",
"patterns": [
{
"name": "constant.other.date.metadata.sqlovely.sql",
"match": "\\b\\d{4}-\\d{2}-\\d{2}\\b"
},
{
"name": "entity.name.other.author.metadata.sqlovely.sql",
"match": "(?<=\\d{4}-\\d{2}-\\d{2}\\s)[^\\r\\n]+$"
}
]
}
]
},
"metadataHeaderGenericFields": {
"patterns": [
{
"name": "meta.field.generic.metadata.sqlovely.sql",
"begin": "^(\\s*)(--)(\\s*)(Object Type|Object Name|Dialect)(\\s*)(:)(\\s*)",
"beginCaptures": {
"2": {
"name": "punctuation.definition.comment.sql.sqlovely"
},
"4": {
"name": "variable.other.property.metadata-field.sqlovely.sql"
},
"6": {
"name": "punctuation.separator.key-value.metadata.sqlovely.sql"
}
},
"end": "$",
"patterns": [
{
"name": "string.unquoted.metadata.value.sqlovely.sql",
"match": "[^\\r\\n]+"
}
]
}
]
}
}
}
19 changes: 19 additions & 0 deletions test/runProjectValidationTests.js
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,25 @@ runTest('SQLovely grammar exposes the expected repository sections', () => {
assert.ok(repositories.includes('operators'));
});


runTest('SQLovely grammar gives generated metadata headers semantic scopes', () => {
const grammarText = fs.readFileSync(path.join(root, 'syntaxes/sqlovely.tmLanguage.json'), 'utf8');

for (const fragment of [
'comment.block.metadata.sqlovely.sql',
'entity.name.section.metadata.begin.sqlovely.sql',
'entity.name.section.metadata.end.sqlovely.sql',
'variable.other.property.metadata-field.sqlovely.sql',
'punctuation.separator.key-value.metadata.sqlovely.sql',
'constant.numeric.version.metadata.sqlovely.sql',
'constant.other.date.metadata.sqlovely.sql',
'comment.line.todo.metadata.placeholder.sqlovely.sql',
'meta.history-entry.metadata.sqlovely.sql'
]) {
assert.ok(grammarText.includes(fragment), `metadata grammar should include ${fragment}`);
}
});

runTest('SQLovely grammar includes audited SQL lexical categories', () => {
const grammarText = fs.readFileSync(path.join(root, 'syntaxes/sqlovely.tmLanguage.json'), 'utf8').toLowerCase();

Expand Down
Loading