packages feed

lingo (empty) → 0.1.0.0

raw patch · 6 files changed

+269/−0 lines, 6 filesdep +basedep +bytestringdep +containersbuild-type:Customsetup-changed

Dependencies added: base, bytestring, containers, filepath, hspec, lingo, raw-strings-qq, text, yaml

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2019, Timothy Clem++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 Timothy Clem 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,11 @@+# Lingo++Detect programming languages from file extensions and common filenames. Based on [linguist's](https://github.com/github/linguist) registry of languages.++## Development++```+cabal new-update+cabal new-configure+cabal new-build+```
+ Setup.hs view
@@ -0,0 +1,116 @@+{-# 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"
+ lingo.cabal view
@@ -0,0 +1,52 @@+cabal-version:       2.4+build-type:          Custom+name:                lingo+version:             0.1.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+license:             BSD-3-Clause+license-file:        LICENSE+author:              Timothy Clem+maintainer:          timothy.clem@gmail.com+category:            Data+extra-source-files:  README.md++custom-setup+  setup-depends:       Cabal+                     , base+                     , bytestring+                     , containers+                     , directory+                     , filepath+                     , text+                     , yaml++library+  exposed-modules: Data.Languages+  other-modules: Gen_Languages+  autogen-modules: Gen_Languages+  build-depends:       base ^>=4.12.0.0+                     , bytestring+                     , containers+                     , filepath+                     , raw-strings-qq+                     , text+                     , yaml+  hs-source-dirs:      src+  default-language:    Haskell2010+  default-extensions:  OverloadedStrings++test-suite test+  type:                exitcode-stdio-1.0+  hs-source-dirs:      test+  main-is:             Spec.hs+  build-depends:       base ^>=4.12.0.0+                     , lingo+                     , hspec+  default-language:    Haskell2010+  default-extensions:  OverloadedStrings++source-repository head+  type:     git+  location: https://github.com/tclem/lingo-haskell
+ src/Data/Languages.hs view
@@ -0,0 +1,35 @@+-- | 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+  ( languageForPath+  , languages+  , languagesByExtension+  , languagesByFileName+  , LanguageKey+  , Language(..)+  ) where++import           Control.Applicative+import qualified Data.Map.Strict as Map+import           Data.Maybe+import           Data.Text (Text)+import qualified Data.Text as Text+import           Gen_Languages+import           System.FilePath.Posix++-- | Find the Language (if any) for a FilePath.+languageForPath :: FilePath -> Maybe Language+languageForPath path = languageForFileName <|> languageForExtension+  where+    languageForFileName = languageFor (takeFileName path) languagesByFileName+    languageForExtension = languageFor (takeExtension path) languagesByExtension+    languageFor k f = maybe Nothing lookupPrimary $ Map.lookup (Text.pack k) f++    -- Some extensions and filenames associate with multiple languages, this is+    -- a dumb way to specify which is the primary language.+    lookupPrimary xs+      | null xs = Nothing+      | "Markdown" `elem` xs = lookup "Markdown"+      | otherwise = lookup (head xs)+      where lookup k = Map.lookup k languages
+ test/Spec.hs view
@@ -0,0 +1,25 @@+module Main where++import Test.Hspec+import Data.Languages++main :: IO ()+main = hspec $ do+  describe "detect language for path" $ do+    it "can detect Ruby by file extension" $+      languageName <$> languageForPath "test.rb" `shouldBe` Just "Ruby"++    it "can detect Ruby by filename" $+      languageName <$> languageForPath "Rakefile" `shouldBe` Just "Ruby"++    it "returns Nothing for unknown files" $+      languageName <$> languageForPath "noideawhatthisis" `shouldBe` Nothing++    it "returns Nothing for unknown extensions" $+      languageName <$> languageForPath ".noideawhatthisis" `shouldBe` Nothing++  describe "languages" $+    it "parsed languages.yml" $ do+      length languages `shouldBe` 519+      length languagesByExtension `shouldBe` 1117+      length languagesByFileName `shouldBe` 235