How do I use core.match in Clojurescript with goog.events.KeyCodes?

812 views Asked by At
(defn editing-mode? []
  "a hardcoded (for the moment) value, will look up in db later"
  false)

(def UP 38) ;; goog.events.KeyCodes.UP
(def DOWN 40) ;; goog.events.KeyCodes.DOWN
(def LEFT 37) ;; goog.events.KeyCodes.LEFT
(def RIGHT 39) ;; goog.events.KeyCodes.RIGHT
(def W 87) ;; goog.events.KeyCodes.W
(def S 83) ;; goog.events.KeyCodes.S
(def A 65) ;; goog.events.KeyCodes.A
(def D 68) ;; goog.events.KeyCodes.D
(def E 69) ;; goog.events.KeyCodes.E
(def ESC 27) ;; goog.events.KeyCodes.ESC

(defn delta [e]
  ;; e is a google closure Event
  (js/console.log (.-keyCode e))
  (js/console.log (editing-mode?))
  (match [(editing-mode?) (.-keyCode e)]
   [false 38] [:slide :up]
   [false 40] [:slide :down]
   [false 37] [:slide :left]
   [false 39] [:slide :right]
   [false 87] [:slide :up]
   [false 83] [:slide :down]
   [false 65] [:slide :left]
   [false 68] [:slide :right]
   [false 69] [:start-editing]
   [true 27]  [:done-editing]
   :else nil))

The above code works. However, If I try to be a little less wordy and use the goog keycodes directly, like so

(match [(editing-mode?) (.-keyCode e)]
  [false goog.events.KeyCodes.UP] [:slide :up]
  [false goog.events.keyCodes.DOWN] [:slide :down]
  ...

I get the following cljsbuild error:

...
Caused by: clojure.lang.ExceptionInfo: Invalid local name: goog.events.KeyCodes.UP ...
...

Ok, so I can't use the goog.events.KeyCodes.* themselves, but maybe I can use a def referenced to them? So I try

(match [(editing-mode?) (.-keyCode e)]
   [false UP] [:slide :up]
   [false DOWN] [:slide :down]
   ...

This does compile, but now match just isn't working. Every key event matches to the [false UP] match clause (core.match always emits [:slide :up]).

Anyway, the first code example does work. But why can't I use goog.events.KeyCodes.* or references to goog.events.KeyCodes.* in my core.match matcher? Is there something I am missing?

3

There are 3 answers

1
Beyamor On BEST ANSWER

A key part of core.match is that symbols will be bound to values rather than being matched against. That is, the current value of UP is not looked up and used in the match; instead, the symbol UP gets bound to the value (.-keyCode e) when false gets matched.

Unfortunately, insofar as I know, there's nothing you can do about that with core.match. It's very much dependent on literal values. However, since your pattern is fairly simple, you could use (conp = ...).

0
ClojureMostly On

You can only match against local bindings:

      (= (let [x 2
               y 2]
           (match [x]
             [0] :a0
             [1] :a1
             [y] :a2
             :else :a3))
        :a2))

So you could achieve your result with first binding the keycodes to a local variables via (let [...]).

0
Timothy Pratley On

I like to construct a map of "keyname" to "keycode" by simply inverting the KeyCodes object:

(ns awesome.sauce
  (:require [clojure.set :as set])
  (:import [goog.events KeyCodes]))

(def codename
  (set/map-invert (js->clj KeyCodes)))

Then I can identify keys with strings instead of clunky interop (and strings are values so play nicely with matching):

(match [(editing-mode?) (codename (.-keyCode e))]
   [false "UP"] [:slide :up]
   [false "DOWN"] [:slide :down])