lingo 0.3.2.0 → 0.4.0.0
raw patch · 6 files changed
+133/−155 lines, 6 filesdep +aesondep +template-haskelldep ~textsetup-changednew-uploaderPVP ok
version bump matches the API change (PVP)
Dependencies added: aeson, template-haskell
Dependency ranges changed: text
API changes (from Hackage documentation)
- Data.Languages: [languageID] :: Language -> Integer
- Data.Languages: [languageName] :: Language -> Text
+ Data.Languages: [languageId] :: Language -> Integer
+ Data.Languages: [languageKey] :: Language -> LanguageKey
+ Data.Languages: languageName :: Language -> Text
+ Data.Languages.Templates: Language :: Integer -> LanguageKey -> [String] -> [String] -> Language
+ Data.Languages.Templates: [languageExtensions] :: Language -> [String]
+ Data.Languages.Templates: [languageFileNames] :: Language -> [String]
+ Data.Languages.Templates: [languageId] :: Language -> Integer
+ Data.Languages.Templates: [languageKey] :: Language -> LanguageKey
+ Data.Languages.Templates: data Language
+ Data.Languages.Templates: generateLanguageMap :: DecsQ
+ Data.Languages.Templates: instance Data.Aeson.Types.FromJSON.FromJSON Data.Languages.Templates.Language
+ Data.Languages.Templates: instance GHC.Classes.Eq Data.Languages.Templates.Language
+ Data.Languages.Templates: instance GHC.Show.Show Data.Languages.Templates.Language
+ Data.Languages.Templates: instance Language.Haskell.TH.Syntax.Lift Data.Languages.Templates.Language
+ Data.Languages.Templates: languageName :: Language -> Text
+ Data.Languages.Templates: type LanguageKey = String
- Data.Languages: Language :: Integer -> Text -> [Text] -> [Text] -> Language
+ Data.Languages: Language :: Integer -> LanguageKey -> [String] -> [String] -> Language
- Data.Languages: [languageExtensions] :: Language -> [Text]
+ Data.Languages: [languageExtensions] :: Language -> [String]
- Data.Languages: [languageFileNames] :: Language -> [Text]
+ Data.Languages: [languageFileNames] :: Language -> [String]
- Data.Languages: languagesByExtension :: Map Text [LanguageKey]
+ Data.Languages: languagesByExtension :: Map String [LanguageKey]
- Data.Languages: languagesByFileName :: Map Text [LanguageKey]
+ Data.Languages: languagesByFileName :: Map String [LanguageKey]
- Data.Languages: type LanguageKey = Text
+ Data.Languages: type LanguageKey = String
Files
- ChangeLog.md +6/−0
- README.md +9/−3
- Setup.hs +2/−116
- lingo.cabal +13/−19
- src/Data/Languages.hs +23/−17
- src/Data/Languages/Templates.hs +80/−0
ChangeLog.md view
@@ -1,3 +1,9 @@+# 0.4.0.0++Rewrite to use Template Haskell splices instead of a custom Setup.hs.++Add support for the Bazel build system.+ # 0.3.0.0 `languageForPath` is now `languagesForPath`, which returns multiple results in cases of ambiguity.
README.md view
@@ -5,7 +5,13 @@ ## Development ```-cabal new-update-cabal new-configure-cabal new-build+cabal v2-update+cabal v2-configure+cabal v2-build+```++You can also use the [Bazel](https://haskell.build) build system:++```+bazel build //... ```
Setup.hs view
@@ -1,116 +1,2 @@-{-# LANGUAGE OverloadedStrings #-}--import qualified Data.ByteString as B-import qualified Data.ByteString.Char8 as BC-import Debug.Trace-import Distribution.PackageDescription-import Distribution.Simple hiding (Language)-import Distribution.Simple.BuildPaths-import Distribution.Simple.LocalBuildInfo-import Distribution.Simple.Setup-import System.Directory-import System.FilePath.Posix ((</>))-import qualified Data.Map.Strict as Map-import Data.Text (Text)-import Data.Text.Encoding (encodeUtf8)-import qualified Data.Yaml as Y-import Data.Yaml (FromJSON (..), (.!=), (.:), (.:?))--main = defaultMainWithHooks simpleUserHooks { postConf = conf }--conf :: Args -> ConfigFlags -> PackageDescription -> LocalBuildInfo -> IO ()-conf _ _ _ info = do- let autogenDir = autogenPackageModulesDir info- createDirectoryIfMissing True autogenDir- traceM $ "Writing Gen_Languages.hs to autogenDir: " <> autogenDir- dir <- getCurrentDirectory- let yamlFile = dir </> "languages.yml"- yaml <- B.readFile yamlFile- B.writeFile (autogenDir </> "Gen_Languages.hs") (contents yaml)- pure ()--contents :: B.ByteString -> B.ByteString-contents yaml =- "module Gen_Languages where\n\- \\n\- \import Data.ByteString (ByteString)\n\- \import Data.Text (Text)\n\- \import qualified Data.Map.Strict as Map\n\- \\n\- \-- | Type synonym for linguist's language name key.\n\- \type LanguageKey = Text\n\- \\n\- \-- | Identifies a programming language.\n\- \data Language\n\- \ = Language\n\- \ { languageID :: Integer\n\- \ , languageName :: Text\n\- \ , languageExtensions :: [Text]\n\- \ , languageFileNames :: [Text]\n\- \ } deriving (Eq, Show)\n\- \\n\- \-- | Complete map of programming languages known to linguist.\n\- \languages :: Map.Map LanguageKey Language\n\- \languages = Map.fromList\- \ [\n "- <>- either (BC.pack . show) (B.intercalate ",\n ") langs- <>- " ]\n\- \\n\- \-- | Map of languages by file extension.\n\- \languagesByExtension :: Map.Map Text [LanguageKey]\n\- \languagesByExtension = Map.fromList\- \ [\n "- <>- either (BC.pack . show) (B.intercalate ",\n ") langsByExt- <>- " ]\n\- \\n\- \-- | Map of languages by filename.\n\- \languagesByFileName :: Map.Map Text [LanguageKey]\n\- \languagesByFileName = Map.fromList\- \ [\n "- <>- either (BC.pack . show) (B.intercalate ",\n ") langsByFileName- <>- " ]\n\- \"-- where- langs = Map.foldrWithKey (\k v xs -> "(\"" <> encodeUtf8 k <> "\", " <> BC.pack (show v { languageName = k }) <> ")" : xs) mempty <$> languages- langsByExt = Map.foldrWithKey (\k vs xs -> "(\"" <> encodeUtf8 k <> "\", [" <> showLanguages vs <> "])" : xs) mempty <$> languagesByExtension- langsByFileName = Map.foldrWithKey (\k vs xs -> "(\"" <> encodeUtf8 k <> "\", [" <> showLanguages vs <> "])" : xs) mempty <$> languagesByFileName-- showLanguages = BC.intercalate ", " . fmap (wrap . encodeUtf8 . languageName)- wrap x = "\"" <> x <> "\""-- languages :: Either Y.ParseException (Map.Map Text Language)- languages = Y.decodeEither' yaml-- languagesByExtension :: Either Y.ParseException (Map.Map Text [Language])- languagesByExtension = Map.foldrWithKey (buildMap languageExtensions) mempty <$> languages-- languagesByFileName :: Either Y.ParseException (Map.Map Text [Language])- languagesByFileName = Map.foldrWithKey (buildMap languageFileNames) mempty <$> languages-- buildMap :: (Language -> [Text]) -> Text -> Language -> Map.Map Text [Language] -> Map.Map Text [Language]- buildMap f k l m = foldr (\ext b -> Map.insertWith (<>) ext (pure lang) b) m (f lang)- where lang = l { languageName = k }--data Language- = Language- { languageID :: Integer- , languageName :: Text- , languageExtensions :: [Text]- , languageFileNames :: [Text]- } deriving (Eq, Show)--instance FromJSON Language where- parseJSON (Y.Object v) =- Language <$>- v .: "language_id" <*>- v .:? "language_name" .!= mempty <*>- v .:? "extensions" .!= mempty <*>- v .:? "filenames" .!= mempty- parseJSON _ = fail "Expected Object for Language value"+import Distribution.Simple+main = defaultMain
lingo.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.4-build-type: Custom+build-type: Simple name: lingo-version: 0.3.2.0+version: 0.4.0.0 synopsis: File extension based programming language detection description: Lingo uses github/linguist's language registry to enable fast detection of a file path's programming langauge based on extension or filename. homepage: https://github.com/tclem/lingo-haskell@@ -11,32 +11,26 @@ maintainer: timothy.clem@gmail.com category: Data extra-source-files: README.md- , languages.yml , ChangeLog.md--tested-with: GHC == 8.6.5- GHC == 8.8.1+data-files: languages.yml -custom-setup- setup-depends: Cabal- , base- , bytestring- , containers- , directory- , filepath- , text- , yaml+tested-with: GHC == 8.6.5+ , GHC == 8.8.1+ , GHC == 8.10.1 library- exposed-modules: Data.Languages- other-modules: Gen_Languages- autogen-modules: Gen_Languages+ exposed-modules: Data.Languages+ , Data.Languages.Templates+ autogen-modules: Paths_lingo+ other-modules: Paths_lingo build-depends: base >= 4.12 && < 5+ , aeson >= 1.4 && <2 , bytestring ^>= 0.10.8.2 , containers ^>= 0.6.0.1 , filepath ^>= 1.4.2.1 , raw-strings-qq ^>= 1.1- , text ^>= 1.2.3.1+ , text ^>= 1.2.3.2+ , template-haskell >= 2.14.0.0 && < 2.17.0.0 , yaml ^>= 0.11.1.2 hs-source-dirs: src default-language: Haskell2010
src/Data/Languages.hs view
@@ -1,23 +1,29 @@+{-# LANGUAGE TemplateHaskell #-}+ -- | Primary interface for looking up the programming language of a file or -- interacting with languages known to linguist -- (https://github.com/github/linguist). module Data.Languages- ( languagesForPath- , languages- , languagesByExtension- , languagesByFileName- , LanguageKey- , Language(..)- ) where+ ( languagesForPath,+ languages,+ languagesByExtension,+ languagesByFileName,+ LanguageKey,+ Language (..),+ languageName+ )+where -import Control.Applicative+import Control.Applicative+import Data.Languages.Templates import qualified Data.Map.Strict as Map-import Data.Maybe-import Data.Text (Text)+import Data.Maybe+import Data.Text (Text) import qualified Data.Text as Text-import Gen_Languages-import System.FilePath.Posix+import System.FilePath.Posix +$(generateLanguageMap)+ -- | Find the set of possible languages for a given file path. -- -- Multiple results will be returned for ambiguous files; for example, @.md@ files can be Markdown or GCC machine descriptions, and @.php@ files can be PHP or Hack source files.@@ -26,8 +32,8 @@ where languageForFileName = languageFor (takeFileName path) languagesByFileName languageForExtension = languageFor (takeExtension path) languagesByExtension- languageFor :: String -> Map.Map Text [LanguageKey] -> [Language]- languageFor k- = foldMap (maybeToList . flip Map.lookup languages)- . fromMaybe []- . Map.lookup (Text.pack k)+ languageFor :: String -> Map.Map String [LanguageKey] -> [Language]+ languageFor k =+ foldMap (maybeToList . flip Map.lookup languages)+ . fromMaybe []+ . Map.lookup k
+ src/Data/Languages/Templates.hs view
@@ -0,0 +1,80 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveLift #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeApplications #-}++module Data.Languages.Templates+ ( Language (..),+ LanguageKey,+ generateLanguageMap,+ languageName,+ )+where++import Data.Aeson+import Data.ByteString (ByteString)+import qualified Data.Map.Strict as Map+import Data.Text (Text)+import qualified Data.Text as Text+import Data.Yaml+import GHC.Generics (Generic)+import Language.Haskell.TH+import Language.Haskell.TH.Syntax+#ifndef LANGUAGES_YAML_PATH+import Paths_lingo+#endif++-- | Type synonym for linguist's language name key.+type LanguageKey = String++-- | Identifies a programming language.+data Language = Language+ { languageId :: Integer,+ languageKey :: LanguageKey,+ languageExtensions :: [String],+ languageFileNames :: [String]+ }+ deriving (Eq, Show, Lift)++languageName :: Language -> Text+languageName = Text.pack . languageKey++instance FromJSON Language where+ parseJSON = withObject "Language" $ \l ->+ Language+ <$> l .: "language_id"+ <*> pure "unspecified" -- this is set later in map iteration+ <*> l .:? "extensions" .!= []+ <*> l .:? "filenames" .!= []++languagesYamlPath :: IO String+#ifdef LANGUAGES_YAML_PATH+languagesYamlPath = pure LANGUAGES_YAML_PATH+#else+languagesYamlPath = getDataFileName "languages.yml"+#endif++generateLanguageMap :: DecsQ+generateLanguageMap = do+ langYaml <- runIO languagesYamlPath+ langs <- runIO (decodeFileThrow @IO @(Map.Map LanguageKey Language) langYaml)+ let normalizedLangs = Map.mapWithKey (\name lang -> lang {languageKey = name}) langs++ byExtension = Map.foldr (process languageExtensions) mempty normalizedLangs+ byFileName = Map.foldr (process languageFileNames) mempty normalizedLangs++ process :: (Language -> [String]) -> Language -> Map.Map String [LanguageKey] -> Map.Map String [LanguageKey]+ process selector lang acc = foldr (\ext -> Map.insertWith mappend ext [languageKey lang]) acc (selector lang)++ [d|+ languages :: Map.Map LanguageKey Language+ languages = Map.fromDistinctAscList $(lift (Map.toAscList normalizedLangs))++ languagesByExtension :: Map.Map String [LanguageKey]+ languagesByExtension = Map.fromDistinctAscList $(lift (Map.toAscList byExtension))++ languagesByFileName :: Map.Map String [LanguageKey]+ languagesByFileName = Map.fromDistinctAscList $(lift (Map.toAscList byFileName))+ |]