diff --git a/CHANGES.txt b/CHANGES.txt
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,7 +1,10 @@
 Changelog for Hoogle
 
+5.0.4
+    #184, use file URLs for local docs
+    #183, make --local look for .txt files recursively
 5.0.3
-    #181, use the proper network constrains
+    #181, use the proper network constraints
     Avoid deprecated Aeson modules
 5.0.2
     Move to haskell-src-exts-1.18
diff --git a/hoogle.cabal b/hoogle.cabal
--- a/hoogle.cabal
+++ b/hoogle.cabal
@@ -1,7 +1,7 @@
 cabal-version:      >= 1.10
 build-type:         Simple
 name:               hoogle
-version:            5.0.3
+version:            5.0.4
 license:            BSD3
 license-file:       LICENSE
 category:           Development
diff --git a/src/Action/Generate.hs b/src/Action/Generate.hs
--- a/src/Action/Generate.hs
+++ b/src/Action/Generate.hs
@@ -128,10 +128,12 @@
 
 readHaskellDir :: Timing -> FilePath -> IO (Map.Map String Package, Set.Set String, Source IO (String, URL, LStr))
 readHaskellDir timing dir = do
-    packages <- map (takeBaseName &&& id) . filter ((==) ".txt" . takeExtension) <$> listFiles dir
+    packages <- map (takeBaseName &&& id) . filter ((==) ".txt" . takeExtension) <$> listFilesRecursive dir
     let source = forM_ packages $ \(name, file) -> do
             src <- liftIO $ strReadFile file
-            yield (name, hackagePackageURL name, lstrFromChunks [src])
+            dir <- liftIO $ canonicalizePath $ takeDirectory file
+            let url = "file://" ++ ['/' | not $ "/" `isPrefixOf` dir] ++ replace "\\" "/" dir ++ "/"
+            yield (name, url, lstrFromChunks [src])
     return (Map.fromList $ map ((,mempty{packageTags=[(T.pack "set",T.pack "all")]}) . fst) packages
            ,Set.fromList $ map fst packages, source)
 
