├── .gitignore
├── README.md
├── project.clj
├── resources
└── templates
│ ├── asc
│ ├── pages
│ │ └── adoc-page.asc
│ └── posts
│ │ └── 2014-10-10-adoc-post.asc
│ ├── config.edn
│ ├── css
│ ├── example.css
│ └── sassexample.scss
│ ├── img
│ ├── cider-logo-w640.png
│ └── cryogen.png
│ ├── md
│ ├── pages
│ │ └── about.md
│ └── posts
│ │ ├── 2017-04-26-beginning-to-hack-on-cider.md
│ │ ├── 2017-04-27-basic-setup-cider-nrepl.md
│ │ ├── 2017-04-30-eval-walkthrough.md
│ │ └── 2017-05-10-font-lock-bug.md
│ └── themes
│ ├── blue
│ ├── css
│ │ └── screen.css
│ ├── html
│ │ ├── 404.html
│ │ ├── archives.html
│ │ ├── author.html
│ │ ├── base.html
│ │ ├── home.html
│ │ ├── page.html
│ │ ├── post-content.html
│ │ ├── post.html
│ │ ├── previews.html
│ │ ├── tag.html
│ │ └── tags.html
│ └── js
│ │ └── highlight.pack.js
│ ├── blue_centered
│ ├── css
│ │ └── screen.css
│ ├── html
│ │ ├── 404.html
│ │ ├── archives.html
│ │ ├── author.html
│ │ ├── base.html
│ │ ├── home.html
│ │ ├── page.html
│ │ ├── post-content.html
│ │ ├── post.html
│ │ ├── previews.html
│ │ ├── tag.html
│ │ └── tags.html
│ └── js
│ │ └── highlight.pack.js
│ └── nucleus
│ ├── css
│ ├── buttons.css
│ ├── menu.css
│ ├── reset.css
│ ├── style.css
│ └── typography.css
│ ├── html
│ ├── 404.html
│ ├── archives.html
│ ├── author.html
│ ├── base.html
│ ├── home.html
│ ├── page.html
│ ├── post-content.html
│ ├── post.html
│ ├── previews.html
│ ├── tag.html
│ └── tags.html
│ └── js
│ ├── highlight.pack.js
│ └── scripts.js
└── src
└── cryogen
├── core.clj
└── server.clj
/.gitignore:
--------------------------------------------------------------------------------
1 | pom.xml
2 | pom.xml.asc
3 | *jar
4 | /lib/
5 | /classes/
6 | /target/
7 | /checkouts/
8 | .lein-deps-sum
9 | .lein-repl-history
10 | .lein-plugins/
11 | .lein-failures
12 | /resources/public/
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Hacking CIDER ##
2 |
3 | This is a site about hacking on CIDER. Anyone can contribute through pull requests with articles about working out bugs, adding features, or just documenting how some aspects of CIDER work. The point is to get more people comfortable in the codebase and willing to add changes. We are all working in Clojure so picking up lisp should come easy, and we want to provide as many guides as we can to that end here.
4 |
5 | This site is a static site compiled with [Cryogen](http://cryogenweb.org/index.html). Blog posts are in markdown and placed in `resources/templates/md/` and must conform to the filename `yyyy-dd-mm-title-name.md`. The top of each file must contain some metadata in the form of a clojure map along the following structure:
6 |
7 | ```clojure
8 | {:title "Basic setup of CIDER for hacking"
9 | :layout :post
10 | :tags ["CIDER" "setup" "emacs"]
11 | :toc true}
12 | ```
13 |
14 | The site is located at [www.hackingcider.com](http://www.hackingcider.com).
15 |
16 | ## How to Add ##
17 |
18 | Just clone the site and run `lein ring server`. This creates the webserver and serves the articles. Make your own article in the directory mentioned above and then send a pull request our way.
19 |
20 | To compile the site, just run `lein run` which will create the `resources/public` directory.
21 |
22 | If you would like to customize the look of the site, please feel free as it is very basic and could use some visual tweaks.
23 |
--------------------------------------------------------------------------------
/project.clj:
--------------------------------------------------------------------------------
1 | (defproject cryogen "0.1.0"
2 | :description "Simple static site generator"
3 | :url "https://github.com/lacarmen/cryogen"
4 | :license {:name "Eclipse Public License"
5 | :url "http://www.eclipse.org/legal/epl-v10.html"}
6 | :dependencies [[org.clojure/clojure "1.8.0"]
7 | [ring/ring-devel "1.5.1"]
8 | [compojure "1.5.2"]
9 | [ring-server "0.4.0"]
10 | [cryogen-markdown "0.1.6"]
11 | [cryogen-core "0.1.55"]]
12 | :plugins [[lein-ring "0.9.7"]]
13 | :main cryogen.core
14 | :ring {:init cryogen.server/init
15 | :handler cryogen.server/handler})
16 |
--------------------------------------------------------------------------------
/resources/templates/asc/pages/adoc-page.asc:
--------------------------------------------------------------------------------
1 | {:title "Adoc Page"
2 | :layout :page
3 | :page-index 0
4 | :navbar? true}
5 |
6 | == Adoc Page ==
7 |
8 | We support http://asciidoc.org/[asciidoc] too!
9 |
--------------------------------------------------------------------------------
/resources/templates/asc/posts/2014-10-10-adoc-post.asc:
--------------------------------------------------------------------------------
1 | {:title "Adoc Post"
2 | :layout :post
3 | :tags ["cryogen" "asciidoc"]
4 | :toc false
5 | }
6 |
7 | :toc: macro
8 |
9 | == Example Asciidoc Post ==
10 | This is an example asciidoc post.
11 |
12 | You can use a manually placed table of contents by setting `:toc false` in the front matter, but use `:toc: macro` at the top of the post, and `toc::[]` where the table of contents is supposed to be, like here:
13 |
14 | toc::[]
15 |
16 | === Section 1 ===
17 |
18 | .Heading
19 |
20 | With some text and maybe even a bulleted list:
21 |
22 | - Thing 1
23 | - Thing 2
24 |
25 | Or how about some *bold* or _italicized_ text?
26 |
27 | === Section 2 ===
28 |
29 | Will a code snippet work?
30 |
31 | .bash
32 | [source,bash]
33 | ----
34 | $ echo "foo"
35 | ----
36 |
37 | .clojure
38 | [source,clojure]
39 | ----
40 | (defn echo [s]
41 | (println s))
42 | ----
43 |
44 |
45 |
--------------------------------------------------------------------------------
/resources/templates/config.edn:
--------------------------------------------------------------------------------
1 | {:site-title "Hacking CIDER"
2 | :author "clojure-emacs"
3 | :description "A site about hacking on CIDER"
4 | :site-url "http://www.hackingcider.com/"
5 | :post-root "posts"
6 | :page-root "pages"
7 | :post-root-uri "posts-output"
8 | :page-root-uri "pages-output"
9 | :tag-root-uri "tags-output"
10 | :author-root-uri "authors-output"
11 | :blog-prefix ""
12 | :rss-name "feed.xml"
13 | :rss-filters ["cryogen"]
14 | :recent-posts 3
15 | :post-date-format "yyyy-MM-dd"
16 | :archive-group-format "yyyy MMMM"
17 | :sass-src []
18 | :sass-path "sass"
19 | :compass-path "compass"
20 | :theme "blue"
21 | :resources ["img"]
22 | :keep-files [".git"]
23 | :disqus? false
24 | :disqus-shortname ""
25 | :ignored-files [#"\.#.*" #".*\.swp$"]
26 | :posts-per-page 5
27 | :blocks-per-preview 4
28 | :previews? true
29 | :clean-urls? true
30 | :hide-future-posts? true
31 | :klipse {}
32 | :debug? false}
33 |
--------------------------------------------------------------------------------
/resources/templates/css/example.css:
--------------------------------------------------------------------------------
1 | a {
2 | text-decoration-style: dashed;
3 | }
--------------------------------------------------------------------------------
/resources/templates/css/sassexample.scss:
--------------------------------------------------------------------------------
1 | body {
2 | a {
3 | text-decoration-style: dashed;
4 | }
5 | }
--------------------------------------------------------------------------------
/resources/templates/img/cider-logo-w640.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/clojure-emacs/hackingcider/1cd304665943d09862df97178f975437628c8c44/resources/templates/img/cider-logo-w640.png
--------------------------------------------------------------------------------
/resources/templates/img/cryogen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/clojure-emacs/hackingcider/1cd304665943d09862df97178f975437628c8c44/resources/templates/img/cryogen.png
--------------------------------------------------------------------------------
/resources/templates/md/pages/about.md:
--------------------------------------------------------------------------------
1 | {:title "About"
2 | :layout :page
3 | :page-index 0
4 | :navbar? true}
5 |
6 | ## Hacking CIDER
7 |
8 | This site aims to be a resource for hacking on CIDER. It's not about flashy websites or graphics, but a way to learn how to delve into the CIDER codebase, both elisp and Clojure. Pull requests are encouraged. Pull requests are not just for experts: feel free to contribute "tiny" fixes that clean up the visuals, articles about your own CIDER hacking and prototyping, or core contributors tips and tricks.
9 |
10 | The great thing about elisp is that if you're here, you're already familiar with parentheses and evaluation. Understanding and modifying elisp will come second nature. All it takes is an introduction to the codebase, a few tools, and how to write some tests.
11 |
--------------------------------------------------------------------------------
/resources/templates/md/posts/2017-04-26-beginning-to-hack-on-cider.md:
--------------------------------------------------------------------------------
1 | {:title "Basic setup of CIDER for hacking"
2 | :layout :post
3 | :tags ["CIDER" "setup" "emacs"]
4 | :toc true}
5 |
6 | ## Installation ##
7 |
8 | Emacs normally downloads packages into a directory in `~/.emacs.d/` or somewhere else not super accessible. The first step to setting up a way to start hacking on CIDER is to have your own copy that you own. [Fork](https://github.com/clojure-emacs/cider) it, and then add the following to your your init.el or equivalent, but substituting wherever you cloned it for `~/projects/cider`:
9 |
10 | ```emacs-lisp
11 | ;; load local version of cider
12 | (add-to-list 'load-path "~/projects/cider")
13 | (require 'cider)
14 | ```
15 |
16 | Delete any residual CIDER code left over in your melpa directory so that you know there's only one copy of CIDER running and its a permanent one. (This, in [prelude](https://github.com/bbatsov/prelude) is located at `~/.emacs.d/elpa`).
17 |
18 | If you don't have CIDER's dependencies from running it previously from melpa, ensure that the following packages are installed:
19 |
20 | - clojure-mode
21 | - pkg-info
22 | - queue
23 | - spinner
24 | - seq
25 |
26 | See the [installation docs](https://cider.readthedocs.io/en/latest/installation/#manual-installation) for more info.
27 |
28 | ## Begin Introspecting ##
29 |
30 | ### Looking at traffic ###
31 |
32 | CIDER at a highlevel is an elisp client that sends messages back and forth with the cider-nrepl clojure server. The best place to start--whether gathering information for bug reports, development or prototyping--is by toggling the recording of these messages. Toggle this by `M-x nrepl-toggle-message-loggin`. After any interaction, be it autocompelete, code evaluation, etc, there will be a messages buffer with the back and forth of your code.
33 |
34 | ```clojure
35 | (-->
36 | id "55"
37 | op "eldoc"
38 | session "d36ae5a8-16e5-4454-985f-aa028767f33f"
39 | time-stamp "2017-04-26 22:52:29.466403690"
40 | ns "fizzbuzz.core"
41 | symbol "div-yo"
42 | )
43 | (<--
44 | id "55"
45 | session "d36ae5a8-16e5-4454-985f-aa028767f33f"
46 | time-stamp "2017-04-26 22:52:29.471443612"
47 | docstring nil
48 | eldoc (("x" "y"))
49 | name "div-yo"
50 | ns "fizzbuzz.core"
51 | status ("done")
52 | type "function"
53 | )
54 | (-->
55 | id "56"
56 | op "eval"
57 | session "d36ae5a8-16e5-4454-985f-aa028767f33f"
58 | time-stamp "2017-04-26 22:52:31.746495637"
59 | code "(div-yo 4 3)
60 | "
61 | column 16
62 | file "*cider-repl fizzbuzz*"
63 | line 68
64 | ns "fizzbuzz.core"
65 | )
66 | (<--
67 | id "56"
68 | session "d36ae5a8-16e5-4454-985f-aa028767f33f"
69 | time-stamp "2017-04-26 22:52:31.755104708"
70 | ns "fizzbuzz.core"
71 | value "4/3"
72 | )
73 | (<--
74 | id "56"
75 | session "d36ae5a8-16e5-4454-985f-aa028767f33f"
76 | time-stamp "2017-04-26 22:52:31.766114712"
77 | status ("done")
78 | )
79 | (<--
80 | id "56"
81 | session "d36ae5a8-16e5-4454-985f-aa028767f33f"
82 | time-stamp "2017-04-26 22:52:31.767151265"
83 | changed-namespaces (dict)
84 | repl-type "clj"
85 | status ("state")
86 | )
87 |
88 | ```
89 |
90 | These are actual messages from a test project, fizzbuzz, that I keep up for prototyping and testing. Broadly, each message is an id, session, timestamp, and then whatever information happens to be going across. Here, the first of these messages, message 55, is a request for eldoc information about the function `div-yo`. Message 56 is requesting evaluation of a simple invocation of the function and its return value, followed by a done message and a state message.
91 |
92 | Each of these values could probably be a good topic of a post. For example, the session dictates which repl you're talking to, sorta. Each repl (really client buffer) has a process attached with actually two sessions. One session is for standard evaluation like above `(div-yo 3 4)` and the other is the tooling session, meant for things like eldoc and compeletion requests. The fact that these two are in the same session is a bug actually. The separation comes so that when you ask for the last result, `*1`, you'll never get a completition result or eldoc signature but the `4/3` that the function returned.
93 |
94 | ### Debugging Elisp Code ###
95 |
96 | Emacs is quite a beast; self-documenting, several vm's, it is a lisp interpreter at heart. Getting familiar with the debugging tooling and navigation will become quickly second nature due to shared keybindings with Clojure navigation and debugging shortcuts.
97 |
98 | Navigating to functions is trivial in emacs with `M-x find-function`, bound to `C-h f` in vanilla emacsen. If you're running emacs 25 you have a really nice new feature with the `Xref` [package](https://www.gnu.org/software/emacs/manual/html_node/emacs/Xref.html) for navigation. Its a general purpose go to definition bound by default to `M-.` and popping with `M-,`, just like CIDER navigation.
99 |
100 | Once you've found the code you'd like to look at, instrumenting for debugging is trivial as well. Again, it is the same keybinding as CIDER, `C-u C-M-x`. This looks like a mouthful but my hands do this chord out of instinct: it's just the [prefix argument](https://www.gnu.org/software/emacs/manual/html_node/elisp/Prefix-Command-Arguments.html) along with the `eval-defun` command. Just like in CIDER, to de-instrument the function simply re-eval it with `C-M-x` or `eval-defun`.
101 |
102 | Instrumenting code gives you a debugger that's quite extensive. But just remember `n` for next and `c` for continue and you've got perhaps 50% of what you need to get stepping and introspecting and hit `?` for some help.
103 |
104 | ## Running Tests ##
105 |
106 | The testing situation in emacs is not, uhh, the best. But there are a few frameworks in use in emacs and CIDER:
107 |
108 | - [buttercup](https://github.com/jorgenschaefer/emacs-buttercup)
109 | - [cask](https://github.com/cask/cask)
110 |
111 | You'll need to install cask yourself to run the tests reliably. Cask will run emacs headless, make sure that all the dependencies are met, byte compile files and then run your tests for you. The others are assertion libraries for actual tests. Once you have this met, testing is simple as:
112 |
113 | ```shell
114 | cider|master⚡ ⇒ cask install
115 | cider|master⚡ ⇒ make test
116 |
117 | ...
118 | ...
119 |
120 | nrepl--merge
121 | preserves id and session keys of dict1
122 | appends all other keys
123 | dict1 is updated destructively
124 |
125 | Ran 248 specs, 0 failed, in 2.3 seconds.
126 | cider|master⚡ ⇒
127 |
128 | ```
129 |
130 | ## Contributing ##
131 |
132 | The above should be enough to get started hacking in CIDER. The issues list always welcomes discussions and questions from new contributors. There's even a bug label [low-hanging fruit](https://github.com/clojure-emacs/cider/labels/low%20hanging%20fruit) to label things as approachable to those who don't know the code-base very well. Feel free to take issues, fix what bugs you, or contribute to discussions about open issues. The chatroom #cider on [slack](https://clojurians.slack.com/) is a place for help and discussion of feature development.
133 |
134 | For pull requests, make sure to follow the following [guidelines](https://github.com/clojure-emacs/cider/blob/master/.github/CONTRIBUTING.md)
135 |
--------------------------------------------------------------------------------
/resources/templates/md/posts/2017-04-27-basic-setup-cider-nrepl.md:
--------------------------------------------------------------------------------
1 | {:title "Basic setup of cider-nrepl for hacking"
2 | :layout :post
3 | :tags ["cider-nrepl" "setup" "clojure"]
4 | :toc true}
5 |
6 | ## Installation ##
7 |
8 | Working with cider-nrepl is a little more difficult and a little less straightforward. The best way that I've found with it is to install it in your maven directory. When I'm developing on it, I run the shell command `rm -rf ~/.m2/repository/cider/ && lein install`.
9 |
10 | Cider looks for the version that matches it's own:
11 |
12 | ```emacs-lisp
13 | (defvar cider-jack-in-lein-plugins nil
14 | "List of Leiningen plugins where elements are lists of artifact name and version.")
15 | (put 'cider-jack-in-lein-plugins 'risky-local-variable t)
16 | (cider-add-to-alist 'cider-jack-in-lein-plugins
17 | "cider/cider-nrepl" (upcase cider-version))
18 |
19 | ```
20 |
21 | So we just make sure that the cider-nrepl version matches the CIDER version you are working against. If you keep both up to date with the source you should almost never have to worry about this. When CIDER asks for the dependency, it will find your local copy in the maven cache and not fetch a version from clojars.
22 |
23 | ```clojure
24 | (def VERSION "0.15.0-SNAPSHOT")
25 |
26 | (defproject cider/cider-nrepl VERSION
27 | :description "nREPL middlewares for CIDER"
28 | ... )
29 |
30 | ```
31 |
32 | More information is found at the [cider docs](https://cider.readthedocs.io/en/latest/hacking_on_cider/#hacking-on-cider-nrepl).
33 |
34 | ## Broad Overview ##
35 |
36 | The middleware lets you define operations on top of what nrepl itself handles. You can define an operation, say which middleware it requires, and then put its implementation. At the bottom of each middleware file is a macro `set-descriptor!` describing which operations the middleware handles and the handler function. So for example, here's the handler function that refreshes namespaces:
37 |
38 | ```clojure
39 | (defn wrap-refresh
40 | "Middleware that provides code reloading."
41 | [handler]
42 | (fn [{:keys [op] :as msg}]
43 | (case op
44 | "refresh" (refresh-reply (assoc msg :scan-fn dir/scan))
45 | "refresh-all" (refresh-reply (assoc msg :scan-fn dir/scan-all))
46 | "refresh-clear" (clear-reply msg)
47 | (handler msg))))
48 | ```
49 |
50 | As always, consult some documentation at the [nrepl github page.](https://github.com/clojure/tools.nrepl#middleware)
51 |
52 | ## Working In the Codebase ##
53 |
54 | You can jack-in and work with things. The debugger works somewhat as well. Just be prepared to restart emacs or the repl as things get wonky easily.
55 |
56 | I often resort to print statements in the codebase and then install in the maven repository to watch the statements bubble up in CIDER. I wish I had a better solution to this and if anyone does, please do a pull request or your own article about your process.
57 |
58 | ## Testing ##
59 |
60 | In order to run all tests, you need to invoke the tests with profiles, as you can see from the `project.clj`:
61 |
62 | ```clojure
63 | :test-clj {:test-paths ["test/clj"]
64 | :java-source-paths ["test/java"]
65 | :resource-paths ["test/resources"]}
66 | :test-cljs {:test-paths ["test/cljs"]
67 | :dependencies [[com.cemerick/piggieback "0.2.1"]
68 | [org.clojure/clojurescript "1.7.189"]]}
69 | ```
70 | You can see that the testpaths for the CIDER test runner are extended with profiles. So when jacked-in, you can't easily just `C-c C-t p` to run all tests in project, nor does `lein test` run them all.
71 |
72 | The best way to run the tests would be
73 |
74 | ```shell
75 | cider-nrepl> lein with-profile +test-clj test
76 | cider-nrepl> lein with-profile +test-cljs test
77 |
78 | ```
79 | I'm not sure I see a benefit of excluding the test-clj test sources from the base profile and the [docs](https://cider.readthedocs.io/en/latest/hacking_on_cider/#testing-the-code_1) even hint that perhaps its time for this to change.
80 |
81 |
--------------------------------------------------------------------------------
/resources/templates/md/posts/2017-04-30-eval-walkthrough.md:
--------------------------------------------------------------------------------
1 | {:title "Lifecycle Of a Evaluation"
2 | :layout :post
3 | :tags ["cider" "evaluation" "walkthrough"]
4 | :toc true}
5 |
6 | ## Lifecycle of a request ##
7 |
8 | I wanted to give a broad overview of the lifecycle of an eval request, from invocation to marking completed. As with any code walkthrough, ultimately every guide will elide details as the only true witness of what will happen is the code itself. That being said, I'm trying to thread a fine line of not drowning in details while still giving a fairly technical overview of the eval mechanism of CIDER.
9 |
10 | This is a good system to serve as a walkthrough as this is largely what CIDER is: emacs lisp sending messages to your project code running in clojure code. There are lots of other features but at its core, any repl interaction will be this functionality. A good place to start is the message logging that happens when you invoke `M-x nrepl-toggle-message-logging`.
11 |
12 | ```clojure
13 | (-->
14 | id "16"
15 | op "eval"
16 | session "42da1513-54f2-4a29-b4b1-603d07a18434"
17 | time-stamp "2017-04-30 11:20:56.916993414"
18 | code "(+ 1 2)
19 | "
20 | column 12
21 | file "*cider-repl CLJS cljc-bug*"
22 | line 49
23 | ns "cljs.user"
24 | )
25 | (<--
26 | id "16"
27 | session "42da1513-54f2-4a29-b4b1-603d07a18434"
28 | time-stamp "2017-04-30 11:20:56.975191155"
29 | ns "cljs.user"
30 | value "3"
31 | )
32 | (<--
33 | id "16"
34 | session "42da1513-54f2-4a29-b4b1-603d07a18434"
35 | time-stamp "2017-04-30 11:20:56.986582000"
36 | status ("done")
37 | )
38 | (<--
39 | id "16"
40 | session "42da1513-54f2-4a29-b4b1-603d07a18434"
41 | time-stamp "2017-04-30 11:20:56.987795668"
42 | changed-namespaces (dict)
43 | repl-type "cljs"
44 | status ("state")
45 | )
46 | ```
47 |
48 | This post tries to present a fairly technical explanation of these messages and the code that drives evaluation in CIDER.
49 |
50 | ### Outgoing ###
51 |
52 | #### cider-interactive-eval ####
53 |
54 | The code quickly hits [cider-interactive-eval](https://github.com/clojure-emacs/cider/blob/master/cider-interaction.el#L1098). Since we might be eval-ing code in a namespace that has not been loaded yet, [cider--prep-interactive-eval](https://github.com/clojure-emacs/cider/blob/master/cider-interaction.el#L1076)) will make sure that the namespace has been found and evaluated.
55 |
56 | #### asynchronous setup ####
57 |
58 | The communication channel with nrepl is asynchronous using registered callbacks to handle the results of interaction with nrepl. To setup the handler defaults for interactive evaluation, `cider-interactive-eval-handler`. But the real important stuff happens in the [nrepl-make-response-handler](https://github.com/clojure-emacs/cider/blob/master/nrepl-client.el#L737).
59 |
60 | Owing to the asynchronous manner of calling, this makes sure that response handlers clean up after themselves. In particular, from nrepl-make-response-handler:
61 |
62 | ```emacs-lisp
63 | (when (member "done" status)
64 | (nrepl--mark-id-completed id)
65 | (when done-handler
66 | (funcall done-handler buffer)))
67 | ```
68 |
69 | With this callback constructed, it heads into last legs of the outgoing side where the spinner is started up and the callback modified to stop it as well.
70 |
71 | #### Nrepl encoding and operation ####
72 |
73 | The penultimate step on the outgoing side is to set which operation is to be performed, `("op" "eval")`, and then finally sent it "across". This last bit of code is fairly straightforward in [nrepl-send-request](https://github.com/clojure-emacs/cider/blob/master/nrepl-client.el#L805):
74 |
75 | ```emacs-lisp
76 | (defun nrepl-send-request (request callback connection &optional tooling)
77 | "Send REQUEST and register response handler CALLBACK using CONNECTION.
78 | REQUEST is a pair list of the form (\"op\" \"operation\" \"par1-name\"
79 | \"par1\" ... ). See the code of `nrepl-request:clone',
80 | `nrepl-request:stdin', etc. This expects that the REQUEST does not have a
81 | session already in it. This code will add it as appropriate to prevent
82 | connection/session drift.
83 | Return the ID of the sent message.
84 | Optional argument TOOLING Set to t if desiring the tooling session rather than the standard session."
85 | (with-current-buffer connection
86 | (when-let ((session (if tooling nrepl-tooling-session nrepl-session)))
87 | (setq request (append request `("session" ,session))))
88 | (let* ((id (nrepl-next-request-id connection))
89 | (request (cons 'dict (lax-plist-put request "id" id)))
90 | (message (nrepl-bencode request)))
91 | (nrepl-log-message request 'request)
92 | (puthash id callback nrepl-pending-requests)
93 | (process-send-string nil message)
94 | id)))
95 | ```
96 |
97 | Here the session id is extracted from the connection. Connections keep buffer-local variables for the two sessions, tooling and standard, which is now put into the request. Previously, this session was put into the request at earlier stages, leading to some subtle bugs. An id is generated from the buffer-local `nrepl-request-counter`, and the message is bencoded, the transport format used for communication. The message is logged (if toggled, ie, the first `(-->` form at the top of this post). The callback is registered in `nrepl-pending-requests` and the actual transmission is accomplished with `(process-send-string nil message)`.
98 |
99 | ### Incoming ###
100 |
101 | Emacs runs the jvm as a process. And the communication is by the above `process-send-string` and by [filter functions](https://www.gnu.org/software/emacs/manual/html_node/elisp/Filter-Functions.html) that read the resulting output written to standard out.
102 |
103 | When creating the client process in [nrepl-start-client-process](https://github.com/clojure-emacs/cider/blob/master/nrepl-client.el#L637), several things happen:
104 |
105 | - `:response-q` is created `(process-put client-proc :response-q (nrepl-response-queue))`
106 | - `:string-q` is created
107 | - project-dir is set
108 | - endpoints are set
109 | - hash-table for pending and completed requests are set
110 | - the filter is set on outcoming text from nrepl to handle responses.
111 |
112 | The [nrepl-client-filter](https://github.com/clojure-emacs/cider/blob/master/nrepl-client.el#L467) watches the output and keeps storing it in a variable associated with the process called :string-q (think string queue) to gather incoming strings. This gets moved over into the response queue when the following failsafe test is true:
113 |
114 | ```emacs-lisp
115 | ;; Start decoding only if the last letter is 'e'
116 | (when (eq ?e (aref string (1- (length string))))
117 | ```
118 | The letter `e` is a fine marker for the end of encoded input. Once the string has been decoded and put into the response queue, the callbacks are called. `nrepl-response-handler-functions`, which is something set globally at repl creation, and the meat: `(nrepl--dispatch-response response)`.
119 |
120 | ```emacs-lisp
121 | (while (queue-head response-q)
122 | (with-current-buffer (process-buffer proc)
123 | (let ((response (queue-dequeue response-q)))
124 | (with-demoted-errors "Error in one of the `nrepl-response-handler-functions': %s"
125 | (run-hook-with-args 'nrepl-response-handler-functions response))
126 | (nrepl--dispatch-response response))))
127 | ```
128 |
129 | The dispatch response function logs the message, gets the callback and invokes it. The importance of the id is shown here, as this is the key logged into the `nrepl-pending-requests` hashmap and used to invoke the callback later after as the response is received. In our example here, this would write the to the repl, but in general this is just a big case statement:
130 |
131 | ```emacs-lisp
132 | (cond (value
133 | (when value-handler
134 | (funcall value-handler buffer value)))
135 | (out
136 | (when stdout-handler
137 | (funcall stdout-handler buffer out)))
138 | (pprint-out
139 | (cond (pprint-out-handler (funcall pprint-out-handler buffer pprint-out))
140 | (stdout-handler (funcall stdout-handler buffer pprint-out))))
141 | (err
142 | (when stderr-handler
143 | (funcall stderr-handler buffer err)))
144 | (status
145 | (when (member "interrupted" status)
146 | (message "Evaluation interrupted."))
147 | (when (member "eval-error" status)
148 | (funcall (or eval-error-handler nrepl-err-handler)))
149 | (when (member "namespace-not-found" status)
150 | (message "Namespace not found."))
151 | (when (member "need-input" status)
152 | (cider-need-input buffer))
153 | (when (member "done" status)
154 | (nrepl--mark-id-completed id)
155 | (when done-handler
156 | (funcall done-handler buffer))))))))
157 | ```
158 |
159 | We can again see when the id is marked complete `(nrepl--mark-id-completed id)`.
160 |
161 | The last bit that happens in the lifecycle of a request is the hook that runs from the client-filter:
162 |
163 | ```emacs-lisp
164 | (run-hook-with-args 'nrepl-response-handler-functions response)
165 | ```
166 |
167 | This serves to invoke the following state handler:
168 |
169 | ```emacs-lisp
170 | (defun cider-repl--state-handler (response)
171 | "Handle the server state contained in RESPONSE.
172 | Currently, this is only used to keep `cider-repl-type' updated."
173 | (with-demoted-errors "Error in `cider-repl--state-handler': %s"
174 | (when (member "state" (nrepl-dict-get response "status"))
175 | (nrepl-dbind-response response (repl-type changed-namespaces)
176 | (when repl-type
177 | (setq cider-repl-type repl-type))
178 | (unless (nrepl-dict-empty-p changed-namespaces)
179 | (setq cider-repl-ns-cache (nrepl-dict-merge cider-repl-ns-cache changed-namespaces))
180 | (dolist (b (buffer-list))
181 | (with-current-buffer b
182 | ;; Metadata changed, so signatures may have changed too.
183 | (setq cider-eldoc-last-symbol nil)
184 | (when (or cider-mode (derived-mode-p 'cider-repl-mode))
185 | (when-let ((ns-dict (or (nrepl-dict-get changed-namespaces (cider-current-ns))
186 | (let ((ns-dict (cider-resolve--get-in (cider-current-ns))))
187 | (when (seq-find (lambda (ns) (nrepl-dict-get changed-namespaces ns))
188 | (nrepl-dict-get ns-dict "aliases"))
189 | ns-dict)))))
190 | (cider-refresh-dynamic-font-lock ns-dict))))))))))
191 | ```
192 |
193 | This is some not very nice code. This watches for the following status messages:
194 |
195 | ```clojure
196 | (<--
197 | id "16"
198 | session "42da1513-54f2-4a29-b4b1-603d07a18434"
199 | time-stamp "2017-04-30 11:20:56.987795668"
200 | changed-namespaces (dict)
201 | repl-type "cljs"
202 | status ("state")
203 | )
204 | ```
205 |
206 | In particular, note the looping over all open buffers **not just clojure buffers** and sets buffer local variables (it hopes) to nil. Further, it's not smart enough to remember its important buffers but uses `cider-mode` and `cider-repl-mode` as markers for important dictionaries of namespaces. These are used to font-lock the relevant buffers with known clojure and project function names, macros, etc.
207 |
208 | The main point of it is to record what the repl type is, `'clj` or `'cljs` as well as font-lock the buffers.
209 |
210 | ## Navigation ##
211 |
212 | Throughout all of this, `xref-find-defintion` and `M-x rgrep` have been invaluable. Getting used to these tools makes navigating CIDER quite easy.
213 |
214 | ## Wrap-up ##
215 |
216 | While its possible that there are some minor mistakes, this cuts quite a swatch across the codebase. the hope is that knowing these mechanics, idioms, and variables gives aid in bug reporting, debugging, and general confidence for newcomers to jump into the codebase. Take a few minutes and navigate through the whole lifecycle.
217 |
218 |
--------------------------------------------------------------------------------
/resources/templates/md/posts/2017-05-10-font-lock-bug.md:
--------------------------------------------------------------------------------
1 | {:title "Font Locking Required Namespaces"
2 | :layout :post
3 | :tags ["cider" "bug"]
4 | :toc true}
5 |
6 | ## Description of the bug ##
7 |
8 | This is a walkthrough of what I've done so far to diagnose the bug report for CIDER [bug 1985](https://github.com/clojure-emacs/cider/issues/1985). When we are loading dependencies, external or just other project namespaces, we are losing the font-locking.
9 |
10 | ```clojure
11 | (ns stuff.core
12 | (:require [stuff.other :as other]))
13 |
14 | (other/function :foo) ;; is not font-locked
15 |
16 | ```
17 |
18 | ## Moving Parts ##
19 |
20 | ### The Handler ###
21 |
22 | The main entry-point for font-locking is in `cider-repl--state-handler`. This watches the nrepl output and when it sees a state message, it inspects it, sets the repl type, watches for new namespaces to cache, and updates the font-locking. CIDER maintains a cache of vars just watching for changes.
23 |
24 | ```emacs-lisp
25 | (defun cider-repl--state-handler (response)
26 | ....
27 | (when-let ((ns-dict (or (nrepl-dict-get changed-namespaces (cider-current-ns))
28 | (let ((ns-dict (cider-resolve--get-in (cider-current-ns))))
29 | (when (seq-find (lambda (ns) (nrepl-dict-get changed-namespaces ns))
30 | (nrepl-dict-get ns-dict "aliases"))
31 | ns-dict)))))
32 | (cider-refresh-dynamic-font-lock ns-dict)))))))))) ;; beginning of the font-locking
33 | ```
34 |
35 | ### cider-refresh-dynamic-font-lock ###
36 |
37 | This function just grabs the necessary information and compiles the regex that is used by the font-locking mechanism. For our purposes, it has two important parts: gets the symbols to font-lock in `(cider-resolve-ns-symbols ns)` (which notable puts the separator `/`) and then calls emacs font-locking mechanism with the compiled font-lock keywords.
38 |
39 | ```emacs-lisp
40 | (setq-local cider--dynamic-font-lock-keywords
41 | (cider--compile-font-lock-keywords
42 | symbols (cider-resolve-ns-symbols (cider-resolve-core-ns))))
43 | (font-lock-add-keywords nil cider--dynamic-font-lock-keywords 'end)
44 | ```
45 |
46 | ### cider--compile-font-lock-keywords ###
47 |
48 | Oddly enough, this function seemingly works correctly. I modified this function so that it does less work and is easier to step through so we can investigate what is going on.
49 |
50 | (defun cider--compile-font-lock-keywords (symbols-plist core-plist)
51 | "Return a list of font-lock rules for the symbols in SYMBOLS-PLIST and CORE-PLIST."
52 | (let ((cider-font-lock-dynamically ;; (if (eq cider-font-lock-dynamically t)
53 | ;; '(function var macro core deprecated)
54 | ;; cider-font-lock-dynamically)
55 | '(function)
56 | )
57 | deprecated enlightened
58 | macros functions vars instrumented traced)
59 | (cl-labels ((handle-plist
60 | (plist)
61 | (let ((do-function (memq 'function cider-font-lock-dynamically))
62 | (do-var (memq 'var cider-font-lock-dynamically))
63 | (do-macro (memq 'macro cider-font-lock-dynamically))
64 | (do-deprecated (memq 'deprecated cider-font-lock-dynamically)))
65 | (while plist
66 | (let ((sym (pop plist))
67 | (meta (pop plist)))
68 | ;; (pcase (nrepl-dict-get meta "cider.nrepl.middleware.util.instrument/breakfunction")
69 | ;; (`nil nil)
70 | ;; (`"#'cider.nrepl.middleware.debug/breakpoint-if-interesting"
71 | ;; (push sym instrumented))
72 | ;; (`"#'cider.nrepl.middleware.enlighten/light-form"
73 | ;; (push sym enlightened)))
74 | ;; ;; The ::traced keywords can be inlined by MrAnderson, so
75 | ;; ;; we catch that case too.
76 | ;; ;; FIXME: This matches values too, not just keys.
77 | ;; (when (seq-find (lambda (k) (and (stringp k)
78 | ;; (string-match (rx "clojure.tools.trace/traced" eos) k)))
79 | ;; meta)
80 | ;; (push sym traced))
81 | ;; (when (and do-deprecated (nrepl-dict-get meta "deprecated"))
82 | ;; (push sym deprecated))
83 | (cond ((and do-macro (nrepl-dict-get meta "macro"))
84 | (push sym macros))
85 | ((and do-function (nrepl-dict-get meta "arglists"))
86 | (push sym functions))
87 | (do-var (push sym vars))))))))
88 | (when (memq 'core cider-font-lock-dynamically)
89 | (let ((cider-font-lock-dynamically '(function var macro core deprecated)))
90 | (handle-plist core-plist)))
91 | (handle-plist symbols-plist))
92 | `(
93 | ,@(when macros
94 | `((,(concat (rx (or "(" "#'")) ; Can't take the value of macros.
95 | "\\(" (regexp-opt macros 'symbols) "\\)")
96 | 1 (cider--unless-local-match font-lock-keyword-face))))
97 | ,@(when functions
98 | `((,(regexp-opt functions 'symbols) 0
99 | (cider--unless-local-match font-lock-function-name-face))))
100 | ;; ,@(when vars
101 | ;; `((,(regexp-opt vars 'symbols) 0
102 | ;; (cider--unless-local-match font-lock-variable-name-face))))
103 | ;; ,@(when deprecated
104 | ;; `((,(regexp-opt deprecated 'symbols) 0
105 | ;; (cider--unless-local-match 'cider-deprecated-face) append)))
106 | ;; ,@(when enlightened
107 | ;; `((,(regexp-opt enlightened 'symbols) 0
108 | ;; (cider--unless-local-match 'cider-enlightened-face) append)))
109 | ;; ,@(when instrumented
110 | ;; `((,(regexp-opt instrumented 'symbols) 0
111 | ;; (cider--unless-local-match 'cider-instrumented-face) append)))
112 | ;; ,@(when traced
113 | ;; `((,(regexp-opt traced 'symbols) 0
114 | ;; (cider--unless-local-match 'cider-traced-face) append)))
115 | )))
116 |
117 | You can see the way that font locking is divided up: `(function var macro core deprecated)` We only want to investigate functions so we set it to that. This also prevents the core from being font-locked as well, as this will make the result quite large. There are several accumulators setup for macros, deprecated, vars, etc. The important bit for this here is
118 |
119 | ```emacs-lisp
120 | ((and do-function (nrepl-dict-get meta "arglists"))
121 | (push sym functions))
122 | ```
123 |
124 | So there's the secret sauce for font-locking: it looks for arglists metadata. If so, you get font-locked. Since we've commented out so much of the function, I instrument it so we can step through it and watch for any bad information. The plist we are working through looks like this:
125 |
126 | ```emacs-lisp
127 | (foo (dict arglists ([x]) doc "I don't do a whole lot.") uses-import (dict arglists ([x])) i/bar (dict arglists ([x])))
128 | ```
129 |
130 | If you visit the nrepl-dict files, you'll find out that a cider dictionary is a list with the first term of `dict`. Owing to grabbing this from string methosd, some quotation marks are missing but basically this is a plist of term to dictionary: `"foo"` has a dictionary of arglists and doc. Since we are looking for arglists, all three of these functions qualify. The `regex-opt` function will compile that down into this beauty:
131 |
132 | ```emacs-lisp
133 | (("\\_<\\(foo\\|i/bar\\|uses-import\\)\\_>" 0
134 | (cider--unless-local-match font-lock-function-name-face)))
135 | ```
136 | Earlier when I said that it was working out, this is my evidence. That regex includes `i/bar` in it. The best I can think of is that emacs internal stuff requires some extra escaping, but it doesn't really make sense. To make matters _more_ confusing, you can edit the separator and then it will work. For example, in `cider-resolve-ns-symbols`, you can see where the separator is introduced when you map over the ns cache aliases:
137 |
138 | ```emacs-lisp
139 | (nrepl-dict-flat-map (lambda (sym meta)
140 | (list (concat alias "/" sym) meta))
141 | (cider-resolve--get-in namespace "interns"))
142 | ```
143 |
144 | So if you turn that slash into `*` and change the separator you use in your code, your code won't compile but it _will_ font-lock. I have no idea and I'm just hoping that if anyone wants to pick up this thread this can help them along the way.
145 |
--------------------------------------------------------------------------------
/resources/templates/themes/blue/css/screen.css:
--------------------------------------------------------------------------------
1 | h1, h2, h3, h4, h5, h6 {
2 | font-family: 'Alegreya';
3 | }
4 |
5 | body {
6 | color: #333;
7 | background-color: #f2f2f2;
8 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
9 | font-size: 16px;
10 | }
11 |
12 | .container {
13 | max-width: 1000px;
14 | }
15 |
16 | .right {
17 | float: right;
18 | text-align: right;
19 | }
20 |
21 | .navbar {
22 | border-radius: 0;
23 | box-shadow: 0 0 0 0,0 6px 12px rgba(34,34,34,0.3);
24 | }
25 |
26 | .navbar-default {
27 | background-color: #428bca;
28 | border: none;
29 | }
30 |
31 | .navbar-default .navbar-brand {
32 | color: #fff;
33 | font-family: 'Alegreya';
34 | }
35 |
36 | .navbar-default .navbar-brand:hover {
37 | color: #fff;
38 | }
39 |
40 | .navbar-default .navbar-nav li a {
41 | color: #fff;
42 | }
43 |
44 | .navbar-default .navbar-nav li a:hover {
45 | color: #fff;
46 | background-color: #3d80ba;
47 | }
48 |
49 | .navbar-default .navbar-nav .active a {
50 | color: #fff;
51 | background-color: #3d80ba;
52 | }
53 |
54 | .navbar-default .navbar-toggle:hover{
55 | background-color: #3d80ba;
56 | }
57 |
58 | .navbar-default .navbar-toggle .icon-bar {
59 | background-color: #fff;
60 | }
61 |
62 | #sidebar {
63 | margin-left: 15px;
64 | margin-top: 50px;
65 | }
66 |
67 | #content {
68 | background-color: #fff;
69 | border-radius: 3px;
70 | box-shadow: 0 0 0 0,0 6px 12px rgba(34,34,34,0.1);
71 | }
72 |
73 | #content img {
74 | max-width: 100%;
75 | height: auto;
76 | }
77 |
78 | footer {
79 | font-size: 14px;
80 | text-align: center;
81 | padding-top: 75px;
82 | padding-bottom: 30px;
83 | }
84 |
85 | #post-tags {
86 | margin-top: 30px;
87 | }
88 |
89 | #prev-next {
90 | padding: 15px 0;
91 | }
92 |
93 | .post-header {
94 | margin-bottom: 20px;
95 | }
96 | .post-header h2 {
97 | font-size: 32px;
98 | }
99 |
100 | #post-meta {
101 | font-size: 14px;
102 | color: rgba(0,0,0,0.4)
103 | }
104 |
105 | #page-header {
106 | border-bottom: 1px solid #dbdbdb;
107 | margin-bottom: 20px;
108 | }
109 | #page-header h2 {
110 | font-size: 32px;
111 | }
112 |
113 | pre {
114 | overflow-x: auto;
115 | }
116 | pre code {
117 | display: block;
118 | padding: 0.5em;
119 | overflow-wrap: normal;
120 | white-space: pre;
121 | }
122 |
123 | code {
124 | color: #428bca;
125 | }
126 |
127 | pre, code, .hljs {
128 | background-color: #f7f9fd;
129 | }
130 |
131 | @media (min-width: 768px) {
132 | .navbar {
133 | min-height: 70px;
134 | }
135 | .navbar-nav>li>a {
136 | padding: 30px 20px;
137 | }
138 | .navbar-default .navbar-brand {
139 | font-size: 36px;
140 | padding: 25px 15px;
141 | }
142 | #content{
143 | margin-top: 30px;
144 | padding: 30px 40px;
145 | }
146 | }
147 |
148 | @media (max-width: 767px) {
149 | body{
150 | font-size: 14px;
151 | }
152 | .navbar-default .navbar-brand {
153 | font-size: 30px;
154 | }
155 | #content{
156 | padding: 15px;
157 | }
158 | #post-meta .right {
159 | float:left;
160 | text-align: left;
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/resources/templates/themes/blue/html/404.html:
--------------------------------------------------------------------------------
1 |
2 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |