<commit_before>(ns paredit.core
  (:use [paredit.parser :exclude [pts]])
  (:use clojure.set)
  (:use [clojure.core.incubator :only [-?>]])
  (:require [paredit.string :as str])
  (:require [paredit.text-utils :as t])
  (:require [clojure.zip :as z])
  (:require [clojure.string :as s])
  (:require [paredit.loc-utils :as l :refer :all]))

#_(set! *warn-on-reflection* true)
;;; adaptable paredit configuration
(def ^String ^:dynamic *newline* "\n")
;;; adaptable paredit configuration

(def ^:dynamic *real-spaces* #{(str \newline) (str \tab) (str \space)})
(def ^:dynamic *extended-spaces* (conj *real-spaces* (str \,)))
(def ^:dynamic *open-brackets* (conj #{"(" "[" "{"} nil)) ; we add nil to the list to also match beginning of text 
(def ^:dynamic *close-brackets* (conj #{")" "]" "}"} nil)) ; we add nil to the list to also match end of text
(def ^:dynamic *form-macro-chars* #{(str \#) (str \~) "~@" (str \') (str \`) (str \@) "^" "#'" "#_" "#!"})
(def ^:dynamic *not-in-code* #{:string :string-body "\"\\" :comment :char :regex :regex-body})

(defmacro with-memoized [func-names & body]
  `(binding [~@(mapcat 
                 (fn [func-name] [func-name `(memoize ~func-name)]) 
                 func-names)]
     ~@body))

(declare ^:dynamic normalized-selection)
(defmacro with-important-memoized [& body]
  `(with-memoized 
     [start-offset
      end-offset
      loc-text
      loc-col
      loc-for-offset
      leave-for-offset
      loc-containing-offset
      contains-offset?
      normalized-selection
      node-text]
     ~@body))

(defn ^:dynamic normalized-selection
  "makes a syntaxically correct selection, that is the returned nodes are siblings.
   returns a vector of 2 locs.
   If the selection is empty, the first loc will give the start (get it via a call to 'loc-start on it)
   and the second loc will be nil.
   If the selection is not empty, the second loc will give the end (get it via a call to 'loc-end on it).
   Pre-requisites: length >=0, offset >=0. rloc = root loc of the tree"
  [rloc offset length]
  (let [left-leave (parse-leave (leave-for-offset rloc offset))
        right-leave (parse-leave (leave-for-offset rloc (+ offset length)))
        right-leave (cond 
                      (root-node-tag? (loc-tag right-leave))
                        (parse-leave (leave-for-offset rloc (dec (+ offset length)))) 
                      (not= (+ offset length) (start-offset right-leave))
                        (parse-node right-leave) 
                      (nil? (seq (previous-leaves right-leave)))
                        (parse-node right-leave)
                      :else
                        (parse-node (first (previous-leaves right-leave))))]
    (if (or
          (= [0 0] [offset length])
          (and 
            (= 0 length)
            (= (start-offset left-leave) offset))
          (and 
            (= (start-offset (parse-node left-leave)) offset)
            (= (end-offset (parse-node right-leave)) (+ offset length))  
            (same-parent? (parse-node left-leave) (parse-node right-leave)))) 
      [left-leave (when-not (zero? length) right-leave)]
      (let [left-leave (parse-node left-leave)
            right-leave (parse-node right-leave)
            min-depth (min (loc-depth left-leave) (loc-depth right-leave))
            left-leave (up-to-depth left-leave min-depth)
            right-leave (up-to-depth right-leave min-depth)]
        (first 
          (filter 
            (fn [[l r]] (= (z/up l) (z/up r))) 
            (iterate 
              (fn [[l r]] [(z/up l) (z/up r)])
              [left-leave right-leave])))))))

(defn parsed-in-tags? 
  [parsed tags-set]
  (tags-set (-> parsed :parents peek :tag)))

(defn parse-stopped-in-code?
  ; TODO the current function is not general enough, it just works for the offset
  ; the parse stopped at  
  "true if character at offset offset is in a code
   position, e.g. not in a string, regexp, literal char or comment"
  [parsed]
  (not (parsed-in-tags? parsed *not-in-code*)))

(defn in-code? 
  [loc] 
  (when-let [loc-tag (loc-tag (parse-node loc))]
    (not (*not-in-code* loc-tag))))

(defn in-code-at-offset?
  "Is the offset in the parse tree in a 'code partition' or not?"
  [parse-tree offset]
  (with-important-memoized 
    (let [offset-loc (-> parse-tree parsed-root-loc (loc-for-offset offset))] 
      (in-code? offset-loc))))

(defmulti paredit (fn [k & args] k))

(defn insert-balanced
  [[o c] t chars-with-no-space-before chars-with-no-space-after]
  (let [add-pre-space? (not (contains? chars-with-no-space-before 
                                       (t/previous-char-str t 1 #_(count o))))
        add-post-space? (not (contains? chars-with-no-space-after 
                                        (t/next-char-str t)))
        ins-str (str (if add-pre-space? " " "")
                     (str o c)
                     (if add-post-space? " " ""))
        offset-shift (if add-post-space? -2 -1)]
    (-> t (t/insert ins-str) (t/shift-offset offset-shift))))

(declare wrap-with-balanced)

(defn open-balanced
  [parsed [o c] {:keys [^String text offset length] :as t} 
   chars-with-no-space-before chars-with-no-space-after]
  (if (zero? length) 
    (let [offset-loc (-> parsed parsed-root-loc (leave-for-offset offset))]
      (if (in-code? offset-loc)
        (insert-balanced [o c] t chars-with-no-space-before chars-with-no-space-after)
        (-> t (t/insert (str o)))))
    (wrap-with-balanced parsed [o c] t)))<commit_msg>fix(paredit): if text is empty, consider in-code? returns true<commit_after>(ns paredit.core
  (:use [paredit.parser :exclude [pts]])
  (:use clojure.set)
  (:use [clojure.core.incubator :only [-?>]])
  (:require [paredit.string :as str])
  (:require [paredit.text-utils :as t])
  (:require [clojure.zip :as z])
  (:require [clojure.string :as s])
  (:require [paredit.loc-utils :as l :refer :all]))

#_(set! *warn-on-reflection* true)
;;; adaptable paredit configuration
(def ^String ^:dynamic *newline* "\n")
;;; adaptable paredit configuration

(def ^:dynamic *real-spaces* #{(str \newline) (str \tab) (str \space)})
(def ^:dynamic *extended-spaces* (conj *real-spaces* (str \,)))
(def ^:dynamic *open-brackets* (conj #{"(" "[" "{"} nil)) ; we add nil to the list to also match beginning of text 
(def ^:dynamic *close-brackets* (conj #{")" "]" "}"} nil)) ; we add nil to the list to also match end of text
(def ^:dynamic *form-macro-chars* #{(str \#) (str \~) "~@" (str \') (str \`) (str \@) "^" "#'" "#_" "#!"})
(def ^:dynamic *not-in-code* #{:string :string-body "\"\\" :comment :char :regex :regex-body})

(defmacro with-memoized [func-names & body]
  `(binding [~@(mapcat 
                 (fn [func-name] [func-name `(memoize ~func-name)]) 
                 func-names)]
     ~@body))

(declare ^:dynamic normalized-selection)
(defmacro with-important-memoized [& body]
  `(with-memoized 
     [start-offset
      end-offset
      loc-text
      loc-col
      loc-for-offset
      leave-for-offset
      loc-containing-offset
      contains-offset?
      normalized-selection
      node-text]
     ~@body))

(defn ^:dynamic normalized-selection
  "makes a syntaxically correct selection, that is the returned nodes are siblings.
   returns a vector of 2 locs.
   If the selection is empty, the first loc will give the start (get it via a call to 'loc-start on it)
   and the second loc will be nil.
   If the selection is not empty, the second loc will give the end (get it via a call to 'loc-end on it).
   Pre-requisites: length >=0, offset >=0. rloc = root loc of the tree"
  [rloc offset length]
  (let [left-leave (parse-leave (leave-for-offset rloc offset))
        right-leave (parse-leave (leave-for-offset rloc (+ offset length)))
        right-leave (cond 
                      (root-node-tag? (loc-tag right-leave))
                        (parse-leave (leave-for-offset rloc (dec (+ offset length)))) 
                      (not= (+ offset length) (start-offset right-leave))
                        (parse-node right-leave) 
                      (nil? (seq (previous-leaves right-leave)))
                        (parse-node right-leave)
                      :else
                        (parse-node (first (previous-leaves right-leave))))]
    (if (or
          (= [0 0] [offset length])
          (and 
            (= 0 length)
            (= (start-offset left-leave) offset))
          (and 
            (= (start-offset (parse-node left-leave)) offset)
            (= (end-offset (parse-node right-leave)) (+ offset length))  
            (same-parent? (parse-node left-leave) (parse-node right-leave)))) 
      [left-leave (when-not (zero? length) right-leave)]
      (let [left-leave (parse-node left-leave)
            right-leave (parse-node right-leave)
            min-depth (min (loc-depth left-leave) (loc-depth right-leave))
            left-leave (up-to-depth left-leave min-depth)
            right-leave (up-to-depth right-leave min-depth)]
        (first 
          (filter 
            (fn [[l r]] (= (z/up l) (z/up r))) 
            (iterate 
              (fn [[l r]] [(z/up l) (z/up r)])
              [left-leave right-leave])))))))

(defn parsed-in-tags? 
  [parsed tags-set]
  (tags-set (-> parsed :parents peek :tag)))

(defn parse-stopped-in-code?
  ; TODO the current function is not general enough, it just works for the offset
  ; the parse stopped at  
  "true if character at offset offset is in a code
   position, e.g. not in a string, regexp, literal char or comment"
  [parsed]
  (not (parsed-in-tags? parsed *not-in-code*)))

(defn in-code? 
  [loc] 
  (when-let [loc-tag (loc-tag (parse-node loc))]
    (not (*not-in-code* loc-tag))))

(defn in-code-at-offset?
  "Is the offset in the parse tree in a 'code partition' or not?"
  [parse-tree offset]
  (with-important-memoized 
    (if-let [offset-loc (-> parse-tree parsed-root-loc (loc-for-offset offset))]
      (in-code? offset-loc)
      true)))

(defmulti paredit (fn [k & args] k))

(defn insert-balanced
  [[o c] t chars-with-no-space-before chars-with-no-space-after]
  (let [add-pre-space? (not (contains? chars-with-no-space-before 
                                       (t/previous-char-str t 1 #_(count o))))
        add-post-space? (not (contains? chars-with-no-space-after 
                                        (t/next-char-str t)))
        ins-str (str (if add-pre-space? " " "")
                     (str o c)
                     (if add-post-space? " " ""))
        offset-shift (if add-post-space? -2 -1)]
    (-> t (t/insert ins-str) (t/shift-offset offset-shift))))

(declare wrap-with-balanced)

(defn open-balanced
  [parsed [o c] {:keys [^String text offset length] :as t} 
   chars-with-no-space-before chars-with-no-space-after]
  (if (zero? length) 
    (let [offset-loc (-> parsed parsed-root-loc (leave-for-offset offset))]
      (if (in-code? offset-loc)
        (insert-balanced [o c] t chars-with-no-space-before chars-with-no-space-after)
        (-> t (t/insert (str o)))))
    (wrap-with-balanced parsed [o c] t)))
{'fraction_non_alphanumeric': 0.15941112716763006, 'fraction_numerical': 0.0016257225433526012, 'mean_word_length': 2.3063601074947746, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1762915', 'n_tokens_mistral': 3760, 'n_tokens_neox': 3609, 'n_words': 1058}