<commit_before>(ns plastic.cogs.editor.ops.selection
  (:require-macros [plastic.macros.logging :refer [log info warn error group group-end]]
                   [plastic.macros.glue :refer [react! dispatch]]
                   [plastic.macros.common :refer [*->]])
  (:require [plastic.cogs.editor.layout.utils :refer [apply-to-selected-editors]]
            [plastic.cogs.editor.model :as editor]
            [plastic.util.helpers :as helpers]))

(defn mid-point [node]
  (let [{:keys [left width] :or {left 0 width 0}} (:geometry node)]
    (+ left (/ width 2))))

(defn find-best-spatial-match [node candidates]
  (let [node-point (mid-point node)
        score (fn [candidate] (helpers/abs (- node-point (mid-point candidate))))
        scores (map (fn [candidate] {:score (score candidate) :candidate candidate}) candidates)
        best-match (first (sort-by :score scores))]
    (:candidate best-match)))

(defn get-selected-node-id [editor]
  (first (editor/get-selection editor)))

(defn structural-movemement [editor op]
  (let [selected-id (get-selected-node-id editor)
        render-info (editor/get-focused-render-info editor)
        structural-web (:structural-web render-info)]
    (if-let [result-id (op (get structural-web selected-id))]
      (editor/set-selection editor #{result-id}))))

(defn find-first-non-empty-line [spatial-web line op]
  (first (drop-while #(and (not (nil? %)) (empty? %)) (map spatial-web (iterate op (op line))))))

; ----------------------------------------------------------------------------------------------------------------

(defmulti op (fn [op-type & _] op-type))

(defmethod op :spatial-down [_ editor]
  (let [selected-id (get-selected-node-id editor)
        render-info (editor/get-focused-render-info editor)
        {:keys [spatial-web all-selections]} render-info
        sel-node (get all-selections selected-id)
        line-selectables (find-first-non-empty-line spatial-web (:line sel-node) inc)
        line-selections (map #(get all-selections (:id %)) line-selectables)
        result (find-best-spatial-match sel-node line-selections)]
    (if result
      (editor/set-selection editor #{(:id result)}))))

(defmethod op :spatial-up [_ editor]
  (let [selected-id (get-selected-node-id editor)
        render-info (editor/get-focused-render-info editor)
        {:keys [spatial-web all-selections]} render-info
        sel-node (get all-selections selected-id)
        line-selectables (find-first-non-empty-line spatial-web (:line sel-node) dec)
        line-selections (map #(get all-selections (:id %)) line-selectables)
        result (find-best-spatial-match sel-node line-selections)]
    (if result
      (editor/set-selection editor #{(:id result)}))))

(defmethod op :spatial-right [_ editor]
  (let [selected-id (get-selected-node-id editor)
        render-info (editor/get-focused-render-info editor)
        {:keys [spatial-web all-selections]} render-info
        sel-node (get all-selections selected-id)
        sel-line (:line sel-node)
        line-selectables (get spatial-web sel-line)
        result (helpers/next-item #(= (:id %) selected-id) line-selectables)]
    (if result
      (editor/set-selection editor #{(:id result)}))))

(defmethod op :spatial-left [_ editor]
  (let [selected-id (get-selected-node-id editor)
        render-info (editor/get-focused-render-info editor)
        {:keys [spatial-web all-selections]} render-info
        sel-node (get all-selections selected-id)
        sel-line (:line sel-node)
        line-selectables (get spatial-web sel-line)
        result (helpers/prev-item #(= (:id %) selected-id) line-selectables)]
    (if result
      (editor/set-selection editor #{(:id result)}))))

(defmethod op :structural-up [_ editor]
  (structural-movemement editor :up))

(defmethod op :structural-down [_ editor]
  (structural-movemement editor :down))

(defmethod op :structural-left [_ editor]
  (structural-movemement editor :left))

(defmethod op :structural-right [_ editor]
  (structural-movemement editor :right))

(defmethod op :move-prev-form [_ editor]
  (let [focused-form-id (editor/get-focused-form-id editor)
        top-level-ids (editor/get-top-level-form-ids editor)
        next-focused-form-id (helpers/prev-item #(= % focused-form-id) top-level-ids)]
    (if next-focused-form-id
      (let [next-selection (editor/get-last-selectable-token-id-for-form editor next-focused-form-id)]
        (-> editor
          (editor/set-focused-form-id next-focused-form-id)
          (editor/set-selection #{next-selection}))))))

(defmethod op :move-next-form [_ editor]
  (let [focused-form-id (editor/get-focused-form-id editor)
        top-level-ids (editor/get-top-level-form-ids editor)
        next-focused-form-id (helpers/next-item #(= % focused-form-id) top-level-ids)]
    (if next-focused-form-id
      (let [next-selection (editor/get-first-selectable-token-id-for-form editor next-focused-form-id)]
        (-> editor
          (editor/set-focused-form-id next-focused-form-id)
          (editor/set-selection #{next-selection}))))))

; ----------------------------------------------------------------------------------------------------------------

(defmethod op :default [command]
  (error (str "Unknown selection operation '" command "'"))<commit_msg>refactoring of selection code<commit_after>(ns plastic.cogs.editor.ops.selection
  (:require-macros [plastic.macros.logging :refer [log info warn error group group-end]]
                   [plastic.macros.glue :refer [react! dispatch]]
                   [plastic.macros.common :refer [*->]])
  (:require [plastic.cogs.editor.layout.utils :refer [apply-to-selected-editors]]
            [plastic.cogs.editor.model :as editor]
            [plastic.util.helpers :as helpers]))

(defn mid-point [node]
  (let [{:keys [left width] :or {left 0 width 0}} (:geometry node)]
    (+ left (/ width 2))))

(defn find-best-spatial-match [node candidates]
  (let [node-point (mid-point node)
        score (fn [candidate] (helpers/abs (- node-point (mid-point candidate))))
        scores (map (fn [candidate] {:score (score candidate) :candidate candidate}) candidates)
        best-match (first (sort-by :score scores))]
    (:candidate best-match)))

(defn get-selected-node-id [editor]
  (first (editor/get-selection editor)))

(defn structural-movemement [editor op]
  (let [selected-id (get-selected-node-id editor)
        render-info (editor/get-focused-render-info editor)
        structural-web (:structural-web render-info)]
    (if-let [result-id (op (get structural-web selected-id))]
      (editor/set-selection editor #{result-id}))))

(defn find-first-non-empty-line-in-given-direction [spatial-web line op]
  (first (drop-while #(and (not (nil? %)) (empty? %)) (map spatial-web (iterate op (op line))))))

(defn spatial-movement-up-down [editor dir-fun]
  (let [selected-id (get-selected-node-id editor)
        render-info (editor/get-focused-render-info editor)
        {:keys [spatial-web all-selections]} render-info
        sel-node (get all-selections selected-id)
        line-selectables (find-first-non-empty-line-in-given-direction spatial-web (:line sel-node) dir-fun)
        line-selections (map #(get all-selections (:id %)) line-selectables)]
    (if-let [result (find-best-spatial-match sel-node line-selections)]
      (editor/set-selection editor #{(:id result)}))))

(defn spatial-movement-left-right [editor dir-fun]
  (let [selected-id (get-selected-node-id editor)
        render-info (editor/get-focused-render-info editor)
        {:keys [spatial-web all-selections]} render-info
        sel-node (get all-selections selected-id)
        line-selectables (get spatial-web (:line sel-node))]
    (if-let [result (dir-fun #(= (:id %) selected-id) line-selectables)]
      (editor/set-selection editor #{(:id result)}))))

(defn move-to-form [editor dir-fun next-sel-fun]
  (let [focused-form-id (editor/get-focused-form-id editor)
        top-level-ids (editor/get-top-level-form-ids editor)
        next-focused-form-id (dir-fun #(= % focused-form-id) top-level-ids)]
    (if next-focused-form-id
      (let [next-selection (next-sel-fun editor next-focused-form-id)]
        (-> editor
          (editor/set-focused-form-id next-focused-form-id)
          (editor/set-selection #{next-selection}))))))

; ----------------------------------------------------------------------------------------------------------------

(defmulti op (fn [op-type & _] op-type))

(defmethod op :spatial-down [_ editor]
  (spatial-movement-up-down editor inc))

(defmethod op :spatial-up [_ editor]
  (spatial-movement-up-down editor dec))

(defmethod op :spatial-right [_ editor]
  (spatial-movement-left-right editor helpers/next-item))

(defmethod op :spatial-left [_ editor]
  (spatial-movement-left-right editor helpers/prev-item))

(defmethod op :structural-up [_ editor]
  (structural-movemement editor :up))

(defmethod op :structural-down [_ editor]
  (structural-movemement editor :down))

(defmethod op :structural-left [_ editor]
  (structural-movemement editor :left))

(defmethod op :structural-right [_ editor]
  (structural-movemement editor :right))

(defmethod op :move-prev-form [_ editor]
  (move-to-form editor helpers/prev-item editor/get-last-selectable-token-id-for-form))

(defmethod op :move-next-form [_ editor]
  (move-to-form editor helpers/next-item editor/get-first-selectable-token-id-for-form))

; ----------------------------------------------------------------------------------------------------------------

(defmethod op :default [command]
  (error (str "Unknown selection operation '" command "'"))
{'fraction_non_alphanumeric': 0.19344806137258969, 'fraction_numerical': 0.0006220194899440182, 'mean_word_length': 2.72327286761868, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 8, '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': '3013534', 'n_tokens_mistral': 3100, 'n_tokens_neox': 2974, 'n_words': 689}