[philoskim/debux "0.5.9"]
dbg
examples
dbgn
examples
dbg
and dbgn
cljs-devtools
dbg-prn
: Debugging the macros for clojurescript at the macro-expansion time.dbg-last
: Debugging inside the thread-last macro ->>
break
examples
debux.el
for Emacs CIDER user
Debux is a simple but useful library for debugging Clojure and ClojureScript. I wrote this library to debug my own Clojure(Script) code and to analyze other developer’s one.
Clojars repository: https://clojars.org/philoskim/debux
clojure 1.8.0 or later
clojurescript 1.10.238 or later
In development, use the philoskim/debux
library. When you use debux macros and
functions from this library, it will emit debugging messages to the REPL window or the
Chrome DevTools' console.
In production, use the philoskim/debux-stubs
(https://github.com/philoskim/debux-stubs) library. This has the same public API as
philoskim/debux
but the macros simply expand only to the given original form itself.
With this setup
in production, your use of debux macros will have zero run-time and compile-time cost,
in development, debux macros are able to be turned off too via the set-debug-mode!
function.
Never use philoskim/debux library in production because it will impose too much
overhead on the peformance, especially in using dbgn and clogn , even though
(set-debug-mode! false) is run.
|
First, please be sure to read the "Two libraries" section immediately above for background.
To include debux
in your project for development, simply add the following to your
project.clj development dependencies:
[philoskim/debux "0.5.9"]
and this to your production dependencies (make sure they are production only):
[philoskim/debux-stubs "0.5.9"]
You can see All change logs since v0.3.0 here. |
v0.5.9
The cljs-devtools support added. See the detalis here.
v0.5.8
The dbg-last
macro added. See the detalis here.
v0.5.7
:print
option added to dbg/clog
. See the detalis here.
v0.5.6
Issue #12 solved.
v0.5.5
:skip-form-itself-type
is split into the :skip-all-args-type
and
:skip-form-itself-type
.
In Clojure, the following line should be included in your file.
(use 'debux.core)
In ClojureScript, the following (:require ...)
line has to be included in your
file.
(ns example.core
(:require [debux.cs.core :as d :refer-macros [clog clogn dbg dbgn break]]))
dbg | dbgn | clog | clogn | break | |
---|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Legend: O
(supported), X
(not supported)
dbg
/dbgn
can be used in Clojure REPL.
dbg
/dbgn
can be used in ClojureScript REPL like
weasel or
figwheel.
Refer to Usage in ClojureScript on Browser REPL for Browser REPL usage.
dbg
/dbgn
, clog
/clogn
and break
can be used in the browser console window
like Chrome DevTools.
I recommend that you should use clog /clogn instead of dbg /dbgn in the browser
console window, because clog /clogn uses the console.log function of browser’s
developer tools to style the form. You can see its effect here.
|
dbg
examplesYou can see every example source code of this document in example folder. |
The features of clog are almost the same as those of dbg .
|
The macro dbg
prints an original form and pretty-prints the evaluated value on the REPL
window. Then it returns the value without interrupting code evaluation.
(* 2 (dbg (+ 10 20)))
; => 60
dbg: (+ 10 20) => | 30
Sometimes you need to see several forms evaluated. To do so, a literal vector form can be used like this.
(defn my-fun
[a {:keys [b c d] :or {d 10 b 20 c 30}} [e f g & h]]
(dbg [a b c d e f g h]))
(my-fun (take 5 (range)) {:c 50 :d 100} ["a" "b" "c" "d" "e"])
; => [(0 1 2 3 4) 20 50 100 "a" "b" "c" ("d" "e")]
dbg: [a b c d e f g h] => | [(0 1 2 3 4) 20 50 100 "a" "b" "c" ("d" "e")]
->
or ->>
When debugging the thread-first macro ->
or thread-last macro ->>
, dbg
prints every expression in the thread macros.
This is an example of thread-first macro ->
.
(dbg (-> "a b c d"
.toUpperCase
(.replace "A" "X")
(.split " ")
first))
;=> "X"
dbg: (-> "a b c d" .toUpperCase (.replace "A" "X") (.split " ") first) => | "a b c d" => | "a b c d" | .toUpperCase => | "A B C D" | (.replace "A" "X") => | "X B C D" | (.split " ") => | ["X", "B", "C", "D"] | first => | "X"
Another example.
(def person
{:name "Mark Volkmann"
:address {:street "644 Glen Summit"
:city "St. Charles"
:state "Missouri"
:zip 63304}
:employer {:name "Object Computing, Inc."
:address {:street "12140 Woodcrest Dr."
:city "Creve Coeur"
:state "Missouri"
:zip 63141}}})
(dbg (-> person :employer :address :city))
; => "Creve Coeur"
dbg: (-> person :employer :address :city) => | person => | {:name "Mark Volkmann", | :address | {:street "644 Glen Summit", | :city "St. Charles", | :state "Missouri", | :zip 63304}, | :employer | {:name "Object Computing, Inc.", | :address | {:street "12140 Woodcrest Dr.", | :city "Creve Coeur", | :state "Missouri", | :zip 63141}}} | :employer => | {:name "Object Computing, Inc.", | :address | {:street "12140 Woodcrest Dr.", | :city "Creve Coeur", | :state "Missouri", | :zip 63141}} | :address => | {:street "12140 Woodcrest Dr.", | :city "Creve Coeur", | :state "Missouri", | :zip 63141} | :city => | "Creve Coeur"
This is an example of thread-last macro ->>
.
(def c 5)
(dbg (->> c (+ 3) (/ 2) (- 1)))
; => 3/4
dbg: (->> c (+ 3) (/ 2) (- 1)) => | c => | 5 | (+ 3) => | 8 | (/ 2) => | 1/4 | (- 1) => | 3/4
If you want to debug one of the expressions in the thread macro ->
or
->>
, don’t do it like this.
(-> {:a [1 2]}
(dbg (get :a))
(conj 3))
; => java.lang.IllegalArgumentException
; Don't know how to create ISeq from: java.lang.Long
You will have some exception. Instead, do it like this.
(-> {:a [1 2]}
(get :a)
dbg
(conj 3))
; => [1 2 3]
dbg: (get {:a [1 2]} :a) => | [1 2]
Another example.
(->> [-1 0 1 2]
(filter pos?)
(map inc)
dbg
(map str))
; => ("2" "3")
dbg: (map inc (filter pos? [-1 0 1 2])) => | (2 3)
See more examples here.
let
or comp
formWhen debugging let
form,
(dbg (let [a (take 5 (range))
{:keys [b c d] :or {d 10 b 20 c 30}} {:c 50 :d 100}
[e f g & h] ["a" "b" "c" "d" "e"]]
[a b c d e f g h]))
; => [(0 1 2 3 4) 20 50 100 "a" "b" "c" ("d" "e")]
each binding will be printed like this.
dbg: (let [a (take 5 (range)) {:keys [b c d], :or {d 10, b 20, c 30}} {:c 5 ... => | a => | (0 1 2 3 4) | {:keys [b c d], :or {d 10, b 20, c 30}} => | {:keys [20 50 100], :or {100 10, 20 20, 50 30}} | [e f g & h] => | ["a" "b" "c" & ("d" "e")]
When debugging comp
form,
(def c (dbg (comp inc inc +)))
(c 10 20)
; => 32
the result of each function will be printed like this.
dbg: (comp inc inc +) => | + => | 30 | inc => | 31 | inc => | 32
dbgn
examples
The features of clogn are almost the same as those of dbgn .
|
The macro dbgn
is for Clojure/CloujureScript REPL and the macro clogn
is for
ClojureScript browser console only. The appended n to these two macro names means
Nested forms. You can debug every nested form without interrupting code
evaluations. This feature is very useful, especially when you analyze other developer’s
source code.
(dbgn (defn foo [a b & [c]]
(if c
(* a b c)
(* a b 100))))
(foo 2 3)
; => 600
(foo 2 3 10)
; => 60
dbgn: (defn foo [a b & [c]] (if c (* a b c) (* a b 100))) => | c => | nil | a => | 2 | b => | 3 | (* a b 100) => | 600 | (if c (* a b c) (* a b 100)) => | 600 | c => | 10 | a => | 2 | b => | 3 | (* a b c) => | 60 | (if c (* a b c) (* a b 100)) => | 60
dbgn
/clogn
don’t have any problem in handling functions.
dbgn
/clogn
, however, can have some problem in case of macros and special forms.
Some macros such as when
don’t have any problem when used in dbgn
/clogn
.
Other macros such as defn
which has a binding vector can have problem because they
have binding symbols which must not be evaluated in dbgn
/clogn
macros. In case of
special forms and those macros in clojure.core
namespace, degn
/clogn
can handle
them appropriately.
In some cases, Clojure developers can write their own macros which dbgn
/clogn
cannot
handle appporiately. So I categorized those macros in clojure.core
namespace as the
following table and you can register your own macros according to the macro types in
the table. I will explain it in How to register your own macros in using dbgn
/clogn
.
Macro types | Macros in clojure.core and special forms |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
|
|
|
|
| |
|
|
|
|
|
|
|
|
:def-type
exampleThis type of macros have the first argument which must not be evaluated and can have
optional doc-string
argument.
(dbgn (def my-function "my-function doc string"
(fn [x] (* x x x))))
(my-function 10)
; => 1000
dbgn: (def my-function "my-function doc string" (fn [x] (* x x x))) => | (fn [x] (* x x x)) => | #function[example.core/eval24554/result--24229--auto----24555] | (def my-function "my-function doc string" (fn [x] (* x x x))) => | #'example.core/my-function | x => | 10 | (* x x x) => | 1000
:defn-type
exampleThis type of macros have the binding vector argument which must not be evaluated and can
have optional doc-string
, attr-map
, or prepost-map
arguments.
(dbgn (defn add
"add doc string"
[a b]
(+ a b)))
(add 10 20)
; => 30
dbgn: (defn add "add doc-string" [a b] (+ a b)) => | a => | 10 | b => | 20 | (+ a b) => | 30
You can debug multiple-arity functions as well.
(dbgn (defn my-add
"my-add doc string"
([] 0)
([a] a)
([a b] (+ a b))
([a b & more] (apply + a b more))))
; The function body in this case doesn't have any symbol to evaluate,
; so no output will be printed.
(my-add)
; => 0
(my-add 10)
; => 10
(my-add 10 20)
; => 30
(my-add 10 20 30 40)
; => 100
dbgn: (defn my-add "my-add doc string" ([] 0) ([a] a) ([a b] (+ a b)) ([a b ... => | a => | 10 | a => | 10 | b => | 20 | (+ a b) => | 30 | + => | #function[clojure.core/+] | a => | 10 | b => | 20 | more => | (30 40) | (apply + a b more) => | 100
You can have multiple dbgn
/clogn
s.
(dbgn (defn calc1 [a1 a2] (+ a1 a2)))
(dbgn (defn calc2 [s1 s2] (- 100 (calc1 s1 s2))))
(dbgn (defn calc3 [m1 m2] (* 10 (calc2 m1 m2))))
(calc3 2 5)
; => 760
dbgn: (defn calc1 [a1 a2] (+ a1 a2)) => dbgn: (defn calc2 [s1 s2] (- 100 (calc1 s1 s2))) => dbgn: (defn calc3 [m1 m2] (* 10 (calc2 m1 m2))) => | m1 => | 2 | m2 => | 5 || s1 => || 2 || s2 => || 5 ||| a1 => ||| 2 ||| a2 => ||| 5 ||| (+ a1 a2) => ||| 7 || (calc1 s1 s2) => || 7 || (- 100 (calc1 s1 s2)) => || 93 | (calc2 m1 m2) => | 93 | (* 10 (calc2 m1 m2)) => | 930
:fn-type
exampleThis type of macros have the binding vector argument which must not be evaluated and can
have optional function name. So it is a little different from :defn-type
macros.
(dbgn (reduce (fn [acc i] (+ acc i)) 0 [1 5 9]))
; => 15
dbgn: (reduce (fn [acc i] (+ acc i)) 0 [1 5 9]) => | (fn [acc i] (+ acc i)) => | #function[example.core/eval25034/result--24229--auto----25035] | [1 5 9] => | [1 5 9] || acc => || 0 || i => || 1 || (+ acc i) => || 1 || acc => || 1 || i => || 5 || (+ acc i) => || 6 || acc => || 6 || i => || 9 || (+ acc i) => || 15 | (reduce (fn [acc i] (clojure.core/binding [debux.common.util/*indent-l ... => | 15
(dbgn (map #(* % 10) [1 5 9]))
; => (10 50 90)
dbgn: (map (fn* [p1__13193#] (* p1__13193# 10)) [1 5 9]) => | (fn* [p1__13193#] (try (clojure.core/reset! (:evals +debux-dbg-opts+) ... => | #object[example.dbgn$eval13194$result__4709__auto____13195 0x1b58788a "example.dbgn$eval13194$result__4709__auto____13195@1b58788a"] | [1 5 9] => | [1 5 9] || p1__13583# => || 1 || (* p1__13583# 10) => || 10 || p1__13583# => || 5 || (* p1__13583# 10) => || 50 || p1__13583# => || 9 || (* p1__13583# 10) => || 90 | (map (fn* [p1__13583#] (clojure.core/binding [debux.common.util/*inden ... => | (10 50 90)
:let-type
exampleThis type of macros have the binding vector argument which must not be evaluated.
(dbgn (let [a (+ 1 2)
[b c] [(+ a 10) (* a 2)]]
(- (+ a b) c)))
; => 10
dbgn: (let [a (+ 1 2) [b c] [(+ a 10) (* a 2)]] (- (+ a b) c)) => | (+ 1 2) => | 3 | a => | 3 | (+ a 10) => | 13 | (* a 2) => | 6 | [(+ a 10) (* a 2)] => | [13 6] | b => | 13 | (+ a b) => | 16 | c => | 6 | (- (+ a b) c) => | 10 | (let [a (+ 1 2) [b c] [(+ a 10) (* a 2)]] (- (+ a b) c)) => | 10
:if-let-type
exampleThis type of macros are a little different from :let-type
macros in that they need only
one or two forms in their bodies.
(def a* 10)
(dbgn (if-let [s a*]
(+ s 100)
false))
; => 110
dbgn: (if-let [s a*] (+ s 100) false) => | a* => | 10 | s => | 10 | (+ s 100) => | 110 | (if-let [s a*] (+ s 100) false) => | 110
:letfn-type
exampleThis type of macro has the special binding vector syntax which is a bit different from
:fn-type
.
(dbgn (letfn [(twice [x]
(* x 2))
(six-times [y]
(* (twice y) 3))]
(six-times 15)))
; => 90
dbgn: (letfn [(twice [x] (* x 2)) (six-times [y] (* (twice y) 3))] (six-time ... => | y => | 15 | x => | 15 | (* x 2) => | 30 | (twice y) => | 30 | (* (twice y) 3) => | 90 | (six-times 15) => | 90 | (letfn [(twice [x] (* x 2)) (six-times [y] (* (twice y) 3))] (six-time ... => | 90
:loop-type
exampleThis type of macro is similiar to :let-type
but has a significant difference because the recur
has to be placed at the tail positon with the loop
form. So it needs a special handling in the implementation of dbgn
/clogn
. Refer to Limited support for the form including recur
for details.
:for-type
exampleThis type of macros have a little different syntax from :let-type
macros, because it
can have :let
, :when
, or :while
clause.
(dbgn (for [x [0 1 2 3 4 5]
:let [y (* x 3)]
:when (even? y)]
y))
; => (0 6 12)
dbgn: (for [x [0 1 2 3 4 5] :let [y (* x 3)] :when (even? y)] y) => | [0 1 2 3 4 5] => | [0 1 2 3 4 5] | x => | 0 | (* x 3) => | 0 | y => | 0 | (even? y) => | true | x => | 1 | (* x 3) => | 3 | y => | 3 | (even? y) => | false | x => | 2 | (* x 3) => | 6 | y => | 6 | (even? y) => | true | x => | 3 | (* x 3) => | 9 | y => | 9 | (even? y) => | false | x => | 4 | (* x 3) => | 12 | y => | 12 | (even? y) => | true | x => | 5 | (* x 3) => | 15 | y => | 15 | (even? y) => | false | (for [x [0 1 2 3 4 5] :let [y (* x 3)] :when (even? y)] (do (debux.com ... => | (0 6 12)
:case-type
exampleThis type of macro has the special syntax. Refer to the details here.
(dbgn (let [mystr "hello"]
(case mystr
"" 0
"hello" (count mystr))))
; => 5
dbgn: (let [mystr "hello"] (case mystr 0 hello (count mystr))) => | mystr => | "hello" | (count mystr) => | 5 | (case mystr "" 0 "hello" (count mystr)) => | 5 | (let [mystr "hello"] (case mystr "" 0 "hello" (count mystr))) => | 5
(dbgn (case 'a
(x y z) "x, y, or z"
"default"))
; => "default"
dbgn: (case (quote a) (x y z) "x, y, or z" "default") => | (case (quote a) (x y z) "x, y, or z" "default") => | "default"
:skip-arg-1-type
exampleThis type of macros have the first argument which must not be evaluated. So dbgn
/clogn
internally skips the evaluation of this argument.
(dbgn (with-precision 10 (/ 1M 6)))
; => 0.1666666667M
dbgn: (with-precision 10 (/ 1M 6)) => | (/ 1M 6) => | 0.1666666667M | (with-precision 10 (/ 1M 6)) => | 0.1666666667M
:skip-arg-2-type
exampleThis type of macros have the second argument which must not be evaluated. So dbgn
/clogn
internally skips the evaluation of this argument.
(dbgn (as-> 0 n
(inc n)
(inc n)))
; => 2
dbgn: (as-> 0 n (inc n) (inc n)) => | n => | 0 | (inc n) => | 1 | n => | 1 | (inc n) => | 2 | (as-> 0 n (inc n) (inc n)) => | 2
:skip-arg-1-2-type
exampleThis type of macros have the first and second arguments which must not be evaluated. So
dbgn
/clogn
internally skips the evaluation of those arguments. However, I can’t find this
type of macros in clojure.core
namespace but add this type for completeness and the
future possibilities of this type of macros.
:skip-arg-1-3-type
exampleThis type of macros have the first and third arguments which must not be evaluated. So
dbgn
/clogn
internally skips the evaluation of those arguments.
(defmulti greeting
(fn [x] (:language x)))
(dbgn (defmethod greeting :english [map]
(str "English greeting: " (:greeting map))))
(dbgn (defmethod greeting :french [map]
(str "French greeting: " (:greeting map))))
(def english-map {:language :english :greeting "Hello!"})
(def french-map {:language :french :greeting "Bonjour!"})
(greeting english-map)
(greeting french-map)
dbgn: (defmethod greeting :english [map] (str "English greeting: " (:greetin ... => | (defmethod greeting :english [map] (str "English greeting: " (:greeting map))) => | #multifn[greeting 0x1c28c1cc] dbgn: (defmethod greeting :french [map] (str "French greeting: " (:greeting ... => | (defmethod greeting :french [map] (str "English greeting: " (:greeting map))) => | #multifn[greeting 0x1c28c1cc] | map => | {:language :english, :greeting "Hello!"} | (:greeting map) => | "Hello!" | (str "English greeting: " (:greeting map)) => | "English greeting: Hello!" | map => | {:language :french, :greeting "Bonjour!"} | (:greeting map) => | "Bonjour!" | (str "French greeting: " (:greeting map)) => | "French greeting: Bonjour!"
:skip-arg-2-3-type
exampleThis type of macros have the second and third arguments which must not be evaluated. So
dbgn
/clogn
internally skips the evaluation of those arguments.
(let [xs (float-array [1 2 3])]
(dbgn (areduce xs i ret (float 0)
(+ ret (aget xs i)))))
; => 6.0
dbgn: (areduce xs i ret (float 0) (+ ret (aget xs i))) => | xs => | [1.0, 2.0, 3.0] | (float 0) => | 0.0 | ret => | 0.0 | i => | 0 | (aget xs i) => | 1.0 | (+ ret (aget xs i)) => | 1.0 | ret => | 1.0 | i => | 1 | (aget xs i) => | 2.0 | (+ ret (aget xs i)) => | 3.0 | ret => | 3.0 | i => | 2 | (aget xs i) => | 3.0 | (+ ret (aget xs i)) => | 6.0 | (areduce xs i ret (float 0) (+ ret (aget xs i))) => | 6.0
:skip-arg-1-2-3-type
exampleThis type of macros have the first, second and third arguments which must not be evaluated. So
dbgn
/clogn
internally skips the evaluation of those arguments. However, I can’t find this
type of macros in clojure.core
namespace but add this type for completeness and the
future possibilities of this type of macros.
:skip-all-args-type
exampleThis type of macros ignores all the arguments and prints the outermost form and its result.
(dbgn (defmacro unless [pred a b]
`(if (not ~pred) ~a ~b)))
dbgn: (defmacro unless [pred a b] (clojure.core/seq (clojure.core/concat (cl ... => | (defmacro unless [pred a b] (clojure.core/seq (clojure.core/concat (cl ... => | #'user/unless
:skip-form-itself-type
exampleThis type of macros ignores the form itself and prints nothing.
(dbgn (try
(/ 1 0)
(catch ArithmeticException e (str "caught exception: " (.getMessage e)))))
dbgn: (try (/ 1 0) (catch ArithmeticException e (str "caught exception: " (. ... => | (try (/ 1 0) (catch ArithmeticException e (str "caught exception: " (. ... => | "caught exception: Divide by zero"
The evaluated resuts of the catch form are not printed in the above example.
|
:expand-type
exampleThis type of macros will be expanded and then the output will be printed.
(dbgn (-> "a b c d"
.toUpperCase
(.replace "A" "X")
(.split " ")
first))
; => "X"
dbgn: (-> "a b c d" .toUpperCase (.replace "A" "X") (.split " ") first) => | (.toUpperCase "a b c d") => | "A B C D" | (.replace (.toUpperCase "a b c d") "A" "X") => | "X B C D" | (.split (.replace (.toUpperCase "a b c d") "A" "X") " ") => | ["X", "B", "C", "D"] | (first (.split (.replace (.toUpperCase "a b c d") "A" "X") " ")) => | "X"
(dbgn (.. "fooBAR" toLowerCase (contains "ooba")))
; => true
dbgn: (.. "fooBAR" toLowerCase (contains "ooba")) => | (. "fooBAR" toLowerCase) => | "foobar" | (. (. "fooBAR" toLowerCase) (contains "ooba")) => | true
(let [x 1 y 2]
(dbgn (cond-> []
(odd? x) (conj "x is odd")
(zero? (rem y 3)) (conj "y is divisible by 3")
(even? y) (conj "y is even"))))
; => ["x is odd" "y is even"]
dbgn: (cond-> [] (odd? x) (conj "x is odd") (zero? (rem y 3)) (conj "y is di ... => | [] => | [] | x => | 1 | (odd? x) => | true | G__14051 => | [] | (conj G__14051 "x is odd") => | ["x is odd"] | (if (odd? x) (conj G__14051 "x is odd") G__14051) => | ["x is odd"] | y => | 2 | (rem y 3) => | 2 | (zero? (rem y 3)) => | false | G__14051 => | ["x is odd"] | (if (zero? (rem y 3)) (conj G__14051 "y is divisible by 3") G__14051) => | ["x is odd"] | (even? y) => | true | (conj G__14051 "y is even") => | ["x is odd" "y is even"] | (if (even? y) (conj G__14051 "y is even") G__14051) => | ["x is odd" "y is even"] | (clojure.core/let [G__14051 [] G__14051 (if (odd? x) (conj G__14051 "x ... => | ["x is odd" "y is even"]
:dot-type
example(dbgn (. (java.util.Date.) getMonth))
; => 5
dbgn: (. (java.util.Date.) getMonth) => | (java.util.Date.) => | #inst "2017-06-27T08:04:46.480-00:00" | (. (java.util.Date.) getMonth) => | 5
recur
dbgn | clogn | |
---|---|---|
|
|
|
|
|
|
Legend: O
(supported), △
(limitedly supported)
loop
~ recur
You can see the evaluated results of the form which incldues loop
~ recur
by using
dbgn
in Clojure and ClojureScript.
(dbgn (loop [acc 1 n 3]
(if (zero? n)
acc
(recur (* acc n) (dec n)))))
; => 6
dbgn: (loop [acc 1 n 3] (if (zero? n) acc (recur (* acc n) (dec n)))) => | n => | 3 | (zero? n) => | false | acc => | 1 | (* acc n) => | 3 | (dec n) => | 2 | n => | 2 | acc => | 3 | (* acc n) => | 6 | (dec n) => | 1 | n => | 1 | acc => | 6 | (dec n) => | 0 | n => | 0 | (zero? n) => | true | (loop [acc 1 n 3] (debux.common.util/insert-blank-line) (if (zero? n) ... => | 6
(dbgn (defn fact [num]
(loop [acc 1 n num]
(if (zero? n)
acc
(recur (* acc n) (dec n))))))
(fact 3)
dbgn: (defn fact [num] (loop [acc 1 n num] (if (zero? n) acc (recur (* acc n ... => | num => | 3 | n => | 3 | (zero? n) => | false | acc => | 1 | (* acc n) => | 3 | (dec n) => | 2 | n => | 2 | acc => | 3 | (* acc n) => | 6 | (dec n) => | 1 | n => | 1 | acc => | 6 | (dec n) => | 0 | n => | 0 | (zero? n) => | true | (loop [acc 1 n num] (debux.common.util/insert-blank-line) (if (zero? n ... => | 6
defn
/defn-
/fn
~ recur
without loop
If you use dbgn in defn /defn- /fn ~ recur form without loop , you
will have the following exception. I am sorry about it, but this is inevitable due to the
implementation restriction.
|
(dbgn (defn factorial [acc n]
(if (zero? n)
acc
(recur (* acc n) (dec n)))))
1. Caused by java.lang.UnsupportedOperationException Cannot recur across try
However, if you temporarily replace recur with function name itself, you can
debug the form as follows. Be careful not to forget to recover function name itself to
recur after debugging.
|
(dbgn (defn factorial [acc n]
(if (zero? n)
acc
(factorial (* acc n) (dec n)))))
(factorial 1 3)
dbgn: (defn factorial [acc n] (if (zero? n) acc (factorial (* acc n) (dec n) ... => | n => | 3 | (zero? n) => | false | acc => | 1 | (* acc n) => | 3 | (dec n) => | 2 || n => || 2 || (zero? n) => || false || acc => || 3 || (* acc n) => || 6 || (dec n) => || 1 ||| n => ||| 1 ||| (zero? n) => ||| false ||| acc => ||| 6 ||| (* acc n) => ||| 6 ||| (dec n) => ||| 0 |||| n => |||| 0 |||| (zero? n) => |||| true |||| acc => |||| 6 |||| (if (zero? n) acc (factorial (* acc n) (dec n))) => |||| 6 ||| (factorial (* acc n) (dec n)) => ||| 6
dbgn
/clogn
If you have some error when analyzing some source code using dbgn
/clogn
, first
of all, you have to figure out what type of macro (refer to Categorized 19 types of macros in dbgn
/clogn
) caused
the error and then register the macro by using register-macros!
.
You can see the registered macros by using show-macros
.
(register-macros! macro-type macros)
(show-macros)
(show-macros macro-type)
(ns example.core)
(use 'debux.core)
(defmacro my-let [bindings & body]
`(let ~bindings ~@body))
;; Registering your own macro
(register-macros! :let-type [my-let])
(dbg (show-macros :let-type))
(dbg (show-macros))
(dbgn (my-let [a 10 b (+ a 10)] (+ a b)))
dbg: (show-macros :let-type) => | {:let-type | #{clojure.core/when-let example.dbgn/my-let clojure.core/let | clojure.core/with-local-vars clojure.core/when-some clojure.core/dotimes | clojure.core/with-open clojure.core/with-redefs clojure.core/binding | clojure.core/with-in-str clojure.core/with-out-str clojure.core/when-first}} dbg: (show-macros) => | {:fn-type #{clojure.core/fn fn*}, | :skip-arg-1-2-3-type #{}, | :skip-form-itself-type | #{clojure.core/definterface clojure.core/defrecord clojure.core/deftype | finally clojure.core/gen-class clojure.core/definline catch | clojure.core/gen-interface clojure.core/defprotocol}, | :case-type #{clojure.core/case}, | :skip-arg-2-3-type #{clojure.core/areduce clojure.core/amap}, | :skip-arg-1-type #{clojure.core/with-precision set!}, | :let-type | #{clojure.core/when-let example.dbgn/my-let clojure.core/let | clojure.core/with-local-vars clojure.core/when-some | clojure.core/dotimes clojure.core/with-open clojure.core/with-redefs | clojure.core/binding clojure.core/with-in-str | clojure.core/with-out-str clojure.core/when-first}, | :skip-arg-2-type #{clojure.core/as->}, | :defn-type #{clojure.core/defn clojure.core/defn-}, | :loop-type #{clojure.core.async/go-loop clojure.core/loop}, | :for-type #{clojure.core/for clojure.core/doseq}, | :def-type #{clojure.core/defonce def}, | :if-let-type #{clojure.core/if-let clojure.core/if-some}, | :letfn-type #{clojure.core/letfn}, | :dot-type #{.}, | :skip-arg-1-2-type #{}, | :skip-all-args-type | #{clojure.core/proxy-super clojure.core/defmacro clojure.core/sync | clojure.core/declare clojure.core/refer-clojure clojure.core/memfn | clojure.core/extend-type new clojure.core/defstruct | clojure.core/defmulti clojure.core/ns clojure.core/proxy | clojure.core/extend clojure.core/extend-protocol var quote | clojure.core/reify clojure.core/import}, | :expand-type | #{clojure.core/doto clojure.core/->> clojure.core/some->> | clojure.core/.. clojure.core/-> clojure.core/some-> | clojure.core/cond-> clojure.core/condp clojure.core/import | clojure.core/cond->>}, | :skip-arg-1-3-type #{clojure.core/defmethod}} dbgn: (my-let [a 10 b (+ a 10)] (+ a b)) => | a => | 10 | (+ a 10) => | 20 | b => | 20 | (+ a b) => | 30 | (my-let [a 10 b (+ a 10)] (debux.common.util/insert-blank-line) (+ a b ... => | 30
(ns example.macro)
(defmacro my-let [bindings & body]
`(let ~bindings ~@body))
(ns example.core
(:require [debux.cs.core :as d :refer-macros [clog clogn dbg dbgn break]])
(:require-macros [example.macro :refer [my-let]]))
;; Registering your own macro
(d/register-macros! :let-type [my-let])
(dbg (d/show-macros :let-type))
(dbg (d/show-macros))
(clogn (my-let [a 10 b (+ a 10)] (+ a b)))
dbg: (d/show-macros :let-type) => | {:let-type | #{example.macro/my-let cljs.core/with-redefs cljs.core/binding | cljs.core/when-first cljs.core/let cljs.core/with-out-str | cljs.core/when-let cljs.core/when-some cljs.core/dotimes}} dbg: (d/show-macros) => | {:fn-type #{fn* cljs.core/fn}, | :skip-arg-1-2-3-type #{}, | :skip-form-itself-type | #{finally cljs.core/defprotocol cljs.core/defrecord cljs.core/deftype | cljs.core/js-comment cljs.core/js-inline-comment catch}, | :case-type #{cljs.core/case}, | :skip-arg-2-3-type #{cljs.core/amap cljs.core/areduce}, | :skip-arg-1-type #{set! cljs.core/this-as}, | :let-type | #{example.macro/my-let cljs.core/with-redefs cljs.core/binding | cljs.core/when-first cljs.core/let cljs.core/with-out-str | cljs.core/when-let cljs.core/when-some cljs.core/dotimes}, | :skip-arg-2-type #{cljs.core/as->}, | :defn-type #{cljs.core/defn- cljs.core/defn}, | :loop-type #{cljs.core/loop}, | :for-type #{cljs.core/doseq cljs.core/for}, | :def-type #{cljs.core/defonce def}, | :if-let-type #{cljs.core/if-some cljs.core/if-let}, | :letfn-type #{cljs.core/letfn}, | :dot-type #{.}, | :skip-arg-1-2-type #{}, | :skip-all-args-type | #{cljs.core/simple-benchmark cljs.core/defmulti cljs.core/specify! | cljs.core/goog-define cljs.core/import-macros cljs.core/specify | cljs.core/use cljs.core/use-macros cljs.core/extend-protocol new | cljs.core/import cljs.core/declare cljs.core/reify cljs.core/require | cljs.core/comment cljs.core/memfn cljs.core/require-macros var | quote cljs.core/refer-clojure cljs.core/extend-type cljs.core/defmacro}, | :expand-type | #{cljs.core/.. cljs.core/some-> cljs.core/-> cljs.core/cond->> | cljs.core/import cljs.core/doto cljs.core/condp cljs.core/cond-> | cljs.core/some->> cljs.core/->>}, | :skip-arg-1-3-type #{cljs.core/defmethod}}
dbg
and dbgn
This feature applies to the multiple use of clog and clogn as well.
|
dbg
inside dbgn
or vice versadbg
can be used inside dbgn
or vice versa. For example, if you want to see the printed
results of ->
, ->>
, let
or comp
in dbg
when using dbgn
, do it
like this.
(defn my-fun [a b c]
(dbgn (+ a b c
(dbg (->> (range (- b a))
(map #(* % %))
(filter even?)
(take a)
(reduce +))))))
(my-fun 10 20 100)
dbgn: (+ a b c (dbg (->> (range (- b a)) (map (fn* [p1__41#] (* p1__41# p1__ ... => | a => | 10 | b => | 20 | c => | 100 |dbg: (->> (range (- b a)) (map (fn* [p1__41#] (* p1__41# p1__41#))) (filter ... => || (range (- b a)) => || (0 1 2 3 4 5 6 7 8 9) || (map (fn* [p1__41#] (* p1__41# p1__41#))) => || (0 1 4 9 16 25 36 49 64 81) || (filter even?) => || (0 4 16 36 64) || (take a) => || (0 4 16 36 64) || (reduce +) => || 120 | (+ a b c (dbg (->> (range (- b a)) (map (fn* [p1__41#] (* p1__41# p1__ ... => | 250
dbgn
and dbg
Multiple dbgn
and dbg
can be used together.
(def n 10)
(defn add [a b]
(dbgn (+ a b)))
(defn mul [a b]
(dbgn (* a b)))
(dbgn (+ n (mul 3 4) (add 10 20)))
dbgn: (+ n (mul 3 4) (add 10 20)) => | n => | 10 |dbgn: (* a b) => || a => || 3 || b => || 4 || (* a b) => || 12 | (mul 3 4) => | 12 |dbgn: (+ a b) => || a => || 10 || b => || 20 || (+ a b) => || 30 | (add 10 20) => | 30 | (+ n (mul 3 4) (add 10 20)) => | 52
(def n 10)
(defn add2 [a b]
(dbg (+ a b)))
(defn mul2 [a b]
(dbg (* a b)))
(dbgn (+ n (mul2 3 4) (add2 10 20)))
dbgn: (+ n (mul2 3 4) (add2 10 20)) => | n => | 10 |dbg: (* a b) => || 12 | (mul2 3 4) => | 12 |dbg: (+ a b) => || 30 | (add2 10 20) => | 30 | (+ n (mul2 3 4) (add2 10 20)) => | 52
The various options can be added and combined in any order after the form.
Options | dbg | dbgn | clog | clogn | break |
---|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Legend: O
(supported), X
(not supported)
You can add your own message in a string and it will be printed between less-than and more-than signs like this.
(dbg (repeat 5 "x") "5 times repeat"))
; => ("x" "x" "x" "x" "x")
dbg: (repeat 5 "x") <5 times repeat> => | ("x" "x" "x" "x" "x")
If you don’t specify a number after the form returning the seq
data type, debux macros
will print and return the default 100 items.
The number option applies only to seqs. This option doesn’t apply to vectors, maps or sets. |
(dbgn (count (range 200)))
; => 200
dbgn: (count (range 200)) => | (range 200) => | (0 1 2 ... 99) | (count (range 200)) => | 200
So, if you want to print less or more than default 100 items, specify the number explicitly like this.
(dbgn (count (range 200)) 200) ; => 200
dbgn: (count (range 200)) => | (range 200) => | (0 1 2 ... 199) | (count (range 200)) => | 200
The same rule applies in case of evaluating an infinite lazy-seq. If you omit the number
in evaluating an infinite lazy-seq, in the same manner it will print and return default
100 elements to prevent OutOfMemoryError
.
(dbgn (take 5 (range)))
; => (0 1 2 3 4)
dbgn: (count (range)) => | (range) => | (0 1 2 ... 99) | (take 5 (range)) => | (0 1 2 3 4)
If you want to change the default number of 100, use set-print-seq-length!
function like
this.
;; in Clojure
(set-print-seq-length! 10)
(dbgn (take 5 (range)))
; => (0 1 2 3 4)
dbgn: (count (range)) => | (range) => | (0 1 2 ... 9) | (take 5 (range)) => | (0 1 2 3 4)
;; in ClojureScript
(ns example.core
(:require [debux.cs.core :as d :refer-macros [clog clogn dbg dbgn break]]))
(d/set-print-seq-length! 10)
(clogn (take 5 (range)))
:if
optionYou can set :if
option like this.
(doseq [i (range 10)]
(dbg i :if (even? i)))
; => (0 1 2 3 4 5 6 7 8 9)
dbg: i => | 0 dbg: i => | 2 dbg: i => | 4 dbg: i => | 6 dbg: i => | 8
:print
option
The :print (or :p in brief) option applies only to dbg/clog .
|
If you don’t want to see the evaluated result itself but the result applied to another operations, use ‘:print one-arg-fn’ (or ‘:p one-arg-fn’) option like this.
(+ 10 (dbg (* 20 30) :print #(type %)))
; => 610
;; equivalent to the above
(+ 10 (dbg (* 20 30) :print type))
dbg: (* 20 30) => | java.lang.Long
The above example prints java.lang.Long
, not 600
(def person
{:name "Mark Volkmann"
:address {:street "644 Glen Summit"
:city "St. Charles"
:state "Missouri"
:zip 63304}
:employer {:name "Object Computing, Inc."
:address {:street "12140 Woodcrest Dr."
:city "Creve Coeur"
:state "Missouri"
:zip 63141}}})
(dbg person :p #(get-in % [:employer :address :city]))
dbg: person => | "Creve Coeur"
The above example prints the most inner :city
part, not person
itself.
:dup
optionThe same duplicate evaluated results are not printed by default as follows.
(dbgn (def my-function "my-function doc string"
(fn [x] (* x x x))))
(my-function 10)
; => 1000
dbgn: (def my-function "my-function doc string" (fn [x] (* x x x))) => | (fn [x] (* x x x)) => | #function[example.core/eval24554/result--24229--auto----24555] | (def my-function "my-function doc string" (fn [x] (* x x x))) => | #'example.core/my-function | x => | 10 | (* x x x) => | 1000
However, you can print the same duplicate evaluated values by :dup
option.
(dbgn (def my-function "my-function doc string"
(fn [x] (* x x x))) :dup)
(my-function 10)
; => 1000
dbgn: (def my-function "my-function doc string" (fn [x] (* x x x))) => | (fn [x] (* x x x)) => | #function[example.core/eval24554/result--24229--auto----24555] | (def my-function "my-function doc string" (fn [x] (* x x x))) => | #'example.core/my-function | x => | 10 | x => | 10 | x => | 10 | (* x x x) => | 1000
You will sometimes need to print every duplicate evaluated value to see exactly what’s going on.
Compare the results of the next two examples.
(dbgn (loop [acc 1 n 3]
(if (zero? n)
acc
(recur (* acc n) (dec n)))))
(dbgn (loop [acc 1 n 3]
(if (zero? n)
acc
(recur (* acc n) (dec n)))) :dup)
dbgn: (loop [acc 1 n 3] (if (zero? n) acc (recur (* acc n) (dec n)))) => | n => | 3 | (zero? n) => | false | acc => | 1 | (* acc n) => | 3 | (dec n) => | 2 | n => | 2 | acc => | 3 | (* acc n) => | 6 | (dec n) => | 1 | n => | 1 | acc => | 6 | (dec n) => | 0 | n => | 0 | (zero? n) => | true | (loop [acc 1 n 3] (debux.common.util/insert-blank-line) (if (zero? n) ... => | 6 dbgn: (loop [acc 1 n 3] (if (zero? n) acc (recur (* acc n) (dec n)))) => | n => | 3 | (zero? n) => | false | acc => | 1 | n => | 3 | (* acc n) => | 3 | n => | 3 | (dec n) => | 2 | n => | 2 | (zero? n) => | false | acc => | 3 | n => | 2 | (* acc n) => | 6 | n => | 2 | (dec n) => | 1 | n => | 1 | (zero? n) => | false | acc => | 6 | n => | 1 | (* acc n) => | 6 | n => | 1 | (dec n) => | 0 | n => | 0 | (zero? n) => | true | acc => | 6 | (loop [acc 1 n 3] (debux.common.util/insert-blank-line) (if (zero? n) ... => | 6
:style
option (CSS Styling)The following is the example of using clog
and clogn
in Chrome browser.
(ns example.core
(:require [debux.cs.core :as d :refer-macros [clog clogn dbg dbgn break]]))
(clog (repeat 5 "x") "5 times repeat")
(clogn (repeat 5 (repeat 5 "x")) "25 times repeat")
You can style the form, using the following predefined keywords.
keyword | abbreviation |
---|---|
:style | :s |
:error | :e |
:warn | :w |
:info | :i |
:debug | :d |
(clog (+ 10 20) :style :error "error style") (clog (+ 10 20) :style :warn "warn style") (clog (+ 10 20) :style :info "info style") (clog (+ 10 20) :style :debug "debug style") (clog (+ 10 20) "debug style is default")
Or in brief
(clog (+ 10 20) :s :e "error style") (clog (+ 10 20) :s :w "warn style") (clog (+ 10 20) :s :i "info style") (clog (+ 10 20) :s :d "debug style") (clog (+ 10 20) "debug style is default")
You can redefine the predefined styles or define your own new style by using
merge-styles
like this.
(d/merge-styles {:warn "background: #9400D3; color: white"
:love "background: #FF1493; color: white"})
(clog (+ 10 20) :style :warn "warn style changed")
(clog (+ 10 20) :style :love "love style")
;; You can style the form directly in string format in any way you want.
(clog (+ 10 20) :style "color:orange; background:blue; font-size: 14pt")
:once
optionIf you add :once
(or :o
in brief) option after the form, the same evaluated value will
not be printed. This is a very useful feature, when you are debugging a game programming,
where successive multiple frames usually have the same evaluated value.
(def a (atom 10))
;; This will be printed.
(clog @a :once)
;; This will not be printed,
;; because the evaluated value is the same as before.
(clog @a :once)
(reset! a 20)
;; This will be printed,
;; because the evaluated value is not the same as before.
(clog @a :once)
;; This will not be printed,
;; because the evaluated value is the same as before.
(clog @a :once)
(:once mode) string is appended after the form header to remind you of :once
mode.
|
:js
optionIf :js
option is added after the form, the JavaScript object will be printed as well, so
you can inspect the internal structures of ClojureScript data types or the JavaScript
objects returned by JavaScript interops in ClojureScript.
(clog {:a 10 :b 20} :js)
cljs-devtools
The debux
supports cljs-devtools
since the version 0.5.9
.
The development version debux
library already includes
cljs-devtools 0.9.11
version.
Of course, you can include the newer version of devtools in your project file.
|
Never use the development version debux in production. Instead, use the
production version debux-stubs in production. See the details
here.
|
So if you want to use cljs-devtools
printing, install it via :preloads
cljs compiler
option like this, following
devtools'
installition guide.
(defproject your-project "0.1.0" ,,,,,, :cljsbuild {:builds [{:compiler {:preloads [devtools.preload] ,,,,,,}}]})
Or install it manually like this.
(ns your-project.devtools (:require [devtools.core :as devtools])) (devtools/install!)
Don’t forget to to turn on the Chrome DevTools' [Settings — Preferences — Console — Enable custom formatters], before using cljs-devtools
' printing.
You should read Why some custom formatters were not rendered?, before using cljs-devtools' printing.
You can confiugre cljs-devtools
in various ways as you like. See the details in
cljs-devtools'
configuration.
dbg-prn
: Debugging the macros for clojurescript at the macro-expansion time.
The function dbg-prn doesn’t follow the usage employed in dbg /clog . It is just
another name of println which can be used at the macro-expansion time.
|
dbg-prn can be used inside the macros for Clojure.
|
See the detailed explaination here.
dbg-last
: Debugging inside the thread-last macro ->>
The dbg-last/clog-last macros must be used inside the thread-last macro
->>
|
The options of dbg-last/clog-last macros are the same as those of dbg/clog .
|
If you want to use dbg
macro with its options inside the thread-last macro
->>
like this, you will have an exception.
(->> (range 10)
(filter odd?)
(dbg 5 "after filter")
(map inc))
; >> 1. Unhandled java.lang.IllegalArgumentException
; Don't know how to create ISeq from: java.lang.Long
The dbg-last
macro is to the rescue of this case.
(->> (range 20)
(filter odd?)
(dbg-last 5 "after filter")
(map inc))
; => (2 4 6 8 10 12 14 16 18 20)
dbg: (filter odd? (range 10)) <after filter> => | (1 3 5 7 9)
There is no problem in case of the dbg
macro with its options inside the thread-first
macro ->
.
(-> (range 10)
(conj 100)
(dbg 5 "after conj")
vec)
; => [100 0 1 2 3 4 5 6 7 8 9]
dbg: (conj (range 10) 100) <after conj> => | (100 0 1 2 3)
break
examplesbreak
optionsYou can use break
to set the breakpoint in the source code like this. You can add string
option for message, or :if
option for conditional break.
(break)
(break "hello world")
(break :if (> 10 20) "this will not be printed")
(break :if (< 10 20) "10 is less than 20")
You can see the message in DevTools' console window.
After setting the breakpoint, you can inspect the callstack, locals, etc. in the browser’s DevTools window.
(defn my-fun2
[a {:keys [b c d] :or {d 10 b 20 c 30}} [e f g & h]]
(break)
(clog [a b c d e f g h]))
(my-fun2 (take 5 (range)) {:c 50 :d 100} ["a" "b" "c" "d" "e"])
You can see the message in DevTools' console window.
:if
option exampleWhen using break
, you can use :if
like this.
(defn my-fun3 []
(let [a 10
b 20]
(dotimes [i 1000]
(break :if (= i 999)))))
(my-fun3)
When (set-debug-mode! false)
is run, the effects of set-ns-whitelist!
and
set-ns-blacklist!
will be ignored.
(set-debug-mode! false)
;; The folowings take no effect at all.
(set-ns-whitelist! ["my-app.*" ])
(set-ns-blacklist! ["my-app.foo" "my-app.bar.*"])
When set-ns-whitelist!
and set-ns-blaklist!
are both run like this, all my-app.*
except my-app.foo
will be run.
(set-ns-whitelist! ["my-app.*" ])
(set-ns-blacklist! ["my-app.foo" "my-app.bar.*"])
The following (in example folder) is an example.
(defproject example ,,,,,, :main example.core ,,,,,,)
(ns example.core
(:require [debux.core :as d])
(:gen-class))
(defn -main []
(println "\nRunning debux examples...\n")
;(d/set-debug-mode! false)
(d/set-ns-whitelist! ["example.dbg*"])
(d/set-ns-blacklist! ["example.dbgn"])
;; You should require dynamically the namespaces that you want to load.
(require 'example.dbg)
(require 'example.options)
(require 'example.dbgn))
(defproject example ,,,,,, :cljsbuild {:builds [{,,,,,, :compiler {,,,,,, :preloads [example.preload] ,,,,,,}}]})
(ns example.preload
(:require [debux.cs.core :as d]))
;(d/set-debug-mode! false)
(d/set-ns-whitelist! ["example.clog*"])
(d/set-ns-blacklist! ["example.clogn"])
You had better use dbg
/dbgn
instead of clog
/clogn
on Node.js JavaScript
console, because Node.js doesn’t support colors in its console.log
function. The
following shows the example.
(ns example.node
(:require [cljs.nodejs :as nodejs]
[debux.cs.core :refer-macros [clog clogn dbg dbgn]] ))
(defn -main [& args]
(dbgn (+ 2 (* 3 4)))
(clogn (+ 2 (* 3 4))))
(set! *main-cli-fn* -main)
dbgn: (+ 2 (* 3 4)) => | (* 3 4) => | 12 | (+ 2 (* 3 4)) => | 14 %cclogn: %c (+ 2 (* 3 4)) %c => color: #8b008b background: #ffc125; color: black color: black | %c (* 3 4) %c => background: #ffc125; color: black color: black | 12 | %c (+ 2 (* 3 4)) %c => background: #ffc125; color: black color: black | 14
Of course, you should use the clog
/clogn
instead of dbg
/dbgn
in
Electron apps on Node.js, because Electron supports colors
in its console.log
function.
You can use both dbg
/dbgn
and clog
/clogn
on the browser REPL. The following is
an example about running the figwheel.
(defproject example "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.8.0"]
[org.clojure/clojurescript "1.10.238"]
[philoskim/debux "0.5.9"]]
:plugins [[lein-cljsbuild "1.1.6"]
[lein-figwheel "0.5.10"]]
:source-paths ["src/clj"]
:clean-targets ^{:protect false}
["resources/public/js/app.js"
"resources/public/js/app.js.map"]
:cljsbuild {:builds [{:id "dev"
:source-paths ["src/cljs"]
:figwheel true
:compiler {:main example.core
:asset-path "js/out"
:output-to "resources/public/js/app.js"
:output-dir "resources/public/js/out"
:source-map true
:optimizations :none} }]})
And then run figwheel like this on terminal window.
$ lein figwheel Figwheel: Cutting some fruit, just a sec ... Figwheel: Validating the configuration found in project.clj Figwheel: Configuration Valid :) Figwheel: Starting server at http://0.0.0.0:3449 Figwheel: Watching build - dev Compiling "resources/public/js/app.js" from ["src/cljs"]... Successfully compiled "resources/public/js/app.js" in 2.14 seconds. Launching ClojureScript REPL for build: dev Figwheel Controls: (stop-autobuild) ;; stops Figwheel autobuilder (start-autobuild [id ...]) ;; starts autobuilder focused on optional ids (switch-to-build id ...) ;; switches autobuilder to different build (reset-autobuild) ;; stops, cleans, and starts autobuilder (reload-config) ;; reloads build config and resets autobuild (build-once [id ...]) ;; builds source one time (clean-builds [id ..]) ;; deletes compiled cljs target files (print-config [id ...]) ;; prints out build configurations (fig-status) ;; displays current state of system (figwheel.client/set-autoload false) ;; will turn autoloading off (figwheel.client/set-repl-pprint false) ;; will turn pretty printing off Switch REPL build focus: :cljs/quit ;; allows you to switch REPL to another build Docs: (doc function-name-here) Exit: Control+C or :cljs/quit Results: Stored in vars *1, *2, *3, *e holds last exception object Prompt will show when Figwheel connects to your application
After that, connect to http://localhost:3449
on your browser.
To quit, type: :cljs/quit cljs.user=> (require '[debux.cs.core :refer-macros [clog clogn dbg dbgn break]]) nil cljs.user=> (dbg (+ 1 2)) dbg: (+ 1 2) => | 3 3 cljs.user=>
Now you can do anything in this browser REPL as in the Clojure REPL. When you evaluate
dbg
/dbgn
in your ClojureScript source code, the result will go to both the REPL window
and the browser’s console window. When you evaluate clog
/clogn
in your ClojureScript
source code, the result will go only to your browser’s console window.
debux.el
for Emacs CIDER userInserting or deleting dbg
/dbgn
/clog
/clogn
manually is very painful. As Emacs user
I wrote debux.el
for Emacs CIDER for my convenience. I think it’s not perfect but better
than nothing. If you find it useful, append the following debux.el
(which is in project
root folder) to the ~/.emacs.d/init.el
.
debux.el
Refer to the source code of debux.el
here.
If you are editing on *.clj
or *.cljc
files, (dbg ...)
or (dbgn
...)
will be inserted or deleted.
If you are editing on *.cljs
files, (clog ...)
or (clogn ...)
will
be inserted or deleted.
dbg
/clog
or dbgn
/clogn
When you double-click the left mouse button on one of the open parentheses and the
following string is not dbg
or clog
, it will be inserted.
The v
of the following examples marks the cursor position.
;; before
;; v
(let [a 1 b 2]
(+ a b))
;; after
(dbg (let [a 1 b 2]
(+ a b)))
When you double-click on a symbol, dbg
or clog
will be inserted as well.
;; before
; v
(+ a b)
;; after
(+ (dbg a) b)
When you double-click on one of the open parentheses while pressing <Ctrl>
key and the
following string is not dbgn
or clogn
, it will be inserted.
;; before
;; v
(defn foo [a b c]
(* a b c))
;; after
(dbgn (defn foo [a b c]
(* a b c)))
dbg
/clog
/dbgn
/clogn
When you double-click on one of the open parentheses and the following string is dbg
,
clog
, dbgn
or clogn
, it will be deleted.
;; before
;; v
(dbg (let [a 1 b 2]
(+ a b)))
;; after
(let [a 1 b 2]
(+ a b))
;; before
;; v
(dbgn (defn foo [a b c]
(* a b c)))
;; after
(defn foo [a b c]
(* a b c))
Copyright © 2015—2020 Young Tae Kim
Distributed under the Eclipse Public License either version 1.0 or any later version.
Can you improve this documentation? These fine people already did:
philoskim, Philos Kim & philosEdit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close