├── docs
├── .gitignore
├── Notes-slimv.txt
├── rust_analyzer_blog.org
├── task-generate_web_pages_from_org_files_in_docs.org
├── links_to_some_dependencies_documentation.org
├── silimar_projects.org
├── aartaka.org
├── jedi.org
├── fast_incremental_peg_parsing.org
├── emacs_eat_emulate_a_terminal.org
├── formatting_code.org
├── listener_features.org
├── parse_files_with_conflicts.org
├── vend.org
├── elpy.org
├── missing_documentation.org
├── debug_adapter_protocol.org
├── helping_with_setting_up_slime_for_remote_sessions.org
├── parsing_common_lisp.org
├── emacs_eglot.org
├── testing-breeze.org
├── language_server_protocol.org
├── visual_studio_code_integration.org
├── improve_cl_docstring_s_at_runtime.org
├── file_watching.org
├── goals.org
├── support_for_bug_reports.org
├── case_with_correction_suggestion.org
├── contributing.org
├── flymake.org
├── linting_asd_files.org
├── error_recovery.org
├── editor_integrations.org
├── e_graphs.org
├── slimv.org
├── breeze_on_the_internets.org
├── roadmap.org
├── neovim.org
├── change_impact_analysis.org
├── index.org
├── programming_with_holes.org
├── sly_slime_integration.org
├── getting_started.org
├── glossary.org
├── support_for_tests.org
├── examples_of_code_that_is_not_handled_correctly_by_slime_and_sly.org
├── faq_from_newbies_about_common_lisp.org
├── introduction.org
├── design_decisions.org
├── eclector.org
├── features.org
├── reader-macros.org
├── syntax_highlighting.org
└── brightlight-green.css
├── .gitattributes
├── scripts
├── ignore-words.txt
├── setup-quicklisp.lisp
├── demo
│ ├── demo.lisp
│ ├── setup-demo.lisp
│ └── demo-recorder.sh
├── test-ecl.sh
├── test-abcl.sh
├── test-ccl.sh
├── test-sbcl.sh
├── test-clasp.sh
├── manifest-abcl.scm
├── manifest-ecl.scm
├── manifest-sbcl.scm
├── manifest.scm
├── doc.sh
├── run-tests.lisp
├── animate.sh
├── emacs-director.patch
├── compile.sh
├── test-pedantic.sh
├── better-trace.lisp
├── profile-loading.lisp
├── load-dependencies.lisp
└── org-publish-project.el
├── .gitmodules
├── tests
├── emacs
│ ├── dot-emacs.d
│ │ └── early-init.el
│ ├── test-straight.el
│ ├── no-listener.el
│ ├── breeze.erts
│ ├── wip-demo.el
│ └── breeze-test.el
├── cmds
│ └── other-files.lisp
├── logging.lisp
├── channel.lisp
├── utils.lisp
├── pattern
│ ├── rewrite.lisp
│ └── compile-pattern.lisp
├── buffer.lisp
├── package-commands.lisp
├── package.lisp
├── iterator.lisp
├── dummy-package.lisp
├── xref.lisp
├── workspace.lisp
└── report.lisp
├── src
├── doctor.lisp
├── cmds
│ ├── blueprint.lisp
│ ├── egraph-command.lisp
│ ├── command-utils.lisp
│ ├── invert.lisp
│ ├── quicklisp.lisp
│ ├── +quickproject.lisp
│ ├── package-commands.lisp
│ ├── capture.lisp
│ ├── editing.lisp
│ ├── completion.lisp
│ ├── project.lisp
│ └── test-commands.lisp
├── breeze.lisp
├── range.lisp
├── configuration.lisp
├── logging.lisp
├── package.lisp
├── ensure-breeze.lisp
├── channel.lisp
├── pattern
│ └── rewrite.lisp
├── thread.lisp
├── xref.lisp
└── cl-todo.lisp
├── data
└── default-capture-template.lisp
├── .gitignore
├── .dockerignore
├── scratch-files
├── ultra.lisp
├── remote-loading.el
├── utils.lisp
├── temporary-buffer.el
├── wip-find-missing-tests.lisp
├── quicklisp.lisp
├── xref-test.lisp
├── breeze+parachute.lisp
├── bundle.lisp
├── unix-socket.el
├── user-test.lisp
├── try-context-menu-mode.el
├── remote-loading.lisp
├── html.lisp
├── notes
│ ├── function-redefinition.lisp
│ └── error-system-loadedp.txt
├── edit-distance.lisp
├── xref.lisp
├── analysis.lisp
├── macroexpand.lisp
├── fsa.lisp
├── function-fingerprinting.lisp
├── definition.lisp
├── refactor-scratch.lisp
├── indentation.lisp
└── test-runner.lisp
├── .gitlab
└── test-job.gitlab-ci.yml
├── .envrc
├── .dir-locals.el
├── README.md
├── LICENSE
├── githooks
└── pre-commit
├── .gitlab-ci.yml
├── .github
└── workflows
│ └── ci.yml
└── Dockerfile
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | *.html
2 | org-roam.db
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.lisp text eol=lf
2 | *.org text eol=lf
3 |
--------------------------------------------------------------------------------
/scripts/ignore-words.txt:
--------------------------------------------------------------------------------
1 | upto
2 | falsy
3 | dum
4 | isnt
5 | skipp
6 | whats
7 | clos
8 | iif
9 | te
10 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "scripts/emacs-director"]
2 | path = scripts/emacs-director
3 | url = https://github.com/bard/emacs-director.git
4 |
--------------------------------------------------------------------------------
/docs/Notes-slimv.txt:
--------------------------------------------------------------------------------
1 | let g:swank_port=4005
2 | let g:swank_host=localhost
3 |
4 | call SlimvConnectSwank
5 | echom SlimvConnectSwank
6 |
7 |
--------------------------------------------------------------------------------
/scripts/setup-quicklisp.lisp:
--------------------------------------------------------------------------------
1 |
2 | (load "quicklisp.lisp")
3 |
4 | (quicklisp-quickstart:install)
5 |
6 | (ql-util:without-prompting
7 | (ql:add-to-init-file))
8 |
--------------------------------------------------------------------------------
/docs/rust_analyzer_blog.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 9fdf7c6a-793f-4ea3-b538-0742c7582aa3
3 | :END:
4 | #+title: Rust analyzer blog
5 |
6 | https://rust-analyzer.github.io/blog
7 |
--------------------------------------------------------------------------------
/tests/emacs/dot-emacs.d/early-init.el:
--------------------------------------------------------------------------------
1 |
2 | ;; Don't automatically load every package installed
3 | (setq package-enable-at-startup nil)
4 |
5 | (message "Early init file loaded.")
6 |
--------------------------------------------------------------------------------
/scripts/demo/demo.lisp:
--------------------------------------------------------------------------------
1 | ;; Setup a lisp process before making a demo
2 |
3 | (cl:in-package #:cl)
4 |
5 | (ql:quickload '(swank breeze))
6 |
7 | (swank:create-server :port 40050 :dont-close t)
8 |
--------------------------------------------------------------------------------
/tests/cmds/other-files.lisp:
--------------------------------------------------------------------------------
1 | (defpackage #:breeze.tests.other-files
2 | (:documentation "Test package for breeze.other-files.")
3 | (:use #:cl #:breeze.other-files))
4 |
5 | (in-package #:breeze.tests.other-files)
6 |
--------------------------------------------------------------------------------
/scripts/test-ecl.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | # This script is used to run the tests with sbcl
4 | #
5 |
6 | set -e
7 |
8 | cd $(dirname $0)/../
9 |
10 | exec ecl \
11 | --eval "(load \"scripts/run-tests.lisp\")"
12 |
--------------------------------------------------------------------------------
/scripts/test-abcl.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | # This script is used to run the tests with sbcl
4 | #
5 |
6 | set -e
7 |
8 | cd $(dirname $0)/../
9 |
10 | exec abcl --batch \
11 | --eval "(load \"scripts/run-tests.lisp\")"
12 |
--------------------------------------------------------------------------------
/scripts/test-ccl.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | # This script is used to run the tests with sbcl
4 | #
5 |
6 | set -e
7 |
8 | cd $(dirname $0)/../
9 |
10 | exec ccl --batch \
11 | --eval "(load \"scripts/run-tests.lisp\")"
12 |
--------------------------------------------------------------------------------
/src/doctor.lisp:
--------------------------------------------------------------------------------
1 | (defpackage #:breeze.doctor
2 | (:documentation "Breeze diagnostic")
3 | (:use #:cl))
4 |
5 | (in-package #:breeze.doctor)
6 |
7 | ;; TODO Check that breeze is configured.
8 | ;; TODO Check the running threads
9 |
--------------------------------------------------------------------------------
/docs/task-generate_web_pages_from_org_files_in_docs.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 02d8e1a2-ecea-4c47-8808-b5f7a906b553
3 | :END:
4 | #+title: Generate web pages from org files in docs/
5 |
6 | * TODO Generate web pages from org files in docs/
7 |
--------------------------------------------------------------------------------
/data/default-capture-template.lisp:
--------------------------------------------------------------------------------
1 |
2 | (ql:quickload '(alexandria))
3 |
4 | ;; Make it easier to debug
5 | (declaim (optimize (speed 0) (safety 3) (debug 3)))
6 |
7 | #|
8 |
9 | Goal:
10 |
11 | Motivation:
12 |
13 | First lead:
14 |
15 | |#
16 |
--------------------------------------------------------------------------------
/scripts/test-sbcl.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | # This script is used to run the tests with sbcl
4 | #
5 |
6 | set -e
7 |
8 | cd $(dirname $0)/../
9 |
10 | exec sbcl --noinform --non-interactive \
11 | --eval "(load \"scripts/run-tests.lisp\")"
12 |
--------------------------------------------------------------------------------
/tests/emacs/test-straight.el:
--------------------------------------------------------------------------------
1 | ;; Work in progress: test that breeze is loadable with straight
2 |
3 | (load (find-library-name "straight"))
4 |
5 | (straight-use-package
6 | '(breeze :type git :host codeberg :repo "fstamour/breeze"
7 | :files "emacs))
8 |
--------------------------------------------------------------------------------
/scripts/test-clasp.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | # This script is used to run the tests with sbcl
4 | #
5 |
6 | set -e
7 |
8 | cd "$(git rev-parse --show-toplevel)"
9 |
10 | exec clasp --non-interactive \
11 | --eval "(load \"scripts/run-tests.lisp\")"
12 |
--------------------------------------------------------------------------------
/docs/links_to_some_dependencies_documentation.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 7d0f5cd2-d216-4882-84ac-27c004ad6fbd
3 | :END:
4 | #+title: Links to some dependencies' documentation
5 | * Resources
6 |
7 | - [[https://alexandria.common-lisp.dev/draft/alexandria.html][alexandria]]
8 |
--------------------------------------------------------------------------------
/docs/silimar_projects.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 62112623-6002-4cb9-87de-cb530ce0a36e
3 | :END:
4 | #+title: Silimar projects
5 |
6 | * Python
7 |
8 | - [[id:5eb1faac-b7b5-4c99-abe9-b91e77bea4ae][Jedi]]
9 | - [[id:5265bce5-c6d0-4cda-8d3e-699ceafcab42][Elpy]]
10 |
--------------------------------------------------------------------------------
/docs/aartaka.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 52254263-fae9-4ead-8467-110735b07a2a
3 | :END:
4 | #+title: AArtaka
5 |
6 | a.k.a. Artyom Bologov
7 |
8 | https://github.com/aartaka/graven-image/tree/master
9 |
10 | https://github.com/aartaka/lisp-config/blob/master/ed.lisp
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.fasl
2 | *.core
3 |
4 | # ecl
5 | *.fas
6 |
7 | *.svg
8 | result
9 | public/
10 |
11 | # org-roam db
12 | *.db
13 |
14 | githooks/*.sample
15 | /.direnv/
16 | *.dot
17 |
18 | *.png
19 | *.log
20 | *~
21 | /docs/sitemap.org
22 |
23 | build/
24 | *.tar
25 | *.tar.gz
--------------------------------------------------------------------------------
/scripts/manifest-abcl.scm:
--------------------------------------------------------------------------------
1 | (specifications->manifest
2 | (list
3 | "coreutils"
4 | "bash"
5 |
6 | "cl-bordeaux-threads"
7 | "cl-alexandria"
8 | "cl-parachute"
9 | "cl-spinneret"
10 | "cl-closer-mop"
11 | "cl-ppcre"
12 | "cl-quickproject"
13 |
14 | "abcl"))
15 |
--------------------------------------------------------------------------------
/docs/jedi.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 5eb1faac-b7b5-4c99-abe9-b91e77bea4ae
3 | :END:
4 | #+title: Jedi
5 |
6 | #+begin_quote
7 | Jedi - an awesome autocompletion, static analysis and refactoring
8 | library for Python
9 | #+end_quote
10 |
11 | https://jedi.readthedocs.io/en/latest/
12 |
--------------------------------------------------------------------------------
/scripts/manifest-ecl.scm:
--------------------------------------------------------------------------------
1 | (specifications->manifest
2 | (list
3 | "coreutils"
4 | "bash"
5 |
6 | "ecl-bordeaux-threads"
7 | "ecl-alexandria"
8 | "ecl-parachute"
9 | "ecl-spinneret"
10 | "ecl-closer-mop"
11 | "ecl-cl-ppcre"
12 | "ecl-quickproject"
13 |
14 | "ecl"))
15 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | # *.core
2 | *.dot
3 | *.fasl
4 | *.log
5 | *.org
6 | *.png
7 | *.svg
8 | *~
9 | .dockerignore
10 | .git/
11 | .gitattributes
12 | .githooks/
13 | .github/
14 | .gitignore
15 | .gitmodules
16 | /.direnv/
17 | dockerfile
18 | githooks/*.sample
19 | public/
20 | result
21 | scratch-files/
--------------------------------------------------------------------------------
/docs/fast_incremental_peg_parsing.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: a1b34261-e041-412a-a291-c26fd0e34668
3 | :END:
4 | #+title: Fast Incremental PEG Parsing
5 |
6 | https://www.youtube.com/watch?v=k3vBR58u3cY
7 |
8 | Presented at SLE 2021, part of SPLASH 2021.
9 | By Zachary Yedidia, Stephen Chong
10 |
--------------------------------------------------------------------------------
/scripts/manifest-sbcl.scm:
--------------------------------------------------------------------------------
1 | (specifications->manifest
2 | (list
3 | "coreutils"
4 | "bash"
5 |
6 | "sbcl-bordeaux-threads"
7 | "sbcl-alexandria"
8 | "sbcl-parachute"
9 | "sbcl-spinneret"
10 | "sbcl-closer-mop"
11 | "sbcl-cl-ppcre"
12 | "sbcl-quickproject"
13 |
14 | "sbcl"))
15 |
--------------------------------------------------------------------------------
/docs/emacs_eat_emulate_a_terminal.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 1af469ee-d6ec-46fe-a335-b044e142eb7d
3 | :END:
4 | #+title: Emacs eat - Emulate a terminal
5 |
6 | Emulate A Terminal, in a region, in a buffer and in Eshell
7 |
8 | https://codeberg.org/akib/emacs-eat
9 |
10 | Seems interesting...
11 |
--------------------------------------------------------------------------------
/docs/formatting_code.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: d487821d-01e7-41a1-b9db-1a856fd7eb01
3 | :END:
4 | #+title: Formatting code
5 |
6 | https://github.com/radian-software/apheleia
7 |
8 | 🌷 Run code formatter on buffer contents without moving point, using
9 | RCS patches and dynamic programming.
10 |
--------------------------------------------------------------------------------
/docs/listener_features.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: d21da464-7b9e-47d2-bc2a-c9ab7a927218
3 | :END:
4 | #+title: Listener features
5 |
6 | condition "package not found" =>
7 | - try to fix spelling
8 | - load a system or file that contains the package
9 | - create a new system or file to create the
10 |
--------------------------------------------------------------------------------
/docs/parse_files_with_conflicts.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: da4c20d5-9947-434c-909e-3e766357244f
3 | :END:
4 | #+title: Parse files with conflicts
5 |
6 | It would be nice if the parser was able to detect git's conflict
7 | markers.
8 |
9 | It would be ever nicer if breeze could help fixing those conflicts.
10 |
--------------------------------------------------------------------------------
/scratch-files/ultra.lisp:
--------------------------------------------------------------------------------
1 | (ql-dist:install-dist "http://dist.ultralisp.org/"
2 | :prompt nil)
3 |
4 |
5 | ;;; Uninstall ultralisp
6 | #+ (or)
7 | (ql-dist:uninstall
8 | (find-if #'(lambda (dist)
9 | (string= "ultralisp"
10 | (ql-dist:name dist)))
11 | (ql-dist:all-dists)))
12 |
--------------------------------------------------------------------------------
/docs/vend.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: d306a11f-4189-4e24-9a26-0b2cb813e54e
3 | :ROAM_ALIASES: vend
4 | :END:
5 | #+title: fosskers/vend
6 | #+filetags: :tool:
7 |
8 | #+begin_quote
9 | Manage your Common Lisp project dependencies.
10 | #+end_quote
11 |
12 | - [[https://github.com/fosskers/vend][Homepage on GitHub]]
13 |
--------------------------------------------------------------------------------
/tests/emacs/no-listener.el:
--------------------------------------------------------------------------------
1 | (require 'ert)
2 | (require 'ert-x)
3 |
4 | (ert-deftest breeze-init-no-listener-loaded ()
5 | "Make sure that `breeze-init' has sane behaviour if it can't find sly or slime."
6 | (should (equal
7 | '(error "Please start either slime or sly.")
8 | (should-error (breeze-init)))))
9 |
--------------------------------------------------------------------------------
/docs/elpy.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 5265bce5-c6d0-4cda-8d3e-699ceafcab42
3 | :END:
4 | #+title: Elpy
5 |
6 | #+begin_quote
7 | Elpy is an extension for the Emacs text editor to work with Python
8 | projects.
9 | #+end_quote
10 |
11 | https://elpy.readthedocs.io/en/latest/
12 |
13 | - Uses [[id:5eb1faac-b7b5-4c99-abe9-b91e77bea4ae][Jedi]]
14 |
--------------------------------------------------------------------------------
/docs/missing_documentation.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 018d969c-f075-406a-95a0-d4bac06df069
3 | :END:
4 | #+title: Missing Documentation
5 |
6 | I do have some utilities to flag missing documentation.
7 |
8 | I found out that Zulu' too made something:
9 |
10 | https://gitlab.com/Zulu-Inuoe/common.lisp/-/blob/4d326828efb78c6518fc2364021a143ec4c98b8b/utils.lisp#L283
11 |
--------------------------------------------------------------------------------
/docs/debug_adapter_protocol.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 44f1edb0-d65f-4e6c-bd87-5bed4fc07376
3 | :END:
4 | #+title: Debug Adapter Protocol
5 |
6 | Not unlike LSP (Language Server Protocol), but for debugger.
7 |
8 | - [[https://microsoft.github.io/debug-adapter-protocol/][Official web page]]
9 | - [[https://github.com/svaante/dape][Dape - Debug Adapter Protocol for Emacs]]
10 |
--------------------------------------------------------------------------------
/docs/helping_with_setting_up_slime_for_remote_sessions.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: b139c21c-3a35-4b69-acd5-00b9d71090ce
3 | :END:
4 | #+title: Helping with setting up slime for remote sessions
5 |
6 | It would be nice if breeze could help with detecting misconfigured (or
7 | not configured) file path translation (=slime-filename-translations=).
8 |
9 | Most people just don't know this feature exists.
10 |
--------------------------------------------------------------------------------
/docs/parsing_common_lisp.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: edbd3cb1-e04b-41f9-b35f-20c123854481
3 | :END:
4 | #+title: Parsing Common Lisp
5 |
6 | * Concrete Syntax Tree
7 | :PROPERTIES:
8 | :ID: 1f979dc4-a4b7-4223-af54-82fa3725c8a3
9 | :END:
10 |
11 | https://github.com/s-expressionists/Concrete-Syntax-Tree
12 |
13 | This library is intended to solve the problem of source tracking for
14 | Common Lisp code.
15 |
--------------------------------------------------------------------------------
/scratch-files/remote-loading.el:
--------------------------------------------------------------------------------
1 | ;;;; See remote-loading.lisp
2 |
3 | (defun get-string-from-file (filePath)
4 | "Return file content as string."
5 | (with-temp-buffer
6 | (insert-file-contents filePath)
7 | (buffer-string)))
8 |
9 | (breeze-eval
10 | (concat "(progn"
11 | (get-string-from-file
12 | (breeze-relative-path "scratch-files/remote-loading.lisp"))
13 | "'loaded)"))
14 |
--------------------------------------------------------------------------------
/docs/emacs_eglot.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 38d6dbd7-0580-4701-bd52-ee97174a0535
3 | :END:
4 | #+title: Emacs' eglot
5 |
6 | Included in emacs since version 29.
7 |
8 | - =eglot--propose-changes-as-diff=
9 | - =eglot-forget-pending-continuations=
10 |
11 | * Related notes
12 |
13 | - [[id:9d5bc298-56d4-40c4-af2e-5b127d5914bf][Language Server Protocol]]
14 | - [[id:44f1edb0-d65f-4e6c-bd87-5bed4fc07376][Debug Adapter Protocol]]
15 |
--------------------------------------------------------------------------------
/docs/testing-breeze.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: e712f3d1-0734-43f0-886a-3008ca5f722d
3 | :END:
4 | #+title: Testing Breeze
5 |
6 | * How to run the tests
7 |
8 | #+begin_src lisp
9 | (ql:quickload "breeze/test")
10 | (asdf:test-system "breeze")
11 | #+end_src
12 |
13 | Or from the command line:
14 |
15 | #+begin_src shell
16 | ./scripts/test-sbcl.sh
17 | #+end_src
18 |
19 | OR
20 |
21 | #+begin_src shell
22 | make test
23 | #+end_src
24 |
--------------------------------------------------------------------------------
/src/cmds/blueprint.lisp:
--------------------------------------------------------------------------------
1 |
2 | (defpackage #:breeze.blueprint
3 | (:documentation "Snippets, but better™")
4 | (:use #:cl))
5 |
6 | (in-package #:breeze.blueprint)
7 |
8 | ;; TODO make "breeze-quickinsert" choose to insert a defun when there's a "defun" at point...
9 | #|
10 |
11 | defun
12 |
13 |
14 | defmethod insert-pattern
15 |
16 | patterns need docstring/documentation/prompt
17 |
18 | insert-symbol
19 | ensure-sym (convert symbol to sym)
20 | |#
21 |
--------------------------------------------------------------------------------
/.gitlab/test-job.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | spec:
2 | inputs:
3 | lisp-impl:
4 | default: sbcl
5 | ---
6 | test-$[[ inputs.lisp-impl ]]:
7 | image: registry.gitlab.com/fstamour/breeze/breeze-ci-$[[ inputs.lisp-impl ]]:latest
8 | script:
9 | - $[[ inputs.lisp-impl ]] --load scripts/quicklisp.lisp
10 | --eval "(quicklisp-quickstart:install)"
11 | --eval "(ql-util:without-prompting (ql:add-to-init-file))"
12 | - scripts/test-$[[ inputs.lisp-impl ]].sh
13 |
--------------------------------------------------------------------------------
/docs/language_server_protocol.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 9d5bc298-56d4-40c4-af2e-5b127d5914bf
3 | :END:
4 | #+title: Language Server Protocol
5 |
6 | * TODO An implementation of the Language Server Protocol for Common Lisp :editor:
7 |
8 | - https://github.com/cxxxr/cl-lsp
9 | - related: https://marketplace.visualstudio.com/items?itemName=ailisp.commonlisp-vscode
10 |
11 |
12 | https://github.com/manateelazycat/lsp-bridge
13 | A blazingly fast LSP client for Emacs
14 |
--------------------------------------------------------------------------------
/src/breeze.lisp:
--------------------------------------------------------------------------------
1 |
2 | (uiop:define-package #:breeze
3 | (:documentation "The breeze package meant for the end-user.")
4 | (:use #:cl)
5 | (:use-reexport)
6 | (:import-from #:breeze.command
7 | #:define-command)
8 | (:export #:define-command))
9 |
10 | (in-package #:breeze)
11 |
12 | #++
13 | (let ((package #:breeze.command)
14 | (symbols '(#:define-command
15 | #:read-string-then-insert)))
16 | (loop import then export))
17 |
--------------------------------------------------------------------------------
/.envrc:
--------------------------------------------------------------------------------
1 |
2 | # TODO Those are common lips implementations found in guix, I'm unsure
3 | # about their name in nixpkgs
4 | # UNSURE="clisp ecl abcl gcl ccl"
5 |
6 | # TODO cl-all https://github.com/Shinmera/cl-all
7 |
8 | PKGS_COMMON="sbcl bash entr fd"
9 |
10 | NIX_PKGS="gnumake codespell"
11 | GUIX_PKGS="make python-codespell"
12 |
13 |
14 | if has guix; then
15 | use guix $PKGS_COMMON $GUIX_PKGS
16 | elif has nix-shell; then
17 | use nix -p $PKGS_COMMON $NIX_PKGS
18 | fi
19 |
--------------------------------------------------------------------------------
/docs/visual_studio_code_integration.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 086c7705-e5ec-4dc0-852d-211c055eb145
3 | :END:
4 | #+title: Visual Studio Code integration
5 |
6 | NOT IMPLEMENTED, this is a design document (for the moment)
7 |
8 | * Existing vscode extension for Common Lisp
9 |
10 | ** TODO ALIVE
11 |
12 | * Related notes
13 |
14 | - [[id:6bd2b06d-0a3c-4d32-9a1e-4f6f36e1003d][Emacs integration]]
15 | - [[id:f3a9c9a2-8180-43a8-9424-e66fd6190caa][Vim and Neovim integration]]
16 |
--------------------------------------------------------------------------------
/tests/emacs/breeze.erts:
--------------------------------------------------------------------------------
1 |
2 | Point-Char: |
3 |
4 | Name: insert-in-package-cl-user
5 | Code: breeze-insert-in-package-cl-user
6 |
7 | =-=
8 | |
9 | =-=
10 | (cl:in-package #:cl-user)
11 | =-=-=
12 |
13 |
14 | ;; WIP
15 | ;; Name: insert-defun
16 | ;; Code: breeze-insert-defun
17 |
18 | ;; =-=
19 | ;; |
20 | ;; =-=
21 | ;; (defun a (b c)
22 | ;; )
23 | ;; =-=-=
24 |
25 |
26 | ;; Corrections of typos
27 | ;; =-=
28 | ;; |(lost 1 2 3)
29 | ;; =-=
30 | ;; |(list 1 2 3)
31 | ;; =-=-=
32 |
--------------------------------------------------------------------------------
/docs/improve_cl_docstring_s_at_runtime.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 9dbbf418-de72-4d31-8347-19e3dc7d8df1
3 | :END:
4 | #+title: Improve CL docstring's at runtime
5 |
6 | I noticed that, on sbcl, you can =(setf (documentation 'x 'function)
7 | ...= on symbols that are part of the =cl= package (which I didn't
8 | expect because of the package lock). It _could_ be nice to load a
9 | package during development that adds examples to the docstrings, and
10 | perhaps even "links" between the defintions?
11 |
--------------------------------------------------------------------------------
/docs/file_watching.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 373c4a22-b450-40e1-8d00-1319e0277b68
3 | :END:
4 | #+title: File watching
5 | * Portable file watching
6 |
7 | https://www.reddit.com/r/lisp/comments/1iatcd/fswatcher_watches_filesystem_changes/
8 |
9 | http://eradman.com/entrproject/
10 |
11 | https://github.com/Ralt/fs-watcher (polls)
12 |
13 | https://github.com/Shinmera/file-notify <===
14 |
15 | 2023-09-25 I briefly talked with Shinmera this summer, and they
16 | mentioned that this project doesn't currently work.
17 |
--------------------------------------------------------------------------------
/scripts/manifest.scm:
--------------------------------------------------------------------------------
1 | ;; this manifest is meant for running integration tests
2 |
3 | ;; TODO variants:
4 | ;; - per lisp implementation
5 | ;; - sly vs slime
6 | ;; - emacs, nvim, vscode
7 | ;; - x vs no-x
8 |
9 | (specifications->manifest
10 | (list
11 | ;; "emacs-no-x"
12 | "emacs" "scrot"
13 | "coreutils"
14 |
15 | ;; "cl-slime"
16 | "emacs-slime"
17 | "emacs-sly"
18 | "cl-bordeaux-threads"
19 | "cl-alexandria"
20 |
21 | "sbcl"
22 | "abcl"
23 | "ecl"
24 | "clisp"
25 | "clasp-cl"
26 | "gcl"
27 | "ccl"))
28 |
--------------------------------------------------------------------------------
/docs/goals.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: e5d64314-8b13-4a6b-997f-1aae94910d63
3 | :END:
4 | #+title: Goals and non-goals
5 |
6 | * Goals
7 |
8 | - Make it easier to develop in common lisp
9 | - by any means
10 | - With any editor (or even without one)
11 | - Be as portable as possible
12 | - Be useful to new and experimented developer (or even
13 | non-developpers, we'll get there)
14 |
15 | * Non-goals
16 |
17 | - Replace slime, sly, slimv, slima, etc
18 | - Replace existing test framework
19 | - Force the user to use a set of conventions
20 |
--------------------------------------------------------------------------------
/scratch-files/utils.lisp:
--------------------------------------------------------------------------------
1 |
2 | (defmacro chain (&body forms)
3 | (reduce (lambda (acc next)
4 | (append acc (list next)))
5 | (butlast forms)
6 | :initial-value (alexandria:lastcar forms)
7 | :from-end t))
8 |
9 | (defmacro chain* (&body forms)
10 | (alexandria:with-gensyms (callback)
11 | `(lambda (,callback)
12 | (chain ,@forms ,callback))))
13 |
14 | (defun reverse-parameter (fn)
15 | "Take a function of arity 2 and call return a function with the 2
16 | parameters inverted."
17 | #'(lambda (x y) (funcall fn y x)))
18 |
--------------------------------------------------------------------------------
/scripts/doc.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | # This script is used to generate the documentation
4 | #
5 |
6 | set -e
7 |
8 | cd "$(git rev-parse --show-toplevel)"
9 |
10 | mkdir -p public/
11 |
12 | sbcl --noinform --non-interactive \
13 | --eval "(declaim (optimize (debug 3) (speed 0) (safety 3)))" \
14 | --eval "(asdf:load-asd (truename \"breeze.asd\"))" \
15 | --eval "(ql:quickload '#:breeze/doc)" \
16 | --eval "(ql:quickload '#:breeze/dogfood)" \
17 | --eval '(breeze.dogfood:generate-breeze-reference)'
18 |
19 | cp docs/style.css public/
20 |
--------------------------------------------------------------------------------
/scripts/run-tests.lisp:
--------------------------------------------------------------------------------
1 | (require 'asdf)
2 |
3 | #-quicklisp
4 | (let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
5 | (user-homedir-pathname))))
6 | (if (probe-file quicklisp-init)
7 | (load quicklisp-init)
8 | (warn "~%~%======~%Quicklisp is not installed.~%======~%~%")))
9 |
10 | (asdf:load-asd (truename "breeze.asd"))
11 |
12 | #+quicklisp
13 | (ql:quickload "breeze/test" :verbose t)
14 |
15 | #-quicklisp
16 | (asdf:load-system "breeze/test")
17 |
18 | (breeze.test.main:run-all-tests :exitp t)
19 |
--------------------------------------------------------------------------------
/scratch-files/temporary-buffer.el:
--------------------------------------------------------------------------------
1 | (defvar breeze-temporary-buffers '())
2 |
3 | (defun breeze-make-temporary-buffer ()
4 | "Make a buffer name, add it to the list."
5 | (let ((buffer-name (make-temp-name (concat "tmp-" (format-time-string "%Y-%m.%dT%H.%M.%S-")))))
6 | (push buffer-name breeze-temporary-buffers)
7 | buffer-name))
8 |
9 | (defun breeze-kill-all-temporary-buffer ()
10 | "Kill all buffers."
11 | (interactive)
12 | (dolist (buffer-name breeze-temporary-buffers)
13 | (when (get-buffer buffer-name)
14 | (kill-buffer buffer-name)))
15 | (setf breeze-temporary-buffers '()))
16 |
--------------------------------------------------------------------------------
/scratch-files/wip-find-missing-tests.lisp:
--------------------------------------------------------------------------------
1 |
2 | (in-package #:breeze.user)
3 |
4 | (defun check-for-untested-functions ()
5 | (let ((missing-tests
6 | (loop
7 | :for package :in (current-packages)
8 | :append (breeze.xref::function-without-test package))))
9 | (if missing-tests
10 | (progn
11 | (princ "There are untested functions in current packages:")
12 | (format t "~&~{ * ~A~%~}"
13 | missing-tests))
14 | (format t "~&No untested function found. ~A" (cheers)))))
15 |
16 | (loop
17 | :for package :in (current-packages)
18 | :append (breeze.xref::function-without-test package))
19 |
--------------------------------------------------------------------------------
/docs/support_for_bug_reports.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 1bfee55a-11ef-47d6-924b-2ce1a9b39f3b
3 | :END:
4 | #+title: Support for bug reports
5 |
6 | * TODO Make it easy to report a bugs :ux:ops:
7 |
8 | - OS version
9 | - lisp version
10 | - editor version
11 | - quicklisp
12 | - client version
13 | - distributions
14 | - for each dependency
15 | - from which distribution the system come from
16 | - version of the system
17 |
18 | * Generalized support for bug reports :idea:
19 |
20 | Maybe there could be a way to report bugs for any projects (not just
21 | breeze)?
22 |
--------------------------------------------------------------------------------
/tests/logging.lisp:
--------------------------------------------------------------------------------
1 | (defpackage #:breeze.test.logging
2 | (:documentation "Tests for breeze.logging.")
3 | (:use #:cl)
4 | (:import-from #:breeze.logging
5 | #:compare-level)
6 | (:import-from #:parachute
7 | #:define-test
8 | #:define-test+run
9 | #:is
10 | #:true
11 | #:false))
12 |
13 | (in-package #:breeze.test.logging)
14 |
15 | (define-test+run compare-level
16 | (false (compare-level #'< :debug :debug))
17 | (false (compare-level #'< :info :debug))
18 | (true (compare-level #'< :debug :critical))
19 | (false (compare-level #'< :critical :debug)))
20 |
--------------------------------------------------------------------------------
/scripts/animate.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | #
3 | # This is an example of how to take a bunch of capture made with tmux
4 | # and assemble them with termtosvg
5 | #
6 | # usage: termtosvg output.svg -c ./animate.sh
7 | # or: termtosvg $demo_root -s -c ./animate.sh
8 | #
9 |
10 | # Stop on first error
11 | set -e
12 |
13 | # Move to repo's root
14 | cd "$(git rev-parse --show-toplevel)"
15 |
16 | demo_root=scripts/demo/
17 |
18 | if [ ! -d "$demo_root" ]; then
19 | echo "Demo folder doesn't exits"
20 | exit 1
21 | fi
22 |
23 | for capture in $(echo $demo_root/*.capture | sort -n) ; do
24 | # echo "$capture"
25 | cat "$capture"
26 | sleep 0.5
27 | done
28 |
--------------------------------------------------------------------------------
/src/cmds/egraph-command.lisp:
--------------------------------------------------------------------------------
1 |
2 | (defpackage #:breeze.egraph-command
3 | (:documentation "Commands that use equivalence graphs (e-graphs) to refactor code.")
4 | (:use #:cl #:breeze.analysis)
5 | (:import-from #:breeze.command
6 | #:define-command
7 | #:message)
8 | (:import-from #:breeze.command-utils
9 | #:pulse-node
10 | #:current-node))
11 |
12 | (in-package #:breeze.egraph-command)
13 |
14 | ;; "E-Graph Good" → egg →🥚 → scramble
15 | (export
16 | (define-command scramble ()
17 | "Scramble the code at point."
18 | (alexandria:when-let (($node (current-node)))
19 | (message (node-string $node)))))
20 |
--------------------------------------------------------------------------------
/docs/case_with_correction_suggestion.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: f50fbc39-3148-4ecb-8e46-719ef3e05fd8
3 | :END:
4 | #+title: Case with correction suggestion
5 |
6 | Image a variant of =cl:ecase= that find the nearest match and provides
7 | a corresponding restart for it.
8 |
9 | #+begin_src lisp
10 | (let ((x 'boo))
11 | (case x
12 | (foo ...)
13 | (bar ...)))
14 | ;; => error: 'boo is not one of '(foo bar)
15 | ;; => restart: use 'foo instead
16 | #+end_src
17 |
18 | What if there are many options with the same edit distance?
19 |
20 |
21 | This might be useful:
22 | "Wrap implementation-dependent accessor for restarts in CL"
23 | https://git.sr.ht/~yana/trivial-restart-accessors/
24 |
--------------------------------------------------------------------------------
/docs/contributing.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 279c4ea6-2004-4a7a-a2c9-905f27fae42c
3 | :END:
4 | #+title: Contributing
5 |
6 | * Contributing
7 |
8 | Start by forking and cloning this repository (e.g. into quicklisp's
9 | local-projects directory).
10 |
11 | Setup the pre-commit hook
12 |
13 | #+begin_src shell
14 | git config core.hooksPath githooks
15 | #+end_src
16 |
17 | Look for TODOs in the code
18 |
19 | #+begin_src shell
20 | grep -ir --include='*.lisp' todo
21 | # or
22 | rg -i todo
23 | #+end_src
24 |
25 | Explore the documentation.
26 |
27 | * Related notes
28 |
29 | - [[id:14d42b3a-0a2f-4a3b-8937-7175e621c6ec][Design Decisions]]
30 | - [[id:11dd9906-75ff-4abc-82a5-b7dda0936f06][Roadmap]]
31 |
--------------------------------------------------------------------------------
/docs/flymake.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 5b8f3aee-0ea4-4688-8de7-e0b3ac140405
3 | :END:
4 | #+title: Flymake
5 |
6 | flymake-show-buffer-diagnostics
7 | flymake-show-project-diagnostics
8 |
9 | next-error-function
10 |
11 | - https://www.gnu.org/software/emacs/manual/html_node/flymake/Troubleshooting.html
12 | - https://www.gnu.org/software/emacs/manual/html_node/flymake/Backend-functions.html
13 | - https://www.gnu.org/software/emacs/manual/html_node/flymake/An-annotated-example-backend.html
14 |
15 | * Troubleshooting
16 |
17 | use =C-u M-x flymake-start= to reset the disabled backends
18 |
19 | * Related notes
20 |
21 | - [[id:f8811c6f-9813-418f-a745-72be32add601][Syntax checking and linting in neovim]]
22 |
--------------------------------------------------------------------------------
/docs/linting_asd_files.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: e55cab7e-beb6-4cb4-be2e-d0d78a8f568a
3 | :END:
4 | #+title: Linting asd files
5 |
6 | * Use =(in-package #:asdf-user)= at the start of the file
7 |
8 | Using (interned) symbols in the .asd files _might_ intern symbols in
9 | =*package*= (especially if someone tries to =(read ...)= the =.asd=
10 | file.
11 |
12 | * Look for missing files
13 |
14 | I could see one nice workflow:
15 | 1. Open the system defintion
16 | 2. Add a file in the =:components= list
17 | 3. The linter highlights the missing file
18 | 4. The user choose a "code action" that creates the file and opens it
19 | in the editor, perhaps even adding a package definition at the
20 | start.
21 |
--------------------------------------------------------------------------------
/docs/error_recovery.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 9eee6140-644d-4615-ae88-be84243f63af
3 | :END:
4 | #+title: Error recovery
5 |
6 | - basic: use "synchronization points", places where it looks like a
7 | good place to restart parsing after an invalid parse
8 |
9 | - it would be much easier to pin-point the source of the failure if we
10 | start from a previous good state (incremental parsing)
11 |
12 | - I noticed that for lisp, a lot of things would be "easy" to parse
13 | backward, this would help tremendously pin-pointing where a "bad
14 | parse" begins.
15 |
16 |
17 |
18 | An easy case for error recovery:
19 |
20 | #+begin_src lisp
21 | #|
22 | it's missing a closing "pipehash" before the end of the string
23 | #+end_src
24 |
--------------------------------------------------------------------------------
/src/cmds/command-utils.lisp:
--------------------------------------------------------------------------------
1 | (defpackage #:breeze.command-utils
2 | (:documentation "Utilities to write commands")
3 | (:use #:cl #:breeze.command #:breeze.analysis)
4 | (:export #:pulse-node
5 | #:current-node))
6 |
7 | (in-package #:breeze.command-utils)
8 |
9 | (defun node-at-point ())
10 |
11 | ;; TODO This should go in a file for "commands that uses parse trees"
12 | (defun pulse-node (node)
13 | (pulse (start node) (end node)))
14 |
15 | (defun current-node (&key pulsep)
16 | (alexandria:when-let*
17 | ((buffer (current-buffer))
18 | (node-iterator (node-iterator buffer)))
19 | (when pulsep (pulse-node node-iterator))
20 | node-iterator))
21 |
22 | ;; TODO Maybe make a command "replace-form" or "replace-car"
23 |
--------------------------------------------------------------------------------
/docs/editor_integrations.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 5d211d9a-0749-4adb-abe0-e66133d09b5b
3 | :END:
4 | #+title: Editor integrations
5 |
6 | - [[id:6bd2b06d-0a3c-4d32-9a1e-4f6f36e1003d][Emacs integration]]
7 | - [[id:086c7705-e5ec-4dc0-852d-211c055eb145][Visual Studio Code integration]] (not implemented)
8 | - [[id:f3a9c9a2-8180-43a8-9424-e66fd6190caa][Neovim]] (not implemented)
9 |
10 | * TODO Make sure the commands are executed against _common lisp_ code :ux:editor:
11 |
12 | Gavinok tried breeze, but ran ~C-.~ on ~breeze.el~, this caused
13 | confusion for everybody.
14 |
15 | Here's some ideas to help with this:
16 |
17 | - check the extension of the buffer or file
18 | - check the mode of the buffer
19 | - make sure it's not in the repl (for now)
20 |
--------------------------------------------------------------------------------
/.dir-locals.el:
--------------------------------------------------------------------------------
1 | ;;; Directory Local Variables -*- no-byte-compile: t -*-
2 | ;;; For more information see (info "(emacs) Directory Variables")
3 |
4 | ((nil . ((eval . (progn
5 | (setq-local org-roam-directory
6 | (file-truename
7 | (file-name-concat
8 | (locate-dominating-file default-directory ".dir-locals.el")
9 | "docs/")))
10 | (setq-local org-roam-db-location
11 | (file-name-concat org-roam-directory "org-roam.db"))))
12 | (org-roam-capture-templates . (("d" "default" plain "%?" :target (file+head "${slug}.org" "#+title: ${title}
13 | ") :unnarrowed t))))))
14 |
--------------------------------------------------------------------------------
/src/cmds/invert.lisp:
--------------------------------------------------------------------------------
1 |
2 | (defpackage #:breeze.invert
3 | (:documentation "Command to invert the form at point.")
4 | (:use #:cl))
5 |
6 | (in-package #:breeze.invert)
7 |
8 | #++
9 | (unless (donep $node)
10 | (cond
11 | ((match (sym :wild t) $node)
12 | ;; replace by nil
13 | )
14 | ((match (sym :wild nil) $node)
15 | ;; replace by t
16 | )))
17 |
18 | ;; TODO true <=> false
19 | ;; TODO if => swap then-form and else-form, or invert the condition
20 | ;; TODO < <=> >=
21 | ;; TODO > <=> <=
22 | ;; TODO = >= /=
23 | ;; TODO eq => (not eq)
24 | ;; TODO while => until
25 | ;; TODO when => unless (or invert the condition)
26 | ;; TODO (null x) <=> x
27 | ;; TODO {string,char} {=,-{,not-}equal,<,<=,>,>=,-{,not-}lessp,-{,not-}greaterp
28 | ;; TODO is <=> isnt (parachute)
29 |
--------------------------------------------------------------------------------
/scripts/demo/setup-demo.lisp:
--------------------------------------------------------------------------------
1 | (defpackage #:breeze.setup-demo
2 | (:documentation "Load and configure everything needed for a demonstration.")
3 | (:use #:cl))
4 |
5 | (in-package #:breeze.setup-demo)
6 |
7 | (ql:quickload "swank")
8 |
9 | ;; See the content of the variable slime-required-modules in emacs to
10 | ;; get the list available modules. See the "needed" variable of
11 | ;; #'slime-load-contribs to know which ones are actually needed.
12 | ;;
13 | ;; Those are the ones required (transitively) by the slime-fancy
14 | ;; contrib.
15 | (swank:swank-require
16 | '(#:swank-indentation
17 | #:swank-trace-dialog
18 | #:swank-package-fu
19 | #:swank-presentations
20 | #:swank-macrostep
21 | #:swank-fuzzy
22 | #:swank-fancy-inspector
23 | #:swank-c-p-c
24 | #:swank-arglists
25 | #:swank-repl))
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Breeze
2 |
3 | - [codeberg](https://codeberg.org/fstamour/breeze)
4 | - main repo
5 | - [gitlab](https://gitlab.com/fstamour/breeze)
6 | - (as of 2025-11-30) used for CI and documentation hosting (gitlab
7 | pages)
8 | - [github](https://github.com/fstamour/breeze)
9 | - (as of 2025-11-30) used for better discoverability
10 |
11 | Breeze is a set of tools that aims to make lisp development a breeze
12 | (hence the name).
13 |
14 | It is very much alpha quality, I'm experimenting with a lot of things
15 | in parallel.
16 |
17 | More information in the [documentation](https://fstamour.gitlab.io/breeze/).
18 |
19 | ## Support me
20 |
21 | I'm doing this for fun, but if you find this useful or just want to
22 | cheer me up :) here's a link for that:
23 |
24 | Support me on Ko-Fi
25 |
--------------------------------------------------------------------------------
/scratch-files/quicklisp.lisp:
--------------------------------------------------------------------------------
1 | (in-package #:breeze.quicklisp)
2 |
3 | #++
4 | (ql-dist:enabled-dists)
5 | ;; => (#)
6 |
7 | (defparameter *ql* (ql-dist:find-dist "quicklisp"))
8 |
9 | ;; Download everything, without installing it.
10 | (loop :for release :in (ql-dist:provided-releases *ql*)
11 | :do (ql-dist:ensure-local-archive-file release))
12 |
13 | #++
14 | (/ (length (ql-dist:installed-releases *ql*))
15 | (length (ql-dist:provided-releases *ql*)))
16 |
17 | ;; Get the total size (in bytes) of all the archives
18 | ;; (require 'osicat)
19 | (loop :for release :in (ql-dist:installed-releases *ql*)
20 | :sum (osicat-posix:stat-size
21 | (osicat-posix:stat
22 | (ql-dist:local-archive-file release))))
23 | ;; => 615869163
24 |
25 | ;; (float (/ 615869163 1024 1024))
26 | ;; => 587.33
27 |
--------------------------------------------------------------------------------
/src/range.lisp:
--------------------------------------------------------------------------------
1 | (defpackage #:breeze.range
2 | (:documentation "A simple class that holds a start and end points,")
3 | (:use #:cl #:breeze.generics)
4 | (:export #:range #:range= #:start #:end))
5 |
6 | (in-package #:breeze.range)
7 |
8 | (defclass range ()
9 | ((start
10 | :initarg :start
11 | :initform 0
12 | :accessor start)
13 | (end
14 | :initarg :end
15 | :initform nil
16 | :accessor end)))
17 |
18 | (defun range (start end)
19 | (make-instance 'range :start start :end end))
20 |
21 | (defmethod print-object ((range range) stream)
22 | (print-unreadable-object
23 | (range stream :type t :identity nil)
24 | (format stream "~s-~s" (start range) (end range))))
25 |
26 | (declaim (inline range=))
27 | (defun range= (a b)
28 | (and (= (start a) (start b))
29 | (= (end a) (end b))))
30 |
31 | (defmethod eqv ((a range) (b range))
32 | (range= a b))
33 |
--------------------------------------------------------------------------------
/docs/e_graphs.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 32155195-1bc4-4f2d-8f6a-12fb0bd68ecc
3 | :ROAM_ALIASES: egraphs
4 | :END:
5 | #+title: E-graphs
6 |
7 | * Introduction
8 |
9 | An e-graph is a data structure that can represent an exponential (or
10 | even infinite, because of loops) number of forms in a polynomial
11 | amount of space.
12 |
13 | They are a specific case of finite tree automata and are closely
14 | related to version-state algebras (VSAs).
15 |
16 | * In common lisp
17 |
18 | I have a proof of concept implementation in common lisp here:
19 | https://gitlab.com/fstamour/catchall/-/tree/master/egraph
20 |
21 | * References
22 |
23 | - https://egraphs-good.github.io/
24 | - https://colab.research.google.com/drive/1tNOQijJqe5tw-Pk9iqd6HHb2abC5aRid?usp=sharing
25 | - https://arxiv.org/pdf/2004.03082.pdf
26 | - https://github.com/philzook58/awesome-egraphs
27 |
28 | ** TODO Find the paper relating FTA, VSAs and e-graphs
29 |
--------------------------------------------------------------------------------
/scripts/emacs-director.patch:
--------------------------------------------------------------------------------
1 | diff --git a/director.el b/director.el
2 | index 6101320..b372278 100644
3 | --- a/director.el
4 | +++ b/director.el
5 | @@ -163,7 +163,7 @@ If DELAY-OVERRIDE is non-nil, the next step is delayed by that value rather than
6 | (director--log (format "FAILURE: %S" director--failure))
7 | (run-with-timer director--delay nil 'director--end))
8 |
9 | - ((length= director--steps 0)
10 | + ((= (length director--steps) 0)
11 | ;; Run after-step callback for last step
12 | (director--after-step)
13 | (run-with-timer (or delay-override director--delay) nil 'director--end))
14 | @@ -216,7 +216,7 @@ If DELAY-OVERRIDE is non-nil, the next step is delayed by that value rather than
15 |
16 | (`(:suspend)
17 | nil)
18 | -
19 | +
20 | (`(:assert ,condition)
21 | (or (eval condition)
22 | (setq director--failure condition))
23 |
--------------------------------------------------------------------------------
/tests/channel.lisp:
--------------------------------------------------------------------------------
1 | #|
2 |
3 | Those were smoke tests that I was running manually when developing
4 | this package.
5 |
6 | I could add actual test to this package... but I already have lots of
7 | tests on the commands which exercise this code _a lot_.
8 |
9 | |#
10 |
11 | (in-package #:breeze.channel)
12 |
13 |
14 | (cons nil nil) == (list nil)
15 |
16 | (let ((q (list nil)))
17 | (enqueue q 42)
18 | q)
19 | ((42) 42)
20 |
21 | (let ((q (list nil)))
22 | (enqueue q 42)
23 | (enqueue q 1)
24 | q)
25 | ((42 1) 42 1)
26 |
27 | (let ((q (list nil)))
28 | (enqueue q 42)
29 | (values (dequeue q) q))
30 | 42
31 | (NIL)
32 |
33 | (let ((q (list nil)))
34 | (enqueue q 42)
35 | (enqueue q 1)
36 | (values (dequeue q) q))
37 | 42
38 | ((1) 42 1)
39 |
40 |
41 |
42 | (let ((c (make-channel)))
43 | (cons (bt2:make-thread
44 | (lambda () (receive c)))
45 | (bt2:make-thread
46 | (lambda () (send c 42)))))
47 |
--------------------------------------------------------------------------------
/scratch-files/xref-test.lisp:
--------------------------------------------------------------------------------
1 | (in-package #:common-lisp-user)
2 |
3 | (uiop:define-package #:breeze.xref.test
4 | (:documentation "Tests for breeze.xref.")
5 | (:mix #:breeze.xref #:cl #:alexandria)
6 | (:import-from #:breeze.test
7 | #:deftest
8 | #:is))
9 |
10 | (in-package #:breeze.xref.test)
11 |
12 | (deftest test-calls-who
13 | (is (equalp '(dum:mul = is) (test-calls-who 'dum:mul))))
14 |
15 | (deftest tested-by
16 | (is (member 'dum:mul (tested-by 'dum:mul)))
17 | (is (member 'dum:2x (tested-by 'dum:mul))))
18 |
19 | (deftest test-case
20 | (is (equal '((dum:mul 2 6) (dum:mul 2 2)) (test-case 'dum:mul))))
21 |
22 |
23 | (deftest package-test
24 | (is (equal '(dum:mul dum:2x) (package-test 'dum)))
25 | (is (equal (package-test 'dum) (package-test (find-package 'dum)))))
26 |
27 | (deftest calls-who
28 | (is (equalp (calls-who 'dum:2x) '(dum:mul))))
29 |
30 | ;; (breeze.xref::function-without-test)
31 |
--------------------------------------------------------------------------------
/tests/utils.lisp:
--------------------------------------------------------------------------------
1 | (in-package #:common-lisp-user)
2 |
3 | (uiop:define-package #:breeze.test.utils
4 | (:documentation "Tests for breeze.test.")
5 | (:mix #:cl #:alexandria #:breeze.utils)
6 | (:import-from #:parachute
7 | #:define-test
8 | #:define-test+run
9 | #:is
10 | #:true
11 | #:false))
12 |
13 | (in-package #:breeze.test.utils)
14 |
15 | (define-test package-apropos)
16 |
17 |
18 | (define-test before-last
19 | (false (before-last '()))
20 | (false (before-last '(a)))
21 | (is eq 'a (before-last '(a b)))
22 | (is eq 'b (before-last '(a b c))))
23 |
24 | #+nil
25 | (minimizing (x)
26 | (x 'a 10)
27 | (x 'b 5))
28 | ;; => B, 5
29 | #+nil
30 | (minimizing (x)
31 | (x 'a nil))
32 | #+nil
33 | (minimizing (x :tracep t)
34 | (x 'a 10))
35 |
36 | (define-test length>1?
37 | (false (length>1? nil))
38 | (false (length>1? '(a)))
39 | (true (length>1? '(a b))))
40 |
--------------------------------------------------------------------------------
/scratch-files/breeze+parachute.lisp:
--------------------------------------------------------------------------------
1 |
2 | (in-package #:common-lisp-user)
3 |
4 | (defpackage #:breeze+parachute
5 | (:use :cl #:alexandria)
6 | (:import-from #:breeze.user
7 | #:current-packages)
8 | (:export
9 | #:run-all-tests))
10 |
11 | (in-package #:breeze+parachute)
12 |
13 | (defun run-all-tests ()
14 | (parachute:test (current-packages)))
15 |
16 | ;; (run-all-tests)
17 |
18 | ;; TODO less reporting, only report errors?
19 | (push (cons "run parachute tests"
20 | #'(lambda (string)
21 | (declare (ignore string))
22 | (run-all-tests)))
23 | breeze.listener::*interactive-eval-hooks*)
24 |
25 | ;; Something to infer which package uses parachute
26 | #+nil
27 | (let ((packages (make-hash-table)))
28 | (loop :for symbol :being :the
29 | :symbol :of (find-package 'binstring.test)
30 | :do (setf (gethash (symbol-package symbol) packages) t))
31 | (hash-table-keys packages))
32 |
--------------------------------------------------------------------------------
/scratch-files/bundle.lisp:
--------------------------------------------------------------------------------
1 | ;;; Trying to bundle breeze's source code in one big file
2 | #|
3 |
4 | See also: remote-loading.el and remote-loading.lisp
5 |
6 | Which makes me think: it would be nice to have some kind
7 | to "transactions" when loading common lisp code. To avoid half-loaded
8 | code.
9 |
10 | EDIT before I commit: I didn't have much time and didn't get far. BUT,
11 | I managed to figure out that (surprise surprise), the dependencies are
12 | hard to handle correctly...
13 |
14 | |#
15 |
16 | ;; Making sure the breeze system can be found.
17 | (asdf:locate-system "breeze")
18 |
19 | (defparameter *concat*
20 | (multiple-value-list
21 | (asdf:operate 'asdf:concatenate-source-op "breeze")))
22 |
23 | (defparameter *bundle*
24 | (multiple-value-list
25 | (asdf:perform 'asdf:concatenate-source-op "breeze")))
26 |
27 | (first *bundle*)
28 | #P"/home/fstamour/.cache/common-lisp/sbcl-2.4.0-linux-x64/home/fstamour/dev/breeze/src/breeze--system.lisp"
29 |
30 | (load (first *bundle*))
31 |
--------------------------------------------------------------------------------
/scripts/compile.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | # This script is used to compile breeze and its tests in order to show
4 | # compilation errors and warnings
5 | #
6 |
7 | set -e
8 |
9 | cd "$(git rev-parse --show-toplevel)"
10 |
11 | # ASDF checks for warnings and errors when a file is compiled. The
12 | # variables asdf:*compile-file-warnings-behaviour* and
13 | # asdf:*compile-file-failure-behaviour* control the handling of any such
14 | # events. The valid values for these variables are :error, :warn, and
15 | # :ignore.
16 |
17 | # TODO this loads stuff twice... because I used ql:quickload to ensure
18 | # that the dependencies are downloaded before we compile again...
19 |
20 | set -x
21 | exec sbcl --noinform --non-interactive \
22 | --eval "(asdf:load-asd (truename \"breeze.asd\"))" \
23 | --eval "(ql:quickload '#:breeze/test :verbose t :explain t)" \
24 | --eval "(setf asdf:*compile-file-warnings-behaviour* :error)" \
25 | --eval "(asdf:compile-system '#:breeze/test :force t :verbose t)"
26 |
--------------------------------------------------------------------------------
/scratch-files/unix-socket.el:
--------------------------------------------------------------------------------
1 | ;;;; I added domain (unix) socket to myelin (another project of mine),
2 | ;;;; I want to see if it would be hard to add them to breeze too.
3 |
4 | (defvar myelin-client nil
5 | "Client to myelin's domain socket")
6 |
7 | (setf myelin-client
8 | (make-network-process
9 | :name "myelin"
10 | :family 'local
11 | :remote (concat
12 | (xdg-runtime-dir)
13 | "/myelin/myelin.sock")
14 | :coding 'utf-8
15 | :filter (lambda (proc string)
16 | (message "Got %S from myelin (%S)" string proc))
17 | ;; :buffer "*myelin*"
18 | ))
19 |
20 | (process-send-string myelin-client "hi")
21 | ;; => Got "hello" from myelin (#>)
22 |
23 | ;; Well, that was easy enough, but myelin's domain socket server is
24 | ;; currently implemented to receive one command and then close the
25 | ;; connection. It will probably harder to manage with breeze,
26 | ;; especially if the message get big.
27 |
--------------------------------------------------------------------------------
/docs/slimv.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 35b3031a-bbd2-4f32-ba1f-7ec7fc268155
3 | :END:
4 | #+title: slimv
5 |
6 | https://github.com/kovisoft/slimv
7 |
8 | #+begin_quote
9 | Superior Lisp Interaction Mode for Vim ("SLIME for Vim")
10 | #+end_quote
11 |
12 | Interesting tid-bits from the readme:
13 |
14 | - Slimv is a SWANK client for Vim
15 | - Slimv comes with Paredit Mode
16 | - Slurpage and Barfage known from Emacs is also possible but in a
17 | different fashion: you don't move the list element in or out of the
18 | list, rather you move the opening or closing parenthesis over the
19 | element or sub-list.
20 | - It requires *with Python feature enabled* and *python* (must be the
21 | same Python version that was Vim compiled against)
22 |
23 | Tutorial: https://kovisoft.github.io/slimv-tutorial/tutorial.html
24 |
25 | * See also
26 |
27 | - [[id:54e6cd55-803b-4e15-82bc-a332130d020e][Sly/Slime integration]]
28 | - [[id:a7e572ba-a80b-44cf-9fd8-91f50307c675][breeze's eval v.s. sly's and slime's eval]]
29 |
--------------------------------------------------------------------------------
/docs/breeze_on_the_internets.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: b9f7e1f4-dc86-46e0-860b-f845f180110e
3 | :END:
4 | #+title: Breeze on the internets
5 |
6 | Keeping track of public discussions about breeze.
7 |
8 | * Breeze on the internets
9 |
10 | ** Lisp project of the day
11 |
12 | https://40ants.com/lisp-project-of-the-day/2020/08/0166-breeze.html
13 |
14 | ** Reddit
15 |
16 | https://old.reddit.com/r/Common_Lisp/comments/pgtfm3/looking_for_feedbackhelp_on_a_project/
17 |
18 | *** [[https://old.reddit.com/user/dzecniv][u/dzecniv]]
19 |
20 | > testing features along with workers and a file watcher? Shouldn't
21 | they be different projects?
22 |
23 | What annoys you when developing in lisp?
24 |
25 | I find that setting up a test framework is more difficult than it
26 | should be, so any effort on this area is appreciated. I mean: starting
27 | with 5am is ok (but could be easier with an editor command), running
28 | it from the CLI/a CI is less OK, getting the correct return code of
29 | the tests needs more work, etc.
30 |
--------------------------------------------------------------------------------
/scratch-files/user-test.lisp:
--------------------------------------------------------------------------------
1 |
2 | (uiop:define-package #:breeze.user.test
3 | (:documentation "Tests for breeze.user.")
4 | (:mix #:breeze.user #:cl #:alexandria)
5 | (:import-from #:breeze.test
6 | #:deftest
7 | #:is))
8 |
9 | (in-package #:breeze.user.test)
10 |
11 | (deftest current-packages
12 | (is (not (current-packages nil)))
13 | (is (equal (list *package*) (current-packages *package*)))
14 | (is (equal (current-packages 'breeze.user) (list (find-package :breeze.user))))
15 | (is (equal (mapcar #'find-package '(breeze cl))
16 | (current-packages '(breeze cl))))
17 | (is (equal
18 | (list
19 | (find-package :cl))
20 | (current-packages #'(lambda ()
21 | (find-package :cl)))))
22 | (is
23 | (equal
24 | (sort
25 | (current-packages "breeze\\.(user|xref)\\.test")
26 | #'string<
27 | :key #'package-name)
28 | (list (find-package '#:breeze.user.test)
29 | (find-package '#:breeze.xref.test)))))
30 |
31 | ;; (main)
32 | ;; (next)
33 |
--------------------------------------------------------------------------------
/docs/roadmap.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 11dd9906-75ff-4abc-82a5-b7dda0936f06
3 | :END:
4 | #+title: Roadmap
5 |
6 | * Roadmap
7 |
8 | This roadmap serves as a rule of thumb for priorisation.
9 |
10 | 1. Make the reader and linting incremental
11 | 2. Make a CLI that can be used
12 | - in the CI to generate a "code quality
13 | report"
14 | - from an editor for linting and fixing
15 | 3. Make commands more declarative (see [[file:src/pattern.lisp][pattern.lisp]])
16 | 4. Improve the documentation
17 | 5. Implement better refactor commands
18 | 6. Improve breeze-eval function, add tests
19 | 7. Add support for other editors
20 | - this should help improving the internals
21 | 8. Reduce breeze's dependencies
22 | - break the "breeze" system in smaller systems, these could have
23 | more dependencies as needed
24 | - vendor alexandria
25 | - maybe vendor bordeaux-threads and/or chanl
26 | - this might not be necessary if breeze could be 100% out-of-image
27 |
28 | ** Status of the roadmap
29 |
30 | As of 2023-11-24, all the points from 1 through 5 are being worked on
31 | concurrently.
32 |
--------------------------------------------------------------------------------
/scratch-files/try-context-menu-mode.el:
--------------------------------------------------------------------------------
1 | ;; https://ruzkuku.com/texts/emacs-mouse.html
2 |
3 | (when (fboundp 'context-menu-mode))
4 |
5 | (defun breeze-context-menu (menu mouse-click-event)
6 | ;; (message "%S - %S" menu major-mode)
7 | (when (eq major-mode 'lisp-mode)
8 | (save-excursion
9 | (mouse-set-point mouse-click-event)
10 | (when
11 | ;; Add a separator at the end
12 | (define-key-after menu
13 | ;; Name of the separator
14 | [breeze-context-menu-separator]
15 | ;; Definition of the binding
16 | menu-bar-separator
17 | ;; After is nil, so the new binding goes at the end of the keymap
18 | ))
19 | (define-key-after menu [breeze-menu-test]
20 | '(menu-item "Breeze quickfix"
21 | breeze-quickfix
22 | :help "Call breeze quickfix at point"))))
23 | menu)
24 |
25 | define-key-after
26 |
27 | (add-hook 'context-menu-functions #'breeze-context-menu)
28 |
29 |
30 | ;; (context-menu-undo context-menu-region context-menu-middle-separator context-menu-local context-menu-minor)
31 |
--------------------------------------------------------------------------------
/scratch-files/remote-loading.lisp:
--------------------------------------------------------------------------------
1 | ;;; Work in progress
2 | #|
3 |
4 | Goal: be able to load breeze into a remote lisp image
5 |
6 | Motivation: making breeze easier to use in more contexts
7 |
8 | Expected issue: Loading breeze might be easy... if we send the source
9 | code through sly/slime's listener. But it probably won't be easy to do
10 | so for all the dependencies...
11 |
12 | Also, I already tried asdf's "bundle" feature... It's not great,
13 | especially for third-party dependencies.
14 |
15 | From a high-level point of view, I can think of 3 major things to
16 | figure out:
17 |
18 | - how to load _one_ file
19 | - how to load a system
20 | - which systems to load?
21 |
22 | |#
23 |
24 | (defpackage #:breeze.remote-loading
25 | (:documentation "Helper code to load breeze into a lisp image that doesn't have
26 | breeze's code locally (i.e. a remote image).")
27 | (:use #:cl))
28 |
29 | (in-package #:breeze.remote-loading)
30 |
31 | #++ (progn
32 | (asdf:already-loaded-systems)
33 | (asdf:system-defsystem-depends-on)
34 |
35 | (defclass fake-load-op (asdf:load-op)
36 | ())
37 |
38 | (asdf:))
39 |
--------------------------------------------------------------------------------
/scripts/test-pedantic.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | # This script is used to run the tests with sbcl
4 | #
5 |
6 | set -e
7 |
8 | cd "$(git rev-parse --show-toplevel)"
9 |
10 | # ASDF checks for warnings and errors when a file is compiled. The
11 | # variables asdf:*compile-file-warnings-behaviour* and
12 | # asdf:*compile-file-failure-behaviour* control the handling of any such
13 | # events. The valid values for these variables are :error, :warn, and
14 | # :ignore.
15 |
16 | # TODO this loads stuff twice... because I used ql:quickload to ensure
17 | # that the dependencies are downloaded before we compile again...
18 |
19 | # TODO it would be nicer if the "lisp script" was in its own file
20 | exec sbcl --noinform --non-interactive \
21 | --eval "(declaim (optimize (debug 3) (speed 0) (safety 3)))" \
22 | --eval "(asdf:load-asd (truename \"breeze.asd\"))" \
23 | --eval "(ql:quickload '#:breeze/test :verbose t)" \
24 | --eval "(setf asdf:*compile-file-warnings-behaviour* :error)" \
25 | --eval "(asdf:compile-system '#:breeze/test :force-not t :force t :verbose t)" \
26 | --eval "(breeze.test.main:run-sbcl-tests :exitp t)"
27 |
--------------------------------------------------------------------------------
/docs/neovim.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: f3a9c9a2-8180-43a8-9424-e66fd6190caa
3 | :END:
4 | #+title: Vim and Neovim integration
5 |
6 | NOT IMPLEMENTED, this is a design document (for the moment)
7 |
8 | * Syntax checking and linting in neovim
9 | :PROPERTIES:
10 | :ID: f8811c6f-9813-418f-a745-72be32add601
11 | :END:
12 |
13 | ** [[https://github.com/vim-syntastic/syntastic][syntastic]] - deprecated in september 2023, they suggests using [[https://github.com/dense-analysis/ale][ALE]] instead
14 |
15 | ** ALE - Asynchronous Lint Engine
16 |
17 | - AFAIK, it can either run an exectutable or connect to a language
18 | server.
19 | - async
20 | - also supports fixing (not just linting) using an executable
21 | - offers many more features than linting and fixing when using a
22 | laguage server.
23 |
24 | * Existing vim plugins for Common Lisp
25 | :PROPERTIES:
26 | :ID: f66155a2-d4fd-4aef-8336-8210cd472735
27 | :END:
28 |
29 | ** TODO vlime
30 |
31 | ** TODO slimv
32 |
33 |
34 | * Related notes
35 |
36 | - [[id:6bd2b06d-0a3c-4d32-9a1e-4f6f36e1003d][Emacs integration]]
37 | - [[id:086c7705-e5ec-4dc0-852d-211c055eb145][Visual Studio Code integration]]
38 |
--------------------------------------------------------------------------------
/tests/pattern/rewrite.lisp:
--------------------------------------------------------------------------------
1 | (in-package #:breeze.test.pattern)
2 |
3 | ;;; Match substitution
4 |
5 | (defun test-pattern-substitute (pattern bindings)
6 | (let ((compiled-pattern (compile-pattern pattern)))
7 | (pattern-substitute compiled-pattern bindings)))
8 |
9 | (define-test+run pattern-substitute
10 | (progn
11 | (is eq nil (test-pattern-substitute nil nil))
12 | (is eq t (test-pattern-substitute t nil))
13 | (is eq 'x (test-pattern-substitute 'x nil)))
14 | (progn
15 | (is eq nil (test-pattern-substitute nil t))
16 | (is eq t (test-pattern-substitute t t))
17 | (is eq 'x (test-pattern-substitute 'x t)))
18 | (progn
19 | (is eql 42 (test-pattern-substitute
20 | :?x (substitutions '((:?x 42)))))
21 | (is eq t (test-pattern-substitute t t))
22 | (is eq 'x (test-pattern-substitute 'x t))))
23 |
24 |
25 |
26 | ;;; Rules and rewrites
27 |
28 |
29 | #++
30 | (let ((r (make-rewrite '(/ ?x ?x) 1)))
31 | (list (eqv (rewrite-pattern r) #(/ (var :?x) (var :?x)))
32 | (rewrite-template r)))
33 |
34 | #++
35 | (make-rewrite '(/ (* ?x ?y) ?z)
36 | '(* ?x (/ ?y ?z)))
37 |
38 | #++
39 | (make-rewrite '(/ ?x 1) ?x)
40 |
--------------------------------------------------------------------------------
/docs/change_impact_analysis.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: f3e3952d-e6f7-4bb9-a85c-662ae82874eb
3 | :END:
4 | #+title: Change Impact Analysis
5 |
6 | I wanted to make breeze able to detect changes that haev impacts that
7 | are hard for beginners to keep in mind (and easy to forget even for
8 | experienced users).
9 |
10 | For example, you define a function, you rename it and re-evaluate the
11 | defun. The source code only has the new function, but the image has
12 | both the new and the old. It would be nice to have breeze help with
13 | perhaps =fmakunbound= it or to update the package's exports (both in
14 | the source and in the image).
15 |
16 | This is hard.
17 |
18 | But I just found a "keyword" that could help me find relevant
19 | techniques: [[https://en.wikipedia.org/wiki/Change_impact_analysis][Change Impact Analysis (Wikipedia)]].
20 |
21 | Furthermore, I found this "change impact analysis" while looking at
22 | papers about "AST hashing", which is something I've had in my mind for
23 | practically a decade, if not more. I did _some_ experiments around
24 | this subject (AST hashing), but I was waiting for a better
25 | reader/parser before trying something more fancy (than hashing nested
26 | lists).
27 |
--------------------------------------------------------------------------------
/scripts/better-trace.lisp:
--------------------------------------------------------------------------------
1 | (defpackage #:breeze.better-trace
2 | (:documentation "Configuring sbcl for more readable traces.")
3 | (:use #:cl))
4 |
5 | (in-package #:breeze.better-trace)
6 |
7 | (defvar *default-trace-report-default* sb-debug:*trace-report-default*)
8 |
9 | ;; tracing is very very useful for debugging, but the default way sbcl
10 | ;; often prints "way too much" stuff
11 | (defun trace-report (depth function event stack-frame values)
12 | (declare (ignorable stack-frame))
13 | ;; (pprint-logical-block stream values :prefix ... :suffix ...)
14 | (let ((*print-pretty* nil)
15 | (stream *standard-output*))
16 | (terpri stream)
17 | (pprint-logical-block (stream values
18 | :per-line-prefix (format nil "~v@{~A~:*~}" depth " |"))
19 | ;; (loop :repeat depth :do (format stream " |"))
20 | (pprint-indent :current depth stream)
21 | (case event
22 | (:enter
23 | (format stream "~3d (~a ~{~a~^ ~})" depth function values))
24 | (:exit
25 | (format stream "~3d => ~{~a~^, ~}" depth values))
26 | (t
27 | (format stream "~3d ~s (~a ~{~a~^ ~})" depth event function values))))))
28 |
29 | (setf sb-debug:*trace-report-default* 'trace-report)
30 |
--------------------------------------------------------------------------------
/scratch-files/html.lisp:
--------------------------------------------------------------------------------
1 | #|
2 |
3 | Playing around with the idea of replacing spinneret because it's pretty
4 | slow to load.
5 |
6 | |#
7 |
8 | (defpackage #:breeze.html
9 | (:documentation "")
10 | (:use #:cl))
11 |
12 | (in-package #:breeze.html)
13 |
14 | (defclass tag ()
15 | ((name :initform nil
16 | :initarg :name
17 | :accessor name)
18 | (attributes :initform nil
19 | :initarg :attributes
20 | :accessor attributes)
21 | (body :initform nil
22 | :initarg :body
23 | :accessor body)))
24 |
25 | (dolist (sym
26 | '(br
27 | a
28 | p
29 | div span
30 | pre
31 | html
32 | link
33 | ol li
34 | h1 h2 h3 h4 h5 h6))
35 | (setf (symbol-value sym) sym)
36 | (export sym))
37 |
38 | (defmethod make-tag (name attributes body)
39 | (values name attributes body))
40 |
41 | (defun tag (name &rest attributes-and-body)
42 | (let ((attributes (butlast attributes-and-body))
43 | (body (car (last attributes-and-body))))
44 | (multiple-value-bind (name attributes body)
45 | (make-tag name attributes body))
46 | (make-instance 'tag
47 | :name name
48 | :attributes attributes
49 | :body body)))
50 |
51 | (tag br)
52 |
--------------------------------------------------------------------------------
/scratch-files/notes/function-redefinition.lisp:
--------------------------------------------------------------------------------
1 |
2 | (example "Change implementation"
3 | :before
4 | ((in-package 'examples)
5 | (defun 2x (x)
6 | (+ x x)))
7 | (:after
8 | ((in-package 'examples)
9 | (defun 2x (x)
10 | (* 2 x)))))
11 |
12 | (example "Add documentation"
13 | :before
14 | ((in-package 'examples)
15 | (defun 2x (x)
16 | (+ x x)))
17 | (:after
18 | ((in-package 'examples)
19 | (defun 2x (x)
20 | "Doubles x"
21 | (+ x x)))))
22 |
23 | (example "Change documentation"
24 | :before
25 | ((in-package 'examples)
26 | (defun 2x (x)
27 | "Doubles x"
28 | (+ x x)))
29 | (:after
30 | ((in-package 'examples)
31 | (defun 2x (x)
32 | "Adds x to itself"
33 | (+ x x)))))
34 |
35 | (example "Change implementation and documentation"
36 | :before
37 | ((in-package 'examples)
38 | (defun 2x (x)
39 | "Doubles x"
40 | (+ x x)))
41 | (:after
42 | ((in-package 'examples)
43 | (defun 2x (x)
44 | "Multiply x by 2"
45 | (* 2 x)))))
46 |
47 | (example "Add a function"
48 | :before
49 | ((in-package 'examples)
50 | (defun 2x (x)
51 | "Doubles x"
52 | (+ x x)))
53 | (:after
54 | ((in-package 'examples)
55 | (defun 2x (x)
56 | "Doubles x"
57 | (+ x x))
58 | (defun 3x (x)
59 | "Multiply x by 3"
60 | (* 2 x)))))
61 |
--------------------------------------------------------------------------------
/scripts/profile-loading.lisp:
--------------------------------------------------------------------------------
1 | (cl:in-package #:cl-user)
2 |
3 | (defpackage #:breeze.profile-loading
4 | (:documentation "A script to figure out which dependency takes the most time to load
5 | when loading breeze.")
6 | (:use #:cl))
7 |
8 | (in-package #:breeze.profile-loading)
9 |
10 | (defvar *system-load-times* (make-hash-table :test 'eql))
11 |
12 | ;; Printing all the type of operations, to make sure I understand
13 | ;; asdf's operation order.
14 | (defmethod asdf:perform :before ((op t) (system asdf:system))
15 | (format t "~&op: ~a system: ~a" op system))
16 |
17 | ;; TODO We currently only check the time it takes to load a system,
18 | ;; not including the time it took to compile it.
19 |
20 | (defmethod asdf:perform :before ((op asdf:load-op) (system asdf:system))
21 | (format t "op")
22 | (setf (gethash system *system-load-times*) (- (get-internal-run-time))))
23 |
24 | (defmethod asdf:perform :after ((op asdf:load-op) (system asdf:system))
25 | (incf (gethash system *system-load-times*) (get-internal-run-time)))
26 |
27 | (asdf:load-asd
28 | (merge-pathnames "../breeze.asd" *load-truename*))
29 |
30 | ;; This doesn't work if some packages where loaded from a read-only
31 | ;; store, like guix or nix...
32 | (asdf:load-system "breeze" :force :all)
33 |
34 |
35 | ;; TODO Run this script in a container with sbcl and quicklisp set-up
36 | ;; TODO Analyze the results
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 2-Clause License
2 |
3 | Copyright (c) 2023, Francis St-Amour
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 |
--------------------------------------------------------------------------------
/src/configuration.lisp:
--------------------------------------------------------------------------------
1 |
2 | (defpackage #:breeze.configuration
3 | (:documentation "Breeze's configuration")
4 | (:nicknames #:breeze.config)
5 | (:use #:cl)
6 | (:export
7 | #:*default-author*
8 | #:*default-system-licence*
9 | #:*capture-folder*
10 | #:*capture-template*
11 | #:load-config-file))
12 |
13 | (in-package #:breeze.configuration)
14 |
15 | ;;; Configurations
16 |
17 | (defvar *default-author* ""
18 | "The default author when generating asdf system.")
19 |
20 | (defvar *default-system-licence* "Public"
21 | "The default licence when generating asdf system.")
22 |
23 | (defvar *capture-folder*
24 | (merge-pathnames "breeze-capture/" (user-homedir-pathname))
25 | "The folder where to save capture files.")
26 |
27 | ;; TODO Load from /data/default-capture-template.lisp
28 | (defvar *capture-template*
29 |
30 | "(ql:quickload '(alexandria))
31 |
32 | ;; make it easier to debug
33 | (declaim (optimize (speed 0) (safety 3) (debug 3)))
34 |
35 | #|
36 |
37 | Goal:
38 |
39 | Motivation:
40 |
41 | What am I going to try first:
42 |
43 | |#
44 |
45 | "
46 | "The format string used to populate a capture file when first creating it.")
47 |
48 | (defun load-config-file ()
49 | "Load breeze's config file."
50 | (let ((path (uiop:xdg-config-home "breeze/config.lisp")))
51 | (when (probe-file path)
52 | (let ((*package* (find-package '#:cl-user)))
53 | (load path)))))
54 |
--------------------------------------------------------------------------------
/docs/index.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 9c910250-abdc-4cbe-961b-46ad5c4f82d4
3 | :END:
4 | #+title: Breeze
5 | #+options: toc:nil
6 |
7 | * Documentation
8 |
9 | - [[id:d08ab932-1204-4e7c-9869-40fc53500071][Introduction]]
10 | - [[id:e5d64314-8b13-4a6b-997f-1aae94910d63][Goals and non-goals]]
11 | - [[id:11dd9906-75ff-4abc-82a5-b7dda0936f06][Roadmap]]
12 | - [[id:306350c9-0fb5-478b-958b-b35cae726280][Getting Started]]
13 | - [[id:5d211d9a-0749-4adb-abe0-e66133d09b5b][Editor integrations]]
14 | - [[id:14d42b3a-0a2f-4a3b-8937-7175e621c6ec][Design Decisions]]
15 | - [[id:279c4ea6-2004-4a7a-a2c9-905f27fae42c][Contributing]]
16 | - [[id:e712f3d1-0734-43f0-886a-3008ca5f722d][Testing Breeze]]
17 | - [[id:bb5c6ad4-0f89-48aa-9295-13e5e248a897][Glossary]]
18 | - [[file:reference.html][Reference]]
19 | - [[file:listing-breeze.html][Source listings]]
20 |
21 | * Internal notes
22 |
23 | - [[id:7d0f5cd2-d216-4882-84ac-27c004ad6fbd][Links to some dependencies' documentation]]
24 | - [[id:598a884c-56d0-4378-b5f5-acb2671d5112][Inbox]]
25 | - [[id:e2ff6189-1fd8-4d3c-9b7d-3d3ddbf2b0aa][Ideas]]
26 | - [[id:b9f7e1f4-dc86-46e0-860b-f845f180110e][Breeze on the internets]]
27 | - [[id:31236780-159e-4a58-9019-37f57f5b4997][FAQ from newbies about common lisp]]
28 | - [[id:62112623-6002-4cb9-87de-cb530ce0a36e][Silimar projects]]
29 | - [[id:13ea055e-4715-4583-811b-bff78ca300ee][Candidate Features]]
30 |
31 | ** More specific notes
32 |
33 | - [[id:32155195-1bc4-4f2d-8f6a-12fb0bd68ecc][E-graphs]]
34 |
--------------------------------------------------------------------------------
/docs/programming_with_holes.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 69ab6084-2e41-4893-82b8-85ac04b1b1ca
3 | :END:
4 | #+title: Programming with holes
5 |
6 | * Programming with holes
7 |
8 | =Holes=, in programming, are something used to tell the language that
9 | a part of the program is incomplete. Some languages like Idris and
10 | Agda natively support =typed holes=. The way I see it, holes are used
11 | to falicitate the conversation between the programmer and the
12 | compiler.
13 |
14 | But, for languages like common lisp that doesn't support holes
15 | out-of-the box, how could we do that? In general, there are no symbol
16 | name that will never clash with other symbols, because symbols in
17 | common lisp can be any string. One idea is to use inline comments,
18 | like ~#| hole-name |#~. Breeze's parser would be able to recognize
19 | them and manipulate them.
20 |
21 | But what for?
22 |
23 | ** Snippets
24 |
25 | Holes can be used to both tell the user what he is expected to enter
26 | in a snippet and tell the editor where the user is expected to enter
27 | stuff.
28 |
29 | ** Typing
30 |
31 | A user could use a hole to tell the editor to infer the type of an
32 | expression or function and replace the hole by the appropriate
33 | declaration.
34 |
35 | ** Program synthesis
36 |
37 | A user could use a hole to tell the editor to find the right
38 | expression where the hole is. This probably requires that the user
39 | specify some more constraints, by giving types, writing tests, etc.
40 |
--------------------------------------------------------------------------------
/src/cmds/quicklisp.lisp:
--------------------------------------------------------------------------------
1 | (defpackage #:breeze.quicklisp
2 | (:documentation "Utilities for quicklisp")
3 | (:use #:cl #:breeze.command)
4 | (:export #:quickload))
5 |
6 | (in-package #:breeze.quicklisp)
7 |
8 | #+quicklisp
9 | (define-command quickload ()
10 | "Choose a system to load with quickload."
11 | (let* ((systems (ql-dist:provided-systems t))
12 | (mapping (make-hash-table :test 'equal))
13 | (choices (mapcar (lambda (system)
14 | (let* ((release (ql-dist:release system))
15 | (string (format nil "~a (~a ~a)"
16 | (ql-dist:name system)
17 | (ql-dist:prefix release)
18 | (ql-dist:short-description (ql-dist:dist release)))))
19 | (setf (gethash string mapping) system)
20 | string))
21 | systems))
22 | (choice (choose "Choose a system: " choices))
23 | (chosen-system (gethash choice mapping)))
24 | (cond
25 | (chosen-system #| TODO |#)
26 | (t #| TODO |#))))
27 |
28 |
29 | ;; TODO create a similar commands that uses asdf only
30 | ;; TODO create a similar command that calls (asdf:test-system)
31 | ;; TODO should extract the "choose a system" part
32 | ;;
33 | ;; other "quicklisp" commands:
34 | ;; - intstall ?? meh
35 | ;; - update client
36 | ;; - update dist
37 | ;; - check for update?
38 |
--------------------------------------------------------------------------------
/docs/sly_slime_integration.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 54e6cd55-803b-4e15-82bc-a332130d020e
3 | :END:
4 | #+title: Sly/Slime integration
5 |
6 | * Other projects with slime/sly integration
7 |
8 | ** log4cl
9 |
10 | - https://github.com/sharplispers/log4cl/blob/master/log4cl.log4slime.asd
11 | - https://github.com/sharplispers/log4cl/blob/master/log4cl.log4sly.asd
12 | - https://github.com/sharplispers/log4cl/tree/master/elisp
13 |
14 | ** cepl
15 |
16 | - https://github.com/cbaggers/cepl/blob/master/docs/single-thread-swank.md
17 | - https://github.com/cbaggers/livesupport
18 | - https://github.com/cbaggers/swank.live
19 |
20 | ** cl-routes
21 |
22 | https://github.com/archimag/cl-routes/blob/master/src/routes-swank.lisp
23 |
24 | * TODO My old elisp snippet to eval with slime and kill the result
25 |
26 | https://gist.github.com/fstamour/2d7569beaf42c0a0883dc0ae559c6638
27 |
28 | #+begin_src emacs-lisp
29 | (defun slime-eval-save-output (string)
30 | "Evaluate STRING in Lisp and save the result in the kill ring."
31 | (slime-eval-async `(swank:eval-and-grab-output ,string)
32 | (lambda (result)
33 | (cl-destructuring-bind (output value) result
34 | (kill-new output)
35 | (message "Evaluation finished; pushed output to kill ring.")))))
36 |
37 |
38 | (defun lisp-eval-defun-in-kill-ring ()
39 | (interactive)
40 | (slime-eval-save-output (slime-defun-at-point)))
41 |
42 | (global-set-key (kbd "C-M-z") 'lisp-eval-defun-in-kill-ring)
43 | #+end_src
44 | * TODO https://github.com/melisgl/mgl-pax for more emacs/slime integration :editor:
45 |
--------------------------------------------------------------------------------
/scratch-files/edit-distance.lisp:
--------------------------------------------------------------------------------
1 |
2 | (defun levenshtein (vec-a vec-b)
3 | (let* ((m (length vec-a))
4 | (n (length vec-b))
5 | (diff (make-array (list 2 (1+ n)) :element-type 'integer))
6 | (p 0)) ;; p for pointer *shrug*
7 |
8 | (loop :for i :upto n :do
9 | (setf (aref diff 1 i) i))
10 | (setf (aref diff 0 0) 1)
11 |
12 | (flet ((a (index) (aref vec-a (1- index)))
13 | (b (index) (aref vec-b (1- index)))
14 | (diff (i j) (aref diff i j))
15 | (p (which) (if (zerop which)
16 | p (if (= 0 p) 1 0))))
17 | (loop :for i :from 1 :upto m :do
18 | (loop :for j :from 1 :upto n
19 | :for cost = (if (eq (a i) (b j)) 0 1) ;; aka substitution-cost
20 | :do
21 | (setf (aref diff (p 0) j)
22 | (min
23 | (1+ (diff (p 1) j)) ;; deletion
24 | (1+ (diff (p 0) (1- j))) ;; insertion
25 | (+ cost (diff (p 1) (1- j))) ;; substitution
26 | )))
27 | (when (/= m i)
28 | (setf p (if (zerop p) 1 0))
29 | (setf (aref diff (p 0) 0) (1+ i))))
30 | (diff (p 0) n))))
31 |
32 | (levenshtein
33 | "ca"
34 | "abc")
35 | ;; => 3
36 |
37 | (levenshtein
38 | "string a"
39 | "string b")
40 | ;; => 1
41 |
42 | (levenshtein
43 | "string"
44 | "string")
45 | ;; => 0
46 |
47 | (levenshtein
48 | "a"
49 | "string")
50 | ;; => 6
51 |
52 | (levenshtein
53 | "string"
54 | "a")
55 | ;; => 6
56 |
--------------------------------------------------------------------------------
/scripts/load-dependencies.lisp:
--------------------------------------------------------------------------------
1 | (cl:in-package #:cl-user)
2 |
3 | (require '#:asdf)
4 |
5 | #-quicklisp
6 | (let ((quicklisp-init #P"/opt/quicklisp/setup.lisp"))
7 | (when (probe-file quicklisp-init)
8 | (load quicklisp-init)))
9 |
10 | (asdf:load-asd
11 | (merge-pathnames "../breeze.asd" *load-truename*))
12 |
13 | (flet ((find-all-related-systems (system)
14 | "Given a system, find all systems defined in the same system definition
15 | file (including the one passed as argument)."
16 | (let ((result ())
17 | (asd-pathname (asdf:system-source-file system)))
18 | (asdf:map-systems (lambda (system)
19 | (when (equal asd-pathname
20 | (asdf:system-source-file system))
21 | (push system result))))
22 | result)))
23 | (let* ((systems (find-all-related-systems "breeze"))
24 | (dependencies (remove-if (lambda (system-name)
25 | (uiop:string-prefix-p "breeze" system-name))
26 | (loop
27 | :for system-name :in systems
28 | :for system = (asdf:find-system system-name)
29 | :for dependecy-list = (asdf:system-depends-on system)
30 | :append (copy-list dependecy-list)))))
31 | (ql:quickload dependencies)
32 | (mapcar #'asdf:register-immutable-system dependencies)))
33 |
34 | (uiop:dump-image "dependencies.core")
35 |
--------------------------------------------------------------------------------
/tests/buffer.lisp:
--------------------------------------------------------------------------------
1 |
2 | (defpackage #:breeze.test.buffer
3 | (:documentation "Test package for #:breeze.buffer")
4 | (:use #:cl #:breeze.buffer)
5 | (:import-from #:parachute
6 | #:define-test
7 | #:define-test+run
8 | #:is
9 | #:true
10 | #:false
11 | #:of-type
12 | #:finish))
13 |
14 | (in-package #:breeze.test.buffer)
15 |
16 | (define-test+run base
17 | (of-type 'buffer (make-buffer))
18 | (is string= "#"
19 | (prin1-to-string (make-buffer)))
20 | (is string= "#"
21 | (prin1-to-string (make-buffer :name "foo.lisp")))
22 | (is string= "#"
23 | (prin1-to-string (make-buffer :name "foo.lisp"
24 | :string ""))))
25 |
26 | (define-test+run current-package
27 | (false (let* ((string "(in-package #:cl-user)")
28 | (buffer (make-buffer :string string)))
29 | (setf (point buffer) 0)
30 | (current-package buffer)))
31 | (false (let* ((string "(in-package #:cl-user)")
32 | (buffer (make-buffer :string string)))
33 | (setf (point buffer) 10)
34 | (current-package buffer)))
35 | (is equalp '(:uninterned "CL-USER")
36 | (let* ((string "(in-package #:cl-user)")
37 | (buffer (make-buffer :string string)))
38 | (setf (point buffer) (length string))
39 | (let (($node (current-package buffer)))
40 | (breeze.analysis:parse-symbol-node $node)))))
41 |
--------------------------------------------------------------------------------
/scratch-files/xref.lisp:
--------------------------------------------------------------------------------
1 |
2 |
3 | (defun package-test (package)
4 | "Find all tests defined in PACKAGE."
5 | (let ((package (find-package package)))
6 | (loop
7 | :for test-name :being :the :hash-key :of breeze.test:*test*
8 | :using (hash-value test-definition)
9 | :for test-package = (second test-definition)
10 | :when (eq test-package package)
11 | :collect test-name)))
12 |
13 | (defun tests-by-package ()
14 | "Return a hash-table of tests keyed by package."
15 | (cl-hash-util:collecting-hash-table (:mode :append)
16 | (loop
17 | :for test-name :being :the :hash-key :of breeze.test:*test*
18 | :using (hash-value test-definition)
19 | :for package = (second test-definition)
20 | :do (cl-hash-util:collect package test-definition))))
21 |
22 |
23 | (defun calls-who (function-name)
24 | "Take a function name and returns a list of all the functions it calls."
25 | (uiop:while-collecting (collect)
26 | (walk-car
27 | (function-body function-name)
28 | (lambda (el)
29 | (when (function-body el)
30 | (collect el))))))
31 |
32 | ;; TODO
33 | #+todo ;; It's done, but not tested nor used
34 | (defun list-function (&optional (package *package*))
35 | "List all the functions, optionally filter by package"
36 | (loop :for function-name :being :the :hash-key :of *function*
37 | :when (if package
38 | (eq package (symbol-package function-name))
39 | t)
40 | :collect function-name))
41 |
42 | ;; TODO
43 | #+todo ;; Look at parse-smth in cover.lisp
44 | (defun function-without-documentation (&optional (package *package*)))
45 |
--------------------------------------------------------------------------------
/scratch-files/analysis.lisp:
--------------------------------------------------------------------------------
1 | (in-package #:breeze.test.analysis)
2 |
3 | ;; TODO move somewhere else
4 | ;; maybe add a file "debug utilities"
5 | (defmacro with-trace ((&rest spec) &body body)
6 | `(progn
7 | ,@(loop :for s :in spec :when (listp s) :collect `(trace ,@s))
8 | (trace ,@(loop :for s :in spec :unless (listp s) :collect s))
9 | (unwind-protect
10 | (progn ,@body)
11 | (untrace ,@(loop :for s :in spec
12 | :collect (if (listp s) (car s) s))))))
13 |
14 | (with-trace ((match :methods t)
15 | breeze.pattern::go-down-together
16 | breeze.pattern::go-up-together)
17 | (match (compile-pattern '(if ?cond)) (parse "(if a)")))
18 |
19 | (with-trace (eqv)
20 | (is eqv
21 | (substitutions `((?cond ,(iterator-value (token 4 5 :name "A")))))
22 | (match (compile-pattern '(if ?cond)) (parse "(if a)")
23 | :skipp #'whitespace-or-comment-node-p)))
24 |
25 | (with-trace ((match :methods t)
26 | next
27 | breeze.pattern::go-down-together
28 | breeze.pattern::go-up-together)
29 | (match (compile-pattern '(if (:maybe ?cond))) (parse "(if)")))
30 |
31 | (with-trace (match ;;(match :methods t)
32 | breeze.pattern::go-down-together
33 | breeze.pattern::go-up-together)
34 | (test-malformed-if-node-p "(if a b c d e)"))
35 |
36 |
37 | (with-trace (match ;;(match :methods t)
38 | breeze.pattern::go-down-together
39 | breeze.pattern::go-up-together)
40 | (test-malformed-if-node-p "(if a b)"))
41 |
42 | (with-trace ((match :methods t))
43 | (match (compile-pattern '(if ?cond)) (parse "(if)")))
44 |
--------------------------------------------------------------------------------
/tests/package-commands.lisp:
--------------------------------------------------------------------------------
1 | (defpackage #:breeze.test.package-commands
2 | (:documentation "Tests for the package breeze.package-commands")
3 | (:use #:cl #:breeze.package-commands)
4 | (:import-from #:parachute
5 | #:define-test
6 | #:define-test+run
7 | #:is
8 | #:true
9 | #:false
10 | #:of-type
11 | #:finish))
12 |
13 | (in-package #:breeze.test.package-commands)
14 |
15 | ;; TODO Variants: *insert-defpackage/cl-user-prefix*
16 | ;; TODO infer-project-name
17 | ;; TODO infer-is-test-file
18 | ;; TODO infer-package-name-from-file
19 | #++
20 | (define-test+run insert-defpackage
21 | (let* ((trace (drive-command #'insert-defpackage
22 | :inputs '("pkg")
23 | :context '())))
24 |
25 | (common-trace-asserts 'insert-defpackage trace 4)
26 | (destructuring-bind (input request) (first trace)
27 | (false input)
28 | (is string= "read-string" (first request))
29 | (is string= "Name of the package: " (second request))
30 | (false (third request)))
31 | (destructuring-bind (input request) (second trace)
32 | (is string= "pkg" input)
33 | (is string= "insert" (first request))
34 | (is equal "(defpackage " (second request)))
35 | (destructuring-bind (input request) (third trace)
36 | (false input)
37 | (is string= "insert" (first request))
38 | (is equal
39 | '("#:pkg"
40 | " (:documentation \"\")"
41 | " (:use #:cl))"
42 | ""
43 | "(in-package #:pkg)")
44 | (split-by-newline (second request))))))
45 |
--------------------------------------------------------------------------------
/scratch-files/macroexpand.lisp:
--------------------------------------------------------------------------------
1 | ;;;; Goal: experimenting with macroexpand-hook to see if it could be
2 | ;;;; of use (e.g. to detect a `defun`).
3 |
4 | (cl:in-package #:common-lisp-user)
5 |
6 | (defpackage #:macroexpand
7 | (:use :cl))
8 |
9 | (in-package #:macroexpand)
10 |
11 | (describe '*macroexpand-hook*)
12 | #|
13 | Documentation:
14 | The value of this variable must be a designator for a function that can
15 | take three arguments,
16 | - a macro expander function,
17 | - the macro form to be expanded, and
18 | - the lexical environment to expand in.
19 |
20 | The function should return the expanded form.
21 | This function is called by MACROEXPAND-1 whenever a *runtime* expansion
22 | is needed.
23 |
24 | Initially this is set to FUNCALL.
25 | |#
26 |
27 |
28 | (defmacro defun* (name (&rest lambda-list) &body body)
29 | `(progn
30 | (format t "~&defun* ~a" name)
31 | (defun ,name ,lambda-list ,@body)))
32 |
33 | (defun macroexpand-hook (expander macro-form environment)
34 | (let ((macroexpanded-form
35 | (funcall expander macro-form environment)))
36 | (format t "~&The macro-form ~%~a~%~%expanded to ~%~a"
37 | macro-form
38 | macroexpanded-form)))
39 |
40 | (setf *macroexpand-hook* #'macroexpand-hook)
41 |
42 |
43 | (defun* x2 (x)
44 | (* x 2))
45 |
46 | #|
47 | The macro-form
48 | (DEFUN* X2
49 | (X)
50 | (* X 2))
51 |
52 | expanded to
53 | (PROGN (FORMAT T ~&defun* ~a NAME) (DEFUN X2 (X) (* X 2)))NIL
54 | |#
55 |
56 | ;;; So I would need a kind of code-walker to find "defuns" in the
57 | ;;; macro-expanded code.
58 | ;;;
59 | ;;; TODO Take a look at cl-walker
60 |
61 | (ql:quickload 'cl-walker)
62 |
63 | ;;; Note: intercepting and analyzing _every_ forms might slow down
64 | ;;; stuff...
65 |
--------------------------------------------------------------------------------
/scratch-files/fsa.lisp:
--------------------------------------------------------------------------------
1 | ;;;; FSA - Finite State Automaton
2 |
3 | (defpackage #:breeze.fsa
4 | (:documentation "")
5 | (:use #:cl))
6 |
7 | (in-package #:breeze.fsa)
8 |
9 | ;; field = commutativity, associativity and distribution of the 2 operations...
10 |
11 | (let* ((classes #(+ a b))
12 | (initial-states #(0))
13 | (occurrences '((a . 2) (b . 1)))
14 | (input #(+ a b a))
15 | (transitions '((0 . (1 2))
16 | (1 . (1 2))
17 | (2 . (1 2)))))
18 | (loop
19 | :with counts = (alexandria:alist-hash-table occurrences)
20 | :for state = (aref initial-states 0)
21 | :then (cdr (assoc class transitions))
22 | :for guard :below 100
23 | :for item :across input
24 | :for class = (position item classes)
25 | :do
26 | (unless (if (listp state)
27 | (find class state)
28 | (= class state))
29 | (return nil))
30 | (alexandria:when-let ((count (gethash item counts)))
31 | (when (zerop count)
32 | (return (values nil (format nil "Too many ~s's" item))))
33 | (decf (gethash item counts)))
34 | :do (print (list item state (alexandria:hash-table-alist counts)))
35 | :finally (progn
36 | (maphash (lambda (k v)
37 | (when (plusp v)
38 | (return (values nil (format nil "Not enough ~s's" k)))))
39 | counts)
40 | (return t))))
41 |
42 |
43 | (flet ((h (x)
44 | (loop :for item :across x
45 | :sum (sxhash item))))
46 | (apply #'=
47 | (mapcar #'h
48 | '(#(+ a b a)
49 | #(+ a a b)
50 | #(+ b a a)))))
51 |
--------------------------------------------------------------------------------
/scratch-files/function-fingerprinting.lisp:
--------------------------------------------------------------------------------
1 | ;;;; 2 march 2020 - Trying to fingerprint functions by generating
2 | ;;;; randomly, but deterministically a bunch of input and hashing all
3 | ;;;; the outputs.
4 |
5 | (in-package :breeze.user)
6 |
7 | (ql:quickload '(random-state ironclad flexi-streams))
8 |
9 | ;;; A Bunch of simple functions
10 | (defun x2 (x) (* x 2))
11 | (defun double (x) (+ x x))
12 | (defun square (x) (* x x))
13 | (defun pow2 (x) (expt x 2))
14 |
15 |
16 |
17 | (defvar *seed* 42)
18 |
19 | (defun repr (x)
20 | "Convert anything to something that can be digested (hashed)."
21 | (flexi-streams:string-to-octets (format nil "~s~s" (type-of x) x)))
22 |
23 | ;; (repr 42)
24 | ;; (repr "42")
25 |
26 | #|
27 | Assumes that the function takes 1 32-bits argument
28 | Could change
29 | * the seed
30 | * the random number generator
31 | * the number of number generated
32 | * the digest algorithm
33 | * the digest size
34 | |#
35 |
36 | (defun fingerprint-function (fn)
37 | ""
38 | (let ((rnd (random-state:make-generator :mersenne-twister-32 *seed*))
39 | (max (1- (expt 2 31)))
40 | (digester (ironclad:make-digest :shake256 :output-length 4)))
41 | (flet ((gen-int () (random-state:random-int rnd (- max) max)))
42 | (loop :for i :below 10000
43 | :for x = (funcall fn (gen-int))
44 | :do (ironclad:update-digest digester (repr x)))
45 | (ironclad:produce-digest digester))))
46 |
47 |
48 |
49 | (fingerprint-function #'x2)
50 | ;; => #(207 80 46 67)
51 |
52 | (fingerprint-function #'double)
53 | ;; => #(207 80 46 67)
54 |
55 | (fingerprint-function #'square)
56 | ;; => #(95 88 2 241)
57 |
58 | (fingerprint-function #'pow2)
59 | ;; => #(95 88 2 241)
60 |
61 |
62 |
63 | ;;;
64 | ;;; Variant: generate a digest for every input and keep the N smallests
65 | ;;; very much like Min-Hash
66 | ;;;
67 |
--------------------------------------------------------------------------------
/scratch-files/notes/error-system-loadedp.txt:
--------------------------------------------------------------------------------
1 | There is no applicable method for the generic function
2 | #
3 | when called with arguments
4 | (#P"/home/fstamour/quicklisp/local-projects/sb-unix-socket/sb-unix-socket.asd").
5 | [Condition of type SB-PCL::NO-APPLICABLE-METHOD-ERROR]
6 | See also:
7 | Common Lisp Hyperspec, 7.6.6 [:section]
8 |
9 | Restarts:
10 | 0: [RETRY] Retry calling the generic function.
11 | 1: [ABORT] abort thread (#)
12 |
13 | Backtrace:
14 | 0: ((:METHOD NO-APPLICABLE-METHOD (T)) # #P"/home/fstamour/quicklisp/local-projects/sb-unix-socket/sb-unix-socket.asd") [fast-method]
15 | 1: (SB-PCL::CALL-NO-APPLICABLE-METHOD # (#P"/home/fstamour/quicklisp/local-projects/sb-unix-socket/sb-unix-socket.asd"))
16 | 2: ((LABELS ASDF/COMPONENT::RECURSE :IN ASDF/COMPONENT:SUB-COMPONENTS) #P"/home/fstamour/quicklisp/local-projects/sb-unix-socket/sb-unix-socket.asd")
17 | 3: (ASDF/COMPONENT:SUB-COMPONENTS #P"/home/fstamour/quicklisp/local-projects/sb-unix-socket/sb-unix-socket.asd" :TYPE T)
18 | 4: (BREEZE.ASDF:LOADEDP "/home/fstamour/quicklisp/local-projects/sb-unix-socket/sb-unix-socket.lisp")
19 | 5: (BREEZE.REFACTOR::MAYBE-ASK-TO-LOAD-SYSTEM)
20 | 6: ((LAMBDA NIL :IN BREEZE.REFACTOR:QUICKFIX))
21 | 7: (BREEZE.COMMAND::CALL-WITH-COMMAND-SIGNAL-HANDLER #)
22 | 8: (BREEZE.COMMAND::CANCEL-COMMAND-ON-ERROR 2 #)
23 | 9: ((LABELS BORDEAUX-THREADS::%BINDING-DEFAULT-SPECIALS-WRAPPER :IN BORDEAUX-THREADS::BINDING-DEFAULT-SPECIALS))
24 |
--------------------------------------------------------------------------------
/githooks/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | # An example hook script to verify what is about to be committed.
4 | # Called by "git commit" with no arguments. The hook should
5 | # exit with non-zero status after issuing an appropriate message if
6 | # it wants to stop the commit.
7 | #
8 |
9 | # Stop on first error
10 | set -e
11 |
12 | echo "Executing pre-commit hook"
13 |
14 | if git rev-parse --verify HEAD >/dev/null 2>&1
15 | then
16 | against=HEAD
17 | else
18 | # Initial commit: diff against an empty tree object
19 | against=$(git hash-object -t tree /dev/null)
20 | fi
21 |
22 | # If you want to allow non-ASCII filenames set this variable to true.
23 | allownonascii=true
24 |
25 | # Redirect output to stderr.
26 | exec 1>&2
27 |
28 | # Cross platform projects tend to avoid non-ASCII filenames; prevent
29 | # them from being added to the repository. We exploit the fact that the
30 | # printable range starts at the space character and ends with tilde.
31 | if [ "$allownonascii" != "true" ] &&
32 | # Note that the use of brackets around a tr range is ok here, (it's
33 | # even required, for portability to Solaris 10's /usr/bin/tr), since
34 | # the square bracket bytes happen to fall in the designated range.
35 | test $(git diff --cached --name-only --diff-filter=A -z $against |
36 | LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
37 | then
38 | cat <<\EOF
39 | Error: Attempt to add a non-ASCII file name.
40 |
41 | This can cause problems if you want to work with people on other platforms.
42 |
43 | To be portable it is advisable to rename the file.
44 | EOF
45 | exit 1
46 | fi
47 |
48 | # If there are whitespace errors, print the offending file names and fail.
49 | git diff-index --check --cached $against --
50 |
51 |
52 | # run the tests
53 | ./scripts/test.sh
54 |
55 | # generate the docs
56 | ./scripts/doc.sh
57 |
58 | echo "Pre-commit hook done"
59 |
--------------------------------------------------------------------------------
/src/cmds/+quickproject.lisp:
--------------------------------------------------------------------------------
1 | (defpackage #:breeze+quickproject
2 | (:documentation "Integration with quickproject.")
3 | (:use #:cl)
4 | (:import-from #:breeze.command
5 | #:define-command
6 | #:read-string
7 | #:find-file
8 | #:message)
9 | (:import-from #:breeze.project
10 | #:choose-local-project-directories
11 | #:confirm-scaffold-directory)
12 | (:import-from #:breeze.config
13 | #:*default-author*
14 | #:*default-system-licence*)
15 | (:export #:breeze-quickproject))
16 |
17 | (in-package #:breeze+quickproject)
18 |
19 | (define-command breeze-quickproject (project-name directory)
20 | "Create a project interactively using quickproject."
21 | (let* (;; TODO Currently the user is able to enter an empty string
22 | (project-name
23 | (or project-name
24 | (read-string "Name of the project: ")))
25 | (directory
26 | (confirm-scaffold-directory
27 | (uiop:ensure-directory-pathname
28 | (or directory
29 | (merge-pathnames project-name
30 | (choose-local-project-directories))))))
31 | (author (read-string "Author of the project: "
32 | *default-author*))
33 | (license (read-string "Licence of the project: "
34 | *default-system-licence*)))
35 | ;; TODO depends-on
36 | ;; TODO include-copyright
37 | ;; TODO template-directory
38 | ;; TODO template-parameters
39 | ;; TODO add validations
40 | (quickproject:make-project
41 | directory
42 | :name project-name
43 | :author author
44 | :license license)
45 | (message "Project \"~a\" created." directory)
46 | (find-file directory)))
47 |
--------------------------------------------------------------------------------
/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | # Only run pipelines for merge requests, tags, and protected branches.
2 | workflow:
3 | rules:
4 | - if: $CI_PIPELINE_SOURCE == "merge_request_event"
5 | - if: $CI_COMMIT_TAG
6 | - if: $CI_COMMIT_REF_PROTECTED == "true"
7 |
8 | include:
9 | - local: .gitlab/test-job.gitlab-ci.yml
10 | inputs:
11 | lisp-impl: sbcl
12 | # disabled:
13 | # 1. abcl fails to load asdf in the container...
14 | # 2. needs to push the container to gitlab
15 | # - local: .gitlab/test-job.gitlab-ci.yml
16 | # inputs:
17 | # lisp-impl: abcl
18 | # disable:
19 | # 1. need to add scripts/manifest-ccl.scm
20 | # 2. need to push the image to gitlab
21 | # - local: .gitlab/test-job.gitlab-ci.yml
22 | # inputs:
23 | # lisp-impl: ccl
24 | # disabled: need to fix 5 tests that are not portable (i.e. they
25 | # return things in a different order in sbcl and ecl)
26 | # - local: .gitlab/test-job.gitlab-ci.yml
27 | # inputs:
28 | # lisp-impl: ecl
29 | # disabled:
30 | # 1. need to add scripts/test-clasp.sh
31 | # 2. need to add scripts/manifest-clasp.scm
32 | # 3. need to push the container to gitlab
33 | # - local: .gitlab/test-job.gitlab-ci.yml
34 | # inputs:
35 | # lisp-impl: clasp
36 |
37 | # Build public/ folder using org-publish on docs/
38 | doc:
39 | image: docker:24.0.7
40 | services:
41 | - docker:24.0.5-dind
42 | script:
43 | - apk add --no-cache make bash
44 | - make public
45 | artifacts:
46 | paths:
47 | - public
48 |
49 | pages:
50 | needs:
51 | - job: doc
52 | artifacts: true
53 | script:
54 | - echo "nothing to do!"
55 | rules:
56 | # automatically run on the default branch
57 | - if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
58 | # otherwise, the job must be run manually
59 | - when: manual
60 | artifacts:
61 | paths:
62 | - public
63 |
--------------------------------------------------------------------------------
/src/cmds/package-commands.lisp:
--------------------------------------------------------------------------------
1 | (defpackage #:breeze.package-commands
2 | (:documentation "Package-related commands")
3 | (:use #:cl
4 | #:breeze.analysis
5 | #:breeze.command
6 | #:breeze.package
7 | #:breeze.workspace)
8 | (:export #:insert-defpackage
9 | #:insert-local-nicknames
10 | #:insert-in-package-cl-user))
11 |
12 | (in-package #:breeze.package-commands)
13 |
14 |
15 | ;;; Commands for inserting and modifying package-related forms
16 |
17 | (define-command insert-defpackage ()
18 | "Insert a defpackage form."
19 | (declare (context :top-level))
20 | (let ((package-name
21 | (read-string
22 | "Name of the package: "
23 | (infer-package-name-from-file (current-buffer-filename)))))
24 | (when (in-package-cl-user-p)
25 | (insert
26 | "(cl:in-package #:cl-user)~%~%"))
27 | (progn
28 | ;; TODO if nil (insert "(uiop:define-package ")
29 | (insert "(defpackage "))
30 | ;; TODO don't insert the (in-package ...) if it already exists
31 | ;; TODO add documentation "Tests for the package X"
32 | (insert
33 | "#:~a~
34 | ~% (:documentation \"\")~
35 | ~% (:use #:cl))~
36 | ~%~
37 | ~%(in-package #:~a)"
38 | package-name package-name)))
39 |
40 | (define-command insert-local-nicknames ()
41 | "Insert local nicknames."
42 | (declare (context (:child-of :package-definition))) ; TODO
43 | (insert
44 | "(:local-nicknames ~{~a~^~%~})"
45 | (loop :for name = (read-string "Name of the package to alias: ")
46 | :while (plusp (length name))
47 | :for alias = (read-string "Alias of the package: ")
48 | :collect (format nil "(#:~a #:~a)" alias name))))
49 |
50 | (define-command insert-in-package-cl-user ()
51 | "Insert ~(cl:in-package #:cl-user)~."
52 | (declare (context :top-level))
53 | (insert "(cl:in-package #:cl-user)"))
54 |
--------------------------------------------------------------------------------
/src/logging.lisp:
--------------------------------------------------------------------------------
1 | (defpackage #:breeze.logging
2 | (:documentation "Utilities for logging.")
3 | (:use #:cl)
4 | (:export
5 | #:log-level
6 | #:log-message
7 | #:log-critical
8 | #:log-error
9 | #:log-warning
10 | #:log-info
11 | #:log-debug))
12 |
13 | (in-package #:breeze.logging)
14 |
15 | (defparameter *log-level* :info
16 | "The current log level")
17 |
18 | (defun log-level () "Get the current log level." *log-level*)
19 |
20 | (defun (setf log-level) (new-value)
21 | (check-type new-value (member :critical :error :warning :info :debug))
22 | (setf *log-level* new-value))
23 |
24 | (defun log-stream ()
25 | "Get the current log output stream."
26 | *trace-output*)
27 |
28 | (defun compare-level (cmp level1 level2)
29 | "Compare two log levels."
30 | ;; It's implemented in a way to have a total order, without actually
31 | ;; assigning a value to each level.
32 | (funcall cmp
33 | (length (member level1 #1='(:critical :error :warning :info :debug)))
34 | (length (member level2 #1#))))
35 |
36 | (defun log-message (level control-string &rest args)
37 | (let ((current-level (log-level))
38 | (stream (log-stream)))
39 | (when (compare-level #'>= level current-level)
40 | ;; TODO maybe print the time too?
41 | (format stream "~&~a ~?~%" level control-string args))))
42 |
43 |
44 | (macrolet ((def (level)
45 | `(defun ,(alexandria:symbolicate 'log- level) (control-string &rest args)
46 | (apply #'log-message ,level control-string args))))
47 | (def :critical)
48 | (def :error)
49 | (def :warning)
50 | (def :info)
51 | (def :debug))
52 |
53 | ;; To manually test if the log level is respected.
54 | #++
55 | (progn
56 | (format (log-stream) "~%~%Current log level: ~s~%~%" (log-level))
57 | (log-debug "debug")
58 | (log-info "info")
59 | (log-warning "warn")
60 | (log-error "err")
61 | (log-critical "crit"))
62 |
--------------------------------------------------------------------------------
/docs/getting_started.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 306350c9-0fb5-478b-958b-b35cae726280
3 | :END:
4 | #+title: Getting Started
5 |
6 | * Clone
7 |
8 | Clone this repository, for example, in quicklisp's local-projects
9 | folder.
10 |
11 | #+begin_src shell
12 | git clone --recursive git@codeberg.org:fstamour/breeze.git ~/quicklisp/local-projects/breeze
13 | #+end_src
14 |
15 | * Getting started with Emacs
16 | :PROPERTIES:
17 | :ID: 3976965c-cb83-4901-9587-3897cc207682
18 | :END:
19 |
20 | ** Load in emacs
21 |
22 | #+begin_src emacs-lisp
23 | (add-to-list 'load-path "~/quicklisp/local-projects/breeze/emacs/")
24 | (require 'breeze)
25 | #+end_src
26 |
27 | ** Configure emacs to use breeze-minor-mode
28 |
29 | Add a hook to enable breeze's minor mode in =lisp-mode= automatically:
30 |
31 | The mode will add some bindings (most notably =C-.=, which is bound to
32 | the command =breeze-quickfix=).
33 |
34 | #+begin_src emacs-lisp
35 | (add-hook 'lisp-mode-hook #'breeze-minor-mode)
36 | #+end_src
37 |
38 | Additionally, breeze can be used as a "on-the-fly" linter for common
39 | lisp source files by enabling flymake and configuring flymake to use
40 | breeze.
41 |
42 | #+begin_src emacs-lisp
43 | ;; Ensure breeze is loaded when a sly or slime connection is opened
44 | (breeze-enable-connected-hook)
45 |
46 | ;; Enable breeze-minor-mode in lisp-mode
47 | (breeze-enable-minor-mode-hook)
48 |
49 | ;; Enable flymake-mode in breeze-minor-mode
50 | (breeze-minor-mode-enable-flymake-mode)
51 |
52 | ;; Configure eldoc to show both documentation and flymake's messages
53 | ;; See https://www.masteringemacs.org/article/seamlessly-merge-multiple-documentation-sources-eldoc for a fancier solution
54 | (setf eldoc-documentation-strategy 'eldoc-documentation-compose)
55 | #+end_src
56 |
57 | * See also
58 |
59 | - [[id:279c4ea6-2004-4a7a-a2c9-905f27fae42c][Contributing]]
60 | - [[id:bb5c6ad4-0f89-48aa-9295-13e5e248a897][Glossary]]
61 | - [[id:31236780-159e-4a58-9019-37f57f5b4997][FAQ from newbies about common lisp]]
62 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: ci
2 |
3 | # When to trigger this workflow
4 | on:
5 | push:
6 | branches: [main]
7 | pull_request:
8 |
9 | jobs:
10 | test:
11 | name: ${{ matrix.os }}
12 |
13 | strategy:
14 | fail-fast: false
15 | matrix:
16 | os:
17 | - macos-latest
18 | - ubuntu-latest
19 | - windows-latest
20 |
21 | # run the job on every combination of "os" above
22 | runs-on: ${{ matrix.os }}
23 |
24 | steps:
25 | - name: "Windows: Install sbcl"
26 | if: matrix.os == 'windows-latest'
27 | run: |
28 | echo "C:\\Program Files\\Steel Bank Common Lisp\\2.0.0\\" >> $env:GITHUB_PATH
29 | cat $env:GITHUB_PATH
30 | echo "SBCL_HOME=C:\\Program Files\\Steel Bank Common Lisp\\2.0.0\\" >> $env:GITHUB_ENV
31 | cat $env:GITHUB_ENV
32 | curl -L http://downloads.sourceforge.net/project/sbcl/sbcl/2.0.0/sbcl-2.0.0-x86-64-windows-binary.msi --output sbcl.msi
33 | msiexec /qn /i sbcl.msi
34 |
35 | - name: "Ubuntu: Install sbcl"
36 | if: matrix.os == 'ubuntu-latest'
37 | run: |
38 | sudo apt-get update
39 | sudo apt-get install -y sbcl
40 |
41 | - name: "MacOs: Install sbcl"
42 | if: matrix.os == 'macos-latest'
43 | run: |
44 | brew install sbcl
45 |
46 | - name: "Print sbcl version"
47 | run: sbcl --version
48 |
49 | - name: Checkout repository
50 | uses: actions/checkout@v3
51 |
52 | - name: Setup quicklisp
53 | run: |
54 | curl -kLO https://beta.quicklisp.org/quicklisp.lisp
55 | printenv | sort
56 | sbcl --noinform --non-interactive --load scripts/setup-quicklisp.lisp
57 |
58 | - name: Tests
59 | shell: bash
60 | run: scripts/test-sbcl.sh
61 |
62 | # - name: 'Upload Artifact'
63 | # uses: actions/upload-artifact@v3
64 | # with:
65 | # name: docs
66 | # path: docs/
67 |
--------------------------------------------------------------------------------
/src/package.lisp:
--------------------------------------------------------------------------------
1 |
2 | (defpackage #:breeze.package
3 | (:documentation "Package utilities")
4 | (:use #:cl #:breeze.analysis)
5 | (:import-from #:alexandria
6 | #:when-let
7 | #:when-let*)
8 | (:export #:in-package-node-p
9 | ;; re-export from breeze.analysis
10 | #:map-top-level-in-package))
11 |
12 | (in-package #:breeze.package)
13 |
14 | ;; TODO I want to check if a node is an "in-package" node...
15 | ;; - [ ] case converting if necessary
16 | ;; - [x] skip whitespaces
17 | ;; - [x] check if there's a package designator
18 | ;;
19 | ;; Now, I have a chicken-and-egg issue because of
20 | ;; package-local-nicknames... I need to know what is the current
21 | ;; package to look for PLNs to find the in-pacakge form, but I need
22 | ;; the in-package to know the current package.
23 |
24 | (define-node-matcher in-package-node-p ((in-package ?package-designator))
25 | (unless (quotedp node-iterator)
26 | (when ?package-designator
27 | (let ((package-designator-node (value ?package-designator)))
28 | (when (or (token-node-p package-designator-node)
29 | (string-node-p package-designator-node)
30 | (sharp-uninterned-node-p package-designator-node))
31 | ;; TODO else... it's a malformed in-package form
32 | ?package-designator)))))
33 |
34 | ;; TODO add tests
35 | (defmethod map-top-level-in-package (function (state state))
36 | "Map FUNCTION over all top-level (in-package ...) forms in STATE."
37 | (map-top-level-forms
38 | (lambda (node-iterator)
39 | (when-let ((package-name (in-package-node-p node-iterator)))
40 | (funcall function node-iterator package-name)))
41 | state))
42 |
43 | ;; TODO add tests
44 | (defmethod locate-package-definition ((package package) #| TODO haystack |#)
45 | (locate-package-definition (package-name package)))
46 |
47 | ;; TODO add tests
48 | (defmethod locate-package-definition ((package node-iterator) #| TODO haystack |#)
49 | (locate-package-definition (node-string-designator package)))
50 |
--------------------------------------------------------------------------------
/tests/package.lisp:
--------------------------------------------------------------------------------
1 |
2 | (defpackage #:breeze.test.package
3 | (:documentation "Tests for the package breeze.package")
4 | (:use #:cl #:breeze.package #:breeze.analysis)
5 | (:import-from #:parachute
6 | #:define-test
7 | #:define-test+run
8 | #:is
9 | #:true
10 | #:false
11 | #:of-type
12 | #:finish))
13 |
14 | (in-package #:breeze.test.package)
15 |
16 | #++ ;; Sanity-check
17 | (mapcar #'read-from-string
18 | '("in-package"
19 | "common-lisp:in-package"
20 | "cl:in-package"
21 | "cl-user::in-package"
22 | "common-lisp-user::in-package"))
23 |
24 | (defun test-in-package-node-p (string)
25 | ;; The funky reader macro and quasiquote is to fuck with slime and
26 | ;; sly's regex-based search for "(in-package". Without this the
27 | ;; rest of the file is evaluated in cl-user by slime and sly.
28 | (let ((package-designator-node
29 | #.`(,'in-package-node-p (make-node-iterator string))))
30 | (when package-designator-node
31 | (node-string package-designator-node))))
32 |
33 | ;; TODO (in-package #+x 'a #-x 'b)
34 | (define-test+run in-package-node-p
35 | (is equal "x" (test-in-package-node-p "(in-package x)"))
36 | (is equal nil (test-in-package-node-p "(in-package #)"))
37 | (is equal ":x" (test-in-package-node-p "(in-package :x)"))
38 | (is equal "#:x" (test-in-package-node-p "(in-package #:x)"))
39 | (is equal "\"x\"" (test-in-package-node-p "(in-package \"x\")"))
40 | (is equal "x" (test-in-package-node-p "( in-package x )"))
41 | (is equal "x" (test-in-package-node-p "( in-package #| ∿ |# x )"))
42 | (is equal "x" (test-in-package-node-p "(cl:in-package x)"))
43 | (is equal "x" (test-in-package-node-p "(cl::in-package x)"))
44 | (is equal "42" (test-in-package-node-p "(cl::in-package 42)"))
45 | ;; TODO ? Not sure it's worth it lol...
46 | ;; (is equal "x" (test-in-package-node-p "('|CL|::|IN-PACKAGE| x)"))
47 | (is eq nil (test-in-package-node-p "(cl:)"))
48 | (is eq nil (test-in-package-node-p "'(in-package x)")))
49 |
--------------------------------------------------------------------------------
/src/ensure-breeze.lisp:
--------------------------------------------------------------------------------
1 | #|
2 |
3 | This file is used to load breeze's system.
4 |
5 | It is used, for example, by emacs in breeze.el.
6 |
7 | TODO "Checkpoints"
8 | TODO Unload (e.g. delete-package) if it fails to load!
9 | TODO _maybe_ add a variable *breeze-loaded-correctly-p*
10 |
11 | |#
12 |
13 | (cl:in-package #:cl-user)
14 |
15 | (defpackage #:breeze.loader
16 | (:use #:cl))
17 |
18 | (in-package #:breeze.loader)
19 |
20 | (defun or-die (error callback)
21 | (let (success)
22 | (multiple-value-bind (result condition)
23 | (ignore-errors
24 | (prog1 (funcall callback)
25 | (setq success t)))
26 | (unless (and success (not condition))
27 | (error (format nil "~A: ~A" error condition)))
28 | result)))
29 |
30 | (or-die "Failed to load asdf."
31 | (lambda () (require 'asdf)))
32 |
33 | (defvar *asd* nil)
34 |
35 | (or-die "Failed to set the path to the system definition."
36 | (lambda ()
37 | (unless (and *asd* (probe-file *asd*))
38 | (setf *asd*
39 | (merge-pathnames "../breeze.asd" *load-truename*)))))
40 |
41 | (or-die "Failed to load the system definition"
42 | (lambda () (asdf:load-asd *asd*)))
43 |
44 | ;; TODO error handling
45 | ;;
46 | ;; TODO if quicklisp is not available, check if all dependencies are
47 | ;; available before trying to load the whole system
48 | ;;
49 | ;; TODO _maybe_ fallback to vendored dependency systems if they can't
50 | ;; be found
51 | ;;
52 | ;; TODO some dependencies and subsystems could be made optional, maybe
53 | ;; this script could take care of setting up some *features*?
54 |
55 |
56 | (if (asdf:component-loaded-p "breeze")
57 | "Already loaded"
58 | (or-die "Failed to load breeze's system."
59 | (lambda ()
60 | (prog1
61 | #+quicklisp
62 | (prog1 "Loaded using quicklisp"
63 | (ql:quickload "breeze"))
64 | #-quicklisp
65 | (prog1
66 | "Loaded using asdf:load-system"
67 | (asdf:load-system '#:breeze))
68 | (format t "~&Breeze loaded!~%")))))
69 |
--------------------------------------------------------------------------------
/docs/glossary.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: bb5c6ad4-0f89-48aa-9295-13e5e248a897
3 | :END:
4 | #+title: Glossary
5 | * Glossary
6 |
7 | #+begin_comment
8 | Trying to document the words/concepts used in the project.
9 | #+end_comment
10 |
11 | ** lisp listener
12 | :PROPERTIES:
13 | :ID: 93da5b9d-9593-45b1-9f71-f49d01c3e95d
14 | :END:
15 |
16 | - More often called "lisp repl".
17 | - I use this term to try to avoid confusion with an hypothetical
18 | future actual REPL.
19 | - You could describe that as a "client-server REPL".
20 |
21 | ** REPL
22 | :PROPERTIES:
23 | :ID: 824a7d5d-d11f-40b0-ae0e-b83ea7dbf812
24 | :END:
25 |
26 | - Stands for "Read-Eval-Print-Loop"
27 | - Most people think about "command line" when they hear REPL, but in
28 | the case of lisp, it usually means a "listener".
29 |
30 | ** e-graph
31 | :PROPERTIES:
32 | :ID: 09937194-8b79-43ff-855a-ec33797b19c7
33 | :END:
34 |
35 | "e-graph" stands for "equivalence graph", it is a data structure.
36 |
37 | See [[id:32155195-1bc4-4f2d-8f6a-12fb0bd68ecc][E-graphs]].
38 |
39 |
40 | ** Package-local nicknames (PLN)
41 | :PROPERTIES:
42 | :ID: 04bdfbdb-ff84-4d7f-a845-a456c886b8f1
43 | :ROAM_ALIASES: "Local nicknames"
44 | :END:
45 |
46 | Also known as local nicknames, these are nicknames given to packages,
47 | but that are scoped to a specific package.
48 |
49 | ** Buffer
50 | :PROPERTIES:
51 | :ID: aef7ae44-a4e2-4a5e-831b-ffc2ae6f085c
52 | :END:
53 |
54 | In breeze, a buffer is an object that represents a file opened in the
55 | editor.
56 |
57 | ** Point
58 | :PROPERTIES:
59 | :ID: b038c05f-dfba-4154-92ca-a3d467e62d3a
60 | :END:
61 |
62 | The "point" refers to the current position of the cursor in a buffer.
63 |
64 | Note: emacs' point starts at 1, breeze's point starts at 0.
65 |
66 | ** Binding
67 | :PROPERTIES:
68 | :ID: 5a2bc469-d927-401e-aa77-81b00a9dcd42
69 | :END:
70 |
71 | An association from a name to a value.
72 |
73 | ** Substitution
74 | :PROPERTIES:
75 | :ID: ea4e8881-2ca6-4c8d-b2dc-dcbd5fdad721
76 | :END:
77 |
78 | A substitution is a set of [[*Binding][bindings]].
79 |
80 | See [[id:fba498f1-2e4c-4e4d-aa96-0f7a9b4ff369][Substitutions]] for more details
81 |
--------------------------------------------------------------------------------
/docs/support_for_tests.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: a9a98f8e-b097-4e8c-a2d1-92d8b8a26707
3 | :END:
4 | #+title: Support for tests
5 |
6 | * PROtocol and TESTcase manager :test:3rd_parties:
7 |
8 | [[https://github.com/phoe/protest][phoe/protest]]
9 |
10 | PROTEST is a tool for defining protocols and test cases written in and
11 | for Common Lisp.
12 | * trying to find discrepancies between the packages and test packages :test:
13 |
14 | or betweew test system and the system under test
15 |
16 | I consider this task "DONE" because I did _try_ to find discrepancies
17 | between the package ~breeze.refactor~ and ~breeze.test.refactor~. I
18 | used the convention that each "command" defined in ~breeze.refator~
19 | should have a test with the same name (i.e. the same symbol-name). I
20 | have a test that fails if this "invariant" is not held.
21 |
22 | In the future, I would like to
23 |
24 | ** TODO Figure out how to generalize "finding missing tests by discrepancies" :test:ux:config:
25 |
26 | Not everyone is going to have the same conventions.
27 |
28 | ** TODO Improve the current test by looking for prefix instead
29 |
30 | E.g package ~a~ has an exported symbol ~s~, it's corresponding test
31 | package is ~a.test~.
32 |
33 | The current implementation would try to find a test named ~s~ in
34 | ~a.test~ (for example ~a.test::s~, or ~"s"~ (test names can be string
35 | in parachute), it would be nice to have it also considers tests that
36 | have the _prefix_ ~s~.
37 |
38 | Why? Because I have some automatically generated test (a bit like
39 | snapshot tests), it's very convenient that they have the same name as
40 | the thing they are testing. Using a prefix would let me have multiple
41 | kind of tests for each (automatically generated or not).
42 |
43 | Do I want to check if each "types" of tests are implemented?
44 |
45 | Can parachute's `deftest`'s be easily augmented with some metadata?
46 | That might help too.
47 |
48 | * TODO Integrate with multiple test framework :test:
49 |
50 | See @phoe's [[https://github.com/phoe/protest][phoe/protest]].
51 |
52 | * TODO It's too easy to kill the test-runner :tech_debt:ux:
53 |
--------------------------------------------------------------------------------
/scratch-files/definition.lisp:
--------------------------------------------------------------------------------
1 |
2 | (defpackage #:breeze.definition
3 | (:documentation "Provides replacements for \"definition forms\" (such as defun and defmacro).
4 | The goal is to (portably) make sure we keep the definitions and not just their [compiled] results.")
5 | (:use :cl)
6 | (:shadow cl:defun cl:fmakunbound)
7 | (:export
8 | #:*function*
9 | #:*function-redifinition-hooks*
10 | #:defun
11 | #:fmakunbound
12 | #:function-body))
13 |
14 | (in-package #:breeze.definition)
15 |
16 | (defvar *function* (make-hash-table)
17 | "Set of all functions defined with breeze.definition:defun")
18 |
19 | (defvar *function-redifinition-hooks* ()
20 | "List of functions to call when a function is redefined")
21 |
22 | (cl:defun flag-funtion-redifinition (name)
23 | "Calls each function in *funtion-redifinition-hooks*."
24 | (loop :for hook :in *function-redifinition-hooks*
25 | :do (funcall hook name)))
26 |
27 | (defmacro defun (&whole whole name lambda-list &body body)
28 | "Define a functions and saves its definition in memory, flag a function redefinition."
29 | `(progn (cl:defun ,name ,lambda-list
30 | ,@body)
31 | (setf (gethash ',name *function*) ',whole)
32 | (flag-funtion-redifinition ',name)
33 | ',name))
34 |
35 | (cl:defun fmakunbound (name)
36 | "Make NAME have no global function definition."
37 | (cl:fmakunbound name)
38 | (remhash name *function*))
39 |
40 | (cl:defun function-body (name)
41 | "Get the body of a function by name"
42 | (cdddr (gethash name *function*)))
43 |
44 | ;; TODO defmacro
45 | ;; TODO defgeneric
46 | ;; TODO defmethod
47 | ;; TODO defclass
48 | ;; TODO what about closures?
49 |
50 | #|
51 | (loop :for symbol :being :the :external-symbol :of :cl
52 | :when (alexandria:starts-with-subseq "DEF" (symbol-name symbol))
53 | :collect symbol)
54 |
55 | (DEFINE-SYMBOL-MACRO
56 | DEFCLASS
57 | DEFUN
58 | DEFSETF
59 | DEFMETHOD
60 | DEFCONSTANT
61 | DEFINE-METHOD-COMBINATION
62 | DEFSTRUCT
63 | DEFVAR
64 | DEFGENERIC
65 | DEFTYPE
66 | DEFMACRO
67 | DEFPARAMETER
68 | DEFPACKAGE
69 | DEFINE-COMPILER-MACRO
70 | DEFINE-MODIFY-MACRO
71 | DEFINE-CONDITION
72 | DEFINE-SETF-EXPANDER)
73 | |#
74 |
--------------------------------------------------------------------------------
/src/cmds/capture.lisp:
--------------------------------------------------------------------------------
1 | (defpackage #:breeze.capture
2 | (:documentation "Utilities for quick capture and management of code.")
3 | (:use #:cl)
4 | (:import-from #:breeze.string
5 | #:remove-indentation
6 | #:whitespacep)
7 | (:import-from #:breeze.command
8 | #:choose
9 | #:define-command
10 | #:message
11 | #:find-file)
12 | (:import-from #:breeze.config
13 | #:*capture-folder*
14 | #:*capture-template*)
15 | (:export #:capture))
16 |
17 | (in-package #:breeze.capture)
18 |
19 | (defun list-existing-captured-files ()
20 | (mapcar #'pathname-name
21 | (directory
22 | (merge-pathnames "*.lisp" *capture-folder*))))
23 |
24 | (defun populate (pathname)
25 | (with-open-file (output pathname
26 | :if-exists :error
27 | :if-does-not-exist :create
28 | :direction :output)
29 | (format output (remove-indentation *capture-template*))))
30 |
31 | (define-command capture ()
32 | "Quickly create a lisp file in a pre-determined directory."
33 | ;; TODO Make sure *capture-folder* is set
34 | ;; TODO Otherwise, ask the user to choose a directory _and save it_
35 | (unless *capture-folder*
36 | (error "*capture-folder* not set"))
37 | ;; TODO ensure that *capture-folder* is a valid directory pathname
38 | ;; TODO check if *capture-folder* exists
39 | (unless (probe-file *capture-folder*)
40 | (if (breeze.command:ask-y-or-n-p "The capture folder ~s does not exist, create it? " *capture-folder*)
41 | (ensure-directories-exist *capture-folder*)
42 | (error "The capture folder ~s does not exist" *capture-folder*)))
43 | ;; We use "chose" instead of "read-string" so that the user can
44 | ;; easily see if he's trying to create a file with a name that
45 | ;; already exists.
46 | (let* ((files (list-existing-captured-files))
47 | (name (concatenate 'string
48 | (choose "Name of the file and package: " files)
49 | ".lisp"))
50 | (pathname (merge-pathnames name *capture-folder*)))
51 | (unless (probe-file pathname)
52 | (populate pathname))
53 | (find-file pathname)))
54 |
--------------------------------------------------------------------------------
/scripts/org-publish-project.el:
--------------------------------------------------------------------------------
1 |
2 | (message "Installing packages (from ELPA)")
3 |
4 | (when package-enable-at-startup
5 | (package-initialize)
6 | (setf package-selected-packages
7 | '(htmlize))
8 | (package-install-selected-packages t))
9 |
10 |
11 | (message "Publishing...")
12 |
13 | (require 'org)
14 | (require 'org-id)
15 | (require 'htmlize)
16 |
17 | ;; See (describe-variable 'org-publish-project-alist)
18 | ;; See https://orgmode.org/manual/Publishing-options.html for more options
19 | (let* ((forcep t) ; "forcep" is for interactive sessions.
20 | (org-id-link-to-org-use-id t)
21 | (default-directory
22 | (expand-file-name
23 | (concat
24 | (file-name-directory (or load-file-name buffer-file-name))
25 | "..")))
26 | (root "docs")
27 | (project-alist
28 | `("breeze"
29 | :base-directory ,root
30 | :publishing-function org-html-publish-to-html
31 | :publishing-directory "./public"
32 |
33 | :author "Francis St-Amour"
34 | :creator "Francis St-Amour"
35 | :with-author nil
36 |
37 | :html-style nil
38 |
39 | :html-validation-link nil
40 |
41 | :html-link-up "" ; this is the default
42 | :html-link-home "index.html"
43 |
44 | ;; this is a format string, the first %s is the up "url",
45 | ;; and the second is the "home" url. Here, we assume the
46 | ;; first is empty
47 | :html-home/up-format ""
50 | :html-head ""
51 |
52 | :auto-sitemap t
53 | ;; https://emacs.stackexchange.com/questions/70824/how-to-use-makeindex
54 | :makeindex t ;; TODO need to add a bunch of #+index: term
55 | :with-toc nil
56 | )))
57 | (org-id-update-id-locations (directory-files root t "\\.org$"))
58 | (org-publish project-alist forcep)
59 | (dolist (file (directory-files "docs/" t "listing-.*\\.html$"))
60 | (copy-file file "public/" :ok-if-already-exists))
61 | (copy-file "docs/reference.html" "public/" :ok-if-already-exists)
62 | (copy-file "docs/style.css" "public/" :ok-if-already-exists))
63 |
--------------------------------------------------------------------------------
/src/channel.lisp:
--------------------------------------------------------------------------------
1 |
2 | (defpackage #:breeze.channel
3 | (:documentation "A simple channel implementation.")
4 | (:use #:cl)
5 | (:export #:make-channel
6 | #:channelp
7 | #:channel
8 | #:send
9 | #:receive
10 | #:emptyp))
11 |
12 | (in-package #:breeze.channel)
13 |
14 | (defclass channel ()
15 | ((queue
16 | :initform (cons nil nil)
17 | :documentation "The content of the channel's queue.")
18 | (lock
19 | :initform (bt2:make-lock)
20 | :documentation "Mutex lock")
21 | (datap
22 | :initform (bt2:make-semaphore)
23 | :documentation "Used to signal available data for the readers."))
24 | (:documentation "A minimal unbounded, queue-backed (FIFO) channel."))
25 |
26 | (defun make-channel ()
27 | "Make a new unbounded channel."
28 | (make-instance 'channel))
29 |
30 | (defun channelp (x)
31 | (typep x 'channel))
32 |
33 | (defmethod print-object ((channel channel) stream)
34 | (print-unreadable-object (channel stream :type t :identity t)
35 | (format stream "~a" (length (car (slot-value channel 'queue))))))
36 |
37 | (declaim (inline empty-queue-p))
38 | (defun empty-queue-p (q)
39 | (and (null (car q)) (null (cdr q))))
40 |
41 | (declaim (inline enqueue))
42 | (defun enqueue (q v)
43 | (let ((end (list v)))
44 | (if (empty-queue-p q)
45 | (setf (car q) end)
46 | (setf (cddr q) end))
47 | (setf (cdr q) end)))
48 |
49 | (declaim (inline dequeue))
50 | (defun dequeue (q)
51 | (prog1 (pop (car q))
52 | (unless (car q)
53 | (setf (cdr q) nil))))
54 |
55 |
56 |
57 | (defun send (c v)
58 | "Send the value V into the channel C, doesn't block (much)."
59 | (with-slots (queue lock datap) c
60 | (bt2:with-lock-held (lock #| TODO :timeout timeout |#)
61 | (enqueue queue v))
62 | (bt2:signal-semaphore datap)))
63 |
64 | (defun receive (c)
65 | "Receive a value from the channel C, blocks if there's not message
66 | available."
67 | (with-slots (queue lock datap) c
68 | (loop
69 | (bt2:wait-on-semaphore datap #| TODO :timeout timeout |#)
70 | (bt2:with-lock-held (lock)
71 | (unless (empty-queue-p queue)
72 | (return (dequeue queue)))))))
73 |
74 | (defun emptyp (c)
75 | (with-slots (queue lock datap) c
76 | (bt2:with-lock-held (lock)
77 | (empty-queue-p queue))))
78 |
--------------------------------------------------------------------------------
/src/pattern/rewrite.lisp:
--------------------------------------------------------------------------------
1 | (in-package #:breeze.pattern)
2 |
3 |
4 | ;;; Match substitution
5 |
6 | (defun pattern-substitute (pattern bindings &optional (result-type 'vector))
7 | (cond
8 | ((and pattern (null bindings)) pattern)
9 | ((null pattern) nil)
10 | ((and pattern bindings)
11 | (check-type pattern atom)
12 | (check-type bindings (or binding substitutions (eql t)))
13 | (flet ((substitute1 (x)
14 | (etypecase x
15 | ((or simple-var var)
16 | (alexandria:if-let ((binding (find-binding bindings (name x))))
17 | (to binding)
18 | ;; TODO this could signal a condition (binding not
19 | ;; found)
20 | x))
21 | ((or symbol number) x))))
22 | (if (vectorp pattern)
23 | (map result-type
24 | ;; Note: we could've recurse directly into
25 | ;; pattern-subtitute, but not doing so make tracing
26 | ;; (and debugging) tremenduously easier.
27 | #'(lambda (subpattern)
28 | (if (vectorp subpattern)
29 | (pattern-substitute subpattern bindings result-type)
30 | (substitute1 subpattern)))
31 | pattern)
32 | (substitute1 pattern))))))
33 |
34 |
35 |
36 | ;;; Rules and rewrites
37 |
38 |
39 | ;; TODO "rules" would be "bidirectional" and "rewrites" wouldn't.
40 | ;; TODO (defun rule (a b) ...)
41 | ;; TODO (defun make-rewrite (antecedent consequent) ...)
42 |
43 | #++ (progn
44 | (defclass abstract-rule () ())
45 |
46 | (defclass rule (abstract-rule) ())
47 |
48 | (defun make-rule (a b)
49 | (list :rule
50 | (compile-pattern a)
51 | (compile-pattern b)))
52 |
53 | (defun make-rewrite (a b)
54 | (list :rewrite
55 | (compile-pattern a)
56 | (compile-pattern b))))
57 |
58 | (defun make-rewrite (pattern template)
59 | (cons ;; TODO use a class instead
60 | (compile-pattern pattern)
61 | (compile-pattern template)))
62 |
63 | (defun rewrite-pattern (rewrite)
64 | "Get the pattern of a REWRITE rule."
65 | (car rewrite))
66 |
67 | (defun rewrite-template (rewrite)
68 | "Get the template of a REWRITE rule."
69 | (cdr rewrite))
70 |
--------------------------------------------------------------------------------
/docs/examples_of_code_that_is_not_handled_correctly_by_slime_and_sly.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 6055422c-7954-4c12-8f0b-32cd757fb2ac
3 | :END:
4 | #+title: Examples of code that is not handled correctly by slime and sly
5 |
6 | * Defuns with bad indentation
7 | :PROPERTIES:
8 | :ID: 82bb6f4b-bd23-46b9-8243-d3dbf5c9ad81
9 | :END:
10 |
11 | With slime, there is no place in the next example where the command
12 | ~slime-compile-defun~ is able to compile the function ~g~, it will
13 | always either compile ~f~ or ~h~.
14 |
15 | #+begin_src lisp
16 | (defun f ())
17 |
18 | (defun g ())
19 |
20 | (defun h())
21 | #+end_src
22 |
23 | * Symbols with escapes
24 | :PROPERTIES:
25 | :ID: ef867d3a-897f-43e6-b406-244f448e479a
26 | :END:
27 |
28 | This is equivalent to ~'cl:in-package~.
29 |
30 | #+begin_src lisp
31 | '|CL|::|IN-PACKAGE|
32 | #+end_src
33 |
34 | - ~slime-eval-defun~ either evaluates this to ~`cl~ or (correctly) to
35 | ~'in-package~ depending on whether the point is on the =|CL|= or
36 | =|IN-PACKAGE|=.
37 | - ~slime-eval-last-expression~ signals a condition of type ~unbound-variable~
38 |
39 | #+begin_example
40 | The variable IN-PACKAGE is unbound.
41 | [Condition of type UNBOUND-VARIABLE]
42 | #+end_example
43 |
44 | * False positive matching =in-package=
45 | :PROPERTIES:
46 | :ID: 5ceaab80-381e-4e68-b0db-d97020412d8f
47 | :END:
48 |
49 | By default the current package is found using
50 | ~slime-search-buffer-package~, which uses a regex to find an
51 | "in-package" form in the current buffer.
52 |
53 | This first exampe is a bit contrived, but it's enough to show the
54 | issue: there is a string that contains an "in-package" form and the
55 | regex picks it up regardless of it being inside a string.
56 |
57 | #+begin_src lisp
58 | (defpackage #:foo
59 | (:use #:cl))
60 |
61 | (in-package #:foo)
62 |
63 | (defun in-package-bar-p (x)
64 | (equal "
65 | (in-package bar)" x))
66 |
67 | ,*package*
68 | ;; => #
69 | #+end_src
70 |
71 | The second example shows something more problemaic: the form
72 | ~(in-package-bar-p x)~ is being picked up by slime's regexp. (I think
73 | it should be possible to fix the regex though!)
74 |
75 | #+begin_src lisp
76 | (in-package #:foo)
77 |
78 | ,*package*
79 | ;; => #
80 |
81 | (defun foo (x)
82 | (in-package-bar-p x))
83 |
84 | ,*package*
85 | ;; => #
86 | #+end_src
87 |
--------------------------------------------------------------------------------
/src/thread.lisp:
--------------------------------------------------------------------------------
1 | (uiop:define-package #:breeze.thread
2 | (:documentation "Utilities to help with concurrent programming.")
3 | (:use #:cl)
4 | (:import-from #:breeze.xref
5 | #:function-designator-p)
6 | (:export
7 | #:find-threads
8 | #:find-threads-by-name
9 | #:find-worker-threads
10 | #:kill-threads
11 | #:kill-threads-by-name
12 | #:kill-worker-threads
13 | #:breeze-kill-worker-threads))
14 |
15 | (in-package #:breeze.thread)
16 |
17 |
18 | ;;; Thread management
19 |
20 | (defun find-threads (&optional predicate (exclude-self-p t))
21 | (let ((current-thread (when exclude-self-p (bt:current-thread))))
22 | (remove-if-not #'(lambda (thread)
23 | (and (not (eq current-thread thread))
24 | (if predicate
25 | (funcall predicate thread)
26 | t)))
27 | (bt:all-threads))))
28 |
29 | (defun find-threads-by-prefix (prefix &key (exclude-self-p t))
30 | (find-threads #'(lambda (thread)
31 | (alexandria:starts-with-subseq prefix (bt:thread-name thread)))
32 | exclude-self-p))
33 |
34 | (defun find-threads-by-name (name &key (exclude-self-p t))
35 | (find-threads #'(lambda (thread)
36 | (string= (bt:thread-name thread) name))
37 | exclude-self-p))
38 |
39 | (defun find-worker-threads (&optional (exclude-self-p t))
40 | (find-threads-by-name "worker" :exclude-self-p exclude-self-p))
41 |
42 | (defun %kill-threads (threads)
43 | (prog1
44 | ;; Always return the number of threads
45 | (length threads)
46 | (when threads
47 | (mapcar #'bordeaux-threads:destroy-thread threads)
48 | (format *debug-io* "~&Killed ~d threads.~%" (length threads)))))
49 |
50 | (defun kill-threads (predicate)
51 | "Find threads by predicate, and destroy them."
52 | (%kill-threads (find-threads predicate)))
53 |
54 | (defun kill-threads-by-name (name)
55 | "Find threads by name, then destroy them."
56 | (%kill-threads (find-threads-by-name name)))
57 |
58 | ;; TODO make a command with this...
59 | (defun kill-worker-threads ()
60 | "Find threads named \"worker\", then destroy them."
61 | (%kill-threads (find-threads-by-name "worker")))
62 |
63 | (breeze.command:define-command breeze-kill-worker-threads ()
64 | "Find threads named \"worker\", then destroy them."
65 | (kill-worker-threads))
66 |
--------------------------------------------------------------------------------
/src/xref.lisp:
--------------------------------------------------------------------------------
1 |
2 | (uiop:define-package #:breeze.xref
3 | (:documentation "Cross-reference and introspection")
4 | (:mix :cl #:breeze.utils #:alexandria)
5 | (:export
6 | #:calls-who
7 | ;; Utilities
8 | #:find-packages-by-prefix
9 | ;; Symbol inspection
10 | #:generic-method-p
11 | #:specialp
12 | #:macrop
13 | #:simple-function-p
14 | #:classp
15 | #:externalp
16 | #:function-designator-p))
17 |
18 | (in-package #:breeze.xref)
19 |
20 | (defun find-packages-by-prefix (prefix)
21 | "Find all packages whose name starts with the given prefix (case insensitive by default)."
22 | (loop
23 | :with prefix = (string-downcase prefix)
24 | :for package :in (list-all-packages)
25 | :when (starts-with-subseq prefix
26 | (string-downcase
27 | (package-name package)))
28 | :collect package))
29 |
30 | (defun generic-method-p (symbol)
31 | "Returns T if SYMBOL designates a generic method"
32 | (and (fboundp symbol)
33 | (subtypep
34 | (type-of (fdefinition symbol))
35 | 'standard-generic-function)))
36 |
37 | (defun specialp (symbol)
38 | "Return true if SYMBOL is a special variable."
39 | (and (symbolp symbol)
40 | (or (boundp symbol)
41 | (eval `(let (,symbol)
42 | (declare (ignorable ,symbol))
43 | (boundp ',symbol))))))
44 |
45 | (defun macrop (symbol)
46 | "Return true if SYMBOL designates a macro."
47 | (and (symbolp symbol)
48 | (macro-function symbol)))
49 |
50 | (defun simple-function-p (symbol)
51 | "Return true if SYMBOL is a function that is nor a macro nor a generic function."
52 | (and (fboundp symbol)
53 | (not (generic-method-p symbol))
54 | (not (macrop symbol))))
55 |
56 | (defun classp (symbol)
57 | "Return true if SYMBOL designate a class."
58 | (find-class symbol nil))
59 |
60 | (defun externalp (symbol)
61 | (and (symbol-package symbol)
62 | (eq :external
63 | (nth-value 1 (find-symbol (symbol-name symbol)
64 | (symbol-package symbol))))))
65 |
66 | (defun function-designator-p (designator)
67 | (or (functionp designator)
68 | (and (symbolp designator)
69 | (fboundp designator)
70 | (not (macrop designator)))))
71 |
72 | ;; TODO
73 | ;; (function-designator-p #'first)
74 | ;; (function-designator-p 'first)
75 | ;; (function-designator-p '(a b c))
76 | ;; (function-designator-p 'defmacro)
77 | ;; (function-designator-p nil)
78 | ;; TODO (setf x)
79 |
--------------------------------------------------------------------------------
/src/cmds/editing.lisp:
--------------------------------------------------------------------------------
1 | (defpackage #:breeze.editing
2 | (:documentation "Text and structural editing")
3 | (:use #:cl
4 | #:breeze.parser
5 | #:breeze.command
6 | #:breeze.buffer)
7 | (:export #:kill-sexp))
8 |
9 | (in-package #:breeze.editing)
10 |
11 | ;; (trace replace-region)
12 |
13 | #|
14 | ;; TODO add NOTE: "can't splice comment", but I wish I could
15 | ;; e.g. ` ;; (some | code)`
16 | ;; paredit-splice-sexp or paredit-splice-sexp-killing-backward
17 |
18 |
19 | (|asdf)qwer
20 | M- paredit-splice-sexp-killing-backward
21 | asfdqwer
22 | -- should be
23 | asdf qwer
24 |
25 | M-( paredit-wrap-round
26 |
27 | I just had to wrap a bunch of forms with (multiple-value-list ...) It
28 | would have been nice to have something to help with this... perhaps
29 | using the region/rectangle?
30 |
31 | TODO next-alternative/previous alternative (e.g. :accessor <-> :reader <-> :writer, in the right context)
32 |
33 | |#
34 |
35 | (define-command kill-sexp ()
36 | "Kill the expression following point."
37 | (let* ((buffer (current-buffer))
38 | (point (point buffer))
39 | (node-iterator (copy-iterator buffer)))
40 | #++ (break "point: ~D, start: ~D end: ~D ~s" point
41 | (start node-iterator)
42 | (end node-iterator) (node-string node-iterator))
43 | (flet ((maybe-include-next-node (it)
44 | (if (= point (1- (end it)))
45 | (1- (end it))
46 | (end
47 | (let ((next-node (next-sibling it)))
48 | ;; (break "next-node: ~s" next-node)
49 | (if (and next-node (whitespace-node-p next-node))
50 | next-node
51 | it))))))
52 | (cond
53 | ((whitespace-node-p (value node-iterator))
54 | (let* ((lastp (lastp node-iterator))
55 | (start (if lastp (start node-iterator) point)))
56 | (unless lastp
57 | (next node-iterator))
58 | (replace-region start (maybe-include-next-node node-iterator) "")))
59 | ((= point (end node-iterator))
60 | (message "END"))
61 | (t
62 | (replace-region point (maybe-include-next-node node-iterator) ""))))))
63 |
64 | #|
65 | ;; TODO
66 | (define-command forward-slurp)
67 |
68 | ;; TODO
69 | (define-command backward-slurp)
70 |
71 | ;; TODO
72 | (define-command forward-barf)
73 |
74 | ;; TODO
75 | (define-command backward-barf)
76 | |#
77 |
78 |
79 | #++ ;; TODO
80 | (define-command beginning-of-defun ()
81 | "Move backward to the beginning of a defun.")
82 |
--------------------------------------------------------------------------------
/tests/iterator.lisp:
--------------------------------------------------------------------------------
1 | ;; TODO move under tests/pattern
2 |
3 | (defpackage #:breeze.test.iterator
4 | (:documentation "Tests for the package breeze.iterator")
5 | (:use #:cl #:breeze.iterator)
6 | (:import-from #:parachute
7 | #:define-test
8 | #:define-test+run
9 | #:is
10 | #:isnt
11 | #:true
12 | #:false
13 | #:of-type
14 | #:fail))
15 |
16 | (in-package #:breeze.test.iterator)
17 |
18 | (define-test+run vector-iterator
19 | (let* ((vector #(1 2 3))
20 | (iterator (make-vector-iterator vector)))
21 | (is eq vector (slot-value iterator 'vector))
22 | (is = 0 (current-position iterator))
23 | (progn
24 | (true (firstp iterator))
25 | (false (before-last-p iterator))
26 | (false (lastp iterator)))
27 | (progn
28 | (next iterator)
29 | (false (firstp iterator))
30 | (true (before-last-p iterator))
31 | (false (lastp iterator)))
32 | (progn
33 | (next iterator)
34 | (false (firstp iterator))
35 | (false (before-last-p iterator))
36 | (true (lastp iterator))))
37 | (let* ((vector #(1 2 3))
38 | (iterator (make-vector-iterator vector :position 2)))
39 | (is eq vector (slot-value iterator 'vector))
40 | (is = 2 (current-position iterator)))
41 | (is equal (list 2 3)
42 | (collect (make-vector-iterator #(1 2 3) :position 1)))
43 | (false
44 | (collect (make-vector-iterator #(1 2 3) :position 10))))
45 |
46 | (define-test+run tree-iterator
47 | (let* ((root #(1 2 3))
48 | (iterator (make-tree-iterator root)))
49 | (is eq root (subtree iterator))
50 | (is = 0 (pos iterator))
51 | (is equal '(1 2 3) (collect iterator)))
52 | (let* ((iterator (make-tree-iterator #())))
53 | (true (donep iterator)))
54 | ;; Testing push-subtree and pop-subtree
55 | (is equal '(1 2 3 4)
56 | (let ((iterator (make-tree-iterator #(1 #(2 3) 4))))
57 | (list (prog1 (value iterator) (next iterator))
58 | (progn (push-subtree iterator (value iterator))
59 | (value iterator))
60 | (progn (next iterator)
61 | (value iterator))
62 | (progn (pop-subtree iterator) (next iterator)
63 | (value iterator))))))
64 |
65 | #++
66 | (collect
67 | (make-concat-iterator
68 | (vector
69 | (make-vector-iterator #(1 2 3))
70 | (make-vector-iterator #(a b c))))
71 | :limit 10)
72 |
73 | #++
74 | (let ((it (make-tree-iterator
75 | #(a b #(c) #(d #(e))))))
76 | (collect it :limit 2))
77 |
--------------------------------------------------------------------------------
/docs/faq_from_newbies_about_common_lisp.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 31236780-159e-4a58-9019-37f57f5b4997
3 | :END:
4 | #+title: FAQ from newbies about common lisp
5 |
6 | This is useful for breeze's development, to figure out the pain
7 | points. Ideally, this should go somewhere else, like in tutorial, a
8 | cookbook or some kind of reference (like [[https://github.com/fstamour/lisp-docs.github.io][lisp-docs.github.io]]).
9 |
10 | See also GitHub issue: https://github.com/fstamour/breeze/issues/35
11 |
12 | * FAQ from newbies about common lisp
13 |
14 | ** What's the difference between load and require?
15 |
16 | ** What's asdf v. quicklisp v. packages v. "os packages"?
17 |
18 | ** The heck is RPLACA?
19 |
20 | ** What's the difference between =setf= and =setq=?
21 |
22 | https://stackoverflow.com/questions/869529/difference-between-set-setq-and-setf-in-common-lisp
23 |
24 | ** Why use #:symbol (especially in =defpackage=)?
25 |
26 | ** Why start a file with =(cl:in-package #:cl-user)=?
27 |
28 | ** Why interactivity is important?
29 |
30 | They don't actually ask that, they usually just don't think or know
31 | about it.
32 |
33 | Here's something that does an OK job at explaining the importance:
34 | https://technotales.wordpress.com/2007/10/03/like-slime-for-vim/
35 |
36 | ** What's the difference between ~defvar~ and ~defparameter~?
37 |
38 | ** Something about using ~setf~ to create variables...
39 |
40 | ** A symbol can represent many things
41 |
42 | - variables/symbol macros
43 | - functions/macros
44 | - classes/conditions/types
45 | - method combinations
46 | - block names
47 | - catch tags
48 | - tagbody tags
49 | - restarts
50 | - packages
51 | - compiler macros
52 | - slot names
53 | - compiler macros
54 |
55 | ** When coming from another language
56 |
57 | *** How to create a function-local variable?
58 |
59 | ** Proclaim v.s. Declaim v.s. Declare
60 |
61 | http://www.lispworks.com/documentation/lw50/LWUG/html/lwuser-90.htm
62 |
63 | ** How packages and symbols works?
64 |
65 | https://flownet.com/ron/packages.pdf
66 |
67 | ** Alternatives to the Hyperspec
68 |
69 | - [[http://clqr.boundp.org/download.html][Common Lisp Quick Reference]]
70 | - Ultraspec (dead)
71 | - Simplified something something
72 | - The lisp cookbook
73 |
74 | ** What the hell are pathnames?
75 |
76 | - Don't forget trailing backslashes for directories.
77 |
78 | ** Where are the functions to operate on strings?
79 |
80 | - Use the functions that operate on sequences.
81 | - Use libraries, like alexandria, split-sequences, serapeum, etc.
82 |
83 | * Related notes
84 |
85 | - [[id:b139c21c-3a35-4b69-acd5-00b9d71090ce][Helping with setting up slime for remote sessions]]
86 |
--------------------------------------------------------------------------------
/scratch-files/refactor-scratch.lisp:
--------------------------------------------------------------------------------
1 | ;;; Trying to refactor stuff
2 |
3 | (cl:defpackage #:refactor-scratch
4 | (:use :cl #:breeze.parser #:breeze.workspace #:breeze.analysis))
5 |
6 | (cl:in-package #:refactor-scratch)
7 |
8 | ;; Trying to update a (parachute) assertion
9 | (let* ((*workspace* (make-workspace))
10 | (buffer (make-buffer :string "(is = (* 2 3) x)"
11 | :point 1))
12 | ($node (node-iterator buffer)))
13 | (progn ;; list
14 | ;; (node-string $node)
15 | ;; T
16 | ;; (match (compile-pattern `(:symbol "IS")) $node)
17 | #++ (node-string
18 | (to
19 | (find-binding (match (compile-pattern `((:symbol "IS") ?x)) $node) '?x)))
20 | (matching ($node ((:symbol "IS" #++ "PARACHUTE")
21 | ?cmp ?expected (:var ?got (:maybe :_))))
22 | #++ (loop :for (from . to) :in (breeze.test.pattern::bindings-alist bindings)
23 | :collect (cons from (node-string to)))
24 | (list ?cmp ?expected ?got)
25 | (mapcar 'node-string (list ?cmp ?expected ?got))
26 | bindings)))
27 |
28 | (let* ((*workspace* breeze.test.workspace:*breeze-workspace*)
29 | (buffer (find-buffer "tests/documentation.lisp"))
30 | (needle ))
31 | (search "(is string=" (source buffer)))
32 |
33 | (defun test-refactor-if (string)
34 | (let ((node (first (parse-string string))))
35 | (when (and (if-p node)
36 | (= 3 (node-length node)))
37 | (setf (node-content (first (node-content node)))
38 | (if (null-node-p (node-lastcar node))
39 | "unless"
40 | "when"))
41 | (unparse-to-string (list node)))))
42 |
43 | (test-refactor-if "(if x nil)")
44 | "(unless x nil)"
45 |
46 | (test-refactor-if "(if x 32)")
47 | "(when x 32)"
48 |
49 | (test-refactor-if
50 | "(if #| comment |# 'x NIL)")
51 | "(unless #| comment |# 'x nil)"
52 |
53 | (test-refactor-if
54 | "(IF
55 | 'x NIL)")
56 | "(unless
57 | 'x nil)" ;; FIXME it should be "NIL"
58 |
59 |
60 |
61 | ;; Forms to add to defsystem to test with parachute
62 | (let ((system-name "breeze"))
63 | (let ((test-system (format nil "~a/test" system-name))
64 | (test-package (format nil "~a/test" system-name))
65 | (test-function system-name))
66 | (format nil
67 | "~{~A~}"
68 | (list
69 | ":in-order-to ((test-op (load-op #:" test-system")))
70 | :perform
71 | (test-op (o c)
72 | (symbol-call
73 | '#:parachute '#:test
74 | (find-symbol (symbol-name '#:" test-function ")
75 | (find-package '#:" test-package "))
76 | :report (find-symbol \"INTERACTIVE\" \"PARACHUTE\")))"))))
77 |
--------------------------------------------------------------------------------
/docs/introduction.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: d08ab932-1204-4e7c-9869-40fc53500071
3 | :END:
4 | #+title: Introduction
5 |
6 | * What is this project?
7 | :PROPERTIES:
8 | :ID: 9b35d7aa-cb8a-433f-b0cb-ba1305740b21
9 | :END:
10 |
11 | This is a git repository that contains lots of common lisp code that I
12 | use to make developing with common lisp easier. It is a personal
13 | project that I work on from time to time, but that I use (and break)
14 | pretty much all the time.
15 |
16 | * Features
17 |
18 | - Emacs integration
19 | - Integration with quickproject
20 | - Context-aware, configurable snippets
21 | - Command for quick code capture (trying out code in a new file)
22 | - Implemented in common lisp to be able to port it to other editors in
23 | the future
24 |
25 | Currently, breeze's main interface is emacs; =breeze.el= adds a few
26 | commands and one minor-mode.
27 |
28 | #+begin_comment
29 | The one binding calls a command named ~breeze-quickfix~ (might rename
30 | in the future). This command suggests applicable actions given the
31 | current context (file name, file content, position in the file,
32 | etc.). For example, if the file ends with ".asd" it will suggest a
33 | command to insert a ~defsystem~ form. If breeze was already
34 | configured, it will pre-fill the ~:maintainer~, ~:author~ and
35 | ~:licence~ fields. Another example is that if the file is empty, or
36 | contains only comments, it will suggest to insert a ~defpackage~ or
37 | ~uiop:define-package~ form. It is also able to detect when you're
38 | trying to edit/evaluate forms that are in a package that doesn't
39 | exists (=did you forget to evaluate the ~defpackage~ form?=).
40 |
41 | The integration with quickproject is pretty simple and lets you
42 | quickly create new projects from the comfort of your editor. The
43 | integration consists of one command that asks you for some
44 | information, like the project name and licence. It takes some default
45 | values from breeze's configuration, but lets you change them. All
46 | this to ease the use of quickproject.
47 |
48 | Another simple command that helps me is ~breeze-capture~, it creates a
49 | new file in a pre-determined (must be configured) folder and fills it
50 | with some pre-configured content (template) and lets you code right
51 | away. This could've easily be done in emacs (that's how I prototyped
52 | the first version), but doing this in common lisp makes it easy to
53 | port it to other editors (or just the repl) in the future.
54 |
55 | I must stress that this whole project is in constant flux, and until I
56 | add more and more tests, stuff might break any time.
57 | #+end_comment
58 |
59 | * Next
60 |
61 | [[id:306350c9-0fb5-478b-958b-b35cae726280][Getting Started]]
62 |
--------------------------------------------------------------------------------
/src/cmds/completion.lisp:
--------------------------------------------------------------------------------
1 | #|
2 |
3 | contextual completions:
4 |
5 | (with-slot (B) A)
6 | - A should probably comes from nearby
7 | - A should be a "clos object", or a form that evaluates to one
8 | - B should be a one of A's slots
9 |
10 | (in-package #:A)
11 | - A must be the name or nickname of a package
12 | - silly question: can A be a local package nickname???
13 |
14 | (defpackage name
15 | (A )
16 | (:use B)
17 | (:import-from C D)
18 | (:export E)
19 | (:nicknames F)
20 | (:shadow G)
21 | (:shadowing-import H I)
22 | (:intern J)
23 | (:documentation K)
24 | (:size L))
25 | - A must be one of defpackage's options
26 | - B must not be already in another :use option
27 | - that would be redundant
28 | - C should probably not be already in another :import-from options
29 | - though it's totally legal, and could be done on purpose
30 | - B, C, H must be package designators
31 | - F must be a "new" package designator
32 | - E, J and G must be symbol designators
33 | - K must be a string
34 | - L must be a positive integer
35 | - D must be a symbol designator for a symbol from the package C
36 | - _technically_, D doesn't need to be exported, but that not what
37 | the majority of users would expect
38 | - I must be a symbol designator
39 | - IDK what else could be said about this one...
40 |
41 |
42 | (declaim A)
43 | (defun B (...)
44 | (declare C))
45 | - A is very probably a declaration about B
46 | - e.g. inline, ftype, optimization
47 | - C is probably a declaration about B's arguments
48 |
49 | fiind-sym
50 | => should suggest find-symbol
51 |
52 | |#
53 |
54 | (defpackage #:breeze.completion
55 | (:documentation "Commands to complete text")
56 | (:use #:cl)
57 | (:import-from #:breeze.command
58 | #:define-command
59 | #:current-buffer
60 | #:return-value-from-command)
61 | (:import-from #:breeze.parser
62 | #:node-iterator)
63 | (:export #:completions-at-point))
64 |
65 | (in-package #:breeze.completion)
66 |
67 | ;; TODO this shouldn't show up in the list of commands suggested by
68 | ;; "quickfix", and it shouldn't generate an interactive command when
69 | (define-command completions-at-point (&optional string)
70 | "completion-at-point"
71 | (declare (ignorable string))
72 | (let* (($node (node-iterator (current-buffer)))
73 | (node (breeze.iterator:value $node)))
74 | (declare (ignorable node))
75 | ;; (break "~s" (breeze.parser:node-string $node))
76 | (return-value-from-command
77 | (list "prin1" "print")
78 | #++
79 | (when (breeze.parser:token-node-p node)
80 |
81 | '("asfd" "qwer" "uiop")))))
82 |
83 | ;; find-most-similar-symbol
84 |
85 | #|
86 | print
87 |
88 | (breeze.listener::find-most-similar-symbol "make-node-iterator")
89 | |#
90 |
--------------------------------------------------------------------------------
/src/cmds/project.lisp:
--------------------------------------------------------------------------------
1 | (defpackage #:breeze.project
2 | (:documentation "Project scaffolding utilities")
3 | (:use #:cl)
4 | (:import-from #:breeze.utils
5 | #:length>1?)
6 | (:import-from #:breeze.command
7 | #:read-string
8 | #:ask-y-or-n-p
9 | #:return-from-command
10 | #:choose)
11 | (:export #:project
12 | #:confirm-scaffold-directory
13 | #:choose-local-project-directories))
14 |
15 | (in-package #:breeze.project)
16 |
17 | (defclass project ()
18 | ((name
19 | :initform nil
20 | :initarg :name
21 | :accessor name
22 | :documentation "Name of the project,")
23 | (root-dir
24 | :initform nil
25 | :initarg :root-dir
26 | :accessor root-dir
27 | :documentation "The path of the root directory of the project.")))
28 |
29 | ;; &key depends-on author include-copyright license
30 | ;; name template-directory template-parameters
31 | ;; &allow-other-keys
32 |
33 | (defun ql-local-project-directories ()
34 | "Get the list of quicklisp local-projects directories (as strings)."
35 | #+quicklisp
36 | (mapcar #'namestring
37 | ql:*local-project-directories*)
38 | ;; To appease sbcl's type checking xD
39 | #-quicklisp (list))
40 |
41 | #|
42 |
43 | ; in: DEFUN CHOOSE-LOCAL-PROJECT-DIRECTORIES
44 | ; (BREEZE.COMMAND:CHOOSE "Please choose where to create the project: "
45 | ; BREEZE.PROJECT::DIRECTORIES)
46 | ;
47 | ; note: deleting unreachable code
48 |
49 | ; (FIRST BREEZE.PROJECT::DIRECTORIES)
50 | ;
51 | ; note: deleting unreachable code
52 |
53 | |#
54 |
55 | (defun choose-local-project-directories ()
56 | (let ((directories (ql-local-project-directories)))
57 | (cond
58 | ((null directories)
59 | (read-string
60 | "Please enter the directory where to create the project: "))
61 | ((length>1? directories)
62 | (choose "Please choose where to create the project: "
63 | directories))
64 | (t
65 | (first directories)))))
66 |
67 | (defun confirm-scaffold-directory (directory)
68 | "If a user choose to scaffold a project into a directory that already
69 | exists, we need their confirmation to continue."
70 | (when (probe-file directory)
71 | (unless (ask-y-or-n-p
72 | "The directory \"~A\" already exists. Scaffolding might result in data loss, are you sure you want to continue? (y/n) "
73 | directory)
74 | (return-from-command)))
75 | directory)
76 |
77 |
78 |
79 | #|
80 | "system.asd"
81 | "readme.org"
82 | "package.lisp"
83 | ("test.lisp"
84 | "(defpackage #:.test
85 | (:documentation \"Tests for the package \")
86 | (:use #:cl))
87 |
88 | (in-package #:loom.test)")
89 | "application.lisp"
90 | "package.lisp"
91 |
92 | |#
93 |
--------------------------------------------------------------------------------
/tests/dummy-package.lisp:
--------------------------------------------------------------------------------
1 | (in-package #:common-lisp-user)
2 |
3 | (uiop:define-package #:breeze.dummy.test
4 | (:mix #| :breeze.definition |# :cl)
5 | (:nicknames :dum)
6 | (:export
7 | ;; Documented symbols
8 | #:*bound-variable*
9 | #:*unbound-variable*
10 | #:a-class
11 | #:a-function
12 | #:a-generic-function
13 | #:a-macro
14 | #:slot
15 | #:an-integer
16 |
17 | ;; Undocumented symbols
18 | #:*bound-variable-undocumented*
19 | #:*unbound-variable-undocumented*
20 | #:class-undocumented
21 | #:function-undocumented
22 | #:generic-function-undocumented
23 | #:macro-undocumented
24 | #:slot-undocumented
25 | #:integer-undocumented
26 |
27 | #:another-generic-function
28 |
29 | ;;
30 | #:mul
31 | #:2x
32 | #:add-one))
33 |
34 | (in-package #:breeze.dummy.test)
35 |
36 |
37 | ;;; Documented symbols
38 |
39 | (defvar *unbound-variable* ()
40 | "A documented unbound symbol.")
41 |
42 | (defvar *bound-variable* t
43 | "A documented bound symbol.")
44 |
45 | (defun a-function ()
46 | "A documented function."
47 | t)
48 |
49 | (defgeneric a-generic-function ()
50 | (:documentation "A documented generic function."))
51 |
52 | (defmethod a-generic-function ()
53 | "A method."
54 | t)
55 |
56 | (defmacro a-macro ()
57 | "A documented macro."
58 | t)
59 |
60 | (defclass a-class ()
61 | ((slot
62 | :accessor slot
63 | :documentation "A documented slot."))
64 | (:documentation "A documented class."))
65 |
66 | (deftype an-integer () "A documented type-specifier." '(integer 0 100))
67 |
68 |
69 | ;;; Undocumented symbols
70 |
71 | (defvar *unbound-variable-undocumented* ())
72 | (defvar *bound-variable-undocumented* t)
73 | (defun function-undocumented ())
74 | (defgeneric generic-function-undocumented ())
75 | (defmethod generic-function-undocumented ())
76 | (defmacro macro-documented ())
77 | (defclass class-undocumented ()
78 | ((slot-undocumented
79 | :accessor slot-undocumented)))
80 |
81 | (deftype integer-undocumented () '(integer 0 100))
82 |
83 |
84 | ;;; Other cases
85 |
86 | (defgeneric another-generic-function (x))
87 | (defmethod another-generic-function ((x (eql '1))))
88 | (defmethod another-generic-function ((x (eql '2)))
89 | "documented" t)
90 |
91 |
92 | ;;;
93 |
94 | (defun mul (x y)
95 | "Multiply x by y."
96 | (* x y))
97 |
98 | (defun 2x (x)
99 | "Multiply x by 2."
100 | (mul 2 x))
101 |
102 | (defun add-one (x)
103 | "Add 1 to x."
104 | (1+ x))
105 |
106 | #+ (or)
107 | (deftest mul
108 | (is (= 4 (mul 2 2)))
109 | (is (= 12 (mul 2 6))))
110 |
111 | #+ (or)
112 | (deftest 2x
113 | (is (= (2x 2) (mul 2 2))))
114 |
115 | #+ (or)
116 | (deftest should-fail
117 | (is (= 6 (mul 2 2))))
118 |
--------------------------------------------------------------------------------
/scratch-files/indentation.lisp:
--------------------------------------------------------------------------------
1 |
2 | ;;; Documentations
3 |
4 | ;; https://joaotavora.github.io/sly/#Semantic-indentation
5 | ;; https://slime.common-lisp.dev/doc/html/Semantic-indentation.html#Semantic-indentation
6 | ;; Info node `(elisp) Indenting Macros'
7 |
8 |
9 | ;;; Trivial-indent common lisp library
10 |
11 | ;; trivial-indent "just" configures slynk or swank
12 | (ql:quickload "trivial-indent")
13 |
14 | (trivial-indent:define-indentation defmacro (4 &lambda &body))
15 | (trivial-indent:define-indentation something-more-complex (4 &rest (&whole 2 0 4 &body)))
16 |
17 | ;; what exactly does "trivial-indent:define-indentation" updates?
18 | ;; How does it relate to the "slime-cl-indent" contrib?
19 | ;;
20 | ;; Ah! "swank-indentation" package is defined in `slime/contrib/swank-indentation.lisp'
21 | ;; And it is indeed the "back-end" part of the "slime-cl-indent" contrib.
22 | ;;
23 | ;; it calls `update-indentation-information` in either
24 | ;; "swank-indentation" or "slynk/indentation" package.
25 |
26 | swank-indentation:update-indentation-information
27 | slynk/indentation:update-indentation-information
28 |
29 | ;; `trivial-indent:initialize-slime' and
30 | ;; `trivial-indent:initialize-sly' sets the variables
31 | ;; `swank-indentation:*application-hints-tables*' and
32 | ;; `slynk/indentation:*application-hints-tables*'
33 |
34 | ;; it does keep a hash-table *indentation-hints*
35 |
36 |
37 | ;;; slime-cl-indent elisp slime contrib
38 |
39 | ;; ‘slime-cl-indent.el’
40 | ;; elisp variables
41 | ;; - common-lisp-style
42 | ;; - common-lisp-styles
43 | ;; macro
44 | ;; `define-common-lisp-style'
45 |
46 | ;; slime-cl-indent "just" configures emacs' `lisp-mode'
47 |
48 |
49 | ;;; emacs' built-in `lisp-mode'
50 |
51 | ;; ‘lisp-mode.el’ defines a bunch of variables to control the indentation:
52 |
53 | (
54 | lisp-align-keywords-in-calls
55 | lisp-backquote-indentation
56 | lisp-indent-defun-method
57 | lisp-indent-maximum-backtracking
58 | lisp-lambda-list-indentation
59 | lisp-lambda-list-keyword-alignment
60 | lisp-lambda-list-keyword-parameter-alignment
61 | lisp-lambda-list-keyword-parameter-indentation
62 | lisp-loop-body-forms-indentation
63 | lisp-loop-clauses-indentation
64 | lisp-loop-indent-body-forms-relative-to-loop-start
65 | lisp-loop-indent-forms-like-keywords
66 | lisp-loop-indent-subclauses
67 | lisp-simple-loop-indentation
68 | lisp-tag-body-indentation
69 | lisp-tag-indentation
70 | )
71 |
72 | interactive function: `indent-sexp'
73 |
74 |
75 | ;;; emacs' built-in syntax.el
76 |
77 | `parse-partial-sexp'
78 | `syntax-ppss' ;; Parse Partial Sexp State
79 |
80 | ;; parse-partial-sexp is a primitive-function in ‘src/syntax.c’.
81 |
82 |
83 |
84 | ;;; Examples that I want to work better
85 |
86 | ;; @ is where the cursor/point is at
87 |
88 | ;; paredit-reindent-defun does nothing is this case, but indent-sexp works
89 | (defun f (x)
90 | x)
91 |
--------------------------------------------------------------------------------
/tests/emacs/wip-demo.el:
--------------------------------------------------------------------------------
1 |
2 | (defmacro breeze/demo/with-read-string (values &rest body)
3 | "Macro to help mock the function read-string"
4 | (let ((values-var (gensym)))
5 | `(let ((,values-var ,values))
6 | (cl-letf (((symbol-function 'read-string)
7 | (lambda (prompt &optional initial-input default-value)
8 | (pop ,values-var))))
9 | ,@body))))
10 |
11 | (defun breeze/demo/find-sldb-buffer ()
12 | (remove-if-not
13 | #'(lambda (buffer-name)
14 | (string-match-p "^\\*sldb" buffer-name))
15 | (mapcar #'buffer-name
16 | (buffer-list))))
17 |
18 | ;; (mapcar #'kill-buffer (breeze/demo/find-sldb-buffer))
19 |
20 | (use-package htmlize)
21 |
22 | (with-current-buffer (get-buffer-create "demo.lisp")
23 | (erase-buffer)
24 | (lisp-mode)
25 | (slime-mode)
26 | (breeze-mode)
27 | ;; Use breeze's command to insert a package definition
28 | (breeze/demo/with-read-string
29 | '("demo" "")
30 | (breeze-insert-defpackage))
31 | ;; Use breeze's command to insert a function
32 | (breeze/demo/with-read-string
33 | '("foo" "")
34 | (breeze-insert-defun))
35 | ;; Write the function's body
36 | (insert "42")
37 | ;; Go "out" of the function's body
38 | (end-of-buffer)
39 | (insert "\n\n")
40 |
41 | ;; Evaluate the whole buffer
42 | (slime-eval-buffer)
43 |
44 | ;; Try to call "foo", but make a typo
45 | (insert "(fop)")
46 | ;; Take """screenshot""" of slime's debugger buffer.
47 | (run-at-time "0.5 sec" nil
48 | ;; TODO Extract this in another function
49 | #'(lambda ()
50 | (with-temp-buffer
51 | ;; Copy SLDB's buffer into the temp buffer
52 | (insert
53 | (with-current-buffer
54 | (get-buffer
55 | ;; Open the first SLDB buffer
56 | (car (breeze/demo/find-sldb-buffer)))
57 | (prog1
58 | (buffer-string)
59 | ;; Abort the evaluation
60 | (sldb-invoke-restart-by-name "ABORT"))))
61 | ;; Export the temp buffer into a new "html" buffer
62 | (let ((html-buffer (htmlize-buffer)))
63 | (with-current-buffer html-buffer
64 | ;; Write the html result
65 | (write-file (breeze/demo/next-to-this-file "demo.html"))
66 | ;; Kill the html buffer
67 | (kill-buffer))))))
68 | (slime-eval-last-expression)
69 | (write-file (breeze/demo/next-to-this-file "demo.lisp"))
70 | ;; (buffer-substring-no-properties (point-min) (point-max))
71 | )
72 |
73 |
74 | ;; NEXT STEP: take the html buffer and extract the CSS and the PRE
75 | ;; element so we can embed the result into another webpage, like
76 | ;; breeze's documentation
77 |
--------------------------------------------------------------------------------
/docs/design_decisions.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 14d42b3a-0a2f-4a3b-8937-7175e621c6ec
3 | :END:
4 | #+title: Design Decisions
5 |
6 | * Design decisions
7 |
8 | ** Write everything in common lisp
9 |
10 | As much as possible, so that breeze can easily be ported to different
11 | platforms and editors.
12 |
13 | ** Wrap definitions :obsolete:
14 |
15 | Decision: Create wrapper macros (e.g. =br:defun=) to keep the original
16 | forms for later analysis.
17 |
18 | This decision is really not definitive.
19 |
20 | This decision is less than ideal, especially for existing systems, but
21 | it was the easiest to start with.
22 |
23 | *** Alternatives
24 |
25 | **** Keep the string being eval'd
26 |
27 | Advising swank's eval function is "a good start" in that direction.
28 |
29 | **** Parse the source code
30 |
31 | - Might be hard, but [[https://github.com/s-expressionists/Eclector][eclector]] could make this easy.
32 | - [[https://github.com/hyotang666/read-as-string][hyotang666/read-as-string]] is another candidate
33 |
34 | ** Migrate to parachute 2022-03-08
35 |
36 | The test framework and the "wrap definition" parts always were
37 | proof-of-concepts: I wanted to be able to define some tests, and run
38 | them when either the test of the system-under-test was redefined. It
39 | worked, but now that I have a more and more complete common lisp
40 | parser, I can do the things properly. So I've move the concerned code
41 | into the folder "scratch-files" and I'll re-introduce them slowly in
42 | the future. (Because I really want something to run the tests in the
43 | background, for example.)
44 |
45 | ** Read from strings instead of streams
46 |
47 | I did some tests and the code was like 100x faster when reading from
48 | string instead of reading from streams. There are multiple reasons: to
49 | extract the "raw" text from the stream require consing new strings
50 | _and_ abusing file-position to move back and forth in the stream, both
51 | of these are very inefficient. Instead, we use displaced arrays which
52 | results in way less consing and no "stream state" to manage. This made
53 | both the code faster and simpler.
54 |
55 | From another point of view: why not? we were already copying the whole
56 | stream into the resulting tree, now we just have references to one
57 | string.
58 |
59 | ** Use =licence= and not =license=
60 |
61 | This is a very tiny decision, but I know I'll forget it.
62 |
63 | What made me decide between the two: =licence= is what asdf use, and
64 | it's what the user will see in their projects.
65 |
66 | ** Only use dependencies from quicklisp's distribution
67 |
68 | This project is not in quicklisp, and I don't plan to add it to
69 | quicklisp until it stabilizes (which might take years). I make sure to
70 | only use dependencies from quicklisp so that if somebody wants to try
71 | it out they'll just need to clone this repository in quicklisp's
72 | local-projects folder.
73 |
--------------------------------------------------------------------------------
/docs/eclector.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 8e956afc-28f4-4fda-b58f-7c111b0d92b3
3 | :END:
4 | #+title: Eclector
5 | #+filetags: :reader:
6 |
7 | [[https://github.com/s-expressionists/Eclector][Eclector on github]]
8 |
9 | #+begin_quote
10 | A portable Common Lisp reader that is highly customizable, can recover
11 | from errors and can return concrete syntax trees
12 | #+end_quote
13 |
14 | * Why not use eclector?
15 | :PROPERTIES:
16 | :ID: 08754391-271f-4e05-b307-3c912f5f4c0a
17 | :END:
18 |
19 | Simply because eclector was not mature enough back when I tried to use
20 | it. It had a few updates since then and it might work well now; at
21 | least the actual reader. I don't know if the CST (Concrete Syntax
22 | Tree) that it provides would be appropriate all the use cases.
23 |
24 | In any case, nothing prevents one to use _both_ reader. Breeze's
25 | reader is meant to be robust and safe, maybe it would make sense to
26 | use breeze's parser as a first pass, as a validation, and if
27 | everything goes well continue with eclector's reader.
28 |
29 | * Differences between eclector and breeze's parser
30 | :PROPERTIES:
31 | :ID: 979f0424-ad37-42ee-a1e6-99135fedc6da
32 | :END:
33 |
34 | Note: eclector's reader is highly customizable, so I'm trying to
35 | qualify eclector's default reader. *Also*, I haven't tried eclector in
36 | a few years, it's highly probable that something changed since.
37 |
38 | +---------------------------------+--------------------------------------------------------+
39 | | breeze's parser | eclector's reader |
40 | +---------------------------------+--------------------------------------------------------+
41 | | meant to be used by an editor | meant to implement ~cl:read~ |
42 | +---------------------------------+--------------------------------------------------------+
43 | | is not customizable | is highly customizable |
44 | +---------------------------------+--------------------------------------------------------+
45 | | doesn't discard any information | discard all whitespaces, comments and |
46 | | | feature expressions that fails (e.g. ~#+(or) this~) |
47 | +---------------------------------+--------------------------------------------------------+
48 | | will never singal an error | signals error and provide restarts to let the client |
49 | | (unless there's a bug!) | decide how to handle the situation |
50 | +---------------------------------+--------------------------------------------------------+
51 |
52 | * TODO Try out the "new" ~skipped-input-recording-client~
53 | :PROPERTIES:
54 | :ID: 03eebc77-bf6a-4172-8496-4dfb2490c01b
55 | :END:
56 |
57 | #+begin_src lisp
58 | (eclector.parse-result:read
59 | (make-instance 'skipped-input-recording-client)
60 | stream nil :eof)
61 | #+end_src
62 |
--------------------------------------------------------------------------------
/docs/features.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 13ea055e-4715-4583-811b-bff78ca300ee
3 | :END:
4 | #+title: Candidate Features
5 |
6 | * See also
7 |
8 | - [[id:5d211d9a-0749-4adb-abe0-e66133d09b5b][Editor integrations]]
9 | - [[id:54e6cd55-803b-4e15-82bc-a332130d020e][Sly/Slime integration]]
10 |
11 | * TODO Integration with occur :ux:emacs:
12 | :PROPERTIES:
13 | :ID: eb1f399a-e723-4a5d-91bd-26b473faca5d
14 | :END:
15 |
16 | - Make breeze's command "work" in occur's buffer
17 | - e.g. eval this (with the right package), or M-.
18 |
19 | * TODO Integration with which-key :ux:emacs:
20 | :PROPERTIES:
21 | :ID: a91ccca0-d156-4bb3-835d-aecaff9ca886
22 | :END:
23 |
24 | https://github.com/justbur/emacs-which-key/
25 |
26 | #+begin_quote
27 | Emacs package that displays available keybindings in popup
28 | #+end_quote
29 |
30 | It is possible to use
31 | e.g. =which-key-add-major-mode-key-based-replacements= to configure
32 | =which-key= to show more user-friendly names for the commands bound to
33 | each keys.
34 |
35 | * TODO Integration with treemacs :ux:emacs:
36 | :PROPERTIES:
37 | :ID: 71f02e53-2520-49f4-866c-40f146712db8
38 | :END:
39 |
40 | https://github.com/Alexander-Miller/treemacs
41 |
42 | #+begin_quote
43 | a tree layout file explorer for Emacs
44 | #+end_quote
45 |
46 | It is possible to use treemacs as "just" a treeview widget. Here's an
47 | example: https://github.com/emacs-lsp/lsp-treemacs
48 |
49 | * TODO Integration with origami.el :ux:emacs:
50 | :PROPERTIES:
51 | :ID: 87cb103c-906a-46d8-8f90-3b86668bc667
52 | :END:
53 |
54 | https://github.com/gregsexton/origami.el
55 |
56 | #+begin_example
57 | A folding minor mode for Emacs
58 | #+end_example
59 |
60 | #+begin_quote
61 | An origami parser is a function that takes a 'create function' and
62 | returns a function taking the string to be parsed. The returned
63 | function should return a list of fold nodes. Fold nodes are created
64 | using the passed-in create function.
65 | #+end_quote
66 |
67 | Note sure if it would be useful, I assume the built-in support for
68 | lisp is already good.
69 |
70 | * TODO Integration with flycheck :ux:emacs:
71 | :PROPERTIES:
72 | :ID: ebbcbf1f-da36-4064-8c22-addd2dc6c664
73 | :END:
74 |
75 | https://www.flycheck.org
76 |
77 | - [[https://www.flycheck.org/en/latest/user/flycheck-versus-flymake.html][Flycheck vs Flymake]]
78 | - [[https://www.flycheck.org/en/latest/developer/developing.html][Adding a syntax checker to Flycheck]]
79 | - Probably want to use ~flycheck-define-generic-checker~
80 |
81 | Note about autoloading:
82 |
83 | #+begin_quote
84 | The flycheck-define-checker macro is an autoload, so using it inside a
85 | with-eval-after-load form will load all of Flycheck. While this
86 | ensures the macro is correctly expanded, it also defeats the purpose
87 | of using with-eval-after-load.
88 | #+end_quote
89 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | ######################################################################
2 | ### Base layers, setup working directory and quicklisp
3 | # FROM docker.io/clfoundation/${LISP}:${LISP_VERSION} as base
4 | FROM alpine:3.18.4 AS base
5 |
6 | RUN mkdir /breeze
7 | WORKDIR /breeze
8 |
9 |
10 | FROM base AS quicklisp
11 |
12 | RUN apk add sbcl
13 | COPY scripts/quicklisp.lisp scripts/quicklisp.lisp
14 | RUN sbcl --non-interactive \
15 | --load scripts/quicklisp.lisp \
16 | --eval "(quicklisp-quickstart:install)" \
17 | --eval "(ql-util:without-prompting (ql:add-to-init-file))"
18 |
19 | ######################################################################
20 | ### Download all needed dependencies (for the main and the test
21 | ### systems).
22 | FROM quicklisp AS deps
23 |
24 | COPY breeze.asd .
25 | COPY scripts/load-dependencies.lisp scripts/load-dependencies.lisp
26 |
27 | RUN sbcl --noinform --non-interactive \
28 | --load scripts/load-dependencies.lisp
29 |
30 |
31 | FROM scratch AS dependencies.core
32 |
33 | COPY --from=deps /breeze/dependencies.core /dependencies.core
34 |
35 | ######################################################################
36 | ### Run the tests and generate some documentation
37 | FROM quicklisp AS test
38 |
39 | COPY . .
40 | RUN sbcl --core dependencies.core \
41 | --eval "(asdf:test-system '#:breeze)"
42 |
43 | FROM base AS org-publish
44 |
45 | RUN apk add bash ca-certificates emacs
46 |
47 | COPY . .
48 | COPY --from=test /breeze/docs /breeze/docs
49 |
50 | RUN emacs -Q --batch --load scripts/org-publish-project.el --kill
51 | RUN ls
52 | RUN ls /breeze/public
53 |
54 | FROM scratch AS public
55 |
56 | COPY --from=org-publish /breeze/public /
57 |
58 |
59 | ######################################################################
60 | ### This is where I left off
61 |
62 | FROM deps AS integration-tests-base
63 |
64 | RUN apk add bash ca-certificates emacs
65 | RUN apk add scrot screen ffmpeg xvfb-run
66 | RUN sbcl --noinform --non-interactive --eval "(ql:quickload '#:swank)"
67 |
68 | COPY . .
69 |
70 | FROM integration-tests-base AS debug
71 |
72 | # RUN apk add x11vnc
73 | RUN echo screen -ls >> ~/.bash_history
74 | RUN echo /breeze/scripts/demo/demo-recorder.sh >> ~/.bash_history
75 | RUN echo screen -R >> ~/.bash_history
76 |
77 | FROM integration-tests-base AS integration-tests-run
78 |
79 | # TODO
80 | # Run some test (fails because slime is not started)
81 | # RUN emacs -batch -l ert -l /breeze/tests/emacs/breeze-test.el -f ert-run-tests-batch-and-exit
82 |
83 | # TODO emacs (x11) complains that stdin is not a tty, might need to run it under screen
84 | #
85 | # -t file, --terminal=file
86 | # Use specified file as the terminal instead of using stdin/stdout. This must be the first argument specified in the command line.
87 | #
88 | # Run the demo
89 | RUN scripts/demo/demo-recorder.sh
90 |
91 | # Copy only the outputs
92 | FROM scratch AS integration-tests
93 | COPY --from=integration-tests-run /breeze/scripts/demo/output/* /
94 |
95 | # for image in $(docker exec breeze-demo-recorder sh -c 'ls /breeze/scripts/demo/*.png'); do
96 | # name=$(basename $image)
97 | # docker cp breeze-demo-recorder:$image $output/$name
98 | # done
99 |
--------------------------------------------------------------------------------
/tests/emacs/breeze-test.el:
--------------------------------------------------------------------------------
1 |
2 | (require 'ert)
3 |
4 | (defun breeze--xor (a b)
5 | (or (and a (not b))
6 | (and (not a) b)))
7 |
8 | (ert-deftest test/breeze--xor ()
9 | (should (equal '(nil t t nil)
10 | (mapcar (lambda (args)
11 | (apply 'breeze--xor args))
12 | '((nil nil)
13 | (nil t)
14 | (t nil)
15 | (t t))))))
16 |
17 |
18 |
19 | (ert-deftest test/breeze-%symbolicate ()
20 | (should (eq 'sly (breeze-%symbolicate2 "sly")))
21 | (should (eq 'sly (breeze-%symbolicate2 'sly)))
22 | (should (eq 'slime (breeze-%symbolicate2 "slime")))
23 | (should (eq 'slime (breeze-%symbolicate2 'slime)))
24 | (should (eq 'sly-eval (breeze-%symbolicate2 'sly "eval")))
25 | (should (eq 'slime-eval (breeze-%symbolicate2 'slime "eval")))
26 | (should (eq 'slime-connected-hook
27 | (breeze-%symbolicate2 'slime "connected-hook"))))
28 |
29 |
30 |
31 | ;; TODO true only if connected!
32 | (ert-deftest test/breeze-connection ()
33 | (should (breeze--xor
34 | (breeze-sly-connected-p)
35 | (breeze-slime-connected-p)))
36 | (should (eq t (breeze-check-if-connected-to-listener))))
37 |
38 | (ert-deftest test/breeze-eval ()
39 | ;; Integers
40 | (should (= (breeze-eval "(+ 1 2)") 3))
41 | ;; Strings
42 |
43 | (should (string= (breeze-eval "\"hi\"") "hi"))
44 | ;; Symbols
45 | (should (eq (breeze-eval "cl:t") t))
46 | (should (eq (breeze-eval "cl:nil") nil))
47 | (should (eq t (breeze-eval-predicate "t")))
48 | (should (eq t (breeze-eval-predicate "T")))
49 | (should (eq nil (breeze-eval-predicate "nil")))
50 | (should (eq nil (breeze-eval-predicate "NIL"))))
51 |
52 | ;; TODO Figure out how to evaluate something without triggering the debugger when an error occurs
53 | ;; (ert-deftest breeze-eval-empty-string ()
54 | ;; :expected-result :failed
55 | ;; (breeze-eval ""))
56 |
57 | ;; (let ((slime-event-hooks (list (lambda (event)
58 | ;; (message "Event: %S" (list (car event)
59 | ;; (length (cdr event))))
60 | ;; nil))))
61 | ;; (breeze-eval "(error \"oups\")"))
62 | ;; (breeze-eval "(read)")
63 |
64 |
65 |
66 |
67 | (ert-deftest test/breeze-relative-path ()
68 | (should (file-exists-p (breeze-relative-path)))
69 | (should (file-exists-p (breeze-relative-path "src/")))
70 | (should (file-exists-p (breeze-relative-path "src/breeze.el")))
71 | (should (file-exists-p (breeze-relative-path "src/ensure-breeze.lisp"))))
72 |
73 | (ert-deftest test/breeze-init ()
74 | (should (eq t (breeze-validate-if-package-exists "CL")))
75 | (should (eq nil (breeze-validate-if-package-exists "this package probably doesn't exists"))))
76 | ;; t
77 |
78 |
79 | ;; TODO only after (breeze-ensure)
80 | ;; (should (eq t (breeze-validate-if-breeze-package-exists)))
81 |
82 | ;; (should (eq t (breeze-ensure)))
83 |
84 |
85 |
86 | (ert-deftest test/breeze-intergration ()
87 | (ert-test-erts-file "breeze.erts"))
88 |
89 |
90 | ;; TODO (ert-deftest test/generate-bindings-documentation )
91 |
92 | ;; TODO (ert-deftest test/update-list-of-stubs)
93 |
--------------------------------------------------------------------------------
/tests/pattern/compile-pattern.lisp:
--------------------------------------------------------------------------------
1 | (in-package #:breeze.test.pattern)
2 |
3 |
4 |
5 | (define-test+run symbol-starts-with
6 | (progn
7 | (false (symbol-starts-with 'x #\?))
8 | (true (symbol-starts-with :? #\?))
9 | (true (symbol-starts-with :?x #\?))
10 | (true (symbol-starts-with '? #\?))
11 | (true (symbol-starts-with '?x #\?)))
12 | (progn
13 | (false (symbol-starts-with 'x "?"))
14 | (true (symbol-starts-with :? "?"))
15 | (true (symbol-starts-with :?x "?"))
16 | (true (symbol-starts-with '? "?"))
17 | (true (symbol-starts-with '?x "?"))))
18 |
19 | (define-test+run var-symbol-p
20 | (true (var-symbol-p :?x))
21 | (false (var-symbol-p 'x))
22 | (false (var-symbol-p "?x")))
23 |
24 | (define-test+run multi-valued-var-symbol-p
25 | (true (multi-valued-var-symbol-p :?*x))
26 | (false (multi-valued-var-symbol-p :?x))
27 | (false (multi-valued-var-symbol-p 'x))
28 | (false (multi-valued-var-symbol-p "?x")))
29 |
30 | (define-test+run wildcard-symbol-p
31 | (true (wildcard-symbol-p :_x))
32 | (false (wildcard-symbol-p 'x))
33 | (false (wildcard-symbol-p "_x")))
34 |
35 |
36 |
37 | (define-test+run "compile-pattern - wildcard"
38 | (is eqv (wildcard) (compile-pattern :_x))
39 | (is eqv (wildcard) (compile-pattern :_))
40 | (is eqv (wildcard) (compile-pattern '_)))
41 |
42 | (define-test+run "compile-pattern - atoms"
43 | (is eqv :x (compile-pattern :x))
44 | (is eqv (svar :?x) (compile-pattern :?x))
45 | (is eqv (svar '?x) (compile-pattern '?x))
46 | (is eqv
47 | (svar '?*x :multi-valued-p t)
48 | (compile-pattern '?*x))
49 | (is eqv 42 (compile-pattern 42))
50 | (is eqv "abc" (compile-pattern "abc")))
51 |
52 | (define-test+run "compile-pattern - :var"
53 | (is eqv (var :?y (wildcard)) (compile-pattern '(:var :?y _)))
54 | (is eqv (var :?z (svar '?a)) (compile-pattern '(:var :?z ?a)))
55 | (let ((p (compile-pattern '(?x ?x))))
56 | (is eq (name (aref p 0)) (name (aref p 1)))))
57 |
58 | (define-test+run "compile-pattern - :maybe"
59 | (is eqv (maybe :x) (compile-pattern '(:maybe :x)))
60 | (is eqv (maybe #(:x :y)) (compile-pattern '(:maybe (:x :y))))
61 | (is eqv (maybe :x :?y) (compile-pattern '(:maybe :x :?y)))
62 | (is eqv (maybe :x :?z) (compile-pattern '(:named :?z (:maybe :x))))
63 | (is eqv (maybe (svar :?x)) (compile-pattern '(:maybe :?x)))
64 | (is eqv (maybe (var :?x (wildcard))) (compile-pattern '(:maybe (:var :?x :_)))))
65 |
66 | (define-test+run "compile-pattern - :zero-or-more"
67 | (is eqv (zero-or-more #(:x)) (compile-pattern '(:zero-or-more :x)))
68 | (is eqv (zero-or-more #(:x :y)) (compile-pattern '(:zero-or-more :x :y))))
69 |
70 | (define-test+run "compile-pattern - :either"
71 | (is eqv (either #(:x)) (compile-pattern '(:either :x)))
72 | (is eqv (either #(:x :y)) (compile-pattern '(:either :x :y))))
73 |
74 | (define-test+run "compile-pattern - :symbol"
75 | (is eqv (sym :wild :wild :wild) (compile-pattern '(:symbol)))
76 | (is eqv (sym :wild :defun :wild) (compile-pattern '(:symbol :defun)))
77 | (is eqv (sym :cl :defun :wild) (compile-pattern '(:symbol :defun :cl)))
78 | (is eqv (sym :cl :defun :qualified) (compile-pattern '(:symbol :defun :cl :qualified))))
79 |
80 | (define-test+run "compile-pattern - vectors"
81 | (is eqv (vector '?xx) (compile-pattern #(?xx))))
82 |
--------------------------------------------------------------------------------
/src/cl-todo.lisp:
--------------------------------------------------------------------------------
1 | ;;; These are the functions left to "classify" in cl.lisp
2 |
3 | LDB
4 | LDB-TEST
5 |
6 |
7 |
8 |
9 | LET
10 | LET*
11 | LISP-IMPLEMENTATION-TYPE
12 | LISP-IMPLEMENTATION-VERSION
13 | LIST
14 | LIST*
15 |
16 | LIST-LENGTH
17 |
18 | LISTP
19 | LOAD
20 | LOAD-LOGICAL-PATHNAME-TRANSLATIONS
21 | LOAD-TIME-VALUE
22 | LOCALLY
23 | LOG
24 |
25 | LOGAND
26 | LOGANDC1
27 | LOGANDC2
28 | LOGBITP
29 | LOGCOUNT
30 | LOGEQV
31 | LOGIOR
32 | LOGNAND
33 | LOGNOR
34 | LOGNOT
35 | LOGORC1
36 | LOGORC2
37 | LOGTEST
38 | LOGXOR
39 |
40 | LONG-SITE-NAME
41 | LOOP
42 | LOOP-FINISH
43 | LOWER-CASE-P
44 | MACHINE-INSTANCE
45 | MACHINE-TYPE
46 | MACHINE-VERSION
47 | MACRO-FUNCTION
48 | MACROEXPAND
49 | MACROEXPAND-1
50 | MACROLET
51 | MAKE-ARRAY
52 |
53 |
54 | MAKE-CONDITION
55 |
56 |
57 |
58 | MAKE-HASH-TABLE
59 | MAKE-INSTANCE
60 | MAKE-INSTANCES-OBSOLETE
61 | MAKE-LIST
62 | MAKE-LOAD-FORM
63 | MAKE-LOAD-FORM-SAVING-SLOTS
64 |
65 | MAKE-PATHNAME
66 | MAKE-RANDOM-STATE
67 |
68 | MAKE-STRING
69 |
70 |
71 | MAP
72 | MAP-INTO
73 | MAPC
74 | MAPCAN
75 | MAPCAR
76 | MAPCON
77 | MAPHASH
78 | MAPL
79 | MAPLIST
80 |
81 | MASK-FIELD
82 |
83 | MEMBER
84 | MEMBER-IF
85 | MEMBER-IF-NOT
86 | MERGE
87 |
88 | SET-PPRINT-DISPATCH
89 | COPY-PPRINT-DISPATCH
90 |
91 |
92 | GET-PROPERTIES
93 |
94 | EVERY
95 | SOME
96 |
97 | ENOUGH-NAMESTRING
98 | ENSURE-DIRECTORIES-EXIST
99 |
100 |
101 | (EQ EQL EQUAL EQUALP)
102 |
103 | ERROR
104 | ETYPECASE
105 | (EVAL EVAL-WHEN)
106 |
107 |
108 | IDENTITY
109 |
110 | IMPORT
111 | IN-PACKAGE
112 |
113 | INSPECT
114 | DOCUMENTATION
115 | DISASSEMBLE
116 | DESCRIBE
117 | DESCRIBE-OBJECT
118 |
119 | FILL
120 | FIND
121 | FIND-ALL-SYMBOLS
122 | FIND-CLASS
123 | FIND-IF
124 | FIND-IF-NOT
125 | FIND-METHOD
126 | FIND-PACKAGE
127 |
128 | FIND-SYMBOL
129 | FINISH-OUTPUT
130 |
131 | CONSTANTP
132 | DEFCONSTANT
133 |
134 | COPY-ALIST
135 | COPY-LIST
136 | COPY-SEQ
137 | COPY-STRUCTURE
138 | COPY-SYMBOL
139 |
140 | ROTATEF
141 | SETQ
142 |
143 | ("Progs!" PROG PROG* PROG1 PROG2 PROGN PROGV)
144 |
145 | NAMESTRING
146 |
147 | NOT
148 | NOTANY
149 | NOTEVERY
150 | NRECONC
151 | NREVERSE
152 |
153 | QUOTE
154 |
155 |
156 |
157 | (READ READ-BYTE READ-CHAR READ-CHAR-NO-HANG READ-DELIMITED-LIST READ-FROM-STRING READ-LINE READ-PRESERVING-WHITESPACE READ-SEQUENCE)
158 |
159 | REMPROP
160 |
161 | (REQUIRE PROVIDE)
162 |
163 | SET
164 |
165 | SHADOW
166 | SHADOWING-IMPORT
167 | SHARED-INITIALIZE
168 | SHIFTF
169 | SHORT-SITE-NAME
170 |
171 | SIGNUM
172 | SIMPLE-BIT-VECTOR-P
173 | SIMPLE-CONDITION-FORMAT-ARGUMENTS
174 | SIMPLE-CONDITION-FORMAT-CONTROL
175 |
176 |
177 | SIMPLE-VECTOR-P
178 |
179 |
180 | SLEEP
181 | SLOT-BOUNDP
182 | SLOT-EXISTS-P
183 | SLOT-MAKUNBOUND
184 | SLOT-MISSING
185 | SLOT-UNBOUND
186 | SLOT-VALUE
187 | SOFTWARE-TYPE
188 | SOFTWARE-VERSION
189 |
190 | STABLE-SORT
191 | SORT
192 |
193 | SPECIAL-OPERATOR-P
194 |
195 |
196 | (TIME
197 | (TRACE UNTRACE))
198 |
199 |
200 | DEFCLASS DEFSTRUCT
201 |
202 | DEFINE-SYMBOL-MACRO
203 | DEFMACRO
204 |
205 | DEFGENERIC DEFMETHOD
206 | DEFINE-METHOD-COMBINATION
207 |
208 | (DEFPARAMETER DEFVAR)
209 |
210 | DESTRUCTURING-BIND
211 |
212 | DIRECTORY DIRECTORY-NAMESTRING
213 |
214 | ((DO DO* DOLIST DOTIMES)
215 | (DO-ALL-SYMBOLS DO-EXTERNAL-SYMBOLS DO-SYMBOLS))
216 |
217 | DEPOSIT-FIELD
218 | DPB
219 |
220 | DRIBBLE
221 | ED
222 |
223 | ENDP
224 |
--------------------------------------------------------------------------------
/docs/reader-macros.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 9cb1f9d0-572f-4b8c-bbc8-4c1bb9a54eb4
3 | :END:
4 | #+title: Reader macros
5 |
6 | https://quickdocs.org/-/search?q=reader
7 |
8 | * Example of a custom reader macro
9 | :PROPERTIES:
10 | :ID: 68e2f3b0-264e-4844-b38f-92be13cca6ea
11 | :END:
12 |
13 | #+begin_src
14 | (with-input-from-string (input "{ hey }")
15 | (let ((*readtable* (copy-readtable)))
16 | (set-macro-character #\{ (lambda (stream char)
17 | (read-delimited-list #\} stream)))
18 | (read input)))
19 | ;; => (HEY)
20 | #+end_src
21 |
22 | * cl-annot
23 | :PROPERTIES:
24 | :ID: 11702123-9dc1-4ca4-9325-53d4ac4188cc
25 | :END:
26 |
27 | =@asfd=
28 |
29 | * Eclector's =syntax-extensions= module
30 | :PROPERTIES:
31 | :ID: 3c68d188-b4c9-4ab1-aeb8-cee25aaa8273
32 | :END:
33 |
34 | From eclector 0.10 release's notes:
35 |
36 | #+begin_src
37 | p::(a b)
38 | ===
39 | (p::a p::b)
40 | #+end_src
41 |
42 | #+begin_src
43 | #; (this form is commented out)
44 | #2; (these 2 forms) (are commented out)
45 | #+end_src
46 |
47 | * Named readtables
48 | :PROPERTIES:
49 | :ID: f5fa06ac-75a3-4dbf-8ed3-17c320ff2927
50 | :END:
51 |
52 | #+begin_src
53 | (in-readtable )
54 | (named-readtables:in-readtable ...)
55 | #+end_src
56 |
57 | * CommonQt/Qtools
58 | :PROPERTIES:
59 | :ID: 16bbdda7-ce07-456b-be44-fd787c712c5f
60 | :END:
61 |
62 | #+begin_src
63 | #_
64 | q+
65 | #> #<
66 | #'
67 | #+end_src
68 |
69 | P.S. These are not maintained anymore
70 |
71 | * Library "Reader"
72 | :PROPERTIES:
73 | :ID: 0cddf3d0-b37a-4a66-83dd-05d1e63dea33
74 | :END:
75 |
76 | - https://quickdocs.org/reader
77 |
78 | #+begin_src
79 | (reader:enable-reader-syntax ...)
80 |
81 | #[
82 | {eq
83 | {eql
84 | {equal
85 | #{
86 | #!
87 |
88 | ! (not )
89 | $ "ensure-string"
90 | #+end_src
91 |
92 | * Shebang
93 | :PROPERTIES:
94 | :ID: bc2db964-8402-42e6-8992-dc754941f8c4
95 | :END:
96 |
97 | at least sbcl and roswell
98 |
99 | #+begin_src
100 | #!
101 | #+end_src
102 |
103 | * cl-interpol
104 | :PROPERTIES:
105 | :ID: 1188ce38-45c1-426d-aab1-b4d209baef62
106 | :END:
107 |
108 | #+begin_src
109 | (named-readtables:in-readtable :interpol-syntax)
110 | or
111 | (cl-interpol:enable-interpol-syntax)
112 | (cl-interpol:disable-interpol-syntax)
113 | #+end_src
114 |
115 | #+begin_quote
116 | The question mark may optionally be followed by an R and an X (case
117 | doesn't matter) - see the section about regular expression syntax
118 | below. If both of them are present, the R must precede the X.
119 | #+end_quote
120 |
121 | #+begin_quote
122 | The next character is the opening outer delimiter which may be one of
123 | - ="= (double quote),
124 | - ='= (apostrophe),
125 | - =|= (vertical bar),
126 | - =#= (sharpsign),
127 | - =/= (slash),
128 | - =(= (left parenthesis),
129 | - =<= (less than),
130 | - =[= (left square bracket), or
131 | - ={= (left curly bracket).
132 | (But see =*OUTER-DELIMITERS*=.)
133 | #+end_quote
134 |
135 | * curry-compose-reader-macros
136 | :PROPERTIES:
137 | :ID: eff9b0b6-ceb3-4882-bdec-1ab212fb20fc
138 | :END:
139 |
140 | https://quickdocs.org/curry-compose-reader-macros
141 |
142 | #+begin_src
143 | (in-readtable :curry-compose-reader-macros)
144 | {+ 1}
145 | [#'list {* 2}]
146 | «list {* 2} {* 3}»
147 | ‹if #'evenp #'1+ #'1-›
148 | #+end_src
149 |
--------------------------------------------------------------------------------
/src/cmds/test-commands.lisp:
--------------------------------------------------------------------------------
1 | (defpackage #:breeze.test-commands
2 | (:documentation "Commands related to tests")
3 | (:use #:cl #:breeze.command)
4 | (:export #:run-test-at-point
5 | #:run-tests-in-file
6 | #:run-tests-in-suite
7 | #:run-tests-in-package
8 | #:run-system-tests
9 | #:run-all-tests
10 | #:quickfix-test
11 | #:goto-test
12 | #:move-to-tests
13 | #:undefine-test-at-point))
14 |
15 | (in-package #:breeze.test-commands)
16 |
17 | #|
18 |
19 | ideas:
20 | - debug tests
21 | - maybe implicitly insert breaks
22 | - generate tests
23 | - re-run tests on change (watch)
24 | - use coverage data (from a full-run) to know which tests to re-run
25 | - command: toggle watch
26 | - command: toggle watch for specific tests or suite
27 | - run with coverage
28 | - show test "status" in the editor (e.g. in emacs' header-line or mode-line)
29 | - run in a different process!
30 | - mutation testing
31 |
32 | it's possible to narrow down which test is probably points to the
33 | right root cause when a lot of tests fails by inspecting the relations
34 | of the functions being called by the tests (i.e. infer the
35 | dependencies between the tests).
36 |
37 | |#
38 |
39 | ;; TODO insert-test-system-definition
40 |
41 | ;; TODO defmethods
42 | ;; TODO support multiple test framework _at the same time_
43 |
44 |
45 | ;;; Functions for working with test frameworks
46 |
47 | (defvar *known-test-frameworks*
48 | '(1am
49 | cacau
50 | check-it
51 | checkl
52 | cl-naive-test
53 | cl-quickcheck
54 | clite
55 | clue #| used by alive, not in quicklisp https://github.com/nobody-famous/clue |#
56 | clunit
57 | clunit2
58 | fiasco
59 | fiveam
60 | lift
61 | lisp-unit
62 | lisp-unit2
63 | monkeylib-test-framework
64 | parachute
65 | parten-test
66 | ptester
67 | rove
68 | prove #| archived in 2020 |#
69 | rt
70 | should-test
71 | simplet
72 | stefil
73 | test-urils
74 | testiere
75 | try
76 | unit-test
77 | xlunit))
78 |
79 | ;; protest
80 |
81 | (defun detect-all-test-framework ())
82 |
83 |
84 | ;;; Commands for working with tests
85 |
86 | (define-command run-test-at-point ()
87 | "Run the test at point."
88 | 'not-implemented-yet)
89 |
90 | (define-command run-tests-in-file ()
91 | "Run the all the tests in a file."
92 | 'not-implemented-yet)
93 |
94 | (define-command run-tests-in-suite ()
95 | "Run the all the tests in a suite."
96 | 'not-implemented-yet)
97 |
98 | (define-command run-tests-in-package ()
99 | "Run the all the tests in a package."
100 | 'not-implemented-yet)
101 |
102 | (define-command run-system-tests ()
103 | "Run a system's tests using ~(asdf:test-system)~."
104 | 'not-implemented-yet)
105 |
106 | (define-command run-all-tests ()
107 | "Run all the tests..."
108 | 'not-implemented-and-not-quite-sure-exactly-what-this-should-do)
109 |
110 | (define-command quickfix-test ()
111 | "Tries to fix the test(s) automagically."
112 | 'not-implemented
113 | #| If the assertion is not complete, run the code and try to
114 | generate the missing part (the "expected" value). |#)
115 |
116 | (define-command goto-test ()
117 | "Go to a specific test."
118 | 'not-implemented)
119 |
120 | (define-command undefine-test-at-point ()
121 | "Remove the test at point"
122 | 'not-implemented)
123 |
124 | (define-command move-to-tests ()
125 | "Move the code at point to a test file."
126 | 'not-implemented)
127 |
--------------------------------------------------------------------------------
/scratch-files/test-runner.lisp:
--------------------------------------------------------------------------------
1 | ;;;; The test runner is a separate thread that receives request to run test.
2 | ;;;; We use a thread (as opposed to straight up running the tests) in
3 | ;;;; order to debounce and group the requests.
4 |
5 | ;;; TODO Logging (maybe use log4cl)
6 |
7 | (defpackage #:breeze.test-runner
8 | (:documentation "Provides a test-runner (and methods to interact
9 | with it). Alternatively, you _could_ run many different
10 | test-runners.")
11 | ;; TODO Don't USE alexandria and anaphora, perhaps not breeze.worker either
12 | (:use :cl #:alexandria #:anaphora
13 | #:breeze.worker)
14 | (:export #:start-test-runner
15 | #:stop-test-runner
16 | #:ensure-test-runner
17 | #:request-to-run-test
18 | #:request-to-run-test*)
19 | (:import-from #:breeze.test
20 | #:run-all-tests))
21 |
22 | (in-package #:breeze.test-runner)
23 |
24 | (defclass test-runner (worker)
25 | ((messages :initform ())
26 | (received-messages-last-iteration-p :initform nil))
27 | (:documentation "A test runner is a worker that run tests on demand (with some debouncing."))
28 |
29 | (defvar *test-runner* (make-instance 'test-runner
30 | :interval 0.25
31 | :name "breeze's test runner")
32 | "The main test-runner.")
33 |
34 | (defun process-messages (message-list)
35 | "Take a list of messages (already debounced), deduplicate and process them."
36 | (run-all-tests
37 | (remove-duplicates message-list &key #'car)))
38 |
39 | (defun receive-messages ()
40 | (worker-receive-all-messages *test-runner*))
41 |
42 | (defmethod worker-report ((test-runner test-runner) level control-string &rest format-arguments)
43 | ;; Make the test-worker quiet (do nothing)
44 | ;; (call-next-method) ;; <- prints to standard-output
45 | )
46 |
47 | (defmethod worker-run ((test-runner test-runner))
48 | (with-slots (messages received-messages-last-iteration-p)
49 | test-runner
50 | (worker-report test-runner :debug "~%running test runner~%Messages: ~A~%received: ~A"
51 | messages received-messages-last-iteration-p)
52 | (aif (receive-messages)
53 | (setf messages (append messages it)
54 | received-messages-last-iteration-p t)
55 | (setf received-messages-last-iteration-p nil))
56 | (when (and messages (not received-messages-last-iteration-p))
57 | (process-messages messages)
58 | (setf received-messages-last-iteration-p nil
59 | messages nil))))
60 |
61 | (defun start-test-runner ()
62 | "Start the test runner"
63 | ;; Empty the message queue before starting.
64 | (receive-messages)
65 | (worker-start *test-runner*))
66 |
67 | (defun stop-test-runner ()
68 | "Stop the test runner"
69 | (worker-stop *test-runner*))
70 |
71 | (defun ensure-test-runner ()
72 | "Start the test runner if it's not already running."
73 | (worker-ensure-alive *test-runner*))
74 |
75 | (defun request-to-run-test (test)
76 | "Take a test name and send it to the test-runner."
77 | (worker-send *test-runner* (list test (get-universal-time))))
78 |
79 | (defun request-to-run-test* (test-list)
80 | "Take a list of test name and send it to the test-runner."
81 | (map nil #'request-to-run-test test-list))
82 |
83 | ;; Bunch of calls used to manually test the test-runner's logic
84 | #|
85 | (worker-start *test-runner*)
86 | (worker-stop *test-runner*)
87 | (worker-alive-p *test-runner*)
88 |
89 | (request-to-run-test 'a)
90 | (request-to-run-test* '(a b c)))
91 | (progn
92 | (request-to-run-test 'a)
93 | (request-to-run-test* '(a b c)))
94 | |#
95 |
--------------------------------------------------------------------------------
/scripts/demo/demo-recorder.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | #
3 | # This script is meant to be run as the main script _inside docker_
4 | #
5 | # It takes care of starting sbcl, load breeze, start swank, start xvfb
6 | # and emacs.
7 | #
8 |
9 | set -xeuo pipefail
10 |
11 | # TODO test if `git` is available first
12 | # This is for running the script outside a container, which is not supported as well (for the moment)
13 | # # Move to repo's root
14 | # cd "$(git rev-parse --show-toplevel)"
15 |
16 | export DEMO_LISTENER_PORT=40050
17 | export DEMO_OUTPUT_DIR=${DEMO_OUTPUT_DIR:-scripts/demo/output}
18 |
19 | mkdir -p ${DEMO_OUTPUT_DIR}
20 |
21 | listener_ready_file="listener-ready"
22 | director_logs=${DEMO_OUTPUT_DIR}/emacs-director.log
23 |
24 | # Keeping that because I might want to demo something that uses
25 | # quicklisp.
26 | # Example of how to use quicklisp:
27 | # --load "/root/quicklisp/setup.lisp" \
28 | # --eval "(asdf:load-asd \"/breeze/breeze.asd\")" \
29 | # --eval "(ql:quickload '(#:breeze #:swank))" \
30 |
31 | # Starting sbcl in a screen
32 | screen -dm sbcl --noinform \
33 | --core dependencies.core \
34 | --eval "(asdf:load-system '#:swank :verbose t :force-not (asdf:already-loaded-systems))" \
35 | --eval "(asdf:load-asd \"/breeze/breeze.asd\")" \
36 | --eval "(asdf:load-system '#:breeze :verbose t :force-not (asdf:already-loaded-systems))" \
37 | --eval "(swank:create-server :dont-close t :port ${DEMO_LISTENER_PORT})" \
38 | --eval "(with-open-file (_ \"$listener_ready_file\" :if-does-not-exist :create))"
39 |
40 | function wait_for_file() {
41 | while test ! -f $1
42 | do
43 | sleep 1
44 | done
45 | sleep 1
46 | }
47 |
48 | echo "Waiting for swank to startup in the background..."
49 | wait_for_file $listener_ready_file
50 | echo "Swank is up!"
51 |
52 | emacs_args=(
53 | # Don't load any init file
54 | -Q
55 | # Load the emacs-director's loader
56 | -l scripts/emacs-director/util/director-bootstrap.el
57 | # Run in fullscreen
58 | --fullscreen
59 | # Run our demo
60 | -l scripts/demo/demo.el
61 | # Trying to pass extra arguments
62 | base-demo
63 | )
64 |
65 | function emacs_x11() {
66 | export DISPLAY=:99
67 |
68 | # -s "-screen 0 1280x800x32"
69 | xvfb-run -e ${DEMO_OUTPUT_DIR}/xvfb-errors.log emacs "${emacs_args[@]}" &
70 | ## sleep 2
71 | ## tail -f $director_logs | sed '/END/q'
72 |
73 | # TODO This is Work in progress..
74 | # I think I should run the whole x11 stuff in screen
75 | # if command x11vnc; then
76 | # TODO vnc
77 | # x11vnc -o ${DEMO_OUTPUT_DIR}/x11vnc.log -display ${DISPLAY} -bg
78 | # -xkb
79 | # -noxrecord -noxfixes -noxdamage
80 | # -repeat -nopw
81 | # -wait 5
82 | # -permitfiletransfer -tightfilexfer
83 | # fi
84 | }
85 |
86 | function emacs_tty() {
87 | # For debugging
88 | emacs -nw "${emacs_args[@]}"
89 | }
90 |
91 | function wait_for_emacs_to_stop() {
92 | # TODO Time out?
93 | while pgrep emacs
94 | do
95 | sleep 1
96 | done
97 | }
98 |
99 | function dump() (
100 | set +e
101 | echo
102 | find -name '*.log'
103 | echo
104 | find / -name 'breeze*.png'
105 | echo
106 | cat $director_logs
107 | echo
108 | cat ${DEMO_OUTPUT_DIR}/messages.log
109 | ls ${DEMO_OUTPUT_DIR}
110 | )
111 |
112 | trap "echo SIGINT; dump; sh" SIGINT # ^c
113 |
114 | emacs_x11
115 | # wait_for_file $director_logs
116 |
117 | sleep 1
118 | wait_for_emacs_to_stop
119 | # emacsclient -nw
120 |
121 | ### below, it's all for testing
122 |
123 | dump
124 |
125 | # if the standard input is a terminal, then open a shell, for
126 | # interactive debugging
127 | if [ -t 0 ]; then
128 | bash
129 | fi
130 |
--------------------------------------------------------------------------------
/docs/syntax_highlighting.org:
--------------------------------------------------------------------------------
1 | :PROPERTIES:
2 | :ID: 8a919b83-89f4-40b8-aeb4-2638c308cddf
3 | :END:
4 | #+title: Syntax highlighting
5 |
6 | Implementing syntax highlighting "in breeze" would:
7 | - improve the situation in emacs
8 | - provide the same "backend" for each editors
9 | - be a very good test for the parser
10 |
11 | * Syntax highlighting in Emacs
12 |
13 | Oftentimes, the syntax highlighting is not exactly right.
14 |
15 | I can't remember a specific example of the syntax highlighting beging
16 | _broken_ (not just inexact) rigth this moment, but it's easy to
17 | imagine a custom reader macro that would cause issues.
18 |
19 | One example, the "cl keywords" (as defined by emacs' syntax
20 | highlighting) are detected as such even if the symbol is actually
21 | shadowed. This is not _always_ want we want.
22 |
23 | It would be nice to have the cl:loop keywords highlighted even when
24 | they're not :keywords.
25 |
26 | The various "literals" are not highlighted at all (like =#\Return=, or
27 | =#x1A=).
28 |
29 | It could be nice to have highlighting specific to a macro... That
30 | would be hard in general, but it might be possible for quite a few
31 | macros...
32 |
33 | It could be nice to highlight the cl:format's control strings.
34 |
35 | ** How syntax highlighting works in emacs
36 |
37 | #+begin_quote
38 | font-lock-mode is the standard way to have Emacs perform syntax
39 | highlighting in the current buffer. It is enabled by default.
40 | #+end_quote
41 | From [[https://www.gnu.org/software/emacs/manual/html_node/efaq/Turning-on-syntax-highlighting.html][How do I turn on syntax highlighting?]]
42 |
43 | See info node: [[info:emacs#Font Lock][emacs#Font Lock]]
44 |
45 | - =font-lock-mode= uses predefined font faces like
46 | =font-lock-string-face=/
47 | - it has a "just in time" syntax highlighting which is called
48 | =jit-locking=
49 |
50 | Syntax highlighting can be done in different ways:
51 | - regex
52 | - syntax tables
53 | - "Use syntax tree produced by a full-blown parser" ← bingo
54 | - it is called "Parser-based font lock"
55 |
56 | *** Parser-based Font Lock
57 |
58 | See info node: [[info:emacs#Parser-based Font Lock][emacs#Parser-based Font Lock]]
59 |
60 | aannnd disappointment, it only talks about treesitter...
61 |
62 | ** Apparently, I'm not the only one wanting the improve CL font-lock
63 |
64 | https://www.n16f.net/blog/custom-font-lock-configuration-in-emacs/
65 |
66 | *** TODO Explore this blog and add notes in there
67 |
68 | This blog seems to have a lot of posts about using lisp and tweaking
69 | editors (emacs) to make it event better.
70 |
71 | ** DIY fontification ??
72 |
73 | *** Using syntax table
74 |
75 | [[info:elisp#Syntax Tables][elisp#Syntax Tables]] are used by Font lock mode...
76 |
77 | https://www.emacswiki.org/emacs/EmacsSyntaxTable
78 |
79 | #+begin_quote
80 | When the syntax table is not flexible enough to specify the syntax of
81 | a language, you can override the syntax table for specific character
82 | occurrences in the buffer, by applying a syntax-table text property.
83 | #+end_quote
84 | From [[info:elisp#Syntax Properties][elisp#Syntax Properties]]
85 |
86 | The function =syntax-propertize-function= is used by font-lock mode
87 | during "during syntactic fontification".
88 |
89 | This [[https://stackoverflow.com/a/25251144][StackOverflow answer]] seems to be a good example of how to
90 | customize this... And I _think_ it would be the right place
91 |
92 | Use =setq-local= though.. or =(make-local-variable
93 | 'syntax-propertize-function)=
94 |
95 | Another interesting explanation of =syntax-propertize-function=.
96 |
97 | =lisp-mode-syntax-table= is the name of the variable containing the
98 | syntax table object for the =lisp-mode=.
99 |
100 | *** Using text properties
101 |
102 | I _could_ set the ='face= property manually too... (it overrides the
103 | ='font-lock-face= property).
104 |
--------------------------------------------------------------------------------
/tests/xref.lisp:
--------------------------------------------------------------------------------
1 | (in-package #:common-lisp-user)
2 |
3 | (uiop:define-package #:breeze.test.xref
4 | (:documentation "Tests for breeze.xref.")
5 | (:mix #:breeze.xref #:cl #:alexandria)
6 | (:import-from #:parachute
7 | #:define-test
8 | #:is
9 | #:true
10 | #:false))
11 |
12 | (in-package #:breeze.test.xref)
13 |
14 | ;; TODO this test is useful, but doesn't scale as-is
15 | #++
16 | (define-test find-package
17 | (is equal
18 | (find-packages-by-prefix "breeze")
19 | (find-packages-by-prefix "breeze.")))
20 |
21 | (defparameter *symbols*
22 | '(dum:*bound-variable*
23 | dum:*unbound-variable*
24 | dum:a-class
25 | dum:a-function
26 | dum:a-macro
27 | dum:slot
28 | (setf dum:slot)))
29 |
30 | (define-test predicate-dont-signal-any-error
31 | (do-external-symbols (symbol 'breeze.dummy.test)
32 | (mapcar #'(lambda (predicate)
33 | (funcall predicate symbol))
34 | '(generic-method-p
35 | classp
36 | specialp
37 | macrop
38 | simple-function-p))))
39 |
40 | (define-test generic-method-p
41 | (false (generic-method-p 'dum:*bound-variable*))
42 | (false (generic-method-p 'dum:*unbound-variable*))
43 | (false (generic-method-p 'dum:a-class))
44 | (false (generic-method-p 'dum:a-function))
45 | (false (generic-method-p 'dum:a-macro))
46 | (true (generic-method-p 'dum:slot))
47 | (true (generic-method-p '(setf dum:slot))))
48 |
49 | (define-test specialp
50 | (true (specialp 'dum:*bound-variable*))
51 | (true (specialp 'dum:*unbound-variable*))
52 | (false (specialp 'dum:a-class))
53 | (false (specialp 'dum:a-function))
54 | (false (specialp 'dum:a-macro))
55 | (false (specialp 'dum:slot))
56 | (false (specialp '(setf dum:slot))))
57 |
58 | (define-test macrop
59 | (false (macrop 'dum:*bound-variable*))
60 | (false (macrop 'dum:*unbound-variable*))
61 | (false (macrop 'dum:a-class))
62 | (false (macrop 'dum:a-function))
63 | (true (macrop 'dum:a-macro))
64 | (false (macrop 'dum:slot))
65 | (false (macrop '(setf dum:slot))))
66 |
67 | (define-test simple-function-p
68 | (false (simple-function-p 'dum:*bound-variable*))
69 | (false (simple-function-p 'dum:*unbound-variable*))
70 | (false (simple-function-p 'dum:a-class))
71 | (true (simple-function-p 'dum:a-function))
72 | (false (simple-function-p 'dum:a-macro))
73 | (false (simple-function-p 'dum:slot))
74 | (false (simple-function-p '(setf dum:slot))))
75 |
76 |
77 | ;; I used this to generate the tests for classp
78 | #+nil
79 | (let* ((fn 'classp)
80 | (test-cases
81 | (with-output-to-string (*standard-output*)
82 | (loop :for symbol :being :the :external-symbol :of 'breeze.dummy.test
83 | :for pass = (funcall fn symbol)
84 | :unless (search "undocumented" (string-downcase (symbol-name symbol)))
85 | :do
86 | (format t "~&(is ")
87 | (unless pass (format t "(not "))
88 | (format t "(~a 'dum:~a)" fn symbol)
89 | (unless pass (format t ")"))
90 | (format t ")")))))
91 | (format t "(define-test ~(~a~%~a~))" fn
92 | (breeze.string:indent-string 2 test-cases)))
93 |
94 | (define-test classp
95 | (false (classp 'dum:*bound-variable*))
96 | (false (classp 'dum:slot))
97 | (true (classp 'dum:a-class))
98 | (false (classp 'dum:a-generic-function))
99 | (false (classp 'dum:a-macro))
100 | (false (classp 'dum:another-generic-function))
101 | (false (classp 'dum:*unbound-variable*))
102 | (false (classp 'dum:a-function)))
103 |
104 | (define-test externalp
105 | (true (externalp 'cl:null))
106 | (null (externalp 'this-unexported-symbol))
107 | (false (externalp '#:uninterned-symbol))
108 | (false (externalp (gensym))))
109 |
--------------------------------------------------------------------------------
/tests/workspace.lisp:
--------------------------------------------------------------------------------
1 | (defpackage #:breeze.test.workspace
2 | (:documentation "Test package for #:breeze.workspace")
3 | (:use #:cl #:breeze.workspace)
4 | (:import-from #:breeze.indirection
5 | #:with-simple-indirections)
6 | (:import-from #:parachute
7 | #:define-test
8 | #:define-test+run
9 | #:is
10 | #:true
11 | #:false
12 | #:of-type
13 | #:finish)
14 | (:export #:*breeze-workspace*))
15 |
16 | (in-package #:breeze.test.workspace)
17 |
18 |
19 |
20 | (define-test infer-project-name
21 | (false
22 | (with-simple-indirections
23 | ((breeze.utils:find-version-control-root))
24 | (infer-project-name "some path"))
25 | "infer-project-name should return nil if the version control root directory was not found")
26 | (is string= "foobar"
27 | (with-simple-indirections
28 | ((breeze.utils:find-version-control-root
29 | #p"/home/nobody/projects/foobar/"))
30 | (infer-project-name "some path"))
31 | "infer-project-name should return the name of the version control root directory when it is found"))
32 |
33 | (defun goto-all-positions ($node)
34 | (loop
35 | :with length = (length (breeze.parser:source $node))
36 | :for i :below length
37 | :do (breeze.parser:goto-position $node i)))
38 |
39 | (defparameter *breeze-workspace* nil)
40 |
41 | ;; this tests add-to-workspace
42 | (define-test+run *breeze-workspace*
43 | (let ((*workspace* (make-workspace)))
44 | (add-files-to-workspace
45 | (breeze.asdf:find-all-related-files 'breeze))
46 | (setf *breeze-workspace* *workspace*)))
47 |
48 | (define-test+run add-to-workspace
49 | :depends-on (*breeze-workspace*)
50 | (finish
51 | (map-workpace-buffers
52 | (lambda (buffer
53 | &aux ($node (breeze.parser:node-iterator buffer)))
54 | (finish
55 | (progn ;; time
56 | (goto-all-positions $node))
57 | "Should be able to \"goto\" every positions in ~s" (name buffer))))))
58 |
59 | (defun suffixp (got expected-suffix)
60 | (alexandria:ends-with-subseq expected-suffix got))
61 |
62 | (define-test+run locate-package-definition
63 | :depends-on (*breeze-workspace*)
64 | (let ((*workspace* *breeze-workspace*))
65 | (let ((location (finish (locate-package-definition "breeze.string"))))
66 | (true location
67 | "Should have been able to find the breeze.string's package definition.")
68 | (true (getf location :buffer)
69 | "Should have returned a plist that contains the key :buffer.")
70 | (true (getf location :node-iterator)
71 | "Should have returned a plist that contains the key :node-iterator.")
72 | (destructuring-bind (&key buffer node-iterator) location
73 | (when (and buffer node-iterator)
74 | (is suffixp "src/string-utils.lisp" (name buffer))
75 | (is string= #1="(defpackage #:breeze.string
76 | (:documentation \"String manipulation utilities\")"
77 | (subseq (breeze.parser:node-string node-iterator)
78 | 0 (length #1#))))))
79 | (let ((location (finish (locate-package-definition "breeze.parser"))))
80 | (true location
81 | "Should have been able to find the breeze.parser's package definition.")
82 | (true (getf location :buffer)
83 | "Should have returned a plist that contains the key :buffer.")
84 | (true (getf location :node-iterator)
85 | "Should have returned a plist that contains the key :node-iterator.")
86 | (destructuring-bind (&key buffer node-iterator) location
87 | (when (and buffer node-iterator)
88 | (is suffixp "src/parser/parser.lisp" (name buffer))
89 | (is string= #2="(uiop:define-package #:breeze.parser
90 | (:documentation \"A fast, lossless, robust and "
91 | (subseq (breeze.parser:node-string node-iterator)
92 | 0 (length #2#))))))))
93 |
--------------------------------------------------------------------------------
/docs/brightlight-green.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css?family=Nunito");html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,textarea,input,select,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{font-family:"Nunito",sans-serif;color:#000}body,html{padding:0;margin:0;overflow-x:hidden;background-color:#fff}nav{font-family:"Nunito",sans-serif;background-color:#212121;color:#fff;display:-webkit-box;display:-ms-flexbox;display:flex}nav header{padding:8px;display:inline}nav header a{text-decoration:none;color:#fff}nav header a:hover{color:#00c853}nav header+input:checked+div{display:block}nav div{display:none;margin-left:auto}nav div ul{list-style:none;display:-webkit-box;display:-ms-flexbox;display:flex;margin:0 10px}nav div ul li a{display:inline-block;padding:8px;color:#fff;text-decoration:none}nav div ul li:hover{background-color:#00c853}nav div ul li ul{right:0;position:relative;background:pink}@media (max-width: 630px){nav{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}nav header{text-align:center}nav div{margin:auto}nav div ul{padding:0}nav div ul li{border-top-left-radius:5px;border-top-right-radius:5px}nav div ul li[active],nav div ul li.active{border:1px solid #00c853;border-bottom:none}blockquote{border-left:4px solid #00c853;padding:8px 5px;margin:0}blockquote p{font-size:0.4rem}}@media (min-width: 630px){nav div ul li.active,nav div ul li[active]{border:1px solid #00c853;border-bottom:none;border-top:none}nav header label{display:none}}section[container]{max-width:38em;margin:auto;padding:5px}h1{font-size:2.35em}h2{font-size:2em}h3{font-size:1.75em}h4{font-size:1.5em}h5{font-size:1.25em}h6{font-size:1em}a{color:#00c853}a:hover{color:#212121}mark{background-color:#00c853}code{font-family:monospace;background-color:#bdbdbd;padding-left:5px;padding-right:5px}blockquote{border-left:4px solid #00c853;padding:8px 10px;width:100%}blockquote p{font-style:italic;font-size:1.1rem}blockquote footer::before{content:"\2014 \00A0"}blockquote footer cite{font-style:italic;color:#bdbdbd}pre{background:#eee;overflow-x:auto;text-align:left;padding:5px}pre code{display:block;padding:0 10px;background:transparent}table{display:table;padding:5px;border-collapse:collapse}table thead,table tbody{text-align:left}table tr th,table tr td{padding:5px 10px;border-bottom:1px solid #00c853}div[overflow]{overflow-x:auto;max-width:100vw}div[overflow] ::-webkit-scrollbar{height:0}img{max-width:100%;border-radius:5px}form div{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:10px 5px}form div p{margin:0px}form input,form select{font-size:1rem;padding:5px;border:1px solid #bdbdbd;color:#212121}form input:active,form input:focus,form select:active,form select:focus{outline-color:#00c853}form input[type="submit"]{padding:10px;background-color:#00c853;color:#000;border-radius:5px;border:none;cursor:pointer}form input[type="submit"]:active,form input[type="submit"]:focus{outline:none}form input[type="submit"]:active{background-color:#212121;color:#00c853}form input[type="submit"]:disabled{background:#bdbdbd;cursor:not-allowed}form input[type="submit"][secondary]{background-color:#212121;color:#00c853}form input[type="submit"][secondary]:active{background-color:#00c853;color:initial}textarea{color:#212121;width:-webkit-fill-available;font-size:1rem;padding:5px}textarea:active,textarea:focus{outline-color:#00c853}button{padding:10px;background-color:#00c853;color:#000;border-radius:5px;border:none;cursor:pointer}button:active,button:focus{outline:none}button:active{background-color:#212121;color:#00c853}button:disabled{background:#bdbdbd;cursor:not-allowed}button[secondary]{background-color:#212121;color:#00c853}button[secondary]:active{background-color:#00c853;color:initial}body>footer{background-color:#212121;position:relative;bottom:0;width:100%;padding:5px;color:#fff}
2 | /*# sourceMappingURL=brightlight-green.css.map */
--------------------------------------------------------------------------------
/tests/report.lisp:
--------------------------------------------------------------------------------
1 | (defpackage #:breeze.test.report
2 | (:documentation "Tests for the package breeze.report")
3 | (:use #:cl #:breeze.report)
4 | ;; importing non-exported symbols, for testing
5 | (:import-from #:breeze.report
6 | #:render-files
7 | #:pages)
8 | (:import-from #:parachute
9 | #:define-test
10 | #:define-test+run
11 | #:is
12 | #:true
13 | #:false
14 | #:of-type
15 | #:finish
16 | #:fail))
17 |
18 | (in-package #:breeze.test.report)
19 |
20 | (define-test+run report
21 | (fail (make-instance 'report))
22 | (finish (make-instance 'report :output-dir "out/")))
23 |
24 | (define-test+run slug
25 | :dependencies (report)
26 | (is string= "nil" (slug nil nil))
27 | (is string= "" (slug nil ""))
28 | (is string= "" (slug nil " "))
29 | (is string= "" (slug nil " "))
30 | (is string= "" (slug nil (string #\newline)))
31 | (is string= "233csmth3e" (slug nil "#"))
32 | (is string= "a--path--to--some--file.txt" (slug nil "a/path/to/some/file.txt")))
33 |
34 | (define-test+run url-to
35 | :dependencies (slug)
36 | (is string= "nil" (url-to nil nil nil))
37 | (is string= "tests--report.lisp"
38 | (url-to nil "tests/report.lisp" nil))
39 | (is string= "foo-tests--report.lisp"
40 | (url-to nil "tests/report.lisp" :foo))
41 | (is string= "listing-tests--report.lisp.html"
42 | (url-to nil "tests/report.lisp" :listing)))
43 |
44 | (define-test+run pathname-to
45 | :dependencies (slug)
46 | (is equalp #P"out/listing-tests--report.lisp.html"
47 | (pathname-to (make-instance 'report :output-dir "out/")
48 | "tests/report.lisp" :listing)))
49 |
50 | (define-test+run link-to-id
51 | (is string= "42" (link-to-id nil "42" "life")))
52 |
53 | (define-test+run link-to-file
54 | :dependencies (pathname-to)
55 | (is string=
56 | "asdf"
57 | (link-to-file
58 | (make-instance 'report :output-dir "out/") "asdf")))
59 |
60 | (define-test+run link-to-page
61 | :dependencies (pathname-to)
62 | (is string=
63 | "asdf — untitled page 42"
64 | (link-to-page
65 | (make-instance 'report :output-dir "out/") "asdf" 42))
66 | (is string=
67 | "Not untitled!"
68 | (link-to-page
69 | (make-instance 'report :output-dir "out/") "asdf" 42
70 | "Not untitled!")))
71 |
72 | (define-test+run paragraphs
73 | (is equalp `("asd" ,(format nil "qwe~%ert") "jkl")
74 | (paragraphs
75 | (format nil "asd~5%qwe~%ert~2%jkl"))))
76 |
77 | (define-test+run remove-leading-semicolons
78 | (is string= "" (remove-leading-semicolons "; ; ; ")))
79 |
80 | (define-test+run escape-html
81 | (is string= "
" (escape-html "
"))
82 | (is string= "<a>" (escape-html ""))
83 | (is string= "" (escape-html ""))
84 | (is string= "=> (#<ASDF/SYSTEM:SYSTEM \"breeze/test\"> #<ASDF/SYSTEM:SYSTEM \"breeze/config\">)"
85 | (escape-html "=> (# #)")))
86 |
87 |
88 | ;;; TODO I want to generate example pages, especially for commands
89 |
90 | ;; TODO extract string-utils's around function's tests
91 | ;; TODO eval-node would be useful!
92 |
93 | ;; Here I render a buffer with syntax errors into an html file
94 | ;; TODO extract function "render-buffer" out of "render-lisp-flle"
95 | #++
96 | (let* ((root (merge-pathnames
97 | "docs/"
98 | (asdf:system-source-directory 'breeze)))
99 | (content "(
100 | #|
101 | #r")
102 | (filename "example.lisp")
103 | (state (breeze.parser:parse content))
104 | (pages (pages state)))
105 | (;; with-output-to-string (out)
106 | ;; alexandria:with-output-to-file (out (merge-pathnames "example.lisp.html" root))
107 | progn
108 | (render-files
109 | (make-instance 'report :output-dir root)
110 | (list (list filename state pages))
111 | (merge-pathnames "example.lisp.html" root))))
112 |
--------------------------------------------------------------------------------