mirror of
https://github.com/logseq/logseq.git
synced 2026-05-21 11:22:44 +00:00
feat(cli): add qmd query (3)
This commit is contained in:
@@ -335,6 +335,48 @@
|
||||
comment-text
|
||||
(str content " " comment-text))))
|
||||
|
||||
(defn- content-first-line
|
||||
[content]
|
||||
(-> (or content "")
|
||||
string/split-lines
|
||||
first
|
||||
(or "")
|
||||
string/trim))
|
||||
|
||||
(defn- block-first-line-fragment
|
||||
[block]
|
||||
(content-first-line (:block/title block)))
|
||||
|
||||
(defn- code-fence-block-line?
|
||||
[content]
|
||||
(string/starts-with? (string/trim (or content "")) "```"))
|
||||
|
||||
(defn- normalize-rendered-match-text
|
||||
[content]
|
||||
(-> (or content "")
|
||||
string/lower-case
|
||||
(string/replace ref-or-tag-re "$1[[]]")
|
||||
(string/replace simple-hashtag-re "$1#[[]]")
|
||||
(string/replace #"\s+" " ")
|
||||
string/trim))
|
||||
|
||||
(defn- rendered-line-matches-block?
|
||||
[block-info content]
|
||||
(when block-info
|
||||
(let [content (or content "")
|
||||
fragment (:first-line-fragment block-info)
|
||||
content* (normalize-rendered-match-text content)
|
||||
fragment* (normalize-rendered-match-text fragment)]
|
||||
(cond
|
||||
(:code-block? block-info)
|
||||
(code-fence-block-line? content)
|
||||
|
||||
(string/blank? fragment)
|
||||
(string/blank? (string/trim content))
|
||||
|
||||
:else
|
||||
(string/includes? content* fragment*)))))
|
||||
|
||||
(defn- code-block?
|
||||
[block]
|
||||
(or (= :code (:logseq.property.node/display-type block))
|
||||
@@ -344,6 +386,7 @@
|
||||
(defn- block-line-info
|
||||
[db block marker]
|
||||
{:db/id (:db/id block)
|
||||
:first-line-fragment (block-first-line-fragment block)
|
||||
:code-block? (code-block? block)
|
||||
:status-marker (when (seq (d/datoms db :eavt (:db/id block) :logseq.property/status))
|
||||
(some-> (:logseq.property/status block) status-marker))
|
||||
@@ -433,31 +476,54 @@
|
||||
[block-line-info' & more-block-line-infos] line-infos
|
||||
lines initial-lines
|
||||
seen-block? false
|
||||
property-indent nil]
|
||||
property-indent nil
|
||||
in-code-block? false]
|
||||
(if (nil? line)
|
||||
(string/join "\n" lines)
|
||||
(if (property-value-line? line property-indent)
|
||||
(cond
|
||||
in-code-block?
|
||||
(recur more
|
||||
(cons block-line-info' more-block-line-infos)
|
||||
(conj lines line)
|
||||
seen-block?
|
||||
property-indent)
|
||||
(if (re-matches markdown-block-line-re line)
|
||||
(let [lines' (cond-> lines
|
||||
(and insert-blank-before-first-block?
|
||||
(not seen-block?)) (conj "")
|
||||
true (into (decorate-block-line db block-line-info' line options)))]
|
||||
property-indent
|
||||
(not (code-fence-block-line? line)))
|
||||
|
||||
(property-value-line? line property-indent)
|
||||
(recur more
|
||||
(cons block-line-info' more-block-line-infos)
|
||||
(conj lines line)
|
||||
seen-block?
|
||||
property-indent
|
||||
false)
|
||||
|
||||
:else
|
||||
(if-let [[_ _ title] (re-matches markdown-block-line-re line)]
|
||||
(if (rendered-line-matches-block? block-line-info' title)
|
||||
(let [lines' (cond-> lines
|
||||
(and insert-blank-before-first-block?
|
||||
(not seen-block?)) (conj "")
|
||||
true (into (decorate-block-line db block-line-info' line options)))]
|
||||
(recur more
|
||||
more-block-line-infos
|
||||
lines'
|
||||
true
|
||||
nil
|
||||
(and (:code-block? block-line-info')
|
||||
(code-fence-block-line? title))))
|
||||
(recur more
|
||||
more-block-line-infos
|
||||
lines'
|
||||
true
|
||||
nil))
|
||||
(cons block-line-info' more-block-line-infos)
|
||||
(conj lines line)
|
||||
seen-block?
|
||||
property-indent
|
||||
false))
|
||||
(let [property-indent' (property-line-indent line)]
|
||||
(recur more
|
||||
(cons block-line-info' more-block-line-infos)
|
||||
(conj lines line)
|
||||
seen-block?
|
||||
property-indent'))))))))
|
||||
property-indent'
|
||||
false))))))))
|
||||
|
||||
(defn- add-page-id-to-rendered-content
|
||||
[db page content options]
|
||||
|
||||
@@ -257,6 +257,12 @@
|
||||
(js->clj parsed :keywordize-keys true)
|
||||
(recur (string/index-of output "[" (inc start)))))))))
|
||||
|
||||
(defn- qmd-json-parse-failed
|
||||
[result]
|
||||
(qmd-error :qmd-json-parse-failed
|
||||
"Unable to parse QMD JSON output"
|
||||
result))
|
||||
|
||||
(defn extract-block-ids
|
||||
[results]
|
||||
(->> (or results [])
|
||||
@@ -472,39 +478,43 @@
|
||||
qmd-result (<run-qmd (qsearch-args action))]
|
||||
(if-not (zero? (:exit qmd-result))
|
||||
(qmd-command-failed "qmd query failed" qmd-result)
|
||||
(let [results (vec (or (parse-qmd-json-output (:out qmd-result)) []))
|
||||
result-count (count results)]
|
||||
(p/let [results (<expand-qmd-result-snippets action results)]
|
||||
(let [ids (extract-block-ids results)]
|
||||
(cond
|
||||
(empty? results)
|
||||
(qsearch-ok-data action 0 [] [])
|
||||
(if-let [parsed-results (parse-qmd-json-output (:out qmd-result))]
|
||||
(let [results (vec parsed-results)
|
||||
result-count (count results)]
|
||||
(p/let [results (<expand-qmd-result-snippets action results)]
|
||||
(let [ids (extract-block-ids results)]
|
||||
(cond
|
||||
(empty? results)
|
||||
(qsearch-ok-data action 0 [] [])
|
||||
|
||||
(not (seq ids))
|
||||
{:status :error
|
||||
:error {:code :qmd-no-block-ids
|
||||
:message "QMD results did not include Markdown Mirror block ids"
|
||||
:hint "Run `logseq qmd init [--graph <graph>]` and retry"}}
|
||||
(not (seq ids))
|
||||
{:status :error
|
||||
:error {:code :qmd-no-block-ids
|
||||
:message "QMD results did not include Markdown Mirror block ids"
|
||||
:hint "Run `logseq qmd init [--graph <graph>]` and retry"}}
|
||||
|
||||
:else
|
||||
(let [result-by-id (qmd-result-by-id results)]
|
||||
(p/let [entities (p/all
|
||||
(map (fn [id]
|
||||
(transport/invoke cfg :thread-api/pull
|
||||
[(:repo action) qsearch-pull-selector id]))
|
||||
ids))
|
||||
pairs (mapv vector ids entities)
|
||||
items (->> pairs
|
||||
(keep (fn [[id entity]]
|
||||
(when (qsearch-entity-present? entity)
|
||||
(normalize-qsearch-item entity
|
||||
(get result-by-id id)))))
|
||||
vec)
|
||||
missing-ids (->> pairs
|
||||
(keep (fn [[id entity]]
|
||||
(when-not (qsearch-entity-present? entity) id)))
|
||||
vec)
|
||||
items (<normalize-qsearch-item-refs cfg (:repo action) items)
|
||||
human-data (when (human-output? config)
|
||||
(<qsearch-human-data cfg action entities))]
|
||||
(qsearch-ok-data action result-count items missing-ids human-data))))))))))
|
||||
:else
|
||||
(let [result-by-id (qmd-result-by-id results)]
|
||||
(p/let [entities (p/all
|
||||
(map (fn [id]
|
||||
(transport/invoke cfg :thread-api/pull
|
||||
[(:repo action) qsearch-pull-selector id]))
|
||||
ids))
|
||||
pairs (mapv vector ids entities)
|
||||
items (->> pairs
|
||||
(keep (fn [[id entity]]
|
||||
(when (qsearch-entity-present? entity)
|
||||
(normalize-qsearch-item entity
|
||||
(get result-by-id id)))))
|
||||
vec)
|
||||
missing-ids (->> pairs
|
||||
(keep (fn [[id entity]]
|
||||
(when-not (qsearch-entity-present? entity) id)))
|
||||
vec)
|
||||
items (<normalize-qsearch-item-refs cfg (:repo action) items)
|
||||
human-data (when (human-output? config)
|
||||
(<qsearch-human-data cfg action entities))]
|
||||
(qsearch-ok-data action result-count items missing-ids human-data)))))))
|
||||
(if (string/blank? (:out qmd-result))
|
||||
(qsearch-ok-data action 0 [] [])
|
||||
(qmd-json-parse-failed qmd-result))))))
|
||||
|
||||
@@ -204,6 +204,28 @@
|
||||
(p/catch (fn [e] (is false (str "unexpected error: " e))))
|
||||
(p/finally done)))))
|
||||
|
||||
(deftest multiline-markdown-list-lines-do-not-consume-next-block-db-id-test
|
||||
(async done
|
||||
(let [{:keys [platform files]} (fake-platform)
|
||||
page-uuid #uuid "33333333-3333-4333-8333-333333333340"
|
||||
conn (db-test/create-conn-with-blocks
|
||||
{:pages-and-blocks [{:page {:block/title "Multiline List"
|
||||
:block/uuid page-uuid}
|
||||
:blocks [{:block/title "first line\n- not a child"}
|
||||
{:block/title "after multiline"}]}]})
|
||||
page (db-test/find-page-by-title @conn "Multiline List")
|
||||
first-block (db-test/find-block-by-content @conn "first line\n- not a child")
|
||||
after-multiline (db-test/find-block-by-content @conn "after multiline")]
|
||||
(-> (markdown-mirror/<mirror-page! test-repo @conn (:db/id page) {:platform platform})
|
||||
(p/then (fn [_]
|
||||
(is (= (str (page-marker page-uuid) "\n\n"
|
||||
"- first line " (block-id-comment first-block) "\n"
|
||||
" - not a child\n"
|
||||
"- after multiline " (block-id-comment after-multiline))
|
||||
(get @files (page-path "pages/Multiline List.md"))))))
|
||||
(p/catch (fn [e] (is false (str "unexpected error: " e))))
|
||||
(p/finally done)))))
|
||||
|
||||
(deftest nested-block-db-id-comments-preserve-indent-test
|
||||
(async done
|
||||
(let [{:keys [platform files]} (fake-platform)
|
||||
@@ -250,6 +272,32 @@
|
||||
(p/catch (fn [e] (is false (str "unexpected error: " e))))
|
||||
(p/finally done)))))
|
||||
|
||||
(deftest code-block-markdown-list-lines-do-not-consume-block-db-ids-test
|
||||
(async done
|
||||
(let [{:keys [platform files]} (fake-platform)
|
||||
page-uuid #uuid "33333333-3333-4333-8333-333333333339"
|
||||
conn (db-test/create-conn-with-blocks
|
||||
{:pages-and-blocks [{:page {:block/title "Code List"
|
||||
:block/uuid page-uuid}
|
||||
:blocks [{:block/title "- not an outline block\n(+ 1 2)"
|
||||
:build/tags [:logseq.class/Code-block]
|
||||
:build/properties {:logseq.property.node/display-type :code
|
||||
:logseq.property.code/lang "clojure"}}
|
||||
{:block/title "after code"}]}]})
|
||||
page (db-test/find-page-by-title @conn "Code List")
|
||||
after-code (db-test/find-block-by-content @conn "after code")]
|
||||
(-> (markdown-mirror/<mirror-page! test-repo @conn (:db/id page) {:platform platform})
|
||||
(p/then (fn [_]
|
||||
(is (= (str (page-marker page-uuid) "\n\n"
|
||||
"- ```clojure\n"
|
||||
" - not an outline block\n"
|
||||
" (+ 1 2)\n"
|
||||
" ```\n"
|
||||
"- after code " (block-id-comment after-code))
|
||||
(get @files (page-path "pages/Code List.md"))))))
|
||||
(p/catch (fn [e] (is false (str "unexpected error: " e))))
|
||||
(p/finally done)))))
|
||||
|
||||
(deftest property-value-lines-do-not-consume-block-db-id-comments-test
|
||||
(async done
|
||||
(let [{:keys [platform files]} (fake-platform)
|
||||
|
||||
@@ -752,6 +752,28 @@
|
||||
(p/catch (fn [e] (is false (str "unexpected error: " e))))
|
||||
(p/finally done))))
|
||||
|
||||
(deftest test-execute-qsearch-errors-on-malformed-qmd-json
|
||||
(async done
|
||||
(-> (p/with-redefs [qmd-command/<run-qmd
|
||||
(fn [_]
|
||||
(p/resolved {:exit 0
|
||||
:out "QMD finished without JSON output"
|
||||
:err ""}))
|
||||
cli-server/ensure-server! (fn [config _repo] config)]
|
||||
(qmd-command/execute-qsearch
|
||||
{:type :qsearch
|
||||
:repo "logseq_db_demo"
|
||||
:query "alpha"
|
||||
:collection "custom"}
|
||||
{}))
|
||||
(p/then (fn [result]
|
||||
(is (= :error (:status result)))
|
||||
(is (= :qmd-json-parse-failed (get-in result [:error :code])))
|
||||
(is (string/includes? (or (get-in result [:error :message]) "")
|
||||
"parse QMD JSON"))))
|
||||
(p/catch (fn [e] (is false (str "unexpected error: " e))))
|
||||
(p/finally done))))
|
||||
|
||||
(deftest test-execute-qsearch-errors-when-qmd-results-have-no-block-ids
|
||||
(async done
|
||||
(-> (p/with-redefs [qmd-command/<run-qmd
|
||||
|
||||
Reference in New Issue
Block a user