packages feed

shortcut-links (empty) → 0.1.0.0

raw patch · 8 files changed

+1123/−0 lines, 8 filesdep +basedep +textsetup-changed

Dependencies added: base, text

Files

+ CHANGELOG.md view
@@ -0,0 +1,3 @@+# 0.1.0.0++First release.
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2015, Aelve++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 Aelve 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.
+ README.md view
@@ -0,0 +1,3 @@+# shortcut-links++Package!
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ shortcut-links.cabal view
@@ -0,0 +1,34 @@+name:                shortcut-links+version:             0.1.0.0+synopsis:            Link shortcuts for use in text markup+description:+  This package is a database of link shortcuts. A Markdown example: +  @[Google](\@w)@ turns into <http://en.wikipedia.org/wiki/Google Google>.+homepage:            http://github.com/aelve/shortcut-links+bug-reports:         http://github.com/aelve/shortcut-links/issues+license:             BSD3+license-file:        LICENSE+author:              Artyom+maintainer:          Artyom <yom@artyom.me>+copyright:           2015–∞ Aelve+category:            Text, Web+build-type:          Simple+extra-source-files:  +  README.md+  CHANGELOG.md+cabal-version:       >=1.10++source-repository head+  type:                git+  location:            git://github.com/aelve/shortcut-links.git++library+  exposed-modules:     ShortcutLinks+                       ShortcutLinks.All+                       ShortcutLinks.Utils+  -- other-modules:       +  -- other-extensions:    +  build-depends:       base >= 4.5 && < 5+                     , text >= 1.1+  hs-source-dirs:      src+  default-language:    Haskell2010
+ src/ShortcutLinks.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE+OverloadedStrings+  #-}++module ShortcutLinks+(+  Result(..),+  allShortcuts,+)+where+++-- Text+import Data.Text (Text)+import qualified Data.Text as T+-- shortcut-links+import ShortcutLinks.All (allShortcuts, Result(..))+import ShortcutLinks.Utils (format)+++-- | Use a shortcut from 'allShortcuts'.+--+-- This is the main function you should use. It does the lookup and so on.+useShortcut+  :: Text          -- ^ Shortcut name+  -> Maybe Text    -- ^ Option+  -> Text          -- ^ Link text+  -> Result Text   -- ^ Resulting URL+useShortcut name option link =+  let givenShortcut (names,_) = name `elem` names+  in  case filter givenShortcut allShortcuts of+        []   -> fail (format "there's no shortcut named '{}'" name)+        [sh] -> (snd sh) option link+        _    -> fail (format "there's more than one shortcut named '{}'" name)
+ src/ShortcutLinks/All.hs view
@@ -0,0 +1,894 @@+{-# LANGUAGE+OverloadedStrings,+DeriveFunctor,+ViewPatterns+  #-}+++module ShortcutLinks.All+(+  Result(..),+  Shortcut,+  allShortcuts,++  -- * Encyclopedias+  wikipedia, tvtropes,++  -- * Social networks+  facebook, vk, googleplus,++  -- * Microblogs+  twitter, juick,++  -- * Major search engines+  google, duckduckgo, yandex, baidu,++  -- * Programming language libraries+  npm, jam, rubygems, pypi, metacpanPod, metacpanRelease, hackage, cargo,+  pub, hex, cran, swiprolog, dub, bpkg, pear,++  -- * Code hosting+  github, gitlab, bitbucket,++  -- * OS packages+  -- ** Mobile+  googleplay,+  -- ** Windows+  chocolatey,+  -- ** OS X+  braumeister,+  -- ** Linux+  debian, aur, mint, fedora, gentoo, opensuse,++  -- * Addons+  -- ** Text editors+  marmalade, melpa, elpa, packagecontrol, atomPackage, atomTheme, jedit, vim,+  -- ** Browsers+  operaExt, operaTheme, firefox, chrome,++  -- * Manuals+  ghcExt,++  -- * Standards and databases+  rfc, ecma, cve,+)+where+++-- General+import Data.Monoid+import Control.Applicative+import Control.Monad+import Data.Maybe+-- Text+import qualified Data.Text as T+import Data.Text (Text)+import Data.Char+-- shortcut-links+import ShortcutLinks.Utils+++data Result a = Failure String | Warning [String] a | Success a+  deriving (Show, Functor)++instance Applicative Result where+  pure = return+  (<*>) = ap++instance Monad Result where+  fail = Failure+  return = Success+  Failure x    >>= _ = Failure x+  Warning wa a >>= f = case f a of+    Success    b -> Warning wa b+    Warning wb b -> Warning (wa ++ wb) b+    Failure x    -> Failure x+  Success    a >>= f = f a++warn :: String -> Result ()+warn s = Warning [s] ()++type Shortcut = Maybe Text -> Text -> Result Text++-- | A list of all functions included in this module, together with suggested+-- names for them.+allShortcuts :: [([Text], Shortcut)]+allShortcuts =+  let (.=) names func = (T.words names, func)+  in [+  -- encyclopedias+  "w wikipedia"             .= wikipedia,+  "tvt tvtropes"            .= tvtropes,+  -- social networks+  "fb facebook"             .= facebook,+  "vk vkontakte"            .= vk,+  "gp gplus googleplus"     .= googleplus,+  -- microblogs+  "t twitter"               .= twitter,+  "juick"                   .= juick,+  -- search engines+  "google"                  .= google,+  "ddg duckduckgo"          .= duckduckgo,+  "yandex"                  .= yandex,+  "baidu"                   .= baidu,+  -- programming language libraries+  "npm"                     .= npm,+  "jam"                     .= jam,+  "gem"                     .= rubygems,+  "pypi"                    .= pypi,+  "cpan"                    .= metacpanPod,+  "cpan-r"                  .= metacpanRelease,+  "hackage"                 .= hackage,+  "cargo"                   .= cargo,+  "pub"                     .= pub,+  "hex"                     .= hex,+  "cran"                    .= cran,+  "swiprolog"               .= swiprolog,+  "dub"                     .= dub,+  "bpkg"                    .= bpkg,+  "pear"                    .= pear,+  -- code hosting+  "gh github"               .= github,+  "gitlab"                  .= gitlab,+  "bitbucket"               .= bitbucket,+  -- OS+  "gplay googleplay"        .= googleplay,+  "chocolatey"              .= chocolatey,+  "brew"                    .= braumeister,+  -- OS – Linux+  "debian"                  .= debian,+  "aur"                     .= aur,+  "mint"                    .= mint,+  "fedora"                  .= fedora,+  "gentoo"                  .= gentoo,+  "opensuse"                .= opensuse,+  -- text editors+  "marmalade"               .= marmalade,+  "melpa"                   .= melpa,+  "elpa"                    .= elpa,+  "sublime"                 .= packagecontrol,+  "atom"                    .= atomPackage,+  "atom-theme"              .= atomTheme,+  "jedit"                   .= jedit,+  "vim"                     .= vim,+  -- browsers+  "opera"                   .= operaExt,+  "opera-theme"             .= operaTheme,+  "firefox"                 .= firefox,+  "chrome"                  .= chrome,+  -- manuals+  "ghc-ext"                 .= ghcExt,+  -- standards and databases+  "rfc"                     .= rfc,+  "ecma"                    .= ecma,+  "cve"                     .= cve ]++-- | <https://facebook.com Facebook>+--+-- Link example:+-- @[green](\@fb)@ →+-- <https://facebook.com/green>+facebook :: Shortcut+facebook _ q = return $ "https://facebook.com/" <> q++-- | <https://vk.com Vkontakte> (Вконтакте)+--+-- Link example (username):+-- @[green](\@vk)@ →+-- <https://vk.com/green>+--+-- Link example (ID):+--+-- @[1337](\@vk)@ →+-- <https://vk.com/id1337>+vk :: Shortcut+vk _ q = return $ "https://vk.com/" <> q'+  where q' = if not (T.null q) && isDigit (T.head q) then "id" <> q else q++-- | <https://plus.google.com Google+>+--+-- Link example (username):+-- @[SergeyBrin](\@gp)@ →+-- <https://plus.google.com/+SergeyBrin SergeyBrin>+-- +-- Link example (username with @+@):+-- @[+SergeyBrin](\@gp)@ →+-- <https://plus.google.com/+SergeyBrin +SergeyBrin>+--+-- Link example (full name – will just be concatenated):+-- @[Sergey Brin](\@gp)@ →+-- <https://plus.google.com/+SergeyBrin Sergey Brin>+--+-- Link example (ID):+-- @[Sergey Brin](\@gp:109813896768294978296)@ →+-- <https://plus.google.com/109813896768294978296 Sergey Brin>+--+-- Link example (hashtag):+-- @[#Australia](\@gp)@ →+-- <https://plus.google.com/explore/Australia #Australia>+googleplus :: Shortcut+googleplus _ q+  | T.null q        = return $ url+  | T.head q == '#' = return $ format "{}/explore/{}" url (T.tail q)+  | T.head q == '+' = return $ format "{}/{}" url q+  | T.all isDigit q = return $ format "{}/{}" url q+  | otherwise       = return $ format "{}/+{}" url (T.concat (T.words q))+  where url = "https://plus.google.com"++-- | <https://twitter.com Twitter>+--+-- Link example (username):+-- @[kmett](\@t)@ →+-- <https://twitter.com/kmett kmett>+-- +-- Link example (username with @\@@):+-- @[\@kmett](\@t)@ →+-- <https://twitter.com/kmett \@kmett>+--+-- Link example (hashtag):+-- @[#haskell](\@t)@ →+-- <https://twitter.com/hashtag/haskell #haskell>+twitter :: Shortcut+twitter _ q+  | T.null q        = return $ url+  | T.head q == '#' = return $ format "{}/hashtag/{}" url (T.tail q)+  | T.head q == '@' = return $ format "{}/{}" url (T.tail q)+  | otherwise       = return $ format "{}/{}" url q+  where url = "https://twitter.com"++-- | <https://juick.com Juick>+--+-- Link example (username):+-- @[thefish](\@juick)@ →+-- <https://juick.com/thefish/ thefish>+-- +-- Link example (username with @\@@):+-- @[\@thefish](\@juick)@ →+-- <https://juick.com/thefish/ \@thefish>+--+-- Link example (tag):+-- @[*Haskell](\@juick)@ →+-- <https://juick.com/tag/Haskell *Haskell>+juick :: Shortcut+juick _ q+  | T.null q        = return $ url+  | T.head q == '*' = return $ format "{}/tag/{}" url (T.tail q)+  | T.head q == '@' = return $ format "{}/{}" url (T.tail q)+  | otherwise       = return $ format "{}/{}" url q+  where url = "https://juick.com"++-- | <https://google.com Google>+--+-- Link example:+-- @[random query](\@google)@ →+-- <https://www.google.com/search?nfpr=1&q=random+query random query>+google :: Shortcut+google _ q = return $+  "https://google.com/search?nfpr=1&q=" <> replaceSpaces '+' q++-- | <https://duckduckgo.com Duckduckgo>+--+-- Link example:+-- @[random query](\@ddg)@ →+-- <https://duckduckgo.com/?q=random+query random query>+duckduckgo :: Shortcut+duckduckgo _ q = return $ "https://duckduckgo.com/?q=" <> replaceSpaces '+' q++-- | <http://yandex.ru Yandex> (Russian search engine)+--+-- Link example:+-- @[random query](\@yandex)@ →+-- <http://yandex.ru/search/?noreask=1&text=random+query random query>+yandex :: Shortcut+yandex _ q = return $+  "http://yandex.ru/search/?noreask=1&text=" <> replaceSpaces '+' q++-- | <http://baidu.com Baidu> (Chinese search engine)+--+-- Link example:+-- @[random query](\@baidu)@ →+-- <http://baidu.com/s?nojc=1&wd=random+query random query>+baidu :: Shortcut+baidu _ q = return $ "http://baidu.com/s?nojc=1&wd=" <> replaceSpaces '+' q++-- | __Node.js__ – <https://npmjs.com NPM>+--+-- Link example:+-- @[markdown](\@npm)@ →+-- <https://www.npmjs.com/package/markdown markdown>+npm :: Shortcut+npm _ q = return $ "https://npmjs.com/package/" <> q++-- | __Javascript__ – <http://jamjs.org/packages/#/ Jam>+--+-- Link example:+-- @[pagedown](\@jam)@ →+-- <http://jamjs.org/packages/#/details/pagedown pagedown>+jam :: Shortcut+jam _ q = return $ "http://jamjs.org/packages/#/details/" <> q++-- | __Ruby__ – <https://rubygems.org RubyGems.org>+--+-- Link example:+-- @[github-markdown](\@gem)@ →+-- <https://rubygems.org/gems/github-markdown github-markdown>+rubygems :: Shortcut+rubygems _ q = return $ "https://rubygems.org/gems/" <> q++-- | __Python__ – <https://pypi.python.org/pypi PyPI>+--+-- Link example:+-- @[Markdown](\@pypi)@ →+-- <https://pypi.python.org/pypi/Markdown Markdown>+pypi :: Shortcut+pypi _ q = return $ "https://pypi.python.org/pypi/" <> q++-- | __Perl__ – <https://metacpan.org MetaCPAN> (by module)+--+-- Link example:+-- @[Text::Markdown](\@cpan)@ →+-- <https://metacpan.org/pod/Text::Markdown Text::Markdown>+metacpanPod :: Shortcut+metacpanPod _ q = return $ "https://metacpan.org/pod/" <> q++-- | __Perl__ – <https://metacpan.org MetaCPAN> (by release)+--+-- Link example:+-- @[Text-Markdown](\@cpan-r)@ →+-- <https://metacpan.org/release/Text-Markdown Text-Markdown>+metacpanRelease :: Shortcut+metacpanRelease _ q = return $ "https://metacpan.org/release/" <> q++-- | __Haskell__ – <https://hackage.haskell.org Hackage>+--+-- Link example:+-- @[cmark](\@hackage)@ →+-- <https://hackage.haskell.org/package/cmark cmark>+hackage :: Shortcut+hackage _ q = return $ "https://hackage.haskell.org/package/" <> q++-- | __Rust__ – <https://crates.io Cargo>+--+-- Link example:+-- @[hoedown](\@cargo)@ →+-- <https://crates.io/crates/hoedown hoedown>+cargo :: Shortcut+cargo _ q = return $ "https://crates.io/crates/" <> q++-- | __PHP__ – <http://pear.php.net PEAR>+--+-- Link example:+-- @[Text_Wiki_Doku](\@pear)@ →+-- <http://pear.php.net/package/Text_Wiki_Doku Text_Wiki_Doku>+pear :: Shortcut+pear _ q = return $ "http://pear.php.net/package/" <> q++-- | __Dart__ – <https://pub.dartlang.org pub>+--+-- Link example:+-- @[md_proc](\@pub)@ →+-- <https://pub.dartlang.org/packages/md_proc md_proc>+pub :: Shortcut+pub _ q = return $ "https://pub.dartlang.org/packages/" <> q++-- | __R__ – <http://cran.r-project.org/web/packages/ CRAN>+--+-- Link example:+-- @[markdown](\@cran)@ →+-- <http://cran.r-project.org/web/packages/markdown markdown>+cran :: Shortcut+cran _ q = return $ "http://cran.r-project.org/web/packages/" <> q++-- | __Erlang__ – <https://hex.pm Hex>+--+-- Link example:+-- @[earmark](\@hex)@ →+-- <https://hex.pm/packages/earmark earmark>+hex :: Shortcut+hex _ q = return $ "https://hex.pm/packages/" <> q++-- | __SWI-Prolog__ – <http://www.swi-prolog.org/pack/list packages>+--+-- Link example:+-- @[markdown](\@swiprolog)@ →+-- <http://www.swi-prolog.org/pack/list?p=markdown markdown>+swiprolog :: Shortcut+swiprolog _ q = return $ "http://www.swi-prolog.org/pack/list?p=" <> q++-- | __D__ – <http://code.dlang.org DUB>+--+-- Link example:+-- @[dmarkdown](\@dub)@ →+-- <http://code.dlang.org/packages/dmarkdown dmarkdown>+dub :: Shortcut+dub _ q = return $ "http://code.dlang.org/packages/" <> q++-- | __Bash__ – <http://bpkg.io bpkg>+--+-- Link example:+-- @[markdown](\@bpkg)@ →+-- <http://www.bpkg.io/pkg/markdown markdown>+bpkg :: Shortcut+bpkg _ q = return $ "http://bpkg.io/pkg/" <> q++-- | <https://github.com Github>+--+-- Link example:+-- @[aelve/shortcut-links](\@gh)@ →+-- <https://github.com/aelve/shortcut-links aelve/shortcut-links>+--+-- The repository owner can also be given as an option:+--+-- @[shortcut-links](\@gh(aelve))@ →+-- <https://github.com/aelve/shortcut-links shortcut-links>+github :: Shortcut+github mbOwner q = case mbOwner of+  Nothing    -> return $ format "https://github.com/{}" q+  Just owner -> return $ format "https://github.com/{}/{}" owner q++-- | <https://bitbucket.org Bitbucket>+--+-- Link example:+-- @[bos/text](\@bitbucket)@ →+-- <https://bitbucket.org/bos/text bos/text>+--+-- The repository owner can also be given as an option:+--+-- @[text](\@bitbucket(bos))@ →+-- <https://bitbucket.org/bos/text text>+bitbucket :: Shortcut+bitbucket mbOwner q = case mbOwner of+  Nothing    -> return $ format "https://bitbucket.org/{}" q+  Just owner -> return $ format "https://bitbucket.org/{}/{}" owner q++-- | <https://gitlab.com Gitlab>+--+-- Link example:+-- @[learnyou/lysa](\@gitlab)@ →+-- <https://gitlab.com/learnyou/lysa learnyou/lysa>+--+-- The repository owner can also be given as an option:+--+-- @[lysa](\@gitlab(learnyou))@ →+-- <https://gitlab.com/learnyou/lysa lysa>+--+-- Note that links like <https://gitlab.com/owner> work but are going to be+-- automatically redirected to either <https://gitlab.com/u/owner> or+-- <https://gitlab.com/groups/owner>, depending on whether it's a user or a+-- team. So, it's a case when the “links have to look as authentic as+-- possible” principle is violated (but c'mon, this “u” thing looks ugly+-- anyway).+gitlab :: Shortcut+gitlab mbOwner q = case mbOwner of+  Nothing    -> return $ format "https://gitlab.com/{}" q+  Just owner -> return $ format "https://gitlab.com/{}/{}" owner q++-- | __Android__ – <https://play.google.com Google Play> (formerly Play Market)+--+-- Link example:+-- @[Opera Mini](\@gplay:com.opera.mini.native)@ →+-- <https://play.google.com/store/apps/details?id=com.opera.mini.native Opera Mini>+googleplay :: Shortcut+googleplay _ q = return $ "https://play.google.com/store/apps/details?id=" <> q++-- | <http://braumeister.org Braumeister> (Homebrew formulas)+--+-- Link example:+-- @[multimarkdown](\@brew)@ →+-- <http://braumeister.org/formula/multimarkdown multimarkdown>+braumeister :: Shortcut+braumeister _ q = return $ "http://braumeister.org/formula/" <> q++-- | <https://chocolatey.org Chocolatey>+--+-- Link example:+-- @[Opera](\@chocolatey)@ →+-- <https://chocolatey.org/packages/Opera Opera>+chocolatey :: Shortcut+chocolatey _ q = return $ "https://chocolatey.org/packages/" <> q++-- | __Debian__ – <https://debian.org/distrib/packages packages>+--+-- Link example (stable distribution):+-- @[ghc](\@debian)@ →+-- <https://packages.debian.org/stable/ghc ghc>+-- +-- Link example (experimental distribution, but could be any other):+-- @[ghc](\@debian(experimental))@ →+-- <https://packages.debian.org/experimental/ghc ghc>+debian :: Shortcut+debian mbDist q = return $ format "https://packages.debian.org/{}/{}" dist q+  where+    dist = fromMaybe "stable" mbDist++-- | __Arch Linux__ – <https://aur.archlinux.org AUR> (“user repository”)+--+-- Link example:+-- @[ghc-git](\@aur)@ →+-- <https://aur.archlinux.org/packages/ghc-git ghc-git>+aur :: Shortcut+aur _ q = return $ "https://aur.archlinux.org/packages/" <> q++-- | __Gentoo__ – <https://packages.gentoo.org packages>+--+-- Link example:+-- @[dev-lang/ghc](\@gentoo)@ →+-- <https://packages.gentoo.org/package/dev-lang/ghc dev-lang/ghc>+-- +-- Link example (category as an option):+-- @[ghc](\@gentoo(dev-lang))@ →+-- <https://packages.gentoo.org/package/dev-lang/ghc ghc>+--+-- Note that if you don't specify any category, the link would still work –+-- but there are a lot of packages with overlapping names (like “ace”, “csv”,+-- “http”), and such links would lead to search pages listing several+-- packages. So, it's better to include categories.+gentoo :: Shortcut+gentoo mbCat q = return $ "https://packages.gentoo.org/package/" <> pkg+  where+    pkg = case mbCat of+      Nothing  -> q+      Just cat -> cat <> "/" <> q++-- | __openSUSE__ – <http://software.opensuse.org packages>+--+-- Link example:+-- @[ghc](\@opensuse)@ →+-- <http://software.opensuse.org/package/ghc ghc>+opensuse :: Shortcut+opensuse _ q = return $ "http://software.opensuse.org/package/" <> q++-- | __Linux Mint__ – <http://community.linuxmint.com/software/browse packages>+--+-- Link example:+-- @[ghc](\@mint)@ →+-- <http://community.linuxmint.com/software/view/ghc ghc>+mint :: Shortcut+mint _ q = return $ "http://community.linuxmint.com/software/view/" <> q++-- | __Fedora__ – <https://admin.fedoraproject.org/pkgdb packages>+--+-- Link example:+-- @[ghc](\@fedora)@ →+-- <https://admin.fedoraproject.org/pkgdb/package/ghc ghc>+fedora :: Shortcut+fedora _ q = return $ "https://admin.fedoraproject.org/pkgdb/package/" <> q++-- | __Emacs__ – <https://marmalade-repo.org Marmalade>+--+-- Link example:+-- @[markdown-mode](\@marmalade)@ →+-- <https://marmalade-repo.org/packages/markdown-mode markdown-mode>+marmalade :: Shortcut+marmalade _ q = return $ "https://marmalade-repo.org/packages/" <> q++-- | __Emacs__ – <http://melpa.org MELPA>+--+-- Link example:+-- @[markdown-mode](\@melpa)@ →+-- <http://melpa.org/#/markdown-mode markdown-mode>+melpa :: Shortcut+melpa _ q = return $ "http://melpa.org/#/" <> q++-- | __Emacs__ – <https://elpa.gnu.org ELPA>+--+-- Link example:+-- @[undo-tree](\@elpa)@ →+-- <https://elpa.gnu.org/packages/undo-tree.html undo-tree>+elpa :: Shortcut+elpa _ q = return $ format "https://elpa.gnu.org/packages/{}.html" q++-- | __Sublime Text__ – <https://packagecontrol.io Package Control>+--+-- Link example:+-- @[MarkdownEditing](\@sublime)@ →+-- <https://packagecontrol.io/packages/MarkdownEditing MarkdownEditing>+packagecontrol :: Shortcut+packagecontrol _ q = return $ "https://packagecontrol.io/packages/" <> q++-- | __Atom__ – <https://atom.io/packages packages>+--+-- Link example:+-- @[tidy-markdown](\@atom)@ →+-- <https://atom.io/packages/tidy-markdown tidy-markdown>+atomPackage :: Shortcut+atomPackage _ q = return $ "https://atom.io/packages/" <> q++-- | __Atom__ – <https://atom.io/themes themes>+--+-- Link example:+-- @[atom-material-ui](\@atom-theme)@ →+-- <https://atom.io/themes/atom-material-ui atom-material-ui>+atomTheme :: Shortcut+atomTheme _ q = return $ "https://atom.io/themes/" <> q++-- | __jEdit__ – <http://plugins.jedit.org packages>+--+-- Link example:+-- @[MarkdownPlugin](\@jedit)@ →+-- <http://plugins.jedit.org/plugins/?MarkdownPlugin MarkdownPlugin>+jedit :: Shortcut+jedit _ q = return $ "http://plugins.jedit.org/plugins/?" <> q++-- | __Vim__ – <http://www.vim.org/scripts/ scripts>+--+-- Link example (ID):+-- @[haskell.vim](\@vim:2062)@ →+-- <http://www.vim.org/scripts/script.php?script_id=2062 haskell.vim>+vim :: Shortcut+vim _ q = return $ "http://www.vim.org/scripts/script.php?script_id=" <> q++-- | __Opera__ – <https://addons.opera.com/extensions/ extensions>+--+-- Link example:+-- @[Amazon](\@opera:amazon-for-opera)@ →+-- <https://addons.opera.com/extensions/details/amazon-for-opera Amazon>+operaExt :: Shortcut+operaExt _ q = return $ "https://addons.opera.com/extensions/details/" <> q++-- | __Opera__ – <https://addons.opera.com/themes/ themes>+--+-- Link example:+-- @[Space theme](\@opera-theme:space-15)@ →+-- <https://addons.opera.com/en/themes/details/space-15 Space theme>+operaTheme :: Shortcut+operaTheme _ q = return $ "https://addons.opera.com/themes/details/" <> q++-- | __Firefox__ – <https://addons.mozilla.org/firefox Add-ons> (extensions, themes)+--+-- Link example:+-- @[tree-style-tab]@ →+-- <https://addons.mozilla.org/firefox/addon/tree-style-tab Tree Style Tab>+firefox :: Shortcut+firefox _ q = return $ "https://addons.mozilla.org/firefox/addon/" <> q++-- | __Chrome__ – <https://chrome.google.com/webstore Chrome Web Store> (extensions, apps, themes)+--+-- Link example:+-- @[hdokiejnpimakedhajhdlcegeplioahd]@ →+-- <https://chrome.google.com/webstore/detail/hdokiejnpimakedhajhdlcegeplioahd LastPass>+chrome :: Shortcut+chrome _ q = return $ "https://chrome.google.com/webstore/detail/" <> q++-- | <https://www.haskell.org/ghc/ GHC> (Glasgow Haskell Compiler) extensions+--+-- Link example:+-- @[ViewPatterns](\@ghc-ext)@ →+-- <https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/syntax-extns.html#view-patterns ViewPatterns>+ghcExt :: Shortcut+ghcExt _ ext = case lookup ext ghcExtsList of+  Nothing   -> fail (format "unknown GHC extension '{}'" ext)+  Just link -> return link++-- | <https://www.ietf.org/rfc.html RFCs>+--+-- Link example:+-- @[RFC 2026](\@rfc)@ →+-- <https://tools.ietf.org/html/rfc2026 RFC 2026>+--+-- Precise format of recognised text: optional “rfc” (case-insensitive), then+-- arbitrary amount of spaces and punctuation (or nothing), then the+-- number. Examples: “RFC 2026”, “RFC-2026”, “rfc2026”, “rfc #2026”, “2026”,+-- “#2026”.+rfc :: Shortcut+rfc _ x = do+  let n = T.dropWhile (not . isAlphaNum) (tryStripPrefixCI "rfc" x)+  -- We don't use 'readMaybe' here because 'readMaybe' isn't available in GHC+  -- 7.4, which Pandoc has to be compatible with.+  unless (T.all isDigit n) $+    warn "non-digits in RFC number"+  when (T.null n) $+    warn "no RFC number"+  let n' = T.dropWhile (== '0') n `orElse` "0"+  return ("https://tools.ietf.org/html/rfc" <> n')++-- | <http://ecma-international.org/publications/index.html Ecma standards and technical reports>+--+-- Link example (standard):+-- @[ECMA-262](\@ecma)@ →+-- <http://www.ecma-international.org/publications/standards/Ecma-262.htm ECMA-262>+--+-- Link example (technical report):+-- @[TR/71](\@ecma)@ →+-- <http://ecma-international.org/publications/techreports/E-TR-071.htm TR/71>+--+-- Precise format of recognised text for standards: optional “ECMA”+-- (case-insensitive), then arbitrary amount of spaces and punctuation (or+-- nothing), then the number. Examples: “ECMA-262”, “ECMA 262”, “ecma262”,+-- “ECMA #262”, “262”, “#262”.+--+-- Format for technical reports is the same, except that “TR” (instead of+-- “ECMA”) is not optional (so, if there's only a number given, it's+-- considered a standard and not a technical report).+ecma :: Shortcut+ecma _ q = do+  -- TODO: move dropSeparators to Utils and use it in 'rfc' and 'cve'+  let dropSeparators = T.dropWhile (not . isAlphaNum)+  let (dropSeparators -> mbNum, isTR) = case stripPrefixCI "tr" q of+        Nothing -> (tryStripPrefixCI "ecma" q, False)+        Just q' -> (q', True)+  -- We don't use 'readMaybe' here because 'readMaybe' isn't available in GHC+  -- 7.4, which Pandoc has to be compatible with.+  unless (T.all isDigit mbNum) $+    warn "non-digits in ECMA standard number"+  when (T.null mbNum) $+    warn "no ECMA standard number"+  -- The number has to have at least 3 digits.+  let num = T.justifyRight 3 '0' mbNum+      url = "http://ecma-international.org/publications" :: Text+  return $ if isTR+    then format "{}/techreports/E-TR-{}.htm" url num+    else format "{}/standards/Ecma-{}.htm" url num++-- | <http://cve.mitre.org CVEs> (Common Vulnerabilities and Exposures)+--+-- Link example:+-- @[CVE-2014-10001](\@cve)@ →+-- <http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-10001>+--+-- Precise format of recognised text: optional “cve” (case-insensitive), then+-- arbitrary amount of spaces and punctuation (or nothing), then the year,+-- ‘-’, and a number. Examples: “CVE-2014-10001”, “cve 2014-10001”,+-- “2014-10001”.+cve :: Shortcut+cve _ x = do+  let n = T.dropWhile (not . isAlphaNum) (tryStripPrefixCI "cve" x)+  unless (T.length n >= 9) $+    warn "CVE-ID is too short"+  let isValid = and [+        T.length n >= 9,+        T.all isDigit (T.take 4 n),+        T.index n 4 == '-',+        T.all isDigit (T.drop 5 n) ]+  unless isValid $+    warn "CVE-ID doesn't follow the <year>-<digits> format"+  return ("http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-" <> n)++-- | <https://wikipedia.org/ Wikipedia>+--+-- Link example (English Wikipedia):+-- @[grey-headed flying fox](\@w)@ →+-- <https://en.wikipedia.org/wiki/Grey-headed_flying_fox>+-- +-- Link example (Russian Wikipedia, but any language code can be used):+-- @[Haskell](\@w(ru))@ →+-- <https://ru.wikipedia.org/wiki/Haskell>+wikipedia :: Shortcut+wikipedia mbLang q = return $+  format "https://{}.wikipedia.org/wiki/{}" lang q'+  where+    lang = fromMaybe "en" mbLang+    q'   = titleFirst (replaceSpaces '_' q)++-- | <http://tvtropes.org TV Tropes>+--+-- Link example (trope):+-- @[so bad, it's good](\@tvtropes)@ →+-- <http://tvtropes.org/pmwiki/pmwiki.php/Main/SoBadItsGood>+--+-- Link example (series):+-- @[Elementary](\@tvtropes(series))@ →+-- <http://tvtropes.org/pmwiki/pmwiki.php/Series/Elementary>+--+-- You can give anything as a category instead of “series”, it'll be+-- capitalised but nothing else.+tvtropes :: Shortcut+tvtropes mbCat q = return $+  format "http://tvtropes.org/pmwiki/pmwiki.php/{}/{}" cat q'+  where+    cat = maybe "Main" titleFirst mbCat+    isSep c = (isSpace c || isPunctuation c) && c /= '\''+    -- Break into words, transform each word like “it's” → “Its”, and concat.+    -- Note that e.g. “man-made” is considered 2 separate words.+    q' = T.concat $ map (titleFirst . T.filter isAlphaNum) (T.split isSep q)++ghcExtsList :: [(Text, Text)]+ghcExtsList = do+  let (.=) = (,)+      base = "https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/"+      prefix x = map (\(a, b) -> (a, x <> b))+  prefix base $ concat [+    prefix "syntax-extns.html" [+      "DisambiguateRecordFields" .= "#disambiguate-fields",+      "EmptyCase" .= "#empty-case",+      "NoImplicitPrelude" .= "#rebindable-syntax",+      "NegativeLiterals" .= "#negative-literals",+      "RebindableSyntax" .= "#rebindable-syntax",+      "PatternGuards" .= "#pattern-guards",+      "ViewPatterns" .= "#view-patterns",+      "UnicodeSyntax" .= "#unicode-syntax",+      "MagicHash" .= "#magic-hash",+      "ParallelListComp" .= "#parallel-list-comprehensions",+      "TransformListComp" .= "#generalised-list-comprehensions",+      "MonadComprehensions" .= "#monad-comprehensions",+      "ExplicitNamespaces" .= "#explicit-namespaces",+      "RecursiveDo" .= "#recursive-do-notation",+      "RecordWildCards" .= "#record-wildcards",+      "NamedFieldPuns" .= "#record-puns",+      "PackageImports" .= "#package-imports",+      "LambdaCase" .= "#lambda-case",+      "MultiWayIf" .= "#multi-way-if",+      "NumDecimals" .= "#num-decimals",+      "BinaryLiterals" .= "#binary-literals",+      "PostfixOperators" .= "#postfix-operators",+      "TupleSections" .= "#tuple-sections",+      "PatternSynonyms" .= "#pattern-synonyms" ],++    prefix "data-type-extensions.html" [+      "GADTs" .= "#gadt",+      "GADTSyntax" .= "#gadt-style",+      "ExistentialQuantification" .= "#existential-quantification",+      "LiberalTypeSynonyms" .= "#type-synonyms",+      "EmptyDataDecls" .= "#nullary-types",+      "DatatypeContexts" .= "#datatype-contexts",+      "TypeOperators" .= "#type-operators" ],++    prefix "other-type-extensions.html" [+      "AllowAmbiguousTypes" .= "#ambiguity",+      "ImplicitParams" .= "#implicit-parameters",+      "NoMonomorphismRestriction" .= "#monomorphism",+      "RelaxedPolyRec" .= "#typing-binds",+      "MonoLocalBinds" .= "#mono-local-binds",+      "ScopedTypeVariables" .= "#scoped-type-variables",+      "ExplicitForAll" .= "#explicit-foralls",+      "PolymorphicComponents" .= "#universal-quantification",+      "Rank2Types" .= "#universal-quantification",+      "RankNTypes" .= "#universal-quantification",+      "ImpredicativeTypes" .= "#impredicative-polymorphism",+      "KindSignatures" .= "#kinding",+      "FlexibleContexts" .= "#flexible-contexts" ],++    prefix "type-class-extensions.html" [+      "IncoherentInstances" .= "#instance-overlap",+      "OverlappingInstances" .= "#instance-overlap",+      "OverloadedLists" .= "#overloaded-lists",+      "OverloadedStrings" .= "#overloaded-strings",+      "UndecidableInstances" .= "#undecidable-instances",+      "TypeSynonymInstances" .= "#flexible-instance-head",+      "FlexibleInstances" .= "#instance-rules",+      "ConstrainedClassMethods" .= "#class-method-types",+      "DefaultSignatures" .= "#class-default-signatures",+      "MultiParamTypeClasses" .= "#multi-param-type-classes",+      "NullaryTypeClasses" .= "#nullary-type-classes",+      "InstanceSigs" .= "#instance-sigs",+      "FunctionalDependencies" .= "#functional-dependencies" ],++    prefix "deriving.html" [+      "AutoDeriveTypeable" .= "#auto-derive-typeable",+      "DeriveDataTypeable" .= "#deriving-typeable",+      "DeriveGeneric" .= "#deriving-typeable",+      "DeriveFunctor" .= "#deriving-extra",+      "DeriveTraversable" .= "#deriving-extra",+      "DeriveFoldable" .= "#deriving-extra",+      "GeneralizedNewtypeDeriving" .= "#newtype-deriving",+      "DeriveAnyClass" .= "#derive-any-class",+      "StandaloneDeriving" .= "#stand-alone-deriving" ],++    prefix "template-haskell.html" [+      "TemplateHaskell" .= "",+      "QuasiQuotes" .= "#th-quasiquotation" ],++    prefix "ffi.html" [+      "ForeignFunctionInterface" .= "",+      "InterruptibleFFI" .= "#ffi-interruptible",+      "CApiFFI" .= "#ffi-capi" ],++    prefix "partial-type-signatures.html" [+      "PartialTypeSignatures" .= "",+      "NamedWildCards" .= "#named-wildcards" ],++    map (.= "safe-haskell.html") [+      "Safe",+      "Trustworthy",+      "Unsafe" ],++    [ "Arrows" .= "arrow-notation.html",+      "ConstraintKinds" .= "constraint-kind.html",+      "DataKinds" .= "promotion.html",+      "ExtendedDefaultRules" .= "interactive-evaluation.html#extended-default-rules",+      "TypeFamilies" .= "type-families.html",+      "PolyKinds" .= "kind-polymorphism.html",+      "BangPatterns" .= "bang-patterns.html",+      "CPP" .= "options-phases.html#c-pre-processor",+      "RoleAnnotations" .= "roles.html#role-annotations",+      "UnboxedTuples" .= "primitives.html#unboxed-tuples" ] ]
+ src/ShortcutLinks/Utils.hs view
@@ -0,0 +1,123 @@+{-# LANGUAGE+OverloadedStrings,+FlexibleInstances+  #-}++{- |+Useful functions when writing your own link rules.+-}+module ShortcutLinks.Utils+(+  replaceSpaces,+  titleFirst,+  tryStripPrefixCI,+  stripPrefixCI,+  orElse,+  format,+)+where+++-- General+import Data.Monoid+-- Text+import Data.Char+import qualified Data.Text as T+import Data.Text (Text)+++-- | Replace spaces in text with chosen character (useful when processing queries containing spaces – they are often turned into “+” or “_”).+--+-- >>> replaceSpaces '_' "hi   there"+-- "hi___there"+replaceSpaces :: Char -> Text -> Text+replaceSpaces r = T.map (\c -> if isSpace c then r else c)++-- | Convert the 1st character of a string to upper case.+--+-- This function is dumber than it could've been; when the 1st character+-- doesn't have a single-character uppercase form (like “ß”), it is left+-- intact instead of being converted (“Ss” in the case of “ß”). This is good,+-- however; for instance, if the “proper” capitalisation rule was applied to+-- e.g. Wikipedia links, a link to the article on “ß” would've been rendered+-- as “Ss”, which is a redirect to “Schutzstaffel”.+titleFirst :: Text -> Text+titleFirst s = case T.uncons s of+  Nothing        -> ""+  Just (c, rest) -> toUpper c `T.cons` rest++-- | Strip given prefix from a string, or do nothing if the string doesn't+-- have given prefix.+--+-- This function is case-insensitive.+--+-- >>> tryStripPrefixCI "FOO" "FooBAR"+-- "BAR"+--+-- >>> tryStripPrefixCI "foo" "quux"+-- "quux"+tryStripPrefixCI :: Text -> Text -> Text+tryStripPrefixCI pref str =+  let pref' = T.toCaseFold pref+      (str_pref, rest) = T.splitAt (T.length pref') str+  in  if T.toCaseFold str_pref == pref' then rest else str++-- | Strip given prefix from a string.+--+-- This function is case-insensitive.+--+-- >>> stripPrefixCI "FOO" "FooBAR"+-- Just "BAR"+--+-- >>> stripPrefixCI "foo" "quux"+-- Nothing+stripPrefixCI :: Text -> Text -> Maybe Text+stripPrefixCI pref str =+  let pref' = T.toCaseFold pref+      (str_pref, rest) = T.splitAt (T.length pref') str+  in  if T.toCaseFold str_pref == pref' then Just rest else Nothing++-- | Choose the 2nd value if the 1st is empty (equal to 'mempty').+orElse :: (Eq a, Monoid a) => a -> a -> a+orElse a b = if a == mempty then b else a++------------------------------------------------------------------------------+-- A micro formatting library which supports Text better than printf.+------------------------------------------------------------------------------++class FormatArg a where+  formatArg :: a -> Text++instance FormatArg Text    where formatArg = id+instance FormatArg String  where formatArg = T.pack+instance FormatArg Int     where formatArg = T.pack . show+instance FormatArg Integer where formatArg = T.pack . show++class FormatType r where+  format' :: Text -> [Text] -> r++instance FormatType String where+  format' str params = T.unpack $ format' str params++instance FormatType Text where+  format' str params = go fragments (reverse params)+    where+      fragments = T.splitOn "{}" str+      go (f:fs) (y:ys) = f <> y <> go fs ys+      go [f] []        = f+      go _ _ = error $ format+        "ShortcutLinks.Utils.format: {} placeholders, but {} parameters"+        (length fragments - 1)+        (length params)++instance (FormatArg a, FormatType r) => FormatType (a -> r) where+  format' str params = \a -> format' str (formatArg a : params)++-- | A 'printf'-like function which fully supports 'Text' as an input and+-- output format and which uses @{}@ instead of @%@ to indicate+-- placeholders. If you use it, don't forget to enable @OverloadedStrings@.+--+-- This is a lightweight alternative to something like the text-format+-- package, and it's closer to 'printf' and simpler to use.+format :: FormatType r => Text -> r+format str = format' str []