From 668870462d7846086d9a3ef356d44164474de2c4 Mon Sep 17 00:00:00 2001 From: Vladimir Kalnitsky Date: Wed, 8 Jul 2020 19:50:29 +0300 Subject: [PATCH 01/45] Update dependencies (#37) --- package.json | 6 +++--- packages.dhall | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index f4b7f9d..7817f70 100644 --- a/package.json +++ b/package.json @@ -44,9 +44,9 @@ "homepage": "https://github.com/spacchetti/purescript-docs-search#readme", "dependencies": {}, "devDependencies": { - "glob": "^7.1.4", - "markdown-it": "^10.0.0", + "glob": "^7.1.6", + "markdown-it": "^11.0.0", "parcel": "^1.12.4", - "spago": "^0.12.1" + "spago": "^0.15.3" } } diff --git a/packages.dhall b/packages.dhall index 29ed14f..e57d76a 100644 --- a/packages.dhall +++ b/packages.dhall @@ -2,7 +2,7 @@ let mkPackage = https://raw.githubusercontent.com/purescript/package-sets/psc-0.13.2-20190725/src/mkPackage.dhall sha256:0b197efa1d397ace6eb46b243ff2d73a3da5638d8d0ac8473e8e4a8fc528cf57 let upstream = - https://github.com/purescript/package-sets/releases/download/psc-0.13.5-20191127/packages.dhall sha256:654e8427ff1f9830542f491623cd5d89b1648774a765520554f98f41d3d1b3b3 + https://github.com/purescript/package-sets/releases/download/psc-0.13.8/packages.dhall sha256:0e95ec11604dc8afc1b129c4d405dcc17290ce56d7d0665a0ff15617e32bbf03 let overrides = { metadata = upstream.metadata // { version = "v0.13.0" } } From 71f8a7591b53bc949c33a09fbc165f7f6addd794 Mon Sep 17 00:00:00 2001 From: klntsky Date: Wed, 29 Jul 2020 20:16:59 +0300 Subject: [PATCH 02/45] Fix CLI autocompleter (now works correctly with capital letters). --- src/Docs/Search/Interactive.purs | 17 ++++++++--------- test/Main.purs | 2 +- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/Docs/Search/Interactive.purs b/src/Docs/Search/Interactive.purs index 40c62b4..549523b 100644 --- a/src/Docs/Search/Interactive.purs +++ b/src/Docs/Search/Interactive.purs @@ -5,7 +5,7 @@ import Docs.Search.Declarations (Declarations, mkDeclarations) import Docs.Search.DocsJson (DataDeclType(..)) import Docs.Search.Engine (mkEngineState, packageInfoToString, Result(..)) import Docs.Search.Engine as Engine -import Docs.Search.Extra (listToString, stringToList, (>#>)) +import Docs.Search.Extra (stringToList, (>#>)) import Docs.Search.IndexBuilder as IndexBuilder import Docs.Search.ModuleIndex (ModuleResult, mkPackedModuleIndex, unpackModuleIndex) import Docs.Search.NodeEngine (nodeEngine) @@ -22,12 +22,12 @@ import Prelude import Data.Array as Array import Data.Identity (Identity(..)) +import Data.List as List import Data.Maybe (fromMaybe) import Data.Newtype (un, unwrap, wrap) import Data.Search.Trie as Trie import Data.String (length) as String -import Data.String.Common (split, trim) as String -import Data.Tuple (fst) +import Data.String.Common (split, toLower, trim) as String import Effect (Effect) import Effect.Aff (launchAff_) import Effect.Class (liftEffect) @@ -105,14 +105,13 @@ mkCompleter -> Effect { completions :: Array String , matched :: String } mkCompleter index input = do - let path = stringToList input - let paths = + let path = stringToList $ String.toLower input + paths = Array.fromFoldable $ - listToString <$> - (fst <$> Trie.query path (unwrap index)) + (\result -> unwrap (unwrap result).name) <$> + List.concat (Trie.queryValues path (unwrap index)) - pure { completions: paths - , matched: input } + pure { completions: paths, matched: input } showResult :: Result -> String diff --git a/test/Main.purs b/test/Main.purs index 695744a..84157a1 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -3,7 +3,7 @@ module Test.Main where import Prelude import Docs.Search.TypeDecoder (Constraint(..), FunDep(..), FunDeps(..), Kind(..), QualifiedName(..), Type(..)) -import Docs.Search.Types +import Docs.Search.Types (Identifier(..)) import Test.TypeQuery as TypeQuery import Test.IndexBuilder as IndexBuilder import Test.Declarations as Declarations From 9631afa01015ad85ff174b074c7382f2cd927077 Mon Sep 17 00:00:00 2001 From: klntsky Date: Wed, 29 Jul 2020 20:17:31 +0300 Subject: [PATCH 03/45] Update changelog, bump version --- CHANGELOG.md | 20 ++++++++++++++++++++ package.json | 2 +- src/Docs/Search/Config.purs | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 305f63a..ac8cad1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.0.9 - 2020-07-29] + +New features: +- Implement sorting by package popularity for declarations. +- Add app version info to the footer. +- Scroll to document top when search bar gets focus. +- Group modules by package in the sidebar (#34) + +Bugfixes: +- Fix CLI autocompleter (now works correctly with capital letters). + +## [0.0.8 - 2020-01-18] + +Skipped due to failed deployment. + +## [0.0.7 - 2020-01-18] + +Changes: +- Consider something a builtin when there's no `sourceSpan` (#32) + ## [0.0.6 - 2019-11-29] New features: diff --git a/package.json b/package.json index 7817f70..45b5abd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "purescript-docs-search", - "version": "0.0.8", + "version": "0.0.9", "description": "Search frontend for the documentation generated by the PureScript compiler.", "directories": { "test": "test" diff --git a/src/Docs/Search/Config.purs b/src/Docs/Search/Config.purs index 4c3c6f9..caaf454 100644 --- a/src/Docs/Search/Config.purs +++ b/src/Docs/Search/Config.purs @@ -30,7 +30,7 @@ config :: , typeIndexDirectory :: String } config = - { version: "0.0.8" + { version: "0.0.9" , outputDirectory: "output" , requiredDirectories: [ "generated-docs" From 17ad192f7188a4f73c5328ad2f0b9c11a275a935 Mon Sep 17 00:00:00 2001 From: klntsky Date: Thu, 9 Jul 2020 11:11:56 +0300 Subject: [PATCH 04/45] Add app version info to the footer. --- src/Docs/Search/App.purs | 34 ++++++++++++++++++++++++++++------ src/Docs/Search/Config.purs | 6 ++++-- src/Docs/Search/Main.purs | 3 ++- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/Docs/Search/App.purs b/src/Docs/Search/App.purs index ce014e9..b796da4 100644 --- a/src/Docs/Search/App.purs +++ b/src/Docs/Search/App.purs @@ -1,17 +1,18 @@ -- | This is the main module of the client-side Halogen app. module Docs.Search.App where +import Prelude + +import Control.Coroutine as Coroutine +import Data.Maybe (Maybe(..)) +import Data.Newtype (wrap) import Docs.Search.App.SearchField as SearchField import Docs.Search.App.SearchResults as SearchResults import Docs.Search.App.Sidebar as Sidebar +import Docs.Search.Config (config) import Docs.Search.Extra (whenJust) import Docs.Search.ModuleIndex as ModuleIndex import Docs.Search.PackageIndex as PackageIndex - -import Prelude -import Control.Coroutine as Coroutine -import Data.Maybe (Maybe(..)) -import Data.Newtype (wrap) import Effect (Effect) import Effect.Aff (launchAff_) import Halogen as H @@ -26,11 +27,11 @@ import Web.DOM.ParentNode as ParentNode import Web.DOM.Text as Text import Web.Event.EventTarget (addEventListener, eventListener) import Web.HTML as HTML +import Web.HTML.Event.EventTypes (focus) import Web.HTML.Event.HashChangeEvent.EventTypes (hashchange) import Web.HTML.HTMLDocument as HTMLDocument import Web.HTML.HTMLElement (fromElement) import Web.HTML.Window as Window -import Web.HTML.Event.EventTypes (focus) main :: Effect Unit @@ -39,6 +40,7 @@ main = do doc <- HTMLDocument.toDocument <$> Window.document win insertStyle doc + insertVersionInfo doc mbContainers <- getContainers doc -- Initialize a `markdown-it` instance (we need it to render the docs as markdown) @@ -163,6 +165,26 @@ insertStyle doc = do void $ Node.appendChild (Element.toNode style) (Element.toNode head) +insertVersionInfo :: Document.Document -> Effect Unit +insertVersionInfo doc = do + let docPN = Document.toParentNode doc + mbVersionInfo <- + ParentNode.querySelector (wrap ".footer > p") docPN + whenJust (mbVersionInfo <#> Element.toNode) + \versionInfo -> do + prefix <- Document.createTextNode " - patched by " doc <#> Text.toNode + linkElement <- Document.createElement "a" doc + let linkNode = Element.toNode linkElement + Element.setAttribute "href" "https://github.com/spacchetti/purescript-docs-search" linkElement + Element.setAttribute "target" "_blank" linkElement + linkText <- Document.createTextNode ("docs-search") doc <#> Text.toNode + suffix <- Document.createTextNode (" " <> config.version) doc <#> Text.toNode + void $ Node.appendChild prefix versionInfo + void $ Node.appendChild linkNode versionInfo + void $ Node.appendChild linkText linkNode + void $ Node.appendChild suffix versionInfo + + -- | Query the DOM for specific elements that should always be present. getContainers :: Document.Document diff --git a/src/Docs/Search/Config.purs b/src/Docs/Search/Config.purs index 86b9fab..4c3c6f9 100644 --- a/src/Docs/Search/Config.purs +++ b/src/Docs/Search/Config.purs @@ -4,7 +4,8 @@ import Prelude -- | Some magic constants. config :: - { declIndexDirectory :: String + { version :: String + , declIndexDirectory :: String , mkIndexPartLoadPath :: Int -> String , mkIndexPartPath :: Int -> String , moduleIndexPath :: String @@ -29,7 +30,8 @@ config :: , typeIndexDirectory :: String } config = - { outputDirectory: "output" + { version: "0.0.8" + , outputDirectory: "output" , requiredDirectories: [ "generated-docs" , "generated-docs/html" diff --git a/src/Docs/Search/Main.purs b/src/Docs/Search/Main.purs index 3bab686..81ce10a 100644 --- a/src/Docs/Search/Main.purs +++ b/src/Docs/Search/Main.purs @@ -5,6 +5,7 @@ import Prelude import Docs.Search.IndexBuilder as IndexBuilder import Docs.Search.Interactive as Interactive +import Docs.Search.Config (config) import Data.Generic.Rep (class Generic) import Data.Generic.Rep.Show (genericShow) @@ -27,7 +28,7 @@ main = do case fromMaybe defaultCommands args of BuildIndex cfg -> IndexBuilder.run cfg Search cfg -> Interactive.run cfg - Version -> log "0.0.8" + Version -> log config.version getArgs :: Effect (Maybe Commands) From 80d005944ab558ca8f6bd85351657f20a9803aea Mon Sep 17 00:00:00 2001 From: klntsky Date: Sat, 18 Jul 2020 00:14:54 +0300 Subject: [PATCH 05/45] Implement sort by package popularity for declarations Allow searching for modules --- src/Docs/Search/App.purs | 49 +++++++++++----- src/Docs/Search/App/SearchResults.purs | 35 +++++++++-- src/Docs/Search/App/Sidebar.purs | 25 ++++++-- src/Docs/Search/BrowserEngine.purs | 2 + src/Docs/Search/Declarations.purs | 3 +- src/Docs/Search/Engine.purs | 71 ++++++++++++++++------ src/Docs/Search/IndexBuilder.purs | 9 +-- src/Docs/Search/Interactive.purs | 42 +++++++------ src/Docs/Search/ModuleIndex.purs | 81 ++++++++++++++++++++++++-- src/Docs/Search/NodeEngine.purs | 2 + src/Docs/Search/PackageIndex.purs | 57 +++++------------- src/Docs/Search/Score.purs | 47 +++++++++++++++ src/Docs/Search/TypeIndex.purs | 2 +- test/Main.purs | 2 + test/Test/ModuleIndex.purs | 24 ++++++++ 15 files changed, 337 insertions(+), 114 deletions(-) create mode 100644 src/Docs/Search/Score.purs create mode 100644 test/Test/ModuleIndex.purs diff --git a/src/Docs/Search/App.purs b/src/Docs/Search/App.purs index b796da4..7f7eb55 100644 --- a/src/Docs/Search/App.purs +++ b/src/Docs/Search/App.purs @@ -3,9 +3,12 @@ module Docs.Search.App where import Prelude +import Control.Alt (alt) import Control.Coroutine as Coroutine import Data.Maybe (Maybe(..)) import Data.Newtype (wrap) +import Data.Tuple (Tuple(..)) +import Data.Tuple.Nested ((/\)) import Docs.Search.App.SearchField as SearchField import Docs.Search.App.SearchResults as SearchResults import Docs.Search.App.Sidebar as Sidebar @@ -50,28 +53,29 @@ main = do , searchResults , pageContents , sidebarContainer - , realSidebar } -> do + , realSidebar + , isIndexHTML + } -> do -- Hide real sidebar completely - we are going to recreate it as Halogen component. ChildNode.remove $ Element.toChildNode realSidebar HA.runHalogenAff do packageIndex <- PackageIndex.loadPackageIndex - moduleIndex <- ModuleIndex.loadModuleIndex + moduleIndex <- ModuleIndex.unpackModuleIndex <$> ModuleIndex.loadModuleIndex + let scores = PackageIndex.mkScoresFromPackageIndex packageIndex - let initialSearchEngineState = { packageIndex: packageIndex - , moduleIndex: moduleIndex + let initialSearchEngineState = { packageIndex + , moduleIndex , index: mempty , typeIndex: mempty + , scores } resultsComponent = SearchResults.mkComponent initialSearchEngineState pageContents markdownIt sfio <- runUI SearchField.component unit searchField - sbio <- do - component <- Sidebar.mkComponent moduleIndex - runUI component unit sidebarContainer srio <- runUI resultsComponent unit searchResults sfio.subscribe $ @@ -91,6 +95,10 @@ main = do addEventListener hashchange listener true (Window.toEventTarget win) + sbio <- do + component <- Sidebar.mkComponent moduleIndex.packageModules isIndexHTML + runUI component unit sidebarContainer + -- Subscribe to window focus events H.liftEffect do @@ -131,11 +139,14 @@ insertStyle doc = do /* Add a margin between badge icons and package/module names. */ margin-right: 0.25em; } - .li-package { + .li-package > details > summary { font-weight: bold; cursor: pointer; color: #c4953a; } + .li-package > details > summary:hover { + color: #7b5904; + } /* Make spaces narrower in the sidebar */ .li-package > details > ul { margin-top: auto; @@ -185,7 +196,8 @@ insertVersionInfo doc = do void $ Node.appendChild suffix versionInfo --- | Query the DOM for specific elements that should always be present. +-- | Query the DOM for specific elements that should always be present and determine if we are on +-- | `index.html` or not. getContainers :: Document.Document -> Effect (Maybe { searchField :: HTML.HTMLElement @@ -193,6 +205,7 @@ getContainers , pageContents :: Element.Element , sidebarContainer :: HTML.HTMLElement , realSidebar :: Element.Element + , isIndexHTML :: Sidebar.IsIndexHTML }) getContainers doc = do let docPN = Document.toParentNode doc @@ -204,19 +217,29 @@ getContainers doc = do ParentNode.querySelector (wrap ".everything-except-footer > .container") docPN mbMainContainer <- ParentNode.querySelector (wrap ".everything-except-footer > main") docPN - mbRealSidebar <- - ParentNode.querySelector (wrap ".col--aside") docPN + mbSidebarStatus <- + alt <$> (map (Tuple Sidebar.NotIndexHTML) <$> + ParentNode.querySelector (wrap ".col--aside") docPN) + -- If there's no sidebar, that means we are currently on `index.html`. + <*> (map (Tuple Sidebar.IsIndexHTML) <$> + ParentNode.querySelector (wrap ".col--main") docPN) case unit of _ | Just banner <- mbBanner , Just everything <- mbEverything , Just pageContents <- mbContainer , Just mainContainer <- mbMainContainer - , Just realSidebar <- mbRealSidebar -> do + , Just (isIndexHTML /\ realSidebar) <- mbSidebarStatus -> do search <- Document.createElement "div" doc void $ Node.appendChild (Element.toNode search) (Element.toNode banner) pure do searchField <- fromElement search searchResults <- fromElement everything sidebarContainer <- fromElement mainContainer - pure { searchField, searchResults, pageContents, realSidebar, sidebarContainer } + pure { searchField + , searchResults + , pageContents + , realSidebar + , sidebarContainer + , isIndexHTML + } | otherwise -> pure Nothing diff --git a/src/Docs/Search/App/SearchResults.purs b/src/Docs/Search/App/SearchResults.purs index 11c6d45..7025238 100644 --- a/src/Docs/Search/App/SearchResults.purs +++ b/src/Docs/Search/App/SearchResults.purs @@ -6,10 +6,11 @@ import Docs.Search.BrowserEngine (PartialIndex, browserSearchEngine) import Docs.Search.Config (config) import Docs.Search.Declarations (DeclLevel(..), declLevelToHashAnchor) import Docs.Search.DocsJson (DataDeclType(..)) -import Docs.Search.Extra (homePageFromRepository, (>#>)) -import Docs.Search.PackageIndex (PackageResult) import Docs.Search.Engine (Result(..)) import Docs.Search.Engine as Engine +import Docs.Search.Extra (homePageFromRepository, (>#>)) +import Docs.Search.ModuleIndex (ModuleResult) +import Docs.Search.PackageIndex (PackageResult) import Docs.Search.SearchResult (ResultInfo(..), SearchResult(..)) import Docs.Search.TypeDecoder (Constraint(..), FunDep(..), FunDeps(..), Kind(..), QualifiedName(..), Type(..), TypeArgument(..), joinForAlls, joinRows) import Docs.Search.TypeIndex (TypeIndex) @@ -230,9 +231,10 @@ renderResult . MD.MarkdownIt -> Result -> Array (HH.HTML a Action) -renderResult markdownIt (DeclResult sr) = renderSearchResult markdownIt sr -renderResult markdownIt (TypeResult sr) = renderSearchResult markdownIt sr -renderResult markdownIt (PackResult sr) = renderPackageResult sr +renderResult markdownIt (DeclResult r) = renderSearchResult markdownIt r +renderResult markdownIt (TypeResult r) = renderSearchResult markdownIt r +renderResult markdownIt (PackResult r) = renderPackageResult r +renderResult markdownIt (MdlResult r) = renderModuleResult r renderPackageResult @@ -263,6 +265,29 @@ renderPackageResult { name, description, repository } = ] +renderModuleResult + :: forall a + . ModuleResult + -> Array (HH.HTML a Action) +renderModuleResult { name, package } = + [ HH.div [ HP.class_ (wrap "result") ] + [ HH.h3 [ HP.class_ (wrap "result__title") ] + [ HH.span [ HP.classes [ wrap "result__badge" + , wrap "badge" + , wrap "badge--module" ] + , HP.title "Module" + ] + [ HH.text "M" ] + + , HH.a [ HP.class_ (wrap "result__link") + , HP.href $ name <> ".html" + ] + [ HH.text name ] + ] + ] + ] + + renderSearchResult :: forall a . MD.MarkdownIt diff --git a/src/Docs/Search/App/Sidebar.purs b/src/Docs/Search/App/Sidebar.purs index c69b510..58cec82 100644 --- a/src/Docs/Search/App/Sidebar.purs +++ b/src/Docs/Search/App/Sidebar.purs @@ -1,7 +1,7 @@ module Docs.Search.App.Sidebar where import Docs.Search.Config (config) -import Docs.Search.ModuleIndex (ModuleIndex) +import Docs.Search.ModuleIndex (PackedModuleIndex) import Docs.Search.Types (ModuleName, PackageName) import Prelude @@ -36,24 +36,32 @@ data GroupingMode = GroupByPackage | DontGroup derive instance groupingModeEq :: Eq GroupingMode +-- | Whether current page is `index.html` or not. `index.html` is special, it +-- | has no sidebar, hence the difference must be taken into account. +data IsIndexHTML = IsIndexHTML | NotIndexHTML + +derive instance isIndexHTMLEq :: Eq IsIndexHTML type State = { moduleIndex :: Map PackageName (Set ModuleName) , groupingMode :: GroupingMode , moduleNames :: Array ModuleName + , isIndexHTML :: IsIndexHTML } mkComponent :: forall i - . ModuleIndex + . PackedModuleIndex + -> IsIndexHTML -> Aff (H.Component HH.HTML Query i Action Aff) -mkComponent moduleIndex = do +mkComponent moduleIndex isIndexHTML = do groupingMode <- H.liftEffect loadGroupingModeFromLocalStorage pure $ H.mkComponent { initialState: const { moduleIndex , groupingMode , moduleNames + , isIndexHTML } , render , eval: H.mkEval $ H.defaultEval { handleAction = handleAction @@ -96,9 +104,14 @@ render :: forall m . State -> H.ComponentHTML Action () m -render { moduleIndex, groupingMode, moduleNames } = - - HH.div [ HP.classes [ wrap "col", wrap "col--aside" ] ] +render { moduleIndex, groupingMode, moduleNames, isIndexHTML } = + + HH.div [ HP.classes [ wrap "col" + , wrap $ if isIndexHTML == IsIndexHTML + then "col--main" + else "col--aside" + ] + ] [ HH.h3_ [ HH.text "Modules" ] , HH.input [ HP.id_ "group-modules__input" diff --git a/src/Docs/Search/BrowserEngine.purs b/src/Docs/Search/BrowserEngine.purs index a79fc64..8063e9b 100644 --- a/src/Docs/Search/BrowserEngine.purs +++ b/src/Docs/Search/BrowserEngine.purs @@ -7,6 +7,7 @@ import Docs.Search.Engine (Engine, EngineState, Index) import Docs.Search.SearchResult (SearchResult) import Docs.Search.TypeIndex (TypeIndex) import Docs.Search.TypeIndex as TypeIndex +import Docs.Search.ModuleIndex as ModuleIndex import Prelude @@ -112,6 +113,7 @@ browserSearchEngine = { queryIndex: query , queryTypeIndex: TypeIndex.query , queryPackageIndex + , queryModuleIndex: ModuleIndex.queryModuleIndex } diff --git a/src/Docs/Search/Declarations.purs b/src/Docs/Search/Declarations.purs index de17249..ab2bb4c 100644 --- a/src/Docs/Search/Declarations.purs +++ b/src/Docs/Search/Declarations.purs @@ -1,12 +1,13 @@ module Docs.Search.Declarations where import Docs.Search.DocsJson (ChildDeclType(..), ChildDeclaration(..), DeclType(..), Declaration(..), DocsJson(..), SourceSpan) -import Docs.Search.PackageIndex (Scores) +import Docs.Search.Score (Scores) import Docs.Search.SearchResult (ResultInfo(..), SearchResult(..)) import Docs.Search.TypeDecoder (Constraint(..), QualifiedName(..), Type(..), Kind, joinForAlls) import Docs.Search.Types (ModuleName, PackageName) import Prelude + import Control.Alt ((<|>)) import Data.Array ((!!)) import Data.Array as Array diff --git a/src/Docs/Search/Engine.purs b/src/Docs/Search/Engine.purs index 5e0b376..db2f6a3 100644 --- a/src/Docs/Search/Engine.purs +++ b/src/Docs/Search/Engine.purs @@ -1,19 +1,22 @@ module Docs.Search.Engine where +import Docs.Search.ModuleIndex (ModuleIndex, ModuleResult) import Docs.Search.PackageIndex (PackageIndex, PackageResult) -import Docs.Search.ModuleIndex (ModuleIndex) +import Docs.Search.Score (Scores) import Docs.Search.SearchResult (SearchResult, typeOfResult) import Docs.Search.TypeQuery (TypeQuery(..), parseTypeQuery, penalty) +import Docs.Search.Types (PackageName, ModuleName) import Prelude -import Data.Newtype (unwrap) import Data.Array as Array import Data.Either (hush) +import Data.Function (on) +import Data.List (List) import Data.Maybe (Maybe(..)) +import Data.Newtype (unwrap) import Data.Search.Trie (Trie) import Data.String.Common (toLower) as String -import Data.List (List) type Index = Trie Char (List SearchResult) @@ -30,6 +33,8 @@ type Engine m index typeIndex = :: Query m typeIndex TypeQuery SearchResult , queryPackageIndex :: Query m PackageIndex String PackageResult + , queryModuleIndex + :: Scores -> ModuleIndex -> String -> Array ModuleResult } @@ -38,6 +43,7 @@ type EngineState index typeIndex , typeIndex :: typeIndex , packageIndex :: PackageIndex , moduleIndex :: ModuleIndex + , scores :: Scores } @@ -47,27 +53,51 @@ mkEngineState -> typeIndex -> PackageIndex -> ModuleIndex + -> Scores -> EngineState index typeIndex -mkEngineState index typeIndex packageIndex moduleIndex = - { index, typeIndex, packageIndex, moduleIndex } +mkEngineState index typeIndex packageIndex moduleIndex scores = + { index, typeIndex, packageIndex, moduleIndex, scores } data Result = DeclResult SearchResult | TypeResult SearchResult | PackResult PackageResult + | MdlResult ModuleResult + + +getResultScore :: Result -> Int +getResultScore (DeclResult r) = (unwrap r).score +getResultScore (TypeResult r) = (unwrap r).score +getResultScore (PackResult r) = r.score +getResultScore (MdlResult r) = r.score -sortByPopularity :: Array Result -> Array Result -sortByPopularity = - Array.sortWith - \result -> - -- Sort by popularity, show packages before - -- ordinary definitions. - - case result of - DeclResult r -> 2 * (unwrap r).score - TypeResult r -> 2 * (unwrap r).score - PackResult r -> 2 * r.score + 1 +getResultPackageName :: Result -> PackageName +getResultPackageName (DeclResult r) = (unwrap r).packageName +getResultPackageName (TypeResult r) = (unwrap r).packageName +getResultPackageName (PackResult r) = r.name +getResultPackageName (MdlResult r) = r.package + + +getResultModuleName :: Result -> ModuleName +getResultModuleName (DeclResult r) = (unwrap r).moduleName +getResultModuleName (TypeResult r) = (unwrap r).moduleName +getResultModuleName (PackResult r) = "" +getResultModuleName (MdlResult r) = r.name + + +sortByPopularity + :: forall index typeIndex + . EngineState index typeIndex + -> Array Result + -> Array Result +sortByPopularity { packageIndex } = + Array.sortBy ( + compare `on` (getResultScore >>> negate) <> + compare `on` getResultPackageName <> + compare `on` getResultModuleName + ) query @@ -80,13 +110,16 @@ query engine state input = -- A declaration/package query Nothing -> do - let lower = String.toLower input + let lowerCased = String.toLower input - response <- engine.queryIndex state.index lower - packageResponse <- engine.queryPackageIndex state.packageIndex lower + response <- engine.queryIndex state.index lowerCased + packageResponse <- engine.queryPackageIndex state.packageIndex lowerCased + let moduleResponse = + engine.queryModuleIndex state.scores state.moduleIndex lowerCased - pure { results: sortByPopularity $ + pure { results: sortByPopularity state $ (packageResponse.results <#> PackResult) <> + (moduleResponse <#> MdlResult) <> (response.results <#> DeclResult) -- No need to update package index (it never changes). diff --git a/src/Docs/Search/IndexBuilder.purs b/src/Docs/Search/IndexBuilder.purs index 0200d02..eee80d3 100644 --- a/src/Docs/Search/IndexBuilder.purs +++ b/src/Docs/Search/IndexBuilder.purs @@ -5,8 +5,9 @@ import Docs.Search.Config (config) import Docs.Search.Declarations (Declarations(..), mkDeclarations) import Docs.Search.DocsJson (DocsJson) import Docs.Search.Extra ((>#>)) -import Docs.Search.ModuleIndex (ModuleIndex, mkModuleIndex) -import Docs.Search.PackageIndex (PackageInfo, mkPackageInfo, mkScores) +import Docs.Search.ModuleIndex (PackedModuleIndex, mkPackedModuleIndex) +import Docs.Search.PackageIndex (PackageInfo, mkPackageInfo) +import Docs.Search.Score (mkScores) import Docs.Search.SearchResult (SearchResult) import Docs.Search.TypeIndex (TypeIndex, mkTypeIndex) @@ -78,7 +79,7 @@ run' cfg = do index = mkDeclarations scores docsJsons typeIndex = mkTypeIndex scores docsJsons packageInfo = mkPackageInfo packageMetas - moduleIndex = mkModuleIndex index + moduleIndex = mkPackedModuleIndex index createDirectories cfg @@ -223,7 +224,7 @@ writePackageInfo packageInfo = do header = "window.DocsSearchPackageIndex = " -writeModuleIndex :: ModuleIndex -> Aff Unit +writeModuleIndex :: PackedModuleIndex -> Aff Unit writeModuleIndex moduleIndex = do writeTextFile UTF8 config.moduleIndexPath $ header <> stringify (encodeJson moduleIndex) diff --git a/src/Docs/Search/Interactive.purs b/src/Docs/Search/Interactive.purs index e3fd65e..fd3560d 100644 --- a/src/Docs/Search/Interactive.purs +++ b/src/Docs/Search/Interactive.purs @@ -1,36 +1,36 @@ -- | Definitions for the "search REPL". module Docs.Search.Interactive where +import Prelude + +import Data.Array as Array +import Data.Identity (Identity(..)) +import Data.Maybe (fromMaybe) +import Data.Newtype (un, unwrap, wrap) +import Data.Search.Trie as Trie +import Data.String (length) as String +import Data.String.Common (split, trim) as String +import Data.Tuple (fst) import Docs.Search.Declarations (Declarations, mkDeclarations) -import Docs.Search.ModuleIndex (mkModuleIndex) import Docs.Search.DocsJson (DataDeclType(..)) import Docs.Search.Engine (mkEngineState, Result(..)) import Docs.Search.Engine as Engine import Docs.Search.Extra (listToString, stringToList, (>#>)) import Docs.Search.IndexBuilder as IndexBuilder +import Docs.Search.ModuleIndex (ModuleResult, mkPackedModuleIndex, unpackModuleIndex) import Docs.Search.NodeEngine (nodeEngine) -import Docs.Search.PackageIndex (PackageResult, mkPackageIndex, mkPackageInfo, mkScores) +import Docs.Search.PackageIndex (PackageResult, mkPackageIndex, mkPackageInfo) +import Docs.Search.Score (mkScores) import Docs.Search.SearchResult (ResultInfo(..), SearchResult(..)) import Docs.Search.Terminal (bold, cyan, green, yellow) import Docs.Search.TypeDecoder (Constraint, FunDeps, Kind, QualifiedName, Type, TypeArgument) import Docs.Search.TypeIndex (resultsWithTypes) import Docs.Search.TypePrinter (keyword, showConstraint, showFunDeps, showKind, showType, showTypeArgument, space, syntax) - -import Prelude - -import Data.Identity (Identity(..)) -import Data.Array as Array -import Data.Maybe (fromMaybe) -import Data.Search.Trie as Trie -import Data.String (length) as String -import Data.String.Common (split, trim) as String -import Data.Tuple (fst) import Effect (Effect) import Effect.Aff (launchAff_) import Effect.Class (liftEffect) import Effect.Console (log) import Node.ReadLine (createConsoleInterface, question) -import Data.Newtype (un, unwrap, wrap) type Config = @@ -52,8 +52,8 @@ run cfg = launchAff_ $ do index = mkDeclarations scores docsJsons typeIndex = docsJsons >>= resultsWithTypes scores packageIndex = mkPackageIndex $ mkPackageInfo packageMetas - moduleIndex = mkModuleIndex index - engineState = mkEngineState (unwrap index) typeIndex packageIndex moduleIndex + moduleIndex = unpackModuleIndex $ mkPackedModuleIndex index + engineState = mkEngineState (unwrap index) typeIndex packageIndex moduleIndex scores let countOfDefinitions = Trie.size $ unwrap index countOfTypeDefinitions = Array.length typeIndex @@ -115,9 +115,10 @@ mkCompleter index input = do showResult :: Result -> String showResult = case _ of - DeclResult sr -> showSearchResult sr - TypeResult sr -> showSearchResult sr - PackResult pr -> showPackageResult pr + DeclResult r -> showSearchResult r + TypeResult r -> showSearchResult r + PackResult r -> showPackageResult r + MdlResult r -> showModuleResult r showSearchResult :: SearchResult -> String @@ -138,6 +139,11 @@ showPackageResult { name, description } = (description >#> \text -> "\n\n" <> leftShift 3 text <> "\n") +showModuleResult :: ModuleResult -> String +showModuleResult { name, package } = + bold (cyan "module") <> " " <> bold (green name) + + showSignature :: forall rest. { name :: String diff --git a/src/Docs/Search/ModuleIndex.purs b/src/Docs/Search/ModuleIndex.purs index bf61eea..efa5ee8 100644 --- a/src/Docs/Search/ModuleIndex.purs +++ b/src/Docs/Search/ModuleIndex.purs @@ -4,30 +4,92 @@ import Docs.Search.Config (config) import Docs.Search.Declarations (Declarations(..)) import Docs.Search.SearchResult (SearchResult(..)) import Docs.Search.Types (ModuleName, PackageName) +import Docs.Search.Extra (stringToList) +import Docs.Search.Score (Scores, normalizePackageName) import Prelude + +import Control.Monad.State (execState, modify_) import Control.Promise (Promise, toAffE) import Data.Argonaut.Core (Json) import Data.Argonaut.Decode (decodeJson) +import Data.Array as Array import Data.Either (hush) -import Data.Foldable (foldr) -import Data.List (List) +import Data.Foldable (foldl) +import Data.Lens ((%~)) +import Data.Lens.Record (prop) +import Data.List (List, (:)) import Data.Map (Map) import Data.Map as Map import Data.Maybe (fromMaybe) +import Data.Search.Trie (Trie) import Data.Search.Trie as Trie import Data.Set (Set) import Data.Set as Set +import Data.String.CodeUnits (toCharArray) as String +import Data.String.Common (split, toLower) as String +import Data.String.Pattern (Pattern(..)) +import Data.Symbol (SProxy(..)) +import Data.Traversable (foldr, for_) +import Data.Tuple.Nested ((/\)) import Effect (Effect) import Effect.Aff (Aff) -type ModuleIndex = Map PackageName (Set ModuleName) +-- | Module index that is actually stored in a JS file. +type PackedModuleIndex = Map PackageName (Set ModuleName) + +-- | "Expanded" module index that can be queried quickly. +type ModuleIndex = { packageModules :: Map PackageName (Set ModuleName) + , modulePackages :: Map ModuleName PackageName + , index :: Trie Char ModuleName + } + + +type ModuleResult + = { name :: ModuleName + , package :: PackageName + , score :: Int + } + + +unpackModuleIndex :: PackedModuleIndex -> ModuleIndex +unpackModuleIndex packageModules = + flip execState { packageModules, modulePackages: mempty, index: mempty } do + for_ (Map.toUnfoldableUnordered packageModules :: Array _) + \(packageName /\ moduleNames) -> do + for_ moduleNames \moduleName -> do + modify_ $ _modulePackages %~ Map.insert moduleName packageName + for_ (extractModuleNameParts moduleName) \part -> do + let partPath = Array.toUnfoldable $ String.toCharArray part + modify_ $ _index %~ Trie.insert partPath moduleName + + +-- | E.g. `"Data.Array.ST" -> ["data.array.st", "array.st", "st"]`. +extractModuleNameParts :: ModuleName -> List String +extractModuleNameParts = + foldl (\acc el -> el : map (_ <> "." <> el) acc) mempty <<< String.split (Pattern ".") <<< String.toLower + + +queryModuleIndex + :: Scores + -> ModuleIndex + -> String + -> Array ModuleResult +queryModuleIndex scores { index, modulePackages } query = + let path = stringToList $ String.toLower query in + Trie.queryValues path index # + Array.fromFoldable # + Array.nub <#> + (\name -> do + package <- Map.lookup name modulePackages + pure { name, package, score: fromMaybe 0 $ Map.lookup (normalizePackageName package) scores }) # + Array.catMaybes -- | Constructs a mapping from packages to modules -mkModuleIndex :: Declarations -> ModuleIndex -mkModuleIndex (Declarations trie) = +mkPackedModuleIndex :: Declarations -> PackedModuleIndex +mkPackedModuleIndex (Declarations trie) = foldr (Map.unionWith Set.union) mempty $ extract <$> Trie.values trie where extract @@ -39,7 +101,7 @@ mkModuleIndex (Declarations trie) = Map.singleton packageName (Set.singleton moduleName) -loadModuleIndex :: Aff ModuleIndex +loadModuleIndex :: Aff PackedModuleIndex loadModuleIndex = do json <- toAffE $ load config.moduleIndexLoadPath pure $ fromMaybe mempty $ hush $ decodeJson json @@ -48,3 +110,10 @@ loadModuleIndex = do foreign import load :: String -> Effect (Promise Json) + + +_modulePackages :: forall a b rest. (a -> b) -> { modulePackages :: a | rest } -> { modulePackages :: b | rest } +_modulePackages = prop (SProxy :: SProxy "modulePackages") + +_index :: forall a b rest. (a -> b) -> { index :: a | rest } -> { index :: b | rest } +_index = prop (SProxy :: SProxy "index") diff --git a/src/Docs/Search/NodeEngine.purs b/src/Docs/Search/NodeEngine.purs index 0c79f6a..5345c60 100644 --- a/src/Docs/Search/NodeEngine.purs +++ b/src/Docs/Search/NodeEngine.purs @@ -3,6 +3,7 @@ module Docs.Search.NodeEngine where import Docs.Search.Extra (stringToList) import Docs.Search.PackageIndex as PackageIndex +import Docs.Search.ModuleIndex as ModuleIndex import Docs.Search.Engine (Engine, Query, Index, sortByDistance) import Docs.Search.SearchResult (SearchResult) import Docs.Search.TypeQuery (TypeQuery) @@ -24,6 +25,7 @@ nodeEngine = { queryIndex , queryTypeIndex , queryPackageIndex: PackageIndex.queryPackageIndex + , queryModuleIndex: ModuleIndex.queryModuleIndex } diff --git a/src/Docs/Search/PackageIndex.purs b/src/Docs/Search/PackageIndex.purs index 1db1fbc..34674a9 100644 --- a/src/Docs/Search/PackageIndex.purs +++ b/src/Docs/Search/PackageIndex.purs @@ -2,24 +2,24 @@ module Docs.Search.PackageIndex where import Docs.Search.Config (config) import Docs.Search.Extra (stringToList) +import Docs.Search.Score (Scores, getPackageScore, mkScores, normalizePackageName) import Prelude -import Data.Either (hush) -import Data.Maybe (Maybe, fromMaybe) -import Data.Newtype (unwrap, wrap) -import Effect (Effect) -import Effect.Aff (Aff) -import Web.Bower.PackageMeta (Dependencies, PackageMeta(..)) import Control.Promise (Promise, toAffE) import Data.Argonaut.Core (Json) import Data.Argonaut.Decode (decodeJson) import Data.Array as Array +import Data.Either (hush) import Data.Map (Map) import Data.Map as Map +import Data.Maybe (Maybe, fromMaybe) +import Data.Newtype (unwrap) import Data.Search.Trie (Trie) import Data.Search.Trie as Trie -import Data.String.CodeUnits as String +import Effect (Effect) +import Effect.Aff (Aff) +import Web.Bower.PackageMeta (PackageMeta(..)) type PackageResult @@ -30,33 +30,11 @@ type PackageResult , repository :: Maybe String } -type Scores = Map String Int - type PackageIndex = Trie Char PackageResult type PackageInfo = Array PackageResult --- | Construct a mapping from package names to their scores, based on number --- of reverse dependencies. -mkScores :: Array PackageMeta -> Scores -mkScores = - Array.foldr - (\pm -> - updateScoresFor (unwrap pm).dependencies >>> - updateScoresFor (unwrap pm).devDependencies - ) - mempty - - where - updateScoresFor :: Dependencies -> Scores -> Scores - updateScoresFor deps scores = - Array.foldr - (\dep -> Map.insertWith add dep 1) - scores - (deps # unwrap >>> map (_.packageName)) - - mkPackageInfo :: Array PackageMeta -> PackageInfo mkPackageInfo pms = Array.fromFoldable $ @@ -66,7 +44,6 @@ mkPackageInfo pms = where packageScores = mkScores pms - insert :: PackageMeta -> Map String PackageResult @@ -82,12 +59,17 @@ mkPackageInfo pms = name { name , description: description - , score: fromMaybe 0 $ Map.lookup name packageScores + , score: getPackageScore packageScores name , dependencies: unwrap dependencies <#> (_.packageName) , repository: repository <#> (_.url) } +mkScoresFromPackageIndex :: PackageIndex -> Scores +mkScoresFromPackageIndex = + Trie.values >>> Array.foldr (\ { name, score } -> Map.insert name score) mempty + + loadPackageIndex :: Aff PackageIndex loadPackageIndex = do json <- toAffE (load config.packageInfoLoadPath) @@ -97,17 +79,10 @@ loadPackageIndex = do mkPackageIndex :: PackageInfo -> PackageIndex mkPackageIndex = - Array.foldr - (\package -> Trie.insert (shortNamePath package.name) package) + (\package -> Trie.insert (stringToList $ normalizePackageName package.name) package) mempty - where - shortNamePath name = - stringToList $ - fromMaybe name $ - String.stripPrefix (wrap "purescript-") name - queryPackageIndex :: forall m @@ -117,9 +92,9 @@ queryPackageIndex -> m { index :: PackageIndex , results :: Array PackageResult } -queryPackageIndex index q = +queryPackageIndex index query = pure { index - , results: Array.fromFoldable $ Trie.queryValues (stringToList q) index + , results: Array.fromFoldable $ Trie.queryValues (stringToList query) index } diff --git a/src/Docs/Search/Score.purs b/src/Docs/Search/Score.purs new file mode 100644 index 0000000..7304470 --- /dev/null +++ b/src/Docs/Search/Score.purs @@ -0,0 +1,47 @@ +module Docs.Search.Score where + +import Docs.Search.Types (PackageName) + +import Prelude + +import Data.Array as Array +import Data.Map (Map) +import Data.Map as Map +import Data.Maybe (fromMaybe) +import Data.Newtype (unwrap, wrap) +import Data.String.CodeUnits as String +import Web.Bower.PackageMeta (Dependencies, PackageMeta) + + +type Scores = Map PackageName Int + + +getPackageScore :: Scores -> PackageName -> Int +getPackageScore _ "" = 1000000 +getPackageScore _ "" = 2000000 +getPackageScore scores name = fromMaybe 0 $ Map.lookup name scores + + +normalizePackageName :: PackageName -> PackageName +normalizePackageName packageName = + fromMaybe packageName $ String.stripPrefix (wrap "purescript-") packageName + + +-- | Construct a mapping from package names to their scores, based on number +-- of reverse dependencies. +mkScores :: Array PackageMeta -> Scores +mkScores = + Array.foldr + (\pm -> + updateScoresFor (unwrap pm).dependencies >>> + updateScoresFor (unwrap pm).devDependencies + ) + mempty + + where + updateScoresFor :: Dependencies -> Scores -> Scores + updateScoresFor deps scores = + Array.foldr + (\dep -> Map.insertWith add dep 1) + scores + (deps # unwrap >>> map (_.packageName >>> normalizePackageName)) diff --git a/src/Docs/Search/TypeIndex.purs b/src/Docs/Search/TypeIndex.purs index 4aede14..ae8e5a5 100644 --- a/src/Docs/Search/TypeIndex.purs +++ b/src/Docs/Search/TypeIndex.purs @@ -4,7 +4,7 @@ module Docs.Search.TypeIndex where import Docs.Search.Config (config) import Docs.Search.Declarations (resultsForDeclaration) import Docs.Search.DocsJson (DocsJson(..)) -import Docs.Search.PackageIndex (Scores) +import Docs.Search.Score (Scores) import Docs.Search.SearchResult (ResultInfo(..), SearchResult(..)) import Docs.Search.TypeDecoder (Type) import Docs.Search.TypeQuery (TypeQuery) diff --git a/test/Main.purs b/test/Main.purs index ac2b506..f7c435c 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -6,6 +6,7 @@ import Docs.Search.TypeDecoder (Constraint(..), FunDep(..), FunDeps(..), Kind(.. import Test.TypeQuery as TypeQuery import Test.IndexBuilder as IndexBuilder import Test.Declarations as Declarations +import Test.ModuleIndex as ModuleIndex import Test.Extra (assertRight) @@ -28,6 +29,7 @@ mainTest = do TypeQuery.tests IndexBuilder.tests Declarations.tests + ModuleIndex.tests let mkJson x = unsafePartial $ fromRight $ jsonParser x suite "FunDeps decoder" do diff --git a/test/Test/ModuleIndex.purs b/test/Test/ModuleIndex.purs new file mode 100644 index 0000000..c6a4e6e --- /dev/null +++ b/test/Test/ModuleIndex.purs @@ -0,0 +1,24 @@ +module Test.ModuleIndex where + +import Docs.Search.ModuleIndex (extractModuleNameParts) + +import Prelude + +import Data.List as List +import Test.Unit (TestSuite, suite, test) +import Test.Unit.Assert as Assert + + +tests :: TestSuite +tests = do + suite "ModuleIndex" do + + test "test #0" do + Assert.equal (extractModuleNameParts "Data.Array.ST") ( + List.fromFoldable [ "st", "array.st", "data.array.st" ] + ) + + test "test #1" do + Assert.equal (extractModuleNameParts "Foo") ( + List.fromFoldable [ "foo" ] + ) From 557bb712c50fd87e6fffda43e68b0c27ffcb011f Mon Sep 17 00:00:00 2001 From: klntsky Date: Fri, 24 Jul 2020 17:47:53 +0300 Subject: [PATCH 06/45] Remove unnecessary FFI --- src/Docs/Search/Interactive.js | 5 ----- src/Docs/Search/Interactive.purs | 21 +++++++++------------ 2 files changed, 9 insertions(+), 17 deletions(-) delete mode 100644 src/Docs/Search/Interactive.js diff --git a/src/Docs/Search/Interactive.js b/src/Docs/Search/Interactive.js deleted file mode 100644 index 7dc4807..0000000 --- a/src/Docs/Search/Interactive.js +++ /dev/null @@ -1,5 +0,0 @@ -/* global exports */ - -exports.consoleClear = function () { - console.clear(); -}; diff --git a/src/Docs/Search/Interactive.purs b/src/Docs/Search/Interactive.purs index fd3560d..0fd2d04 100644 --- a/src/Docs/Search/Interactive.purs +++ b/src/Docs/Search/Interactive.purs @@ -29,7 +29,7 @@ import Docs.Search.TypePrinter (keyword, showConstraint, showFunDeps, showKind, import Effect (Effect) import Effect.Aff (launchAff_) import Effect.Class (liftEffect) -import Effect.Console (log) +import Effect.Console (log, clear) as Console import Node.ReadLine (createConsoleInterface, question) @@ -43,7 +43,7 @@ run :: Config -> Effect Unit run cfg = launchAff_ $ do liftEffect do - log "Loading search index..." + Console.log "Loading search index..." docsJsons <- IndexBuilder.decodeDocsJsons cfg packageMetas <- IndexBuilder.decodeBowerJsons cfg @@ -60,7 +60,7 @@ run cfg = launchAff_ $ do countOfPackages = Array.length packageMetas liftEffect do - log $ + Console.log $ "Loaded " <> show countOfDefinitions <> " definitions and " <> @@ -81,15 +81,15 @@ run cfg = launchAff_ $ do let total = Array.length results - consoleClear + Console.clear - if total > 0 then do - log $ + Console.log $ + if total > 0 then do Array.intercalate "\n\n\n" $ - showResult <$> Array.reverse results - else do - log $ + showResult <$> Array.reverse results + else "Your search for " <> bold input <> " did not yield any results." + call inputHandler interface interface <- createConsoleInterface (mkCompleter index) @@ -296,6 +296,3 @@ leftPad w str = Array.fold (Array.replicate w " ") <> str rightPad :: Int -> String -> String rightPad w str = str <> Array.fold (Array.replicate (w - String.length str) " ") - - -foreign import consoleClear :: Effect Unit From 2effbda58bc8430c5cf6488fe04ce574d829b458 Mon Sep 17 00:00:00 2001 From: klntsky Date: Sat, 25 Jul 2020 13:53:39 +0300 Subject: [PATCH 07/45] Scroll to document top on window focus; Minor refactoring. --- src/Docs/Search/App/SearchField.purs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/Docs/Search/App/SearchField.purs b/src/Docs/Search/App/SearchField.purs index f3f64c8..10631c5 100644 --- a/src/Docs/Search/App/SearchField.purs +++ b/src/Docs/Search/App/SearchField.purs @@ -18,12 +18,10 @@ import Halogen.HTML.Properties as HP import Halogen.Query.EventSource as ES import Web.DOM.Document as Document import Web.DOM.ParentNode as ParentNode -import Web.HTML (window) as Web import Web.HTML as HTML import Web.HTML.HTMLDocument as HTMLDocument -import Web.HTML.HTMLElement (blur, focus) as Web +import Web.HTML.HTMLElement as HTMLElement import Web.HTML.HTMLInputElement as HTMLInputElement -import Web.HTML.Window (document) as Web import Web.HTML.Window as Window import Web.UIEvent.KeyboardEvent (KeyboardEvent) import Web.UIEvent.KeyboardEvent as KE @@ -78,7 +76,7 @@ handleAction = case _ of InitKeyboardListener -> do - document <- H.liftEffect $ Web.document =<< Web.window + document <- H.liftEffect $ Window.document =<< HTML.window H.subscribe' \sid -> ES.eventListenerEventSource KET.keyup @@ -92,14 +90,14 @@ handleAction = case _ of when (not state.focused) do H.liftEffect do withSearchField HTMLInputElement.select - withSearchField (HTMLInputElement.toHTMLElement >>> Web.focus) + withSearchField (HTMLInputElement.toHTMLElement >>> HTMLElement.focus) when (KE.code ev == "Escape") do state <- H.get if state.focused then do H.liftEffect do - withSearchField (HTMLInputElement.toHTMLElement >>> Web.blur) + withSearchField (HTMLInputElement.toHTMLElement >>> HTMLElement.blur) else clearInput InputAction input -> do @@ -108,16 +106,22 @@ handleAction = case _ of EnterPressed -> do state <- H.get H.liftEffect do - withSearchField (HTMLInputElement.toHTMLElement >>> Web.blur) + withSearchField (HTMLInputElement.toHTMLElement >>> HTMLElement.blur) H.liftEffect (URIHash.setInput state.input) H.raise $ InputUpdated state.input - FocusChanged status -> do - H.modify_ (_ { focused = status }) + FocusChanged isFocused -> do + H.modify_ (_ { focused = isFocused }) H.raise - if status + if isFocused then Focused else LostFocus + when isFocused scrollToTop + +scrollToTop :: H.HalogenM State Action () SearchFieldMessage Aff Unit +scrollToTop = do + H.liftEffect do + HTML.window >>= Window.scroll 0 0 clearInput :: H.HalogenM State Action () SearchFieldMessage Aff Unit clearInput = do From 26bc704b55f074e78c79d40bae005e4f4281aad0 Mon Sep 17 00:00:00 2001 From: klntsky Date: Sun, 26 Jul 2020 00:28:02 +0300 Subject: [PATCH 08/45] Refactor sorting by package popularity --- src/Docs/Search/IndexBuilder.purs | 2 +- src/Docs/Search/Interactive.purs | 2 +- src/Docs/Search/PackageIndex.purs | 10 ++++------ 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Docs/Search/IndexBuilder.purs b/src/Docs/Search/IndexBuilder.purs index eee80d3..07f2835 100644 --- a/src/Docs/Search/IndexBuilder.purs +++ b/src/Docs/Search/IndexBuilder.purs @@ -78,7 +78,7 @@ run' cfg = do let scores = mkScores packageMetas index = mkDeclarations scores docsJsons typeIndex = mkTypeIndex scores docsJsons - packageInfo = mkPackageInfo packageMetas + packageInfo = mkPackageInfo scores packageMetas moduleIndex = mkPackedModuleIndex index createDirectories cfg diff --git a/src/Docs/Search/Interactive.purs b/src/Docs/Search/Interactive.purs index 0fd2d04..6433543 100644 --- a/src/Docs/Search/Interactive.purs +++ b/src/Docs/Search/Interactive.purs @@ -51,7 +51,7 @@ run cfg = launchAff_ $ do let scores = mkScores packageMetas index = mkDeclarations scores docsJsons typeIndex = docsJsons >>= resultsWithTypes scores - packageIndex = mkPackageIndex $ mkPackageInfo packageMetas + packageIndex = mkPackageIndex $ mkPackageInfo scores packageMetas moduleIndex = unpackModuleIndex $ mkPackedModuleIndex index engineState = mkEngineState (unwrap index) typeIndex packageIndex moduleIndex scores diff --git a/src/Docs/Search/PackageIndex.purs b/src/Docs/Search/PackageIndex.purs index 34674a9..09503d7 100644 --- a/src/Docs/Search/PackageIndex.purs +++ b/src/Docs/Search/PackageIndex.purs @@ -2,7 +2,7 @@ module Docs.Search.PackageIndex where import Docs.Search.Config (config) import Docs.Search.Extra (stringToList) -import Docs.Search.Score (Scores, getPackageScore, mkScores, normalizePackageName) +import Docs.Search.Score (Scores, getPackageScore, normalizePackageName) import Prelude @@ -35,15 +35,13 @@ type PackageIndex = Trie Char PackageResult type PackageInfo = Array PackageResult -mkPackageInfo :: Array PackageMeta -> PackageInfo -mkPackageInfo pms = +mkPackageInfo :: Scores -> Array PackageMeta -> PackageInfo +mkPackageInfo packageScores pms = Array.fromFoldable $ Map.values $ Array.foldr insert mempty pms where - packageScores = mkScores pms - insert :: PackageMeta -> Map String PackageResult @@ -59,7 +57,7 @@ mkPackageInfo pms = name { name , description: description - , score: getPackageScore packageScores name + , score: getPackageScore packageScores $ normalizePackageName name , dependencies: unwrap dependencies <#> (_.packageName) , repository: repository <#> (_.url) } From 16e1a4b28079a6cb2d949b88e210c3d8a63ede6d Mon Sep 17 00:00:00 2001 From: klntsky Date: Sun, 26 Jul 2020 00:47:20 +0300 Subject: [PATCH 09/45] Make results ordering stable. --- src/Docs/Search/Engine.purs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Docs/Search/Engine.purs b/src/Docs/Search/Engine.purs index db2f6a3..93b253d 100644 --- a/src/Docs/Search/Engine.purs +++ b/src/Docs/Search/Engine.purs @@ -87,6 +87,13 @@ getResultModuleName (PackResult r) = "" getResultModuleName (MdlResult r) = r.name +getResultName :: Result -> String +getResultName (DeclResult r) = (unwrap r).name +getResultName (TypeResult r) = (unwrap r).name +getResultName (PackResult r) = r.name +getResultName (MdlResult r) = r.name + + sortByPopularity :: forall index typeIndex . EngineState index typeIndex @@ -96,7 +103,10 @@ sortByPopularity { packageIndex } = Array.sortBy ( compare `on` (getResultScore >>> negate) <> compare `on` getResultPackageName <> - compare `on` getResultModuleName + compare `on` getResultModuleName <> + -- Identifier name comes last: we want to make sure no `Result`s are + -- equal, to avoid having unstable ordering. + compare `on` getResultName ) From 5bf02c20992ca23cc828282f7b533683f4c7d422 Mon Sep 17 00:00:00 2001 From: klntsky Date: Sun, 26 Jul 2020 22:30:42 +0300 Subject: [PATCH 10/45] Wrap ModuleName and PackageName strings into newtypes --- src/Docs/Search/App/SearchResults.purs | 50 +++++++-------- src/Docs/Search/App/Sidebar.purs | 13 ++-- src/Docs/Search/Declarations.purs | 84 ++++++++++++++------------ src/Docs/Search/Engine.purs | 20 +++--- src/Docs/Search/Interactive.purs | 40 ++++++------ src/Docs/Search/ModuleIndex.purs | 15 +++-- src/Docs/Search/PackageIndex.purs | 27 +++++---- src/Docs/Search/Score.purs | 29 +++++---- src/Docs/Search/SearchResult.purs | 5 +- src/Docs/Search/TypeDecoder.purs | 8 +-- src/Docs/Search/TypeIndex.purs | 4 +- src/Docs/Search/TypePrinter.purs | 4 +- src/Docs/Search/TypeQuery.purs | 6 +- src/Docs/Search/TypeShape.purs | 2 +- src/Docs/Search/Types.purs | 58 +++++++++++++++++- test/Main.purs | 32 +++++----- test/Test/Declarations.purs | 25 ++++---- test/Test/ModuleIndex.purs | 5 +- test/Test/TypeQuery.purs | 6 +- 19 files changed, 258 insertions(+), 175 deletions(-) diff --git a/src/Docs/Search/App/SearchResults.purs b/src/Docs/Search/App/SearchResults.purs index 7025238..16159e3 100644 --- a/src/Docs/Search/App/SearchResults.purs +++ b/src/Docs/Search/App/SearchResults.purs @@ -14,6 +14,7 @@ import Docs.Search.PackageIndex (PackageResult) import Docs.Search.SearchResult (ResultInfo(..), SearchResult(..)) import Docs.Search.TypeDecoder (Constraint(..), FunDep(..), FunDeps(..), Kind(..), QualifiedName(..), Type(..), TypeArgument(..), joinForAlls, joinRows) import Docs.Search.TypeIndex (TypeIndex) +import Docs.Search.Types (ModuleName(..), packageInfoToString) import Prelude @@ -21,7 +22,7 @@ import Data.Array ((!!)) import Data.Array as Array import Data.List as List import Data.Maybe (Maybe(..), isJust, fromMaybe) -import Data.Newtype (wrap) +import Data.Newtype (wrap, unwrap) import Data.String.CodeUnits (stripSuffix) as String import Data.String.Common (null, trim) as String import Data.String.Pattern (Pattern(..)) as String @@ -63,7 +64,7 @@ data Query a data Action - = SearchResultClicked String + = SearchResultClicked ModuleName | MoreResultsRequested @@ -145,7 +146,7 @@ handleAction = case _ of location <- Window.location window pathname <- Location.pathname location pure $ isJust $ - String.stripSuffix (String.Pattern $ moduleName <> ".html") pathname + String.stripSuffix (String.Pattern $ unwrap moduleName <> ".html") pathname when onThisPage do showPageContents @@ -254,7 +255,7 @@ renderPackageResult { name, description, repository } = , HH.a [ HP.class_ (wrap "result__link") , HP.href $ fromMaybe "" repository # homePageFromRepository ] - [ HH.text name ] + [ HH.text $ unwrap name ] ] ] ] <> @@ -280,9 +281,9 @@ renderModuleResult { name, package } = [ HH.text "M" ] , HH.a [ HP.class_ (wrap "result__link") - , HP.href $ name <> ".html" + , HP.href $ unwrap name <> ".html" ] - [ HH.text name ] + [ HH.text $ unwrap name ] ] ] ] @@ -300,7 +301,7 @@ renderSearchResult markdownIt (SearchResult result) = [ HH.a [ HP.class_ (wrap "result__link") , HE.onClick $ const $ Just $ SearchResultClicked result.moduleName , HP.href $ - result.moduleName <> ".html#" <> + unwrap result.moduleName <> ".html#" <> result.hashAnchor <> ":" <> result.name ] [ HH.text result.name ] @@ -321,7 +322,7 @@ renderSearchResult markdownIt (SearchResult result) = , HP.title "Package" ] [ HH.text "P" ] - , HH.text result.packageName + , HH.text $ packageInfoToString result.packageInfo ] , HH.span [ HP.class_ (wrap "result__actions__item") ] @@ -331,7 +332,7 @@ renderSearchResult markdownIt (SearchResult result) = , HP.title "Module" ] [ HH.text "M" ] - , HH.text result.moduleName + , HH.text $ unwrap result.moduleName ] ] ] @@ -341,7 +342,7 @@ renderResultType :: forall a rest . { info :: ResultInfo , name :: String - , moduleName :: String + , moduleName :: ModuleName | rest } -> Array (HH.HTML a Action) @@ -371,7 +372,7 @@ renderResultType result = renderValueSignature :: forall a rest - . { moduleName :: String + . { moduleName :: ModuleName , name :: String | rest } @@ -391,7 +392,7 @@ renderTypeClassSignature , arguments :: Array TypeArgument , superclasses :: Array Constraint } - -> { name :: String, moduleName :: String | rest } + -> { name :: String, moduleName :: ModuleName | rest } -> Array (HH.HTML a Action) renderTypeClassSignature { fundeps, arguments, superclasses } { name, moduleName } = [ keyword "class" @@ -520,14 +521,14 @@ renderType = case _ of TypeOp qname -> renderQualifiedName true TypeLevel qname TypeApp (TypeApp (TypeConstructor - (QualifiedName { moduleName: [ "Prim" ] + (QualifiedName { moduleNameParts: [ "Prim" ] , name: "Function" })) t1) t2 -> HH.span_ [ renderType t1 , syntax " -> " , renderType t2 ] - TypeApp (TypeConstructor (QualifiedName { moduleName: [ "Prim" ] + TypeApp (TypeConstructor (QualifiedName { moduleNameParts: [ "Prim" ] , name: "Record" })) row -> renderRow false row @@ -635,6 +636,9 @@ renderRow asRow = opening = if asRow then "( " else "{ " closing = if asRow then " )" else " }" + primRecord :: QualifiedName + primRecord = QualifiedName { moduleNameParts: [ "Prim" ], name: "Record" } + renderConstraint :: forall a @@ -652,18 +656,18 @@ renderQualifiedName -> DeclLevel -> QualifiedName -> HH.HTML a Action -renderQualifiedName isInfix level (QualifiedName { moduleName, name }) +renderQualifiedName isInfix level (QualifiedName { moduleNameParts, name }) = if isBuiltIn then HH.text name else HH.a [ HE.onClick $ const $ Just $ - SearchResultClicked moduleNameString - , makeHref level isInfix moduleNameString name + SearchResultClicked $ moduleName + , makeHref level isInfix moduleName name ] [ HH.text name ] where - moduleNameString = Array.intercalate "." moduleName - isBuiltIn = moduleName !! 0 == Just "Prim" + moduleName = ModuleName $ Array.intercalate "." $ moduleNameParts + isBuiltIn = moduleNameParts !! 0 == Just "Prim" renderKind @@ -681,20 +685,16 @@ makeHref :: forall t rest . DeclLevel -> Boolean - -> String + -> ModuleName -> String -> HH.IProp ( href :: String | rest ) t makeHref level isInfix moduleName name = HP.href $ - moduleName <> ".html#" <> + unwrap moduleName <> ".html#" <> declLevelToHashAnchor level <> ":" <> if isInfix then "type (" <> name <> ")" else name -primRecord :: QualifiedName -primRecord = QualifiedName { moduleName: [ "Prim" ], name: "Record" } - - keyword :: forall a . String diff --git a/src/Docs/Search/App/Sidebar.purs b/src/Docs/Search/App/Sidebar.purs index 58cec82..2f3b36f 100644 --- a/src/Docs/Search/App/Sidebar.purs +++ b/src/Docs/Search/App/Sidebar.purs @@ -2,9 +2,10 @@ module Docs.Search.App.Sidebar where import Docs.Search.Config (config) import Docs.Search.ModuleIndex (PackedModuleIndex) -import Docs.Search.Types (ModuleName, PackageName) +import Docs.Search.Types (ModuleName, PackageName(..)) import Prelude + import Data.Array as Array import Data.Lens ((.~)) import Data.Lens.Record (prop) @@ -12,7 +13,7 @@ import Data.List (foldr) import Data.Map (Map) import Data.Map as Map import Data.Maybe (Maybe(..), isJust) -import Data.Newtype (wrap) +import Data.Newtype (wrap, unwrap) import Data.Set (Set) import Data.Set as Set import Data.Symbol (SProxy(..)) @@ -132,7 +133,7 @@ render { moduleIndex, groupingMode, moduleNames, isIndexHTML } = ] where - renderPackageEntry (packageName /\ modules) = + renderPackageEntry (PackageName packageName /\ modules) = HH.li [ HP.classes [ wrap "li-package" ] ] [ HH.details_ [ HH.summary_ [ HH.text packageName ] @@ -142,11 +143,11 @@ render { moduleIndex, groupingMode, moduleNames, isIndexHTML } = renderModuleName moduleName = HH.li_ - [ HH.a [ HP.href (moduleName <> ".html") ] - [ HH.text moduleName ] + [ HH.a [ HP.href (unwrap moduleName <> ".html") ] + [ HH.text $ unwrap moduleName ] ] - packageList :: Array (String /\ Set ModuleName) + packageList :: Array (PackageName /\ Set ModuleName) packageList = Map.toUnfoldable moduleIndex diff --git a/src/Docs/Search/Declarations.purs b/src/Docs/Search/Declarations.purs index ab2bb4c..82e7483 100644 --- a/src/Docs/Search/Declarations.purs +++ b/src/Docs/Search/Declarations.purs @@ -1,10 +1,10 @@ module Docs.Search.Declarations where import Docs.Search.DocsJson (ChildDeclType(..), ChildDeclaration(..), DeclType(..), Declaration(..), DocsJson(..), SourceSpan) -import Docs.Search.Score (Scores) +import Docs.Search.Score (Scores, getPackageScore, getPackageScoreForPackageName) import Docs.Search.SearchResult (ResultInfo(..), SearchResult(..)) import Docs.Search.TypeDecoder (Constraint(..), QualifiedName(..), Type(..), Kind, joinForAlls) -import Docs.Search.Types (ModuleName, PackageName) +import Docs.Search.Types (ModuleName(..), PackageName(..), PackageInfo(..)) import Prelude @@ -14,7 +14,6 @@ import Data.Array as Array import Data.Foldable (foldr) import Data.List (List, (:)) import Data.List as List -import Data.Map as Map import Data.Maybe (Maybe(..), fromMaybe) import Data.Newtype (class Newtype, unwrap, wrap) import Data.Search.Trie (Trie, alter) @@ -24,8 +23,7 @@ import Data.String.Common (toLower) import Data.String.Pattern (Pattern(..)) -newtype Declarations - = Declarations (Trie Char (List SearchResult)) +newtype Declarations = Declarations (Trie Char (List SearchResult)) derive instance newtypeDeclarations :: Newtype Declarations _ derive newtype instance semigroupDeclarations :: Semigroup Declarations @@ -42,7 +40,7 @@ insertDocsJson -> Trie Char (List SearchResult) -> Trie Char (List SearchResult) insertDocsJson scores (DocsJson { name, declarations }) trie - = foldr (insertDeclaration scores name) trie declarations + = foldr (insertDeclaration scores $ ModuleName name) trie declarations insertDeclaration @@ -63,13 +61,13 @@ insertSearchResult -> Trie Char (List SearchResult) insertSearchResult { path, result } trie = let path' = List.fromFoldable $ toCharArray $ toLower path in - alter path' (Just <<< updateResults) trie - where - updateResults mbOldResults - | Just oldResults <- mbOldResults = - result : oldResults - | otherwise = - List.singleton result + alter path' (Just <<< updateResults) trie + where + updateResults mbOldResults + | Just oldResults <- mbOldResults = + result : oldResults + | otherwise = + List.singleton result -- | For each declaration, extract its own `SearchResult` and `SearchResult`s @@ -82,19 +80,17 @@ resultsForDeclaration , result :: SearchResult } resultsForDeclaration scores moduleName indexEntry@(Declaration entry) = - let { info, title, sourceSpan, comments, children } = entry - { name, declLevel } = getLevelAndName info.declType title - packageName = extractPackageName moduleName sourceSpan - in case mkInfo declLevel indexEntry of - Nothing -> mempty - Just info' -> + case mkInfo declLevel indexEntry of + Nothing -> mempty + Just info' -> let result = SearchResult { name: title , comments , hashAnchor: declLevelToHashAnchor declLevel , moduleName , sourceSpan - , packageName - , score: fromMaybe 0 $ Map.lookup packageName scores + , packageInfo + , score: + fromMaybe 0 $ getPackageScoreForPackageName scores <$> mbPackageName , info: info' } in @@ -104,8 +100,16 @@ resultsForDeclaration scores moduleName indexEntry@(Declaration entry) = } ) <> ( List.fromFoldable children >>= - resultsForChildDeclaration scores packageName moduleName result + resultsForChildDeclaration scores packageInfo moduleName result ) + where + { info, title, sourceSpan, comments, children } = entry + { name, declLevel } = getLevelAndName info.declType title + packageInfo = extractPackageName moduleName sourceSpan + mbPackageName = + case packageInfo of + Package packageName -> Just packageName + _ -> Nothing mkInfo :: DeclLevel -> Declaration -> Maybe ResultInfo @@ -188,31 +192,30 @@ getLevelAndName DeclExternKind name = { name, declLevel: KindLevel } -- | Extract package name from `sourceSpan.name`, which contains path to -- | the source file. If `ModuleName` string starts with `Prim.`, it's a -- | built-in (guaranteed by the compiler). -extractPackageName :: ModuleName -> Maybe SourceSpan -> PackageName -extractPackageName moduleName _ - | String.split (Pattern ".") moduleName !! 0 == Just "Prim" = "" -extractPackageName _ Nothing = "" +extractPackageName :: ModuleName -> Maybe SourceSpan -> PackageInfo +extractPackageName (ModuleName moduleName) _ + | String.split (Pattern ".") moduleName !! 0 == Just "Prim" = Builtin +extractPackageName _ Nothing = UnknownPackage extractPackageName _ (Just { name }) = - let dirs = String.split (Pattern "/") name - in - fromMaybe "" do - topLevelDir <- dirs !! 0 - if topLevelDir == ".spago" - then dirs !! 1 - else do - bowerDirIx <- Array.findIndex (_ == "bower_components") dirs - dirs !! (bowerDirIx + 1) + fromMaybe LocalPackage do + topLevelDir <- dirs !! 0 + if topLevelDir == ".spago" + then Package <<< PackageName <$> dirs !! 1 + else do + bowerDirIx <- Array.findIndex (_ == "bower_components") dirs + Package <<< PackageName <$> dirs !! (bowerDirIx + 1) + where dirs = String.split (Pattern "/") name -- | Extract `SearchResults` from a `ChildDeclaration`. resultsForChildDeclaration :: Scores - -> PackageName + -> PackageInfo -> ModuleName -> SearchResult -> ChildDeclaration -> List { path :: String, result :: SearchResult } -resultsForChildDeclaration scores packageName moduleName parentResult +resultsForChildDeclaration scores packageInfo moduleName parentResult child@(ChildDeclaration { title, info, comments, mbSourceSpan }) | Just resultInfo <- mkChildInfo parentResult child = { path: title @@ -225,8 +228,8 @@ resultsForChildDeclaration scores packageName moduleName parentResult , hashAnchor: "v" , moduleName , sourceSpan: mbSourceSpan - , packageName - , score: fromMaybe 0 $ Map.lookup packageName scores + , packageInfo + , score: getPackageScore scores packageInfo , info: resultInfo } } # List.singleton @@ -261,7 +264,8 @@ mkChildInfo -- Then we construct a qualified name of the type class. constraintClass = - QualifiedName { moduleName: String.split (wrap ".") moduleName + QualifiedName { moduleNameParts: + String.split (wrap ".") $ unwrap moduleName , name: resultName } -- We concatenate two lists: diff --git a/src/Docs/Search/Engine.purs b/src/Docs/Search/Engine.purs index 93b253d..d4d4eb2 100644 --- a/src/Docs/Search/Engine.purs +++ b/src/Docs/Search/Engine.purs @@ -5,7 +5,7 @@ import Docs.Search.PackageIndex (PackageIndex, PackageResult) import Docs.Search.Score (Scores) import Docs.Search.SearchResult (SearchResult, typeOfResult) import Docs.Search.TypeQuery (TypeQuery(..), parseTypeQuery, penalty) -import Docs.Search.Types (PackageName, ModuleName) +import Docs.Search.Types (PackageInfo(..), PackageName(..), ModuleName(..)) import Prelude @@ -73,25 +73,25 @@ getResultScore (PackResult r) = r.score getResultScore (MdlResult r) = r.score -getResultPackageName :: Result -> PackageName -getResultPackageName (DeclResult r) = (unwrap r).packageName -getResultPackageName (TypeResult r) = (unwrap r).packageName -getResultPackageName (PackResult r) = r.name -getResultPackageName (MdlResult r) = r.package +getResultPackageInfo :: Result -> PackageInfo +getResultPackageInfo (DeclResult r) = (unwrap r).packageInfo +getResultPackageInfo (TypeResult r) = (unwrap r).packageInfo +getResultPackageInfo (PackResult r) = Package r.name +getResultPackageInfo (MdlResult r) = Package $ r.package getResultModuleName :: Result -> ModuleName getResultModuleName (DeclResult r) = (unwrap r).moduleName getResultModuleName (TypeResult r) = (unwrap r).moduleName -getResultModuleName (PackResult r) = "" +getResultModuleName (PackResult r) = ModuleName "" getResultModuleName (MdlResult r) = r.name getResultName :: Result -> String getResultName (DeclResult r) = (unwrap r).name getResultName (TypeResult r) = (unwrap r).name -getResultName (PackResult r) = r.name -getResultName (MdlResult r) = r.name +getResultName (PackResult r) = unwrap r.name +getResultName (MdlResult r) = unwrap r.name sortByPopularity @@ -102,7 +102,7 @@ sortByPopularity sortByPopularity { packageIndex } = Array.sortBy ( compare `on` (getResultScore >>> negate) <> - compare `on` getResultPackageName <> + compare `on` getResultPackageInfo <> compare `on` getResultModuleName <> -- Identifier name comes last: we want to make sure no `Result`s are -- equal, to avoid having unstable ordering. diff --git a/src/Docs/Search/Interactive.purs b/src/Docs/Search/Interactive.purs index 6433543..771fd7f 100644 --- a/src/Docs/Search/Interactive.purs +++ b/src/Docs/Search/Interactive.purs @@ -1,16 +1,6 @@ -- | Definitions for the "search REPL". module Docs.Search.Interactive where -import Prelude - -import Data.Array as Array -import Data.Identity (Identity(..)) -import Data.Maybe (fromMaybe) -import Data.Newtype (un, unwrap, wrap) -import Data.Search.Trie as Trie -import Data.String (length) as String -import Data.String.Common (split, trim) as String -import Data.Tuple (fst) import Docs.Search.Declarations (Declarations, mkDeclarations) import Docs.Search.DocsJson (DataDeclType(..)) import Docs.Search.Engine (mkEngineState, Result(..)) @@ -26,6 +16,18 @@ import Docs.Search.Terminal (bold, cyan, green, yellow) import Docs.Search.TypeDecoder (Constraint, FunDeps, Kind, QualifiedName, Type, TypeArgument) import Docs.Search.TypeIndex (resultsWithTypes) import Docs.Search.TypePrinter (keyword, showConstraint, showFunDeps, showKind, showType, showTypeArgument, space, syntax) +import Docs.Search.Types (PackageName, ModuleName, PackageInfo, packageInfoToString) + +import Prelude + +import Data.Array as Array +import Data.Identity (Identity(..)) +import Data.Maybe (fromMaybe) +import Data.Newtype (un, unwrap, wrap) +import Data.Search.Trie as Trie +import Data.String (length) as String +import Data.String.Common (split, trim) as String +import Data.Tuple (fst) import Effect (Effect) import Effect.Aff (launchAff_) import Effect.Class (liftEffect) @@ -122,33 +124,37 @@ showResult = case _ of showSearchResult :: SearchResult -> String -showSearchResult (SearchResult result@{ name, comments, moduleName, packageName }) = +showSearchResult (SearchResult result@{ name, comments, moduleName, packageInfo }) = showSignature result <> "\n" <> (fromMaybe "\n" $ comments <#> \comment -> "\n" <> leftShift 3 (String.trim comment) <> "\n\n") <> - bold (cyan (rightPad 40 packageName)) <> space <> bold (green moduleName) + bold ( + cyan (rightPad 40 $ packageInfoToString packageInfo) + ) <> + space <> + bold (green $ unwrap moduleName) showPackageResult :: PackageResult -> String showPackageResult { name, description } = - bold (cyan "package") <> " " <> bold (yellow name) <> + bold (cyan "package") <> " " <> bold (yellow $ unwrap name) <> (description >#> \text -> "\n\n" <> leftShift 3 text <> "\n") showModuleResult :: ModuleResult -> String showModuleResult { name, package } = - bold (cyan "module") <> " " <> bold (green name) + bold (cyan "module") <> " " <> bold (green $ unwrap name) showSignature :: forall rest. { name :: String - , moduleName :: String - , packageName :: String + , moduleName :: ModuleName + , packageInfo :: PackageInfo , info :: ResultInfo | rest } @@ -185,7 +191,7 @@ showTypeClassSignature , arguments :: Array TypeArgument , superclasses :: Array Constraint } - -> { name :: String, moduleName :: String | rest } + -> { name :: String, moduleName :: ModuleName | rest } -> String showTypeClassSignature { fundeps, arguments, superclasses } { name, moduleName } = diff --git a/src/Docs/Search/ModuleIndex.purs b/src/Docs/Search/ModuleIndex.purs index efa5ee8..b057b96 100644 --- a/src/Docs/Search/ModuleIndex.purs +++ b/src/Docs/Search/ModuleIndex.purs @@ -3,9 +3,9 @@ module Docs.Search.ModuleIndex where import Docs.Search.Config (config) import Docs.Search.Declarations (Declarations(..)) import Docs.Search.SearchResult (SearchResult(..)) -import Docs.Search.Types (ModuleName, PackageName) +import Docs.Search.Types (ModuleName, PackageName, PackageInfo(..)) import Docs.Search.Extra (stringToList) -import Docs.Search.Score (Scores, normalizePackageName) +import Docs.Search.Score (Scores) import Prelude @@ -22,6 +22,7 @@ import Data.List (List, (:)) import Data.Map (Map) import Data.Map as Map import Data.Maybe (fromMaybe) +import Data.Newtype (unwrap) import Data.Search.Trie (Trie) import Data.Search.Trie as Trie import Data.Set (Set) @@ -68,7 +69,9 @@ unpackModuleIndex packageModules = -- | E.g. `"Data.Array.ST" -> ["data.array.st", "array.st", "st"]`. extractModuleNameParts :: ModuleName -> List String extractModuleNameParts = - foldl (\acc el -> el : map (_ <> "." <> el) acc) mempty <<< String.split (Pattern ".") <<< String.toLower + unwrap >>> String.toLower >>> + String.split (Pattern ".") >>> + foldl (\acc el -> el : map (_ <> "." <> el) acc) mempty queryModuleIndex @@ -83,7 +86,7 @@ queryModuleIndex scores { index, modulePackages } query = Array.nub <#> (\name -> do package <- Map.lookup name modulePackages - pure { name, package, score: fromMaybe 0 $ Map.lookup (normalizePackageName package) scores }) # + pure { name, package, score: fromMaybe 0 $ Map.lookup package scores }) # Array.catMaybes @@ -97,9 +100,9 @@ mkPackedModuleIndex (Declarations trie) = -> Map PackageName (Set ModuleName) extract = foldr (Map.unionWith Set.union) mempty <<< map mkEntry where - mkEntry (SearchResult { packageName, moduleName }) = + mkEntry (SearchResult { packageInfo: Package packageName, moduleName }) = Map.singleton packageName (Set.singleton moduleName) - + mkEntry _ = mempty loadModuleIndex :: Aff PackedModuleIndex loadModuleIndex = do diff --git a/src/Docs/Search/PackageIndex.purs b/src/Docs/Search/PackageIndex.purs index 09503d7..8f3685a 100644 --- a/src/Docs/Search/PackageIndex.purs +++ b/src/Docs/Search/PackageIndex.purs @@ -2,7 +2,8 @@ module Docs.Search.PackageIndex where import Docs.Search.Config (config) import Docs.Search.Extra (stringToList) -import Docs.Search.Score (Scores, getPackageScore, normalizePackageName) +import Docs.Search.Score (Scores, getPackageScoreForPackageName, normalizePackageName) +import Docs.Search.Types (PackageName, RawPackageName(..)) import Prelude @@ -23,10 +24,10 @@ import Web.Bower.PackageMeta (PackageMeta(..)) type PackageResult - = { name :: String + = { name :: PackageName , description :: Maybe String , score :: Int - , dependencies :: Array String + , dependencies :: Array PackageName , repository :: Maybe String } @@ -44,24 +45,26 @@ mkPackageInfo packageScores pms = where insert :: PackageMeta - -> Map String PackageResult - -> Map String PackageResult + -> Map PackageName PackageResult + -> Map PackageName PackageResult insert (PackageMeta { name , description , dependencies , devDependencies , repository }) = - - Map.insert - name - { name + Map.insert + packageName + { name: packageName , description: description - , score: getPackageScore packageScores $ normalizePackageName name - , dependencies: unwrap dependencies <#> (_.packageName) + , score: getPackageScoreForPackageName packageScores packageName + , dependencies: + unwrap dependencies <#> + (_.packageName >>> RawPackageName >>> normalizePackageName) , repository: repository <#> (_.url) } + where packageName = normalizePackageName $ RawPackageName name mkScoresFromPackageIndex :: PackageIndex -> Scores mkScoresFromPackageIndex = @@ -78,7 +81,7 @@ loadPackageIndex = do mkPackageIndex :: PackageInfo -> PackageIndex mkPackageIndex = Array.foldr - (\package -> Trie.insert (stringToList $ normalizePackageName package.name) package) + (\package -> Trie.insert (stringToList $ unwrap package.name) package) mempty diff --git a/src/Docs/Search/Score.purs b/src/Docs/Search/Score.purs index 7304470..03e276d 100644 --- a/src/Docs/Search/Score.purs +++ b/src/Docs/Search/Score.purs @@ -1,6 +1,6 @@ module Docs.Search.Score where -import Docs.Search.Types (PackageName) +import Docs.Search.Types (RawPackageName(..), PackageName(..), PackageInfo(..)) import Prelude @@ -15,16 +15,9 @@ import Web.Bower.PackageMeta (Dependencies, PackageMeta) type Scores = Map PackageName Int - -getPackageScore :: Scores -> PackageName -> Int -getPackageScore _ "" = 1000000 -getPackageScore _ "" = 2000000 -getPackageScore scores name = fromMaybe 0 $ Map.lookup name scores - - -normalizePackageName :: PackageName -> PackageName -normalizePackageName packageName = - fromMaybe packageName $ String.stripPrefix (wrap "purescript-") packageName +normalizePackageName :: RawPackageName -> PackageName +normalizePackageName (RawPackageName p) = + fromMaybe (PackageName p) $ map wrap $ String.stripPrefix (wrap "purescript-") p -- | Construct a mapping from package names to their scores, based on number @@ -44,4 +37,16 @@ mkScores = Array.foldr (\dep -> Map.insertWith add dep 1) scores - (deps # unwrap >>> map (_.packageName >>> normalizePackageName)) + (deps # unwrap >>> map (_.packageName >>> RawPackageName >>> normalizePackageName)) + + +getPackageScore :: Scores -> PackageInfo -> Int +getPackageScore scores = case _ of + Package p -> getPackageScoreForPackageName scores p + Builtin -> 100000 + LocalPackage -> 200000 + UnknownPackage -> 0 + + +getPackageScoreForPackageName :: Scores -> PackageName -> Int +getPackageScoreForPackageName scores p = fromMaybe 0 $ Map.lookup p scores diff --git a/src/Docs/Search/SearchResult.purs b/src/Docs/Search/SearchResult.purs index 6739184..8d5af24 100644 --- a/src/Docs/Search/SearchResult.purs +++ b/src/Docs/Search/SearchResult.purs @@ -2,6 +2,7 @@ module Docs.Search.SearchResult where import Docs.Search.DocsJson (DataDeclType) import Docs.Search.TypeDecoder (Constraint, FunDeps, Kind, QualifiedName, Type, TypeArgument) +import Docs.Search.Types (PackageName, PackageInfo, ModuleName) import Prelude @@ -59,8 +60,8 @@ newtype SearchResult { name :: String , comments :: Maybe String , hashAnchor :: String - , moduleName :: String - , packageName :: String + , moduleName :: ModuleName + , packageInfo :: PackageInfo , score :: Int , sourceSpan :: Maybe { start :: Array Int , end :: Array Int diff --git a/src/Docs/Search/TypeDecoder.purs b/src/Docs/Search/TypeDecoder.purs index f7c7ff1..89f2138 100644 --- a/src/Docs/Search/TypeDecoder.purs +++ b/src/Docs/Search/TypeDecoder.purs @@ -25,20 +25,20 @@ instance showQualifiedName :: Show QualifiedName where show = genericShow newtype QualifiedName - = QualifiedName { moduleName :: Array String + = QualifiedName { moduleNameParts :: Array String , name :: String } instance decodeJsonQualifiedName :: DecodeJson QualifiedName where decodeJson json = do decodeTuple - (\moduleName name -> QualifiedName { moduleName, name }) + (\moduleNameParts name -> QualifiedName { moduleNameParts, name }) (mkJsonError "QualifiedName" json) json instance encodeJsonQualifiedName :: EncodeJson QualifiedName where - encodeJson (QualifiedName { moduleName, name }) = - encodeTuple moduleName name + encodeJson (QualifiedName { moduleNameParts, name }) = + encodeTuple moduleNameParts name mkJsonError :: String -> Json -> (forall i. i -> String) mkJsonError name json _ = diff --git a/src/Docs/Search/TypeIndex.purs b/src/Docs/Search/TypeIndex.purs index ae8e5a5..bd0c1a3 100644 --- a/src/Docs/Search/TypeIndex.purs +++ b/src/Docs/Search/TypeIndex.purs @@ -8,6 +8,7 @@ import Docs.Search.Score (Scores) import Docs.Search.SearchResult (ResultInfo(..), SearchResult(..)) import Docs.Search.TypeDecoder (Type) import Docs.Search.TypeQuery (TypeQuery) +import Docs.Search.Types (ModuleName(..)) import Docs.Search.TypeShape (shapeOfType, shapeOfTypeQuery, stringifyShape) import Prelude @@ -48,7 +49,8 @@ mkTypeIndex scores docsJsons = allResults :: Scores -> DocsJson -> Array SearchResult allResults scores (DocsJson { name, declarations }) = - declarations >>= (resultsForDeclaration scores name >>> map (_.result) >>> Array.fromFoldable) + declarations >>= (resultsForDeclaration scores (ModuleName name) >>> + map (_.result) >>> Array.fromFoldable) resultsWithTypes :: Scores -> DocsJson -> Array SearchResult diff --git a/src/Docs/Search/TypePrinter.purs b/src/Docs/Search/TypePrinter.purs index 346a646..b98867c 100644 --- a/src/Docs/Search/TypePrinter.purs +++ b/src/Docs/Search/TypePrinter.purs @@ -21,13 +21,13 @@ showType = case _ of TypeOp qname -> showQualifiedName qname TypeApp (TypeApp (TypeConstructor - (QualifiedName { moduleName: [ "Prim" ] + (QualifiedName { moduleNameParts: [ "Prim" ] , name: "Function" })) t1) t2 -> showType t1 <> syntax " -> " <> showType t2 - TypeApp (TypeConstructor (QualifiedName { moduleName: [ "Prim" ] + TypeApp (TypeConstructor (QualifiedName { moduleNameParts: [ "Prim" ] , name: "Record" })) row -> showRow false row diff --git a/src/Docs/Search/TypeQuery.purs b/src/Docs/Search/TypeQuery.purs index 5b68946..1c170b0 100644 --- a/src/Docs/Search/TypeQuery.purs +++ b/src/Docs/Search/TypeQuery.purs @@ -248,7 +248,7 @@ unify query type_ = go Nil (List.singleton { q: query, t: type_ }) -- * Functions go acc ({ q: QFun q1 q2 , t: TypeApp (TypeApp (TypeConstructor - (QualifiedName { moduleName: [ "Prim" ] + (QualifiedName { moduleNameParts: [ "Prim" ] , name: "Function" })) t1) t2 } : rest) = go acc ({ q: q1, t: t1 } : { q: q2, t: t2 } : rest) go acc ({ q: q@(QFun q1 q2), t } : rest) = @@ -257,7 +257,7 @@ unify query type_ = go Nil (List.singleton { q: query, t: type_ }) -- * Rows go acc ({ q: QApp (QConst "Record") (QRow qRows) , t: TypeApp (TypeConstructor - (QualifiedName { moduleName: [ "Prim" ] + (QualifiedName { moduleNameParts: [ "Prim" ] , name: "Record" })) row } : rest) = let { rows, ty } = joinRows row qRowsLength = List.length qRows @@ -407,7 +407,7 @@ typeSize = go 0 <<< List.singleton go n (TypeOp _ : rest) = go (n + 1) rest go n (TypeApp (TypeApp (TypeConstructor - (QualifiedName { moduleName: [ "Prim" ] + (QualifiedName { moduleNameParts: [ "Prim" ] , name: "Function" })) t1) t2 : rest) = go (n + 1) (t1 : t2 : rest) go n (TypeApp q1 q2 : rest) = diff --git a/src/Docs/Search/TypeShape.purs b/src/Docs/Search/TypeShape.purs index d7741a6..7a38798 100644 --- a/src/Docs/Search/TypeShape.purs +++ b/src/Docs/Search/TypeShape.purs @@ -101,7 +101,7 @@ shapeOfType ty = List.reverse $ go (pure ty) Nil TypeWildcard -> go rest (PVar : acc) - TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleName: ["Prim"] + TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Prim"] , name: "Function" })) t1) t2 -> go (t1 : t2 : rest) (PFun : acc) diff --git a/src/Docs/Search/Types.purs b/src/Docs/Search/Types.purs index 1f65654..9c1b979 100644 --- a/src/Docs/Search/Types.purs +++ b/src/Docs/Search/Types.purs @@ -1,5 +1,59 @@ module Docs.Search.Types where -type ModuleName = String +import Prelude -type PackageName = String +import Data.Argonaut.Decode (class DecodeJson) +import Data.Argonaut.Decode.Generic.Rep (genericDecodeJson) +import Data.Argonaut.Encode (class EncodeJson) +import Data.Argonaut.Encode.Generic.Rep (genericEncodeJson) +import Data.Generic.Rep (class Generic) +import Data.Generic.Rep.Show (genericShow) +import Data.Newtype (class Newtype) + + +newtype ModuleName = ModuleName String + +derive instance newtypeModuleName :: Newtype ModuleName _ +derive instance genericModuleName :: Generic ModuleName _ +derive newtype instance eqModuleName :: Eq ModuleName +derive newtype instance ordModuleName :: Ord ModuleName +derive newtype instance decodeJsonModuleName :: DecodeJson ModuleName +derive newtype instance encodeJsonModuleName :: EncodeJson ModuleName + + +-- | Non-normalized package name, e.g. `purescript-prelude` or just `prelude`. +newtype RawPackageName = RawPackageName String + +derive instance newtypeRawPackageName :: Newtype RawPackageName _ + + +-- | Normalized package name without "purescript-" prefix. +newtype PackageName = PackageName String + +derive instance newtypePackageName :: Newtype PackageName _ +derive newtype instance eqPackageName :: Eq PackageName +derive newtype instance ordPackageName :: Ord PackageName +derive newtype instance showPackageName :: Show PackageName +derive newtype instance decodeJsonPackageName :: DecodeJson PackageName +derive newtype instance encodeJsonPackageName :: EncodeJson PackageName +derive instance genericPackageName :: Generic PackageName _ + + +data PackageInfo = Package PackageName | Builtin | LocalPackage | UnknownPackage + +derive instance eqPackageInfo :: Eq PackageInfo +derive instance ordPackageInfo :: Ord PackageInfo +derive instance genericPackageInfo :: Generic PackageInfo _ +instance showPackageInfo :: Show PackageInfo where + show = genericShow + +instance decodeJsonPackageInfo :: DecodeJson PackageInfo where + decodeJson = genericDecodeJson +instance encodeJsonPackageInfo :: EncodeJson PackageInfo where + encodeJson = genericEncodeJson + +packageInfoToString :: PackageInfo -> String +packageInfoToString (Package (PackageName p)) = p +packageInfoToString Builtin = "" +packageInfoToString LocalPackage = "" +packageInfoToString UnknownPackage = "" diff --git a/test/Main.purs b/test/Main.purs index f7c435c..fa4c96b 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -67,7 +67,7 @@ mainTest = do """ assertRight (decodeJson qualifiedName) - (QualifiedName { moduleName: ["Prim"] + (QualifiedName { moduleNameParts: ["Prim"] , name: "Type" } ) @@ -87,7 +87,7 @@ mainTest = do """ assertRight (decodeJson namedKind) - (NamedKind $ QualifiedName { moduleName: ["Prim"] + (NamedKind $ QualifiedName { moduleNameParts: ["Prim"] , name: "Type" } ) @@ -111,7 +111,7 @@ mainTest = do """ assertRight (decodeJson row) - (Row $ NamedKind $ QualifiedName { moduleName: ["Prim"] + (Row $ NamedKind $ QualifiedName { moduleNameParts: ["Prim"] , name: "Type" } ) @@ -154,11 +154,11 @@ mainTest = do } """ assertRight (decodeJson funKind) - (FunKind (Row $ NamedKind $ QualifiedName { moduleName: ["Prim"] + (FunKind (Row $ NamedKind $ QualifiedName { moduleNameParts: ["Prim"] , name: "Type" } ) - (Row $ NamedKind $ QualifiedName { moduleName: ["Prim"] + (Row $ NamedKind $ QualifiedName { moduleNameParts: ["Prim"] , name: "Type" } ) @@ -181,7 +181,7 @@ mainTest = do } """ assertRight (decodeJson constraint) - (Constraint { constraintClass: QualifiedName { moduleName: ["Prim"] + (Constraint { constraintClass: QualifiedName { moduleNameParts: ["Prim"] , name: "Partial" } , constraintArgs: [] @@ -230,7 +230,7 @@ mainTest = do assertRight (decodeJson typeApp1) $ TypeApp - (TypeConstructor (QualifiedName { moduleName: + (TypeConstructor (QualifiedName { moduleNameParts: [ "Control" , "Monad" , "ST" @@ -256,7 +256,7 @@ mainTest = do } """ assertRight (decodeJson typeOp) $ - TypeOp $ QualifiedName { moduleName: [ "Data", "NaturalTransformation" ] + TypeOp $ QualifiedName { moduleNameParts: [ "Data", "NaturalTransformation" ] , name: "~>" } @@ -293,7 +293,7 @@ mainTest = do assertRight (decodeJson binaryNoParens) $ BinaryNoParensType - (TypeOp $ QualifiedName { moduleName: ["Data", "NaturalTransformation"], name: "~>" }) + (TypeOp $ QualifiedName { moduleNameParts: ["Data", "NaturalTransformation"], name: "~>" }) (TypeVar "m") (TypeVar "n") @@ -335,14 +335,14 @@ mainTest = do assertRight (decodeJson parensInType) $ ParensInType $ TypeApp - (TypeConstructor (QualifiedName { moduleName: + (TypeConstructor (QualifiedName { moduleNameParts: [ "Data" , "Maybe" ], name: "Maybe" } )) - (TypeConstructor (QualifiedName { moduleName: + (TypeConstructor (QualifiedName { moduleNameParts: [ "Prim" ], name: "String" @@ -389,7 +389,7 @@ mainTest = do assertRight (decodeJson rcons) $ RCons "tail" - (TypeApp (TypeConstructor $ QualifiedName { moduleName: [ "Data", "Symbol" ], name: "SProxy" }) + (TypeApp (TypeConstructor $ QualifiedName { moduleNameParts: [ "Data", "Symbol" ], name: "SProxy" }) (TypeVar "t")) REmpty @@ -545,8 +545,8 @@ mainTest = do """ assertRight (decodeJson forallJson) $ ForAll "f" - (Just (FunKind (NamedKind (QualifiedName { moduleName: ["Prim","RowList"], name: "RowList" })) (NamedKind (QualifiedName { moduleName: ["Prim"], name: "Type" })))) - (TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleName: ["Prim"], name: "Function" })) (TypeApp (TypeVar "f") (TypeVar "l"))) (TypeApp (TypeConstructor (QualifiedName { moduleName: ["Data","List","Types"], name: "List" })) (ParensInType (TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleName: ["Data","Tuple"], name: "Tuple" })) (TypeConstructor (QualifiedName { moduleName: ["Prim"], name: "String" }))) (TypeConstructor (QualifiedName { moduleName: ["Prim"], name: "String" })))))) + (Just (FunKind (NamedKind (QualifiedName { moduleNameParts: ["Prim","RowList"], name: "RowList" })) (NamedKind (QualifiedName { moduleNameParts: ["Prim"], name: "Type" })))) + (TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Prim"], name: "Function" })) (TypeApp (TypeVar "f") (TypeVar "l"))) (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Data","List","Types"], name: "List" })) (ParensInType (TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Data","Tuple"], name: "Tuple" })) (TypeConstructor (QualifiedName { moduleNameParts: ["Prim"], name: "String" }))) (TypeConstructor (QualifiedName { moduleNameParts: ["Prim"], name: "String" })))))) @@ -558,7 +558,7 @@ mainTest = do {"annotation":[],"tag":"ForAll","contents":["o",{"annotation":[],"tag":"ForAll","contents":["r",{"annotation":[],"tag":"ForAll","contents":["l",{"annotation":[],"tag":"ConstrainedType","contents":[{"constraintAnn":[],"constraintClass":[["Type","Data","Boolean"],"And"],"constraintArgs":[{"annotation":[],"tag":"TypeVar","contents":"l"},{"annotation":[],"tag":"TypeVar","contents":"r"},{"annotation":[],"tag":"TypeVar","contents":"o"}],"constraintData":null},{"annotation":[],"tag":"TypeApp","contents":[{"annotation":[],"tag":"TypeApp","contents":[{"annotation":[],"tag":"TypeConstructor","contents":[["Prim"],"Function"]},{"annotation":[],"tag":"TypeApp","contents":[{"annotation":[],"tag":"TypeConstructor","contents":[["Type","Data","Boolean"],"BProxy"]},{"annotation":[],"tag":"TypeVar","contents":"l"}]}]},{"annotation":[],"tag":"TypeApp","contents":[{"annotation":[],"tag":"TypeApp","contents":[{"annotation":[],"tag":"TypeConstructor","contents":[["Prim"],"Function"]},{"annotation":[],"tag":"TypeApp","contents":[{"annotation":[],"tag":"TypeConstructor","contents":[["Type","Data","Boolean"],"BProxy"]},{"annotation":[],"tag":"TypeVar","contents":"r"}]}]},{"annotation":[],"tag":"TypeApp","contents":[{"annotation":[],"tag":"TypeConstructor","contents":[["Type","Data","Boolean"],"BProxy"]},{"annotation":[],"tag":"TypeVar","contents":"o"}]}]}]}]},null]},null]},null]} """ - assertRight (decodeJson json) $ (ForAll "o" Nothing (ForAll "r" Nothing (ForAll "l" Nothing (ConstrainedType (Constraint { constraintArgs: [(TypeVar "l"),(TypeVar "r"),(TypeVar "o")], constraintClass: (QualifiedName { moduleName: ["Type","Data","Boolean"], name: "And" }) }) (TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleName: ["Prim"], name: "Function" })) (TypeApp (TypeConstructor (QualifiedName { moduleName: ["Type","Data","Boolean"], name: "BProxy" })) (TypeVar "l"))) (TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleName: ["Prim"], name: "Function" })) (TypeApp (TypeConstructor (QualifiedName { moduleName: ["Type","Data","Boolean"], name: "BProxy" })) (TypeVar "r"))) (TypeApp (TypeConstructor (QualifiedName { moduleName: ["Type","Data","Boolean"], name: "BProxy" })) (TypeVar "o")))))))) + assertRight (decodeJson json) $ (ForAll "o" Nothing (ForAll "r" Nothing (ForAll "l" Nothing (ConstrainedType (Constraint { constraintArgs: [(TypeVar "l"),(TypeVar "r"),(TypeVar "o")], constraintClass: (QualifiedName { moduleNameParts: ["Type","Data","Boolean"], name: "And" }) }) (TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Prim"], name: "Function" })) (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Type","Data","Boolean"], name: "BProxy" })) (TypeVar "l"))) (TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Prim"], name: "Function" })) (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Type","Data","Boolean"], name: "BProxy" })) (TypeVar "r"))) (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Type","Data","Boolean"], name: "BProxy" })) (TypeVar "o")))))))) suite "Kind encoder" do test "FunKind" do @@ -570,4 +570,4 @@ mainTest = do qualified :: Array String -> String -> QualifiedName -qualified moduleName name = QualifiedName { moduleName, name } +qualified moduleNameParts name = QualifiedName { moduleNameParts, name } diff --git a/test/Test/Declarations.purs b/test/Test/Declarations.purs index fe3d9a3..60ea61d 100644 --- a/test/Test/Declarations.purs +++ b/test/Test/Declarations.purs @@ -2,8 +2,11 @@ module Test.Declarations where import Prelude -import Data.Maybe (Maybe(..)) import Docs.Search.Declarations (extractPackageName) +import Docs.Search.Types (PackageName(..), PackageInfo(..)) + +import Data.Maybe (Maybe(..)) +import Data.Newtype (wrap) import Test.Unit (TestSuite, suite, test) import Test.Unit.Assert as Assert @@ -12,26 +15,26 @@ tests :: TestSuite tests = do suite "Declarations" do test "extractPackageName" do - Assert.equal "" (extractPackageName "Prim" Nothing) - Assert.equal "" (extractPackageName "Prim.Foo" Nothing) - Assert.equal "" (extractPackageName "Prim.Foo.Bar" Nothing) - Assert.equal "" (extractPackageName "Primitive" Nothing) - Assert.equal "foo" - (extractPackageName "Foo" $ + Assert.equal Builtin (extractPackageName (wrap "Prim") Nothing) + Assert.equal Builtin (extractPackageName (wrap "Prim.Foo") Nothing) + Assert.equal Builtin (extractPackageName (wrap "Prim.Foo.Bar") Nothing) + Assert.equal UnknownPackage (extractPackageName (wrap "Primitive") Nothing) + Assert.equal (Package $ PackageName "foo") + (extractPackageName (wrap "Foo") $ Just { start: [] , end: [] , name: ".spago/foo/src/Foo.purs" } ) - Assert.equal "bar" - (extractPackageName "Bar" $ + Assert.equal (Package $ PackageName "bar") + (extractPackageName (wrap "Bar") $ Just { start: [] , end: [] , name: "/path/to/somewhere/bower_components/bar/src/Bar.purs" } ) - Assert.equal "" - (extractPackageName "Bar" $ + Assert.equal LocalPackage + (extractPackageName (wrap "Bar") $ Just { start: [] , end: [] , name: "/path/to/somewhere/src/Bar.purs" diff --git a/test/Test/ModuleIndex.purs b/test/Test/ModuleIndex.purs index c6a4e6e..df4a59d 100644 --- a/test/Test/ModuleIndex.purs +++ b/test/Test/ModuleIndex.purs @@ -5,6 +5,7 @@ import Docs.Search.ModuleIndex (extractModuleNameParts) import Prelude import Data.List as List +import Data.Newtype (wrap) import Test.Unit (TestSuite, suite, test) import Test.Unit.Assert as Assert @@ -14,11 +15,11 @@ tests = do suite "ModuleIndex" do test "test #0" do - Assert.equal (extractModuleNameParts "Data.Array.ST") ( + Assert.equal (extractModuleNameParts $ wrap "Data.Array.ST") ( List.fromFoldable [ "st", "array.st", "data.array.st" ] ) test "test #1" do - Assert.equal (extractModuleNameParts "Foo") ( + Assert.equal (extractModuleNameParts $ wrap "Foo") ( List.fromFoldable [ "foo" ] ) diff --git a/test/Test/TypeQuery.purs b/test/Test/TypeQuery.purs index 4afaafa..f508269 100644 --- a/test/Test/TypeQuery.purs +++ b/test/Test/TypeQuery.purs @@ -245,7 +245,7 @@ tests = do c2 = constr (qname [""] "GenericEq") [TypeVar "rep"] fun t1 t2 = - TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleName: ["Prim"] + TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Prim"] , name: "Function" })) t1) t2 type_ = ForAll "a" Nothing $ @@ -391,7 +391,7 @@ nl :: forall t5 t6. Foldable t6 => t5 -> t6 t5 -> NonEmptyList t5 nl x rst = NonEmptyList.cons' x $ List.fromFoldable rst unitType :: Type -unitType = TypeConstructor (QualifiedName { moduleName: [] +unitType = TypeConstructor (QualifiedName { moduleNameParts: [] , name: "Unit" }) @@ -399,7 +399,7 @@ countFreeVars :: TypeQuery -> Int countFreeVars = getFreeVariables >>> Set.size qname :: Array String -> String -> QualifiedName -qname m n = QualifiedName { moduleName: m, name: n } +qname m n = QualifiedName { moduleNameParts: m, name: n } constr :: QualifiedName -> Array Type -> Constraint constr c a = Constraint { constraintClass: c, constraintArgs: a } From fee7abefc0bf7e2d742939c9794dfa89341acb53 Mon Sep 17 00:00:00 2001 From: klntsky Date: Mon, 27 Jul 2020 22:24:32 +0300 Subject: [PATCH 11/45] Wrap package scores and identifiers in newtypes --- src/Docs/Search/App/SearchResults.purs | 50 +++--- src/Docs/Search/Declarations.purs | 8 +- src/Docs/Search/Engine.purs | 15 +- src/Docs/Search/Interactive.purs | 50 +++--- src/Docs/Search/ModuleIndex.purs | 9 +- src/Docs/Search/PackageIndex.purs | 4 +- src/Docs/Search/Score.purs | 18 +- src/Docs/Search/SearchResult.purs | 6 +- src/Docs/Search/TypeDecoder.purs | 10 +- src/Docs/Search/TypePrinter.purs | 12 +- src/Docs/Search/TypeQuery.purs | 61 +++---- src/Docs/Search/TypeShape.purs | 3 +- src/Docs/Search/Types.purs | 27 ++- test/Main.purs | 78 +++------ test/Test/TypeQuery.purs | 223 ++++++++++++++----------- 15 files changed, 299 insertions(+), 275 deletions(-) diff --git a/src/Docs/Search/App/SearchResults.purs b/src/Docs/Search/App/SearchResults.purs index 16159e3..c5f9e82 100644 --- a/src/Docs/Search/App/SearchResults.purs +++ b/src/Docs/Search/App/SearchResults.purs @@ -6,7 +6,7 @@ import Docs.Search.BrowserEngine (PartialIndex, browserSearchEngine) import Docs.Search.Config (config) import Docs.Search.Declarations (DeclLevel(..), declLevelToHashAnchor) import Docs.Search.DocsJson (DataDeclType(..)) -import Docs.Search.Engine (Result(..)) +import Docs.Search.Engine (Result(..), packageInfoToString) import Docs.Search.Engine as Engine import Docs.Search.Extra (homePageFromRepository, (>#>)) import Docs.Search.ModuleIndex (ModuleResult) @@ -14,7 +14,7 @@ import Docs.Search.PackageIndex (PackageResult) import Docs.Search.SearchResult (ResultInfo(..), SearchResult(..)) import Docs.Search.TypeDecoder (Constraint(..), FunDep(..), FunDeps(..), Kind(..), QualifiedName(..), Type(..), TypeArgument(..), joinForAlls, joinRows) import Docs.Search.TypeIndex (TypeIndex) -import Docs.Search.Types (ModuleName(..), packageInfoToString) +import Docs.Search.Types (ModuleName(..), Identifier(..)) import Prelude @@ -302,9 +302,9 @@ renderSearchResult markdownIt (SearchResult result) = , HE.onClick $ const $ Just $ SearchResultClicked result.moduleName , HP.href $ unwrap result.moduleName <> ".html#" <> - result.hashAnchor <> ":" <> result.name + result.hashAnchor <> ":" <> unwrap result.name ] - [ HH.text result.name ] + [ HH.text $ unwrap result.name ] ] ] @@ -341,7 +341,7 @@ renderSearchResult markdownIt (SearchResult result) = renderResultType :: forall a rest . { info :: ResultInfo - , name :: String + , name :: Identifier , moduleName :: ModuleName | rest } @@ -373,7 +373,7 @@ renderResultType result = renderValueSignature :: forall a rest . { moduleName :: ModuleName - , name :: String + , name :: Identifier | rest } -> Type @@ -381,7 +381,7 @@ renderValueSignature renderValueSignature result ty = [ HH.a [ makeHref ValueLevel false result.moduleName result.name , HE.onClick $ const $ Just $ SearchResultClicked result.moduleName ] - [ HH.text result.name ] + [ HH.text $ unwrap result.name ] , HH.text " :: " , renderType ty ] @@ -392,7 +392,7 @@ renderTypeClassSignature , arguments :: Array TypeArgument , superclasses :: Array Constraint } - -> { name :: String, moduleName :: ModuleName | rest } + -> { name :: Identifier, moduleName :: ModuleName | rest } -> Array (HH.HTML a Action) renderTypeClassSignature { fundeps, arguments, superclasses } { name, moduleName } = [ keyword "class" @@ -414,7 +414,7 @@ renderTypeClassSignature { fundeps, arguments, superclasses } { name, moduleName , HE.onClick $ const $ Just $ SearchResultClicked moduleName ] - [ HH.text name ] + [ HH.text $ unwrap name ] , space ] <> ( Array.intercalate [ space ] $ @@ -444,10 +444,10 @@ renderTypeClassMemberSignature , typeClass :: QualifiedName , typeClassArguments :: Array TypeArgument } - -> { name :: String | rest } + -> { name :: Identifier | rest } -> Array (HH.HTML a Action) renderTypeClassMemberSignature { type: ty, typeClass, typeClassArguments } result = - [ HH.text result.name + [ HH.text $ unwrap result.name , HH.text " :: " , renderType ty ] @@ -457,7 +457,7 @@ renderDataSignature :: forall a rest . { typeArguments :: Array TypeArgument , dataDeclType :: DataDeclType } - -> { name :: String | rest } + -> { name :: Identifier | rest } -> Array (HH.HTML a Action) renderDataSignature { typeArguments, dataDeclType } { name } = [ keyword @@ -465,7 +465,7 @@ renderDataSignature { typeArguments, dataDeclType } { name } = NewtypeDataDecl -> "newtype" DataDataDecl -> "data" , space - , HH.text name + , HH.text $ unwrap name , space , HH.span_ $ Array.intercalate [ space ] $ @@ -478,12 +478,12 @@ renderTypeSynonymSignature . { type :: Type , arguments :: Array TypeArgument } - -> { name :: String | rest } + -> { name :: Identifier | rest } -> Array (HH.HTML a Action) renderTypeSynonymSignature { type: ty, arguments } { name } = [ keyword "type" , space - , HH.text name + , HH.text $ unwrap name , space , HH.span_ $ Array.intercalate [ space ] $ @@ -499,10 +499,10 @@ renderTypeArgument :: forall a. TypeArgument -> Array (HH.HTML a Action) renderTypeArgument (TypeArgument { name, mbKind }) = case mbKind of Nothing -> - [ HH.text name ] + [ HH.text $ name ] Just kind -> [ HH.text "(" - , HH.text name + , HH.text $ name , HH.text " :: " , renderKind kind , HH.text ")" @@ -522,14 +522,14 @@ renderType = case _ of TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: [ "Prim" ] - , name: "Function" })) t1) t2 -> + , name: Identifier "Function" })) t1) t2 -> HH.span_ [ renderType t1 , syntax " -> " , renderType t2 ] TypeApp (TypeConstructor (QualifiedName { moduleNameParts: [ "Prim" ] - , name: "Record" })) + , name: Identifier "Record" })) row -> renderRow false row @@ -624,7 +624,7 @@ renderRow asRow = ( Array.intercalate [ HH.text ", " ] $ Array.fromFoldable $ rows <#> \entry -> - [ HH.span_ [ HH.text $ entry.row <> " :: " + [ HH.span_ [ HH.text $ unwrap entry.row <> " :: " , renderType entry.ty ] ] ) <> @@ -637,7 +637,7 @@ renderRow asRow = closing = if asRow then " )" else " }" primRecord :: QualifiedName - primRecord = QualifiedName { moduleNameParts: [ "Prim" ], name: "Record" } + primRecord = QualifiedName { moduleNameParts: [ "Prim" ], name: Identifier "Record" } renderConstraint @@ -658,13 +658,13 @@ renderQualifiedName -> HH.HTML a Action renderQualifiedName isInfix level (QualifiedName { moduleNameParts, name }) = if isBuiltIn then - HH.text name + HH.text $ unwrap name else HH.a [ HE.onClick $ const $ Just $ SearchResultClicked $ moduleName , makeHref level isInfix moduleName name ] - [ HH.text name ] + [ HH.text $ unwrap name ] where moduleName = ModuleName $ Array.intercalate "." $ moduleNameParts isBuiltIn = moduleNameParts !! 0 == Just "Prim" @@ -686,13 +686,13 @@ makeHref . DeclLevel -> Boolean -> ModuleName - -> String + -> Identifier -> HH.IProp ( href :: String | rest ) t makeHref level isInfix moduleName name = HP.href $ unwrap moduleName <> ".html#" <> declLevelToHashAnchor level <> ":" <> - if isInfix then "type (" <> name <> ")" else name + if isInfix then "type (" <> unwrap name <> ")" else unwrap name keyword diff --git a/src/Docs/Search/Declarations.purs b/src/Docs/Search/Declarations.purs index 82e7483..ff36bdb 100644 --- a/src/Docs/Search/Declarations.purs +++ b/src/Docs/Search/Declarations.purs @@ -4,7 +4,7 @@ import Docs.Search.DocsJson (ChildDeclType(..), ChildDeclaration(..), DeclType(. import Docs.Search.Score (Scores, getPackageScore, getPackageScoreForPackageName) import Docs.Search.SearchResult (ResultInfo(..), SearchResult(..)) import Docs.Search.TypeDecoder (Constraint(..), QualifiedName(..), Type(..), Kind, joinForAlls) -import Docs.Search.Types (ModuleName(..), PackageName(..), PackageInfo(..)) +import Docs.Search.Types (ModuleName(..), PackageName(..), PackageInfo(..), Identifier(..)) import Prelude @@ -83,14 +83,14 @@ resultsForDeclaration scores moduleName indexEntry@(Declaration entry) = case mkInfo declLevel indexEntry of Nothing -> mempty Just info' -> - let result = SearchResult { name: title + let result = SearchResult { name: Identifier title , comments , hashAnchor: declLevelToHashAnchor declLevel , moduleName , sourceSpan , packageInfo , score: - fromMaybe 0 $ getPackageScoreForPackageName scores <$> mbPackageName + fromMaybe zero $ getPackageScoreForPackageName scores <$> mbPackageName , info: info' } in @@ -219,7 +219,7 @@ resultsForChildDeclaration scores packageInfo moduleName parentResult child@(ChildDeclaration { title, info, comments, mbSourceSpan }) | Just resultInfo <- mkChildInfo parentResult child = { path: title - , result: SearchResult { name: title + , result: SearchResult { name: Identifier title , comments -- `ChildDeclaration`s are always either data -- constructors, type class members or instances. diff --git a/src/Docs/Search/Engine.purs b/src/Docs/Search/Engine.purs index d4d4eb2..8c338d8 100644 --- a/src/Docs/Search/Engine.purs +++ b/src/Docs/Search/Engine.purs @@ -5,7 +5,7 @@ import Docs.Search.PackageIndex (PackageIndex, PackageResult) import Docs.Search.Score (Scores) import Docs.Search.SearchResult (SearchResult, typeOfResult) import Docs.Search.TypeQuery (TypeQuery(..), parseTypeQuery, penalty) -import Docs.Search.Types (PackageInfo(..), PackageName(..), ModuleName(..)) +import Docs.Search.Types (PackageInfo(..), ModuleName(..), PackageName(..), PackageScore) import Prelude @@ -66,7 +66,7 @@ data Result | MdlResult ModuleResult -getResultScore :: Result -> Int +getResultScore :: Result -> PackageScore getResultScore (DeclResult r) = (unwrap r).score getResultScore (TypeResult r) = (unwrap r).score getResultScore (PackResult r) = r.score @@ -88,8 +88,8 @@ getResultModuleName (MdlResult r) = r.name getResultName :: Result -> String -getResultName (DeclResult r) = (unwrap r).name -getResultName (TypeResult r) = (unwrap r).name +getResultName (DeclResult r) = unwrap (unwrap r).name +getResultName (TypeResult r) = unwrap (unwrap r).name getResultName (PackResult r) = unwrap r.name getResultName (MdlResult r) = unwrap r.name @@ -158,3 +158,10 @@ sortByDistance -> Array SearchResult sortByDistance typeQuery = Array.sortWith (map (penalty typeQuery) <<< typeOfResult) + + +packageInfoToString :: PackageInfo -> String +packageInfoToString (Package (PackageName p)) = p +packageInfoToString Builtin = "" +packageInfoToString LocalPackage = "" +packageInfoToString UnknownPackage = "" diff --git a/src/Docs/Search/Interactive.purs b/src/Docs/Search/Interactive.purs index 771fd7f..40c62b4 100644 --- a/src/Docs/Search/Interactive.purs +++ b/src/Docs/Search/Interactive.purs @@ -3,7 +3,7 @@ module Docs.Search.Interactive where import Docs.Search.Declarations (Declarations, mkDeclarations) import Docs.Search.DocsJson (DataDeclType(..)) -import Docs.Search.Engine (mkEngineState, Result(..)) +import Docs.Search.Engine (mkEngineState, packageInfoToString, Result(..)) import Docs.Search.Engine as Engine import Docs.Search.Extra (listToString, stringToList, (>#>)) import Docs.Search.IndexBuilder as IndexBuilder @@ -16,7 +16,7 @@ import Docs.Search.Terminal (bold, cyan, green, yellow) import Docs.Search.TypeDecoder (Constraint, FunDeps, Kind, QualifiedName, Type, TypeArgument) import Docs.Search.TypeIndex (resultsWithTypes) import Docs.Search.TypePrinter (keyword, showConstraint, showFunDeps, showKind, showType, showTypeArgument, space, syntax) -import Docs.Search.Types (PackageName, ModuleName, PackageInfo, packageInfoToString) +import Docs.Search.Types (ModuleName, PackageInfo, Identifier) import Prelude @@ -150,19 +150,19 @@ showModuleResult { name, package } = bold (cyan "module") <> " " <> bold (green $ unwrap name) -showSignature :: - forall rest. - { name :: String - , moduleName :: ModuleName - , packageInfo :: PackageInfo - , info :: ResultInfo - | rest - } +showSignature + :: forall rest + . { name :: Identifier + , moduleName :: ModuleName + , packageInfo :: PackageInfo + , info :: ResultInfo + | rest + } -> String showSignature result@{ name, info } = case info of ValueResult { type: ty } -> - yellow name <> syntax " :: " <> showType ty + yellow (unwrap name) <> syntax " :: " <> showType ty TypeClassResult info' -> showTypeClassSignature info' result @@ -180,9 +180,9 @@ showSignature result@{ name, info } = showExternDataSignature info' result ValueAliasResult -> - yellow ("(" <> name <> ")") + yellow ("(" <> unwrap name <> ")") - _ -> yellow name + _ -> yellow $ unwrap name showTypeClassSignature @@ -191,7 +191,7 @@ showTypeClassSignature , arguments :: Array TypeArgument , superclasses :: Array Constraint } - -> { name :: String, moduleName :: ModuleName | rest } + -> { name :: Identifier, moduleName :: ModuleName | rest } -> String showTypeClassSignature { fundeps, arguments, superclasses } { name, moduleName } = @@ -210,7 +210,7 @@ showTypeClassSignature { fundeps, arguments, superclasses } { name, moduleName } syntax "<=" ) <> space <> - yellow name <> + yellow (unwrap name) <> space <> ( Array.intercalate space $ arguments <#> showTypeArgument @@ -221,14 +221,14 @@ showTypeClassSignature { fundeps, arguments, superclasses } { name, moduleName } showTypeClassMemberSignature :: forall rest - . { type :: Type + . { "type" :: Type , typeClass :: QualifiedName , typeClassArguments :: Array TypeArgument } - -> { name :: String | rest } + -> { name :: Identifier | rest } -> String -showTypeClassMemberSignature { type: ty, typeClass, typeClassArguments } result = - yellow result.name <> +showTypeClassMemberSignature { "type": ty, typeClass, typeClassArguments } result = + yellow (unwrap result.name) <> syntax " :: " <> showType ty @@ -237,7 +237,7 @@ showDataSignature :: forall rest . { typeArguments :: Array TypeArgument , dataDeclType :: DataDeclType } - -> { name :: String | rest } + -> { name :: Identifier | rest } -> String showDataSignature { typeArguments, dataDeclType } { name } = ( keyword @@ -246,7 +246,7 @@ showDataSignature { typeArguments, dataDeclType } { name } = DataDataDecl -> "data" ) <> space <> - yellow name <> + yellow (unwrap name) <> space <> ( Array.intercalate space $ typeArguments <#> showTypeArgument @@ -258,12 +258,12 @@ showTypeSynonymSignature . { type :: Type , arguments :: Array TypeArgument } - -> { name :: String | rest } + -> { name :: Identifier | rest } -> String showTypeSynonymSignature { type: ty, arguments } { name } = keyword "type" <> space <> - yellow name <> + yellow (unwrap name) <> space <> ( Array.intercalate space $ arguments <#> showTypeArgument @@ -277,12 +277,12 @@ showTypeSynonymSignature { type: ty, arguments } { name } = showExternDataSignature :: forall rest . { kind :: Kind } - -> { name :: String | rest } + -> { name :: Identifier | rest } -> String showExternDataSignature { kind } { name } = keyword "foreign data" <> space <> - yellow name <> + yellow (unwrap name) <> space <> syntax " :: " <> showKind kind diff --git a/src/Docs/Search/ModuleIndex.purs b/src/Docs/Search/ModuleIndex.purs index b057b96..81906cd 100644 --- a/src/Docs/Search/ModuleIndex.purs +++ b/src/Docs/Search/ModuleIndex.purs @@ -3,9 +3,9 @@ module Docs.Search.ModuleIndex where import Docs.Search.Config (config) import Docs.Search.Declarations (Declarations(..)) import Docs.Search.SearchResult (SearchResult(..)) -import Docs.Search.Types (ModuleName, PackageName, PackageInfo(..)) +import Docs.Search.Types (ModuleName, PackageName, PackageInfo(..), PackageScore) import Docs.Search.Extra (stringToList) -import Docs.Search.Score (Scores) +import Docs.Search.Score (Scores, getPackageScoreForPackageName) import Prelude @@ -50,7 +50,7 @@ type ModuleIndex = { packageModules :: Map PackageName (Set ModuleName) type ModuleResult = { name :: ModuleName , package :: PackageName - , score :: Int + , score :: PackageScore } @@ -86,7 +86,8 @@ queryModuleIndex scores { index, modulePackages } query = Array.nub <#> (\name -> do package <- Map.lookup name modulePackages - pure { name, package, score: fromMaybe 0 $ Map.lookup package scores }) # + pure { name, package + , score: getPackageScoreForPackageName scores package }) # Array.catMaybes diff --git a/src/Docs/Search/PackageIndex.purs b/src/Docs/Search/PackageIndex.purs index 8f3685a..dddbd71 100644 --- a/src/Docs/Search/PackageIndex.purs +++ b/src/Docs/Search/PackageIndex.purs @@ -3,7 +3,7 @@ module Docs.Search.PackageIndex where import Docs.Search.Config (config) import Docs.Search.Extra (stringToList) import Docs.Search.Score (Scores, getPackageScoreForPackageName, normalizePackageName) -import Docs.Search.Types (PackageName, RawPackageName(..)) +import Docs.Search.Types (PackageName, RawPackageName(..), PackageScore) import Prelude @@ -26,7 +26,7 @@ import Web.Bower.PackageMeta (PackageMeta(..)) type PackageResult = { name :: PackageName , description :: Maybe String - , score :: Int + , score :: PackageScore , dependencies :: Array PackageName , repository :: Maybe String } diff --git a/src/Docs/Search/Score.purs b/src/Docs/Search/Score.purs index 03e276d..633cea1 100644 --- a/src/Docs/Search/Score.purs +++ b/src/Docs/Search/Score.purs @@ -1,6 +1,6 @@ module Docs.Search.Score where -import Docs.Search.Types (RawPackageName(..), PackageName(..), PackageInfo(..)) +import Docs.Search.Types (RawPackageName(..), PackageName(..), PackageInfo(..), PackageScore(..)) import Prelude @@ -13,7 +13,7 @@ import Data.String.CodeUnits as String import Web.Bower.PackageMeta (Dependencies, PackageMeta) -type Scores = Map PackageName Int +type Scores = Map PackageName PackageScore normalizePackageName :: RawPackageName -> PackageName normalizePackageName (RawPackageName p) = @@ -35,18 +35,18 @@ mkScores = updateScoresFor :: Dependencies -> Scores -> Scores updateScoresFor deps scores = Array.foldr - (\dep -> Map.insertWith add dep 1) + (\dep -> Map.insertWith add dep one) scores (deps # unwrap >>> map (_.packageName >>> RawPackageName >>> normalizePackageName)) -getPackageScore :: Scores -> PackageInfo -> Int +getPackageScore :: Scores -> PackageInfo -> PackageScore getPackageScore scores = case _ of Package p -> getPackageScoreForPackageName scores p - Builtin -> 100000 - LocalPackage -> 200000 - UnknownPackage -> 0 + Builtin -> PackageScore 100000 + LocalPackage -> PackageScore 200000 + UnknownPackage -> zero -getPackageScoreForPackageName :: Scores -> PackageName -> Int -getPackageScoreForPackageName scores p = fromMaybe 0 $ Map.lookup p scores +getPackageScoreForPackageName :: Scores -> PackageName -> PackageScore +getPackageScoreForPackageName scores p = fromMaybe zero $ Map.lookup p scores diff --git a/src/Docs/Search/SearchResult.purs b/src/Docs/Search/SearchResult.purs index 8d5af24..11ff65d 100644 --- a/src/Docs/Search/SearchResult.purs +++ b/src/Docs/Search/SearchResult.purs @@ -2,7 +2,7 @@ module Docs.Search.SearchResult where import Docs.Search.DocsJson (DataDeclType) import Docs.Search.TypeDecoder (Constraint, FunDeps, Kind, QualifiedName, Type, TypeArgument) -import Docs.Search.Types (PackageName, PackageInfo, ModuleName) +import Docs.Search.Types (ModuleName, PackageInfo, Identifier, PackageScore) import Prelude @@ -57,12 +57,12 @@ typeOf _ = Nothing -- | Common metadata for all types of search results. newtype SearchResult = SearchResult - { name :: String + { name :: Identifier , comments :: Maybe String , hashAnchor :: String , moduleName :: ModuleName , packageInfo :: PackageInfo - , score :: Int + , score :: PackageScore , sourceSpan :: Maybe { start :: Array Int , end :: Array Int , name :: String diff --git a/src/Docs/Search/TypeDecoder.purs b/src/Docs/Search/TypeDecoder.purs index 89f2138..c49ad71 100644 --- a/src/Docs/Search/TypeDecoder.purs +++ b/src/Docs/Search/TypeDecoder.purs @@ -1,5 +1,7 @@ module Docs.Search.TypeDecoder where +import Docs.Search.Types (Identifier) + import Prelude import Control.Alt ((<|>)) @@ -26,7 +28,7 @@ instance showQualifiedName :: Show QualifiedName where newtype QualifiedName = QualifiedName { moduleNameParts :: Array String - , name :: String + , name :: Identifier } instance decodeJsonQualifiedName :: DecodeJson QualifiedName where @@ -149,7 +151,7 @@ data Type -- | An empty row | REmpty -- | A non-empty row - | RCons String Type Type + | RCons Identifier Type Type {- -- | A type with a kind annotation | Kinded Type Kind @@ -412,7 +414,7 @@ joinForAlls ty = go Nil ty go ({ name, mbKind } : acc) ty' go acc ty' = { binders: acc, ty: ty' } -joinRows :: Type -> { rows :: List { row :: String +joinRows :: Type -> { rows :: List { row :: Identifier , ty :: Type } , ty :: Maybe Type } @@ -428,7 +430,7 @@ joinRows = go Nil } -- | Only returns a list of type class names (lists of arguments are omitted). -joinConstraints :: Type -> { constraints :: List String +joinConstraints :: Type -> { constraints :: List Identifier , ty :: Type } joinConstraints = go Nil where diff --git a/src/Docs/Search/TypePrinter.purs b/src/Docs/Search/TypePrinter.purs index b98867c..34c8ae9 100644 --- a/src/Docs/Search/TypePrinter.purs +++ b/src/Docs/Search/TypePrinter.purs @@ -5,10 +5,12 @@ import Prelude import Docs.Search.Extra ((>#>)) import Docs.Search.Terminal (cyan) import Docs.Search.TypeDecoder (Constraint(..), FunDep(..), FunDeps(..), Kind(..), QualifiedName(..), Type(..), TypeArgument(..), joinForAlls, joinRows) +import Docs.Search.Types (Identifier(..)) -import Data.Maybe (Maybe(..), fromMaybe) import Data.Array as Array import Data.List as List +import Data.Maybe (Maybe(..), fromMaybe) +import Data.Newtype (unwrap) -- | A pretty-printer for types, for TTY with colors. @@ -22,13 +24,13 @@ showType = case _ of TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: [ "Prim" ] - , name: "Function" })) + , name: Identifier "Function" })) t1) t2 -> showType t1 <> syntax " -> " <> showType t2 TypeApp (TypeConstructor (QualifiedName { moduleNameParts: [ "Prim" ] - , name: "Record" })) + , name: Identifier "Record" })) row -> showRow false row @@ -89,7 +91,7 @@ showQualifiedName :: QualifiedName -> String showQualifiedName (QualifiedName { name }) - = name + = unwrap name showRow @@ -107,7 +109,7 @@ showRow asRow = opening <> ( Array.intercalate ", " $ Array.fromFoldable $ rows <#> \entry -> - entry.row <> syntax " :: " <> showType entry.ty + unwrap entry.row <> syntax " :: " <> showType entry.ty ) <> (ty >#> \ty' -> " | " <> showType ty') <> diff --git a/src/Docs/Search/TypeQuery.purs b/src/Docs/Search/TypeQuery.purs index 1c170b0..a52c4c3 100644 --- a/src/Docs/Search/TypeQuery.purs +++ b/src/Docs/Search/TypeQuery.purs @@ -13,6 +13,7 @@ where import Docs.Search.Config (config) import Docs.Search.Extra (foldl1, foldr1) import Docs.Search.TypeDecoder (QualifiedName(..), Type(..), joinConstraints, joinRows) +import Docs.Search.Types (Identifier(..)) import Prelude @@ -41,13 +42,13 @@ import Text.Parsing.StringParser.Combinators (fix, sepBy, sepBy1, sepEndBy, sepE -- | We need type queries because we don't have a full-featured type parser -- | available. data TypeQuery - = QVar String - | QConst String + = QVar Identifier + | QConst Identifier | QFun TypeQuery TypeQuery | QApp TypeQuery TypeQuery - | QForAll (NonEmptyList String) TypeQuery - | QConstraint String (List TypeQuery) TypeQuery - | QRow (List (Tuple String TypeQuery)) + | QForAll (NonEmptyList Identifier) TypeQuery + | QConstraint Identifier (List TypeQuery) TypeQuery + | QRow (List (Tuple Identifier TypeQuery)) derive instance eqTypeQuery :: Eq TypeQuery derive instance genericTypeQuery :: Generic TypeQuery _ @@ -69,7 +70,8 @@ typeQueryParser = fix \typeQuery -> row = string "(" *> rowFields <* string ")" - record = QApp (QConst "Record") <$> (string "{" *> rowFields <* string "}") + record = QApp (QConst $ Identifier "Record") <$> + (string "{" *> rowFields <* string "}") binders = string "forall" *> some space *> sepEndBy1 ident skipSpaces <* string "." <* skipSpaces @@ -111,32 +113,32 @@ concrete :: Parser TypeQuery concrete = QConst <$> upperCaseIdent -ident :: Parser String +ident :: Parser Identifier ident = do head <- anyLetter rest <- Array.many (alphaNum <|> char '\'') - pure $ fromCharArray $ pure head <> rest + pure $ Identifier <$> fromCharArray $ pure head <> rest -upperCaseIdent :: Parser String +upperCaseIdent :: Parser Identifier upperCaseIdent = do head <- upperCaseChar rest <- Array.many (alphaNum <|> char '\'') - pure $ fromCharArray $ pure head <> rest + pure $ Identifier $ fromCharArray $ pure head <> rest -lowerCaseIdent :: Parser String +lowerCaseIdent :: Parser Identifier lowerCaseIdent = do head <- lowerCaseChar rest <- Array.many (alphaNum <|> char '\'') - pure $ fromCharArray $ pure head <> rest + pure $ Identifier $ fromCharArray $ pure head <> rest space :: Parser Char space = char ' ' -- | Used only in `getFreeVariables`. -data FreeVarCounterQueueEntry = Unbind (Set.Set String) | Next TypeQuery +data FreeVarCounterQueueEntry = Unbind (Set.Set Identifier) | Next TypeQuery -getFreeVariables :: TypeQuery -> Set.Set String +getFreeVariables :: TypeQuery -> Set.Set Identifier getFreeVariables query = go Set.empty Set.empty (List.singleton $ Next query) where insertIfUnbound bound var free = @@ -172,11 +174,11 @@ getFreeVariables query = go Set.empty Set.empty (List.singleton $ Next query) data Substitution - = Instantiate String Type - | Match String String - | Generalize TypeQuery String - | Substitute String String - | MatchConstraints (Set String) (Set String) + = Instantiate Identifier Type + | Match Identifier Identifier + | Generalize TypeQuery Identifier + | Substitute Identifier Identifier + | MatchConstraints (Set Identifier) (Set Identifier) | MissingConstraint | ExcessiveConstraint | RowsMismatch Int Int @@ -225,9 +227,9 @@ unify query type_ = go Nil (List.singleton { q: query, t: type_ }) -- * Type variables go acc ({ q: QVar q, t: TypeVar v } : rest) = - go (Substitute q v : acc) rest + go (Substitute q (Identifier v) : acc) rest go acc ({ q, t: TypeVar v } : rest ) = - go (Generalize q v : acc) rest + go (Generalize q (Identifier v) : acc) rest go acc ({ q: QVar v, t } : rest) = go (Instantiate v t : acc) rest @@ -249,16 +251,16 @@ unify query type_ = go Nil (List.singleton { q: query, t: type_ }) go acc ({ q: QFun q1 q2 , t: TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: [ "Prim" ] - , name: "Function" })) t1) t2 } : rest) = + , name: Identifier "Function" })) t1) t2 } : rest) = go acc ({ q: q1, t: t1 } : { q: q2, t: t2 } : rest) go acc ({ q: q@(QFun q1 q2), t } : rest) = go (Mismatch q t : acc) rest -- * Rows - go acc ({ q: QApp (QConst "Record") (QRow qRows) + go acc ({ q: QApp (QConst (Identifier "Record")) (QRow qRows) , t: TypeApp (TypeConstructor (QualifiedName { moduleNameParts: [ "Prim" ] - , name: "Record" })) row } : rest) = + , name: Identifier "Record" })) row } : rest) = let { rows, ty } = joinRows row qRowsLength = List.length qRows rowsLength = List.length rows in @@ -319,8 +321,11 @@ typeVarPenalty substs = insertion v1 v2 = Map.insertWith append v1 (Set.singleton v2) varSubstMapWith - :: (String -> String -> Map String (Set String) -> Map String (Set String)) - -> Map String (Set String) + :: (Identifier -> + Identifier -> + Map Identifier (Set Identifier) -> + Map Identifier (Set Identifier)) + -> Map Identifier (Set Identifier) varSubstMapWith f = List.foldr (case _ of Substitute v1 v2 -> @@ -363,7 +368,7 @@ mismatchPenalty = go 0 -- | Only returns a list of type class names (lists of arguments are omitted). -joinQueryConstraints :: TypeQuery -> { constraints :: List String +joinQueryConstraints :: TypeQuery -> { constraints :: List Identifier , ty :: TypeQuery } joinQueryConstraints = go Nil where @@ -408,7 +413,7 @@ typeSize = go 0 <<< List.singleton go (n + 1) rest go n (TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: [ "Prim" ] - , name: "Function" })) t1) t2 : rest) = + , name: Identifier "Function" })) t1) t2 : rest) = go (n + 1) (t1 : t2 : rest) go n (TypeApp q1 q2 : rest) = go (n + 1) (q1 : q2 : rest) diff --git a/src/Docs/Search/TypeShape.purs b/src/Docs/Search/TypeShape.purs index 7a38798..fc4b3bf 100644 --- a/src/Docs/Search/TypeShape.purs +++ b/src/Docs/Search/TypeShape.purs @@ -5,6 +5,7 @@ module Docs.Search.TypeShape where import Docs.Search.TypeDecoder (QualifiedName(..), Type(..), joinForAlls, joinRows) import Docs.Search.TypeQuery (TypeQuery(..), getFreeVariables) +import Docs.Search.Types (Identifier(..)) import Prelude @@ -102,7 +103,7 @@ shapeOfType ty = List.reverse $ go (pure ty) Nil go rest (PVar : acc) TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Prim"] - , name: "Function" })) t1) t2 -> + , name: Identifier "Function" })) t1) t2 -> go (t1 : t2 : rest) (PFun : acc) TypeConstructor (QualifiedName { name }) -> diff --git a/src/Docs/Search/Types.purs b/src/Docs/Search/Types.purs index 9c1b979..deacb07 100644 --- a/src/Docs/Search/Types.purs +++ b/src/Docs/Search/Types.purs @@ -11,6 +11,17 @@ import Data.Generic.Rep.Show (genericShow) import Data.Newtype (class Newtype) +newtype Identifier = Identifier String + +derive instance newtypeIdentifier :: Newtype Identifier _ +derive instance genericIdentifier :: Generic Identifier _ +derive newtype instance eqIdentifier :: Eq Identifier +derive newtype instance ordIdentifier :: Ord Identifier +derive newtype instance showIdentifier :: Show Identifier +derive newtype instance decodeJsonIdentifier :: DecodeJson Identifier +derive newtype instance encodeJsonIdentifier :: EncodeJson Identifier + + newtype ModuleName = ModuleName String derive instance newtypeModuleName :: Newtype ModuleName _ @@ -52,8 +63,14 @@ instance decodeJsonPackageInfo :: DecodeJson PackageInfo where instance encodeJsonPackageInfo :: EncodeJson PackageInfo where encodeJson = genericEncodeJson -packageInfoToString :: PackageInfo -> String -packageInfoToString (Package (PackageName p)) = p -packageInfoToString Builtin = "" -packageInfoToString LocalPackage = "" -packageInfoToString UnknownPackage = "" +newtype PackageScore = PackageScore Int + +derive instance newtypePackageScore :: Newtype PackageScore _ +derive instance genericPackageScore :: Generic PackageScore _ +derive newtype instance eqPackageScore :: Eq PackageScore +derive newtype instance ordPackageScore :: Ord PackageScore +derive newtype instance semiringPackageScore :: Semiring PackageScore +derive newtype instance ringPackageScore :: Ring PackageScore +derive newtype instance showPackageScore :: Show PackageScore +derive newtype instance decodeJsonPackageScore :: DecodeJson PackageScore +derive newtype instance encodeJsonPackageScore :: EncodeJson PackageScore diff --git a/test/Main.purs b/test/Main.purs index fa4c96b..695744a 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -3,6 +3,7 @@ module Test.Main where import Prelude import Docs.Search.TypeDecoder (Constraint(..), FunDep(..), FunDeps(..), Kind(..), QualifiedName(..), Type(..)) +import Docs.Search.Types import Test.TypeQuery as TypeQuery import Test.IndexBuilder as IndexBuilder import Test.Declarations as Declarations @@ -67,10 +68,7 @@ mainTest = do """ assertRight (decodeJson qualifiedName) - (QualifiedName { moduleNameParts: ["Prim"] - , name: "Type" - } - ) + (qualified ["Prim"] "Type") test "NamedKind" do let namedKind = mkJson """ @@ -87,10 +85,7 @@ mainTest = do """ assertRight (decodeJson namedKind) - (NamedKind $ QualifiedName { moduleNameParts: ["Prim"] - , name: "Type" - } - ) + (NamedKind $ qualified ["Prim"] "Type") test "Row" do let row = mkJson """ @@ -110,11 +105,7 @@ mainTest = do } """ - assertRight (decodeJson row) - (Row $ NamedKind $ QualifiedName { moduleNameParts: ["Prim"] - , name: "Type" - } - ) + assertRight (decodeJson row) (Row $ NamedKind $ qualified ["Prim"] "Type") test "FunKind" do let funKind = mkJson """ @@ -154,14 +145,8 @@ mainTest = do } """ assertRight (decodeJson funKind) - (FunKind (Row $ NamedKind $ QualifiedName { moduleNameParts: ["Prim"] - , name: "Type" - } - ) - (Row $ NamedKind $ QualifiedName { moduleNameParts: ["Prim"] - , name: "Type" - } - ) + (FunKind (Row $ NamedKind $ qualified ["Prim"] "Type") + (Row $ NamedKind $ qualified ["Prim"] "Type") ) @@ -181,9 +166,7 @@ mainTest = do } """ assertRight (decodeJson constraint) - (Constraint { constraintClass: QualifiedName { moduleNameParts: ["Prim"] - , name: "Partial" - } + (Constraint { constraintClass: qualified ["Prim"] "Partial" , constraintArgs: [] }) @@ -230,14 +213,12 @@ mainTest = do assertRight (decodeJson typeApp1) $ TypeApp - (TypeConstructor (QualifiedName { moduleNameParts: - [ "Control" - , "Monad" - , "ST" - , "Internal" - ], - name: "ST" - } + (TypeConstructor (qualified [ "Control" + , "Monad" + , "ST" + , "Internal" + ] + "ST" )) (TypeVar "h") @@ -256,9 +237,7 @@ mainTest = do } """ assertRight (decodeJson typeOp) $ - TypeOp $ QualifiedName { moduleNameParts: [ "Data", "NaturalTransformation" ] - , name: "~>" - } + TypeOp $ qualified [ "Data", "NaturalTransformation" ] "~>" test "BinaryNoParens" do let binaryNoParens = mkJson """ @@ -293,7 +272,7 @@ mainTest = do assertRight (decodeJson binaryNoParens) $ BinaryNoParensType - (TypeOp $ QualifiedName { moduleNameParts: ["Data", "NaturalTransformation"], name: "~>" }) + (TypeOp $ qualified ["Data", "NaturalTransformation"] "~>") (TypeVar "m") (TypeVar "n") @@ -335,19 +314,8 @@ mainTest = do assertRight (decodeJson parensInType) $ ParensInType $ TypeApp - (TypeConstructor (QualifiedName { moduleNameParts: - [ "Data" - , "Maybe" - ], - name: "Maybe" - } - )) - (TypeConstructor (QualifiedName { moduleNameParts: - [ "Prim" - ], - name: "String" - } - )) + (TypeConstructor (qualified [ "Data", "Maybe" ] "Maybe")) + (TypeConstructor (qualified [ "Prim" ] "String")) test "RCons" do let rcons = mkJson """ @@ -388,8 +356,8 @@ mainTest = do assertRight (decodeJson rcons) $ RCons - "tail" - (TypeApp (TypeConstructor $ QualifiedName { moduleNameParts: [ "Data", "Symbol" ], name: "SProxy" }) + (Identifier "tail") + (TypeApp (TypeConstructor $ qualified [ "Data", "Symbol" ] "SProxy") (TypeVar "t")) REmpty @@ -545,8 +513,8 @@ mainTest = do """ assertRight (decodeJson forallJson) $ ForAll "f" - (Just (FunKind (NamedKind (QualifiedName { moduleNameParts: ["Prim","RowList"], name: "RowList" })) (NamedKind (QualifiedName { moduleNameParts: ["Prim"], name: "Type" })))) - (TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Prim"], name: "Function" })) (TypeApp (TypeVar "f") (TypeVar "l"))) (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Data","List","Types"], name: "List" })) (ParensInType (TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Data","Tuple"], name: "Tuple" })) (TypeConstructor (QualifiedName { moduleNameParts: ["Prim"], name: "String" }))) (TypeConstructor (QualifiedName { moduleNameParts: ["Prim"], name: "String" })))))) + (Just (FunKind (NamedKind (QualifiedName { moduleNameParts: ["Prim","RowList"], name: Identifier "RowList" })) (NamedKind (QualifiedName { moduleNameParts: ["Prim"], name: Identifier "Type" })))) + (TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Prim"], name: Identifier "Function" })) (TypeApp (TypeVar "f") (TypeVar "l"))) (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Data","List","Types"], name: Identifier "List" })) (ParensInType (TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Data","Tuple"], name: Identifier "Tuple" })) (TypeConstructor (QualifiedName { moduleNameParts: ["Prim"], name: Identifier "String" }))) (TypeConstructor (QualifiedName { moduleNameParts: ["Prim"], name: Identifier "String" })))))) @@ -558,7 +526,7 @@ mainTest = do {"annotation":[],"tag":"ForAll","contents":["o",{"annotation":[],"tag":"ForAll","contents":["r",{"annotation":[],"tag":"ForAll","contents":["l",{"annotation":[],"tag":"ConstrainedType","contents":[{"constraintAnn":[],"constraintClass":[["Type","Data","Boolean"],"And"],"constraintArgs":[{"annotation":[],"tag":"TypeVar","contents":"l"},{"annotation":[],"tag":"TypeVar","contents":"r"},{"annotation":[],"tag":"TypeVar","contents":"o"}],"constraintData":null},{"annotation":[],"tag":"TypeApp","contents":[{"annotation":[],"tag":"TypeApp","contents":[{"annotation":[],"tag":"TypeConstructor","contents":[["Prim"],"Function"]},{"annotation":[],"tag":"TypeApp","contents":[{"annotation":[],"tag":"TypeConstructor","contents":[["Type","Data","Boolean"],"BProxy"]},{"annotation":[],"tag":"TypeVar","contents":"l"}]}]},{"annotation":[],"tag":"TypeApp","contents":[{"annotation":[],"tag":"TypeApp","contents":[{"annotation":[],"tag":"TypeConstructor","contents":[["Prim"],"Function"]},{"annotation":[],"tag":"TypeApp","contents":[{"annotation":[],"tag":"TypeConstructor","contents":[["Type","Data","Boolean"],"BProxy"]},{"annotation":[],"tag":"TypeVar","contents":"r"}]}]},{"annotation":[],"tag":"TypeApp","contents":[{"annotation":[],"tag":"TypeConstructor","contents":[["Type","Data","Boolean"],"BProxy"]},{"annotation":[],"tag":"TypeVar","contents":"o"}]}]}]}]},null]},null]},null]} """ - assertRight (decodeJson json) $ (ForAll "o" Nothing (ForAll "r" Nothing (ForAll "l" Nothing (ConstrainedType (Constraint { constraintArgs: [(TypeVar "l"),(TypeVar "r"),(TypeVar "o")], constraintClass: (QualifiedName { moduleNameParts: ["Type","Data","Boolean"], name: "And" }) }) (TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Prim"], name: "Function" })) (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Type","Data","Boolean"], name: "BProxy" })) (TypeVar "l"))) (TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Prim"], name: "Function" })) (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Type","Data","Boolean"], name: "BProxy" })) (TypeVar "r"))) (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Type","Data","Boolean"], name: "BProxy" })) (TypeVar "o")))))))) + assertRight (decodeJson json) $ (ForAll "o" Nothing (ForAll "r" Nothing (ForAll "l" Nothing (ConstrainedType (Constraint { constraintArgs: [(TypeVar "l"),(TypeVar "r"),(TypeVar "o")], constraintClass: (QualifiedName { moduleNameParts: ["Type","Data","Boolean"], name: Identifier "And" }) }) (TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Prim"], name: Identifier "Function" })) (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Type","Data","Boolean"], name: Identifier "BProxy" })) (TypeVar "l"))) (TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Prim"], name: Identifier "Function" })) (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Type","Data","Boolean"], name: Identifier "BProxy" })) (TypeVar "r"))) (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Type","Data","Boolean"], name: Identifier "BProxy" })) (TypeVar "o")))))))) suite "Kind encoder" do test "FunKind" do @@ -570,4 +538,4 @@ mainTest = do qualified :: Array String -> String -> QualifiedName -qualified moduleNameParts name = QualifiedName { moduleNameParts, name } +qualified moduleNameParts name = QualifiedName { moduleNameParts, name: Identifier name } diff --git a/test/Test/TypeQuery.purs b/test/Test/TypeQuery.purs index f508269..540acf2 100644 --- a/test/Test/TypeQuery.purs +++ b/test/Test/TypeQuery.purs @@ -3,6 +3,7 @@ module Test.TypeQuery where import Docs.Search.TypeDecoder (Constraint(..), QualifiedName(..), Type(..)) import Docs.Search.TypeQuery (Substitution(..), TypeQuery(..), getFreeVariables, parseTypeQuery, penalty, typeVarPenalty) import Docs.Search.TypeShape (ShapeChunk(..), shapeOfType, shapeOfTypeQuery) +import Docs.Search.Types (Identifier(..)) import Prelude @@ -25,193 +26,195 @@ tests = do test "test #0" do let input = "a" - assertRight (parseTypeQuery input) (QVar "a") + assertRight (parseTypeQuery input) (qVar "a") test "test #1" do let input = "ab" - assertRight (parseTypeQuery input) (QVar "ab") + assertRight (parseTypeQuery input) (qVar "ab") test "test #2" do let input = "a b" - assertRight (parseTypeQuery input) (QApp (QVar "a") (QVar "b")) + assertRight (parseTypeQuery input) (QApp (qVar "a") (qVar "b")) test "test #3" do let input = "a b c" - assertRight (parseTypeQuery input) (QApp (QApp (QVar "a") (QVar "b")) (QVar "c")) + assertRight (parseTypeQuery input) (QApp (QApp (qVar "a") (qVar "b")) (qVar "c")) test "test #4" do let input = "a -> b" - assertRight (parseTypeQuery input) (QFun (QVar "a") (QVar "b")) + assertRight (parseTypeQuery input) (QFun (qVar "a") (qVar "b")) test "test #5" do let input = "a -> b c" - assertRight (parseTypeQuery input) (QFun (QVar "a") (QApp (QVar "b") (QVar "c"))) + assertRight (parseTypeQuery input) (QFun (qVar "a") (QApp (qVar "b") (qVar "c"))) test "test #6" do let input = "a b -> c" - assertRight (parseTypeQuery input) (QFun (QApp (QVar "a") (QVar "b")) (QVar "c")) + assertRight (parseTypeQuery input) (QFun (QApp (qVar "a") (qVar "b")) (qVar "c")) test "test #7" do let input = "a b" - assertRight (parseTypeQuery input) (QApp (QVar "a") (QVar "b")) + assertRight (parseTypeQuery input) (QApp (qVar "a") (qVar "b")) test "test #8" do let input = "a (b c)" - assertRight (parseTypeQuery input) (QApp (QVar "a") (QApp (QVar "b") (QVar "c"))) + assertRight (parseTypeQuery input) (QApp (qVar "a") (QApp (qVar "b") (qVar "c"))) test "test #9" do let input = "(a b) (c d)" assertRight (parseTypeQuery input) - (QApp (QApp (QVar "a") (QVar "b")) - (QApp (QVar "c") (QVar "d"))) + (QApp (QApp (qVar "a") (qVar "b")) + (QApp (qVar "c") (qVar "d"))) test "test #10" do let input = "a ( b c )" - assertRight (parseTypeQuery input) (QApp (QVar "a") (QApp (QVar "b") (QVar "c"))) + assertRight (parseTypeQuery input) (QApp (qVar "a") (QApp (qVar "b") (qVar "c"))) test "test #11" do let input = "aaa" - assertRight (parseTypeQuery input) (QVar "aaa") + assertRight (parseTypeQuery input) (qVar "aaa") test "test #12" do let input = "aaa ( bbb ccc )" - assertRight (parseTypeQuery input) (QApp (QVar "aaa") (QApp (QVar "bbb") (QVar "ccc"))) + assertRight (parseTypeQuery input) (QApp (qVar "aaa") (QApp (qVar "bbb") (qVar "ccc"))) test "test #13" do let input = "(a -> b) -> (c -> d)" - assertRight (parseTypeQuery input) (QFun (QFun (QVar "a") (QVar "b")) - (QFun (QVar "c") (QVar "d"))) + assertRight (parseTypeQuery input) (QFun (QFun (qVar "a") (qVar "b")) + (QFun (qVar "c") (qVar "d"))) test "test #14" do let input = "a -> b -> c -> d" - assertRight (parseTypeQuery input) (QFun (QVar "a") - (QFun (QVar "b") - (QFun (QVar "c") (QVar "d")))) + assertRight (parseTypeQuery input) (QFun (qVar "a") + (QFun (qVar "b") + (QFun (qVar "c") (qVar "d")))) test "test #15" do let input = "a -> b -> c" - assertRight (parseTypeQuery input) (QFun (QVar "a") - (QFun (QVar "b") - (QVar "c"))) + assertRight (parseTypeQuery input) (QFun (qVar "a") + (QFun (qVar "b") + (qVar "c"))) test "test #16" do let input = "forall a b c. c" - assertRight (parseTypeQuery input) (QForAll (nl "a" ["b", "c"]) (QVar "c")) + assertRight (parseTypeQuery input) (QForAll (nl "a" ["b", "c"]) (qVar "c")) test "test #17" do let input = "forall a. Maybe a" - assertRight (parseTypeQuery input) (QForAll (nl "a" $ []) (QApp (QConst "Maybe") (QVar "a"))) + assertRight (parseTypeQuery input) (QForAll (nl "a" $ []) (QApp (qConst "Maybe") (qVar "a"))) test "test #18" do let input = "forall m a. Monad m => a -> m a" assertRight (parseTypeQuery input) (QForAll (nl "m" ["a"]) - (QConstraint "Monad" (l [QVar "m"]) - (QFun (QVar "a") - (QApp (QVar "m") (QVar "a"))))) + (qConstraint "Monad" (l [qVar "m"]) + (QFun (qVar "a") + (QApp (qVar "m") (qVar "a"))))) test "test #19" do let input = "{ a :: Int }" assertRight (parseTypeQuery input) - (QApp (QConst "Record") (QRow (pure (Tuple "a" (QConst "Int"))))) + (QApp (qConst "Record") (QRow (pure (Tuple (Identifier "a") (qConst "Int"))))) test "test #20" do let input = "{a::Int}" assertRight (parseTypeQuery input) - (QApp (QConst "Record") (QRow (pure (Tuple "a" (QConst "Int"))))) + (QApp (qConst "Record") (QRow (pure (Tuple (Identifier "a") (qConst "Int"))))) test "test #21" do let input = "Int" - assertRight (parseTypeQuery input) (QConst "Int") + assertRight (parseTypeQuery input) (qConst "Int") test "test #22" do let input = "a->b" - assertRight (parseTypeQuery input) (QFun (QVar "a") (QVar "b")) + assertRight (parseTypeQuery input) (QFun (qVar "a") (qVar "b")) test "test #23" do let input = "forall m a. MonadRec m => Process m a -> m a" assertRight (parseTypeQuery input) (QForAll (nl "m" ("a" : Nil)) - (QConstraint "MonadRec" (l [QVar "m"]) - (QFun (QApp (QApp (QConst "Process") - (QVar "m")) (QVar "a")) - (QApp (QVar "m") (QVar "a"))))) + (qConstraint "MonadRec" (l [qVar "m"]) + (QFun (QApp (QApp (qConst "Process") + (qVar "m")) (qVar "a")) + (QApp (qVar "m") (qVar "a"))))) test "test #24" do let input = "forall t f a. Foldable1 t => Apply f => f" assertRight (parseTypeQuery input) (QForAll (nl "t" ["f", "a"]) - (QConstraint "Foldable1" (l [QVar "t"]) - (QConstraint "Apply" (l [QVar "f"]) (QVar "f")))) + (qConstraint "Foldable1" (l [qVar "t"]) + (qConstraint "Apply" (l [qVar "f"]) (qVar "f")))) test "test #25" do let input = "forall m a.MonadRec m=>Process m a->m a" assertRight (parseTypeQuery input) ((QForAll (nl "m" ("a" : Nil)) - (QConstraint "MonadRec" (l [QVar "m"]) - (QFun (QApp (QApp (QConst "Process") - (QVar "m")) (QVar "a")) - (QApp (QVar "m") (QVar "a")))))) + (qConstraint "MonadRec" (l [qVar "m"]) + (QFun (QApp (QApp (qConst "Process") + (qVar "m")) (qVar "a")) + (QApp (qVar "m") (qVar "a")))))) test "test #26" do let input = "m a -> (a -> m b) -> m b" - assertRight (parseTypeQuery input) (QFun (QApp (QVar "m") (QVar "a")) (QFun (QFun (QVar "a") (QApp (QVar "m") (QVar "b"))) (QApp (QVar "m") (QVar "b")))) + assertRight (parseTypeQuery input) (QFun (QApp (qVar "m") (qVar "a")) (QFun (QFun (qVar "a") (QApp (qVar "m") (qVar "b"))) (QApp (qVar "m") (qVar "b")))) test "test #27" do let input = "forall f a. Alternative f => Lazy (f (List a)) => f a -> f (List a)" assertRight (parseTypeQuery input) ((QForAll (nl "f" ["a"])) - (QConstraint "Alternative" (l [QVar "f"]) - (QConstraint "Lazy" (l [QApp (QVar "f") - (QApp (QConst "List") (QVar "a"))]) - (QFun (QApp (QVar "f") (QVar "a")) - (QApp (QVar "f") - (QApp (QConst "List") (QVar "a"))))))) + (qConstraint "Alternative" (l [qVar "f"]) + (qConstraint "Lazy" (l [QApp (qVar "f") + (QApp (qConst "List") (qVar "a"))]) + (QFun (QApp (qVar "f") (qVar "a")) + (QApp (qVar "f") + (QApp (qConst "List") (qVar "a"))))))) test "test #28" do let input = "forall f a. Alternative f => Lazy(f (List a))=>f a -> f (List a)" assertRight (parseTypeQuery input) ((QForAll (nl "f" ["a"])) - (QConstraint "Alternative" (l [QVar "f"]) - (QConstraint "Lazy" (l [QApp (QVar "f") - (QApp (QConst "List") (QVar "a"))]) - (QFun (QApp (QVar "f") (QVar "a")) - (QApp (QVar "f") - (QApp (QConst "List") (QVar "a"))))))) + (qConstraint "Alternative" (l [qVar "f"]) + (qConstraint "Lazy" (l [QApp (qVar "f") + (QApp (qConst "List") (qVar "a"))]) + (QFun (QApp (qVar "f") (qVar "a")) + (QApp (qVar "f") + (QApp (qConst "List") (qVar "a"))))))) test "test #29" do let input = "{a::Int,b::Int}" assertRight (parseTypeQuery input) - (QApp (QConst "Record") (QRow (List.fromFoldable [ Tuple "a" (QConst "Int"), Tuple "b" (QConst "Int")]))) + (QApp (qConst "Record") (QRow (List.fromFoldable + [ Tuple (Identifier "a") (qConst "Int") + , Tuple (Identifier "b") (qConst "Int")]))) test "test #30" do let input = "{record''' :: Int}" assertRight (parseTypeQuery input) - (QApp (QConst "Record") (QRow (List.fromFoldable [ Tuple "record'''" (QConst "Int")]))) + (QApp (qConst "Record") (QRow (List.fromFoldable [ Tuple (Identifier "record'''") (qConst "Int")]))) test "test #31" do let input = "(row''' :: Int)" assertRight (parseTypeQuery input) - (QRow (List.fromFoldable [ Tuple "row'''" (QConst "Int")])) + (QRow (List.fromFoldable [ Tuple (Identifier "row'''") (qConst "Int")])) test "test #32" do let input = "(row1 :: Int, row2 :: (),row3::(row4::{}))" assertRight (parseTypeQuery input) - (QRow (l [ Tuple "row1" (QConst "Int") - , Tuple "row2" (QRow Nil) - , Tuple "row3" (QRow (l [ Tuple "row4" (QApp (QConst "Record") (QRow Nil)) ])) ])) + (QRow (l [ Tuple (Identifier "row1") (qConst "Int") + , Tuple (Identifier "row2") (QRow Nil) + , Tuple (Identifier "row3") (QRow (l [ Tuple (Identifier "row4") (QApp (qConst "Record") (QRow Nil)) ])) ])) test "test #33" do let input = "Foldable1 t => Apply f => t (f a) -> f Unit" assertRight (parseTypeQuery input) - (QConstraint "Foldable1" ((QVar "t") : Nil) (QConstraint "Apply" ((QVar "f") : Nil) (QFun (QApp (QVar "t") (QApp (QVar "f") (QVar "a"))) (QApp (QVar "f") (QConst "Unit"))))) + (qConstraint "Foldable1" ((qVar "t") : Nil) (qConstraint "Apply" ((qVar "f") : Nil) (QFun (QApp (qVar "t") (QApp (qVar "f") (qVar "a"))) (QApp (qVar "f") (qConst "Unit"))))) test "test #34" do let input = "Foldable1 t => Apply f => t (f a) -> f a" assertRight (parseTypeQuery input) - (QConstraint "Foldable1" ((QVar "t") : Nil) (QConstraint "Apply" ((QVar "f") : Nil) (QFun (QApp (QVar "t") (QApp (QVar "f") (QVar "a"))) (QApp (QVar "f") (QVar "a"))))) + (qConstraint "Foldable1" ((qVar "t") : Nil) (qConstraint "Apply" ((qVar "f") : Nil) (QFun (QApp (qVar "t") (QApp (qVar "f") (qVar "a"))) (QApp (qVar "f") (qVar "a"))))) test "test #35" do let input = "Generic a rep => GenericEq rep => a -> a -> Boolean" assertRight (parseTypeQuery input) - (QConstraint "Generic" ((QVar "a") : (QVar "rep") : Nil) - (QConstraint "GenericEq" ((QVar "rep") : Nil) - (QFun (QVar "a") (QFun (QVar "a") (QConst "Boolean"))))) + (qConstraint "Generic" ((qVar "a") : (qVar "rep") : Nil) + (qConstraint "GenericEq" ((qVar "rep") : Nil) + (QFun (qVar "a") (QFun (qVar "a") (qConst "Boolean"))))) suite "polish notation" do @@ -246,7 +249,7 @@ tests = do fun t1 t2 = TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleNameParts: ["Prim"] - , name: "Function" })) t1) t2 + , name: Identifier "Function" })) t1) t2 type_ = ForAll "a" Nothing $ ForAll "rep" Nothing $ @@ -314,70 +317,70 @@ tests = do Assert.equal 0 (typeVarPenalty mempty) test "#1" do - Assert.equal 0 (typeVarPenalty $ l [ Substitute "a" "b" - , Substitute "b" "a" + Assert.equal 0 (typeVarPenalty $ l [ substitute "a" "b" + , substitute "b" "a" ]) test "#2" do - Assert.equal 0 (typeVarPenalty $ l [ Substitute "a" "b" - , Substitute "a" "b" - , Substitute "a" "b" + Assert.equal 0 (typeVarPenalty $ l [ substitute "a" "b" + , substitute "a" "b" + , substitute "a" "b" ]) test "#3" do - Assert.equal 1 (typeVarPenalty $ l [ Substitute "a" "b" - , Substitute "a" "c" + Assert.equal 1 (typeVarPenalty $ l [ substitute "a" "b" + , substitute "a" "c" ]) test "#4" do - Assert.equal 1 (typeVarPenalty $ l [ Substitute "a" "b" - , Substitute "b" "a" - , Substitute "b" "c" + Assert.equal 1 (typeVarPenalty $ l [ substitute "a" "b" + , substitute "b" "a" + , substitute "b" "c" ]) test "#5" do - Assert.equal 0 (typeVarPenalty $ l [ Substitute "a" "b" - , Substitute "b" "c" - , Substitute "c" "a" + Assert.equal 0 (typeVarPenalty $ l [ substitute "a" "b" + , substitute "b" "c" + , substitute "c" "a" ]) test "#6" do - Assert.equal 2 (typeVarPenalty $ l [ Substitute "a" "b" - , Substitute "a" "c" - , Substitute "a" "a" + Assert.equal 2 (typeVarPenalty $ l [ substitute "a" "b" + , substitute "a" "c" + , substitute "a" "a" ]) test "#7" do - Assert.equal 2 (typeVarPenalty $ l [ Substitute "a" "a" - , Substitute "b" "a" - , Substitute "c" "a" + Assert.equal 2 (typeVarPenalty $ l [ substitute "a" "a" + , substitute "b" "a" + , substitute "c" "a" ]) test "#8" do - Assert.equal 4 (typeVarPenalty $ l [ Substitute "a" "a" - , Substitute "b" "a" - , Substitute "c" "a" - , Substitute "a" "b" - , Substitute "a" "c" - , Substitute "a" "a" + Assert.equal 4 (typeVarPenalty $ l [ substitute "a" "a" + , substitute "b" "a" + , substitute "c" "a" + , substitute "a" "b" + , substitute "a" "c" + , substitute "a" "a" ]) test "#9" do - Assert.equal 0 (typeVarPenalty $ l [ Substitute "a" "e" - , Substitute "b" "d" - , Substitute "c" "f" + Assert.equal 0 (typeVarPenalty $ l [ substitute "a" "e" + , substitute "b" "d" + , substitute "c" "f" ]) suite "unification" do test "instantiation #0" do - let mVarQuery = QVar "m" - unitConstQuery = QConst "Unit" + let mVarQuery = qVar "m" + unitConstQuery = qConst "Unit" Assert.assert "instantiation #0" $ (penalty unitConstQuery unitType < penalty mVarQuery unitType) test "generalization #0" do - let query = QVar "m" + let query = qVar "m" t1 = TypeVar "m" Assert.assert "qeneralization #0" $ @@ -387,19 +390,37 @@ tests = do l :: forall f. Foldable f => (forall a. f a -> List a) l = List.fromFoldable -nl :: forall t5 t6. Foldable t6 => t5 -> t6 t5 -> NonEmptyList t5 -nl x rst = NonEmptyList.cons' x $ List.fromFoldable rst +nl + :: forall t + . Foldable t + => Functor t + => String + -> t String + -> NonEmptyList Identifier +nl x rst = NonEmptyList.cons' (Identifier x) $ List.fromFoldable (rst <#> Identifier) unitType :: Type unitType = TypeConstructor (QualifiedName { moduleNameParts: [] - , name: "Unit" + , name: Identifier "Unit" }) countFreeVars :: TypeQuery -> Int countFreeVars = getFreeVariables >>> Set.size qname :: Array String -> String -> QualifiedName -qname m n = QualifiedName { moduleNameParts: m, name: n } +qname m n = QualifiedName { moduleNameParts: m, name: Identifier n } constr :: QualifiedName -> Array Type -> Constraint constr c a = Constraint { constraintClass: c, constraintArgs: a } + +qVar :: String -> TypeQuery +qVar = QVar <<< Identifier + +qConst :: String -> TypeQuery +qConst = QConst <<< Identifier + +qConstraint :: String -> List TypeQuery -> TypeQuery -> TypeQuery +qConstraint = QConstraint <<< Identifier + +substitute :: String -> String -> Substitution +substitute a b = Substitute (Identifier a) (Identifier b) From 93091a612e0395842479b099f968ebdd20b7dd14 Mon Sep 17 00:00:00 2001 From: klntsky Date: Wed, 29 Jul 2020 20:16:59 +0300 Subject: [PATCH 12/45] Fix CLI autocompleter (now works correctly with capital letters). --- src/Docs/Search/Interactive.purs | 17 ++++++++--------- test/Main.purs | 2 +- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/Docs/Search/Interactive.purs b/src/Docs/Search/Interactive.purs index 40c62b4..549523b 100644 --- a/src/Docs/Search/Interactive.purs +++ b/src/Docs/Search/Interactive.purs @@ -5,7 +5,7 @@ import Docs.Search.Declarations (Declarations, mkDeclarations) import Docs.Search.DocsJson (DataDeclType(..)) import Docs.Search.Engine (mkEngineState, packageInfoToString, Result(..)) import Docs.Search.Engine as Engine -import Docs.Search.Extra (listToString, stringToList, (>#>)) +import Docs.Search.Extra (stringToList, (>#>)) import Docs.Search.IndexBuilder as IndexBuilder import Docs.Search.ModuleIndex (ModuleResult, mkPackedModuleIndex, unpackModuleIndex) import Docs.Search.NodeEngine (nodeEngine) @@ -22,12 +22,12 @@ import Prelude import Data.Array as Array import Data.Identity (Identity(..)) +import Data.List as List import Data.Maybe (fromMaybe) import Data.Newtype (un, unwrap, wrap) import Data.Search.Trie as Trie import Data.String (length) as String -import Data.String.Common (split, trim) as String -import Data.Tuple (fst) +import Data.String.Common (split, toLower, trim) as String import Effect (Effect) import Effect.Aff (launchAff_) import Effect.Class (liftEffect) @@ -105,14 +105,13 @@ mkCompleter -> Effect { completions :: Array String , matched :: String } mkCompleter index input = do - let path = stringToList input - let paths = + let path = stringToList $ String.toLower input + paths = Array.fromFoldable $ - listToString <$> - (fst <$> Trie.query path (unwrap index)) + (\result -> unwrap (unwrap result).name) <$> + List.concat (Trie.queryValues path (unwrap index)) - pure { completions: paths - , matched: input } + pure { completions: paths, matched: input } showResult :: Result -> String diff --git a/test/Main.purs b/test/Main.purs index 695744a..84157a1 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -3,7 +3,7 @@ module Test.Main where import Prelude import Docs.Search.TypeDecoder (Constraint(..), FunDep(..), FunDeps(..), Kind(..), QualifiedName(..), Type(..)) -import Docs.Search.Types +import Docs.Search.Types (Identifier(..)) import Test.TypeQuery as TypeQuery import Test.IndexBuilder as IndexBuilder import Test.Declarations as Declarations From 68e36ead7b15033df48973c12be1076995f9dfc9 Mon Sep 17 00:00:00 2001 From: klntsky Date: Wed, 29 Jul 2020 20:17:31 +0300 Subject: [PATCH 13/45] Update changelog, bump version --- CHANGELOG.md | 20 ++++++++++++++++++++ package.json | 2 +- src/Docs/Search/Config.purs | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 305f63a..ac8cad1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.0.9 - 2020-07-29] + +New features: +- Implement sorting by package popularity for declarations. +- Add app version info to the footer. +- Scroll to document top when search bar gets focus. +- Group modules by package in the sidebar (#34) + +Bugfixes: +- Fix CLI autocompleter (now works correctly with capital letters). + +## [0.0.8 - 2020-01-18] + +Skipped due to failed deployment. + +## [0.0.7 - 2020-01-18] + +Changes: +- Consider something a builtin when there's no `sourceSpan` (#32) + ## [0.0.6 - 2019-11-29] New features: diff --git a/package.json b/package.json index 7817f70..45b5abd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "purescript-docs-search", - "version": "0.0.8", + "version": "0.0.9", "description": "Search frontend for the documentation generated by the PureScript compiler.", "directories": { "test": "test" diff --git a/src/Docs/Search/Config.purs b/src/Docs/Search/Config.purs index 4c3c6f9..caaf454 100644 --- a/src/Docs/Search/Config.purs +++ b/src/Docs/Search/Config.purs @@ -30,7 +30,7 @@ config :: , typeIndexDirectory :: String } config = - { version: "0.0.8" + { version: "0.0.9" , outputDirectory: "output" , requiredDirectories: [ "generated-docs" From 5fa7580d755fe4daf74b5e2a3a61c71b4defb383 Mon Sep 17 00:00:00 2001 From: klntsky Date: Fri, 31 Jul 2020 11:53:33 +0300 Subject: [PATCH 14/45] Change sidebar title to "Packages" when grouping modules by package name (#39) --- src/Docs/Search/App/Sidebar.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Docs/Search/App/Sidebar.purs b/src/Docs/Search/App/Sidebar.purs index 2f3b36f..c6859c0 100644 --- a/src/Docs/Search/App/Sidebar.purs +++ b/src/Docs/Search/App/Sidebar.purs @@ -114,7 +114,7 @@ render { moduleIndex, groupingMode, moduleNames, isIndexHTML } = ] ] - [ HH.h3_ [ HH.text "Modules" ] + [ HH.h3_ [ HH.text $ if groupingMode == DontGroup then "Modules" else "Packages" ] , HH.input [ HP.id_ "group-modules__input" , HP.type_ HP.InputCheckbox , HP.checked (groupingMode == GroupByPackage) From 635056150fce23a829f5370490cfeae3954b62c7 Mon Sep 17 00:00:00 2001 From: klntsky Date: Fri, 31 Jul 2020 12:13:13 +0300 Subject: [PATCH 15/45] Group modules by default (#39) --- src/Docs/Search/App/Sidebar.purs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Docs/Search/App/Sidebar.purs b/src/Docs/Search/App/Sidebar.purs index c6859c0..5b1b589 100644 --- a/src/Docs/Search/App/Sidebar.purs +++ b/src/Docs/Search/App/Sidebar.purs @@ -84,9 +84,9 @@ handleAction (ToggleGrouping groupingMode) = do window <- HTML.window localStorage <- Window.localStorage window - if groupingMode == GroupByPackage - then Storage.setItem config.groupModulesItem "true" localStorage - else Storage.removeItem config.groupModulesItem localStorage + if groupingMode == DontGroup + then Storage.setItem config.groupModulesItem "false" localStorage + else Storage.removeItem config.groupModulesItem localStorage handleQuery @@ -156,8 +156,8 @@ loadGroupingModeFromLocalStorage :: Effect GroupingMode loadGroupingModeFromLocalStorage = do window <- HTML.window localStorage <- Window.localStorage window - mbGroupModules <- Storage.getItem config.groupModulesItem localStorage - pure $ if isJust mbGroupModules then GroupByPackage else DontGroup + mbDontGroupModules <- Storage.getItem config.groupModulesItem localStorage + pure $ if isJust mbDontGroupModules then DontGroup else GroupByPackage -- | Convert checkbox status to sidebar mode From 86304888a4991ef7f1e36e0587ff3622f51cfdff Mon Sep 17 00:00:00 2001 From: klntsky Date: Mon, 3 Aug 2020 13:59:21 +0300 Subject: [PATCH 16/45] Add --package-name flag: - Allows to specify current package name that will be used in the results; Add Meta: - Allows to store some static info, as for now - just local package name; Code style improvements. --- src/Docs/Search/App.purs | 8 +++++- src/Docs/Search/App/SearchResults.purs | 34 +++++++++++++---------- src/Docs/Search/Config.purs | 10 +++++++ src/Docs/Search/Engine.purs | 10 +++---- src/Docs/Search/IndexBuilder.purs | 24 ++++++++++++----- src/Docs/Search/Interactive.purs | 30 ++++++++++++--------- src/Docs/Search/Loader.js | 26 ++++++++++++++++++ src/Docs/Search/Loader.purs | 37 ++++++++++++++++++++++++++ src/Docs/Search/Main.purs | 37 +++++++++++++++++--------- src/Docs/Search/Meta.purs | 21 +++++++++++++++ src/Docs/Search/Types.purs | 6 +++++ 11 files changed, 191 insertions(+), 52 deletions(-) create mode 100644 src/Docs/Search/Loader.js create mode 100644 src/Docs/Search/Loader.purs create mode 100644 src/Docs/Search/Meta.purs diff --git a/src/Docs/Search/App.purs b/src/Docs/Search/App.purs index 7f7eb55..9bc2a75 100644 --- a/src/Docs/Search/App.purs +++ b/src/Docs/Search/App.purs @@ -16,6 +16,7 @@ import Docs.Search.Config (config) import Docs.Search.Extra (whenJust) import Docs.Search.ModuleIndex as ModuleIndex import Docs.Search.PackageIndex as PackageIndex +import Docs.Search.Meta as Meta import Effect (Effect) import Effect.Aff (launchAff_) import Halogen as H @@ -63,6 +64,7 @@ main = do HA.runHalogenAff do packageIndex <- PackageIndex.loadPackageIndex moduleIndex <- ModuleIndex.unpackModuleIndex <$> ModuleIndex.loadModuleIndex + meta <- Meta.load let scores = PackageIndex.mkScoresFromPackageIndex packageIndex let initialSearchEngineState = { packageIndex @@ -73,7 +75,11 @@ main = do } resultsComponent = - SearchResults.mkComponent initialSearchEngineState pageContents markdownIt + SearchResults.mkComponent + initialSearchEngineState + pageContents + markdownIt + meta sfio <- runUI SearchField.component unit searchField srio <- runUI resultsComponent unit searchResults diff --git a/src/Docs/Search/App/SearchResults.purs b/src/Docs/Search/App/SearchResults.purs index c5f9e82..1714eed 100644 --- a/src/Docs/Search/App/SearchResults.purs +++ b/src/Docs/Search/App/SearchResults.purs @@ -14,7 +14,8 @@ import Docs.Search.PackageIndex (PackageResult) import Docs.Search.SearchResult (ResultInfo(..), SearchResult(..)) import Docs.Search.TypeDecoder (Constraint(..), FunDep(..), FunDeps(..), Kind(..), QualifiedName(..), Type(..), TypeArgument(..), joinForAlls, joinRows) import Docs.Search.TypeIndex (TypeIndex) -import Docs.Search.Types (ModuleName(..), Identifier(..)) +import Docs.Search.Types (Identifier(..), ModuleName(..), PackageName) +import Docs.Search.Meta (Meta) import Prelude @@ -56,6 +57,7 @@ type State = { engineState :: EngineState , resultsCount :: Int , mode :: Mode , markdownIt :: MD.MarkdownIt + , localPackageName :: PackageName } @@ -73,8 +75,9 @@ mkComponent . EngineState -> Element -> MD.MarkdownIt + -> Meta -> H.Component HH.HTML Query i o Aff -mkComponent initialEngineState contents markdownIt = +mkComponent initialEngineState contents markdownIt { localPackageName } = H.mkComponent { initialState: const { engineState: initialEngineState , results: [] @@ -83,6 +86,7 @@ mkComponent initialEngineState contents markdownIt = , resultsCount: config.resultsCount , mode: Off , markdownIt + , localPackageName } , render , eval: H.mkEval $ H.defaultEval { handleQuery = handleQuery @@ -191,13 +195,12 @@ render state@{ mode: Active, results: [] } = , HH.text " did not yield any results." ] ] - render state@{ mode: Active } = renderContainer $ [ HH.h1_ [ HH.text "Search results" ] , HH.div_ $ - Array.concat $ shownResults <#> renderResult state.markdownIt + Array.concat $ shownResults <#> renderResult state , HH.div [ HP.class_ (wrap "load_more"), HP.id_ "load-more" ] [ if Array.length shownResults < Array.length state.results @@ -211,7 +214,6 @@ render state@{ mode: Active } = where shownResults = Array.take state.resultsCount state.results - renderContainer :: forall a b. Array (HH.HTML b a) -> HH.HTML b a renderContainer = HH.div [ HP.classes [ wrap "container", wrap "clearfix" ] ] <<< @@ -229,13 +231,17 @@ renderSummary text = renderResult :: forall a - . MD.MarkdownIt + . State -> Result -> Array (HH.HTML a Action) -renderResult markdownIt (DeclResult r) = renderSearchResult markdownIt r -renderResult markdownIt (TypeResult r) = renderSearchResult markdownIt r -renderResult markdownIt (PackResult r) = renderPackageResult r -renderResult markdownIt (MdlResult r) = renderModuleResult r +renderResult state (DeclResult r) = + renderSearchResult state r +renderResult state (TypeResult r) = + renderSearchResult state r +renderResult state (PackResult r) = + renderPackageResult r +renderResult state (MdlResult r) = + renderModuleResult r renderPackageResult @@ -291,10 +297,10 @@ renderModuleResult { name, package } = renderSearchResult :: forall a - . MD.MarkdownIt + . State -> SearchResult -> Array (HH.HTML a Action) -renderSearchResult markdownIt (SearchResult result) = +renderSearchResult state (SearchResult result) = -- class names here and below are from Pursuit. [ HH.div [ HP.class_ (wrap "result") ] [ HH.h3 [ HP.class_ (wrap "result__title") ] @@ -311,7 +317,7 @@ renderSearchResult markdownIt (SearchResult result) = , HH.div [ HP.class_ (wrap "result__body") ] $ renderResultType result <> - result.comments >#> pure <<< MDH.render_ markdownIt + result.comments >#> pure <<< MDH.render_ state.markdownIt , HH.div [ HP.class_ (wrap "result__actions") ] @@ -322,7 +328,7 @@ renderSearchResult markdownIt (SearchResult result) = , HP.title "Package" ] [ HH.text "P" ] - , HH.text $ packageInfoToString result.packageInfo + , HH.text $ packageInfoToString state.localPackageName result.packageInfo ] , HH.span [ HP.class_ (wrap "result__actions__item") ] diff --git a/src/Docs/Search/Config.purs b/src/Docs/Search/Config.purs index caaf454..1ecbc3c 100644 --- a/src/Docs/Search/Config.purs +++ b/src/Docs/Search/Config.purs @@ -2,6 +2,8 @@ module Docs.Search.Config where import Prelude +import Docs.Search.Types (PackageName(..)) + -- | Some magic constants. config :: { version :: String @@ -10,6 +12,9 @@ config :: , mkIndexPartPath :: Int -> String , moduleIndexPath :: String , moduleIndexLoadPath :: String + , metaPath :: String + , metaLoadPath :: String + , metaItem :: String , groupModulesItem :: String , packageInfoPath :: String , packageInfoLoadPath :: String @@ -28,6 +33,7 @@ config :: , requiredDirectories :: Array String , resultsCount :: Int , typeIndexDirectory :: String + , defaultPackageName :: PackageName } config = { version: "0.0.9" @@ -51,6 +57,9 @@ config = , moduleIndexPath: "generated-docs/html/index/modules.js" , moduleIndexLoadPath: "./index/modules.js" -- ^ Used to load mode index to the browser scope. + , metaPath: "generated-docs/html/index/meta.js" + , metaLoadPath: "./index/meta.js" + , metaItem: "DocsSearchMeta" , groupModulesItem: "PureScriptDocsSearchGroupModules" -- ^ localStorage key to save sidebar checkbox value to. , packageInfoPath: "generated-docs/html/index/packages.js" @@ -69,4 +78,5 @@ config = } -- ^ Penalties used to determine how "far" a type query is from a given type. -- See Docs.Search.TypeQuery + , defaultPackageName: PackageName "" } diff --git a/src/Docs/Search/Engine.purs b/src/Docs/Search/Engine.purs index 8c338d8..c5e4cec 100644 --- a/src/Docs/Search/Engine.purs +++ b/src/Docs/Search/Engine.purs @@ -160,8 +160,8 @@ sortByDistance typeQuery = Array.sortWith (map (penalty typeQuery) <<< typeOfResult) -packageInfoToString :: PackageInfo -> String -packageInfoToString (Package (PackageName p)) = p -packageInfoToString Builtin = "" -packageInfoToString LocalPackage = "" -packageInfoToString UnknownPackage = "" +packageInfoToString :: PackageName -> PackageInfo -> String +packageInfoToString _ (Package (PackageName p)) = p +packageInfoToString _ Builtin = "" +packageInfoToString localPackageName LocalPackage = unwrap localPackageName +packageInfoToString _ UnknownPackage = "" diff --git a/src/Docs/Search/IndexBuilder.purs b/src/Docs/Search/IndexBuilder.purs index 07f2835..14137cc 100644 --- a/src/Docs/Search/IndexBuilder.purs +++ b/src/Docs/Search/IndexBuilder.purs @@ -10,6 +10,8 @@ import Docs.Search.PackageIndex (PackageInfo, mkPackageInfo) import Docs.Search.Score (mkScores) import Docs.Search.SearchResult (SearchResult) import Docs.Search.TypeIndex (TypeIndex, mkTypeIndex) +import Docs.Search.Types (PackageName) +import Docs.Search.Meta (Meta) import Prelude import Data.Argonaut.Core (stringify) @@ -49,6 +51,7 @@ type Config = , bowerFiles :: Array String , generatedDocs :: String , noPatch :: Boolean + , packageName :: PackageName } @@ -67,12 +70,15 @@ run' cfg = do docsJsons <- decodeDocsJsons cfg packageMetas <- decodeBowerJsons cfg + let countOfPackages = Array.length packageMetas + countOfModules = Array.length docsJsons + liftEffect do log $ "Indexing " <> - show (Array.length docsJsons) <> + show countOfModules <> " modules from " <> - show (Array.length packageMetas) <> + show countOfPackages <> " packages..." let scores = mkScores packageMetas @@ -80,6 +86,7 @@ run' cfg = do typeIndex = mkTypeIndex scores docsJsons packageInfo = mkPackageInfo scores packageMetas moduleIndex = mkPackedModuleIndex index + meta = { localPackageName: cfg.packageName } createDirectories cfg @@ -88,15 +95,13 @@ run' cfg = do <*> parallel (writeTypeIndex cfg typeIndex) <*> parallel (writePackageInfo packageInfo) <*> parallel (writeModuleIndex moduleIndex) - <*> parallel (if cfg.noPatch - then pure unit - else patchDocs cfg) + <*> parallel (writeMeta meta) + <*> parallel (when (not cfg.noPatch) $ patchDocs cfg) <*> parallel (copyAppFile cfg) let countOfDefinitions = Trie.size $ unwrap index countOfTypeDefinitions = sum $ fromMaybe 0 <$> map Array.length <$> Map.values (unwrap typeIndex) - countOfPackages = Array.length packageMetas liftEffect do log $ @@ -231,6 +236,13 @@ writeModuleIndex moduleIndex = do where header = "window.DocsSearchModuleIndex = " +writeMeta :: Meta -> Aff Unit +writeMeta meta = do + writeTextFile UTF8 config.metaPath $ + header <> stringify (encodeJson meta) + where + header = "window." <> config.metaItem <> " = " + -- | Get a mapping from index parts to index contents. getIndex :: Declarations -> Map Int (Array (Tuple String (Array SearchResult))) getIndex (Declarations trie) = diff --git a/src/Docs/Search/Interactive.purs b/src/Docs/Search/Interactive.purs index 549523b..3cb4617 100644 --- a/src/Docs/Search/Interactive.purs +++ b/src/Docs/Search/Interactive.purs @@ -16,7 +16,7 @@ import Docs.Search.Terminal (bold, cyan, green, yellow) import Docs.Search.TypeDecoder (Constraint, FunDeps, Kind, QualifiedName, Type, TypeArgument) import Docs.Search.TypeIndex (resultsWithTypes) import Docs.Search.TypePrinter (keyword, showConstraint, showFunDeps, showKind, showType, showTypeArgument, space, syntax) -import Docs.Search.Types (ModuleName, PackageInfo, Identifier) +import Docs.Search.Types (ModuleName, PackageName, PackageInfo, Identifier) import Prelude @@ -38,6 +38,7 @@ import Node.ReadLine (createConsoleInterface, question) type Config = { docsFiles :: Array String , bowerFiles :: Array String + , packageName :: PackageName } @@ -88,7 +89,7 @@ run cfg = launchAff_ $ do Console.log $ if total > 0 then do Array.intercalate "\n\n\n" $ - showResult <$> Array.reverse results + showResult cfg <$> Array.reverse results else "Your search for " <> bold input <> " did not yield any results." @@ -114,16 +115,16 @@ mkCompleter index input = do pure { completions: paths, matched: input } -showResult :: Result -> String -showResult = case _ of - DeclResult r -> showSearchResult r - TypeResult r -> showSearchResult r +showResult :: Config -> Result -> String +showResult cfg = case _ of + DeclResult r -> showSearchResult cfg r + TypeResult r -> showSearchResult cfg r PackResult r -> showPackageResult r MdlResult r -> showModuleResult r -showSearchResult :: SearchResult -> String -showSearchResult (SearchResult result@{ name, comments, moduleName, packageInfo }) = +showSearchResult :: Config -> SearchResult -> String +showSearchResult cfg (SearchResult result@{ name, comments, moduleName, packageInfo }) = showSignature result <> "\n" <> (fromMaybe "\n" $ @@ -131,10 +132,10 @@ showSearchResult (SearchResult result@{ name, comments, moduleName, packageInfo "\n" <> leftShift 3 (String.trim comment) <> "\n\n") <> bold ( - cyan (rightPad 40 $ packageInfoToString packageInfo) + cyan (rightPad 40 $ packageInfoToString cfg.packageName packageInfo) ) <> space <> - bold (green $ unwrap moduleName) + showModuleName moduleName showPackageResult :: PackageResult -> String @@ -146,7 +147,11 @@ showPackageResult { name, description } = showModuleResult :: ModuleResult -> String showModuleResult { name, package } = - bold (cyan "module") <> " " <> bold (green $ unwrap name) + bold (cyan "module") <> " " <> showModuleName name + + +showModuleName :: ModuleName -> String +showModuleName = bold <<< green <<< unwrap showSignature @@ -204,8 +209,7 @@ showTypeClassSignature { fundeps, arguments, superclasses } { name, moduleName } superclasses <#> showConstraint ) ) <> - syntax ")" <> - space <> + syntax ") " <> syntax "<=" ) <> space <> diff --git a/src/Docs/Search/Loader.js b/src/Docs/Search/Loader.js new file mode 100644 index 0000000..0dfd479 --- /dev/null +++ b/src/Docs/Search/Loader.js @@ -0,0 +1,26 @@ +/* global exports */ + +exports.loadFromScript = function (globalIdentifier) { + return function (url) { + return function () { + return new Promise(function (resolve, reject) { + if (typeof window[globalIdentifier] === 'undefined') { + var script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = url; + script.addEventListener('load', function () { + if (typeof window[globalIdentifier] === 'undefined') { + reject(new Error("Couldn't load package index.")); + } else { + resolve(window[globalIdentifier]); + } + }); + script.addEventListener('error', reject); + document.body.appendChild(script); + } else { + resolve(window[globalIdentifier]); + } + }); + }; + }; +}; diff --git a/src/Docs/Search/Loader.purs b/src/Docs/Search/Loader.purs new file mode 100644 index 0000000..2ce1ff5 --- /dev/null +++ b/src/Docs/Search/Loader.purs @@ -0,0 +1,37 @@ +module Docs.Search.Loader where + +import Docs.Search.Types (GlobalIdentifier, URL) + +import Prelude + +import Control.Monad.Error.Class (throwError) +import Control.Promise (Promise, toAffE) +import Data.Argonaut.Core (Json) +import Data.Argonaut.Decode.Class (class DecodeJson, decodeJson) +import Data.Either (either) +import Data.Newtype (unwrap) +import Effect (Effect) +import Effect.Aff (Aff) +import Effect.Exception (error) + + + +load + :: forall a + . DecodeJson a + => GlobalIdentifier + -> URL + -> Aff a +load globalIdentifier url = do + json <- toAffE (loadFromScript globalIdentifier url) + either throw pure $ decodeJson json + where + throw err = throwError $ error $ + "Couldn't load content from window." <> + unwrap globalIdentifier <> ": " <> err + + +foreign import loadFromScript + :: GlobalIdentifier + -> URL + -> Effect (Promise Json) diff --git a/src/Docs/Search/Main.purs b/src/Docs/Search/Main.purs index 81ce10a..2699e64 100644 --- a/src/Docs/Search/Main.purs +++ b/src/Docs/Search/Main.purs @@ -6,12 +6,14 @@ import Prelude import Docs.Search.IndexBuilder as IndexBuilder import Docs.Search.Interactive as Interactive import Docs.Search.Config (config) +import Docs.Search.Types (PackageName(..)) import Data.Generic.Rep (class Generic) import Data.Generic.Rep.Show (genericShow) import Data.List as List import Data.List.NonEmpty as NonEmpty import Data.Maybe (Maybe, fromMaybe, optional) +import Data.Newtype (unwrap) import Data.Unfoldable (class Unfoldable) import Effect (Effect) import Effect.Console (log) @@ -23,7 +25,10 @@ main :: Effect Unit main = do args <- getArgs - let defaultCommands = Search { docsFiles: defaultDocsFiles, bowerFiles: defaultBowerFiles } + let defaultCommands = Search { docsFiles: defaultDocsFiles + , bowerFiles: defaultBowerFiles + , packageName: config.defaultPackageName + } case fromMaybe defaultCommands args of BuildIndex cfg -> IndexBuilder.run cfg @@ -42,16 +47,8 @@ getArgs = execParser opts data Commands - = BuildIndex - { docsFiles :: Array String - , bowerFiles :: Array String - , generatedDocs :: String - , noPatch :: Boolean - } - | Search - { docsFiles :: Array String - , bowerFiles :: Array String - } + = BuildIndex IndexBuilder.Config + | Search Interactive.Config | Version @@ -88,6 +85,8 @@ buildIndex = ado bowerFiles <- bowerFilesOption + packageName <- packageNameOption + generatedDocs <- strOption ( long "generated-docs" <> metavar "DIR" @@ -99,7 +98,8 @@ buildIndex = ado <> help "Do not patch the HTML docs, only build indices" ) - in BuildIndex { docsFiles, bowerFiles, generatedDocs, noPatch } + + in BuildIndex { docsFiles, bowerFiles, generatedDocs, noPatch, packageName } startInteractive :: Parser Commands @@ -109,7 +109,9 @@ startInteractive = ado bowerFiles <- bowerFilesOption - in Search { docsFiles, bowerFiles } + packageName <- packageNameOption + + in Search { docsFiles, bowerFiles, packageName } docsFilesOption :: Parser (Array String) @@ -136,6 +138,15 @@ bowerFilesOption = fromMaybe defaultBowerFiles <$> ) +packageNameOption :: Parser PackageName +packageNameOption = + PackageName <$> strOption + ( long "package-name" + <> metavar "PACKAGE" + <> value (unwrap $ config.defaultPackageName) + ) + + defaultDocsFiles :: Array String defaultDocsFiles = [ "output/**/docs.json" ] diff --git a/src/Docs/Search/Meta.purs b/src/Docs/Search/Meta.purs new file mode 100644 index 0000000..bc17f78 --- /dev/null +++ b/src/Docs/Search/Meta.purs @@ -0,0 +1,21 @@ +module Docs.Search.Meta where + +import Docs.Search.Config (config) +import Docs.Search.Loader as Loader +import Docs.Search.Types (GlobalIdentifier(..), PackageName) + +import Prelude + +import Effect.Aff (Aff, catchError) + + +type Meta = + { localPackageName :: PackageName + } + + +load :: Aff Meta +load = + Loader.load (GlobalIdentifier config.metaItem) config.metaLoadPath + `catchError` const (pure defaultMeta) + where defaultMeta = { localPackageName: config.defaultPackageName } diff --git a/src/Docs/Search/Types.purs b/src/Docs/Search/Types.purs index deacb07..0e2c023 100644 --- a/src/Docs/Search/Types.purs +++ b/src/Docs/Search/Types.purs @@ -74,3 +74,9 @@ derive newtype instance ringPackageScore :: Ring PackageScore derive newtype instance showPackageScore :: Show PackageScore derive newtype instance decodeJsonPackageScore :: DecodeJson PackageScore derive newtype instance encodeJsonPackageScore :: EncodeJson PackageScore + +type URL = String + +newtype GlobalIdentifier = GlobalIdentifier String + +derive instance newtypeGlobalIdentifier :: Newtype GlobalIdentifier _ From 73a6c092a0377408b7390d6d8fb71fb56e1ced42 Mon Sep 17 00:00:00 2001 From: klntsky Date: Mon, 3 Aug 2020 18:35:14 +0300 Subject: [PATCH 17/45] Refactor `Docs.Search.Config` --- src/Docs/Search/App.purs | 19 ++-- src/Docs/Search/App/SearchResults.purs | 6 +- src/Docs/Search/App/Sidebar.purs | 8 +- src/Docs/Search/BrowserEngine.purs | 8 +- src/Docs/Search/Config.purs | 150 ++++++++++++------------- src/Docs/Search/IndexBuilder.purs | 14 +-- src/Docs/Search/Main.purs | 8 +- src/Docs/Search/Meta.purs | 6 +- src/Docs/Search/ModuleIndex.purs | 4 +- src/Docs/Search/PackageIndex.purs | 4 +- src/Docs/Search/TypeIndex.purs | 8 +- src/Docs/Search/TypeQuery.purs | 18 +-- src/Docs/Search/Types.purs | 7 +- 13 files changed, 132 insertions(+), 128 deletions(-) diff --git a/src/Docs/Search/App.purs b/src/Docs/Search/App.purs index 9bc2a75..ca87d25 100644 --- a/src/Docs/Search/App.purs +++ b/src/Docs/Search/App.purs @@ -1,6 +1,15 @@ -- | This is the main module of the client-side Halogen app. module Docs.Search.App where +import Docs.Search.App.SearchField as SearchField +import Docs.Search.App.SearchResults as SearchResults +import Docs.Search.App.Sidebar as Sidebar +import Docs.Search.Config as Config +import Docs.Search.Extra (whenJust) +import Docs.Search.ModuleIndex as ModuleIndex +import Docs.Search.PackageIndex as PackageIndex +import Docs.Search.Meta as Meta + import Prelude import Control.Alt (alt) @@ -9,14 +18,6 @@ import Data.Maybe (Maybe(..)) import Data.Newtype (wrap) import Data.Tuple (Tuple(..)) import Data.Tuple.Nested ((/\)) -import Docs.Search.App.SearchField as SearchField -import Docs.Search.App.SearchResults as SearchResults -import Docs.Search.App.Sidebar as Sidebar -import Docs.Search.Config (config) -import Docs.Search.Extra (whenJust) -import Docs.Search.ModuleIndex as ModuleIndex -import Docs.Search.PackageIndex as PackageIndex -import Docs.Search.Meta as Meta import Effect (Effect) import Effect.Aff (launchAff_) import Halogen as H @@ -195,7 +196,7 @@ insertVersionInfo doc = do Element.setAttribute "href" "https://github.com/spacchetti/purescript-docs-search" linkElement Element.setAttribute "target" "_blank" linkElement linkText <- Document.createTextNode ("docs-search") doc <#> Text.toNode - suffix <- Document.createTextNode (" " <> config.version) doc <#> Text.toNode + suffix <- Document.createTextNode (" " <> Config.version) doc <#> Text.toNode void $ Node.appendChild prefix versionInfo void $ Node.appendChild linkNode versionInfo void $ Node.appendChild linkText linkNode diff --git a/src/Docs/Search/App/SearchResults.purs b/src/Docs/Search/App/SearchResults.purs index 1714eed..0369209 100644 --- a/src/Docs/Search/App/SearchResults.purs +++ b/src/Docs/Search/App/SearchResults.purs @@ -3,7 +3,7 @@ module Docs.Search.App.SearchResults where import Docs.Search.App.SearchField (SearchFieldMessage(..)) import Docs.Search.BrowserEngine (PartialIndex, browserSearchEngine) -import Docs.Search.Config (config) +import Docs.Search.Config as Config import Docs.Search.Declarations (DeclLevel(..), declLevelToHashAnchor) import Docs.Search.DocsJson (DataDeclType(..)) import Docs.Search.Engine (Result(..), packageInfoToString) @@ -83,7 +83,7 @@ mkComponent initialEngineState contents markdownIt { localPackageName } = , results: [] , input: "" , contents - , resultsCount: config.resultsCount + , resultsCount: Config.resultsCount , mode: Off , markdownIt , localPackageName @@ -116,7 +116,7 @@ handleQuery (MessageFromSearchField (InputUpdated input_) next) = do H.modify_ (_ { mode = Off }) showPageContents else do - H.modify_ (_ { mode = Loading, resultsCount = config.resultsCount }) + H.modify_ (_ { mode = Loading, resultsCount = Config.resultsCount }) void $ H.fork do { index, results } <- H.liftAff $ diff --git a/src/Docs/Search/App/Sidebar.purs b/src/Docs/Search/App/Sidebar.purs index 5b1b589..5d19b27 100644 --- a/src/Docs/Search/App/Sidebar.purs +++ b/src/Docs/Search/App/Sidebar.purs @@ -1,6 +1,6 @@ module Docs.Search.App.Sidebar where -import Docs.Search.Config (config) +import Docs.Search.Config as Config import Docs.Search.ModuleIndex (PackedModuleIndex) import Docs.Search.Types (ModuleName, PackageName(..)) @@ -85,8 +85,8 @@ handleAction (ToggleGrouping groupingMode) = do localStorage <- Window.localStorage window if groupingMode == DontGroup - then Storage.setItem config.groupModulesItem "false" localStorage - else Storage.removeItem config.groupModulesItem localStorage + then Storage.setItem Config.groupModulesItem "false" localStorage + else Storage.removeItem Config.groupModulesItem localStorage handleQuery @@ -156,7 +156,7 @@ loadGroupingModeFromLocalStorage :: Effect GroupingMode loadGroupingModeFromLocalStorage = do window <- HTML.window localStorage <- Window.localStorage window - mbDontGroupModules <- Storage.getItem config.groupModulesItem localStorage + mbDontGroupModules <- Storage.getItem Config.groupModulesItem localStorage pure $ if isJust mbDontGroupModules then DontGroup else GroupByPackage diff --git a/src/Docs/Search/BrowserEngine.purs b/src/Docs/Search/BrowserEngine.purs index 8063e9b..b3af249 100644 --- a/src/Docs/Search/BrowserEngine.purs +++ b/src/Docs/Search/BrowserEngine.purs @@ -1,7 +1,7 @@ -- | A search engine that is used in the browser. module Docs.Search.BrowserEngine where -import Docs.Search.Config (config) +import Docs.Search.Config as Config import Docs.Search.PackageIndex (queryPackageIndex) import Docs.Search.Engine (Engine, EngineState, Index) import Docs.Search.SearchResult (SearchResult) @@ -68,7 +68,7 @@ query index@(PartialIndex indexMap) input = do eiPartJson <- - try $ toAffE $ loadIndex_ partId $ config.mkIndexPartLoadPath partId + try $ toAffE $ loadIndex_ partId $ Config.mkIndexPartLoadPath partId let mbNewTrie :: Maybe (Trie Char (List SearchResult)) @@ -121,9 +121,9 @@ browserSearchEngine = -- | Find in which part of the index this path can be found. getPartId :: List Char -> Int getPartId (a : b : _) = - (Char.toCharCode a + Char.toCharCode b) `mod` config.numberOfIndexParts + (Char.toCharCode a + Char.toCharCode b) `mod` Config.numberOfIndexParts getPartId (a : _) = - Char.toCharCode a `mod` config.numberOfIndexParts + Char.toCharCode a `mod` Config.numberOfIndexParts getPartId _ = 0 diff --git a/src/Docs/Search/Config.purs b/src/Docs/Search/Config.purs index 1ecbc3c..5842778 100644 --- a/src/Docs/Search/Config.purs +++ b/src/Docs/Search/Config.purs @@ -2,81 +2,79 @@ module Docs.Search.Config where import Prelude -import Docs.Search.Types (PackageName(..)) - --- | Some magic constants. -config :: - { version :: String - , declIndexDirectory :: String - , mkIndexPartLoadPath :: Int -> String - , mkIndexPartPath :: Int -> String - , moduleIndexPath :: String - , moduleIndexLoadPath :: String - , metaPath :: String - , metaLoadPath :: String - , metaItem :: String - , groupModulesItem :: String - , packageInfoPath :: String - , packageInfoLoadPath :: String - , mkShapeScriptPath :: String -> String - , numberOfIndexParts :: Int - , outputDirectory :: String - , penalties :: { excessiveConstraint :: Int - , generalize :: Int - , instantiate :: Int - , match :: Int - , matchConstraint :: Int - , missingConstraint :: Int - , rowsMismatch :: Int - , typeVars :: Int - } - , requiredDirectories :: Array String - , resultsCount :: Int - , typeIndexDirectory :: String - , defaultPackageName :: PackageName +import Docs.Search.Types + +version :: String +version = "0.0.9" + +mkShapeScriptPath :: String -> String +mkShapeScriptPath shape = "./index/types/" <> shape <> ".js" + +-- | In how many parts the index should be splitted? +numberOfIndexParts :: Int +numberOfIndexParts = 50 + +mkIndexPartPath :: Int -> String +mkIndexPartPath partId = "html/index/declarations/" <> show partId <> ".js" + +mkIndexPartLoadPath :: Int -> String +mkIndexPartLoadPath partId = "./index/declarations/" <> show partId <> ".js" + +moduleIndexPath :: String +moduleIndexPath = "generated-docs/html/index/modules.js" + +-- | Used to load mode index to the browser scope. +moduleIndexLoadPath :: String +moduleIndexLoadPath = "./index/modules.js" + +typeIndexDirectory :: String +typeIndexDirectory = "generated-docs/html/index/types" + +metaPath :: String +metaPath = "generated-docs/html/index/meta.js" + +metaLoadPath :: URL +metaLoadPath = URL "./index/meta.js" + +metaItem :: String +metaItem = "DocsSearchMeta" + +-- | localStorage key to save sidebar checkbox value to. +groupModulesItem :: String +groupModulesItem = "PureScriptDocsSearchGroupModules" + +packageInfoPath :: String +packageInfoPath = "generated-docs/html/index/packages.js" + +packageInfoLoadPath :: String +packageInfoLoadPath = "./index/packages.js" + +-- | How many results to show by default? +resultsCount :: Int +resultsCount = 25 + +-- | Penalties used to determine how "far" a type query is from a given type. +-- See Docs.Search.TypeQuery +penalties :: + { excessiveConstraint :: Int + , generalize :: Int + , instantiate :: Int + , match :: Int + , matchConstraint :: Int + , missingConstraint :: Int + , rowsMismatch :: Int + , typeVars :: Int } -config = - { version: "0.0.9" - , outputDirectory: "output" - , requiredDirectories: - [ "generated-docs" - , "generated-docs/html" - , "output" - ] - -- ^ Directories required by IndexBuilder - , declIndexDirectory: "generated-docs/html/index/declarations" - , typeIndexDirectory: "generated-docs/html/index/types" - , mkShapeScriptPath: - \shape -> "./index/types/" <> shape <> ".js" - , numberOfIndexParts: 50 - -- ^ In how many parts the index should be splitted? - , mkIndexPartPath: - \(partId :: Int) -> "html/index/declarations/" <> show partId <> ".js" - , mkIndexPartLoadPath: - \(partId :: Int) -> "./index/declarations/" <> show partId <> ".js" - , moduleIndexPath: "generated-docs/html/index/modules.js" - , moduleIndexLoadPath: "./index/modules.js" - -- ^ Used to load mode index to the browser scope. - , metaPath: "generated-docs/html/index/meta.js" - , metaLoadPath: "./index/meta.js" - , metaItem: "DocsSearchMeta" - , groupModulesItem: "PureScriptDocsSearchGroupModules" - -- ^ localStorage key to save sidebar checkbox value to. - , packageInfoPath: "generated-docs/html/index/packages.js" - , packageInfoLoadPath: "./index/packages.js" - -- ^ Used to load package index to the browser scope. - , resultsCount: 25 - -- ^ How many results to show by default? - , penalties: { typeVars: 2 - , match: 2 - , matchConstraint: 1 - , instantiate: 2 - , generalize: 2 - , rowsMismatch: 3 - , missingConstraint: 1 - , excessiveConstraint: 1 - } - -- ^ Penalties used to determine how "far" a type query is from a given type. - -- See Docs.Search.TypeQuery - , defaultPackageName: PackageName "" +penalties = + { typeVars: 2 + , match: 2 + , matchConstraint: 1 + , instantiate: 2 + , generalize: 2 + , rowsMismatch: 3 + , missingConstraint: 1 + , excessiveConstraint: 1 } + +defaultPackageName :: PackageName +defaultPackageName = PackageName "" diff --git a/src/Docs/Search/IndexBuilder.purs b/src/Docs/Search/IndexBuilder.purs index 14137cc..247adc4 100644 --- a/src/Docs/Search/IndexBuilder.purs +++ b/src/Docs/Search/IndexBuilder.purs @@ -1,7 +1,7 @@ module Docs.Search.IndexBuilder where import Docs.Search.BrowserEngine (getPartId) -import Docs.Search.Config (config) +import Docs.Search.Config as Config import Docs.Search.Declarations (Declarations(..), mkDeclarations) import Docs.Search.DocsJson (DocsJson) import Docs.Search.Extra ((>#>)) @@ -209,7 +209,7 @@ decodeBowerJsons { bowerFiles } = do writeTypeIndex :: Config -> TypeIndex -> Aff Unit writeTypeIndex { generatedDocs } typeIndex = for_ entries \(Tuple typeShape results) -> do - writeTextFile UTF8 (config.typeIndexDirectory <> "/" <> typeShape <> ".js") + writeTextFile UTF8 (Config.typeIndexDirectory <> "/" <> typeShape <> ".js") (mkHeader typeShape <> stringify (encodeJson results)) where mkHeader typeShape = @@ -222,7 +222,7 @@ writeTypeIndex { generatedDocs } typeIndex = writePackageInfo :: PackageInfo -> Aff Unit writePackageInfo packageInfo = do - writeTextFile UTF8 config.packageInfoPath $ + writeTextFile UTF8 Config.packageInfoPath $ header <> stringify (encodeJson packageInfo) where @@ -231,17 +231,17 @@ writePackageInfo packageInfo = do writeModuleIndex :: PackedModuleIndex -> Aff Unit writeModuleIndex moduleIndex = do - writeTextFile UTF8 config.moduleIndexPath $ + writeTextFile UTF8 Config.moduleIndexPath $ header <> stringify (encodeJson moduleIndex) where header = "window.DocsSearchModuleIndex = " writeMeta :: Meta -> Aff Unit writeMeta meta = do - writeTextFile UTF8 config.metaPath $ + writeTextFile UTF8 Config.metaPath $ header <> stringify (encodeJson meta) where - header = "window." <> config.metaItem <> " = " + header = "window." <> Config.metaItem <> " = " -- | Get a mapping from index parts to index contents. getIndex :: Declarations -> Map Int (Array (Tuple String (Array SearchResult))) @@ -285,7 +285,7 @@ writeIndex { generatedDocs } = getIndex >>> \resultsMap -> do "// This file was generated by purescript-docs-search.\n" <> "window.DocsSearchIndex[\"" <> show indexPartId <> "\"] = " - writeTextFile UTF8 (generatedDocs <> config.mkIndexPartPath indexPartId) $ + writeTextFile UTF8 (generatedDocs <> Config.mkIndexPartPath indexPartId) $ header <> stringify (encodeJson results) diff --git a/src/Docs/Search/Main.purs b/src/Docs/Search/Main.purs index 2699e64..95504c1 100644 --- a/src/Docs/Search/Main.purs +++ b/src/Docs/Search/Main.purs @@ -5,7 +5,7 @@ import Prelude import Docs.Search.IndexBuilder as IndexBuilder import Docs.Search.Interactive as Interactive -import Docs.Search.Config (config) +import Docs.Search.Config as Config import Docs.Search.Types (PackageName(..)) import Data.Generic.Rep (class Generic) @@ -27,13 +27,13 @@ main = do args <- getArgs let defaultCommands = Search { docsFiles: defaultDocsFiles , bowerFiles: defaultBowerFiles - , packageName: config.defaultPackageName + , packageName: Config.defaultPackageName } case fromMaybe defaultCommands args of BuildIndex cfg -> IndexBuilder.run cfg Search cfg -> Interactive.run cfg - Version -> log config.version + Version -> log Config.version getArgs :: Effect (Maybe Commands) @@ -143,7 +143,7 @@ packageNameOption = PackageName <$> strOption ( long "package-name" <> metavar "PACKAGE" - <> value (unwrap $ config.defaultPackageName) + <> value (unwrap Config.defaultPackageName) ) diff --git a/src/Docs/Search/Meta.purs b/src/Docs/Search/Meta.purs index bc17f78..435ddfd 100644 --- a/src/Docs/Search/Meta.purs +++ b/src/Docs/Search/Meta.purs @@ -1,6 +1,6 @@ module Docs.Search.Meta where -import Docs.Search.Config (config) +import Docs.Search.Config as Config import Docs.Search.Loader as Loader import Docs.Search.Types (GlobalIdentifier(..), PackageName) @@ -16,6 +16,6 @@ type Meta = load :: Aff Meta load = - Loader.load (GlobalIdentifier config.metaItem) config.metaLoadPath + Loader.load (GlobalIdentifier Config.metaItem) Config.metaLoadPath `catchError` const (pure defaultMeta) - where defaultMeta = { localPackageName: config.defaultPackageName } + where defaultMeta = { localPackageName: Config.defaultPackageName } diff --git a/src/Docs/Search/ModuleIndex.purs b/src/Docs/Search/ModuleIndex.purs index 81906cd..d3162fd 100644 --- a/src/Docs/Search/ModuleIndex.purs +++ b/src/Docs/Search/ModuleIndex.purs @@ -1,6 +1,6 @@ module Docs.Search.ModuleIndex where -import Docs.Search.Config (config) +import Docs.Search.Config as Config import Docs.Search.Declarations (Declarations(..)) import Docs.Search.SearchResult (SearchResult(..)) import Docs.Search.Types (ModuleName, PackageName, PackageInfo(..), PackageScore) @@ -107,7 +107,7 @@ mkPackedModuleIndex (Declarations trie) = loadModuleIndex :: Aff PackedModuleIndex loadModuleIndex = do - json <- toAffE $ load config.moduleIndexLoadPath + json <- toAffE $ load Config.moduleIndexLoadPath pure $ fromMaybe mempty $ hush $ decodeJson json diff --git a/src/Docs/Search/PackageIndex.purs b/src/Docs/Search/PackageIndex.purs index dddbd71..9f25786 100644 --- a/src/Docs/Search/PackageIndex.purs +++ b/src/Docs/Search/PackageIndex.purs @@ -1,6 +1,6 @@ module Docs.Search.PackageIndex where -import Docs.Search.Config (config) +import Docs.Search.Config as Config import Docs.Search.Extra (stringToList) import Docs.Search.Score (Scores, getPackageScoreForPackageName, normalizePackageName) import Docs.Search.Types (PackageName, RawPackageName(..), PackageScore) @@ -73,7 +73,7 @@ mkScoresFromPackageIndex = loadPackageIndex :: Aff PackageIndex loadPackageIndex = do - json <- toAffE (load config.packageInfoLoadPath) + json <- toAffE (load Config.packageInfoLoadPath) let packageInfo = fromMaybe mempty $ hush $ decodeJson json pure $ mkPackageIndex packageInfo diff --git a/src/Docs/Search/TypeIndex.purs b/src/Docs/Search/TypeIndex.purs index bd0c1a3..a322064 100644 --- a/src/Docs/Search/TypeIndex.purs +++ b/src/Docs/Search/TypeIndex.purs @@ -1,7 +1,7 @@ -- | Partial type index, can be loaded on demand in the browser. module Docs.Search.TypeIndex where -import Docs.Search.Config (config) +import Docs.Search.Config as Config import Docs.Search.Declarations (resultsForDeclaration) import Docs.Search.DocsJson (DocsJson(..)) import Docs.Search.Score (Scores) @@ -21,7 +21,7 @@ import Data.Either (hush) import Data.Map (Map) import Data.Map as Map import Data.Maybe (Maybe(..), fromMaybe', isJust) -import Data.Newtype (class Newtype, unwrap, wrap) +import Data.Newtype (class Newtype, over) import Effect (Effect) import Effect.Aff (Aff, try) @@ -80,7 +80,7 @@ lookup key index@(TypeIndex map) = case Map.lookup key map of Just results -> pure { index, results: Array.fold results } Nothing -> do - eiJson <- try (toAffE (lookup_ key $ config.mkShapeScriptPath key)) + eiJson <- try (toAffE (lookup_ key $ Config.mkShapeScriptPath key)) pure $ fromMaybe' (\_ -> { index: insert key Nothing index, results: [] }) do @@ -94,7 +94,7 @@ lookup key index@(TypeIndex map) = -> Maybe (Array SearchResult) -> TypeIndex -> TypeIndex - insert k v = unwrap >>> Map.insert k v >>> wrap + insert k v = over TypeIndex (Map.insert k v) query diff --git a/src/Docs/Search/TypeQuery.purs b/src/Docs/Search/TypeQuery.purs index a52c4c3..d3a5953 100644 --- a/src/Docs/Search/TypeQuery.purs +++ b/src/Docs/Search/TypeQuery.purs @@ -10,7 +10,7 @@ module Docs.Search.TypeQuery ) where -import Docs.Search.Config (config) +import Docs.Search.Config as Config import Docs.Search.Extra (foldl1, foldr1) import Docs.Search.TypeDecoder (QualifiedName(..), Type(..), joinConstraints, joinRows) import Docs.Search.Types (Identifier(..)) @@ -302,7 +302,7 @@ unify query type_ = go Nil (List.singleton { q: query, t: type_ }) penalty :: TypeQuery -> Type -> Int penalty typeQuery ty = let substs = unify typeQuery ty in - typeVarPenalty substs * config.penalties.typeVars + + typeVarPenalty substs * Config.penalties.typeVars + namesPenalty substs + mismatchPenalty substs @@ -341,12 +341,12 @@ namesPenalty = go 0 go p Nil = p go p (Match a b : rest) | a == b = go p rest - | otherwise = go (p + config.penalties.match) rest + | otherwise = go (p + Config.penalties.match) rest go p (MatchConstraints qcs tcs : rest) = let p' = Set.size (Set.union qcs tcs) - Set.size (Set.intersection qcs tcs) in - go (p + config.penalties.matchConstraint * p') rest - go p (RowsMismatch n m : rest) = go (config.penalties.rowsMismatch * abs (n - m)) rest + go (p + Config.penalties.matchConstraint * p') rest + go p (RowsMismatch n m : rest) = go (Config.penalties.rowsMismatch * abs (n - m)) rest go p (_ : rest) = go p rest @@ -356,11 +356,11 @@ mismatchPenalty = go 0 where go n Nil = n go n (Instantiate q t : rest) = go (n + typeSize t * - config.penalties.instantiate) rest + Config.penalties.instantiate) rest go n (Generalize q t : rest) = go (n + typeQuerySize q * - config.penalties.generalize) rest - go n (ExcessiveConstraint : rest) = go (n + config.penalties.excessiveConstraint) rest - go n (MissingConstraint : rest) = go (n + config.penalties.missingConstraint) rest + Config.penalties.generalize) rest + go n (ExcessiveConstraint : rest) = go (n + Config.penalties.excessiveConstraint) rest + go n (MissingConstraint : rest) = go (n + Config.penalties.missingConstraint) rest go n (Mismatch q t : rest) = go (n + typeQuerySize q + typeSize t) rest go n (TypeMismatch t : rest) = go (n + typeSize t) rest go n (QueryMismatch q : rest) = go (n + typeQuerySize q) rest diff --git a/src/Docs/Search/Types.purs b/src/Docs/Search/Types.purs index 0e2c023..83d6d61 100644 --- a/src/Docs/Search/Types.purs +++ b/src/Docs/Search/Types.purs @@ -63,6 +63,7 @@ instance decodeJsonPackageInfo :: DecodeJson PackageInfo where instance encodeJsonPackageInfo :: EncodeJson PackageInfo where encodeJson = genericEncodeJson + newtype PackageScore = PackageScore Int derive instance newtypePackageScore :: Newtype PackageScore _ @@ -75,7 +76,11 @@ derive newtype instance showPackageScore :: Show PackageScore derive newtype instance decodeJsonPackageScore :: DecodeJson PackageScore derive newtype instance encodeJsonPackageScore :: EncodeJson PackageScore -type URL = String + +newtype URL = URL String + +derive instance newtypeURL :: Newtype URL _ + newtype GlobalIdentifier = GlobalIdentifier String From 853ace81127af90028c0370f6edd322b5f63328f Mon Sep 17 00:00:00 2001 From: klntsky Date: Tue, 4 Aug 2020 14:55:31 +0300 Subject: [PATCH 18/45] Add local package modules and Prim* to the sidebar More newtype wrappers. --- src/Docs/Search/App.purs | 4 +-- src/Docs/Search/App/Sidebar.purs | 26 +++++++++++++------ src/Docs/Search/BrowserEngine.purs | 17 ++++++------- src/Docs/Search/Config.purs | 41 +++++++++++++++++------------- src/Docs/Search/Engine.purs | 2 +- src/Docs/Search/IndexBuilder.purs | 14 +++++----- src/Docs/Search/Meta.purs | 4 +-- src/Docs/Search/ModuleIndex.purs | 25 +++++++++--------- src/Docs/Search/PackageIndex.js | 24 ----------------- src/Docs/Search/PackageIndex.purs | 19 +++----------- src/Docs/Search/Types.purs | 16 +++++++++++- 11 files changed, 92 insertions(+), 100 deletions(-) delete mode 100644 src/Docs/Search/PackageIndex.js diff --git a/src/Docs/Search/App.purs b/src/Docs/Search/App.purs index ca87d25..fb0067b 100644 --- a/src/Docs/Search/App.purs +++ b/src/Docs/Search/App.purs @@ -103,8 +103,8 @@ main = do addEventListener hashchange listener true (Window.toEventTarget win) sbio <- do - component <- Sidebar.mkComponent moduleIndex.packageModules isIndexHTML - runUI component unit sidebarContainer + component <- Sidebar.mkComponent moduleIndex.packageModules isIndexHTML meta + runUI component unit sidebarContainer -- Subscribe to window focus events H.liftEffect do diff --git a/src/Docs/Search/App/Sidebar.purs b/src/Docs/Search/App/Sidebar.purs index 5d19b27..4b53781 100644 --- a/src/Docs/Search/App/Sidebar.purs +++ b/src/Docs/Search/App/Sidebar.purs @@ -1,8 +1,9 @@ module Docs.Search.App.Sidebar where import Docs.Search.Config as Config +import Docs.Search.Meta (Meta) import Docs.Search.ModuleIndex (PackedModuleIndex) -import Docs.Search.Types (ModuleName, PackageName(..)) +import Docs.Search.Types (ModuleName, PackageInfo(..), PackageName) import Prelude @@ -43,10 +44,11 @@ data IsIndexHTML = IsIndexHTML | NotIndexHTML derive instance isIndexHTMLEq :: Eq IsIndexHTML -type State = { moduleIndex :: Map PackageName (Set ModuleName) +type State = { moduleIndex :: Map PackageInfo (Set ModuleName) , groupingMode :: GroupingMode , moduleNames :: Array ModuleName , isIndexHTML :: IsIndexHTML + , localPackageName :: PackageName } @@ -54,8 +56,9 @@ mkComponent :: forall i . PackedModuleIndex -> IsIndexHTML + -> Meta -> Aff (H.Component HH.HTML Query i Action Aff) -mkComponent moduleIndex isIndexHTML = do +mkComponent moduleIndex isIndexHTML { localPackageName } = do groupingMode <- H.liftEffect loadGroupingModeFromLocalStorage pure $ H.mkComponent @@ -63,6 +66,7 @@ mkComponent moduleIndex isIndexHTML = do , groupingMode , moduleNames , isIndexHTML + , localPackageName } , render , eval: H.mkEval $ H.defaultEval { handleAction = handleAction @@ -105,7 +109,7 @@ render :: forall m . State -> H.ComponentHTML Action () m -render { moduleIndex, groupingMode, moduleNames, isIndexHTML } = +render { moduleIndex, groupingMode, moduleNames, isIndexHTML, localPackageName } = HH.div [ HP.classes [ wrap "col" , wrap $ if isIndexHTML == IsIndexHTML @@ -133,10 +137,16 @@ render { moduleIndex, groupingMode, moduleNames, isIndexHTML } = ] where - renderPackageEntry (PackageName packageName /\ modules) = + renderPackageEntry (package /\ modules) = HH.li [ HP.classes [ wrap "li-package" ] ] - [ HH.details_ - [ HH.summary_ [ HH.text packageName ] + [ HH.details_ $ + [ HH.summary_ [ HH.text $ + case package of + Package packageName -> unwrap packageName + LocalPackage -> unwrap localPackageName + Builtin -> "Built-in" + UnknownPackage -> "Unknown package" + ] , HH.ul_ $ Set.toUnfoldable modules <#> renderModuleName ] ] @@ -147,7 +157,7 @@ render { moduleIndex, groupingMode, moduleNames, isIndexHTML } = [ HH.text $ unwrap moduleName ] ] - packageList :: Array (PackageName /\ Set ModuleName) + packageList :: Array (PackageInfo /\ Set ModuleName) packageList = Map.toUnfoldable moduleIndex diff --git a/src/Docs/Search/BrowserEngine.purs b/src/Docs/Search/BrowserEngine.purs index b3af249..d71c212 100644 --- a/src/Docs/Search/BrowserEngine.purs +++ b/src/Docs/Search/BrowserEngine.purs @@ -7,6 +7,7 @@ import Docs.Search.Engine (Engine, EngineState, Index) import Docs.Search.SearchResult (SearchResult) import Docs.Search.TypeIndex (TypeIndex) import Docs.Search.TypeIndex as TypeIndex +import Docs.Search.Types (PartId(..), URL) import Docs.Search.ModuleIndex as ModuleIndex import Prelude @@ -32,7 +33,7 @@ import Effect.Aff (Aff, try) newtype PartialIndex - = PartialIndex (Map Int Index) + = PartialIndex (Map PartId Index) derive instance newtypePartialIndex :: Newtype PartialIndex _ derive newtype instance semigroupPartialIndex :: Semigroup PartialIndex @@ -52,13 +53,11 @@ query -> Aff { index :: PartialIndex, results :: Array SearchResult } query index@(PartialIndex indexMap) input = do let - path :: List Char path = List.fromFoldable $ String.toCharArray $ input - partId :: Int partId = getPartId path case Map.lookup partId indexMap of @@ -119,16 +118,16 @@ browserSearchEngine = -- | Find in which part of the index this path can be found. -getPartId :: List Char -> Int +getPartId :: List Char -> PartId getPartId (a : b : _) = - (Char.toCharCode a + Char.toCharCode b) `mod` Config.numberOfIndexParts + PartId $ (Char.toCharCode a + Char.toCharCode b) `mod` Config.numberOfIndexParts getPartId (a : _) = - Char.toCharCode a `mod` Config.numberOfIndexParts -getPartId _ = 0 + PartId $ Char.toCharCode a `mod` Config.numberOfIndexParts +getPartId _ = PartId 0 -- | Load a part of the index by injecting a