diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Revision history for pandoc-link-context
+
+## 1.0.0.0
+
+* First version. Released on an unsuspecting world.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2020, Sridhar Ratnakumar
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Sridhar Ratnakumar nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/pandoc-link-context.cabal b/pandoc-link-context.cabal
new file mode 100644
--- /dev/null
+++ b/pandoc-link-context.cabal
@@ -0,0 +1,41 @@
+cabal-version:      2.2
+name:               pandoc-link-context
+version:            1.0.0.0
+synopsis:           Extract "contextual links" from Pandoc
+description:        A library to pull out all links with their surrounding context in your Pandoc documents. Useful for software dealing with wiki-links and Zettelkasten.
+category:           Text
+license:            BSD-3-Clause
+license-file:       LICENSE
+author:             Sridhar Ratnakumar
+maintainer:         srid@srid.ca
+build-type:         Simple
+extra-source-files: CHANGELOG.md
+
+library
+  default-language:   Haskell2010
+  hs-source-dirs:     src
+  exposed-modules:    Text.Pandoc.LinkContext
+  build-depends:
+      base >=4.12 && < 4.15
+    , containers
+    , pandoc-types
+    , relude        >=0.7.0.0
+    , text
+
+  mixins:
+    base hiding (Prelude),
+    relude (Relude as Prelude, Relude.Extra)
+
+  default-extensions:
+    FlexibleContexts
+    FlexibleInstances
+    LambdaCase
+    OverloadedStrings
+      RecordWildCards
+      ScopedTypeVariables
+      TupleSections
+    TypeApplications
+    ViewPatterns
+
+  ghc-options:
+    -Wall -Wincomplete-record-updates -Wincomplete-uni-patterns
diff --git a/src/Text/Pandoc/LinkContext.hs b/src/Text/Pandoc/LinkContext.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/Pandoc/LinkContext.hs
@@ -0,0 +1,52 @@
+module Text.Pandoc.LinkContext (queryLinksWithContext) where
+
+import Data.List (nub)
+import qualified Data.Map.Strict as Map
+import qualified Text.Pandoc.Builder as B
+import Text.Pandoc.Definition (Block, Inline (Link), Pandoc (..))
+import qualified Text.Pandoc.Walk as W
+
+type Url = Text
+
+-- | Query the pandoc document for all links
+--
+-- Return a map, containing the "surrounding context" (as Pandoc blocks) for
+-- each link.
+queryLinksWithContext :: Pandoc -> Map Url [Block]
+queryLinksWithContext =
+  fmap nub
+    . Map.fromListWith (<>)
+    . fmap (second one)
+    . W.query go
+  where
+    go :: Block -> [(Url, Block)]
+    go blk =
+      fmap (,blk) $ case blk of
+        B.Para is ->
+          queryLinkUrls is
+        B.Plain is ->
+          queryLinkUrls is
+        B.LineBlock is ->
+          queryLinkUrls is
+        B.Header _ _ is ->
+          queryLinkUrls is
+        B.DefinitionList xs ->
+          -- Gather all filenames linked, and have them put (see above) in the
+          -- same definition list block.
+          concat $
+            xs <&> \(is, bss) ->
+              let def = queryLinkUrls is
+                  body = fmap (fmap (fmap fst . go)) bss
+               in def <> concat (concat body)
+        _ -> mempty
+
+    queryLinkUrls :: W.Walkable Inline b => b -> [Url]
+    queryLinkUrls =
+      W.query (maybeToList . getLinkUrl)
+
+    getLinkUrl :: Inline -> Maybe Url
+    getLinkUrl = \case
+      Link _attr _inlines (url, _title) -> do
+        pure url
+      _ ->
+        Nothing
