Maybe. I do sometimes look into other projects to see if there is something I also need. That might be something like parinfer og rainbow parenthesis. If I did parinfer, it should be with an on/off switch I think. I want to keep the core as simple as possible and provide extra functionality through extensions, so I aim at making Liquid extensible rather then with a lot of features. I like the features not to be intrusive, so I can work with text without things and suggestions popping up all the time. But I do like advanced features to be available at the click of a button/keyboard. There are already advanced features like, "Jump to definition", "Jump to file", "Paren matching", "Lookup function". When I need something I usually just implement it. If it is a basic feature, I put it into core. If it does not belong to core, I put it into the Additives project and if I am probably the only one going to use it, I implement it as a local extension. I do have a lot of local extensions to ease specific parts of my day to day workflow. What I am about to focus on is making the features easier for others to find and use. And making it easier to understand how to extend Liquid. This means more documentation and more tutorials and more focus on best practices.
@mogenslund: making it obvious how to write the "init.el" of liquid would be huge; I was thinking about the minimal set of features I need to port over from emacs to hit current levels of productivity in liquid, and I got it down to: https://github.com/abo-abo/lispy + https://github.com/clojure-emacs/cider and that's basically enough
something I've recently started doing, (not sure what your take on this is) --- is I've found using spec intensely allows me to define the "nouns" of the project, which are sorta like types, and this makes it easier to read (as I can see what the input/output "shapes" are
Lispy seems like a good fit for Liquid, since it is command based and not automatic. I would be able to add one function at a time and allow the user to redefine the keyboard shortcuts to fit their needs. I will add to my todo to take a further look at it. The features you would like from Cider, is that connecting to an external repl and push code? I really want to leverage spec, so it is definitely on my "roadmap" to make use of that. I just need some practice 🙂 In Liquid the corresponding to "init.el" I call ".liq". But I actually want to get rid of that again. After Clojure 1.9 and the introduction of deps.edn and the clj command, I would like the user to have a small local project depending on Liquid. This allows easy extension from source, Clojars or Maven through the deps.edn file. This means that git and Maven can be used as some kind of package manager. I have tried to describe it in http://salza.dk/setupbasic.html, but it probably needs some better explanation and simplificion, to lower the effort needed to get started. This is a key job for me to do.
(re spec) :// here is a macro I use:
(do ;; macros
(defmacro a! [e msg] `(let [o# ~e] (assert o# ~msg) o#))
(def tag-is (fn [t] (fn [x] (= (:tag x) t))))
(ct/is (= ((tag-is :foo) {:tag :foo}) true))
(ct/is (= ((tag-is :foo) {:tag :bar}) false))
(let [s-compare #(compare (str %1) (str %2))
sort0 #(sort-by key s-compare %)
def-fields-m #(for [[k v] (sort0 %)] `(s/def ~k ~v))
def-fields-inv #(for [[v ks] (sort0 %) k ks] `(s/def ~k ~v))
kw->symn #(symbol (str (name %) "-raw"))
def-types-m (fn [args] (for [[kw spec-def] (sort0 args)]
`(do (s/def ~kw (s/and (tag-is ~kw) ~spec-def))
(defn ~(kw->symn kw) [obj#]
(s/assert ~kw (into {:tag ~kw} obj#))))))]
(defmacro def-nouns [{:keys [fields types invs]}]
`(do ~@(def-fields-m fields) ~@(def-fields-inv invs) ~@(def-types-m types)))))
then I can use it as follows:
(def-nouns ;; geom
{:invs {number? [::x ::y ::x0 ::x1 ::y0 ::y1 ::width ::height
::xs ::ys
::italic]}
:fields {::data any?}
:types {::vec2 (s/keys :req [::x ::y])
::size (s/keys :req [::width ::height])
::box (s/keys :req [::x0 ::x1 ::y0 ::y1])}})
(def-nouns ;; events
{:invs {boolean? [::altKey ::ctrlKey ::metaKey ::shiftKey]
string? [::key]}
:fields {::objId some?
::events #(set/subset? % events)}
:types {::windowSize (s/keys :req [::size])
::onClick (s/keys :req [::objId])
::onTouchStart (s/keys :req [::objId])
::keyEvent (s/keys :req [::altKey ::ctrlKey ::metaKey ::shiftKey
::key])
::keyDown (s/keys :req [::keyEvent])}})
(def-nouns ;; svg elems
{:invs {number? [::unicode ::strokeWidth ::strokeOpacity ::fontSize ::fillOpacity
::btnW ::btnH ::padding]
::vec2 [ ::p0 ::p1]
colorStr [::strokeColor ::fillColor]}
:fields {::className string?
::ibox ::box
::fontFamily keyword?
::node (s-shallow-one-of [::line ::rect ::text ::group ::domSvg])
::nodes (s/coll-of ::node :kind vector?)}
:types {::bnode (s/keys :req [ ::vec2 ::ibox ::nodes])
::line (s/keys :req [ ::p0 ::p1]
:opt [::className])
::rect (s/keys :req [ ::vec2 ::size]
:opt [::className ::objId ::events ::rx ::ry])
::text (s/keys :req [ ::fontSize ::fontFamily ::unicode]
:opt [::className])
::group (s/keys :req [ ::vec2 ::nodes])
::domSvg (s/keys :req [::windowSize ::nodes])}})
so the ::types is basically like a struct, it's saying "here are the keys of this map/noun" the :fields define fields of (spec-kw, function) the :inv is for when you have ots of fields that map to the same function/spec-kw
the useful thing is taht it createw functions of name line-raw ... which then asserts that all fields are correct (so you get an runtime error asap instead of later on)
Thank you. I have added the snippet to my notes, so it does not get lost on slack 🙂
I have used Paredit, Lispy, ParInfer before. Paredit + Lispy I like. Parinfer I nefer understood. I have a question for you: how do you intend to implement something like paredit / lispy? It seems that to get the movement keys right, we have to get a lisp sexp ast first, which means basically parsing :before and :afte, which seems like it may be expensive ops.