<commit_before>(ns plastic.cogs.editor.model
  (:require-macros [plastic.macros.logging :refer [log info warn error group group-end]])
  (:require [rewrite-clj.zip :as zip]
            [rewrite-clj.zip.findz :as findz]
            [rewrite-clj.zip.editz :as editz]
            [rewrite-clj.node.protocols :as node]
            [clojure.walk :refer [prewalk]]
            [plastic.cogs.editor.parser.utils :as parser]
            [plastic.util.helpers :as helpers]
            [plastic.util.zip :as zip-utils]))

(defn parsed? [editor]
  (contains? editor :parse-tree))

(defn get-parse-tree [editor]
  (let [parse-tree (get editor :parse-tree)]
    (assert parse-tree)
    parse-tree))

(defn set-parse-tree [editor parse-tree]
  {:pre [(= (node/tag parse-tree) :forms)]}
  (assoc editor :parse-tree parse-tree))

(defn get-selections [editor]
  (or (get editor :selections) {}))

(defn set-selections [editor selections]
  (assoc editor :selections selections))

(defn shift-selections [editor shift]
  (let [selections (get-selections editor)
        shift #(if (number? %) (+ % shift) %)
        shifted-selections (prewalk shift selections)]
    (set-selections editor shifted-selections)))

(defn get-focused-form-id [editor]
  (get-in editor [:selections :focused-form-id]))

(defn set-focused-form-id [editor form-id]
  {:pre [form-id]}
  (assoc-in editor [:selections :focused-form-id] form-id))

(defn get-focused-selection [editor]
  (let [selections (get-selections editor)
        focused-form-id (get-focused-form-id editor)]
    (get selections focused-form-id)))

(defn set-focused-selection [editor selection]
  (let [selections (get-selections editor)
        new-selections (assoc selections (get-focused-form-id editor) selection)]
    (set-selections editor new-selections)))

(defn set-render-state [editor render-state]
  (assoc-in editor [:render-state] render-state))

(defn get-id [editor]
  {:post [(pos? %)]}
  (:id editor))

