Attempt to cache component state lookups

Systems locally cache state on each frame so if there are common
component states being selected by the entities being worked on they
will hit the cache. Has not resulted in a FPS improvement.

--- src/cljs/chocolatier/engine/ecs.cljs
+++ src/cljs/chocolatier/engine/ecs.cljs
@@ -133,7 +133,7 @@
   (keyword (str (name k1) "-" (name k2))))

 (defn select-component-states
-  [hm state entity-id select-component-ids]
+  [hm state entity-id select-components cached-get-component-state]
   ;; If the component is a vector then the first is the component-id
   ;; and the second is the entity-id. They will appear in the context
   ;; under :<component-id>-<entity-id>
@@ -141,18 +141,24 @@
                   (if (vector? component)
                     (let [[component-id entity-id] component]
                       [(concat-keywords component-id entity-id)
-                       (get-component-state state
-                                            component-id
-                                            entity-id)])
+                       (cached-get-component-state state
+                                                   component-id
+                                                   entity-id)])
                     [component
-                     (get-component-state state
-                                          component
-                                          entity-id)])))
-        select-component-ids))
+                     (cached-get-component-state state
+                                                 component
+                                                 entity-id)])))
+        select-components))

 (defn get-component-context
-  "Returns a hashmap of context for use with a component fn."
-  [state entity-id component]
+  "Returns a hashmap of context for use with a component fn.
+   Args:
+   - state: The game state
+   - entity-id: The unique ID of the entity
+   - component: A hashmap representing the component meta data
+   - cached-get-component-state: A function for getting component-state from the game state.
+     This allows the passing of a memoized function to cache results"
+  [state entity-id component cached-get-component-state]
   (let [{:keys [subscriptions select-components]} component]
     (cond-> {}
       subscriptions (assoc :inbox
@@ -163,14 +169,15 @@
                             ;; the selectors, this
                             ;; ensures messages are
                             ;; per entity
-                            (doall (map #(vector % entity-id) subscriptions))))
-      select-components (select-component-states state entity-id select-components))))
+                            (map #(vector % entity-id) subscriptions)))
+      select-components (select-component-states state entity-id select-components cached-get-component-state))))

 (defn system-next-state [state component-id]
   (let [entity-ids (entities-with-component state component-id)
         component-states (get-all-component-state state component-id)
         component (get-component state component-id)
         component-fn (:fn component)
+        cached-get-component-state (memoize get-component-state)
         event-accum (transient [])]
     [(assoc-in
       state
@@ -181,7 +188,8 @@
                (let [component-state (get component-states entity-id)
                      context (get-component-context state
                                                     entity-id
-                                                    component)
+                                                    component
+                                                    cached-get-component-state)
                      next-comp-state (component-fn entity-id
                                                    component-state
                                                    context)]


[TEMP COMMIT] Make selected lessons draggable

We still need to remove those fake lessons on revert.

Signed-off-by: Pang Yan Han <22594c345857f664c61cd411f607c4d40a9d4af9@gmail.com>

--- src/cljs/cljs_nusmods/timetable.cljs
+++ src/cljs/cljs_nusmods/timetable.cljs
@@ -409,7 +409,8 @@
 (defn- add-module-lesson
   "Adds a single lesson of a module (obtained from the `ModulesMap` global) to
    the timetable, and returns its jQuery div element."
-  [moduleCode moduleName lessonType lessonLabel modulesMapLesson bgColorCssClass]
+  [moduleCode moduleName lessonType lessonLabel modulesMapLesson bgColorCssClass
+   isActuallySelected?]
   (let [rowNum    (find-free-row-for-lesson modulesMapLesson)
         day       (:day modulesMapLesson)
         startTime (:startTime modulesMapLesson)
@@ -439,6 +440,9 @@
     (.append $divElem (text ($ "<p />") (str lessonType " [" lessonLabel "]")))
     (.append $divElem (text ($ "<p />") (:venue modulesMapLesson)))
     (width $divElem (str (* half-hour-pixels slotsOcc) "px"))
+    ; make the <div> less opaque for a lesson added by jQuery UI draggable
+    (if (not isActuallySelected?)
+        (.css $divElem "opacity" 0.5))
     (let [dayHTMLElem (nth HTML-Timetable day)
           rowHTMLElem (nth (children dayHTMLElem "tr") rowNum)
           tdHTMLElem  (nth (children
@@ -452,9 +456,14 @@
       (doseq [siblingTdElem (take (- slotsOcc 1) (.nextAll tdHTMLElem))]
         (.remove siblingTdElem))

-      ; Returns a `ModulesSelectedLessonInfo` object augmented with the $divElem
+      ; Returns a `ModulesSelectedLessonInfo` object augmented with the
+      ; `:divElem`, `:moduleCode`, `:lessonType`, and `:lessonGroup` keys
       {:day day, :rowNum rowNum, :startTime startTime, :endTime endTime,
+       :moduleCode moduleCode, :lessonType lessonType, :lessonGroup lessonLabel,
        :divElem $divElem})))
+
+; Forward declaration
+(declare add-module-lesson-group)

 (defn- lesson-draggable-start-evt-handler-maker
   "Returns a function that can be used as the `start` event handler for a
@@ -463,59 +472,94 @@
    Details of the returned function can be found here:

        http://api.jqueryui.com/draggable/#event-start"
-  [moduleCode lessonType lessonGroup]
-  (let []
-    (fn [evt ui])
-    )
-  )
+  [moduleCode lessonType selectedLessonGroup bgColorCssClass]
+  (fn [evt ui]
+    (let [ModulesMap         (aget js/window "ModulesMap")
+          ; Get all lesson groups
+          allLessonGroupsMap (get-in ModulesMap [moduleCode "lessons"
+                                                 lessonType])
+          ; Exclude the selected lesson group
+          unselectedLessonGroupsMap
+          (dissoc allLessonGroupsMap selectedLessonGroup)
+          ; Add all the <div> elements
+          augmentedTTLessonInfoSeq
+          (doall
+            (map (fn [[lessonGroup _]]
+                   (add-module-lesson-group moduleCode lessonType lessonGroup
+                                            bgColorCssClass false))
+                 unselectedLessonGroupsMap))])))
+
+; TODO: Make lesson type with only a single lessongroup non-draggable
+(defn- make-added-lessons-draggable
+  "Makes the <div> elements of selected lessons draggable."
+  [$divElemSeq moduleCode lessonType lessonLabel bgColorCssClass]
+  ; And Drag event handler
+  (doseq [$divElem $divElemSeq]
+    (.draggable $divElem
+                (js-obj "zIndex" 100
+                        ; TODO: Replace this `revert` function with one
+                        ;       which detects whether a revert should
+                        ;       occur
+                        "revert" (fn [] true)
+                        "start"  (lesson-draggable-start-evt-handler-maker
+                                   moduleCode lessonType lessonLabel
+                                   bgColorCssClass)))))

 (defn- add-module-lesson-group
-  "Adds a lesson group of a module to the timetable."
-  [moduleCode lessonType lessonLabel bgColorCssClass
+  "Adds a lesson group of a module to the timetable.
+
+   The `isActuallySelected?` param is used to indicate if the current lesson
+   group is selected by the user.
+   A value of `true` indicates that the user actually selected the lesson group.
+   A value of `false` indicates that the current lesson group is added by the
+   `lesson-draggable-start-evt-handler-maker` function and is not selected by
+   the user.
+
+   Returns a sequence of `ModulesSelectedLessonInfo` objects each augmented
+   with the `:divElem`, `:moduleCode`, `:lessonType`, `:lessonGroup` keys."
+  [moduleCode lessonType lessonLabel bgColorCssClass isActuallySelected?
    & {:keys [ModulesMap]
       :or   {ModulesMap (aget js/window "ModulesMap")}}]
   (let [moduleName          (get-in ModulesMap [moduleCode "name"])
         modulesMapLessonSeq (get-in ModulesMap [moduleCode "lessons" lessonType
                                                 lessonLabel])]
     (if (and moduleName modulesMapLessonSeq)
-        (let [augLessonInfoSeq (map (fn [modulesMapLesson]
-                                      (add-module-lesson moduleCode
-                                                         moduleName
-                                                         lessonType
-                                                         lessonLabel
-                                                         modulesMapLesson
-                                                         bgColorCssClass))
-                                    modulesMapLessonSeq)
-              lessonInfoSeq    (doall (map #(dissoc %1 :divElem)
-                                           augLessonInfoSeq))
-              $divElemSeq      (map #(:divElem %1) augLessonInfoSeq)]
-          ; Update ModulesSelected with the lesson group
-          (set! ModulesSelected
-                (assoc-in ModulesSelected [moduleCode lessonType]
-                          {:label lessonLabel, :info lessonInfoSeq}))
-
-          ; Add to ModulesSelectedOrder
-          (if (not-any? #{moduleCode} ModulesSelectedOrder)
+        (let [augLessonInfoSeq
+              (doall (map (fn [modulesMapLesson]
+                            (add-module-lesson moduleCode moduleName
+                                                          lessonType
+                                                          lessonLabel
+                                                          modulesMapLesson
+                                                          bgColorCssClass
+                                                          isActuallySelected?))
+                                    modulesMapLessonSeq))
+              lessonInfoSeq (map #(dissoc %1 :divElem :moduleCode :lessonType
+                                          :lessonGroup)
+                                 augLessonInfoSeq)
+              $divElemSeq   (map #(:divElem %1) augLessonInfoSeq)]
+
+          (if isActuallySelected?
               (do
-                (set! ModulesSelectedOrder
-                      (conj ModulesSelectedOrder moduleCode))
-                ; Update select2 box.
-                ; NOTE: This does a quadratic amount of work but I do not have
-                ;       a workaround.
-                (select2/select2-box-set-val
-                  select2/$Select2-Box
-                  (get-selected-module-codes-as-js-array))))
-
-          ; And Drag event handler
-          (doseq [$divElem $divElemSeq]
-            (.draggable $divElem
-                        (js-obj
-                          "zIndex" 100
-                          ; TODO: Replace this `revert` function
-                          "revert" (fn [] true)
-                          "start"  (lesson-draggable-start-evt-handler-maker
-                                     moduleCode lessonType lessonLabel)
-                                )))))))
+                ; Update ModulesSelected with the lesson group
+                (set! ModulesSelected
+                      (assoc-in ModulesSelected [moduleCode lessonType]
+                                {:label lessonLabel, :info lessonInfoSeq}))
+
+                ; Add to ModulesSelectedOrder
+                (if (not-any? #{moduleCode} ModulesSelectedOrder)
+                    (do
+                      (set! ModulesSelectedOrder
+                            (conj ModulesSelectedOrder moduleCode))
+                      ; Update select2 box.
+                      ; NOTE: This does a quadratic amount of work but I do not
+                      ;       have a workaround.
+                      (select2/select2-box-set-val
+                        select2/$Select2-Box
+                        (get-selected-module-codes-as-js-array))))
+
+                (make-added-lessons-draggable $divElemSeq moduleCode lessonType
+                                              lessonLabel bgColorCssClass)
+                augLessonInfoSeq))))))

 (defn add-module
   "Adds a module to the timetable.
@@ -543,6 +587,7 @@
                                    (:lessonType moduleInfo)
                                    (:lessonGroup moduleInfo)
                                    bgColorCssClass
+                                   true
                                    ModulesMap))

         ; Update URL hash with newly added module
@@ -706,7 +751,8 @@
         (add-module-lesson-group moduleCode
                                  (:lessonType modInfo)
                                  (:lessonGroup modInfo)
-                                 bgColorCssClass)))
+                                 bgColorCssClass
+                                 true)))

     ; Update url hash
     (set-document-location-hash-based-on-modules-order)))


Change data representation of `Timetable` and `ModulesSelected` globals

Some progress was made in implementing the removal of modules on a
separate branch, and along the way, we discovered some critical
weaknesses of our current data representation, in particular for the
`Timetable` global.

This patch introduces some changes to the `Timetable` and
`ModulesSelected` globals. For the `ModulesSelected` global, we remove
the `:divElem` key, instead forcing our functions to lookup the
appropriate day and row in the `Timetable` global for the actual jQuery
<div> object representing a lesson.

For the `Timetable` global, we abolish the use of a vector for marking
and searching for timeslots. Instead, we use a map to represent a row in
a day. Each key of this map is a `TimetableLessonInfo` data type defined
at the start of the `src/cljs/cljs_nusmods/timetable.cljs` file, and
each value is the jQuery <div> object for that lesson.

This will hopefully lead to a cleaner implementation when we implement
the remaining code required for removing modules.

Signed-off-by: Pang Yan Han <22594c345857f664c61cd411f607c4d40a9d4af9@gmail.com>

--- src/cljs/cljs_nusmods/timetable.cljs
+++ src/cljs/cljs_nusmods/timetable.cljs
@@ -10,10 +10,12 @@
 ; Data type Definitions
 ; =====================
 ;
-; TimetableLessonInfo (short form: ttLessonInfo)
-; ----------------------------------------------
-; Describes a lesson in the `Timetable` global.
-; Each instance is a map in the following format:
+; TimetableLessonInfo (abbrev: ttLessonInfo)
+; ------------------------------------------
+; Describes a lesson in the `Timetable` global; this is the key used for each
+; row in a day of the `Timetable`.
+;
+; Each instance of `TimetableLessonInfo` is a map in the following format:
 ;
 ;   {
 ;     :moduleCode  -> module code string of this lesson
@@ -21,6 +23,19 @@
 ;     :lessonGroup -> lesson group string of this lesson
 ;     :startTime   -> 0-indexed start time of the lesson
 ;     :endTime     -> 0-indexed end time of the lesson
+;   }
+;
+;
+; ModulesSelectedLessonInfo (abbrev: modSelLessonInfo)
+; ----------------------------------------------------
+; Describes a lesson in the `ModulesSelected` global.
+; Each instance is a map in the following format:
+;
+;   {
+;     :day       -> 0-indexed integer of the day of the lesson
+;     :rowNum    -> 0-indexed integer of the row
+;     :startTime -> 0-indexed start time of the lesson
+;     :endTime   -> 0-indexed end time of the lesson
 ;   }

 (def ^{:doc     "Width in pixels of a half hour timeslot"
@@ -269,27 +284,13 @@
                    (fn [ttRow]
                      (assoc ttRow ttLessonInfo $lessonDiv)))))

-; TODO - update this function
-; It was formerly called timetable-mark-lesson-slots-free
 (defn- timetable-remove-lesson
   "Removes a lesson from the `Timetable` global, effectively 'marking' the
    time interval occupied by that lesson as free."
-  [day row ttLessonInfo]
-  (let [day         (:day lessonInfo)
-        row         (:rowNum lessonInfo)
-        startTime   (:startTime lessonInfo)
-        endTime     (:endTime lessonInfo)
-        ttRow       (get-in Timetable [day row])
-        occupiedVec (reduce (fn [occupiedVec timeIdx]
-                              (assoc occupiedVec timeIdx nil))
-                            (:occupied ttRow)
-                            (range startTime endTime))]
-    (set! Timetable
-          (update-in
-            Timetable [day row]
-            (fn [ttRow]
-              {:nrLessons (dec (:nrLessons ttRow))
-               :occupied  occupiedVec})))))
+  [day rowNum ttLessonInfo]
+  (set! Timetable
+        (update-in Timetable [day rowNum]
+                   (fn [ttRow] (dissoc ttRow ttLessonInfo)))))

 (defn- find-free-row-for-lesson
   "Returns the 0-indexed row which can accomodate the given lesson (timeslots
@@ -352,11 +353,11 @@
 (defn- add-module-lesson
   "Adds a single lesson of a module (obtained from the `ModulesMap` global) to
    the timetable, and returns its jQuery div element."
-  [moduleCode moduleName lessonType lessonLabel lesson bgColorCssClass]
-  (let [rowNum    (find-free-row-for-lesson lesson)
-        day       (:day lesson)
-        startTime (:startTime lesson)
-        endTime   (:endTime lesson)
+  [moduleCode moduleName lessonType lessonLabel modulesMapLesson bgColorCssClass]
+  (let [rowNum    (find-free-row-for-lesson modulesMapLesson)
+        day       (:day modulesMapLesson)
+        startTime (:startTime modulesMapLesson)
+        endTime   (:endTime modulesMapLesson)
         slotsOcc  (- endTime startTime)
         $divElem  ($ "<div />" (js-obj "class" "lesson"))

@@ -367,7 +368,8 @@
         (add-new-row-to-timetable-day day))

     ; Update in-memory representation of timetable
-    (timetable-add-lesson day rowNum
+    (timetable-add-lesson day
+                          rowNum
                           {:moduleCode moduleCode,
                            :lessonType lessonType,
                            :lessonGroup lessonLabel,
@@ -379,7 +381,7 @@
     (.addClass $divElem bgColorCssClass)
     (.append $divElem (text ($ "<p />") (str moduleCode " " moduleName)))
     (.append $divElem (text ($ "<p />") (str lessonType " [" lessonLabel "]")))
-    (.append $divElem (text ($ "<p />") (:venue lesson)))
+    (.append $divElem (text ($ "<p />") (:venue modulesMapLesson)))
     (width $divElem (str (* half-hour-pixels slotsOcc) "px"))
     (let [dayHTMLElem (nth HTML-Timetable day)
           rowHTMLElem (nth (children dayHTMLElem "tr") rowNum)
@@ -394,32 +396,27 @@
       (doseq [siblingTdElem (take (- slotsOcc 1) (.nextAll tdHTMLElem))]
         (.remove siblingTdElem))

-      ; Returns a map containing information to make it easier to remove the
-      ; lesson
-      {:day day, :rowNum rowNum, :startTime startTime, :endTime endTime,
-       :divElem $divElem})))
+      ; Returns a ModulesSelectedLessonInfo object
+      {:day day, :rowNum rowNum, :startTime startTime, :endTime endTime})))

 (defn- add-module-lesson-group
   "Adds a lesson group of a module to the timetable."
   [moduleCode lessonType lessonLabel bgColorCssClass
    & {:keys [ModulesMap]
       :or   {ModulesMap (aget js/window "ModulesMap")}}]
-  (let [moduleName (get-in ModulesMap [moduleCode "name"])
-        lessons    (get-in ModulesMap [moduleCode "lessons" lessonType
-                                       lessonLabel])]
-    (if (and moduleName lessons)
+  (let [moduleName          (get-in ModulesMap [moduleCode "name"])
+        modulesMapLessonSeq (get-in ModulesMap [moduleCode "lessons" lessonType
+                                                lessonLabel])]
+    (if (and moduleName modulesMapLessonSeq)
         (let [lessonInfoSeq (doall
-                              ; remove lessonIdx
-                              (map (fn [[lessonIdx lesson]]
+                              (map (fn [modulesMapLesson]
                                      (add-module-lesson moduleCode
                                                         moduleName
                                                         lessonType
                                                         lessonLabel
-                                                        lesson
-                                                        lessonIdx
+                                                        modulesMapLesson
                                                         bgColorCssClass))
-                                   (map vector
-                                        (range 0 (count lessons)) lessons)))]
+                                   modulesMapLessonSeq))]
           ; Update ModulesSelected with the lesson group
           (set! ModulesSelected
                 (assoc-in ModulesSelected [moduleCode lessonType]
@@ -629,9 +626,9 @@
 (defn- add-missing-td-elements-replacing-lesson
   "Adds <td> elements that were removed by the `add-module-lesson` function
    to make way for the lesson."
-  [$parentTd lessonInfo]
-  (let [startTime (:startTime lessonInfo)
-        endTime   (:endTime lessonInfo)]
+  [$parentTd ttLessonInfo]
+  (let [startTime (:startTime ttLessonInfo)
+        endTime   (:endTime ttLessonInfo)]
     (loop [$currentTd $parentTd
            timeIdx    (inc startTime)]
       (if (>= timeIdx endTime)
@@ -650,29 +647,31 @@
   "Removes a lesson group for a module from the HTML timetable.
    This does NOT remove any empty rows / perform module shifting.

-   Returns an array of the map with the following keys:
-
-     :day       -> 0-indexed day where the lesson was stored
-     :rowNum    -> 0-indexed row to the day in `Timetable` where the lesson
-                   was stored
-     :startTime -> 0-indexed start time of the lesson
-     :endTime   -> 0-indexed end time of the lesson"
+   Returns a sequence of `ttLessonInfo` objects which contain
+   additional `:day` and `:rowNum` keys."
   [moduleCode lessonType]
   (let [lessonGroupDetails (get-in ModulesSelected [moduleCode lessonType])
-        lessonLabel        (:label lessonGroupDetails)
+        lessonGroup        (:label lessonGroupDetails)
         lessonInfoSeq      (:info lessonGroupDetails)]
-    (map (fn [lessonInfo]
-           (let [$lessonDiv (:divElem lessonInfo)
-                 $parentTd  (parent $lessonDiv)]
+    (map (fn [modSelLessonInfo]
+           (let [day          (:day modSelLessonInfo)
+                 rowNum       (:rowNum modSelLessonInfo)
+                 startTime    (:startTime modSelLessonInfo)
+                 endTime      (:endTime modSelLessonInfo)
+                 ttRow        (get-in Timetable [day rowNum])
+                 ttLessonInfo {:moduleCode moduleCode, :lessonType lessonType,
+                               :lessonGroup lessonGroup, :startTime startTime,
+                               :endTime endTime}
+                 $lessonDiv   (get ttRow ttLessonInfo)
+                 $parentTd    (parent $lessonDiv)]
              ; Remove the `colspan` attribute of the parent <td> so it has
              ; colspan=1
              (remove-attr $parentTd "colspan")
              ; Remove the HTML div elem
              (.remove $lessonDiv)
              ; Add the missing <td> elements
-             (add-missing-td-elements-replacing-lesson $parentTd lessonInfo)
-             ; Remove the same lessonInfo without :divElem key
-             (dissoc lessonInfo :divElem)))
+             (add-missing-td-elements-replacing-lesson $parentTd ttLessonInfo)
+             (assoc ttLessonInfo :day day, :rowNum rowNum)))
          lessonInfoSeq)))

 (defn remove-module
@@ -684,12 +683,15 @@
       (let [lessonTypes
             (keys (get ModulesSelected moduleCode))

-            seqOfRemovedLessons
+            ttLessonInfoAugmentedSeq
             (flatten (map (fn [lessonType]
                             (remove-lesson-group-html moduleCode lessonType))
                           lessonTypes))]
-        (doseq [lessonInfo seqOfRemovedLessons]
-          (timetable-mark-lesson-slots-free lessonInfo))
+        (doseq [ttLessonInfoAug ttLessonInfoAugmentedSeq]
+          (let [day          (:day ttLessonInfoAug)
+                rowNum       (:rowNum ttLessonInfoAug)
+                ttLessonInfo (dissoc ttLessonInfoAug :day :rowNum)]
+          (timetable-remove-lesson day rowNum ttLessonInfo)))
         ; Remove from `ModulesSelected`
         (set! ModulesSelected (dissoc ModulesSelected moduleCode))
         ; Remove from `ModulesSelectedOrder`


Remove unused code.

Remove unused code.

--- test/subprocess/core_test.clj
+++ test/subprocess/core_test.clj
@@ -1,7 +0,0 @@
-(ns subprocess.core-test
-  (:require [clojure.test :refer :all]
-            [subprocess.core :refer :all]))
-
-(deftest a-test
-  (testing "FIXME, I fail."
-    (is (= 0 1))))


unify control wave design

unify control wave design

--- etc/synthdefs/synth-designs.clj
+++ etc/synthdefs/synth-designs.clj
@@ -1333,12 +1333,15 @@
           rate                (/ 1 phase)
           pulse_width         (lag pulse_width pulse_width_slide)
           double_phase_offset (* 2 phase_offset)
-          ctl-wave            (select:kr wave [(lf-saw:kr rate (+ double_phase_offset 1))
-                                               (- (* 2 (lf-pulse:kr rate (+ 0.5 phase_offset) pulse_width)) 1)
-                                               (lf-tri:kr rate (- double_phase_offset 1))
-                                               (sin-osc:kr rate (* (- phase_offset 0.25) (* Math/PI 2)))])
-          amp-mul             (* -1 (- (* 2 (> invert_wave 0)) 1))
-          slice-amp           (* ctl-wave amp-mul)
+
+          ctl-wave            (select:kr wave [(* -1 (lf-saw:kr rate (+ double_phase_offset 1)))
+                                               (- (* 2 (lf-pulse:kr rate phase_offset pulse_width)) 1)
+                                               (lf-tri:kr rate (+ double_phase_offset 1))
+                                               (sin-osc:kr rate (* (+ phase_offset 0.25) (* Math/PI 2)))])
+
+          ctl-wave-mul        (- (* 2 (> invert_wave 0)) 1)
+
+          slice-amp           (* ctl-wave ctl-wave-mul)
           slice-amp           (lin-lin slice-amp -1 1 amp_min amp_max)
           [in-l in-r]         (in in_bus 2)
           [new-l new-r]       (* slice-amp [in-l in-r])
@@ -1372,12 +1375,15 @@
           rate                (/ 1 phase)
           pulse_width         (lag pulse_width pulse_width_slide)
           double_phase_offset (* 2 phase_offset)
-          ctl-wave            (select:kr wave [(lf-saw:kr rate (+ double_phase_offset 1))
-                                               (- (* 2 (lf-pulse:kr rate (+ 0.5 phase_offset) pulse_width)) 1)
-                                               (lf-tri:kr rate (- double_phase_offset 1))
-                                               (sin-osc:kr rate (* (- phase_offset 0.25) (* Math/PI 2)))])
-          amp-mul             (* -1 (- (* 2 (> invert_wave 0)) 1))
-          slice-amp           (* ctl-wave amp-mul)
+
+          ctl-wave            (select:kr wave [(* -1 (lf-saw:kr rate (+ double_phase_offset 1)))
+                                               (- (* 2 (lf-pulse:kr rate phase_offset pulse_width)) 1)
+                                               (lf-tri:kr rate (+ double_phase_offset 1))
+                                               (sin-osc:kr rate (* (+ phase_offset 0.25) (* Math/PI 2)))])
+
+          ctl-wave-mul        (- (* 2 (> invert_wave 0)) 1)
+
+          slice-amp           (* ctl-wave ctl-wave-mul)
           slice-amp           (lin-lin slice-amp -1 1 amp_min amp_max)
           [in-l in-r]         (in out_bus 2)
           [new-l new-r]       (* slice-amp [in-l in-r])
@@ -1418,11 +1424,13 @@
           cutoff_min          (midicps cutoff_min)
           cutoff_max          (midicps cutoff_max)
           double_phase_offset (* 2 phase_offset)
-          ctl-wave            (select:kr wave [(lf-saw:kr rate (+ double_phase_offset 1))
-                                               (- (* 2 (lf-pulse:kr rate (+ 0.5 phase_offset) pulse_width)) 1)
-                                               (lf-tri:kr rate (- double_phase_offset 1))
-                                               (sin-osc:kr rate (* (- phase_offset 0.25) (* Math/PI 2)))])
-          ctl-wave-mul        (* -1 (- (* 2 (> invert_wave 0)) 1))
+
+          ctl-wave            (select:kr wave [(* -1 (lf-saw:kr rate (+ double_phase_offset 1)))
+                                               (- (* 2 (lf-pulse:kr rate phase_offset pulse_width)) 1)
+                                               (lf-tri:kr rate (+ double_phase_offset 1))
+                                               (sin-osc:kr rate (* (+ phase_offset 0.25) (* Math/PI 2)))])
+
+          ctl-wave-mul        (- (* 2 (> invert_wave 0)) 1)

           cutoff-freq         (lin-exp:kr (* ctl-wave-mul ctl-wave) -1 1 cutoff_min cutoff_max)

@@ -1468,11 +1476,12 @@
           cutoff_min          (midicps cutoff_min)
           cutoff_max          (midicps cutoff_max)
           double_phase_offset (* 2 phase_offset)
-          ctl-wave            (select:kr wave [(lf-saw:kr rate (+ double_phase_offset 1))
-                                               (- (* 2 (lf-pulse:kr rate (+ 0.5 phase_offset) pulse_width)) 1)
-                                               (lf-tri:kr rate (- double_phase_offset 1))
-                                               (sin-osc:kr rate (* (- phase_offset 0.25) (* Math/PI 2)))])
-          ctl-wave-mul        (* -1 (- (* 2 (> invert_wave 0)) 1))
+          ctl-wave            (select:kr wave [(* -1 (lf-saw:kr rate (+ double_phase_offset 1)))
+                                               (- (* 2 (lf-pulse:kr rate phase_offset pulse_width)) 1)
+                                               (lf-tri:kr rate (+ double_phase_offset 1))
+                                               (sin-osc:kr rate (* (+ phase_offset 0.25) (* Math/PI 2)))])
+
+          ctl-wave-mul        (- (* 2 (> invert_wave 0)) 1)

           cutoff-freq         (lin-exp:kr (* ctl-wave-mul ctl-wave) -1 1 cutoff_min cutoff_max)


Fixes for setting and broadcasting

Fixes for setting and broadcasting

--- src/main/clj/core/matrix/complex.clj
+++ src/main/clj/core/matrix/complex.clj
@@ -40,7 +40,7 @@
     (dimension-count [m x]
       (mp/dimension-count (.real m) x))

-    mp/PIndexedAccess
+   mp/PIndexedAccess
     (get-1d [m x]
       (c/complex-number (double (mp/get-1d (.real m) x))
                         (double (mp/get-1d (.imag m) x))))
@@ -50,10 +50,34 @@
     (get-nd [m indexes]
       (c/complex-number (double (mp/get-nd (.real m) indexes))
                         (double (mp/get-nd (.imag m) indexes))))
-
-   java.lang.Object
-     (toString [m]
-       (str "(" (str (.real m)) ", " (str (.imag m)) ")]"))
+
+    mp/PIndexedSetting
+	    (set-1d [m row v]
+	      (ComplexArray. (mp/set-1d real row (core.matrix.complex/real v))
+                       (mp/set-1d imag row (core.matrix.complex/imag v))))
+	    (set-2d [m row column v]
+	      (ComplexArray. (mp/set-2d real row column (core.matrix.complex/real v))
+                       (mp/set-2d imag row column (core.matrix.complex/imag v))))
+	    (set-nd [m indexes v]
+	      (ComplexArray. (mp/set-nd real indexes (core.matrix.complex/real v))
+                       (mp/set-nd imag indexes (core.matrix.complex/imag v))))
+	    (is-mutable? [m]
+	      (mp/is-mutable? real))
+
+     mp/PIndexedSettingMutable
+     (set-1d! [m x v]
+      (mp/set-1d! real x (core.matrix.complex/real v))
+       (mp/set-1d! imag x (core.matrix.complex/imag v)))
+     (set-2d! [m x y v]
+      (mp/set-2d! real x y (core.matrix.complex/real v))
+      (mp/set-2d! imag x y (core.matrix.complex/imag v)))
+     (set-nd! [m indexes v]
+      (mp/set-nd! real indexes (core.matrix.complex/real v))
+      (mp/set-nd! imag indexes (core.matrix.complex/imag v)))
+
+    java.lang.Object
+    (toString [m]
+      (str "(" (str (.real m)) ", " (str (.imag m)) ")]"))

    clojure.lang.Seqable
      (seq [m]
@@ -82,7 +106,7 @@
 	    (instance? ComplexArray m) (.imag ^ComplexArray m)
 	    (instance? Complex m) (.getImaginary ^Complex m)
 	    (number? m) 0.0
-	    (and (m/array? m) (m/numerical? m)) (mp/broadcast-coerce m 0.0)
+	    (and (m/array? m) (m/numerical? m)) (mp/coerce-param m (mp/broadcast-like m 0.0))
 	    :else (error "Unable to get imaginary part of object: " m))))


Rebind clojure.test/*test-out* in the repl

Fixes #55

--- src/main/clojure/vimclojure/repl.clj
+++ src/main/clojure/vimclojure/repl.clj
@@ -21,6 +21,8 @@
 ; THE SOFTWARE.

 (ns vimclojure.repl
+  (:require
+    clojure.test)
   (:use
      [vimclojure.util :only (stream->seq pretty-print pretty-print-causetrace)])
   (:import
@@ -51,7 +53,8 @@
   invokations. The members correspond to the Vars as bound be with-binding."}
   repl
   :id :ns :warn-on-reflection :print-meta :print-length :print-level
-  :compile-path :command-line-args :expr1 :expr2 :expr3 :exception :line)
+  :compile-path :command-line-args :expr1 :expr2 :expr3 :exception :test-out
+  :line)

 (defn make-repl
   "Create a new Repl."
@@ -73,6 +76,7 @@
                :expr3              nil
                :exception          nil
                :print-pretty       vimclojure.repl/*print-pretty*
+               :test-out           nil
                :line               0)))

 (defn start
@@ -137,7 +141,10 @@
        #'*2                   (the-repl :expr2)
        #'*3                   (the-repl :expr3)
        #'*e                   (the-repl :exception)
-       #'vimclojure.repl/*print-pretty* (the-repl :print-pretty)}
+       #'vimclojure.repl/*print-pretty* (the-repl :print-pretty)
+       #'clojure.test/*test-out* (if-let [test-out (the-repl :test-out)]
+                                   test-out
+                                   *out*)}
       (try
         (thunk)
         (finally
@@ -158,6 +165,9 @@
                      :expr3              *3
                      :exception          *e
                      :print-pretty       vimclojure.repl/*print-pretty*
+                     :test-out           (let [test-out clojure.test/*test-out*]
+                                           (when-not (identical? test-out *out*)
+                                             test-out))
                      :line               (dec (.getLineNumber *in*))))))))))

 (defmacro with-repl


Add FIXMEs

Add FIXMEs

--- src/detritus/variants.clj
+++ src/detritus/variants.clj
@@ -128,3 +128,7 @@
                  (let [[_ {:keys ~members}] x#]
                    (and ~@(:pre ?pre-map))))))
          nil)))
+
+;; FIXME: def closed variant?
+;; FIXME: def open variant?
+;; FIXME: variant dispatch?


Because I am a Clever Developer, `list-tentacles` was returning a vector
of vectors of seqs. Uh. -__-

Fix that.

--- src/doctopus/doctopus.clj
+++ src/doctopus/doctopus.clj
@@ -32,9 +32,9 @@
       (assoc this :heads (doall (map #(h/bootstrap-tentacles % heads-dir) heads)))))
   (list-heads [this] (:heads this))
   (list-tentacles [this]
-    (into [] (for [head (:heads this)
-                   :let [tentacles (h/list-tentacles head)]]
-               [tentacles])))
+    (into [] (flatten (for [head (:heads this)
+                    :let [tentacles (h/list-tentacles head)]]
+                tentacles))))
   (list-tentacles-by-head [this head]
     (map h/list-tentacles (filter #(= head (:name %)) (:heads this))))
   (load-routes [this]


Add verbose flag to lumo

Add verbose flag to lumo

--- build.boot
+++ build.boot
@@ -135,6 +135,7 @@
             (proc "lumo"
                   "-c" (str "\"" (System/getProperty "fake.class.path") "\"")
                   "-k" "lumo_cache"
+                  "-v"
                   "src/test/cljs/mikron/test_runner/node.cljs")
             (boot-cljs-test/test-cljs :js-env        :node
                                       :namespaces    cljs-test-namespaces


Bump reagent/re-frame/re-com

Bump reagent/re-frame/re-com

--- build.boot
+++ build.boot
@@ -23,12 +23,12 @@

                        ;; App deps
                        [org.clojure/core.async      "0.2.374"]
-                       [reagent                     "0.5.1"]
-                       [re-frame                    "0.5.0"]
+                       [reagent                     "0.6.0-SNAPSHOT"]
+                       [re-frame                    "0.7.0"]
                        [replumb/replumb             "0.2.2-SNAPSHOT"]
                        [cljsjs/highlight            "8.4-0"]
                        [re-console                  "0.1.4-SNAPSHOT"]
-                       [re-com                      "0.7.0-alpha2"]
+                       [re-com                      "0.8.1"]
                        [cljs-ajax                   "0.5.1"]
                        [hickory                     "0.5.4"]
                        [cljsjs/showdown             "0.4.0-1"]


update boot-cljx

update boot-cljx

--- build.boot
+++ build.boot
@@ -13,7 +13,7 @@
   :dependencies '[[adzerk/boot-cljs       "0.0-2411-5"]
                   [adzerk/boot-cljs-repl  "0.1.7"]
                   [adzerk/boot-reload     "0.2.0"]
-                  [deraen/boot-cljx       "0.2.0"]
+                  [deraen/boot-cljx       "0.2.1"]
                   [deraen/boot-less       "0.1.0-SNAPSHOT"]

                   [org.clojure/clojure "1.6.0"]


Remove bootlaces and build and deploy tasks

--- build.boot
+++ build.boot
@@ -2,14 +2,10 @@
   :source-paths #{"test"}
   :dependencies '[[org.clojure/clojure "1.7.0"     :scope "provided"]
                   [boot/core           "2.5.5"     :scope "provided"]
-                  [adzerk/bootlaces    "0.1.13"    :scope "test"]
                   [http-kit            "2.1.19"    :scope "test"]])

-(require '[adzerk.bootlaces :refer :all])

 (def +version+ "0.4.3-SNAPSHOT")
-
-(bootlaces! +version+)

 (task-options!
   pom {:project     'adzerk/boot-reload
@@ -19,3 +15,14 @@
        :scm         {:url "https://github.com/adzerk/boot-reload"}
        :license     {"Eclipse Public License"
                      "http://www.eclipse.org/legal/epl-v10.html"}})
+
+(deftask build []
+  (comp
+   (pom)
+   (jar)
+   (install)))
+
+(deftask deploy []
+  (comp
+   (build)
+   (push :repo "clojars" :gpg-sign (not (.endsWith +version+ "-SNAPSHOT")))))


Update play-cljs template again

Update play-cljs template again

--- build.boot
+++ build.boot
@@ -14,7 +14,7 @@
                   [leiningen "2.8.1" :exclusions [leiningen.search]]
                   [ring "1.6.1"]
                   [hawk "0.2.11"]
-                  [play-cljs/lein-template "0.11.0.1"]
+                  [play-cljs/lein-template "0.11.0.2"]
                   [eval-soup "1.2.3" :exclusions [org.clojure/core.async]]
                   [org.eclipse.jgit/org.eclipse.jgit "4.6.0.201612231935-r"]]
   :repositories (conj (get-env :repositories)


formatting

formatting

--- env/dev/desdemona/ui/dev.cljs
+++ env/dev/desdemona/ui/dev.cljs
@@ -6,7 +6,7 @@
 (enable-console-print!)

 (figwheel/watch-and-reload
-  :websocket-url "ws://localhost:3449/figwheel-ws"
-  :jsload-callback core/mount-root)
+ :websocket-url "ws://localhost:3449/figwheel-ws"
+ :jsload-callback core/mount-root)

 (core/init!)


Bring jar text up to date with current shell wrappers.

--- test/test_jar.clj
+++ test/test_jar.clj
@@ -25,8 +25,10 @@
     (is (= "bin/nom" (manifest "Leiningen-shell-wrapper")))
     (is (re-find #"org/clojure/clojure/1\.1\.0/" bin))
     (is (re-find #"org\\clojure\\clojure\\1\.1\.0" bat))
-    (is (re-find #"use 'nom\.nom\.nom\)\(apply -main .command-line-args." bin))
-    (is (re-find #"use 'nom\.nom\.nom\)\(apply -main .command-line-args." bat))
+    (is (re-find #"MAIN=\"nom\.nom\.nom\"" bin))
+    (is (re-find #"set MAIN=\"nom\.nom\.nom\"" bat))
+    (is (re-find #"use '\$MAIN\)\(apply -main .command-line-args." bin))
+    (is (re-find #"use '%MAIN%\)\(apply -main .command-line-args." bat))
     (is (re-find #"\$HOME/\.m2/repository/rome/rome/0\.9/rome-0\.9\.jar" bin))
     (is (re-find
          #"%USERPROFILE%\\\.m2\\repository\\rome\\rome\\0\.9\\rome-0\.9\.jar"


remove useless goog.require

remove useless goog.require

only causes GCC to complain

--- src/main/shadow/build/js_support.clj
+++ src/main/shadow/build/js_support.clj
@@ -111,6 +111,5 @@
                 :shadow-js
                 (str js-ns-alias " = " (npm/shadow-js-require rc false) prop-access ";\n")

-                (str "goog.require(\"" ns "\");"
-                     js-ns-alias " = " ns prop-access ";\n"))
+                (str js-ns-alias " = " ns prop-access ";\n"))
               )))}))


RS & CM | #22 | Check for draft content in functional tests

--- functional/objective8/functional_tests.clj
+++ functional/objective8/functional_tests.clj
@@ -4,6 +4,9 @@
             [clj-webdriver.taxi :as wd]
             [clj-webdriver.core :as wc]
             [clojure.java.io :as io]
+            [endophile.core :as ec]
+            [endophile.hiccup :as eh]
+            [hiccup.core :as hc]
             [objective8.core :as core]
             [objective8.integration-helpers :as integration-helpers]
             [dev-helpers.stub-twitter :refer [stub-twitter-auth-config]]))
@@ -32,6 +35,9 @@
               file-seq
               (filter #(re-matches #".*\.png$" (.getName %)))
               (map io/delete-file))))
+
+(def SOME_MARKDOWN  "A heading\n===\nSome content")
+(def SOME_HTML (hc/html (eh/to-hiccup (ec/mp SOME_MARKDOWN))))

 (facts "About user journeys" :functional
        (against-background
@@ -135,7 +141,7 @@
                  (wait-for-title "Edit draft | Objective[8]")
                  (screenshot "12_edit_draft_empty")

-                 (wd/input-text "#clj-edit-draft-content" "Functional test draft title\n===\nSome content")
+                 (wd/input-text "#clj-edit-draft-content" SOME_MARKDOWN)
                  (wd/click "button[value='preview']")
                  (wait-for-title "Edit draft | Objective[8]")
                  (screenshot "13_preview_draft")
@@ -144,11 +150,12 @@

                  (wait-for-title "Policy draft | Objective[8]")
                  (screenshot "14_submitted_draft")
-                 ;; TODO: Check that draft content is present

-                 (wd/title)
+                 {:page-title (wd/title)
+                  :page-source (wd/page-source)}

                  (catch Exception e
                    (screenshot "ERROR-Can-submit-a-draft")
                    (throw e)))
-               => "Policy draft | Objective[8]")))
+               => (contains {:page-title "Policy draft | Objective[8]"
+                             :page-source (contains SOME_HTML)}))))


in-msec -> in-millis because of deprecation

--- src/midje/emission/plugins/junit.clj
+++ src/midje/emission/plugins/junit.clj
@@ -63,7 +63,7 @@
       (str (fact/file fact) ":" (fact/line fact))))

 (defn process-fact [fact]
-  (let [elapsed (/ (time/in-msecs (time/interval (-> fact :attrs :start-time)
+  (let [elapsed (/ (time/in-millis (time/interval (-> fact :attrs :start-time)
                                                  (-> fact :attrs :stop-time)))
                    1000.0)
         dissoc-times (fn [attrs] (-> attrs


Removed debug printlns.

Removed debug printlns.

--- test/async/http/client/test.clj
+++ test/async/http/client/test.clj
@@ -84,9 +84,7 @@
                            (.addCookie hResp (Cookie. "foo" "bar"))
                            (doseq [c (.getCookies hReq)]
                              (.addCookie hResp c)))
-               "/branding" (let [ua (.getHeader hReq "User-Agent")]
-                             (println "User-Agent is " ua)
-                             (.setHeader hResp "X-User-Agent" ua))
+               "/branding" (.setHeader hResp "X-User-Agent" (.getHeader hReq "User-Agent"))
                (doseq [n (enumeration-seq (.getParameterNames hReq))]
                  (doseq [v (.getParameterValues hReq n)]
                    (.addHeader hResp n v))))
@@ -324,7 +322,6 @@
   (let [ua-brand "Branded User Agent/1.0"]
     (with-ahc {:user-agent ua-brand}
       (let [headers (:headers @(GET "http://localhost:8123/branding"))]
-        (println "Received headers: " headers)
         (is (contains? headers :x-user-agent))
         (is (= (:x-user-agent headers) ua-brand))))))


correct logic error in simplify-accounts

--- test/clj_money/test_helpers.clj
+++ test/clj_money/test_helpers.clj
@@ -83,7 +83,7 @@
   (map #(if (seq (:children %))
           ( -> %
                (select-keys (concat [:name :children] additional-attributes))
-               (update-in [:children] simplify-accounts))
+               (update-in [:children] simplify-accounts additional-attributes))
           (select-keys % [:name]))
        accounts))


cjl - Update gameplay mock ai

cjl - Update gameplay mock ai

--- test/icfp2015/gameplay_test.clj
+++ test/icfp2015/gameplay_test.clj
@@ -14,9 +14,7 @@
           cmd [:move :southeast]
           mock-ai (fn [board]
                     (let [new-board (board/transition-board board cmd)]
-                      (if (board/valid-position? new-board)
-                        [new-board cmd]
-                        [(board/transition-board board [:lock]) :lock])))
+                      [new-board cmd]))
           actual-commands (handle-unit mock-ai board unit)
           expected-commands (repeat 5 cmd)]
       (is (= expected-commands actual-commands)))))


fixed accidental breakage

fixed accidental breakage

--- test/midje/ideas/t_formulas.clj
+++ test/midje/ideas/t_formulas.clj
@@ -53,7 +53,7 @@
 ;; There is syntactic sugar for binding *num-trials*
 (defn-call-countable k-maker [] "k")
 (with-num-trials 1000
-  (formula [a 1] (k-maker) => "kkkkkk")
+  (formula [a 1] (k-maker) => "k")
   (formula [a 1] (k-maker) => "k")
   (formula [a 1] (k-maker) => "k"))
 (fact @k-maker-count => 3000)
@@ -122,7 +122,7 @@

 ;;;; Validation

-;; The following facts express an assortment of ways that formulas
+;; The following factsexpress an assortment of ways that formulas
 ;; could be expressed with invalid syntax

 (unfinished h)


Fix interpose test

Fix interpose test

--- test/simple_check/core_test.clj
+++ test/simple_check/core_test.clj
@@ -86,19 +86,24 @@
                  (let [v (gen/vector gen/int)]
                    (sc/quick-check 1000 concat-counts-correct [v v]))))))

-;; Interpose
+;; Interpose (Count)
 ;; ---------------------------------------------------------------------------

-(defn interpose-twice-the-length
+(defn interpose-twice-the-length ;; (or one less)
   [v]
-  (= (count (interpose :i v))
-     (* 2 (count v))))
+  (let [interpose-count (count (interpose :i v))]
+    (or
+      (= (* 2 interpose-count))
+      (= (dec (* 2 interpose-count))))))
+

 (deftest interpose-creates-sequence-twice-the-length
-  (is "Interposing a collection with a value makes it's count
-      twice the original collection"
-      (:result
-        (sc/quick-check 1000 interpose-twice-the-length [(gen/vector gen/int)]))))
+  (testing
+    "Interposing a collection with a value makes it's count
+    twice the original collection, or ones less."
+    (is (:result
+          (sc/quick-check 1000 interpose-twice-the-length
+                          [(gen/vector gen/int)])))))

 ;; Tests are deterministic
 ;; ---------------------------------------------------------------------------


Add a "fast" test watcher

Add a "fast" test watcher

--- build.boot
+++ build.boot
@@ -49,7 +49,13 @@
    (build)
    (test)))

+(deftask fast-watch []
+  (comp
+   (solver)
+   (watch)
+   (test)))
+
 (deftask watch-tests []
   (comp
-   (watch :verbose true)
+   (watch)
    (tests)))


Bump version

Bump version

--- build.boot
+++ build.boot
@@ -11,7 +11,7 @@
          '[adzerk.boot-cljs   :refer [cljs]]
          '[pandeiro.boot-http :refer [serve]])

-(def +version+ "2.1.3")
+(def +version+ "2.1.4-SNAPSHOT")

 (task-options!
   pom {:project     'adzerk/boot-cljs


Add bootlaces to deploy

Add bootlaces to deploy

--- build.boot
+++ build.boot
@@ -5,6 +5,7 @@
           :source-paths   #{"test"}
           :dependencies   '[[org.clojure/clojure "RELEASE"]
                             [adzerk/boot-test "RELEASE" :scope "test"]
+                            [adzerk/bootlaces "0.1.13" :scope "test"]
                             [org.clojars.bensu/commons-text "0.1-SNAPSHOT-0"]])

 (task-options!
@@ -22,3 +23,6 @@
   (comp (pom) (jar) (install)))

 (require '[adzerk.boot-test :refer [test]])
+(require '[adzerk.bootlaces :refer :all])
+
+(bootlaces! version)


Update boot-http to v0.7.3

Update boot-http to v0.7.3

--- build.boot
+++ build.boot
@@ -3,7 +3,7 @@
  :source-paths #{"src/clj" "src/cljs"}
  :dependencies '[[adzerk/boot-cljs "1.7.228-1"]
                  [adzerk/boot-reload "0.4.5"]
-                 [pandeiro/boot-http "0.7.1-SNAPSHOT"]
+                 [pandeiro/boot-http "0.7.3"]
                  [org.martinklepsch/boot-garden "1.3.0-0"]
                  [ring "1.4.0"]
                  [compojure "1.4.0"]


clean up db-refresh task

clean up db-refresh task

--- build.boot
+++ build.boot
@@ -41,16 +41,19 @@
 ;; Load testing tasks
 (require '[adzerk.boot-test :refer :all])

+(require '[datomic.api :as d])
+
 (deftask dev []
   (set-env! :source-paths #(conj % "dev/src"))

   (require 'user))

-(deftask refresh-db []
-  (require '[datomic.api])
+(deftask refresh-db
+  "resets the database"
+  []

   (let [datomic-uri "datomic:free://localhost:4334/wombats-dev"
-        _ (datomic.api/delete-database datomic-uri)
-        _ (datomic.api/create-database datomic-uri)
-        conn (datomic.api/connect datomic-uri)]
-    (datomic.api/transact conn (load-file "resources/datomic/schema.edn"))))
+        _ (d/delete-database datomic-uri)
+        _ (d/create-database datomic-uri)
+        conn (d/connect datomic-uri)]
+    (d/transact conn (load-file "resources/datomic/schema.edn"))))


Add timestamps to benchmark results [skip ci]

--- build.boot
+++ build.boot
@@ -36,6 +36,8 @@
          '[codox.boot :as boot-codox]

          '[mikron.core :as mikron])
+
+(import '[java.util Date])

 ;; Util

@@ -125,7 +127,8 @@
                           (read-string)
                           (conj {:stats   (vec stats)
                                  :schema  schema
-                                 :results results})
+                                 :results results
+                                 :time    (.getTime (Date.))})
                           (pprint/pprint)
                           (with-out-str))))
               (-> fileset


Enable the re-frame tracing again

Enable the re-frame tracing again

The was a google define to add for dev mode: :closure-defines {"clairvoyant.core.devmode" true}
See :closure-defines {"clairvoyant.core.devmode" true}

--- build.boot
+++ build.boot
@@ -61,7 +61,9 @@
 ;;;;;;;;;;;;;;;;;;;;;;

 (def dev-compiler-options
-  {:source-map-timestamp true})
+  {:source-map-timestamp true
+   :elide-asserts true
+   :closure-defines {"clairvoyant.core.devmode" true}})

 (def prod-compiler-options
   {:closure-defines {"goog.DEBUG" false}


transport.websockets: fix a bug handling non-channel HTTP reqs.

--- src/riemann/transport/websockets.clj
+++ src/riemann/transport/websockets.clj
@@ -116,11 +116,16 @@
 (defn put-events-handler
   "Accepts events from the body of an HTTP request as JSON objects, one per
   line, and applies them to streams."
-  [core ch req]
+  [core stats ch req]
   (let [body (:body req)
         body (if-not (channel? body)
                (closed-channel body)
                body)]
+
+    ; Track connections
+    (swap! (:conns stats) inc)
+    (on-closed body #(swap! (:conns stats) dec))
+
     (->> body
       json-channel
       (map* (fn handle [event]
@@ -143,12 +148,13 @@
           (:query-string req))

     ; Stats
-    (swap! (:conns stats) inc)
-    (on-closed ch #(swap! (:conns stats) dec))
+    (when (:websocket? req)
+      (swap! (:conns stats) inc)
+      (on-closed ch #(swap! (:conns stats) dec)))

     ; Route request
     (condp re-matches (:uri req)
-      #"/events/?"       (put-events-handler @core ch req)
+      #"/events/?"       (put-events-handler @core stats ch req)
       #"/index/?"        (ws-index-handler @core ch req)
       #"/pubsub/[^/]+/?" (ws-pubsub-handler @core ch req)
       :else (do


Add more examples.

Add more examples.

--- src/african_polyphony_and_polyrhythm/talk.clj
+++ src/african_polyphony_and_polyrhythm/talk.clj
@@ -2,8 +2,7 @@
   (:require [overtone.live :refer :all :exclude [stop scale]]
             [leipzig.melody :refer :all]
             [leipzig.canon :refer [canon]]
-            [leipzig.scale :refer [scale A B high low from]]
-            [leipzig.temperament :as temperament]
+            [leipzig.scale :refer [scale high low from]]
             [leipzig.live :as live]
             [leipzig.live :refer [stop]]))

@@ -137,12 +136,21 @@
 ; Raw frequencies
 (comment
   (->> (phrase (repeat 1/4) [440 494 554 622 659 740 831 880])
+       live/play)
+
+  ; Inaudible
+  (->> (phrase (repeat 1/4) [0 1 2 3 4 5 6 7])
        live/play))

 ; Midi
 (comment
   (->> (phrase (repeat 1/4) [69 71 73 74 76 78 80 81])
-       (where :pitch temperament/equal)
+       (where :pitch midi->hz)
+       live/play)
+
+  ; Inaudible
+  (->> (phrase (repeat 1/4) [0 1 2 3 4 5 6 7])
+       (where :pitch midi->hz)
        live/play))

 ; Scales
@@ -152,8 +160,30 @@
 (def central-african-scale (comp high pentatonic -))

 (comment
+  (major 0)
+  (major 2)
+  (minor 2)
+
+  (central-african-scale 0)
+  (central-african-scale 2)
+
+  ; Inaudible
+  (->> (phrase (repeat 1/4) [0 1 2 3 4 5 6 7])
+       (where :pitch (comp midi->hz major))
+       live/play))
+
+; Keys
+(def A #(+ 69 %))
+(def B #(+ 71 %))
+(def C #(+ 72 %))
+
+(comment
   (->> (phrase (repeat 1/4) (range 0 8))
-       (where :pitch (comp temperament/equal A major))
+       (where :pitch (comp midi->hz A major))
+       live/play)
+
+  (->> (phrase (repeat 1/4) (range 0 8))
+       (where :pitch (comp midi->hz A central-african-scale))
        live/play))

@@ -223,7 +253,7 @@
   (->>
     ndereje-balendoro
     (map pan)
-    (where :pitch (comp temperament/equal A central-african-scale))
+    (where :pitch (comp midi->hz A central-african-scale))
     (tempo (bpm 120))
     (live/play)))


Whitespace and naming.

Whitespace and naming.

--- src/african_polyphony_and_polyrhythm/talk.clj
+++ src/african_polyphony_and_polyrhythm/talk.clj
@@ -57,7 +57,7 @@
          vary
          (map #(all :part instrument %)))))

-(defn introduce-after
+(defn introduce-successively
   "Gradually introduce each part after t beats."
   [t parts]
   (->> parts
@@ -121,7 +121,7 @@
   (let [drums [child mother father]]
     (->> drums
          (map rand-variations)
-         (introduce-after 4)
+         (introduce-successively 4)
          (tempo (bpm 90)))))

 (comment
@@ -302,19 +302,20 @@
 (defn ndereje-balendoro
   "Linda horn music - page 316"
   []
-  (->> [tete ta ha tulule bongo
+  (->> [tete ;ta ha tulule bongo
         ;(big tete) (big ta) (big ha) (big tulule) (big bongo)
         ;(big (big tete)) (big (big ta)) (big (big ha)) (big (big tulule)) (big (big bongo))
         ;(big (big (big tete))) (big (big (big ta))) (big (big (big ha)))
         ]
        (map rand-variations)
-       (introduce-after 4)
+       (introduce-successively 4)
        (map pan)
        (where :pitch (comp midi->hz high A little-endian-pentatonic))
        (tempo (bpm 120))))

 (comment
   (live/play (ndereje-balendoro)))
+


Close over construction of Om descriptors (perf).

--- frontend/core.cljs
+++ frontend/core.cljs
@@ -183,10 +183,10 @@
     :shared {:comms comms
              :timer-atom (setup-timer-atom)
              :_app-state-do-not-use state}
-    :instrument (fn [f cursor m]
-                  (let [methods (cond-> state-graft/no-local-state-methods
-                                  instrument? instrumentation/instrument-methods)
-                        descriptor (state-graft/no-local-descriptor methods)]
+    :instrument (let [methods (cond-> state-graft/no-local-state-methods
+                                instrument? instrumentation/instrument-methods)
+                      descriptor (state-graft/no-local-descriptor methods)]
+                  (fn [f cursor m]
                     (om/build* f cursor (assoc m :descriptor descriptor))))}))

 (defn find-top-level-node []


added refresh! to data fetch

added refresh! to data fetch

--- src/untangled/client/data_fetch.cljc
+++ src/untangled/client/data_fetch.cljc
@@ -335,3 +335,6 @@
       (failed? state) (failed-render props)
       (and not-present-render (nil? props)) (not-present-render props)
       :else (data-render props))))
+
+(defn refresh! [component]
+  (load component (om/get-ident component) (om/react-type component)))


check for babel.generate/generate's returning a keyword (likely :fail)

--- src/babel/english.cljc
+++ src/babel/english.cljc
@@ -41,7 +41,8 @@
    (let [result (generate/generate spec model
                                    :max-total-depth max-total-depth
                                    :truncate-children truncate-children)]
-     (if result
+     (if (keyword? result)
+       (throw (Exception. (str "please don't send me a keyword :( : this is what you sent me: " result)))
        (conj {:surface (morph result)}
              result)))))


Remove unused source file

Remove unused source file

--- src/bitcoin/wallet.clj
+++ src/bitcoin/wallet.clj
@@ -1,88 +0,0 @@
-(ns bitcoin.wallet
-  (:gen-class :main true)
-  (:import java.awt.image.BufferedImage
-           [java.awt Font Color]
-           com.google.zxing.qrcode.QRCodeWriter
-           com.google.zxing.common.BitMatrix
-           com.google.zxing.BarcodeFormat
-           com.google.zxing.client.j2se.MatrixToImageWriter
-           com.fruitcat.bitcoin.BIP38
-           java.math.BigInteger
-           [org.bitcoinj.core Address ECKey NetworkParameters DumpedPrivateKey])
-  (:require [clojure.string :as str]
-            [pandect.algo.sha256 :refer :all]))
-
-;; generate qr code as an image
-(defn qr [s]
-  (let [writer (QRCodeWriter.)
-        matrix (.encode writer s BarcodeFormat/QR_CODE 300 300)]
-    (MatrixToImageWriter/toBufferedImage matrix)))
-
-(defn address [k]
-  (Address. (NetworkParameters/prodNet) (.getPubKeyHash k)))
-
-(defn gen-image [qr-key qr-add key-str add-str]
-  ;; I'm no artist, I just draw everything with absolute coordinates
-  ;; in a way that looks ok for me.
-  (let [img (BufferedImage. 800 350 BufferedImage/TYPE_INT_RGB)
-        g (.getGraphics img)]
-    (.setPaint g (Color/WHITE))
-    (.fillRect g 0 0 800 350)
-    (.drawImage g qr-add 0 20 nil)
-    (.drawImage g qr-key 500 20 nil)
-    (.setPaint g (Color/BLACK))
-    (.setFont g (Font. "Courier" Font/PLAIN 14))
-    (.drawString g "Bitcoin Address" 35 30)
-    (if (= \5 (first key-str))
-      (do
-        (.drawString g "Private Key (Wallet Import Format)" 400 30)
-        (.drawString g key-str 360 320))
-      (do
-        (.drawString g "Private Key (Encrypted Format)" 420 30)
-        (.drawString g key-str 320 320)))
-    (.drawString g add-str 35 320)
-    (.setPaint g (Color/GREEN))
-    (.setFont g (Font. "Courier" Font/BOLD 20))
-    (.drawString g "SHARE" 210 30)
-    (.setPaint g (Color/RED))
-    (.drawString g "SECRET" 695 30)
-    (javax.imageio.ImageIO/write img "png" (java.io.File. "wallet.png"))))
-
-(defn gen-key []
-  ;; hack to get the key uncompressed, because bitcoinj makes it compressed by default.
-  (.getPrivateKeyEncoded (ECKey. (.getPrivKeyBytes (ECKey.)) nil) (NetworkParameters/prodNet)))
-
-(defn minikey-candidate []
-  (let [key-str (.toString (gen-key))]
-    (str "S" (subs key-str 2 31))))
-
-(defn minikey-validate [key-str]
-  (.startsWith (sha256 (str key-str "?")) "00"))
-
-(defn gen-minikey []
-  (loop [testkey (minikey-candidate)]
-    (if (minikey-validate testkey)
-         testkey
-      (recur (minikey-candidate)))))
-
-(defn minikey-to-address [minikey]
-  (.toString (address (ECKey/fromPrivate (BigInteger. (sha256 minikey) 16) false))))
-
-(defn gen-wallet [passphrase]
-  (let [key-str (if passphrase
-                  (com.fruitcat.bitcoin.BIP38/generateEncryptedKey passphrase)
-                  (.toString (gen-key)))
-        plain-key (if passphrase
-                    (com.fruitcat.bitcoin.BIP38/decrypt passphrase key-str)
-                    key-str)
-        k (.getKey (DumpedPrivateKey. (NetworkParameters/prodNet) plain-key))
-        add-str (.toString (address k))]
-    (println add-str key-str passphrase)))
-
-(defn gen-miniwallet []
-  (let [key-str (gen-minikey)
-        add-str (minikey-to-address key-str)]
-    (println add-str key-str)))
-
-(defn -main [& args]
-  (doseq [x args] (gen-miniwallet)))


serve index.html by default

serve index.html by default

--- src/clj/mpd/handler.clj
+++ src/clj/mpd/handler.clj
@@ -6,12 +6,19 @@
         ring.middleware.file-info)
   (:require [org.httpkit.server :as http-kit]))

+(defn wrap-dir-index [handler]
+  (fn [req]
+    (handler
+      (update-in req [:uri]
+                 #(if (= "/" %) "/index.html" %)))))
+
 (def app
   (-> (fn [req] {:status 404
                  :headers {"Content-Type" "text/html"}
                  :body "Gone"})
       (wrap-resource "public")
       (wrap-file-info)
+      (wrap-dir-index)
       (wrap-with-logger)
       (wrap-reload)))


Remove unused variables

Remove unused variables

--- src/cljam/io/bigwig.clj
+++ src/cljam/io/bigwig.clj
@@ -414,8 +414,7 @@
 (defn- read-blocks
   "Reads blocks according to BbiChromInfo data and returns tracks described
   Wig or BedGraph format."
-  [^RandomAccessFile r ^BbiChromInfo chrom-info ^URL url
-   {:keys [fixed-width-header cir-tree]}]
+  [^RandomAccessFile r ^BbiChromInfo chrom-info ^URL url {:keys [cir-tree]}]
   (let [blocks (fetch-overlapping-blocks-group r chrom-info cir-tree)
         rng {:start 0, :end (:size chrom-info)}]
     (letfn [(bigwig-> [offset after blocks acc]
@@ -426,7 +425,7 @@
                   (->> (with-open [is (cio/input-stream url)]
                          (lsb/skip is offset)
                          (with-open [is (InflaterInputStream. is)]
-                           (let [chrom-id (lsb/read-uint is)
+                           (let [_chrom-id (lsb/read-uint is)
                                  start (lsb/read-uint is)
                                  end (lsb/read-uint is)
                                  item-step (lsb/read-uint is)


Fix documentation in `clojure.core`.

Fix documentation in `clojure.core`.

--- src/cljc/quil/core.cljc
+++ src/cljc/quil/core.cljc
@@ -404,8 +404,9 @@
   "Draws an arc in the display window. Arcs are drawn along the outer
   edge of an ellipse defined by the `x`, `y`, `width` and `height`
   parameters. The origin or the arc's ellipse may be changed with the
-  [[ellipse-mode]] function. The `start` and `stop` parameters specify the
-  angles at which to draw the arc. The `mode` is either `:open`, `:chord` or `:pie`."
+  [[ellipse-mode]] function. The `start` and `stop` parameters specify
+  the angles at which to draw the arc. The `mode` is either `:open`,
+  `:chord` or `:pie`."
   ([x y width height start stop]
    (.arc (current-graphics) (float x) (float y) (float width) (float height)
          (float start) (float stop)))
@@ -529,10 +530,10 @@
     :subcategory "Vertex"
     :added "2.0"}
   begin-contour
-  "Use the [[begin-contour]] and [[end-contour]] function to create negative
-  shapes within shapes. These functions can only be used within a
-  [[begin-shape]]/[[end-shape]] pair and they only work with the `:p2d` and `:p3d`
-  renderers."
+  "Use the [[begin-contour]] and [[end-contour]] function to create
+  negative shapes within shapes. These functions can only be used
+  within a [[begin-shape]]/[[end-shape]] pair and they only work with
+  the `:p2d` and `:p3d` renderers."
   []
   (.beginContour (current-graphics)))

@@ -712,10 +713,11 @@
     :added "1.0"}
   blend
   "Blends a region of pixels from one image into another with full alpha
-  channel support. If `src` is not specified it defaults to [[current-graphics]].
-  If dest is not specified it defaults to [[current-graphics]].
-
-  Note: it is recommended to use the [[blend-mode]] function instead of this one.
+  channel support. If `src` is not specified it defaults to
+  [[current-graphics]]. If `dest-img` is not specified it defaults to
+  [[current-graphics]].
+
+  Note: it is recommended to use [[blend-mode]] instead of [[blend]].

   Available blend modes are:

@@ -998,7 +1000,7 @@
     :added "1.0"}
   color-mode
   "Changes the way Processing interprets color data. Available modes
-  are `:rgb` and `:hsb` (and `:hsl` in clojurescript).
+  are `:rgb` and `:hsb` (and `:hsl` in Clojurescript).
   By default, the parameters for [[fill]], [[stroke]],
   [[background]], and [[color]] are defined by values between 0 and 255 using
   the `:rgb` color model. The [[color-mode]] function is used to change the
@@ -1169,13 +1171,14 @@
   to use [[create-graphics]] with the `:opengl` renderer, because it doesn't
   allow offscreen use. The `:pdf` renderer requires the filename parameter.

-  Note: don't use [[create-graphics]] in draw in clojurescript, it leaks memory.
+  Note: don't use [[create-graphics]] in draw in Clojurescript, it leaks memory.
   You should create graphic in setup and reuse it in draw instead of creating
   a new one.

-  It's important to call any drawing commands between `(.beginDraw graphics)` and
-  `(.endDraw graphics)` statements or use [[with-graphics]] macro. This is also true
-  for any commands that affect drawing, such as [[smooth]] or [[color-mode]].
+  It's important to call any drawing commands between `(.beginDraw graphics)`
+  and `(.endDraw graphics)` statements or use [[with-graphics]] macro. This is
+  also true for any commands that affect drawing, such as [[smooth]] or
+  [[color-mode]].

   If you're using `:pdf` renderer - don't forget to call `(.dispose graphics)`
   as last command inside [[with-graphics]] macro, otherwise graphics won't be
@@ -1333,11 +1336,11 @@
     :subcategory "Curves"
     :added "1.0"}
   curve-point
-  "Evaluates the curve at point `t` for points `a`, `b`, `c`, `d`. The parameter
-  `t` varies between 0 and 1, `a` and `d` are points on the curve, and `b` and `c`
-  are the control points. This can be done once with the x
-  coordinates and a second time with the y coordinates to get the
-  location of a curve at `t`."
+  "Evaluates the curve at point `t` for points `a`, `b`, `c`, `d`. The
+  parameter `t` varies between 0 and 1, `a` and `d` are points on the
+  curve, and `b` and `c` are the control points. This can be done once
+  with the x coordinates and a second time with the y coordinates to
+  get the location of a curve at `t`."
   [a b c d t]
   (.curvePoint (current-graphics) (float a) (float b) (float c) (float d) (float t)))

@@ -1505,8 +1508,8 @@
     :added "2.5"}
   do-record
   "Macro for drawing on graphics which saves result in the file at the end.
-  Similar to [[with-graphics]] macro. [[do-record]] assumed to be used with `:pdf`
-  graphics.
+  Similar to [[with-graphics]] macro. [[do-record]] assumed to be used
+  with `:pdf` graphics.

   Example:
   ```
@@ -1602,10 +1605,10 @@
     :subcategory "Vertex"
     :added "2.0"}
   end-contour
-  "Use the [[begin-contour]] and [[end-contour]] function to create negative
-  shapes within shapes. These functions can only be within a
-  [[begin-shape]]/[[end-shape]] pair and they only work with the `:p2d` and `:p3d`
-  renderers."
+  "Use the [[begin-contour]] and [[end-contour]] function to create
+  negative shapes within shapes. These functions can only be within a
+  [[begin-shape]]/[[end-shape]] pair and they only work with the
+  `:p2d` and `:p3d` renderers."
   []
   (.endContour (current-graphics)))

@@ -1779,8 +1782,8 @@
        :subcategory "Pixels"
        :added "2.0"}
      filter-shader
-     "Originally named filter in Processing Language.
-  Filters the display window with given shader (only in `:p2d` and `:p3d` modes)."
+     "Originally named filter in Processing Language. Filters the display
+     window with given shader (only in `:p2d` and `:p3d` modes)."
      [^PShader shader-obj] (.filter (current-graphics) shader-obj)))

 (defn
@@ -3135,10 +3138,12 @@
   embedded to provide more control.

   The style information controlled by the following functions are
-  included in the style: [[fill]], [[stroke]], [[tint]], [[stroke-weight]],
-  [[stroke-cap]], [[stroke-join]], [[image-mode]], [[rect-mode]], [[ellipse-mode]],
-  [[shape-mode]], [[color-mode]], [[text-align]], [[text-font]], [[text-mode]], [[text-size]],
-  [[text-leading]], [[emissive]], [[specular]], [[shininess]], and [[ambient]]."
+  included in the
+  style: [[fill]], [[stroke]], [[tint]], [[stroke-weight]],
+  [[stroke-cap]], [[stroke-join]], [[image-mode]], [[rect-mode]],
+  [[ellipse-mode]], [[shape-mode]], [[color-mode]], [[text-align]],
+  [[text-font]], [[text-mode]], [[text-size]], [[text-leading]],
+  [[emissive]], [[specular]], [[shininess]], and [[ambient]]."
   []
   #?(:clj (.pushStyle (current-graphics))
      :cljs (.push (current-graphics))))
@@ -3434,7 +3439,7 @@
 (defn
   ^{:requires-bindings true
     :processing-name "resize()"
-    :p5js-name "resize()"
+    :p5js-name "Image.resize()"
     :category "Image"
     :processing-link "https://processing.org/reference/PImage_resize_.html"
     :added "2.1.0"}
@@ -4201,16 +4206,17 @@
     :added "1.0"}
   text
   "Draws text to the screen in the position specified by the `x` and `y`
-  parameters (and the optional `z` parameter in clj). A default font will be used
-  unless a font is set with the [[text-font]] function. Change the color of the
-  text with the [[fill]] function. The text displays in relation to the
-  [[text-align]] function, which gives the option to draw to the left, right, and
-  center of the coordinates.
-
-  The `x1`, `y1`, `x2` and `y2` parameters define a
-  rectangular area to display within and may only be used with string
-  data. For text drawn inside a rectangle, the coordinates are
-  interpreted based on the current [[rect-mode]] setting."
+  parameters (and the optional `z` parameter in clj). A default font
+  will be used unless a font is set with the [[text-font]]
+  function. Change the color of the text with the [[fill]]
+  function. The text displays in relation to the
+  [[text-align]] function, which gives the option to draw to the left,
+  right, and center of the coordinates.
+
+  The `x1`, `y1`, `x2` and `y2` parameters define a rectangular area
+  to display within and may only be used with string data. For text
+  drawn inside a rectangle, the coordinates are interpreted based on
+  the current [[rect-mode]] setting."
   ([^String s x y]
    (when (current-fill)
      (.text (current-graphics) s (float x) (float y))))
@@ -4805,68 +4811,75 @@
   inlined and that redefinitions to the original functions are reflected in
   the visualisation.

-  * `:size`           - A vector of width and height for the sketch or :fullscreen.
-                        Defaults to `[500 300]`. If you're using :fullscreen you may
-                        want to enable present mode - :features [:present].
-                        :fullscreen size works only in Clojure. In ClojureScript
-                        all sketches are support fullscreen when you press F11.
-  * `:renderer`       - Specifies the renderer type. One of `:p2d`, `:p3d`, `:java2d`,
-                        `:opengl`, `:pdf`, `:svg`). Defaults to `:java2d`. `:dxf` renderer
-                        can't be used as sketch renderer. Use [[begin-raw]] method
-                        instead. In clojurescript only `:p2d` and `:p3d` renderers
+  * `:size`           - A vector of width and height for the sketch or
+                        `:fullscreen`. Defaults to `[500 300]`. If you're using
+                        `:fullscreen` you may  want to enable present mode using
+                        `:features [:present]`. `:fullscreen` size works only in
+                        Clojure. In ClojureScript all sketches support
+                        fullscreen when you press `F11`.
+  * `:renderer`       - Specifies the renderer type. One of `:p2d`, `:p3d`,
+                        `:java2d`, `:opengl`, `:pdf`, `:svg`). Defaults to
+                        `:java2d`. `:dxf` renderer can't be used as sketch
+                        renderer. Use [[begin-raw]] method instead. In
+                        Clojurescript only `:p2d` and `:p3d` renderers
                         are supported.
-  * `:output-file`    - Specifies an output file path. Only used in `:pdf` and `:svg`
-                        modes. Not supported in clojurescript. When writing to a
-                        file, call [[exit]] at the end of the draw call to end
-                        the sketch and not write repeatedly to the file.
+  * `:output-file`    - Specifies an output file path. Only used in `:pdf` and
+                        `:svg` modes. Not supported in Clojurescript. When
+                        writing to a file, call [[exit]] at the end of the
+                        `draw` call to end the sketch and not write repeatedly
+                        to the file.
   * `:title`          - A string which will be displayed at the top of
-                        the sketch window. Not supported in clojurescript.
+                        the sketch window. Not supported in Clojurescript.
   * `:features`       - A vector of keywords customizing sketch behaviour.
                         Supported features:
     - `:keep-on-top`   - Sketch window will always be above other windows.
-                         Note: some platforms might not support always-on-top windows.
-                         Not supported in clojurescript.
+                         Note: some platforms might not support always-on-top
+                         windows. Not supported in clojurescript.
     - `:exit-on-close` - Shutdown JVM  when sketch is closed.
-                         Not supported in clojurescript.
-    - `:resizable`     - Makes sketch resizable. Not supported in clojurescript.
-    - `:no-safe-fns`   - Do not catch and print exceptions thrown inside functions
-                         provided to sketch (like draw, [[mouse-clicked?]],
-                         [[key-pressed?]] and others). By default all exceptions
-                         thrown inside these functions are caught. This prevents
-                         sketch from breaking when bad function was provided and
-                         allows you to fix it and reload it on fly. You can
-                         disable this behaviour by enabling `:no-safe-fns`
-                         feature. Not supported in clojurescript.
+                         Not supported in Clojurescript.
+    - `:resizable`     - Makes sketch resizable. Not supported in Clojurescript.
+    - `:no-safe-fns`   - Do not catch and print exceptions thrown inside
+                         functions provided to sketch (like draw,
+                         [[mouse-pressed?]], [[key-pressed?]] and others). By
+                         default all exceptions thrown inside these functions
+                         are caught. This prevents the sketch from breaking when
+                         a bad function is provided and allows you to fix it and
+                         reload it on the fly. You can disable this behaviour by
+                         enabling the `:no-safe-fns` feature. Not supported in
+                         Clojurescript.
     - `:present`       - Switch to present mode (fullscreen without borders, OS
                          panels). You may want to use this feature together with
                          `:size :fullscreen`. Not supported in ClojureScript. In
                          ClojureScript fullscreen is enabled by pressing F11 and
                          it's enabled on all sketches automatically.
-    - `:no-start`      - Disables autostart if sketch was created using defsketch
-                         macro. To start sketch you have to call function created
-                         defsketch. Supported only in ClojureScript.
+    - `:no-start`      - Disables autostart if sketch was created using the
+                         [[defsketch]] macro. To start sketch you have to call
+                         function created [[defsketch]]. Supported only in
+                         ClojureScript.
                         Usage example: `:features [:keep-on-top :present]`
   * `:bgcolor`        - Sets background color for unused space in present mode.
                         Color is specified in hex format for example
                         `:bgcolor \"#00FFFF\"` (cyan background)
                         Not supported in ClojureScript.
   * `:display`        - Sets what display should be used by this sketch.
-                        Displays are numbered starting from 0. Example: `:display 1`.
+                        Displays are numbered starting from 0.
+                        Example: `:display 1`.
                         Not supported in ClojureScript.
   * `:setup`          - A function to be called once when setting the sketch up.
   * `:draw`           - A function to be repeatedly called at most n times per
                         second where n is the target [[frame-rate]] set for
                         the visualisation.
   * `:host`           - String id of canvas element or DOM element itself.
-                        Specifies host for the sketch. Must be specified in sketch,
-                        may be omitted in defsketch. If omitted in defsketch,
-                        :host is set to the name of the sketch. If element with
-                        specified id is not found on the page and page is empty -
-                        new canvas element will be created. Used in ClojureScript.
+                        Specifies host for the sketch. Must be specified in
+                        sketch, may be omitted in defsketch. If omitted in
+                        [[defsketch]], `:host` is set to the name of the sketch.
+                        If element with specified id is not found on the page
+                        and page is empty, a new canvas element will be created.
+                        Used in ClojureScript.
   * `:focus-gained`   - Called when the sketch gains focus.
                         Not supported in ClojureScript.
-  * `:focus-lost`     - Called when the sketch loses focus.
-                        Not supported in ClojureScript.
+  * `:focus-lost`     - Called when the sketch loses focus. Not supported in
+                        ClojureScript.
   * `:mouse-entered`  - Called when the mouse enters the sketch window.
   * `:mouse-exited`   - Called when the mouse leaves the sketch window
   * `:mouse-pressed`  - Called every time a mouse button is pressed.
@@ -4878,10 +4891,10 @@
   * `:mouse-dragged`  - Called every time the mouse moves and a button is
                         pressed.
   * `:mouse-wheel`    - Called every time mouse wheel is rotated.
-                        Takes 1 argument - wheel rotation, an int.
+                        Takes 1 argument - wheel rotation, an `int`.
                         Negative values if the mouse wheel was rotated
                         up/away from the user, and positive values
-                        if the mouse wheel was rotated down/ towards the user
+                        if the mouse wheel was rotated down/towards the user.
   * `:key-pressed`    - Called every time any key is pressed.
   * `:key-released`   - Called every time any key is released.
   * `:key-typed`      - Called once every time non-modifier keys are
@@ -4889,12 +4902,14 @@
   * `:on-close`       - Called once, when sketch is closed.
                         Not supported in ClojureScript.
   * `:middleware`     - Vector of middleware to be applied to the sketch.
-                        Middleware will be applied in the same order as in comp
-                        function: [f g] will be applied as `(f (g options))`.
+                        Middleware will be applied in the same order as in
+                        `comp` function: `[f g]` will be applied as
+                        `(f (g options))`.
   * `:settings`       - Cousin of `:setup`. A function to be called once when
-                        setting sketch up. Should be used only for [[smooth]] and
-                        [[no-smooth]]. Due to Processing limitations these functions
-                        cannot be used neither in `:setup` nor in `:draw`."
+                        setting sketch up. Should be used only for [[smooth]]
+                        and [[no-smooth]]. Due to Processing limitations these
+                        functions cannot be used neither in `:setup` nor in
+                        `:draw`."
   [app-name & options]
   #?(:clj
      (if (u/clj-compilation?)
@@ -4924,8 +4939,8 @@
         :added "1.6"}
   key-as-keyword
   "Returns a keyword representing the currently pressed key. Modifier
-  keys are represented as: `:up`, `:down`, `:left`, `:right`, `:alt`, `:control`,
-  `:shift`, `:command`, `:f1-24`"
+  keys are represented as: `:up`, `:down`, `:left`, `:right`, `:alt`,
+  `:control`, `:shift`, `:command`, `:f1-24`"
   []
   (let [key-char (raw-key)
         code     (key-code)]