feat: tumblr grammar with highlights, injections, and tests

This commit is contained in:
2026-05-17 05:50:17 +02:00
parent 1596154284
commit c61fa5c53b
11 changed files with 1950 additions and 55 deletions
+101
View File
@@ -0,0 +1,101 @@
; Punctuation
"{" @punctuation.bracket
"}" @punctuation.bracket
; Keywords that introduce a tag form. The grammar exposes the opening
; sequence as a hidden node, so capture the parent and let the editor
; colour the leading {block:, {/block:, {lang: literally via the
; tokenizer. The colon delimiter inside a variable_prefix tag is captured
; below.
":" @punctuation.delimiter
"-" @punctuation.delimiter
"=" @operator
; Block names. Known data-block names get @function.builtin; If/IfNot
; toggles get @keyword.conditional (theme authors may define arbitrary
; If* / IfNot* names via <meta name="if:..."> so we match by prefix).
((block_name) @keyword.conditional
(#match? @keyword.conditional "^[Ii]f([Nn]ot)?[A-Z]"))
((block_name) @function.builtin
(#any-of? @function.builtin
"Album" "AlbumArt" "Answer" "Answerer" "Aperture" "Artist" "AskEnabled"
"Audio" "AudioEmbed" "AudioPlayer" "Author" "Camera" "Caption" "Chat"
"ContentSource" "CurrentPage" "Date" "DayPage" "DayPagination"
"Description" "Even" "Excerpt" "Exif" "Exposure" "ExternalAudio"
"FeaturedTags" "FocalLength" "Followed" "Following" "GroupMember"
"GroupMembers" "HasAvatar" "HasFeaturedTags" "HasPages" "HasPermalink"
"HasTags" "HideAvatar" "HideDescription" "HideFromSearchEnabled"
"HideHeaderImage" "HideTitle" "HighRes" "HomePage" "Host" "IndexPage"
"IsActive" "IsDeactivated" "isOriginalEntry" "JumpPage" "Label"
"LikeCount" "Likes" "Lines" "Link" "LinkURL" "More" "NewDayDate"
"NextDayPage" "NextPage" "NextPost" "NoLikes" "NoSearchResults"
"NoSourceLogo" "NoteCount" "NotReblog" "Odd" "Pages" "Pagination"
"Panorama" "PermalinkPage" "PermalinkPagination" "Photo" "Photos"
"Photoset" "PinnedPostLabel" "PlayCount" "Post5" "PostNotes" "Posts"
"PostSummary" "PostTitle" "PreviousDayPage" "PreviousPage"
"PreviousPost" "Quote" "ReblogCount" "RebloggedFrom" "Reblogs"
"RelatedPosts" "ReplyCount" "SameDayDate" "SearchPage" "ShowAvatar"
"ShowDescription" "ShowHeaderImage" "ShowTitle" "Source" "SourceLogo"
"Submission" "SubmissionsEnabled" "TagPage" "Tags" "Text" "Thumbnail"
"Title" "TrackName" "Video" "VideoThumbnail" "VideoThumbnails"))
(block_name) @function
; Variable names. Known builtins get @variable.builtin, others get @variable.
((variable_name) @variable.builtin
(#any-of? @variable.builtin
"AccentColor" "Album" "AlbumArtURL" "Alt" "AmPm" "Answer" "Answerer"
"AnswererPortraitURL" "Aperture" "Artist" "Asker" "AskerPortraitURL"
"AskLabel" "AudioEmbed" "AudioPlayer" "Author" "AvatarShape"
"BackgroundColor" "Beats" "BlackLogoURL" "BlogURL" "Body" "Camera"
"CapitalAmPm" "Caption" "CopyrightYears" "CurrentPage" "CustomCSS"
"DayOfMonth" "DayOfMonthSuffix" "DayOfMonthWithZero" "DayOfWeek"
"DayOfWeekNumber" "DayOfYear" "Description" "EmbedUrl" "Excerpt"
"Exposure" "ExternalAudioURL" "Favicon" "FocalLength" "FollowedName"
"FollowedPortraitURL" "FollowedTitle" "FollowedURL" "FormattedPlayCount"
"GroupMemberName" "GroupMemberPortraitURL" "GroupMemberTitle"
"GroupMemberURL" "HeaderImage" "Host" "JSDescription" "JSPhotosetLayout"
"JSPlaintextDescription" "Label" "Length" "LikeButton" "LikeCount"
"Likes" "Line" "LinkCloseTag" "LinkOpenTag" "LinkURL" "LogoHeight"
"LogoWidth" "MetaDescription" "Minutes" "Month" "MonthNumber"
"MonthNumberWithZero" "Name" "NextDayPage" "NextPage" "NextPost"
"NoteCount" "NoteCountWithLabel" "NPF" "PageNumber" "Permalink"
"PhotoAlt" "PhotoCount" "PhotoHeight" "Photoset" "PhotosetLayout"
"PhotoURL" "PhotoWidth" "PinnedPostLabel" "PlaintextName" "PlayCount"
"PlayCountWithLabel" "PortraitURL" "PostAuthorName"
"PostAuthorPortraitURL" "PostAuthorTitle" "PostAuthorURL" "PostID"
"PostNotes" "PostNotesURL" "PostSummary" "PostTitle" "PostType"
"PreviousDayPage" "PreviousPage" "PreviousPost" "Question" "Quote"
"RawAudioURL" "ReblogButton" "ReblogCount" "ReblogParentName"
"ReblogParentPortraitURL" "ReblogParentTitle" "ReblogParentURL"
"ReblogRootName" "ReblogRootPortraitURL" "ReblogRootTitle"
"ReblogRootURL" "RelativePermalink" "Replies" "ReplyCount" "RSS"
"SearchQuery" "SearchResultCount" "Seconds" "ShortDayOfWeek"
"ShortMonth" "ShortURL" "ShortYear" "Source" "SourceTitle" "SourceURL"
"SubmitLabel" "Submitter" "SubmitterPortraitURL" "SubmitterURL" "Tag"
"TagsAsClasses" "TagURL" "TagURLChrono" "Target" "Thumbnail" "TimeAgo"
"Timestamp" "Title" "TitleColor" "TitleFont" "TitleFontWeight"
"TotalPages" "TrackName" "URL" "URLEncodedPermalink"
"URLSafeSearchQuery" "URLSafeTag" "Username" "UserNumber" "Video"
"VideoEmbed" "VideoThumbnailURL" "WeekOfYear" "Year"))
(variable_name) @variable
; Size suffix on URL-style variables. Numeric forms read as numbers,
; named forms (HighRes, Panorama, ...) as constants.
((variable_modifier) @number
(#match? @number "^[0-9]+(sq)?$"))
(variable_modifier) @constant.builtin
; text: / color: / font: / image: prefix and its argument.
(variable_prefix) @keyword
(prefix_argument) @variable
; {lang:Translatable string}
(lang_tag) @string.special
(lang_text) @string
; Block attributes: {block:Photoset rows="3"}
(attribute_name) @attribute
(attribute_value) @string
+7
View File
@@ -0,0 +1,7 @@
; All non-tag content is HTML. injection.combined concatenates every
; content node so an HTML tag opened in one chunk can close in another
; (across a {block:Foo}...{/block:Foo}). HTML's own injection queries
; then take over for <style> and <script>, giving us CSS and JS for free.
((content) @injection.content
(#set! injection.language "html")
(#set! injection.combined))