    {- |
       Module      :  XMonad.Actions.Search
       Description :  Easily run Internet searches on web sites through xmonad.
       Copyright   :  (C) 2007 Gwern Branwen
       License     :  None; public domain

       Maintainer  :  <gwern0@gmail.com>
       Stability   :  unstable
       Portability :  unportable; depends on XSelection, XPrompt

       A module for easily running Internet searches on web sites through xmonad.
       Modeled after the handy Surfraw CLI search tools at <https://secure.wikimedia.org/wikipedia/en/wiki/Surfraw>.

       Additional sites welcomed. -}
    module XMonad.Actions.Search (   -- * Usage
                                     -- $usage
                                     search,
                                     SearchEngine(..),
                                     searchEngine,
                                     searchEngineF,
                                     promptSearch,
                                     promptSearchBrowser,
                                     promptSearchBrowser',
                                     selectSearch,
                                     selectSearchBrowser,
                                     isPrefixOf,
                                     escape,
                                     use,
                                     intelligent,
                                     (!>),
                                     prefixAware,
                                     namedEngine,

                                     alpha,
                                     amazon,
                                     arXiv,
                                     aur,
                                     clojureDocs,
                                     codesearch,
                                     cratesIo,
                                     deb,
                                     debbts,
                                     debpts,
                                     dictionary,
                                     duckduckgo,
                                     ebay,
                                     flora,
                                     github,
                                     google,
                                     hackage,
                                     homeManager,
                                     hoogle,
                                     images,
                                     imdb,
                                     lucky,
                                     maps,
                                     mathworld,
                                     ncatlab,
                                     nixos,
                                     noogle,
                                     openstreetmap,
                                     protondb,
                                     rosettacode,
                                     rustStd,
                                     scholar,
                                     sourcehut,
                                     stackage,
                                     steam,
                                     thesaurus,
                                     vocabulary,
                                     voidpgks_x86_64,
                                     voidpgks_x86_64_musl,
                                     wayback,
                                     wikipedia,
                                     wiktionary,
                                     youtube,
                                     zbmath,
                                     multi,
                                      -- * Use case: searching with a submap
                                      -- $tip

                                      -- * Types
                                     Browser, Site, Query, Name, Search
                              ) where

    import           Codec.Binary.UTF8.String (encode)
    import           Text.Printf
    import           XMonad                   (X (), liftIO)
    import           XMonad.Prompt            (XPConfig (), XPrompt (showXPrompt, nextCompletion, commandToComplete),
                                               getNextCompletion,
                                               historyCompletionP, mkXPrompt)
    import           XMonad.Prelude           (isAlphaNum, isAscii, isPrefixOf)
    import           XMonad.Prompt.Shell      (getBrowser)
    import           XMonad.Util.Run          (safeSpawn)
    import           XMonad.Util.XSelection   (getSelection)

    {- $usage

       This module is intended to allow easy access to databases on the
       Internet through xmonad's interface. The idea is that one wants to
       run a search but the query string and the browser to use must come
       from somewhere. There are two places the query string can come from
       - the user can type it into a prompt which pops up, or the query
       could be available already in the X Windows copy\/paste buffer
       (perhaps you just highlighted the string of interest).

       Thus, there are two main functions: 'promptSearch', and
       'selectSearch' (implemented using the more primitive 'search'). To
       each of these is passed an engine function; this is a function that
       knows how to search a particular site.

       For example, the 'google' function knows how to search Google, and
       so on. You pass 'promptSearch' and 'selectSearch' the engine you
       want, the browser you want, and anything special they might need;
       this whole line is then bound to a key of you choosing in your
       xmonad.hs. For specific examples, see each function.  This module
       is easily extended to new sites by using 'searchEngine'.

       The currently available search engines are:

    * 'alpha' -- Wolfram|Alpha query.

    * 'amazon' -- Amazon keyword search.

    * 'arXiv' -- Open-access preprint archive.

    * 'aur' -- Arch User Repository.

    * 'clojureDocs' -- Documentation and examples repository for Clojure.

    * 'codesearch' -- Google Labs Code Search search.

    * 'cratesIo' -- Rust crate registry.

    * 'deb'    -- Debian package search.

    * 'debbts' -- Debian Bug Tracking System.

    * 'debpts'  -- Debian Package Tracking System.

    * 'dictionary' -- dictionary.reference.com search.

    * 'duckduckgo' -- DuckDuckGo search engine.

    * 'ebay' -- Ebay keyword search.

    * 'flora' -- Prettier Haskell package database.

    * 'github' -- GitHub keyword search.

    * 'google' -- basic Google search.

    * 'hackage' -- Hackage, the Haskell package database.

    * 'homeManager' -- Search Nix's home-manager's options.

    * 'hoogle' -- Hoogle, the Haskell libraries API search engine.

    * 'images' -- Google images.

    * 'imdb'   -- the Internet Movie Database.

    * 'lucky' -- Google "I'm feeling lucky" search.

    * 'maps'   -- Google maps.

    * 'mathworld' -- Wolfram MathWorld search.

    * 'ncatlab' -- Higer Algebra, Homotopy and Category Theory Wiki.

    * 'nixos' -- Search NixOS packages and options.

    * 'noogle' -- 'hoogle'-like Nix API search engine.

    * 'openstreetmap' -- OpenStreetMap free wiki world map.

    * 'protondb' -- Steam Proton Game Database.

    * 'rosettacode' -- Programming chrestomathy wiki.

    * 'rustStd' -- Rust standard library documentation.

    * 'scholar' -- Google scholar academic search.

    * 'sourcehut' -- Sourcehut projects search.

    * 'stackage' -- Stackage, An alternative Haskell libraries API search engine.

    * 'steam' -- Steam games search.

    * 'thesaurus' -- thesaurus.com search.

    * 'vocabulary' -- Dictionary search.

    * 'voidpgks_x86_64' -- Void Linux packages search for @x86_64@.

    * 'voidpgks_x86_64_musl' -- Void Linux packages search for @x86_64-musl@.

    * 'wayback' -- the Wayback Machine.

    * 'wikipedia' -- basic Wikipedia search.

    * 'wiktionary' -- Wiktionary search.

    * 'youtube' -- Youtube video search.

    * 'zbmath' -- Open alternative to MathSciNet.

    * 'multi' -- Search based on the prefix. \"amazon:Potter\" will use amazon, etc. With no prefix searches google.

    Feel free to add more! -}

    {- $tip

    In combination with "XMonad.Actions.Submap" you can create a powerful
    and easy way to search without adding a whole bunch of bindings.

    First import the necessary modules:

    > import qualified XMonad.Prompt         as P
    > import qualified XMonad.Actions.Submap as SM
    > import qualified XMonad.Actions.Search as S

    Then add the following to your key bindings:

    > ...
    > -- Search commands
    > , ((modm, xK_s), SM.submap $ searchEngineMap $ S.promptSearch P.def)
    > , ((modm .|. shiftMask, xK_s), SM.submap $ searchEngineMap $ S.selectSearch)
    >
    > ...
    >
    > searchEngineMap method = M.fromList $
    >       [ ((0, xK_g), method S.google)
    >       , ((0, xK_h), method S.hoogle)
    >       , ((0, xK_w), method S.wikipedia)
    >       ]

    Or in combination with XMonad.Util.EZConfig:

    > ...
    > ] -- end of regular keybindings
    > -- Search commands
    > ++ [("M-s " ++ k, S.promptSearch P.def f) | (k,f) <- searchList ]
    > ++ [("M-S-s " ++ k, S.selectSearch f) | (k,f) <- searchList ]
    >
    > ...
    >
    > searchList :: [(String, S.SearchEngine)]
    > searchList = [ ("g", S.google)
    >              , ("h", S.hoogle)
    >              , ("w", S.wikipedia)
    >              ]

    Make sure to set firefox to open new pages in a new window instead of
    in a new tab: @Firefox -> Edit -> Preferences -> Tabs -> New pages
    should be opened in...@

    Now /mod-s/ + /g/\//h/\//w/ prompts you for a search string, then
    opens a new firefox window that performs the search on Google, Hoogle
    or Wikipedia respectively.

    If you select something in whatever application and hit /mod-shift-s/ +
    /g/\//h/\//w/ it will search the selected string with the specified
    engine.

    Happy searching! -}

    -- | A customized prompt indicating we are searching, and the name of the site.
    newtype Search = Search Name
    instance XPrompt Search where
        showXPrompt :: Search -> String
    showXPrompt (Search String
    name)= String
    "Search [" String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    name String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    "]: "
        nextCompletion :: Search -> String -> [String] -> String
    nextCompletion Search
    _ = String -> [String] -> String
    getNextCompletion
        commandToComplete :: Search -> String -> String
    commandToComplete Search
    _ String
    c = String
    c

    -- | Escape the search string so search engines understand it. Only
    -- digits and ASCII letters are not encoded. All non ASCII characters
    -- which are encoded as UTF8
    escape :: String -> String
    escape :: String -> String
    escape = (Char -> String) -> String -> String
    forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
    concatMap Char -> String
    escapeURIChar

    escapeURIChar :: Char -> String
    escapeURIChar :: Char -> String
    escapeURIChar Char
    c | Char -> Bool
    isAscii Char
    c Bool -> Bool -> Bool
    && Char -> Bool
    isAlphaNum Char
    c = [Char
    c]
                    | Bool
    otherwise                 = (Word8 -> String) -> [Word8] -> String
    forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
    concatMap (String -> Word8 -> String
    forall r. PrintfType r => String -> r
    printf String
    "%%%02X") ([Word8] -> String) -> [Word8] -> String
    forall a b. (a -> b) -> a -> b
    $ String -> [Word8]
    encode [Char
    c]

    type Browser      = FilePath
    type Query        = String
    type Site         = String -> String
    type Name         = String
    data SearchEngine = SearchEngine Name Site

    -- | Given an already defined search engine, extracts its transformation
    --   function, making it easy to create compound search engines.
    --   For an instance you can use @use google@ to get a function which
    --   makes the same transformation as the google search engine would.
    use :: SearchEngine -> Site
    use :: SearchEngine -> String -> String
    use (SearchEngine String
    _ String -> String
    engine) = String -> String
    engine

    -- | Given a browser, a search engine's transformation function, and a search term, perform the
    --   requested search in the browser.
    search :: Browser -> Site -> Query -> X ()
    search :: String -> (String -> String) -> String -> X ()
    search String
    browser String -> String
    site String
    query = String -> [String] -> X ()
    forall (m :: * -> *). MonadIO m => String -> [String] -> m ()
    safeSpawn String
    browser [String -> String
    site String
    query]

    {- | Given a base URL, create the 'SearchEngine' that escapes the query and
       appends it to the base. You can easily define a new engine locally using
       exported functions without needing to modify "XMonad.Actions.Search":

    > myNewEngine = searchEngine "site" "https://site.com/search="

       The important thing is that the site has a interface which accepts the escaped query
       string as part of the URL. Alas, the exact URL to feed searchEngine varies
       from site to site, often considerably, so there\'s no general way to cover this.

       Generally, examining the resultant URL of a search will allow you to reverse-engineer
       it if you can't find the necessary URL already described in other projects such as Surfraw. -}
    searchEngine :: Name -> String -> SearchEngine
    searchEngine :: String -> String -> SearchEngine
    searchEngine String
    name String
    site = String -> (String -> String) -> SearchEngine
    searchEngineF String
    name (\String
    s -> String
    site String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String -> String
    escape String
    s)

    {- | If your search engine is more complex than this (you may want to identify
       the kind of input and make the search URL dependent on the input or put the query
       inside of a URL instead of in the end) you can use the alternative 'searchEngineF' function.

    > searchFunc :: String -> String
    > searchFunc s | "wiki:"    `isPrefixOf` s = "https://en.wikipedia.org/wiki/" ++ (escape $ drop 1 $ snd $ break (==':') s)
    >              | "https://" `isPrefixOf` s = s
    >              | otherwise                 = (use google) s
    > myNewEngine = searchEngineF "mymulti" searchFunc

       @searchFunc@ here searches for a word in wikipedia if it has a prefix
       of \"wiki:\" (you can use the 'escape' function to escape any forbidden characters), opens an address
       directly if it starts with \"https:\/\/\" and otherwise uses the provided google search engine.
       You can use other engines inside of your own through the 'use' function as shown above to make
       complex searches.

       The user input will be automatically escaped in search engines created with 'searchEngine',
       'searchEngineF', however, completely depends on the transformation function passed to it. -}
    searchEngineF :: Name -> Site -> SearchEngine
    searchEngineF :: String -> (String -> String) -> SearchEngine
    searchEngineF = String -> (String -> String) -> SearchEngine
    SearchEngine

    -- The engines.
    alpha, amazon, arXiv, aur, clojureDocs, codesearch, cratesIo, deb, debbts, debpts, dictionary, duckduckgo, ebay, flora,
      github, google, hackage, homeManager, hoogle, images, imdb, lucky, maps, mathworld, ncatlab, nixos, noogle, openstreetmap, protondb,
      rosettacode, rustStd, scholar, sourcehut, stackage, steam, thesaurus, vocabulary, voidpgks_x86_64, voidpgks_x86_64_musl, wayback,
      wikipedia, wiktionary, youtube, zbmath :: SearchEngine
    alpha :: SearchEngine
    alpha         = String -> String -> SearchEngine
    searchEngine String
    "alpha"         String
    "https://www.wolframalpha.com/input/?i="
    amazon :: SearchEngine
    amazon        = String -> String -> SearchEngine
    searchEngine String
    "amazon"        String
    "https://www.amazon.com/s/ref=nb_sb_noss_2?url=search-alias%3Daps&field-keywords="
    arXiv :: SearchEngine
    arXiv         = String -> (String -> String) -> SearchEngine
    searchEngineF String
    "arXiv"        (\String
    s -> String
    "https://arxiv.org/search/?query=" String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> String
    s String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> String
    "&searchtype=all")
    aur :: SearchEngine
    aur           = String -> String -> SearchEngine
    searchEngine String
    "aur"           String
    "https://aur.archlinux.org/packages?&K="
    clojureDocs :: SearchEngine
    clojureDocs   = String -> String -> SearchEngine
    searchEngine String
    "clojureDocs"   String
    "https://clojuredocs.org/search?q="
    codesearch :: SearchEngine
    codesearch    = String -> String -> SearchEngine
    searchEngine String
    "codesearch"    String
    "https://developers.google.com/s/results/code-search?q="
    cratesIo :: SearchEngine
    cratesIo      = String -> String -> SearchEngine
    searchEngine  String
    "cratesIo"     String
    "https://crates.io/search?q="
    deb :: SearchEngine
    deb           = String -> String -> SearchEngine
    searchEngine String
    "deb"           String
    "https://packages.debian.org/"
    debbts :: SearchEngine
    debbts        = String -> String -> SearchEngine
    searchEngine String
    "debbts"        String
    "https://bugs.debian.org/"
    debpts :: SearchEngine
    debpts        = String -> String -> SearchEngine
    searchEngine String
    "debpts"        String
    "https://packages.qa.debian.org/"
    dictionary :: SearchEngine
    dictionary    = String -> String -> SearchEngine
    searchEngine String
    "dict"          String
    "https://dictionary.reference.com/browse/"
    duckduckgo :: SearchEngine
    duckduckgo    = String -> String -> SearchEngine
    searchEngine String
    "duckduckgo"    String
    "https://duckduckgo.com/?t=lm&q="
    ebay :: SearchEngine
    ebay          = String -> String -> SearchEngine
    searchEngine String
    "ebay"          String
    "https://www.ebay.com/sch/i.html?_nkw="
    flora :: SearchEngine
    flora         = String -> String -> SearchEngine
    searchEngine String
    "flora"         String
    "https://flora.pm/search?q="
    github :: SearchEngine
    github        = String -> String -> SearchEngine
    searchEngine String
    "github"        String
    "https://github.com/search?q="
    google :: SearchEngine
    google        = String -> String -> SearchEngine
    searchEngine String
    "google"        String
    "https://www.google.com/search?q="
    hackage :: SearchEngine
    hackage       = String -> String -> SearchEngine
    searchEngine String
    "hackage"       String
    "https://hackage.haskell.org/package/"
    homeManager :: SearchEngine
    homeManager   = String -> String -> SearchEngine
    searchEngine String
    "homeManager"   String
    "https://mipmip.github.io/home-manager-option-search/?query="
    hoogle :: SearchEngine
    hoogle        = String -> String -> SearchEngine
    searchEngine String
    "hoogle"        String
    "https://hoogle.haskell.org/?hoogle="
    images :: SearchEngine
    images        = String -> String -> SearchEngine
    searchEngine String
    "images"        String
    "https://images.google.fr/images?q="
    imdb :: SearchEngine
    imdb          = String -> String -> SearchEngine
    searchEngine String
    "imdb"          String
    "https://www.imdb.com/find?s=all&q="
    lucky :: SearchEngine
    lucky         = String -> String -> SearchEngine
    searchEngine String
    "lucky"         String
    "https://www.google.com/search?btnI&q="
    maps :: SearchEngine
    maps          = String -> String -> SearchEngine
    searchEngine String
    "maps"          String
    "https://maps.google.com/maps?q="
    mathworld :: SearchEngine
    mathworld     = String -> String -> SearchEngine
    searchEngine String
    "mathworld"     String
    "https://mathworld.wolfram.com/search/?query="
    ncatlab :: SearchEngine
    ncatlab       = String -> String -> SearchEngine
    searchEngine String
    "ncatlab"       String
    "https://ncatlab.org/nlab/search?query="
    nixos :: SearchEngine
    nixos         = String -> String -> SearchEngine
    searchEngine String
    "nixos"         String
    "https://search.nixos.org/packages?channel=unstable&from=0&size=200&sort=relevance&type=packages&query="
    noogle :: SearchEngine
    noogle        = String -> (String -> String) -> SearchEngine
    searchEngineF String
    "noogle"       (\String
    s -> String
    "https://noogle.dev/?search=" String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> String
    s String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> String
    "&page=1&to=any&from=any")
    openstreetmap :: SearchEngine
    openstreetmap = String -> String -> SearchEngine
    searchEngine String
    "openstreetmap" String
    "https://www.openstreetmap.org/search?query="
    protondb :: SearchEngine
    protondb      = String -> String -> SearchEngine
    searchEngine String
    "protondb"      String
    "https://www.protondb.com/search?q="
    rosettacode :: SearchEngine
    rosettacode   = String -> String -> SearchEngine
    searchEngine String
    "rosettacode"   String
    "https://rosettacode.org/w/index.php?search="
    rustStd :: SearchEngine
    rustStd       = String -> String -> SearchEngine
    searchEngine String
    "rustStd"       String
    "https://doc.rust-lang.org/std/index.html?search="
    scholar :: SearchEngine
    scholar       = String -> String -> SearchEngine
    searchEngine String
    "scholar"       String
    "https://scholar.google.com/scholar?q="
    sourcehut :: SearchEngine
    sourcehut     = String -> String -> SearchEngine
    searchEngine String
    "sourcehut"     String
    "https://sr.ht/projects?search="
    stackage :: SearchEngine
    stackage      = String -> String -> SearchEngine
    searchEngine String
    "stackage"      String
    "https://www.stackage.org/lts/hoogle?q="
    steam :: SearchEngine
    steam         = String -> String -> SearchEngine
    searchEngine String
    "steam"         String
    "https://store.steampowered.com/search/?term="
    thesaurus :: SearchEngine
    thesaurus     = String -> String -> SearchEngine
    searchEngine String
    "thesaurus"     String
    "https://thesaurus.com/browse/"
    vocabulary :: SearchEngine
    vocabulary    = String -> String -> SearchEngine
    searchEngine String
    "vocabulary"    String
    "https://www.vocabulary.com/search?q="
    voidpgks_x86_64 :: SearchEngine
    voidpgks_x86_64      = String -> String -> SearchEngine
    searchEngine String
    "voidpackages" String
    "https://voidlinux.org/packages/?arch=x86_64&q="
    voidpgks_x86_64_musl :: SearchEngine
    voidpgks_x86_64_musl = String -> String -> SearchEngine
    searchEngine String
    "voidpackages" String
    "https://voidlinux.org/packages/?arch=x86_64-musl&q="
    wayback :: SearchEngine
    wayback       = String -> (String -> String) -> SearchEngine
    searchEngineF String
    "wayback"      (String
    "https://web.archive.org/web/*/"String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++)
    wikipedia :: SearchEngine
    wikipedia     = String -> String -> SearchEngine
    searchEngine String
    "wiki"          String
    "https://en.wikipedia.org/wiki/Special:Search?go=Go&search="
    wiktionary :: SearchEngine
    wiktionary    = String -> String -> SearchEngine
    searchEngine String
    "wikt"          String
    "https://en.wiktionary.org/wiki/Special:Search?go=Go&search="
    youtube :: SearchEngine
    youtube       = String -> String -> SearchEngine
    searchEngine String
    "youtube"       String
    "https://www.youtube.com/results?search_type=search_videos&search_query="
    zbmath :: SearchEngine
    zbmath        = String -> String -> SearchEngine
    searchEngine String
    "zbmath"        String
    "https://zbmath.org/?q="

    multi :: SearchEngine
    multi :: SearchEngine
    multi = String -> SearchEngine -> SearchEngine
    namedEngine String
    "multi" (SearchEngine -> SearchEngine) -> SearchEngine -> SearchEngine
    forall a b. (a -> b) -> a -> b
    $ (SearchEngine -> SearchEngine -> SearchEngine)
    -> [SearchEngine] -> SearchEngine
    forall a. (a -> a -> a) -> [a] -> a
    forall (t :: * -> *) a. Foldable t => (a -> a -> a) -> t a -> a
    foldr1 SearchEngine -> SearchEngine -> SearchEngine
    (!>) [SearchEngine
    alpha, SearchEngine
    amazon, SearchEngine
    aur, SearchEngine
    codesearch, SearchEngine
    deb, SearchEngine
    debbts, SearchEngine
    debpts, SearchEngine
    dictionary, SearchEngine
    duckduckgo, SearchEngine
    ebay, SearchEngine
    flora, SearchEngine
    github, SearchEngine
    hackage, SearchEngine
    hoogle, SearchEngine
    images, SearchEngine
    imdb, SearchEngine
    lucky, SearchEngine
    maps, SearchEngine
    mathworld, SearchEngine
    ncatlab, SearchEngine
    openstreetmap, SearchEngine
    protondb, SearchEngine
    rosettacode, SearchEngine
    scholar, SearchEngine
    sourcehut, SearchEngine
    stackage, SearchEngine
    steam, SearchEngine
    thesaurus, SearchEngine
    vocabulary, SearchEngine
    voidpgks_x86_64, SearchEngine
    voidpgks_x86_64_musl, SearchEngine
    wayback, SearchEngine
    wikipedia, SearchEngine
    wiktionary, SearchEngine
    youtube, SearchEngine -> SearchEngine
    prefixAware SearchEngine
    google]

    {- | This function wraps up a search engine and creates a new one, which works
       like the argument, but goes directly to a URL if one is given rather than
       searching.

    > myIntelligentGoogleEngine = intelligent google

       Now if you search for https:\/\/xmonad.org it will directly open in your browser-}
    intelligent :: SearchEngine -> SearchEngine
    intelligent :: SearchEngine -> SearchEngine
    intelligent (SearchEngine String
    name String -> String
    site) = String -> (String -> String) -> SearchEngine
    searchEngineF String
    name (\String
    s -> if (Char -> Bool) -> String -> String
    forall a. (a -> Bool) -> [a] -> [a]
    takeWhile (Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Char
    ':') String
    s String -> [String] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` [String
    "http", String
    "https", String
    "ftp"] then String
    s else String -> String
    site String
    s)

    -- | > removeColonPrefix "foo://bar" ~> "//bar"
    -- > removeColonPrefix "foo//bar" ~> "foo//bar"
    removeColonPrefix :: String -> String
    removeColonPrefix :: String -> String
    removeColonPrefix String
    s = if Char
    ':' Char -> String -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` String
    s then Int -> String -> String
    forall a. Int -> [a] -> [a]
    drop Int
    1 (String -> String) -> String -> String
    forall a b. (a -> b) -> a -> b
    $ (Char -> Bool) -> String -> String
    forall a. (a -> Bool) -> [a] -> [a]
    dropWhile (Char
    ':' Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    /=) String
    s else String
    s

    {- | Connects a few search engines into one. If the search engines\' names are
       \"s1\", \"s2\" and \"s3\", then the resulting engine will use s1 if the query
       is @s1:word@, s2 if you type @s2:word@ and s3 in all other cases.

       Example:

    > multiEngine = intelligent (wikipedia !> mathworld !> (prefixAware google))

      Now if you type \"wiki:Haskell\" it will search for \"Haskell\" in Wikipedia,
      \"mathworld:integral\" will search mathworld, and everything else will fall back to
      google. The use of intelligent will make sure that URLs are opened directly. -}
    (!>) :: SearchEngine -> SearchEngine -> SearchEngine
    (SearchEngine String
    name1 String -> String
    site1) !> :: SearchEngine -> SearchEngine -> SearchEngine
    !> (SearchEngine String
    name2 String -> String
    site2) = String -> (String -> String) -> SearchEngine
    searchEngineF (String
    name1 String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    "/" String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    name2) (\String
    s -> if (String
    name1String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++String
    ":") String -> String -> Bool
    forall a. Eq a => [a] -> [a] -> Bool
    `isPrefixOf` String
    s then String -> String
    site1 (String -> String
    removeColonPrefix String
    s) else String -> String
    site2 String
    s)
    infixr 6 !>

    {- | Makes a search engine prefix-aware. Especially useful together with '!>'.
       It will automatically remove the prefix from a query so that you don\'t end
         up searching for google:xmonad if google is your fallback engine and you
         explicitly add the prefix. -}
    prefixAware :: SearchEngine -> SearchEngine
    prefixAware :: SearchEngine -> SearchEngine
    prefixAware (SearchEngine String
    name String -> String
    site) = String -> (String -> String) -> SearchEngine
    SearchEngine String
    name (\String
    s -> if (String
    nameString -> String -> String
    forall a. [a] -> [a] -> [a]
    ++String
    ":") String -> String -> Bool
    forall a. Eq a => [a] -> [a] -> Bool
    `isPrefixOf` String
    s then String -> String
    site (String -> String) -> String -> String
    forall a b. (a -> b) -> a -> b
    $ String -> String
    removeColonPrefix String
    s else String -> String
    site String
    s)

    {- | Changes search engine's name -}
    namedEngine :: Name -> SearchEngine -> SearchEngine
    namedEngine :: String -> SearchEngine -> SearchEngine
    namedEngine String
    name (SearchEngine String
    _ String -> String
    site) = String -> (String -> String) -> SearchEngine
    searchEngineF String
    name String -> String
    site

    {- | Like 'search', but for use with the output from a Prompt; it grabs the
       Prompt's result, passes it to a given searchEngine and opens it in a given
       browser. -}
    promptSearchBrowser :: XPConfig -> Browser -> SearchEngine -> X ()
    promptSearchBrowser :: XPConfig -> String -> SearchEngine -> X ()
    promptSearchBrowser XPConfig
    config String
    browser (SearchEngine String
    name String -> String
    site) = do
        ComplFunction
    hc <- (String -> Bool) -> X ComplFunction
    historyCompletionP (String
    "Search [" String -> String -> Bool
    forall a. Eq a => [a] -> [a] -> Bool
    `isPrefixOf`)
        Search -> XPConfig -> ComplFunction -> (String -> X ()) -> X ()
    forall p.
    XPrompt p =>
    p -> XPConfig -> ComplFunction -> (String -> X ()) -> X ()
    mkXPrompt (String -> Search
    Search String
    name) XPConfig
    config ComplFunction
    hc ((String -> X ()) -> X ()) -> (String -> X ()) -> X ()
    forall a b. (a -> b) -> a -> b
    $ String -> (String -> String) -> String -> X ()
    search String
    browser String -> String
    site

    {- | Like 'promptSearchBrowser', but only suggest previous searches for the
       given 'SearchEngine' in the prompt. -}
    promptSearchBrowser' :: XPConfig -> Browser -> SearchEngine -> X ()
    promptSearchBrowser' :: XPConfig -> String -> SearchEngine -> X ()
    promptSearchBrowser' XPConfig
    config String
    browser (SearchEngine String
    name String -> String
    site) = do
        ComplFunction
    hc <- (String -> Bool) -> X ComplFunction
    historyCompletionP (String
    searchName String -> String -> Bool
    forall a. Eq a => [a] -> [a] -> Bool
    `isPrefixOf`)
        Search -> XPConfig -> ComplFunction -> (String -> X ()) -> X ()
    forall p.
    XPrompt p =>
    p -> XPConfig -> ComplFunction -> (String -> X ()) -> X ()
    mkXPrompt (String -> Search
    Search String
    name) XPConfig
    config ComplFunction
    hc ((String -> X ()) -> X ()) -> (String -> X ()) -> X ()
    forall a b. (a -> b) -> a -> b
    $ String -> (String -> String) -> String -> X ()
    search String
    browser String -> String
    site
      where
        searchName :: String
    searchName = Search -> String
    forall t. XPrompt t => t -> String
    showXPrompt (String -> Search
    Search String
    name)

    {- | Like 'search', but in this case, the string is not specified but grabbed
     from the user's response to a prompt. Example:

    > , ((modm, xK_g), promptSearch greenXPConfig google)

       This specializes "promptSearchBrowser" by supplying the browser argument as
       supplied by 'getBrowser' from "XMonad.Prompt.Shell". -}
    promptSearch :: XPConfig -> SearchEngine -> X ()
    promptSearch :: XPConfig -> SearchEngine -> X ()
    promptSearch XPConfig
    config SearchEngine
    engine = IO String -> X String
    forall a. IO a -> X a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO IO String
    getBrowser X String -> (String -> X ()) -> X ()
    forall a b. X a -> (a -> X b) -> X b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= \ String
    browser -> XPConfig -> String -> SearchEngine -> X ()
    promptSearchBrowser XPConfig
    config String
    browser SearchEngine
    engine

    -- | Like 'search', but for use with the X selection; it grabs the selection,
    --   passes it to a given searchEngine and opens it in a given browser.
    selectSearchBrowser :: Browser -> SearchEngine -> X ()
    selectSearchBrowser :: String -> SearchEngine -> X ()
    selectSearchBrowser String
    browser (SearchEngine String
    _ String -> String
    site) = String -> (String -> String) -> String -> X ()
    search String
    browser String -> String
    site (String -> X ()) -> X String -> X ()
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< X String
    forall (m :: * -> *). MonadIO m => m String
    getSelection

    {- | Like 'search', but for use with the X selection; it grabs the selection,
       passes it to a given searchEngine and opens it in the default browser . Example:

    > , ((modm .|. shiftMask, xK_g), selectSearch google)

       This specializes "selectSearchBrowser" by supplying the browser argument as
       supplied by 'getBrowser' from "XMonad.Prompt.Shell". -}
    selectSearch :: SearchEngine -> X ()
    selectSearch :: SearchEngine -> X ()
    selectSearch SearchEngine
    engine = IO String -> X String
    forall a. IO a -> X a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO IO String
    getBrowser X String -> (String -> X ()) -> X ()
    forall a b. X a -> (a -> X b) -> X b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= \String
    browser -> String -> SearchEngine -> X ()
    selectSearchBrowser String
    browser SearchEngine
    engine