From 17d1786b1d1a9f9ef3b3709614c58030dd2f6b26 Mon Sep 17 00:00:00 2001 From: Huahai Yang Date: Fri, 24 Jan 2025 16:31:57 -0800 Subject: [PATCH] 0.6.6 --- .github/workflows/build.yml | 16 +++- CHANGELOG.md | 2 +- project.clj | 2 +- src/editscript/util/index.cljc | 142 +++++++++++++++++++++++++++++++++ 4 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 src/editscript/util/index.cljc diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0e91e56..03efbe4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,9 +16,23 @@ jobs: - name: Git checkout uses: actions/checkout@v1 + - name: Prepare java + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '8' + + - name: Install clojure tools + uses: DeLaGuardo/setup-clojure@13.0 + with: + lein: 2.9.1 + + - uses: actions/setup-node@v4 + with: + node-version: '20' + - name: Run JVM tests run: lein test - name: Run node tests run: lein doo node once - diff --git a/CHANGELOG.md b/CHANGELOG.md index 04329ca..35f3e68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## [0.6.5] - 2025-01-24 +## [0.6.6] - 2025-01-24 ### Added - `data-nodes` to return the number of nodes of a piece of data. - `change-ratio` to return an approximation of an editscript's ratio of change diff --git a/project.clj b/project.clj index 1c2dee1..fa3b670 100644 --- a/project.clj +++ b/project.clj @@ -1,4 +1,4 @@ -(defproject juji/editscript "0.6.5" +(defproject juji/editscript "0.6.6" :description "A diff library for Clojure/ClojureScript data structures" :url "https://github.com/juji-io/editscript" :lein-release {:deploy-via :clojars} diff --git a/src/editscript/util/index.cljc b/src/editscript/util/index.cljc new file mode 100644 index 0000000..95317f6 --- /dev/null +++ b/src/editscript/util/index.cljc @@ -0,0 +1,142 @@ +;; +;; Copyright (c) Huahai Yang. All rights reserved. +;; The use and distribution terms for this software are covered by the +;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +;; which can be found in the file LICENSE at the root of this distribution. +;; By using this software in any fashion, you are agreeing to be bound by +;; the terms of this license. +;; You must not remove this notice, or any other, from this software. +;; + +(ns editscript.util.index + (:require [editscript.edit :as e] + #?(:cljs [goog.math.Long :refer [getMaxValue]])) + #?(:clj (:import [clojure.lang PersistentVector] + [java.io Writer])) ) + +;; indexing + +(defprotocol INode + (get-path [this] "Get the path to the node from root") + (get-value [this] "Get the actual data") + (get-children [this] "Get all children node in a map") + (add-child [this node] "Add a child node") + (get-key [this] "Get the key of this node") + (get-parent [this] "Get the parent node") + (get-first [this] "Get the first child node") + (get-last [this] "Get the last child node") + (get-next [this] "Get the next sibling node") + (set-next [this node] "Set the next sibling node") + (set-order [this o] "Set the traversal order of this node") + (get-order [this] "Get the order of this node in traversal") + (get-size [this] "Get the size of sub-tree, used to estimate cost") + (set-size [this s] "Set the size of sub-tree")) + +(deftype Node [^PersistentVector path + value + parent + ^:unsynchronized-mutable children + ^:unsynchronized-mutable first + ^:unsynchronized-mutable last + ^:unsynchronized-mutable next + ^:unsynchronized-mutable index + ^:unsynchronized-mutable ^long order + ^:unsynchronized-mutable ^long size] + INode + (get-path [_] path) + (get-key [this] (-> this get-path peek)) + (get-value [_] value) + (get-parent [_] parent) + (get-children [_] children) + (get-first [_] first) + (get-last [_] last) + (get-next [_] next) + (set-next [_ n] (set! next n)) + (get-order [_] order) + (set-order [this o] (set! order (long o)) this) + (get-size [_] size) + (set-size [this s] (set! size (long s)) this) + (add-child [_ node] + (set! children (assoc children (get-key node) node)) + (when last (set-next last node)) + (when-not first (set! first node)) + (set! last node) + node)) + +#?(:clj + (defmethod print-method Node + [x ^Writer writer] + (print-method {:value (get-value x) + :order (get-order x) + :children (get-children x)} + writer))) + +(declare index*) + +(defn- associative-children + "map and vector are associative" + [order path data parent] + (reduce-kv + (fn [_ k v] + (index* order (conj path k) v parent)) + nil + data)) + +(defn- set-children + "set is a map of keys to themselves" + [order path data parent] + (doseq [x data] + (index* order (conj path x) x parent))) + +(defn- list-children + "add index as key" + [order path data parent] + (reduce + (fn [i x] + (index* order (conj path i) x parent) + (inc ^long i)) + 0 + data)) + +(defn- inc-order + "order value reflects the size of elements" + [order ^long size] + (vswap! order (fn [o] (+ size ^long o)))) + +(defn- index-collection + [type order path data parent] + (let [node (->Node path data parent {} nil nil nil 0 0 1)] + (add-child parent node) + (case type + (:map :vec) (associative-children order path data node) + :set (set-children order path data node) + :lst (list-children order path data node)) + (let [^long cs (->> (get-children node) vals (map get-size) (reduce +)) + size (+ (long (get-size node)) cs)] + (doto node + (set-order @order) + (set-size size)) + (inc-order order size)) + node)) + +(defn- index-value + [order path data parent] + (let [node (->Node path data parent nil nil nil nil 0 @order 1)] + (add-child parent node) + (inc-order order 1) + node)) + +(defn- index* + [order path data parent] + (let [type (e/get-type data)] + (if (or (= type :val) (= type :str)) + (index-value order path data parent) + (index-collection type order path data parent)))) + +(defn index + "Traverse data to build an indexing tree of Nodes, + compute path, sizes of sub-trees, siblings, etc. for each Node. + This takes little time" + [data] + (let [order (volatile! 0)] + (index* order [] data (->Node [] ::dummy nil {} nil nil nil 0 -1 0))))