diff --git a/.ghci b/.ghci deleted file mode 100644 index b258954f2f..0000000000 --- a/.ghci +++ /dev/null @@ -1,31 +0,0 @@ --- See docs/💡ProTip!.md -:def! pretty \ _ -> return ":set -interactive-print Semantic.Util.Pretty.prettyShow" -:def! no-pretty \_ -> return ":set -interactive-print System.IO.print" -:def! r \_ -> return (unlines [":reload", ":pretty"]) - --- See docs/💡ProTip!.md for documentation & examples. -:{ -assignmentExample lang = case lang of - "Python" -> mk "py" "python" - "Go" -> mk "go" "go" - "Ruby" -> mk "rb" "ruby" - "JavaScript" -> mk "js" "typescript" - "TypeScript" -> mk "ts" "typescript" - "Haskell" -> mk "hs" "haskell" - "Markdown" -> mk "md" "markdown" - "JSON" -> mk "json" "json" - "Java" -> mk "java" "java" - "PHP" -> mk "php" "php" - _ -> mk "" "" - where mk fileExtension parser = putStrLn ("example: fmap (() <$) . runTask . parse " ++ parser ++ "Parser =<< Semantic.Util.blob \"example." ++ fileExtension ++ "\"") >> return ("import Parsing.Parser\nimport Semantic.Task\nimport Semantic.Util") -:} -:def! assignment assignmentExample - --- Enable breaking on errors for code written in the repl. -:seti -fbreak-on-error - --- Continue loading after warnings when in the repl. -:set -Wwarn - --- Use a cyan lambda as the prompt. -:set prompt "\ESC[1;36m\STXλ \ESC[m\STX" diff --git a/.ghci.sample b/.ghci.sample new file mode 100644 index 0000000000..dcbd090ba3 --- /dev/null +++ b/.ghci.sample @@ -0,0 +1,31 @@ +-- Consider copying this to your ~/.ghc/ghci.conf file: + +-- Pretty-printing +:set -ignore-package pretty-simple -package pretty-simple +:def! pretty \ _ -> pure ":set -interactive-print Text.Pretty.Simple.pPrint" +:def! no-pretty \ _ -> pure ":set -interactive-print System.IO.print" +:def! r \_ -> pure ":reload\n:pretty" + +-- Turn on some language extensions you use a lot +:seti -XFlexibleContexts -XOverloadedStrings -XTypeApplications + +-- Break on errors +:seti -fbreak-on-error + +-- Automatically show the code around breakpoints +:set stop :list + +-- Use a cyan lambda as the prompt +:set prompt "\ESC[1;36m\STXλ \ESC[m\STX" + +-- Better errors +:set -ferror-spans -freverse-errors -fprint-expanded-synonyms + +-- Path-local ghci history +:set -flocal-ghci-history + +-- Better typed holes +:set -funclutter-valid-hole-fits -fabstract-refinement-hole-fits -frefinement-level-hole-fits=2 + +-- Enable pretty-printing immediately +:pretty diff --git a/.ghci.semantic b/.ghci.semantic new file mode 100644 index 0000000000..e34f0a0d54 --- /dev/null +++ b/.ghci.semantic @@ -0,0 +1,70 @@ +-- GHCI settings for semantic, collected by running cabal repl -v and checking out the flags cabal passes to ghc. +-- These live here instead of script/repl for ease of commenting. +-- These live here instead of .ghci so cabal repl remains unaffected. + +-- Basic verbosity +:set -v1 + +-- No optimizations +:set -O0 + +-- Compile to object code +:set -fwrite-interface -fobject-code + +-- Write build products to dist-repl (so that we don’t clobber 'cabal build' outputs) +:set -outputdir dist-repl/build/x86_64-osx/ghc-8.6.5/semantic-0.8.0.0/build +:set -odir dist-repl/build/x86_64-osx/ghc-8.6.5/semantic-0.8.0.0/build +:set -hidir dist-repl/build/x86_64-osx/ghc-8.6.5/semantic-0.8.0.0/build +:set -stubdir dist-repl/build/x86_64-osx/ghc-8.6.5/semantic-0.8.0.0/build + +-- Look for autogen’d files in dist-repl +:set -idist-repl/build/x86_64-osx/ghc-8.6.5/semantic-0.8.0.0/build/autogen + +-- Load all our sources… remember to keep this up to date when we add new packages! +-- But don’t add semantic-source, it’s important that we get that from hackage. +:set -isemantic-analysis/src +:set -isemantic-ast/src +:set -isemantic-core/src +:set -isemantic-java/src +:set -isemantic-json/src +:set -isemantic-python/src +:set -isemantic-tags/src +:set -iapp +:set -isrc +:set -ibench +:set -itest + +-- Default language mode & extensions +:set -XHaskell2010 +:set -XStrictData + +-- Warnings for compiling .hs files +:set -Weverything +:set -Wno-all-missed-specialisations +:set -Wno-implicit-prelude +:set -Wno-missed-specialisations +:set -Wno-missing-import-lists +:set -Wno-missing-local-signatures +:set -Wno-monomorphism-restriction +:set -Wno-name-shadowing +:set -Wno-safe +:set -Wno-unsafe +:set -Wno-star-is-type +-- Bonus: silence “add these modules to your .cabal file” warnings for files we :load +:set -Wno-missing-home-modules + +-- Don’t fail on warnings when in the repl +:set -Wwarn + +-- Warnings for code written in the repl +:seti -Weverything +:seti -Wno-all-missed-specialisations +:seti -Wno-implicit-prelude +:seti -Wno-missed-specialisations +:seti -Wno-missing-import-lists +:seti -Wno-missing-local-signatures +:seti -Wno-monomorphism-restriction +:seti -Wno-name-shadowing +:seti -Wno-safe +:seti -Wno-unsafe +:seti -Wno-star-is-type diff --git a/.gitignore b/.gitignore index 6ad025336b..adba497525 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,8 @@ profiles cabal.project.local* dist dist-newstyle +dist-repl +.ghci .ghc.environment.* .ghci_history diff --git "a/docs/\360\237\222\241ProTip!.md" "b/docs/\360\237\222\241ProTip!.md" index f65e3ca71a..8c36f2b238 100644 --- "a/docs/\360\237\222\241ProTip!.md" +++ "b/docs/\360\237\222\241ProTip!.md" @@ -148,19 +148,27 @@ _Voilà!_ You’re now looking at the source code for the datatype generated fro ## GHCi -The Haskell interactive repl (GHCi) allows you to quickly typecheck your work and test out ideas interactively. It’s always worth having a repl open, but we’ve particularly tuned some workflows, e.g. semantic assignment development, for the repl. +The Haskell interactive repl (GHCi) allows you to quickly typecheck your work and test out ideas interactively. It’s always worth having a repl open, and we’ve particularly tuned some workflows for the repl. -[pretty-printing]: pretty-printing +Full docs for ghci can be found in the [user’s guide][ghci user’s guide]. +[ghci user’s guide]: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghci.html -### Configuration -We configure `ghci` with defaults & macros for use with `semantic` via the [`.ghci` file][] at the project root, and you can further customize its behaviour via the `~/.ghci` file. +### Multiple components -Full docs for ghci can be found in the [user’s guide][ghci user’s guide]. +`semantic` consists of multiple packages and components, which makes it somewhat challenging to load into `ghci` using e.g. `cabal repl`. To help that, we provide [`script/repl`][] to automate working with multiple components & packages. Unlike when using `cabal repl`, after loading you will need to explicitly `:load` (at least some of) the sources you need to work with. For example, when working in the main `semantic` package, almost everything can be loaded with `:load Semantic.CLI`, since the `Semantic.CLI` module ultimately depends on just about everything else in the repo. -[ghci user’s guide]: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghci.html -[`.ghci` file]: https://github.com/github/semantic/blob/master/.ghci +This script is also set up to store intermediate build products in a separate `dist-repl` dir to avoid colliding with normal builds. + +[`script/repl`]: https://github.com/github/semantic/blob/master/script/repl + + +### Configuration + +`ghci` can be configured with scripts containing Haskell statements and repl commands. By default, the `~/.ghc/ghci.conf` file will be loaded, as well as a `.ghci` file in the working directory, if any. We don’t currently provide such for use with `semantic`, but we do provide a [`.ghci.sample`][] file which we suggest copying to `~/.ghc/ghci.conf` for better typed holes, pretty-printing via `pretty-simple`, and a simple prompt. + +[`.ghci.sample`]: https://github.com/github/semantic/blob/master/.ghci.sample ### Managing history @@ -178,9 +186,9 @@ maxHistorySize: Nothing ### Pretty-printing -By default, GHCi prints the results of expressions using their `Show` instances, which can be particularly difficult to read for large recursive structures like `Term`s and `Diff`s. The project’s [`.ghci` file][] provides `:pretty` & `:no-pretty` macros which respectively enable & disable colourized, pretty-printed formatting of result values instead. These macros depend on the the `pretty-show` & `hscolour` packages. +By default, GHCi prints the results of expressions using their `Show` instances, which can be particularly difficult to read for large recursive structures like `Term`s and `Diff`s. The project’s [`.ghci.sample`][] file provides `:pretty` & `:no-pretty` macros which respectively enable & disable colourized, pretty-printed formatting of result values instead. These macros depend on the the `pretty-simple` package. -Since `:reload`ing resets local bindings, the [`.ghci` file][] also provides a convenient `:r` macro which reloads and then immediately re-enables `:pretty`. +Since `:reload`ing resets local bindings, the file also provides a convenient `:r` macro which reloads and then immediately re-enables `:pretty`. You can use `:pretty` & `:no-pretty` like so: @@ -203,60 +211,12 @@ You can use `:pretty` & `:no-pretty` like so: ``` -### Working in Assignment - -When working in assignment, some setup is required. This macro automates that by automatically importing the necessary modules and outputs an example command. If you provide the language you are working with as an optional parameter, the example command is formatted for that language's specific needs (parser, example file extension, etc.). - -The macro is defined as: - -``` -:{ -assignmentExample lang = case lang of - "Python" -> mk "py" "python" - "Go" -> mk "go" "go" - "Ruby" -> mk "rb" "ruby" - "JavaScript" -> mk "js" "typescript" - "TypeScript" -> mk "ts" "typescript" - "Haskell" -> mk "hs" "haskell" - "Markdown" -> mk "md" "markdown" - "JSON" -> mk "json" "json" - _ -> mk "" "" - where mk fileExtension parser = putStrLn ("example: fmap (() <$) . runTask . parse " ++ parser ++ "Parser =<< Semantic.Util.blob \"example." ++ fileExtension ++ "\"") >> return ("import Parsing.Parser\nimport Semantic.Task\nimport Semantic.Util") -:} - -:def assignment assignmentExample -``` - -And is invoked in GHCi like: - -``` -λ :assignment Python -``` - -The output produces a one line expression assuming the syntax to assign is in a file named `example` with the relevant programming language extension: - -```haskell -quieterm <$> parseFile pythonParser "example.py" -``` - - -### Inspecting TreeSitter ASTs - -Inspecting the parse tree from TreeSitter can be helpful for debugging. In GHCi, the command below allows viewing the TreeSitter production name of each node in the TreeSitter AST: - -```haskell -import TreeSitter.Java -fmap nodeSymbol <$> parseFile javaASTParser "example.java" -``` - - -### Using Threadscope +## Using Threadscope Threadscope is a tool for profiling the multi-threaded performance of Haskell programs. It allows us to see how work is shared across processors and identify performance issues related to garbage collection or bottlenecks in our processes. To install threadscope: -1. Download a prebuilt binary from https://github.com/haskell/ThreadScope/releases . -2. `chmod a+x` the result of extracting the release. -3. `brew install gtk+ gtk-mac-integration`. -4. profit. +1. Download a prebuilt binary from https://github.com/haskell/ThreadScope/releases +2. `chmod a+x` the result of extracting the release +3. `brew install gtk+ gtk-mac-integration` diff --git a/script/repl b/script/repl new file mode 100755 index 0000000000..e3b153a573 --- /dev/null +++ b/script/repl @@ -0,0 +1,15 @@ +#!/bin/bash +# Usage: script/repl [ARGS...] +# Run a repl session capable of loading all of the packages and their individual components. Any passed arguments, e.g. module names or flags, will be passed to ghci. + +set -e + +cd $(dirname "$0")/.. + +repl_builddir=dist-repl + +if [[ ! -d $repl_builddir ]]; then + echo "$repl_builddir does not exist, first run 'cabal repl --builddir=$repl_builddir', exit, and then re-run $0" +else + cabal exec --builddir=$repl_builddir ghci -- -ghci-script=.ghci.semantic $@ +fi