1{-# LANGUAGE CPP                #-}
2{-# LANGUAGE DeriveAnyClass     #-}
3{-# LANGUAGE DerivingStrategies #-}
4
5module Development.IDE.Spans.Common (
6  showGhc
7, showNameWithoutUniques
8, unqualIEWrapName
9, safeTyThingId
10, safeTyThingType
11, SpanDoc(..)
12, SpanDocUris(..)
13, emptySpanDoc
14, spanDocToMarkdown
15, spanDocToMarkdownForTest
16, DocMap
17, KindMap
18) where
19
20import           Control.DeepSeq
21import           Data.List.Extra
22import           Data.Maybe
23import qualified Data.Text                    as T
24import           GHC.Generics
25
26import           ConLike
27import           DynFlags
28import           GHC
29import           NameEnv
30import           Outputable                   hiding ((<>))
31import           Var
32
33import           Development.IDE.GHC.Compat   (oldMkUserStyle,
34                                               oldRenderWithStyle)
35import           Development.IDE.GHC.Orphans  ()
36import           Development.IDE.GHC.Util
37import qualified Documentation.Haddock.Parser as H
38import qualified Documentation.Haddock.Types  as H
39import           RdrName                      (rdrNameOcc)
40
41type DocMap = NameEnv SpanDoc
42type KindMap = NameEnv TyThing
43
44showGhc :: Outputable a => a -> T.Text
45showGhc = showSD . ppr
46
47showSD :: SDoc -> T.Text
48showSD = T.pack . unsafePrintSDoc
49
50showNameWithoutUniques :: Outputable a => a -> T.Text
51showNameWithoutUniques = T.pack . prettyprint
52  where
53    dyn = unsafeGlobalDynFlags `gopt_set` Opt_SuppressUniques
54    prettyprint x = oldRenderWithStyle dyn (ppr x) style
55    style = oldMkUserStyle dyn neverQualify AllTheWay
56
57-- | Shows IEWrappedName, without any modifier, qualifier or unique identifier.
58unqualIEWrapName :: IEWrappedName RdrName -> T.Text
59unqualIEWrapName = showNameWithoutUniques . rdrNameOcc . ieWrappedName
60
61-- From haskell-ide-engine/src/Haskell/Ide/Engine/Support/HieExtras.hs
62safeTyThingType :: TyThing -> Maybe Type
63safeTyThingType thing
64  | Just i <- safeTyThingId thing = Just (varType i)
65safeTyThingType (ATyCon tycon)    = Just (tyConKind tycon)
66safeTyThingType _                 = Nothing
67
68safeTyThingId :: TyThing -> Maybe Id
69safeTyThingId (AnId i)           = Just i
70safeTyThingId (AConLike conLike) = conLikeWrapId_maybe conLike
71safeTyThingId _                  = Nothing
72
73-- Possible documentation for an element in the code
74data SpanDoc
75  = SpanDocString HsDocString SpanDocUris
76SpanDocText   [T.Text] SpanDocUris
77  deriving stock (Eq, Show, Generic)
78  deriving anyclass NFData
79
80data SpanDocUris =
81  SpanDocUris
82  { spanDocUriDoc :: Maybe T.Text -- ^ The haddock html page
83  , spanDocUriSrc :: Maybe T.Text -- ^ The hyperlinked source html page
84  } deriving stock (Eq, Show, Generic)
85    deriving anyclass NFData
86
87emptySpanDoc :: SpanDoc
88emptySpanDoc = SpanDocText [] (SpanDocUris Nothing Nothing)
89
90spanDocToMarkdown :: SpanDoc -> [T.Text]
91spanDocToMarkdown (SpanDocString docs uris)
92  = [T.pack $ haddockToMarkdown $ H.toRegular $ H._doc $ H.parseParas Nothing $ unpackHDS docs]
93    <> ["\n"] <> spanDocUrisToMarkdown uris
94  -- Append the extra newlines since this is markdown --- to get a visible newline,
95  -- you need to have two newlines
96spanDocToMarkdown (SpanDocText txt uris) = txt <> ["\n"] <> spanDocUrisToMarkdown uris
97
98spanDocUrisToMarkdown :: SpanDocUris -> [T.Text]
99spanDocUrisToMarkdown (SpanDocUris mdoc msrc) = catMaybes
100  [ linkify "Documentation" <$> mdoc
101  , linkify "Source" <$> msrc
102  ]
103  where linkify title uri = "[" <> title <> "](" <> uri <> ")"
104
105spanDocToMarkdownForTest :: String -> String
106spanDocToMarkdownForTest
107  = haddockToMarkdown . H.toRegular . H._doc . H.parseParas Nothing
108
109-- Simple (and a bit hacky) conversion from Haddock markup to Markdown
110haddockToMarkdown
111  :: H.DocH String String -> String
112
113haddockToMarkdown H.DocEmpty
114  = ""
115haddockToMarkdown (H.DocAppend d1 d2)
116  = haddockToMarkdown d1 ++ " " ++ haddockToMarkdown d2
117haddockToMarkdown (H.DocString s)
118  = escapeBackticks s
119haddockToMarkdown (H.DocParagraph p)
120  = "\n\n" ++ haddockToMarkdown p
121haddockToMarkdown (H.DocIdentifier i)
122  = "`" ++ i ++ "`"
123haddockToMarkdown (H.DocIdentifierUnchecked i)
124  = "`" ++ i ++ "`"
125#if MIN_VERSION_haddock_library(1,10,0)
126haddockToMarkdown (H.DocModule (H.ModLink i Nothing))
127  = "`" ++ escapeBackticks i ++ "`"
128-- See https://github.com/haskell/haddock/pull/1315
129-- Module references can be labeled in markdown style, e.g. [some label]("Some.Module")
130-- However, we don't want to use the link markup here, since the module name would be covered
131-- up by the label. Thus, we keep both the label and module name in the following style:
132-- some label ( `Some.Module` )
133haddockToMarkdown (H.DocModule (H.ModLink i (Just label)))
134  = haddockToMarkdown label ++ " ( `" ++ escapeBackticks i ++ "` )"
135#else
136haddockToMarkdown (H.DocModule i)
137  = "`" ++ escapeBackticks i ++ "`"
138#endif
139haddockToMarkdown (H.DocWarning w)
140  = haddockToMarkdown w
141haddockToMarkdown (H.DocEmphasis d)
142  = "*" ++ haddockToMarkdown d ++ "*"
143haddockToMarkdown (H.DocBold d)
144  = "**" ++ haddockToMarkdown d ++ "**"
145haddockToMarkdown (H.DocMonospaced d)
146  = "`" ++ removeUnescapedBackticks (haddockToMarkdown d) ++ "`"
147haddockToMarkdown (H.DocCodeBlock d)
148  = "\n```haskell\n" ++ haddockToMarkdown d ++ "\n```\n"
149haddockToMarkdown (H.DocExamples es)
150  = "\n```haskell\n" ++ unlines (map exampleToMarkdown es) ++ "\n```\n"
151  where
152    exampleToMarkdown (H.Example expr result)
153      = ">>> " ++ expr ++ "\n" ++ unlines result
154haddockToMarkdown (H.DocHyperlink (H.Hyperlink url Nothing))
155  = "<" ++ url ++ ">"
156haddockToMarkdown (H.DocHyperlink (H.Hyperlink url (Just label)))
157  = "[" ++ haddockToMarkdown label ++ "](" ++ url ++ ")"
158haddockToMarkdown (H.DocPic (H.Picture url Nothing))
159  = "![](" ++ url ++ ")"
160haddockToMarkdown (H.DocPic (H.Picture url (Just label)))
161  = "![" ++ label ++ "](" ++ url ++ ")"
162haddockToMarkdown (H.DocAName aname)
163  = "[" ++ escapeBackticks aname ++ "]:"
164haddockToMarkdown (H.DocHeader (H.Header level title))
165  = replicate level '#' ++ " " ++ haddockToMarkdown title
166
167haddockToMarkdown (H.DocUnorderedList things)
168  = '\n' : (unlines $ map (("+ " ++) . trimStart . splitForList . haddockToMarkdown) things)
169haddockToMarkdown (H.DocOrderedList things)
170  = '\n' : (unlines $ map (("1. " ++) . trimStart . splitForList . haddockToMarkdown) things)
171haddockToMarkdown (H.DocDefList things)
172  = '\n' : (unlines $ map (\(term, defn) -> "+ **" ++ haddockToMarkdown term ++ "**: " ++ haddockToMarkdown defn) things)
173
174-- we cannot render math by default
175haddockToMarkdown (H.DocMathInline _)
176  = "*cannot render inline math formula*"
177haddockToMarkdown (H.DocMathDisplay _)
178  = "\n\n*cannot render display math formula*\n\n"
179
180-- TODO: render tables
181haddockToMarkdown (H.DocTable _t)
182  = "\n\n*tables are not yet supported*\n\n"
183
184-- things I don't really know how to handle
185haddockToMarkdown (H.DocProperty _)
186  = ""  -- don't really know what to do
187
188escapeBackticks :: String -> String
189escapeBackticks ""       = ""
190escapeBackticks ('`':ss) = '\\':'`':escapeBackticks ss
191escapeBackticks (s  :ss) = s:escapeBackticks ss
192
193removeUnescapedBackticks :: String -> String
194removeUnescapedBackticks = \case
195  '\\' : '`' : ss -> '\\' : '`' : removeUnescapedBackticks ss
196  '`' : ss        -> removeUnescapedBackticks ss
197  ""              -> ""
198  s : ss          -> s : removeUnescapedBackticks ss
199
200splitForList :: String -> String
201splitForList s
202  = case lines s of
203      []           -> ""
204      (first:rest) -> unlines $ first : map (("  " ++) . trimStart) rest
205