#+TITLE: Dojo | Emacs London #+EXPORT_FILE_NAME: ./dojo.html * Emacs Lisp dojo A dojo is a meetup format that involves coding on projects or exercises, usually in groups, for the purpose of practice and learning. There are many dojo meetups that run for different audiences. Attendees may be familiar with the [[http://www.londonclojurians.org/code-dojo/][London Clojure Dojo]]. Our format is: 1. Everyone suggests ideas for a project. 2. Break into small groups, each focusing on a project (or on the exercises below). 3. Work for 90 minutes. 4. Present some learnings, or maybe head straight to a pub. * [[https://raw.githubusercontent.com/london-emacs-hacking/london-emacs-hacking.github.io/master/dojo.org][Interactive exercises]] These are some exercises to help learn and explore Emacs and Elisp. The aim is to fill in the functions labelled ~__~, so that all the ~Pass:~ messages show ~t~ instead of ~nil~. They are inspired by the interactive Clojure exercises on [[https://www.google.co.uk/url?sa=t&rct=j&q=&esrc=s&source=web&cd=2&ved=2ahUKEwiTyNypxavnAhXLQEEAHVdVB5gQjBAwAXoECAgQCA&url=http%3A%2F%2Fwww.4clojure.com%2Fproblems&usg=AOvVaw0pa24yxcj-qETh0ze7m_08][4Clojure]]. Alternatively, you may want to explore the [[Open-ended project ideas][open-ended project ideas]] at the bottom of this page. ** Working with this org-mode file This web page is written as an org-mode file, which means you can [[https://raw.githubusercontent.com/london-emacs-hacking/london-emacs-hacking.github.io/master/dojo.org][get it on github]], open it in Emacs, and execute the code snippets by moving to a code block and pressing ~C-c C-c~. You can also open and edit the code block in emacs-lisp mode by pressing ~C-c '~. ** Resources to help If you're new to Elisp, the resources below can act as starting points or references. - Emacs itself: there are some very useful features to help you learn about the language, available functions, current values of your variables, and more. These are all accessible via ~C-h~. You can [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Help.html][start reading about them in the manual]]. - The [[https://www.gnu.org/software/emacs/manual/html_node/elisp/index.html][Emacs Lisp reference manual]] is very comprehensive if you know what you want to read about. This can also be read within Emacs by using ~C-h i m Elisp RET~. - Steve Yegge wrote [[https://steve-yegge.blogspot.com/2008/01/emergency-elisp.html][a brief overview of all the language features]]. It's quite old now but is still a nice place to start. - [[http://ergoemacs.org/emacs/elisp.html][Xah Lee's practical Emacs Lisp guide]] may be easier to digest than the full manual, and covers a lot of topics. ** Contributing Changes and new exercises are very welcome! Please [[https://github.com/london-emacs-hacking/london-emacs-hacking.github.io][submit PRs on github]]. ** Index | Title | |----------------------------------------------------| | [[Strings - uppercase]] | | [[Penultimate element]] | | [[Fizzbuzz]] | | [[Flatten an arbitrarily nested list]] | | [[Count the lines and words in a buffer]] | | [[Convert a list into a hash table]] | | [[Reimplement mapcar]] | | [[Handle errors]] | | [[Throw and catch]] | | [[Use a regex to extract numbers from a buffer ]] | | [[Make an HTTP request and parse the JSON response]] | | [[Read output from a shell command]] | | [[Parse data from an org buffer]] | | [[Apply a custom font face to TODO words in a buffer]] | | [[Anagram finder]] | | [[Analyse a tic tac toe board]] | | [[Advice: 2 + 2 = 5]] | ** Strings - uppercase Transform the given string into uppercase: #+begin_src emacs-lisp (defun __ (s)) (let* ((result (__ "helloworld")) (pass (string= result "HELLOWORLD"))) (format "Pass:%S\n%S" pass result)) #+end_src ** Penultimate element Return the second last element in a list. If there is only one item in the list, return nil. #+begin_src emacs-lisp (defun __ (li) nil) (let* ((result1 (__ '(1 2 3 4 5))) (pass1 (equal result1 4)) (result2 (__ '("a" "b" "c"))) (pass2 (equal result2 "b")) (result3 (__ '([1 2] [3 4]))) (pass3 (equal result3 [1 2])) (result4 (__ '(1))) (pass4 (equal result4 nil))) (format "Pass:%S\n%S\n\nPass:%S:\n%S\n\nPass:%S\n%S\n\nPass:%S\n%S" pass1 result1 pass2 result2 pass3 result3 pass4 result4)) #+end_src (Ported from [[http://www.4clojure.com/problem/20][4Clojure #20]]) ** Fizzbuzz Return a list with the results of fizzbuzz for a range of 1-100. This means: - If the number is divisible by 3, the value is "Fizz". - If the number is divisible by 5, the value is "Buzz". - If the number is divisible by 3 and 5, the value is "FizzBuzz". #+begin_src emacs-lisp (defun __ () nil) (let* ((expected '(1 2 "Fizz" 4 "Buzz" "Fizz" 7 8 "Fizz" "Buzz" 11 "Fizz" 13 14 "FizzBuzz" 16 17 "Fizz" 19 "Buzz" "Fizz" 22 23 "Fizz" "Buzz" 26 "Fizz" 28 29 "FizzBuzz" 31 32 "Fizz" 34 "Buzz" "Fizz" 37 38 "Fizz" "Buzz" 41 "Fizz" 43 44 "FizzBuzz" 46 47 "Fizz" 49 "Buzz" "Fizz" 52 53 "Fizz" "Buzz" 56 "Fizz" 58 59 "FizzBuzz" 61 62 "Fizz" 64 "Buzz" "Fizz" 67 68 "Fizz" "Buzz" 71 "Fizz" 73 74 "FizzBuzz" 76 77 "Fizz" 79 "Buzz" "Fizz" 82 83 "Fizz" "Buzz" 86 "Fizz" 88 89 "FizzBuzz" 91 92 "Fizz" 94 "Buzz" "Fizz" 97 98 "Fizz" "Buzz" )) (result (__)) (pass (equal result expected))) (format "Pass:%S\n%S" pass result)) #+end_src ** Flatten an arbitrarily nested list Pull out all the nested elements into a single list: #+begin_src emacs-lisp (defun __ (a &rest b)) (let* ((result1 (__ '(1 2 3))) (pass1 (equal result1 '(1 2 3))) (result2 (__ '(1 2 (3 4) (5 (6 (7)))))) (pass2 (equal result2 '(1 2 3 4 5 6 7))) (result3 (__ '("a" "b") '(c nil d) '(1 (2 3 (4 5))))) (pass3 (equal result3 '("a" "b" c d 1 2 3 4 5)))) (format "Pass:%S\n%S\nPass:%S\n%S\nPass:%S\n%S" pass1 result1 pass2 result2 pass3 result3)) #+end_src ** Convert a list into a hash table Given a list of cons cells (eg. ~'((a . 123) (b . 456))~) create a hash table where the car is the key and the cdr is the value. #+begin_src emacs-lisp (defun __ (s)) (let* ((result (__ '((a . 123) (b . 456) (c . "789")))) (pass (equal result #s(hash-table size 3 data (a 123 b 456 c "789"))))) (format "Pass:%S\n%S" pass result)) #+end_src ** Count the lines and words in a buffer Depending on whether the second argument is the symbol ~'lines~ or ~'words~, you should return a number that represents the appropriate count. Bonus points if you don't use any of the default line/word count functions. #+begin_src emacs-lisp (defun __ (buffer arg) 0) (with-temp-buffer (insert "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Maecenas sed enim ut sem viverra aliquet. In mollis nunc sed id semper risus in. In hac habitasse platea dictumst quisque sagittis. Facilisis magna etiam tempor orci eu. Tempus egestas sed sed risus pretium quam vulputate dignissim. Eros donec ac odio tempor orci dapibus ultrices. Nam libero justo laoreet sit. Quis imperdiet massa tincidunt nunc pulvinar. Quis imperdiet massa tincidunt nunc pulvinar sapien et. Consequat semper viverra nam libero justo. Volutpat maecenas volutpat blandit aliquam etiam erat. Viverra tellus in hac habitasse platea. Condimentum vitae sapien pellentesque habitant. Eleifend mi in nulla posuere sollicitudin aliquam ultrices sagittis orci. Vitae elementum curabitur vitae nunc sed velit dignissim sodales. Vel risus commodo viverra maecenas. Tellus molestie nunc non blandit massa enim. Sed ullamcorper morbi tincidunt ornare massa eget egestas purus viverra. Donec ultrices tincidunt arcu non sodales neque sodales ut. Eu tincidunt tortor aliquam nulla. Amet justo donec enim diam vulputate. Tristique senectus et netus et malesuada fames. Tellus elementum sagittis vitae et. Blandit cursus risus at ultrices mi tempus. Sit amet purus gravida quis blandit turpis. Metus vulputate eu scelerisque felis imperdiet. Nulla porttitor massa id neque. Dictum fusce ut placerat orci nulla pellentesque. Pulvinar mattis nunc sed blandit libero volutpat sed. Amet venenatis urna cursus eget nunc scelerisque viverra mauris in. Morbi quis commodo odio aenean. Pellentesque massa placerat duis ultricies. Tristique sollicitudin nibh sit amet. Gravida cum sociis natoque penatibus et magnis dis parturient. Ut ornare lectus sit amet est. Enim nunc faucibus a pellentesque sit amet porttitor. Nisl suscipit adipiscing bibendum est ultricies integer quis. Risus pretium quam vulputate dignissim suspendisse in.") (let* ((result1 (__ (current-buffer) 'lines)) (pass1 (= result1 21)) (result2 (__ (current-buffer) 'words)) (pass2 (= result2 283))) (format "Pass:%S\n%S\nPass:%S\n%S" pass1 result1 pass2 result2))) #+end_src ** Reimplement mapcar [[https://en.wikipedia.org/wiki/Map_(higher-order_function)][map]] is a function that applies a given function to each element of a given list. In Emacs there are a [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Mapping-Functions.html][few variants of map functions]]. Write a function that mimics the behaviour of ~mapcar~. It accepts two argument: the function to apply, and the list of args to operate on. #+begin_src emacs-lisp (defun __ (fn args)) (let* ((result1 (__ 'upcase '("apple" "banana" "pear"))) (pass1 (equal result1 '("APPLE" "BANANA" "PEAR"))) (result2 (__ (lambda (arg) (= 0 (mod arg 2))) '(0 1 2 3 4 5 6 7 8 9))) (pass2 (equal result2 '(t nil t nil t nil t nil t nil)))) (format "Pass:%S\n%S\nPass:%S\n%S" pass1 result1 pass2 result2)) #+end_src ** Handle errors You can find some information on error handling [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Handling-Errors.html#Handling-Errors][in the manual]], along with a [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Standard-Errors.html#Standard-Errors][list of standard errors]]. Write a function that catches some particular errors and returns their value. Hint: you probably want to use ~(condition-case)~. #+begin_src emacs-lisp (defun __ (fn)) (let* ((result1 (__ (lambda () (error "Something went wrong: %s" 'mysymbol)))) (pass1 (equal result1 "Something went wrong: mysymbol")) (result2 (__ (lambda () (/ 5 0)))) (pass2 (equal result2 "Division by zero")) (result3 (__ (lambda () (throw 'catchmeplease "myvalue")))) (pass3 (equal result3 '(uncaught . "myvalue")))) (format "Pass:%S\n%S\nPass:%S\n%S\nPass:%S\n%S" pass1 result1 pass2 result2 pass3 result3)) #+end_src ** Throw and catch ~catch~ and ~throw~ are used for control flow, but not necessarily errors. Write a function that executes the given ~fn~, catches the ~throw~ from our lambda, and returns the expected value: #+begin_src emacs-lisp (defun __ (fn)) (let* ((result (__ (lambda () (throw 'label "message")))) (pass (equal result '(label . "message")))) (format "Pass:%S\n%S" pass result)) #+end_src ** Use a regex to extract numbers from a buffer The numbers should be extracted into a list. Note: ~\d~ is not supported. #+begin_src emacs-lisp (defun __ (buffer)) (let* ((result1 (with-temp-buffer (insert "There are 2 numbers in this buffer. The second one is 123") (__ (current-buffer)))) (pass1 (equal result1 '(2 123))) (result2 (with-temp-buffer (insert "There are no numbers in this buffer") (__ (current-buffer)))) (pass2 (equal result2 nil))) (format "Pass:%S\n%S\nPass:%S\n%S" pass1 result1 pass2 result2)) #+end_src ** Make an HTTP request and parse the JSON response Make an HTTP request to httpbin.org and convert the "args" from the JSON response into an alist. #+begin_src emacs-lisp (defun __ (url) nil) (let* ((result (__ "https://httpbin.org/get?one=two&three=four")) (pass (equal result '((one . "two") (three . "four"))))) (format "Pass:%S\n%S" pass result)) #+end_src ** Read output from a shell command There are various ways to call external processes in Emacs. Eval the given argument in a shell and return the result. #+begin_src emacs-lisp (defun __ (shellcommand)) (let* ((result (__ "echo 'always eval user input'")) (pass (equal result "always eval user input"))) (format "Pass:%S\n%S" pass result)) #+end_src ** Parse data from an org buffer Given the string below, convert it to an org-mode buffer, parse out the headline and tags, and return a list where each item looks like ~("HEADLINE" . '("TAG1" "TAG2"))~. #+begin_src emacs-lisp (defun __ (s) nil) (let* ((org-string "* Headline one :foo: ,* Headline two :foo:bar: ,* Headline three :bar:") (result (__ org-string)) (pass (equal result '(("Headline one" . ("foo")) ("Headline two" . ("foo" "bar")) ("Headline three" . ("bar")))))) (format "Pass:%S\n%S" pass result)) #+end_src ** Apply a custom font face to TODO words in a buffer Each "TODO" word in the buffer below should have ~my-todo-face~ applied. You might find it easier to open a test buffer where you can visually check the faces. When you're done, try adjusting the exercise to support and test multiple faces, or extend the face to apply to the rest of the line matching TODO. The [[https://www.emacswiki.org/emacs/fic-mode.el][fic-mode codebase]] may help you: it's a minor-mode that essentially just highlights some user-defined keywords. #+begin_src emacs-lisp (defface emacs-london/my-todo-face '((t (:inherit 'warning))) "") (defun emacs-london/check-faces (buffer) "This returns a list where each item is t if the TODO font matches my-todo-face, else nil" (with-current-buffer buffer (save-excursion (goto-char (point-min)) (cl-loop until (not (search-forward-regexp "TODO" nil t)) collect (equal (face-at-point t) 'emacs-london/my-todo-face))))) (defun __ (buffer)) (with-temp-buffer (insert "import math # TODO: remove unused import def main(arg): helloworld = 'helloworld' # TODO: inline this? return helloworld # TODO: execute main()?") (let* ((result (progn (__ (current-buffer)) (emacs-london/check-faces (current-buffer)))) (pass (equal result '(t t t) ))) (format "Pass:%S\n%S" pass result))) #+end_src ** Anagram finder Write a function which finds all the anagrams in a given vector of words. Your function should return a list of lists, where each sub-list is a group of words which are anagrams of each other. Words without any anagrams should not be included in the result. #+begin_src emacs-lisp (defun __ (v) nil) (let* ((result (__ ["meat" "mat" "team" "mate" "eat"])) (pass (equal result '(("meat" "team" "mate")))) (result2 (__ ["veer" "lake" "item" "kale" "mite" "ever"])) (pass2 (equal result '(("veer" "ever") ("lake" "kale") ("mite item"))))) (format "Pass:%S\n%S\n\nPass:%S\n%S" pass result pass2 result2)) #+end_src (Ported from [[http://www.4clojure.com/problem/77][4Clojure #77]]). ** Analyse a tic tac toe board A tic-tac-toe board is represented by a two dimensional vector. X is represented by ~'x~, O is represented by ~'o~, and empty is represented by ~'e~. A player wins by placing three Xs or three Os in a horizontal, vertical, or diagonal row. Write a function which analyses a tic-tac-toe board and returns ~'x~ if X has won, ~'o~ if O has won, and ~nil~ if neither player has won. #+begin_src emacs-lisp (defun __ (board) nil) (let* ((result1 (__ [[e e e] [e e e] [e e e]])) (pass1 (equal nil result1)) (result2 (__ [[x e o] [x e e] [x e o]])) (pass2 (equal 'x result2)) (result3 (__ [[e x e] [o o o] [x e x]])) (pass3 (equal 'o result3)) (result4 (__ [[x e o] [x x e] [o x o]])) (pass4 (equal nil result4)) (result5 (__ [[x e o] [o o e] [o e x]])) (pass5 (equal 'o result5))) (format "Pass:%S\n%S\nPass:%S\n%S\nPass:%S\n%S\nPass:%S\n%S\nPass:%S\n%S\n" pass1 result1 pass2 result2 pass3 result3 pass4 result4 pass5 result5)) #+end_src (Ported from http://www.4clojure.com/problem/73). ** Advice: 2 + 2 = 5 The advice feature allows you to decorate other functions. This means you can patch existing code to change its behaviour. The first time your function is called, it should advise the ~+~ function so that ~(+ 2 2)~ returns 5. The second time it's called, it should remove the advise, so that subsequent calls to ~+~ return 4 again. *Be careful with this. Breaking ~(+)~ could have unintended consequences!* #+begin_src emacs-lisp (defun __ () nil) (let* ((result1 (+ 2 2)) (pass1 (equal result1 4)) (result2 (progn (__) (+ 2 2))) (pass2 (equal result2 5)) (result3 (progn (__) (+ 2 2))) (pass3 (equal result3 4))) (format "Pass:%S\n%S\n\nPass:%S:\n%S\n\nPass:%S\n%S" pass1 result1 pass2 result2 pass3 result3)) #+end_src ** COMMENT exercise topics - something with symbols - convert to/from string - vectors - buffers - frames - moving the point - hash tables - strings - regex - catch / throw / unwind-protect - read input with completing-read - defstruct - class - manipulating dates - compare them, display as certain vals - macros - environment variables - clipboard - keymaps - replacing stdlib functions, eg. count-lines - files - windows - external commands/processes - regions - text properties - indirect buffers - defining modes - variable scope - use let. * Open-ended project ideas Some ideas to get the conversation started: - Write a syntax highlighter for a language of your choice. - Build a fuzzy browser search interface to replace Spotlight/Alfred. You can use [[http://xenodium.com/emacs-utilities-for-your-os/][Alvaro's excellent post]] as a starting point. - Write a test runner that can run tests and display their output. - Write your own modeline. See the [[file:projects.org][projects page]] for more ideas.