1{-# LANGUAGE OverloadedStrings #-} 2module Matterhorn.Themes 3 ( InternalTheme(..) 4 5 , defaultTheme 6 , internalThemes 7 , lookupTheme 8 , themeDocs 9 10 -- * Attribute names 11 , currentUserAttr 12 , timeAttr 13 , channelHeaderAttr 14 , channelListHeaderAttr 15 , currentChannelNameAttr 16 , unreadChannelAttr 17 , unreadGroupMarkerAttr 18 , mentionsChannelAttr 19 , currentTeamAttr 20 , urlAttr 21 , codeAttr 22 , emailAttr 23 , emojiAttr 24 , channelNameAttr 25 , clientMessageAttr 26 , clientHeaderAttr 27 , strikeThroughAttr 28 , clientEmphAttr 29 , clientStrongAttr 30 , dateTransitionAttr 31 , pinnedMessageIndicatorAttr 32 , newMessageTransitionAttr 33 , gapMessageAttr 34 , errorMessageAttr 35 , helpAttr 36 , helpEmphAttr 37 , channelSelectPromptAttr 38 , channelSelectMatchAttr 39 , completionAlternativeListAttr 40 , completionAlternativeCurrentAttr 41 , permalinkAttr 42 , dialogAttr 43 , dialogEmphAttr 44 , recentMarkerAttr 45 , replyParentAttr 46 , loadMoreAttr 47 , urlListSelectedAttr 48 , messageSelectAttr 49 , messageSelectStatusAttr 50 , urlSelectStatusAttr 51 , misspellingAttr 52 , editedMarkingAttr 53 , editedRecentlyMarkingAttr 54 , tabSelectedAttr 55 , tabUnselectedAttr 56 , buttonAttr 57 , buttonFocusedAttr 58 59 -- * Username formatting 60 , colorUsername 61 , attrForUsername 62 , usernameColorHashBuckets 63 ) 64where 65 66import Prelude () 67import Matterhorn.Prelude 68 69import Brick 70import Brick.Themes 71import Brick.Widgets.List 72import qualified Brick.Widgets.FileBrowser as FB 73import Brick.Widgets.Skylighting ( attrNameForTokenType 74 , attrMappingsForStyle 75 , highlightedCodeBlockAttr 76 ) 77import Brick.Forms ( focusedFormInputAttr ) 78import Data.Hashable ( hash ) 79import qualified Data.Map as M 80import qualified Data.Text as T 81import Graphics.Vty 82import qualified Skylighting.Styles as Sky 83import Skylighting.Types ( TokenType(..) ) 84 85import Matterhorn.Types ( InternalTheme(..), specialUserMentions ) 86 87 88helpAttr :: AttrName 89helpAttr = "help" 90 91helpEmphAttr :: AttrName 92helpEmphAttr = "helpEmphasis" 93 94recentMarkerAttr :: AttrName 95recentMarkerAttr = "recentChannelMarker" 96 97replyParentAttr :: AttrName 98replyParentAttr = "replyParentPreview" 99 100pinnedMessageIndicatorAttr :: AttrName 101pinnedMessageIndicatorAttr = "pinnedMessageIndicator" 102 103loadMoreAttr :: AttrName 104loadMoreAttr = "loadMoreMessages" 105 106urlListSelectedAttr :: AttrName 107urlListSelectedAttr = "urlListCursor" 108 109messageSelectAttr :: AttrName 110messageSelectAttr = "messageSelectCursor" 111 112editedMarkingAttr :: AttrName 113editedMarkingAttr = "editedMarking" 114 115editedRecentlyMarkingAttr :: AttrName 116editedRecentlyMarkingAttr = "editedRecentlyMarking" 117 118permalinkAttr :: AttrName 119permalinkAttr = "permalink" 120 121dialogAttr :: AttrName 122dialogAttr = "dialog" 123 124dialogEmphAttr :: AttrName 125dialogEmphAttr = "dialogEmphasis" 126 127channelSelectMatchAttr :: AttrName 128channelSelectMatchAttr = "channelSelectMatch" 129 130channelSelectPromptAttr :: AttrName 131channelSelectPromptAttr = "channelSelectPrompt" 132 133completionAlternativeListAttr :: AttrName 134completionAlternativeListAttr = "tabCompletionAlternative" 135 136completionAlternativeCurrentAttr :: AttrName 137completionAlternativeCurrentAttr = "tabCompletionCursor" 138 139timeAttr :: AttrName 140timeAttr = "time" 141 142currentUserAttr :: AttrName 143currentUserAttr = "currentUser" 144 145channelHeaderAttr :: AttrName 146channelHeaderAttr = "channelHeader" 147 148channelListHeaderAttr :: AttrName 149channelListHeaderAttr = "channelListSectionHeader" 150 151currentChannelNameAttr :: AttrName 152currentChannelNameAttr = "currentChannelName" 153 154channelNameAttr :: AttrName 155channelNameAttr = "channelName" 156 157unreadChannelAttr :: AttrName 158unreadChannelAttr = "unreadChannel" 159 160unreadGroupMarkerAttr :: AttrName 161unreadGroupMarkerAttr = "unreadChannelGroupMarker" 162 163mentionsChannelAttr :: AttrName 164mentionsChannelAttr = "channelWithMentions" 165 166currentTeamAttr :: AttrName 167currentTeamAttr = "currentTeam" 168 169tabSelectedAttr :: AttrName 170tabSelectedAttr = "tabSelected" 171 172tabUnselectedAttr :: AttrName 173tabUnselectedAttr = "tabUnselected" 174 175dateTransitionAttr :: AttrName 176dateTransitionAttr = "dateTransition" 177 178newMessageTransitionAttr :: AttrName 179newMessageTransitionAttr = "newMessageTransition" 180 181urlAttr :: AttrName 182urlAttr = "url" 183 184codeAttr :: AttrName 185codeAttr = "codeBlock" 186 187emailAttr :: AttrName 188emailAttr = "email" 189 190emojiAttr :: AttrName 191emojiAttr = "emoji" 192 193clientMessageAttr :: AttrName 194clientMessageAttr = "clientMessage" 195 196clientHeaderAttr :: AttrName 197clientHeaderAttr = "markdownHeader" 198 199strikeThroughAttr :: AttrName 200strikeThroughAttr = "markdownStrikethrough" 201 202clientEmphAttr :: AttrName 203clientEmphAttr = "markdownEmph" 204 205clientStrongAttr :: AttrName 206clientStrongAttr = "markdownStrong" 207 208errorMessageAttr :: AttrName 209errorMessageAttr = "errorMessage" 210 211gapMessageAttr :: AttrName 212gapMessageAttr = "gapMessage" 213 214misspellingAttr :: AttrName 215misspellingAttr = "misspelling" 216 217messageSelectStatusAttr :: AttrName 218messageSelectStatusAttr = "messageSelectStatus" 219 220urlSelectStatusAttr :: AttrName 221urlSelectStatusAttr = "urlSelectStatus" 222 223buttonAttr :: AttrName 224buttonAttr = "button" 225 226buttonFocusedAttr :: AttrName 227buttonFocusedAttr = buttonAttr <> "focused" 228 229lookupTheme :: Text -> Maybe InternalTheme 230lookupTheme n = find ((== n) . internalThemeName) internalThemes 231 232internalThemes :: [InternalTheme] 233internalThemes = validateInternalTheme <$> 234 [ darkColorTheme 235 , darkColor256Theme 236 , lightColorTheme 237 , lightColor256Theme 238 ] 239 240validateInternalTheme :: InternalTheme -> InternalTheme 241validateInternalTheme it = 242 let un = undocumentedAttrNames (internalTheme it) 243 in if not $ null un 244 then error $ "Internal theme " <> show (T.unpack (internalThemeName it)) <> 245 " references undocumented attribute names: " <> show un 246 else it 247 248undocumentedAttrNames :: Theme -> [AttrName] 249undocumentedAttrNames t = 250 let noDocs k = isNothing $ attrNameDescription themeDocs k 251 in filter noDocs (M.keys $ themeDefaultMapping t) 252 253defaultTheme :: InternalTheme 254defaultTheme = darkColorTheme 255 256lightColorTheme :: InternalTheme 257lightColorTheme = InternalTheme name theme desc 258 where 259 theme = newTheme def $ lightAttrs usernameColors16 260 name = "builtin:light" 261 def = black `on` white 262 desc = "A color theme for terminal windows with light background colors" 263 264lightColor256Theme :: InternalTheme 265lightColor256Theme = InternalTheme name theme desc 266 where 267 theme = newTheme def $ lightAttrs usernameColors256 268 name = "builtin:light256" 269 def = black `on` white 270 desc = "Like builtin:light, but with 256-color username colors" 271 272lightAttrs :: [Attr] -> [(AttrName, Attr)] 273lightAttrs usernameColors = 274 let sty = Sky.kate 275 in [ (timeAttr, fg black) 276 , (buttonAttr, black `on` cyan) 277 , (buttonFocusedAttr, black `on` yellow) 278 , (currentUserAttr, defAttr `withStyle` bold) 279 , (channelHeaderAttr, fg black) 280 , (channelListHeaderAttr, fg cyan) 281 , (currentChannelNameAttr, black `on` yellow `withStyle` bold) 282 , (unreadChannelAttr, black `on` cyan `withStyle` bold) 283 , (unreadGroupMarkerAttr, fg black `withStyle` bold) 284 , (mentionsChannelAttr, black `on` red `withStyle` bold) 285 , (urlAttr, fg brightYellow) 286 , (emailAttr, fg yellow) 287 , (codeAttr, fg magenta) 288 , (emojiAttr, fg yellow) 289 , (channelNameAttr, fg blue) 290 , (clientMessageAttr, fg black) 291 , (clientEmphAttr, fg black `withStyle` bold) 292 , (clientStrongAttr, fg black `withStyle` bold `withStyle` underline) 293 , (clientHeaderAttr, fg red `withStyle` bold) 294 , (strikeThroughAttr, defAttr `withStyle` strikethrough) 295 , (dateTransitionAttr, fg green) 296 , (newMessageTransitionAttr, black `on` yellow) 297 , (errorMessageAttr, fg red) 298 , (gapMessageAttr, fg red) 299 , (helpAttr, fg black) 300 , (pinnedMessageIndicatorAttr, black `on` cyan) 301 , (helpEmphAttr, fg blue `withStyle` bold) 302 , (channelSelectMatchAttr, black `on` magenta `withStyle` underline) 303 , (channelSelectPromptAttr, fg black) 304 , (completionAlternativeListAttr, white `on` blue) 305 , (completionAlternativeCurrentAttr, black `on` yellow) 306 , (dialogAttr, black `on` cyan) 307 , (dialogEmphAttr, fg white) 308 , (permalinkAttr, fg green) 309 , (listSelectedFocusedAttr, black `on` yellow) 310 , (recentMarkerAttr, fg black `withStyle` bold) 311 , (loadMoreAttr, black `on` cyan) 312 , (urlListSelectedAttr, black `on` yellow) 313 , (messageSelectAttr, black `on` yellow) 314 , (messageSelectStatusAttr, fg black) 315 , (urlSelectStatusAttr, fg black) 316 , (misspellingAttr, fg red `withStyle` underline) 317 , (editedMarkingAttr, fg yellow) 318 , (editedRecentlyMarkingAttr, black `on` yellow) 319 , (tabSelectedAttr, black `on` yellow) 320 , (focusedFormInputAttr, black `on` yellow) 321 , (currentTeamAttr, black `on` yellow) 322 , (FB.fileBrowserCurrentDirectoryAttr, white `on` blue) 323 , (FB.fileBrowserSelectionInfoAttr, white `on` blue) 324 , (FB.fileBrowserDirectoryAttr, fg blue) 325 , (FB.fileBrowserBlockDeviceAttr, fg magenta) 326 , (FB.fileBrowserCharacterDeviceAttr, fg green) 327 , (FB.fileBrowserNamedPipeAttr, fg yellow) 328 , (FB.fileBrowserSymbolicLinkAttr, fg cyan) 329 , (FB.fileBrowserUnixSocketAttr, fg red) 330 ] <> 331 ((\(i, a) -> (usernameAttr i, a)) <$> zip [0..usernameColorHashBuckets-1] (cycle usernameColors)) <> 332 (filter skipBaseCodeblockAttr $ attrMappingsForStyle sty) 333 334darkAttrs :: [Attr] -> [(AttrName, Attr)] 335darkAttrs usernameColors = 336 let sty = Sky.espresso 337 in [ (timeAttr, fg white) 338 , (buttonAttr, black `on` cyan) 339 , (buttonFocusedAttr, black `on` yellow) 340 , (currentUserAttr, defAttr `withStyle` bold) 341 , (channelHeaderAttr, fg white) 342 , (channelListHeaderAttr, fg cyan) 343 , (currentChannelNameAttr, black `on` yellow `withStyle` bold) 344 , (unreadChannelAttr, black `on` cyan `withStyle` bold) 345 , (unreadGroupMarkerAttr, fg white `withStyle` bold) 346 , (mentionsChannelAttr, black `on` brightMagenta `withStyle` bold) 347 , (urlAttr, fg yellow) 348 , (emailAttr, fg yellow) 349 , (codeAttr, fg magenta) 350 , (emojiAttr, fg yellow) 351 , (channelNameAttr, fg cyan) 352 , (pinnedMessageIndicatorAttr, fg cyan `withStyle` bold) 353 , (clientMessageAttr, fg white) 354 , (clientEmphAttr, fg white `withStyle` bold) 355 , (clientStrongAttr, fg white `withStyle` bold `withStyle` underline) 356 , (clientHeaderAttr, fg red `withStyle` bold) 357 , (strikeThroughAttr, defAttr `withStyle` strikethrough) 358 , (dateTransitionAttr, fg green) 359 , (newMessageTransitionAttr, fg yellow `withStyle` bold) 360 , (errorMessageAttr, fg red) 361 , (gapMessageAttr, black `on` yellow) 362 , (helpAttr, fg white) 363 , (helpEmphAttr, fg cyan `withStyle` bold) 364 , (channelSelectMatchAttr, black `on` magenta `withStyle` underline) 365 , (channelSelectPromptAttr, fg white) 366 , (completionAlternativeListAttr, white `on` blue) 367 , (completionAlternativeCurrentAttr, black `on` yellow) 368 , (dialogAttr, black `on` cyan) 369 , (dialogEmphAttr, fg white) 370 , (permalinkAttr, fg brightCyan) 371 , (listSelectedFocusedAttr, black `on` yellow) 372 , (recentMarkerAttr, fg yellow `withStyle` bold) 373 , (loadMoreAttr, black `on` cyan) 374 , (urlListSelectedAttr, black `on` yellow) 375 , (messageSelectAttr, black `on` yellow) 376 , (messageSelectStatusAttr, fg white) 377 , (urlSelectStatusAttr, fg white) 378 , (misspellingAttr, fg red `withStyle` underline) 379 , (editedMarkingAttr, fg yellow) 380 , (editedRecentlyMarkingAttr, black `on` yellow) 381 , (tabSelectedAttr, black `on` yellow) 382 , (focusedFormInputAttr, black `on` yellow) 383 , (currentTeamAttr, black `on` yellow) 384 , (FB.fileBrowserCurrentDirectoryAttr, white `on` blue) 385 , (FB.fileBrowserSelectionInfoAttr, white `on` blue) 386 , (FB.fileBrowserDirectoryAttr, fg blue) 387 , (FB.fileBrowserBlockDeviceAttr, fg magenta) 388 , (FB.fileBrowserCharacterDeviceAttr, fg green) 389 , (FB.fileBrowserNamedPipeAttr, fg yellow) 390 , (FB.fileBrowserSymbolicLinkAttr, fg cyan) 391 , (FB.fileBrowserUnixSocketAttr, fg red) 392 ] <> 393 ((\(i, a) -> (usernameAttr i, a)) <$> zip [0..usernameColorHashBuckets-1] (cycle usernameColors)) <> 394 (filter skipBaseCodeblockAttr $ attrMappingsForStyle sty) 395 396skipBaseCodeblockAttr :: (AttrName, Attr) -> Bool 397skipBaseCodeblockAttr = ((/= highlightedCodeBlockAttr) . fst) 398 399darkColorTheme :: InternalTheme 400darkColorTheme = InternalTheme name theme desc 401 where 402 theme = newTheme def $ darkAttrs usernameColors16 403 name = "builtin:dark" 404 def = defAttr 405 desc = "A color theme for terminal windows with dark background colors" 406 407darkColor256Theme :: InternalTheme 408darkColor256Theme = InternalTheme name theme desc 409 where 410 theme = newTheme def $ darkAttrs usernameColors256 411 name = "builtin:dark256" 412 def = defAttr 413 desc = "Like builtin:dark, but with 256-color username colors" 414 415usernameAttr :: Int -> AttrName 416usernameAttr i = "username" <> (attrName $ show i) 417 418-- | Render a string with a color chosen based on the text of a 419-- username. 420-- 421-- This function takes some display text and renders it using an 422-- attribute based on the username associated with the text. If the 423-- username associated with the text is equal to the username of 424-- the user running Matterhorn, the display text is formatted with 425-- 'currentAttr'. Otherwise it is formatted with an attribute chosen 426-- by hashing the associated username and choosing from amongst the 427-- username color hash buckets with 'usernameAttr'. 428-- 429-- Usually the first argument to this function will be @myUsername st@, 430-- where @st@ is a 'ChatState'. 431-- 432-- The most common way to call this function is 433-- 434-- @colorUsername (myUsername st) u u 435-- 436-- The third argument is allowed to vary from the second since sometimes 437-- we call this with the user's status sigil as the third argument. 438colorUsername :: Text 439 -- ^ The username for the user currently running 440 -- Matterhorn 441 -> Text 442 -- ^ The username associated with the text to render 443 -> Text 444 -- ^ The text to render 445 -> Widget a 446colorUsername current username display = 447 let aName = attrForUsername username 448 maybeWithCurrentAttr = if current == username 449 then withAttr currentUserAttr 450 else id 451 in withDefAttr aName $ 452 maybeWithCurrentAttr $ 453 txt (display) 454 455-- | Return the attribute name to use for the specified username. 456-- The input username is expected to be the username only (i.e. no 457-- sigil). 458-- 459-- If the input username is a special reserved username such as "all", 460-- the @clientEmphAttr@ attribute name will be returned. Otherwise 461-- a hash-bucket username attribute name will be returned based on 462-- the hash value of the username and the number of hash buckets 463-- (@usernameColorHashBuckets@). 464attrForUsername :: Text 465 -- ^ The username to get an attribute for 466 -> AttrName 467attrForUsername username = 468 let normalizedUsername = T.toLower username 469 aName = if normalizedUsername `elem` specialUserMentions 470 then clientEmphAttr 471 else usernameAttr h 472 h = hash normalizedUsername `mod` usernameColorHashBuckets 473 in aName 474 475-- | The number of hash buckets to use when hashing usernames to choose 476-- their colors. 477usernameColorHashBuckets :: Int 478usernameColorHashBuckets = 50 479 480usernameColors16 :: [Attr] 481usernameColors16 = 482 [ fg red 483 , fg green 484 , fg yellow 485 , fg blue 486 , fg magenta 487 , fg cyan 488 , fg brightRed 489 , fg brightGreen 490 , fg brightYellow 491 , fg brightBlue 492 , fg brightMagenta 493 , fg brightCyan 494 ] 495 496usernameColors256 :: [Attr] 497usernameColors256 = mkColor <$> username256ColorChoices 498 where 499 mkColor (r, g, b) = defAttr `withForeColor` rgbColor r g b 500 501username256ColorChoices :: [(Integer, Integer, Integer)] 502username256ColorChoices = 503 [ (255, 0, 86) 504 , (158, 0, 142) 505 , (14, 76, 161) 506 , (255, 229, 2) 507 , (149, 0, 58) 508 , (255, 147, 126) 509 , (164, 36, 0) 510 , (98, 14, 0) 511 , (0, 0, 255) 512 , (106, 130, 108) 513 , (0, 174, 126) 514 , (194, 140, 159) 515 , (0, 143, 156) 516 , (95, 173, 78) 517 , (255, 2, 157) 518 , (255, 116, 163) 519 , (152, 255, 82) 520 , (167, 87, 64) 521 , (254, 137, 0) 522 , (1, 208, 255) 523 , (187, 136, 0) 524 , (117, 68, 177) 525 , (165, 255, 210) 526 , (122, 71, 130) 527 , (0, 71, 84) 528 , (181, 0, 255) 529 , (144, 251, 146) 530 , (189, 211, 147) 531 , (229, 111, 254) 532 , (222, 255, 116) 533 , (0, 255, 120) 534 , (0, 155, 255) 535 , (0, 100, 1) 536 , (0, 118, 255) 537 , (133, 169, 0) 538 , (0, 185, 23) 539 , (120, 130, 49) 540 , (0, 255, 198) 541 , (255, 110, 65) 542 ] 543 544-- Functions for dealing with Skylighting styles 545 546attrNameDescription :: ThemeDocumentation -> AttrName -> Maybe Text 547attrNameDescription td an = M.lookup an (themeDescriptions td) 548 549themeDocs :: ThemeDocumentation 550themeDocs = ThemeDocumentation $ M.fromList $ 551 [ ( timeAttr 552 , "Timestamps on chat messages" 553 ) 554 , ( channelHeaderAttr 555 , "Channel headers displayed above chat message lists" 556 ) 557 , ( channelListHeaderAttr 558 , "The heading of the channel list sections" 559 ) 560 , ( currentChannelNameAttr 561 , "The currently selected channel in the channel list" 562 ) 563 , ( unreadChannelAttr 564 , "A channel in the channel list with unread messages" 565 ) 566 , ( unreadGroupMarkerAttr 567 , "The channel group marker indicating unread messages" 568 ) 569 , ( mentionsChannelAttr 570 , "A channel in the channel list with unread mentions" 571 ) 572 , ( urlAttr 573 , "A URL in a chat message" 574 ) 575 , ( codeAttr 576 , "A code block in a chat message with no language indication" 577 ) 578 , ( emailAttr 579 , "An e-mail address in a chat message" 580 ) 581 , ( emojiAttr 582 , "A text emoji indication in a chat message" 583 ) 584 , ( channelNameAttr 585 , "A channel name in a chat message" 586 ) 587 , ( clientMessageAttr 588 , "A Matterhorn diagnostic or informative message" 589 ) 590 , ( clientHeaderAttr 591 , "Markdown heading" 592 ) 593 , ( strikeThroughAttr 594 , "Markdown strikethrough text" 595 ) 596 , ( clientEmphAttr 597 , "Markdown 'emphasized' text" 598 ) 599 , ( clientStrongAttr 600 , "Markdown 'strong' text" 601 ) 602 , ( dateTransitionAttr 603 , "Date transition lines between chat messages on different days" 604 ) 605 , ( pinnedMessageIndicatorAttr 606 , "The indicator for messages that have been pinned" 607 ) 608 , ( newMessageTransitionAttr 609 , "The 'New Messages' line that appears above unread messages" 610 ) 611 , ( tabSelectedAttr 612 , "Selected tabs in tabbed windows" 613 ) 614 , ( tabUnselectedAttr 615 , "Unselected tabs in tabbed windows" 616 ) 617 , ( errorMessageAttr 618 , "Matterhorn error messages" 619 ) 620 , ( gapMessageAttr 621 , "Matterhorn message gap information" 622 ) 623 , ( helpAttr 624 , "The help screen text" 625 ) 626 , ( helpEmphAttr 627 , "The help screen's emphasized text" 628 ) 629 , ( channelSelectPromptAttr 630 , "Channel selection: prompt" 631 ) 632 , ( channelSelectMatchAttr 633 , "Channel selection: the portion of a channel name that matches" 634 ) 635 , ( completionAlternativeListAttr 636 , "Tab completion alternatives" 637 ) 638 , ( completionAlternativeCurrentAttr 639 , "The currently-selected tab completion alternative" 640 ) 641 , ( permalinkAttr 642 , "A post permalink" 643 ) 644 , ( dialogAttr 645 , "Dialog box text" 646 ) 647 , ( dialogEmphAttr 648 , "Dialog box emphasized text" 649 ) 650 , ( recentMarkerAttr 651 , "The marker indicating the channel last visited" 652 ) 653 , ( replyParentAttr 654 , "The first line of parent messages appearing above reply messages" 655 ) 656 , ( loadMoreAttr 657 , "The 'Load More' line that appears at the top of a chat message list" 658 ) 659 , ( urlListSelectedAttr 660 , "URL list: the selected URL" 661 ) 662 , ( messageSelectAttr 663 , "Message selection: the currently-selected message" 664 ) 665 , ( messageSelectStatusAttr 666 , "Message selection: the message selection actions" 667 ) 668 , ( urlSelectStatusAttr 669 , "Link selection: the message selection actions" 670 ) 671 , ( misspellingAttr 672 , "A misspelled word in the chat message editor" 673 ) 674 , ( editedMarkingAttr 675 , "The 'edited' marking that appears on edited messages" 676 ) 677 , ( editedRecentlyMarkingAttr 678 , "The 'edited' marking that appears on newly-edited messages" 679 ) 680 , ( highlightedCodeBlockAttr 681 , "The base attribute for syntax-highlighted code blocks" 682 ) 683 , ( attrNameForTokenType KeywordTok 684 , "Syntax highlighting: Keyword" 685 ) 686 , ( attrNameForTokenType DataTypeTok 687 , "Syntax highlighting: DataType" 688 ) 689 , ( attrNameForTokenType DecValTok 690 , "Syntax highlighting: Declaration" 691 ) 692 , ( attrNameForTokenType BaseNTok 693 , "Syntax highlighting: BaseN" 694 ) 695 , ( attrNameForTokenType FloatTok 696 , "Syntax highlighting: Float" 697 ) 698 , ( attrNameForTokenType ConstantTok 699 , "Syntax highlighting: Constant" 700 ) 701 , ( attrNameForTokenType CharTok 702 , "Syntax highlighting: Char" 703 ) 704 , ( attrNameForTokenType SpecialCharTok 705 , "Syntax highlighting: Special Char" 706 ) 707 , ( attrNameForTokenType StringTok 708 , "Syntax highlighting: String" 709 ) 710 , ( attrNameForTokenType VerbatimStringTok 711 , "Syntax highlighting: Verbatim String" 712 ) 713 , ( attrNameForTokenType SpecialStringTok 714 , "Syntax highlighting: Special String" 715 ) 716 , ( attrNameForTokenType ImportTok 717 , "Syntax highlighting: Import" 718 ) 719 , ( attrNameForTokenType CommentTok 720 , "Syntax highlighting: Comment" 721 ) 722 , ( attrNameForTokenType DocumentationTok 723 , "Syntax highlighting: Documentation" 724 ) 725 , ( attrNameForTokenType AnnotationTok 726 , "Syntax highlighting: Annotation" 727 ) 728 , ( attrNameForTokenType CommentVarTok 729 , "Syntax highlighting: Comment" 730 ) 731 , ( attrNameForTokenType OtherTok 732 , "Syntax highlighting: Other" 733 ) 734 , ( attrNameForTokenType FunctionTok 735 , "Syntax highlighting: Function" 736 ) 737 , ( attrNameForTokenType VariableTok 738 , "Syntax highlighting: Variable" 739 ) 740 , ( attrNameForTokenType ControlFlowTok 741 , "Syntax highlighting: Control Flow" 742 ) 743 , ( attrNameForTokenType OperatorTok 744 , "Syntax highlighting: Operator" 745 ) 746 , ( attrNameForTokenType BuiltInTok 747 , "Syntax highlighting: Built-In" 748 ) 749 , ( attrNameForTokenType ExtensionTok 750 , "Syntax highlighting: Extension" 751 ) 752 , ( attrNameForTokenType PreprocessorTok 753 , "Syntax highlighting: Preprocessor" 754 ) 755 , ( attrNameForTokenType AttributeTok 756 , "Syntax highlighting: Attribute" 757 ) 758 , ( attrNameForTokenType RegionMarkerTok 759 , "Syntax highlighting: Region Marker" 760 ) 761 , ( attrNameForTokenType InformationTok 762 , "Syntax highlighting: Information" 763 ) 764 , ( attrNameForTokenType WarningTok 765 , "Syntax highlighting: Warning" 766 ) 767 , ( attrNameForTokenType AlertTok 768 , "Syntax highlighting: Alert" 769 ) 770 , ( attrNameForTokenType ErrorTok 771 , "Syntax highlighting: Error" 772 ) 773 , ( attrNameForTokenType NormalTok 774 , "Syntax highlighting: Normal text" 775 ) 776 , ( listSelectedFocusedAttr 777 , "The selected channel" 778 ) 779 , ( focusedFormInputAttr 780 , "A form input that has focus" 781 ) 782 , ( FB.fileBrowserAttr 783 , "The base file browser attribute" 784 ) 785 , ( FB.fileBrowserCurrentDirectoryAttr 786 , "The file browser current directory attribute" 787 ) 788 , ( FB.fileBrowserSelectionInfoAttr 789 , "The file browser selection information attribute" 790 ) 791 , ( FB.fileBrowserDirectoryAttr 792 , "Attribute for directories in the file browser" 793 ) 794 , ( FB.fileBrowserBlockDeviceAttr 795 , "Attribute for block devices in the file browser" 796 ) 797 , ( FB.fileBrowserRegularFileAttr 798 , "Attribute for regular files in the file browser" 799 ) 800 , ( FB.fileBrowserCharacterDeviceAttr 801 , "Attribute for character devices in the file browser" 802 ) 803 , ( FB.fileBrowserNamedPipeAttr 804 , "Attribute for named pipes in the file browser" 805 ) 806 , ( FB.fileBrowserSymbolicLinkAttr 807 , "Attribute for symbolic links in the file browser" 808 ) 809 , ( FB.fileBrowserUnixSocketAttr 810 , "Attribute for Unix sockets in the file browser" 811 ) 812 , ( buttonAttr 813 , "Attribute for input form buttons" 814 ) 815 , ( buttonFocusedAttr 816 , "Attribute for focused input form buttons" 817 ) 818 , ( currentUserAttr 819 , "Attribute for the username of the user running Matterhorn" 820 ) 821 , ( currentTeamAttr 822 , "The currently-selected team" 823 ) 824 ] <> [ (usernameAttr i, T.pack $ "Username color " <> show i) 825 | i <- [0..usernameColorHashBuckets-1] 826 ] 827