<commit_before>(ns selmer.tags
  (:require selmer.node
            [selmer.filter-parser :refer [split-filter-val safe-filter compile-filter-body fix-accessor get-accessor]]
            [selmer.filters :refer [filters]]
            [selmer.util :refer :all]
            [json-html.core :refer [edn->html]])
  (:import [selmer.node INode TextNode FunctionNode]))

;; A tag can modify the context map for its body
;; It has full control of its body which means that it has to
;; take care of its compilation.
(defn parse-arg [^String arg]
  (fix-accessor (.split arg "\\.")))

(defn create-value-mappings [context-map ids value]
  (if (= 1 (count ids))
    (assoc-in context-map (first ids) value)
    (reduce
      (fn [m [path value]] (assoc-in m path value))
      context-map (map vector ids value))))

(defn aggregate-args [args]
  (->> args
       (map #(.split ^String % ","))
       (apply concat)
       (split-with (partial not= "in"))))

(defn compile-filters [items filter-names]
  (map #(compile-filter-body (str items "|" %) false) filter-names))

(defn apply-filters [item filters context-map items]
  (reduce
    (fn [value filter] (filter (assoc context-map items value)))
    item filters))

(defn for-handler [args tag-content render rdr]
  (let [content       (tag-content rdr :for :empty :endfor)
        for-content   (get-in content [:for :content])
        empty-content (get-in content [:empty :content])
        [ids [_ items]] (aggregate-args args)
        ids           (map parse-arg ids)
        [items & filter-names] (if items (.split ^String items "\\|"))
        filters       (compile-filters items filter-names)
        item-keys     (parse-arg items)]
    (fn [context-map]
      (let [buf    (StringBuilder.)
            items  (-> (reduce get-accessor context-map item-keys)
                       (apply-filters filters context-map items))
            length (count items)]
        (if (and empty-content (empty? items))
          (.append buf (render empty-content context-map))
          (doseq [[counter value] (map-indexed vector items)]
            (let [loop-info
                  {:length      length
                   :counter0    counter
                   :counter     (inc counter)
                   :revcounter  (- length (inc counter))
                   :revcounter0 (- length counter)
                   :first       (= counter 0)<commit_msg>Allow for multiple keyword filters in for tag (#155)<commit_after>(ns selmer.tags
  (:require selmer.node
            [selmer.filter-parser :refer [split-filter-val safe-filter compile-filter-body fix-accessor get-accessor]]
            [selmer.filters :refer [filters]]
            [selmer.util :refer :all]
            [json-html.core :refer [edn->html]])
  (:import [selmer.node INode TextNode FunctionNode]))

;; A tag can modify the context map for its body
;; It has full control of its body which means that it has to
;; take care of its compilation.
(defn parse-arg [^String arg]
  (fix-accessor (.split arg "\\.")))

(defn create-value-mappings [context-map ids value]
  (if (= 1 (count ids))
    (assoc-in context-map (first ids) value)
    (reduce
      (fn [m [path value]] (assoc-in m path value))
      context-map (map vector ids value))))

(defn aggregate-args [args]
  (->> args
       (map #(.split ^String % ","))
       (apply concat)
       (split-with (partial not= "in"))))

(defn compile-filters [items filter-names]
  (map #(compile-filter-body (str items "|" %) false) filter-names))

(defn apply-filters [item filters context-map items]
  (reduce
    (fn [value filter]
      (filter (assoc context-map
                     (keyword items) value
                     (name items) value)))
    item filters))

(defn for-handler [args tag-content render rdr]
  (let [content       (tag-content rdr :for :empty :endfor)
        for-content   (get-in content [:for :content])
        empty-content (get-in content [:empty :content])
        [ids [_ items]] (aggregate-args args)
        ids           (map parse-arg ids)
        [items & filter-names] (if items (.split ^String items "\\|"))
        filters       (compile-filters items filter-names)
        item-keys     (parse-arg items)]
    (fn [context-map]
      (let [buf    (StringBuilder.)
            items  (-> (reduce get-accessor context-map item-keys)
                       (apply-filters filters context-map items))
            length (count items)]
        (if (and empty-content (empty? items))
          (.append buf (render empty-content context-map))
          (doseq [[counter value] (map-indexed vector items)]
            (let [loop-info
                  {:length      length
                   :counter0    counter
                   :counter     (inc counter)
                   :revcounter  (- length (inc counter))
                   :revcounter0 (- length counter)
                   :first       (= counter 0)
{'fraction_non_alphanumeric': 0.12723941368078176, 'fraction_numerical': 0.002239413680781759, 'mean_word_length': 2.135290363752393, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 11, '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': '1027961', 'n_tokens_mistral': 1452, 'n_tokens_neox': 1408, 'n_words': 461}