Skip to content

Commit ca3611f

Browse files
authored
Merge pull request #819 from tsoding/817
(#817) Escape special org-mode characters while rendering gist documents
2 parents 59c80d9 + b5f75c7 commit ca3611f

File tree

6 files changed

+99
-32
lines changed

6 files changed

+99
-32
lines changed

HyperNerd.cabal

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ executable HyperNerd
107107
, Schedule
108108
, Bot.Asciify
109109
, Free
110+
, OrgMode
110111

111112
-- LANGUAGE extensions used by modules in this package.
112113
other-extensions: OverloadedStrings
@@ -224,6 +225,8 @@ test-suite HyperNerdTest
224225
, Bot.FridayTest
225226
, Bot.ExprTest
226227
, Bot.GitHub
228+
, OrgMode
229+
, OrgModeTest
227230
, Data.Maybe.Extra
228231
, Data.Time.Extra
229232
, Data.Time.ExtraTest

src/Bot/Friday.hs

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import Data.Time
3232
import Effect
3333
import Entity
3434
import HyperNerd.Comonad
35+
import OrgMode
3536
import Property
3637
import Reaction
3738
import Regexp
@@ -240,20 +241,21 @@ videoCountCommand =
240241

241242
renderQueue :: [FridayVideo] -> T.Text
242243
renderQueue queue@(FridayVideo {fridayVideoAuthor = user}:_) =
243-
T.unlines $
244-
([qmb|** {user}
245-
246-
Video Count {length queue}
247-
248-
|] :) $
249-
map
250-
(\video ->
251-
let ytId = fromMaybe "dQw4w9WgXcQ" $ ytLinkId $ fridayVideoName video
252-
in [qms||{fridayVideoDate video}
253-
|{fridayVideoAuthor video}
254-
|{fridayVideoName video}
255-
|[[https://img.youtube.com/vi/{ytId}/default.jpg]]||])
256-
queue
244+
[qmb|** {user}
245+
246+
Video Count {length queue}\n\n
247+
|] <>
248+
renderTable
249+
["Date", "Submitter", "Video", "Thumbnail"]
250+
(map
251+
(\video ->
252+
let ytId = fromMaybe "dQw4w9WgXcQ" $ ytLinkId $ fridayVideoName video
253+
in [ [qms|{fridayVideoDate video}|]
254+
, [qms|{fridayVideoAuthor video}|]
255+
, [qms|{fridayVideoName video}|]
256+
, [qms|[[https://img.youtube.com/vi/{ytId}/default.jpg]]|]
257+
])
258+
queue)
257259
renderQueue [] = ""
258260

259261
renderQueues :: Maybe T.Text -> VideoQueues -> T.Text

src/Bot/Help.hs

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import Data.Proxy
2020
import qualified Data.Text as T
2121
import Effect
2222
import Entity
23+
import OrgMode
2324
import Property
2425
import Reaction
2526
import Text.InterpolatedString.QM
@@ -73,28 +74,31 @@ refreshHelpGistId =
7374
Reaction replyMessage
7475

7576
gistRenderCommandTable :: CommandTable -> T.Text
76-
gistRenderCommandTable =
77-
([qms|* Builtin Commands\n{header}\n|-\n|] <>) .
78-
T.unlines . map renderRow . M.toList
77+
gistRenderCommandTable commandTable = [qms|* Builtin Commands\n{table}\n|]
7978
where
80-
header :: T.Text
81-
header = "|Name|Description|Location|"
82-
renderRow :: (T.Text, BuiltinCommand) -> T.Text
83-
renderRow (name, command) =
84-
[qms||{name}|{bcDescription command}|{location}||]
85-
where
86-
location :: T.Text
87-
location = [qms|[[{bcGitHubLocation command}][Source↗]]|]
79+
table :: T.Text
80+
table =
81+
renderTable ["Name", "Description", "Location"] $
82+
map
83+
(\(name, command) ->
84+
[ name
85+
, bcDescription command
86+
, [qms|[[{bcGitHubLocation command}][Source↗]]|]
87+
]) $
88+
M.toList commandTable
8889

8990
gistRenderCustomCommandsTable :: [Entity CustomCommand] -> T.Text
90-
gistRenderCustomCommandsTable =
91-
([qms|* Custom commands\n{header}\n|-\n|] <>) .
92-
T.unlines . map (renderRow . entityPayload)
91+
gistRenderCustomCommandsTable customCommands =
92+
[qms|* Custom commands\n{table}\n|]
9393
where
94-
header :: T.Text
95-
header = "|Name|Definition|%times|"
96-
renderRow (CustomCommand name message times) =
97-
[qms||{name}|{message}|{times}||]
94+
table :: T.Text
95+
table =
96+
renderTable ["Name", "Definition", "%times"] $
97+
map
98+
((\(CustomCommand name message times) ->
99+
[name, message, T.pack $ show times]) .
100+
entityPayload)
101+
customCommands
98102

99103
refreshHelpGist :: CommandTable -> GistId -> Effect ()
100104
refreshHelpGist commandTable gistId = do

src/OrgMode.hs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{-# LANGUAGE OverloadedStrings #-}
2+
3+
module OrgMode
4+
( renderTable
5+
) where
6+
7+
import Data.List
8+
import qualified Data.Text as T
9+
10+
charEscapeList :: String
11+
charEscapeList = "|"
12+
13+
renderTable :: [T.Text] -> [[T.Text]] -> T.Text
14+
renderTable header rows =
15+
T.unlines ([renderRow header, "|-"] <> map (renderRow . normalizeRow) rows)
16+
where
17+
normalizeRow row = take (length header) (row ++ repeat "")
18+
renderRow :: [T.Text] -> T.Text
19+
renderRow columns =
20+
"|" <> T.concat (intersperse "|" $ map escapeColumn columns) <> "|"
21+
escapeColumn :: T.Text -> T.Text
22+
escapeColumn = T.filter $ not . (`elem` charEscapeList)

test/OrgModeTest.hs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{-# LANGUAGE OverloadedStrings #-}
2+
3+
module OrgModeTest
4+
( spec
5+
) where
6+
7+
import OrgMode
8+
import Test.HUnit
9+
10+
spec :: Test
11+
spec =
12+
TestLabel "Rendering OrgMode table" $
13+
TestCase $
14+
assertEqual
15+
""
16+
"|hello|world|foo|\n\
17+
\|-\n\
18+
\|1|2|3|\n\
19+
\|1|2|3|\n\
20+
\|1|2||\n\
21+
\|1|||\n\
22+
\||||\n\
23+
\|1|2|3|\n\
24+
\|~|||\n" $
25+
renderTable
26+
["hello", "world", "foo"]
27+
[ ["1", "2", "3"]
28+
, ["1", "2", "3"]
29+
, ["1", "2"]
30+
, ["1"]
31+
, []
32+
, ["1", "2", "3", "4", "5"]
33+
, ["|~"]
34+
]

test/Test.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import qualified Bot.PollTest
1818
import qualified Bot.TwitchTest
1919
import qualified CommandTest
2020
import qualified Data.Time.ExtraTest
21+
import qualified OrgModeTest
2122
import qualified Sqlite.EntityPersistenceTest
2223
import System.Exit
2324
import Test.HUnit
@@ -36,6 +37,7 @@ main = do
3637
, CommandTest.spec
3738
, Sqlite.EntityPersistenceTest.spec
3839
, Data.Time.ExtraTest.spec
40+
, OrgModeTest.spec
3941
]
4042
if errors results + failures results == 0
4143
then exitSuccess

0 commit comments

Comments
 (0)