{"id":19293953,"url":"https://github.com/igrishaev/bogus","last_synced_at":"2025-08-23T06:11:15.762Z","repository":{"id":43138420,"uuid":"469475071","full_name":"igrishaev/bogus","owner":"igrishaev","description":"A simple GUI debugger for Clojure","archived":false,"fork":false,"pushed_at":"2025-07-30T12:15:49.000Z","size":978,"stargazers_count":26,"open_issues_count":0,"forks_count":4,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-19T12:27:17.690Z","etag":null,"topics":["clojure","debug","repl","swing"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/igrishaev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-03-13T19:36:19.000Z","updated_at":"2025-07-30T12:15:52.000Z","dependencies_parsed_at":"2024-11-09T22:48:28.560Z","dependency_job_id":null,"html_url":"https://github.com/igrishaev/bogus","commit_stats":{"total_commits":66,"total_committers":2,"mean_commits":33.0,"dds":"0.015151515151515138","last_synced_commit":"ae5c333ef10d8925d5236e9d7b19cd6c80f62cfd"},"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/igrishaev/bogus","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igrishaev%2Fbogus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igrishaev%2Fbogus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igrishaev%2Fbogus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igrishaev%2Fbogus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/igrishaev","download_url":"https://codeload.github.com/igrishaev/bogus/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igrishaev%2Fbogus/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271745695,"owners_count":24813516,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-23T02:00:09.327Z","response_time":69,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojure","debug","repl","swing"],"created_at":"2024-11-09T22:36:40.325Z","updated_at":"2025-08-23T06:11:15.750Z","avatar_url":"https://github.com/igrishaev.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Bogus\n\n[NIH]: https://en.wikipedia.org/wiki/Not_invented_here\n\nA small, GUI-powered, [NIH][NIH]-reasoned debugger for Clojure.\n\n## Table of Contents\n\n\u003c!-- toc --\u003e\n\n- [Installation](#installation)\n- [Usage](#usage)\n- [Conditional break](#conditional-break)\n- [How it works](#how-it-works)\n- [Why](#why)\n- [An issue with remote REPL by SSH on MacOS](#an-issue-with-remote-repl-by-ssh-on-macos)\n- [Other](#other)\n\n\u003c!-- tocstop --\u003e\n\n## Installation\n\nLein:\n\n```clojure\n[com.github.igrishaev/bogus \"0.1.11\"]\n```\n\nDeps.edn\n\n```clojure\n{com.github.igrishaev/bogus {:mvn/version \"0.1.11\"}}\n```\n\nThe best way to use Bogus is to setup it locally in your `profiles.clj` file:\n\n```clojure\n;; ~/.lein/profiles.clj\n\n{:user\n {:dependencies [[com.github.igrishaev/bogus \"0.1.11\"]]\n  :injections [(require 'bogus.core)]}}\n```\n\n## Usage\n\nOnce you have the dependency added and the `bogus.core` namespace imported,\nplace one of these two forms into your code:\n\n```clojure\n(bogus.core/debug)\n;; or\n#bogus\n;; or\n#bg/debug ;; deprecated tag\n```\n\nFor example:\n\n```clojure\n(defn do-some-action []\n  (let [a 1\n        b 2\n        c (+ a b)]\n    #bogus ;; or (bogus.core/debug)\n    (+ a b c)))\n```\n\nNow run the function, and you'll see the UI:\n\n![](img/screen1.png)\n\nThe UI window blocks execution of the code. In the example above, you'll hang\nright before executing the next `(+ a b c)` form. Close the window to continue\nexecution of the code.\n\nThe UI consists from three parts: the input area, the output, and the log. Type\nany Clojure-friendly form in the input textarea and press \"Eval\". The result\nwill take place in the output textarea. You can copy it from there to your\neditor.\n\nThe input can take many Clojure forms at once. They are executed as follows:\n\n```clojure\n(eval '(do (form1) (form2) ...))\n```\n\nso you'll get the result of the last one.\n\nIf you mark some text in the input with selection, only this fragment of code\nwill be executed.\n\n![](img/screen2.png)\n\nIn the input code, you can use any local variables as they're global ones. In the\nexample above we've executed the `(+ a b c)` form referencing local `a`, `b`,\nand `c` from the `let` clause.\n\nIn the bottom of the form, there is a widget with pretty-printed local\nvariables.\n\nYou can have several debug breakpoints, for example:\n\n```clojure\n(defn do-some-action []\n  (let [a 1\n        b 2\n        c (+ a b)]\n    #bogus\n    (+ a b c)\n    (let [d 9]\n      #bogus\n      (* a b c d))))\n```\n\nThe first debug session will take the `a`, `b`, and `c` locals, whereas the\nsecond one will have `a`, `b`, `c`, and `d`. You won't proceed to the second\nsession until you close the first window.\n\nBogus debugger works in tests, in nREPL, in ordinary REPL, in Manifold, in the\nfutures as well. Here is a small demo of having two debug sessions in parallel\nthreads:\n\n```clojure\n(let [f1 (future\n           (let [a 1]\n             #bogus\n             (+ a 1)))\n      f2 (future\n           (let [b 2]\n             #bogus\n             (+ b 1)))]\n  (+ @f1 @f2))\n```\n\nIf you run this code, you'll get the two windows each having their own locals:\n\n![](img/screen5.png)\n\nThe second session has the `f1` local var captured from the `let` clause. If you\ntry to `deref` it, the entire REPL will hang due to the mutual blocking, so be\ncareful when dealing with parallel debugging.\n\n## Conditional break\n\nThe breakpoints might carry a condition which is useful in cycles. Pass the\n`:when` clause to the form's metadata. The debugger will only pop up if the\nclause gets evaluated to true:\n\n```clojure\n(doseq [x (range 9)]\n  #bogus ^{:when (= x 3)}\n  (println x))\n```\n\nThe idea is stolen from the nREPL/Cider debugger.\n\n## How it works\n\nThe under-hood of Bogus is simple: it captures the local vars from the `\u0026env`\nmapping available in a macro. Then there is a special `eval+` function that\nshifts the locals inside the form it's going to evaluate:\n\n```\n;; locals\n'{a 1 b 2}\n\n;; form\n'(+ a b)\n\n;; result\n(binding [*locals* locals]\n  (eval '(let [a (get *locals* 'a)\n               b (get *locals* 'b)]\n           (+ a b))))\n```\n\nIn the initial version of Bogus, there were some dirty tricks with the global\nvariables using `intern` and `resolve`. But nowadays they're gone.\n\n## Why\n\nThe idea of making my own debugger came into my mind while I was writing a new\nchapter about nREPL and code evaluation. Although Cider provides much more\npowerful tools for debugging, I still believe Bogus might be useful for someone\nnew to Clojure. The main benefit of Bogus is, it doesn't require the whole nREPL\nstuff and Emacs. One can use it with any editor or environment. After all,\ntinkering with Bogus gave me some good material for the book.\n\n## An issue with remote REPL by SSH on MacOS\n\nI had an interesting setup in one project which prevented me to use Bogus at\nfirst. I connected to a local laptop (connected by Ethernet) by SSH and ran\nrepl, then tried to use Bogus. I was also connected to the laptop by Screen\nSharing and expected the Bogus window to pop up. But it didn't work as the JVM\nthought it was running in headless mode:\n\n~~~\nException in thread \"main\" java.awt.HeadlessException:\nThe application is not running in a desktop session,\nbut this program performed an operation which requires it.\n~~~\n\nIt turned out, there are several ways to fix the issue, but the simplest one is\nto pass a special `AWT_FORCE_HEADFUL` variable when running nREPL:\n\n~~~bash\nAWT_FORCE_HEADFUL=true clj -M:nrepl ...\n# or\nAWT_FORCE_HEADFUL=true lein repl ...\n~~~\n\n[aahlenst]: https://aahlenst.dev/blog/starting-java-guis-on-macos-over-ssh/\n\nWith that var set, the Bogus widget has successfully appeared inside Screen\nSharing window. You're welcome to read [this blog post][aahlenst] for more\ndetails.\n\n## Other\n\n~~~\n©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©\nIvan Grishaev, 2022. © UNLICENSE ©\n©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©\n~~~\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Figrishaev%2Fbogus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Figrishaev%2Fbogus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Figrishaev%2Fbogus/lists"}