This commit is contained in:
Tienson Qin
2020-05-07 11:42:40 +08:00
parent 8366359c52
commit 85cc9e03d6
12 changed files with 180 additions and 78 deletions

View File

@@ -0,0 +1,189 @@
(ns frontend.format.block
(:require [frontend.util :as util]
[clojure.walk :as walk]
[clojure.string :as string]
[frontend.format :as format]
[frontend.utf8 :as utf8]
[medley.core :as medley]
[datascript.core :as d]
[clojure.set :as set]))
(defn heading-block?
[block]
(and
(vector? block)
(= "Heading" (first block))))
(defn page-reference-block?
[block]
(and
(vector? block)
(= "Link" (first block))
(= "Search" (first (:url (second block))))
(not (contains? #{\# \*} (first (second (:url (second block))))))
))
(defn task-block?
[block]
(and
(heading-block? block)
(some? (:marker (second block)))))
;; FIXME:
(defn extract-title
[block]
(-> (:title (second block))
first
second))
(defn- paragraph-block?
[block]
(and
(vector? block)
(= "Paragraph" (first block))))
(defn- timestamp-block?
[block]
(and
(vector? block)
(= "Timestamp" (first block))))
(defn- paragraph-timestamp-block?
[block]
(and (paragraph-block? block)
(timestamp-block? (first (second block)))))
(defn extract-timestamp
[block]
(-> block
second
first
second))
(defn ->tags
[tags]
(mapv (fn [tag]
{:db/id tag
:tag/name tag})
tags))
(defn with-refs
[{:keys [title children] :as heading}]
(let [ref-pages (atom [])]
(walk/postwalk
(fn [form]
(when (page-reference-block? form)
(let [page (second (:url (second form)))]
(swap! ref-pages conj (string/capitalize page))))
form)
(concat title children))
(assoc heading :ref-pages (vec @ref-pages))))
(defn safe-headings
[headings]
(mapv (fn [heading]
(let [heading (util/remove-nils heading)
heading (if (:heading/uuid heading)
heading
(assoc heading :heading/uuid (d/squuid)))]
(medley/map-keys
(fn [k] (keyword "heading" k))
heading)))
headings))
;; TODO create a dummy heading if no headings exists
(defn extract-headings
[blocks last-pos]
(loop [headings []
heading-children []
blocks (reverse blocks)
timestamps {}
last-pos last-pos]
(if (seq blocks)
(let [block (first blocks)
level (:level (second block))]
(cond
(paragraph-timestamp-block? block)
(let [timestamp (extract-timestamp block)
timestamps' (conj timestamps timestamp)]
(recur headings heading-children (rest blocks) timestamps' last-pos))
(heading-block? block)
(let [heading (-> (assoc (second block)
:children (reverse heading-children)
:timestamps timestamps)
(assoc-in [:meta :end-pos] last-pos)
(update :tags ->tags))
heading (with-refs heading)
last-pos' (get-in heading [:meta :pos])]
(recur (conj headings heading) [] (rest blocks) {} last-pos'))
:else
(let [heading-children' (conj heading-children block)]
(recur headings heading-children' (rest blocks) timestamps last-pos))))
(reverse headings))))
;; marker: DOING | IN-PROGRESS > TODO > WAITING | WAIT > DONE > CANCELED | CANCELLED
;; priority: A > B > C
(defn sort-tasks
[headings]
(let [markers ["DOING" "IN-PROGRESS" "TODO" "WAITING" "WAIT" "DONE" "CANCELED" "CANCELLED"]
markers (zipmap markers (reverse (range 1 (count markers))))
priorities ["A" "B" "C" "D" "E" "F" "G"]
priorities (zipmap priorities (reverse (range 1 (count priorities))))]
(sort (fn [t1 t2]
(let [m1 (get markers (:heading/marker t1) 0)
m2 (get markers (:heading/marker t2) 0)
p1 (get priorities (:heading/priority t1) 0)
p2 (get priorities (:heading/priority t2) 0)]
(cond
(and (= m1 m2)
(= p1 p2))
(compare (str (:heading/title t1))
(str (:heading/title t2)))
(= m1 m2)
(> p1 p2)
:else
(> m1 m2))))
headings)))
(defn parse-heading
[{:heading/keys [uuid content meta file page] :as heading} format]
(let [ast (format/to-edn content format nil)
start-pos (:pos meta)
encoded-content (utf8/encode content)
content-length (utf8/length encoded-content)
headings (extract-headings ast content-length)
headings (safe-headings headings)
ref-pages-atom (atom [])
headings (doall
(map-indexed
(fn [idx {:heading/keys [ref-pages meta] :as heading}]
(let [heading (merge
heading
{:heading/file file
:heading/page page
:heading/content (utf8/substring encoded-content
(:pos meta)
(:end-pos meta))}
(when (zero? idx)
{:heading/uuid uuid})
(when (seq ref-pages)
{:heading/ref-pages
(mapv
(fn [page]
(let [page-name (string/capitalize page)
page {:page/name page-name}]
(swap! ref-pages-atom conj page)
page))
ref-pages)}))]
(-> heading
(assoc-in [:heading/meta :pos] (+ (:pos meta) start-pos))
(assoc-in [:heading/meta :end-pos] (+ (:end-pos meta) start-pos)))))
headings))
pages (vec (distinct @ref-pages-atom))]
{:headings headings
:pages pages
:start-pos start-pos
:end-pos (+ start-pos content-length)}))