Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Next release #16

Merged
merged 8 commits into from
Dec 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dev/analysis.edn

Large diffs are not rendered by default.

26 changes: 26 additions & 0 deletions doc/elin-mapping.txt
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,22 @@ ElinPrintLastResult
Calls `elin.handler.evaluate/print-last-result` handler.
Key is mapped to |<Plug>(elin_print_last_result)|.

*ElinOverviewCurrentList*
ElinOverviewCurrentList
Calls `elin.handler.evaluate/evaluate-current-list` handler
with the following interceptors:
- `elin.interceptor.handler/overview`

Key is mapped to |<Plug>(elin_overview_current_list)|.

*ElinOverviewCurrentTopList*
ElinOverviewCurrentTopList
Calls `elin.handler.evaluate/evaluate-current-top-list` handler
with the following interceptors:
- `elin.interceptor.handler/overview`

Key is mapped to |<Plug>(elin_overview_current_top_list)|.

*ElinInterrupt*
ElinInterrupt
Calls `elin.handler.evaluate/interrupt` handler.
Expand Down Expand Up @@ -259,6 +275,14 @@ MAPPINGS *elin-mappings*
<Plug>(elin_print_last_result)
Same as |ElinPrintLastResult|.

*<Plug>(elin_overview_current_list)*
<Plug>(elin_overview_current_list)
Same as |ElinOverviewCurrentList|.

*<Plug>(elin_overview_current_top_list)*
<Plug>(elin_overview_current_top_list)
Same as |ElinOverviewCurrentTopList|.

*<Plug>(elin_interrupt)*
<Plug>(elin_interrupt)
Same as |ElinInterrupt|.
Expand Down Expand Up @@ -373,6 +397,8 @@ n <Leader>en <Plug>(elin_eval_ns_form)
n <Leader>ea <Plug>(elin_eval_at_mark)
n <Leader>ece <Plug>(elin_eval_in_context)
n <Leader>ep <Plug>(elin_print_last_result)
n <Leader>oe <Plug>(elin_overview_current_list)
n <Leader>ot <Plug>(elin_overview_current_top_list)
n <Leader>eq <Plug>(elin_interrupt)
n <Leader>eu <Plug>(elin_undef)
n <Leader>eU <Plug>(elin_undef_all)
Expand Down
28 changes: 28 additions & 0 deletions doc/pages/debugging/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,31 @@ The easiest way is to put `#dbg` to your code, and evaluate it.
Once you evaluate `(fib 10)`, debugger will launch.

This feature is implemented by the <<_interceptor_debugprocess_debugger>> interceptor.

=== Tapped values

In elin, values tapped with https://clojuredocs.org/clojure.core/tap%3E[tap>] are displayed in the <<Information buffer>>, regardless of where they were tapped, and can be used as a substitute for debug prints.
Compared to debug prints like `println`, the advantage of using `tap>` is that you can inspect the tapped values in detail later.

Specifically, in elin, the value `tap>` is applied to can be referenced from an elin-specific var named `*tapped*` by default.
This allows you to use `tap>` as a substitute for a debug print, letting you refer to the value as needed.

[source,clojure]
----
(tap> [:random-uuid (random-uuid)])
;; => true

