From 4ce20e3251f8f069a3990b93a1aff9438496abbf Mon Sep 17 00:00:00 2001 From: Vladimir Kalnitsky Date: Sun, 21 Jul 2019 17:29:43 +0300 Subject: [PATCH] added: CLI interface; added: search REPL (#5) * added: CLI interface added: search REPL * added: shebang to the executable file * added: automatic deployments * better heuristics for package name detection * added: previews --- .gitignore | 1 + .travis.yml | 17 +- README.md | 33 +++- package.json | 43 +++++ packages.dhall | 22 +++ preview.png | Bin 0 -> 58317 bytes spago.dhall | 2 + src/Docs/Search/App/SearchResults.purs | 101 ++++------ src/Docs/Search/Config.purs | 13 +- src/Docs/Search/Declarations.purs | 14 +- src/Docs/Search/DocsJson.purs | 7 +- src/Docs/Search/Engine.purs | 59 ++++++ src/Docs/Search/Extra.js | 9 + src/Docs/Search/Extra.purs | 22 ++- src/Docs/Search/Index.purs | 1 + src/Docs/Search/IndexBuilder.js | 7 + src/Docs/Search/IndexBuilder.purs | 183 ++++++++++++------ src/Docs/Search/Interactive.js | 5 + src/Docs/Search/Interactive.purs | 258 +++++++++++++++++++++++++ src/Docs/Search/Main.purs | 103 ++++++++++ src/Docs/Search/Terminal.purs | 23 +++ src/Docs/Search/TypeIndex.purs | 12 +- src/Docs/Search/TypePrinter.purs | 156 +++++++++++++++ src/Docs/Search/TypeQuery.purs | 179 ++++++++++------- test/Test/IndexBuilder.purs | 14 +- 25 files changed, 1039 insertions(+), 245 deletions(-) create mode 100644 package.json create mode 100644 preview.png create mode 100644 src/Docs/Search/Engine.purs create mode 100644 src/Docs/Search/Extra.js create mode 100644 src/Docs/Search/IndexBuilder.js create mode 100644 src/Docs/Search/Interactive.js create mode 100644 src/Docs/Search/Interactive.purs create mode 100644 src/Docs/Search/Main.purs create mode 100644 src/Docs/Search/Terminal.purs create mode 100644 src/Docs/Search/TypePrinter.purs diff --git a/.gitignore b/.gitignore index 5e1fd31..e17f215 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ /.purs* /.psa* /.spago/ +/dist/ diff --git a/.travis.yml b/.travis.yml index f61a120..8103279 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,24 +19,31 @@ install: - tar -xvf $HOME/purescript.tar.gz -C $HOME/ - chmod a+x $HOME/purescript - npm install -g spago +- npm install - spago install script: - spago build - spago test - spago docs -- spago bundle-app -m Docs.Search.App --to docs-search-app.js -- spago bundle-app -m Docs.Search.IndexBuilder --to index-builder.js -- node index-builder.js +- npm run build +- node dist/main.js build-index deploy: - provider: releases api_key: $API_KEY file: - - docs-search-app.js - - index-builder.js + - dist/docs-search-app.js + - dist/main.js skip_cleanup: true on: tags: true script: - echo 'done' + - provider: npm + api_key: $NPM_API_KEY + email: klntsky@gmail.com + skip_cleanup: true + on: + tags: true + branch: master diff --git a/README.md b/README.md index 7d24981..eb6a4b5 100644 --- a/README.md +++ b/README.md @@ -4,21 +4,34 @@ An app that adds search capabilities to generated documentation for purescript code. -The goal is to replicate all functionality of pursuit, including querying by type. +It supports nearly-all functionality of [Pursuit](https://github.com/purescript/pursuit), including querying by type. -See [#89](https://github.com/spacchetti/spago/issues/89). +## Installing -To see it in action, run the following: +Run `npm install purescript-docs-search`. -``` -spago build -spago docs -spago bundle-app -m Docs.Search.App --to generated-docs/docs-search-app.js -spago run -m Docs.Search.IndexBuilder -``` +## Usage -## UI +There are two usage scenarios: + +### Patching static documentation + +Use `purescript-docs-search build-index` command to patch HTML files located in `generated-docs/html`. You then will be able to search for declarations or types: + +![Preview](preview.png) The user interface of the app is optimised for keyboard-only use. **S** hotkey can be used to focus on the search field, **Escape** can be used to leave it. Pressing **Escape** twice will close the search results listing. + +### Using the CLI + +Running `purescript-docs-search` within a project directory will open an interactive command-line session. + +Note that unlike in Pursuit, most relevant results will appear last. + +A quick demo: + +[![asciicast](https://asciinema.org/a/Hexie5JoWjlAqLqv2IgafIdb9.svg)](https://asciinema.org/a/Hexie5JoWjlAqLqv2IgafIdb9) + +You may notice that the CLI offers slightly better results than the web interface. This is a performance tradeoff. diff --git a/package.json b/package.json new file mode 100644 index 0000000..a495f69 --- /dev/null +++ b/package.json @@ -0,0 +1,43 @@ +{ + "name": "purescript-docs-search", + "version": "0.0.1", + "description": "Search frontend for the documentation generated by the PureScript compiler.", + "main": "dist/main.js", + "directories": { + "test": "test" + }, + "bin": { + "purescript-docs-search": "dist/main.js" + }, + "files": [ + "dist/main.js", + "dist/docs-search-app.js", + "README.md" + ], + "scripts": { + "test": "spago test", + "bundle-app": "spago bundle-app -m Docs.Search.App --to dist/docs-search-app.js", + "bundle-main": "spago bundle-app -m Docs.Search.Main --to dist/main.js && browserify --no-builtins --no-commondir --no-detect-globals --node dist/main.js --outfile dist/main-bundled.js && echo \"#!/usr/bin/env node\" > dist/main.js && cat dist/main-bundled.js >> dist/main.js && rm dist/main-bundled.js", + "build": "npm run bundle-app && npm run bundle-main", + "clean": "rm -rf dist" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/spacchetti/purescript-docs-search.git" + }, + "keywords": [ + "purescript" + ], + "author": "Kalnitsky Vladimir ", + "license": "BSD-3-Clause", + "bugs": { + "url": "https://github.com/spacchetti/purescript-docs-search/issues" + }, + "homepage": "https://github.com/spacchetti/purescript-docs-search#readme", + "dependencies": {}, + "devDependencies": { + "browserify": "^16.3.0", + "glob": "^7.1.4", + "spago": "^0.8.5" + } +} diff --git a/packages.dhall b/packages.dhall index c5188a1..6d40ce1 100644 --- a/packages.dhall +++ b/packages.dhall @@ -47,6 +47,28 @@ let additions = [ "css", "halogen" ] "https://github.com/slamdata/purescript-halogen-css.git" "v8.0.0" + , optparse = + mkPackage + [ "prelude" + , "effect" + , "exitcodes" + , "strings" + , "ordered-collections" + , "arrays" + , "console" + , "memoize" + , "transformers" + , "exists" + , "node-process" + , "free" + ] + "https://github.com/f-o-a-m/purescript-optparse.git" + "v3.0.1" + , exitcodes = + mkPackage + [ "enums" ] + "https://github.com/Risto-Stevcev/purescript-exitcodes.git" + "v4.0.0" } in upstream ⫽ overrides ⫽ additions diff --git a/preview.png b/preview.png new file mode 100644 index 0000000000000000000000000000000000000000..10702237a9963145f24229911d18ace87569eadb GIT binary patch literal 58317 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 B!z};+ literal 0 HcmV?d00001 diff --git a/spago.dhall b/spago.dhall index 643f9e5..72bee33 100644 --- a/spago.dhall +++ b/spago.dhall @@ -21,6 +21,8 @@ , "node-fs" , "node-fs-aff" , "node-process" + , "node-readline" + , "optparse" , "profunctor" , "search-trie" , "string-parsers" diff --git a/src/Docs/Search/App/SearchResults.purs b/src/Docs/Search/App/SearchResults.purs index d5df217..04c9221 100644 --- a/src/Docs/Search/App/SearchResults.purs +++ b/src/Docs/Search/App/SearchResults.purs @@ -7,24 +7,19 @@ import Docs.Search.Config (config) import Docs.Search.Declarations (DeclLevel(..), declLevelToHashAnchor) import Docs.Search.DocsJson (DataDeclType(..)) import Docs.Search.Extra ((>#>)) -import Docs.Search.Index (Index) -import Docs.Search.Index as Index -import Docs.Search.SearchResult (ResultInfo(..), SearchResult, typeOf) +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.TypeIndex as TypeIndex -import Docs.Search.TypeQuery (TypeQuery(..), parseTypeQuery, penalty) +import Docs.Search.Engine as SearchEngine +import Docs.Search.Engine (ResultsType(..)) import CSS (textWhitespace, whitespacePreWrap) import Data.Array ((!!)) import Data.Array as Array -import Data.Either (hush) import Data.List as List -import Data.Maybe (Maybe(..), isJust, maybe) +import Data.Maybe (Maybe(..), isJust) import Data.Newtype (unwrap, wrap) -import Data.String (length) as String import Data.String.CodeUnits (stripSuffix) as String -import Data.String.Common (toLower, trim) as String +import Data.String.Common (null, trim) as String import Data.String.Pattern (Pattern(..)) as String import Effect.Aff (Aff) import Halogen as H @@ -38,15 +33,11 @@ import Web.HTML as HTML import Web.HTML.Location as Location import Web.HTML.Window as Window -data Mode = Off | Loading | Active | InputTooShort +data Mode = Off | Loading | Active derive instance eqMode :: Eq Mode --- | Is it a search by type or by name? -data ResultsType = TypeResults TypeQuery | DeclResults - -type State = { index :: Index - , typeIndex :: TypeIndex +type State = { searchEngineState :: SearchEngine.State , results :: Array SearchResult , resultsType :: ResultsType , input :: String @@ -68,8 +59,7 @@ mkComponent -> H.Component HH.HTML Query i o Aff mkComponent contents = H.mkComponent - { initialState: const { index: mempty - , typeIndex: mempty + { initialState: const { searchEngineState: mempty , results: [] , resultsType: DeclResults , input: "" @@ -99,36 +89,20 @@ handleQuery (MessageFromSearchField (InputUpdated input_) next) = do state <- H.modify (_ { input = input }) - if String.length input < 2 + if String.null input then do - if input == "" - then do H.modify_ (_ { mode = Off }) showPageContents - else do - H.modify_ (_ { mode = InputTooShort }) - hidePageContents else do H.modify_ (_ { mode = Loading, resultsCount = config.resultsCount }) void $ H.fork do - let resultsType = - maybe DeclResults TypeResults (hush (parseTypeQuery state.input) - >>= isValuableTypeQuery) - - case resultsType of - - DeclResults -> do - { index, results } <- H.liftAff $ Index.query state.index (String.toLower state.input) - H.modify_ (_ { results = results - , mode = Active - , index = index }) - - TypeResults query -> do - { index, results } <- H.liftAff $ TypeIndex.query state.typeIndex query - H.modify_ (_ { results = sortByDistance query results - , mode = Active - , typeIndex = index }) + { searchEngineState, results, resultsType } <- H.liftAff $ + SearchEngine.query state.searchEngineState state.input + H.modify_ (_ { results = results + , mode = Active + , searchEngineState = searchEngineState + , resultsType = resultsType }) hidePageContents @@ -181,12 +155,6 @@ render { mode: Off } = HH.div_ [] render { mode: Loading } = renderContainer $ [ HH.h1_ [ HH.text "Loading..." ] ] -render { mode: InputTooShort } = - renderContainer $ - [ HH.h1_ [ HH.text "Error" ] ] <> - [ HH.div [ HP.classes [ wrap "result", wrap "result--empty" ] ] - [ HH.text "Search query is too short." ] - ] render state@{ mode: Active, results: [] } = renderContainer $ @@ -299,11 +267,7 @@ renderResultType renderResultType result = case result.info of ValueResult { type: ty } -> - wrapSignature [ HH.a [ makeHref ValueLevel false result.moduleName result.name - , HE.onClick $ const $ Just $ SearchResultClicked result.moduleName ] - [ HH.text result.name ] - , HH.text " :: " - , renderType ty ] + wrapSignature $ renderValueSignature result ty TypeClassResult info -> wrapSignature $ renderTypeClassSignature info result @@ -321,6 +285,21 @@ renderResultType result = wrapSignature signature = [ HH.pre [ HP.class_ (wrap "result__signature") ] [ HH.code_ signature ] ] +renderValueSignature + :: forall a rest + . { moduleName :: String + , name :: String + | rest + } + -> Type + -> Array (HH.HTML a Action) +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 " :: " + , renderType ty ] + renderTypeClassSignature :: forall a rest . { fundeps :: FunDeps @@ -582,8 +561,8 @@ renderQualifiedName isInfix level (QualifiedName { moduleName, name }) renderKind :: forall a - . Kind -> - HH.HTML a Action + . Kind + -> HH.HTML a Action renderKind = case _ of Row k1 -> HH.span_ [ HH.text "# ", renderKind k1 ] FunKind k1 k2 -> HH.span_ [ renderKind k1, syntax " -> ", renderKind k2 ] @@ -616,17 +595,3 @@ syntax str = HH.span [ HP.class_ (wrap "syntax") ] [ HH.text str ] space :: forall a b. HH.HTML a b space = HH.text " " - -isValuableTypeQuery :: TypeQuery -> Maybe TypeQuery -isValuableTypeQuery (QVar _) = Nothing -isValuableTypeQuery (QConst _) = Nothing -isValuableTypeQuery query = Just query - -sortByDistance :: TypeQuery -> Array SearchResult -> Array SearchResult -sortByDistance typeQuery results = - _.result <$> Array.sortBy comparePenalties resultsWithPenalties - where - comparePenalties r1 r2 = compare r1.penalty r2.penalty - resultsWithPenalties = results <#> - \result -> { penalty: typeOf (unwrap result).info <#> penalty typeQuery - , result } diff --git a/src/Docs/Search/Config.purs b/src/Docs/Search/Config.purs index 36fa956..ad7ea39 100644 --- a/src/Docs/Search/Config.purs +++ b/src/Docs/Search/Config.purs @@ -17,19 +17,20 @@ config = , numberOfIndexParts: 50 -- ^ In how many parts the index should be splitted? , mkIndexPartPath: - \(partId :: Int) -> "generated-docs/index/declarations/" <> show partId <> ".js" + \(partId :: Int) -> "/index/declarations/" <> show partId <> ".js" , mkIndexPartLoadPath: \(partId :: Int) -> "../index/declarations/" <> show partId <> ".js" , resultsCount: 25 -- ^ How many results to show by default? - , penalties: { typeVars: 6 + , penalties: { typeVars: 2 , match: 2 , matchConstraint: 1 - , instantiate: 1 - , generalize: 4 - , rowsMismatch: 6 - , mismatch: 10 + , 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 } diff --git a/src/Docs/Search/Declarations.purs b/src/Docs/Search/Declarations.purs index 1f11efd..843a918 100644 --- a/src/Docs/Search/Declarations.purs +++ b/src/Docs/Search/Declarations.purs @@ -8,6 +8,7 @@ import Docs.Search.TypeDecoder (Constraint(..), QualifiedName(..), Type(..), joi import Control.Alt ((<|>)) import Data.Array ((!!)) +import Data.Array as Array import Data.Foldable (foldr) import Data.List (List, (:)) import Data.List as List @@ -15,8 +16,8 @@ import Data.Maybe (Maybe(..), fromMaybe) import Data.Newtype (class Newtype, unwrap, wrap) import Data.Search.Trie (Trie, alter) import Data.String.CodeUnits (stripPrefix, stripSuffix, toCharArray) -import Data.String.Common (toLower) import Data.String.Common (split) as String +import Data.String.Common (toLower) import Data.String.Pattern (Pattern(..)) type ModuleName = String @@ -178,11 +179,18 @@ extractPackageName name = let chunks = String.split (Pattern "/") name in fromMaybe "" $ chunks !! 0 >>= \dir -> - -- TODO: is it safe to assume that directory name is ".spago"? if dir == ".spago" then chunks !! 1 else - Just "" + let + bowerComponentsIndex = + Array.findIndex (_ == "bower_components") chunks + in + case bowerComponentsIndex of + Just n -> + chunks !! (n + 1) + Nothing -> + Just "" resultsForChildDeclaration :: PackageName diff --git a/src/Docs/Search/DocsJson.purs b/src/Docs/Search/DocsJson.purs index 69156af..a98d91f 100644 --- a/src/Docs/Search/DocsJson.purs +++ b/src/Docs/Search/DocsJson.purs @@ -2,10 +2,9 @@ module Docs.Search.DocsJson where import Prelude -import Docs.Search.TypeDecoder +import Docs.Search.TypeDecoder (Constraint, FunDeps, Kind, Type, TypeArgument) -import Control.Promise (Promise, toAffE) -import Data.Argonaut.Core (Json, fromString, stringify, toString) +import Data.Argonaut.Core (fromString, stringify, toString) import Data.Argonaut.Decode (class DecodeJson, decodeJson, (.:), (.:?)) import Data.Argonaut.Encode (class EncodeJson, encodeJson) import Data.Either (Either(..)) @@ -13,8 +12,6 @@ import Data.Generic.Rep (class Generic) import Data.Generic.Rep.Show (genericShow) import Data.Maybe (Maybe(..)) import Data.Newtype (class Newtype, unwrap) -import Effect (Effect) -import Effect.Aff (Aff) newtype DocsJson = DocsJson { name :: String diff --git a/src/Docs/Search/Engine.purs b/src/Docs/Search/Engine.purs new file mode 100644 index 0000000..93fba11 --- /dev/null +++ b/src/Docs/Search/Engine.purs @@ -0,0 +1,59 @@ +module Docs.Search.Engine where + +import Prelude + +import Docs.Search.TypeQuery (TypeQuery(..), parseTypeQuery, penalty) +import Docs.Search.SearchResult (SearchResult, typeOf) +import Docs.Search.Index as Index +import Docs.Search.Index (Index) +import Docs.Search.TypeIndex as TypeIndex +import Docs.Search.TypeIndex (TypeIndex) + +import Data.Array as Array +import Data.Either (hush) +import Data.Maybe (Maybe(..)) +import Data.Newtype (unwrap) +import Data.String.Common as String +import Effect.Aff (Aff) + +data ResultsType = TypeResults TypeQuery | DeclResults + +type State = { index :: Index + , typeIndex :: TypeIndex + } + +query + :: State + -> String + -> Aff { searchEngineState :: State + , results :: Array SearchResult + , resultsType :: ResultsType + } +query { index, typeIndex } input = + case hush (parseTypeQuery input) >>= isValuableTypeQuery of + Nothing -> do + response <- Index.query index (String.toLower input) + pure { searchEngineState: { index: response.index, typeIndex } + , results: response.results + , resultsType: DeclResults } + + Just typeQuery -> do + response <- TypeIndex.query typeIndex typeQuery + pure { searchEngineState: { index, typeIndex: response.typeIndex } + , results: sortByDistance typeQuery response.results + , resultsType: TypeResults typeQuery } + +isValuableTypeQuery :: TypeQuery -> Maybe TypeQuery +isValuableTypeQuery (QVar _) = Nothing +isValuableTypeQuery (QConst _) = Nothing +isValuableTypeQuery other = Just other + +sortByDistance :: TypeQuery -> Array SearchResult -> Array SearchResult +sortByDistance typeQuery results = + _.result <$> Array.sortBy comparePenalties resultsWithPenalties + where + comparePenalties r1 r2 = compare r1.penalty r2.penalty + resultsWithPenalties = + results <#> + \result -> { penalty: typeOf (unwrap result).info <#> penalty typeQuery + , result } diff --git a/src/Docs/Search/Extra.js b/src/Docs/Search/Extra.js new file mode 100644 index 0000000..5d32f33 --- /dev/null +++ b/src/Docs/Search/Extra.js @@ -0,0 +1,9 @@ +/* global exports require */ + +var glob = require('glob'); + +exports.glob = function (pattern) { + return function () { + return glob.sync(pattern); + }; +}; diff --git a/src/Docs/Search/Extra.purs b/src/Docs/Search/Extra.purs index d06f0ef..7027fff 100644 --- a/src/Docs/Search/Extra.purs +++ b/src/Docs/Search/Extra.purs @@ -2,8 +2,12 @@ module Docs.Search.Extra where import Prelude -import Data.Foldable (class Foldable, foldMap) +import Data.Foldable (class Foldable, foldMap, foldl) +import Data.List.NonEmpty (NonEmptyList, cons', uncons) import Data.Maybe (Maybe(..)) +import Effect (Effect) +import Data.List as List +import Data.List ((:)) whenJust :: forall a m. Monad m => Maybe a -> (a -> m Unit) -> m Unit whenJust (Just a) f = f a @@ -13,3 +17,19 @@ foldMapFlipped :: forall a m f. Foldable f => Monoid m => f a -> (a -> m) -> m foldMapFlipped = flip foldMap infixr 7 foldMapFlipped as >#> + +foreign import glob :: String -> Effect (Array String) + +foldl1 :: forall a. (a -> a -> a) -> NonEmptyList a -> a +foldl1 f as = + case uncons as of + { head, tail } -> foldl f head tail + +foldr1 :: forall a. (a -> a -> a) -> NonEmptyList a -> a +foldr1 f = go List.Nil + where + go acc x = case uncons x of + { head, tail } -> case List.uncons tail of + Nothing -> List.foldl (flip f) head acc + Just { head: head1, tail: tail1 } -> + go (head : acc) (cons' head1 tail1) diff --git a/src/Docs/Search/Index.purs b/src/Docs/Search/Index.purs index 1975395..df384a4 100644 --- a/src/Docs/Search/Index.purs +++ b/src/Docs/Search/Index.purs @@ -98,6 +98,7 @@ getPartId (a : _) = Char.toCharCode a `mod` config.numberOfIndexParts getPartId _ = 0 +-- | Load a part of the index by injecting a