Open bobbicodes opened 11 months ago
I've simplified the commands by only implementing the forward versions, which are the only ones I've ever used. Tbh I barely ever use barf, I only use slurp forward in the case that I want to add a form at the beginning of a list, which is quite often.
(n/closest (n/tree state from)
(every-pred n/coll?
#(not (some-> % n/with-prefix n/right n/end-edge?))))
(first (remove n/line-comment? (n/rights (n/with-prefix parent))))
from
(let [edge (n/down-last parent)]
[{:from (-> target n/end)
:insert (n/name edge)}
(-> edge n/from-to (j/assoc! :insert " "))])
(-> (n/tree state from) (n/closest n/coll?))
(some->> (n/down-last parent)
n/lefts
(remove n/line-comment?)
(drop 1)
first)
(min (n/end target) from)
[{:from (n/end target)
:insert (n/name (n/down-last parent))}
(-> (n/down-last parent)
n/from-to
(j/assoc! :insert " "))]
This is the general idea. They will just need to be modified to conform to the specs above.
function slurp(view) {
var doc = view.state.doc.toString()
var pos = mainSelection(view.state).from
var node = tree(view.state, pos)
var parent = up(node)
console.log("rights")
var target = rights(node)[0]
var edge = parent.lastChild
var s1 = right(node).from
var e1 = right(node).to
var s2 = node.from + 1
var e2 = node.to
view.dispatch({
changes: { from: pos, to: 7,
insert: doc.substring(s1, e1) + doc.substring(s2, e2)}
})
return true
}
function barf(view) {
var doc = view.state.doc.toString()
var pos = mainSelection(view.state).from
var node = tree(view.state, pos)
var parent = up(node)
var target = lefts(node)[0]
var edge = parent.lastChild
var s1 = node.to - 1
var e1 = node.to
var s2 = node.from + 1
var e2 = node.to - 1
view.dispatch({
changes: { from: pos, to: 7,
insert: doc.substring(s1, e1) + doc.substring(s2, e2)}
})
return true
}
Note: These will convert the form ()range
to (range)
and back again with the cursor in the second position, but will fail on anything else. A decent start.
The Clojure functions above for defining the node parent, target, cursor, and changes values are dependent on numerous sub-processes, so I'll enumerate them here for completeness. Then I'm hoping that the clearest implementation should follow naturally.
(n/closest (n/tree state from)
(every-pred n/coll?
#(not (some-> % n/with-prefix n/right n/end-edge?))))
(defn ancestors [^js node]
(when-some [parent (up node)]
(cons parent
(lazy-seq (ancestors parent)))))
(defn ^js closest [node pred]
(if (pred node)
node
(reduce (fn [_ x]
(if (pred x) (reduced x) nil))
nil (ancestors node))))
(defn ^js tree
"Returns a (Tree https://lezer.codemirror.net/docs/ref/#common.Tree) for editor state
or the SyntaxNode at pos.
If pos is given and we're using Clojure language support embedded in other languages (e.g. markdown)
enters overlaid Clojure nodes (https://lezer.codemirror.net/docs/ref/#common.MountedTree)."
([^js state] (language/syntaxTree state))
([^js state pos] (-> state language/syntaxTree (.resolveInner pos)))
([^js state pos dir] (-> state language/syntaxTree (.resolveInner pos dir))))
(defn guard [x f] (when (f x) x))
(defn ^js up [node] (.-parent ^js node))
(defn ^js down [node]
{:pre [(not (fn? (.-lastChild ^js node)))]}
(.-firstChild ^js node))
(defn prefix [node]
(when-some [parent (up node)]
(or (u/guard parent prefix-container?)
(u/guard (down parent) prefix-edge?))))
(defn with-prefix [node]
(cond-> node
(prefix node) up))
(defn ^number end [^js node]
{:pre [(.-to node)]}
(.-to node))
(defn ^js right [node]
(.childAfter (up node) (end node))
#_(.-nextSibling node))
(defn ^lz-tree/NodeType type [^js node] (.-type node))
(defn ^boolean end-edge-type? [node-type] (.prop ^js node-type end-edge-prop))
(defn ^boolean end-edge? [n] (end-edge-type? (type n)))
(first (remove n/line-comment? (n/rights (n/with-prefix parent))))
(defn line-comment? [node] (identical? "LineComment" (name node)))
(defn ^js right [node]
(.childAfter (up node) (end node))
#_(.-nextSibling node))
(defn rights [node]
(take-while identity (iterate right (right node))))
(defn ^js down-last [node]
{:pre [(not (fn? (.-lastChild ^js node)))]}
(.-lastChild ^js node))
(defn ^string name [^js node] (.-name node))
(defn ^number start [^js node]
{:pre [(.-from node)]}
(.-from node))
(defn from-to
([from to] #js{:from from :to to})
([node]
(from-to (start node) (end node))))
(let [edge (n/down-last parent)]
[{:from (-> target n/end)
:insert (n/name edge)}
(-> edge n/from-to (j/assoc! :insert " "))])
(-> (n/tree state from) (n/closest n/coll?))
(some->> (n/down-last parent)
n/lefts
(remove n/line-comment?)
(drop 1)
first)
(min (n/end target) from)
[{:from (n/end target)
:insert (n/name (n/down-last parent))}
(-> (n/down-last parent)
n/from-to
(j/assoc! :insert " "))]
This has all been figured out before by the people at Nextjournal: https://github.com/nextjournal/clojure-mode/blob/7b911bf6feab0f67b60236036d124997627cbe5e/src/nextjournal/clojure_mode/commands.cljs#L138
It just needs to be translated into JavaScript.
The function doing most of the work is
update-ranges
:Notice the comment, (see
changeByRange
). This is referring to the Codemirror API: https://codemirror.net/examples/change/