Files
logseq/src/main/frontend/format/block.cljs
2024-10-03 18:35:31 +08:00

147 lines
6.1 KiB
Clojure

(ns frontend.format.block
"Block code needed by app but not graph-parser"
(:require [cljs-time.format :as tf]
[clojure.string :as string]
[frontend.config :as config]
[frontend.date :as date]
[frontend.db :as db]
[frontend.format :as format]
[frontend.handler.notification :as notification]
[frontend.state :as state]
[logseq.graph-parser.block :as gp-block]
[logseq.graph-parser.property :as gp-property]
[lambdaisland.glogi :as log]
[frontend.format.mldoc :as mldoc]))
(defn extract-blocks
"Wrapper around logseq.graph-parser.block/extract-blocks that adds in system state
and handles unexpected failure."
[blocks content format {:keys [page-name parse-block]}]
(let [repo (state/get-current-repo)]
(try
(let [blocks (gp-block/extract-blocks blocks content format
{:user-config (state/get-config)
:parse-block parse-block
:block-pattern (config/get-block-pattern format)
:db (db/get-db repo)
:date-formatter (state/get-date-formatter)
:page-name page-name
:db-graph-mode? (config/db-based-graph? repo)})]
(if (config/db-based-graph? repo)
(map (fn [block]
(cond-> (dissoc block :block/properties :block/macros :block/properties-order)
(:block/properties block)
(merge (update-keys (:block/properties block)
(fn [k]
(or ({:heading :logseq.property/heading} k)
(throw (ex-info (str "Don't know how to save graph-parser property " (pr-str k)) {}))))))))
blocks)
blocks))
(catch :default e
(log/error :exception e)
(state/pub-event! [:capture-error {:error e
:payload {:type "Extract-blocks"}}])
(notification/show! "An unexpected error occurred during block extraction." :error)
[]))))
(defn- normalize-as-percentage
[block]
(some->> block
str
(re-matches #"(-?\d+\.?\d*)%")
second
(#(/ % 100))))
(defn- normalize-as-date
[block]
(some->> block
str
date/normalize-date
(tf/unparse date/custom-formatter)))
(defn normalize-block
"Normalizes supported formats such as dates and percentages.
Be careful, this function may harm query sort performance!
- nlp-date? - Enable NLP parsing on date items.
Requires heavy computation (see `normalize-as-date` for details)"
[block nlp-date?]
(->> [normalize-as-percentage (when nlp-date? normalize-as-date) identity]
(remove nil?)
(map #(% (if (set? block) (first block) block)))
(remove nil?)
(first)))
(defn parse-block
[{:block/keys [uuid title format] :as block}]
(when-not (string/blank? title)
(let [block (dissoc block :block/pre-block?)
format (or format :markdown)
parse-config (mldoc/get-default-config format)
ast (format/to-edn title format parse-config)
;; Disable extraction for display-type blocks as there isn't a reason to have
;; it enabled yet and can cause visible bugs when '#' is used
blocks (if (and (config/db-based-graph? (state/get-current-repo))
(:logseq.property.node/display-type block))
[block]
(extract-blocks ast title format {:parse-block block}))
new-block (first blocks)
block (cond->
(merge block new-block)
(> (count blocks) 1)
(assoc :block/warning :multiple-blocks))
block (dissoc block :block.temp/ast-body :block/level)]
(if uuid (assoc block :block/uuid uuid) block))))
(defn parse-title-and-body
([block]
(when (map? block)
(merge block
(parse-title-and-body (:block/uuid block)
(:block/format block)
(:block/pre-block? block)
(:block/title block)))))
([block-uuid format pre-block? content]
(when-not (string/blank? content)
(let [content (if pre-block? content
(str (config/get-block-pattern format) " " (string/triml content)))]
(if-let [result (state/get-block-ast block-uuid content)]
result
(let [parse-config (mldoc/get-default-config format)
ast (->> (format/to-edn content format parse-config)
(map first))
title (when (gp-block/heading-block? (first ast))
(:title (second (first ast))))
body (vec (if title (rest ast) ast))
body (drop-while gp-property/properties-ast? body)
result (cond->
(if (seq body) {:block.temp/ast-body body} {})
title
(assoc :block.temp/ast-title title))]
(state/add-block-ast-cache! block-uuid content result)
result))))))
(defn break-line-paragraph?
[[typ break-lines]]
(and (= typ "Paragraph")
(every? #(= % ["Break_Line"]) break-lines)))
(defn trim-paragraph-special-break-lines
[ast]
(let [[typ paras] ast]
(if (= typ "Paragraph")
(let [indexed-paras (map-indexed vector paras)]
[typ (->> (filter
#(let [[index value] %]
(not (and (> index 0)
(= value ["Break_Line"])
(contains? #{"Timestamp" "Macro"}
(first (nth paras (dec index)))))))
indexed-paras)
(map #(last %)))])
ast)))
(defn trim-break-lines!
[ast]
(drop-while break-line-paragraph?
(map trim-paragraph-special-break-lines ast)))