(-> *tapped* first)
;; => {:id "0f307a48-82ff-4a31-80b9-9c67ff856464", :time "2112-09-03T11:22:33.123456", :value [:random-uuid #uuid "0d3a1159-5563-4530-8fe8-ae07768395c5"]}

(-> *tapped* first :value second)
;; => #uuid "0d3a1159-5563-4530-8fe8-ae07768395c5"
----

This var name is defined in <<_interceptor_tapaccess_tapped_values>> interceptor and can be freely <<_server_configuration_files,changed>>.


[source,clojure]
----
{:interceptor {:config-map {elin.interceptor.tap/access-tapped-values {:var-name "foobar"}}}}
----
9 changes: 9 additions & 0 deletions plugin/elin.vim
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ command! ElinEvalAtMark call elin#notify('elin.handler.evaluate/evaluate-at-mark
command! ElinEvalInContext call elin#notify('elin-alias-evaluate-current-list-in-context', [])
command! ElinPrintLastResult call elin#notify('elin.handler.evaluate/print-last-result', [])

command! ElinOverviewCurrentList call elin#notify('elin-alias-overview-current-list', [])
command! ElinOverviewCurrentTopList call elin#notify('elin-alias-overview-current-top-list', [])

command! ElinInterrupt call elin#notify('elin.handler.evaluate/interrupt', [])
command! ElinUndef call elin#notify('elin.handler.evaluate/undef', [])
command! ElinUndefAll call elin#notify('elin.handler.evaluate/undef-all', [])
Expand Down Expand Up @@ -162,6 +165,9 @@ nnoremap <silent> <Plug>(elin_eval_at_mark) <Cmd>ElinEvalAtMark<CR>
nnoremap <silent> <Plug>(elin_eval_in_context) <Cmd>ElinEvalInContext<CR>
nnoremap <silent> <Plug>(elin_print_last_result) <Cmd>ElinPrintLastResult<CR>

nnoremap <silent> <Plug>(elin_overview_current_list) <Cmd>ElinOverviewCurrentList<CR>
nnoremap <silent> <Plug>(elin_overview_current_top_list) <Cmd>ElinOverviewCurrentTopList<CR>

nnoremap <silent> <Plug>(elin_interrupt) <Cmd>ElinInterrupt<CR>
nnoremap <silent> <Plug>(elin_undef) <Cmd>ElinUndef<CR>
nnoremap <silent> <Plug>(elin_undef_all) <Cmd>ElinUndefAll<CR>
Expand Down Expand Up @@ -243,6 +249,9 @@ function! s:default_key_mappings() abort
call s:define_mapping('nmap', '<Leader>ece', '<Plug>(elin_eval_in_context)')
call s:define_mapping('nmap', '<Leader>ep', '<Plug>(elin_print_last_result)')

call s:define_mapping('nmap', '<Leader>oe', '<Plug>(elin_overview_current_list)')
call s:define_mapping('nmap', '<Leader>ot', '<Plug>(elin_overview_current_top_list)')

call s:define_mapping('nmap', '<Leader>eq', '<Plug>(elin_interrupt)')
call s:define_mapping('nmap', '<Leader>eu', '<Plug>(elin_undef)')
call s:define_mapping('nmap', '<Leader>eU', '<Plug>(elin_undef_all)')
Expand Down
9 changes: 7 additions & 2 deletions resources/config.edn
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@
elin.handler.navigate/jump-to-definition {:interceptor {:includes [elin.interceptor.handler/jump-to-file]}}
elin.handler.navigate/references {:interceptor {:includes [elin.interceptor.handler/jump-to-file]}}
elin.handler.navigate/local-references {:interceptor {:includes [elin.interceptor.handler/jump-to-file]}}
elin.handler.tap/tapped {:interceptor {:uses [elin.interceptor.handler/append-result-to-info-buffer {:show-temporarily? true}]}}
elin.handler.tap/tapped {:interceptor {:uses [elin.interceptor.handler/append-result-to-info-buffer {:show-temporarily? true
:header ";; Tapped value\n"}]}}
elin.handler.test/rerun-last-failed-tests {:interceptor #ref [:interceptor :preset :test]}
elin.handler.test/rerun-last-tests {:interceptor #ref [:interceptor :preset :test]}
elin.handler.test/run-test-under-cursor {:interceptor #ref [:interceptor :preset :test]}
Expand All @@ -116,7 +117,11 @@
:aliases {elin-alias-evaluate-current-list-in-context {:handler elin.handler.evaluate/evaluate-current-list
:config {:interceptor {:includes [elin.interceptor.evaluate/eval-with-context]}}}
elin-alias-run-test-focused-current-testing {:handler elin.handler.test/run-test-under-cursor
:config {:interceptor {:includes [elin.interceptor.test/focus-current-testing]}}}}
:config {:interceptor {:includes [elin.interceptor.test/focus-current-testing]}}}
elin-alias-overview-current-list {:handler elin.handler.evaluate/evaluate-current-list
:config {:interceptor {:includes [elin.interceptor.handler/overview]}}}
elin-alias-overview-current-top-list {:handler elin.handler.evaluate/evaluate-current-top-list
:config {:interceptor {:includes [elin.interceptor.handler/overview]}}}}

:initialize {:export {"g:elin_http_server_port" #ref [:http-server :port]}}}

Expand Down
57 changes: 53 additions & 4 deletions src/elin/interceptor/handler.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
(:require
[clojure.core.async :as async]
[clojure.java.io :as io]
[clojure.pprint :as pp]
[clojure.string :as str]
[elin.constant.interceptor :as e.c.interceptor]
[elin.error :as e]
[elin.message :as e.message]
[elin.protocol.host :as e.p.host]
[elin.util.interceptor :as e.u.interceptor]
[elin.util.overview :as e.u.overview]
[exoscale.interceptor :as ix]))

(def handling-error
Expand All @@ -34,14 +36,61 @@
(ix/discard))})

(def append-result-to-info-buffer
"Interceptor to show handler result temporarily."
"Interceptor to show handler result temporarily.

.Configuration
[%autowidth.stretch]
|===
| key | type | description

| show-temporarily? | boolean | If `true`, show result on temporal buffer also. Default value is `false`.
| header | string | Header of result. Default value is `nil`.
| footer | string | Footer of result. Default value is `nil`.
|==="
{:kind e.c.interceptor/handler
:leave (-> (fn [{:as ctx :component/keys [host] :keys [response]}]
(when (and (string? response)
(seq response))
(let [config (or (e.u.interceptor/config ctx #'append-result-to-info-buffer)
{})]
(e.p.host/append-to-info-buffer host response config))))
(let [{:as config :keys [header footer]} (or (e.u.interceptor/config ctx #'append-result-to-info-buffer)
{})
text (if (and (string? header) (seq header))
(str header response)
response)
text (if (and (string? footer) (seq footer))
(str text footer)
text)]
(e.p.host/append-to-info-buffer host text config))))
(ix/discard))})

(def overview
"Interceptor to overview handler result.

.Configuration
[%autowidth.stretch]
|===
| key | type | description

| max-depth | integer | Max depth to overview data. Default value is `2`.
| max-list-length | integer | Max length to overview IPersistentList. Default value is `20`.
| max-vector-length | integer | Max length to overview IPersistentVector. Default value is `20`.
| max-set-length | integer | Max length to overview IPersistentSet. Default value is `20`.
| max-map-length | integer | Max length to overview IPersistentMap. Default value is `20`.
| max-string-length | integer | Max length to overview String. Default value is `20`.
|==="
{:kind e.c.interceptor/handler
:leave (-> (fn [{:as ctx :component/keys [host] :keys [response]}]
(let [config (e.u.interceptor/config ctx #'overview)
sexpr (try
(read-string response)
(catch Exception _ nil))
overviewed (e.u.overview/overview sexpr config)
overviewed-str (or (when overviewed
(with-out-str
(pp/pprint overviewed)))
"")]
(when (seq overviewed-str)
(e.p.host/append-to-info-buffer
host overviewed-str {:show-temporarily? true}))))
(ix/discard))})

(def jump-to-file
Expand Down
111 changes: 111 additions & 0 deletions src/elin/util/overview.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
(ns elin.util.overview)

(def default-overview-context
{:depth 0
:max-depth 2
:max-list-length 20
:max-vector-length 20
:max-set-length 20
:max-map-length 20
:max-string-length 20})

(def overview-options
{"max-depth" "Max depth to overview data."
"max-list-length" "Max length to overview IPersistentList."
"max-vector-length" "Max length to overview IPersistentVector."
"max-set-length" "Max length to overview IPersistentSet."
"max-map-length" "Max length to overview IPersistentMap."
"max-string-length" "Max length to overview String."})

(defprotocol ICuttable ; {{{
(cut [n x]))

(extend-protocol ICuttable
Object
(cut [x _] x)

nil
(cut [_ _] nil)

clojure.lang.IPersistentList
(cut [x n]
(let [l (count x)]
(cond-> (take n x)
(and (not= 0 l) (> l n)) (concat '[...]))))

clojure.lang.IPersistentVector
(cut [x n]
(let [l (count x)]
(cond-> (vec (take n x))
(and (not= 0 l) (> l n)) (conj '...))))

clojure.lang.IPersistentMap
(cut [x n]
(let [l (count x)]
(into {} (cond-> (take n x)
(and (not= 0 l) (> l n))
(concat '[[etc ...]])))))

clojure.lang.IPersistentSet
(cut [x n]
(let [l (count x)]
(cond-> (set (take n x))
(and (not= 0 l) (> l n)) (conj '...))))

String
(cut [x n]
(let [l (count x)]
(cond-> (subs x 0 (max 0 (min l n)))
(and (not= 0 l) (> l n)) (str "...")))))
;; }}}

(defprotocol IOverview ; {{{
(overview* [x context]))

(extend-protocol IOverview
Object
(overview* [x _] x)

nil
(overview* [_ _] nil)

clojure.lang.IPersistentList
(overview* [x {:keys [depth max-depth max-list-length] :as context}]
(->> (cut x (if (< depth max-depth) max-list-length 1))
(map #(and % (overview* % (update context :depth inc))))))

clojure.lang.IPersistentVector
(overview* [x {:keys [depth max-depth max-vector-length] :as context}]
(if (<= depth max-depth)
(->> (cut x max-vector-length)
(mapv #(and % (overview* % (update context :depth inc)))))
'...))

clojure.lang.IPersistentMap
(overview* [x {:keys [depth max-depth max-map-length] :as context}]
(if (<= depth max-depth)
(-> (cut x max-map-length)
(update-vals #(and % (overview* % (update context :depth inc)))))
'...))

clojure.lang.IPersistentSet
(overview* [x {:keys [depth max-depth max-set-length] :as context}]
(->> (cut x (if (< depth max-depth) max-set-length 1))
(map #(and % (overview* % (update context :depth inc))))
set))

String
(overview* [x {:keys [depth max-depth max-string-length]}]
(cond-> x
(>= depth max-depth) (cut max-string-length))))
;; }}}

(defn overview
([x] (overview x {}))
([x context]
(overview* x (merge default-overview-context context))))

(comment (overview {:alice [1 [2 [3 [4 [5]]]]]
:bob (vec (range 10))
:charlie {:a {:b {:c {:d {:e {:f {:g 1}}}}}}}
:dave [[[[[[[[1]]]]]]]]}))
Loading
Loading