(defn get-editing-set [editor]
  {:post [(set? %)]}
  (or (:editing editor) #{}))

(defn set-editing-set [editor editing-set]
  {:pre [(or (nil? editing-set) (set? editing-set))]}
  (assoc editor :editing (or editing-set #{})))

(defn editing? [editor]
  (let [editing (:editing editor)]
    (and editing (not (empty? editing)))))

(defn get-top-level-locs [editor]
  (let [top-loc (zip-utils/make-zipper (get-parse-tree editor)) ; root "forms" node
        first-top-level-form-loc (zip/down top-loc)]
    (zip-utils/collect-all-right first-top-level-form-loc)))    ; TODO: here we should use explicit zipping policy

(defn loc-id? [id loc]
  (= (:id (zip/node loc)) id))

(defn commit-value-to-loc [loc node-id value]
  (let [node-loc (findz/find-depth-first loc (partial loc-id? node-id))
        new-value (symbol value)]
    (assert (zip-utils/valid-loc? node-loc))
    (if (not= (node/sexpr (zip/node node-loc)) new-value)
      (editz/edit node-loc (fn [_prev] new-value)))))

(defn commit-node-value [editor node-id value]
  {:pre [node-id value]}
  (let [old-root (get-parse-tree editor)
        modified-loc (commit-value-to-loc (zip-utils/make-zipper old-root) node-id value)]
    (if-not modified-loc
      editor
      (let [parse-tree (parser/make-nodes-unique (zip/root modified-loc))
            id-shift (- (:id parse-tree) (:id old-root))]
        (-> editor
          (set-parse-tree parse-tree)
          (shift-selections id-shift))))))
<commit_msg>prevent selection jumping when toggling docs/code visibility<commit_after>(ns plastic.cogs.editor.model
  (:require-macros [plastic.macros.logging :refer [log info warn error group group-end]])
  (:require [rewrite-clj.zip :as zip]
            [rewrite-clj.zip.findz :as findz]
            [rewrite-clj.zip.editz :as editz]
            [rewrite-clj.node.protocols :as node]
            [clojure.walk :refer [prewalk]]
            [plastic.cogs.editor.parser.utils :as parser]
            [plastic.util.helpers :as helpers]
            [plastic.util.zip :as zip-utils]
            [plastic.cogs.editor.render.dom :as dom]))

(defn parsed? [editor]
  (contains? editor :parse-tree))

(defn get-parse-tree [editor]
  (let [parse-tree (get editor :parse-tree)]
    (assert parse-tree)
    parse-tree))

(defn set-parse-tree [editor parse-tree]
  {:pre [(= (node/tag parse-tree) :forms)]}
  (assoc editor :parse-tree parse-tree))

(defn get-selections [editor]
  (or (get editor :selections) {}))

(defn set-selections [editor selections]
  (assoc editor :selections selections))

(defn shift-selections [editor shift]
  (let [selections (get-selections editor)
        shift #(if (number? %) (+ % shift) %)
        shifted-selections (prewalk shift selections)]
    (set-selections editor shifted-selections)))

(defn get-focused-form-id [editor]
  (get-in editor [:selections :focused-form-id]))

(defn set-focused-form-id [editor form-id]
  {:pre [form-id]}
  (assoc-in editor [:selections :focused-form-id] form-id))

(defn get-focused-selection [editor]
  (let [selections (get-selections editor)
        focused-form-id (get-focused-form-id editor)]
    (get selections focused-form-id)))

(defn set-focused-selection [editor selection]
  (let [selections (get-selections editor)
        new-selections (assoc selections (get-focused-form-id editor) selection)]
    (set-selections editor new-selections)))

(defn set-render-state [editor render-state]
  (if (= (:render-state editor) render-state)
    editor
    (let [new-editor (assoc-in editor [:render-state] render-state)]
      (dom/postpone-selection-overlay-display-until-next-update editor)
      (dom/postpone-selection-overlay-display-until-next-update new-editor)
      new-editor)))

(defn get-id [editor]
  {:post [(pos? %)]}
  (:id editor))

(defn get-editing-set [editor]
  {:post [(set? %)]}
  (or (:editing editor) #{}))

(defn set-editing-set [editor editing-set]
  {:pre [(or (nil? editing-set) (set? editing-set))]}
  (assoc editor :editing (or editing-set #{})))

(defn editing? [editor]
  (let [editing (:editing editor)]
    (and editing (not (empty? editing)))))

(defn get-top-level-locs [editor]
  (let [top-loc (zip-utils/make-zipper (get-parse-tree editor)) ; root "forms" node
        first-top-level-form-loc (zip/down top-loc)]
    (zip-utils/collect-all-right first-top-level-form-loc))) ; TODO: here we should use explicit zipping policy

(defn loc-id? [id loc]
  (= (:id (zip/node loc)) id))

(defn commit-value-to-loc [loc node-id value]
  (let [node-loc (findz/find-depth-first loc (partial loc-id? node-id))
        new-value (symbol value)]
    (assert (zip-utils/valid-loc? node-loc))
    (if (not= (node/sexpr (zip/node node-loc)) new-value)
      (editz/edit node-loc (fn [_prev] new-value)))))

(defn commit-node-value [editor node-id value]
  {:pre [node-id value]}
  (let [old-root (get-parse-tree editor)
        modified-loc (commit-value-to-loc (zip-utils/make-zipper old-root) node-id value)]
    (if-not modified-loc
      editor
      (let [parse-tree (parser/make-nodes-unique (zip/root modified-loc))
            id-shift (- (:id parse-tree) (:id old-root))]
        (-> editor
          (set-parse-tree parse-tree)
          (shift-selections id-shift))))))

{'fraction_non_alphanumeric': 0.16265479337693056, 'fraction_numerical': 0.0, 'mean_word_length': 3.263345195729537, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2225136', 'n_tokens_mistral': 2560, 'n_tokens_neox': 2504, 'n_words': 609}