Bläddra i källkod

Merge pull request #5265 from logseq/enhance/graph-parser-part-two

Enhance: Migrate mldoc and text to graph-parser
Tienson Qin 3 år sedan
förälder
incheckning
0e5d1614a0
46 ändrade filer med 618 tillägg och 482 borttagningar
  1. 3 1
      .carve/config.edn
  2. 4 2
      .carve/ignore
  3. 3 0
      .clj-kondo/config.edn
  4. 3 1
      .github/workflows/build.yml
  5. 3 3
      docs/dev-practices.md
  6. 1 1
      package.json
  7. 9 8
      src/main/frontend/components/block.cljs
  8. 1 1
      src/main/frontend/components/hierarchy.cljs
  9. 1 1
      src/main/frontend/components/journal.cljs
  10. 3 3
      src/main/frontend/components/page.cljs
  11. 1 1
      src/main/frontend/components/repo.cljs
  12. 1 1
      src/main/frontend/components/select.cljs
  13. 1 1
      src/main/frontend/db/conn.cljs
  14. 4 3
      src/main/frontend/db/query_dsl.cljs
  15. 1 1
      src/main/frontend/db/query_react.cljs
  16. 1 1
      src/main/frontend/diff.cljs
  17. 1 1
      src/main/frontend/encrypt.cljs
  18. 1 1
      src/main/frontend/extensions/code.cljs
  19. 1 1
      src/main/frontend/external/roam.cljs
  20. 8 5
      src/main/frontend/format.cljs
  21. 9 9
      src/main/frontend/format/block.cljs
  22. 13 209
      src/main/frontend/format/mldoc.cljs
  23. 11 10
      src/main/frontend/handler/editor.cljs
  24. 3 2
      src/main/frontend/handler/export.cljs
  25. 4 3
      src/main/frontend/handler/extract.cljs
  26. 2 2
      src/main/frontend/handler/plugin.cljs
  27. 3 2
      src/main/frontend/handler/route.cljs
  28. 4 3
      src/main/frontend/handler/search.cljs
  29. 3 3
      src/main/frontend/mobile/intent.cljs
  30. 0 18
      src/main/frontend/util.cljc
  31. 3 2
      src/main/frontend/util/drawer.cljs
  32. 4 3
      src/main/frontend/util/property.cljs
  33. 1 1
      src/main/frontend/util/thingatpt.cljs
  34. 6 0
      src/main/logseq/graph_parser/log.cljs
  35. 215 0
      src/main/logseq/graph_parser/mldoc.cljc
  36. 29 37
      src/main/logseq/graph_parser/text.cljs
  37. 1 1
      src/main/logseq/graph_parser/utf8.cljs
  38. 32 0
      src/main/logseq/graph_parser/util.cljs
  39. 0 39
      src/test/frontend/format/mldoc_test.cljs
  40. 3 33
      src/test/frontend/handler/repo_test.cljs
  41. 0 28
      src/test/frontend/parser.cljs
  42. 34 0
      src/test/frontend/test/docs_graph_helper.cljs
  43. 130 0
      src/test/logseq/graph_parser/mldoc_test.cljs
  44. 21 0
      src/test/logseq/graph_parser/nbb_test_runner.cljs
  45. 27 36
      src/test/logseq/graph_parser/text_test.cljs
  46. 9 4
      yarn.lock

+ 3 - 1
.carve/config.edn

@@ -5,5 +5,7 @@
                   ;; Ignore b/c too many false positives
                   frontend.db
                   ;; Used for debugging
-                  frontend.db.debug]
+                  frontend.db.debug
+                  ;; carve doesn't detect nbb only usage
+                  logseq.graph-parser.log]
  :report {:format :ignore}}

+ 4 - 2
.carve/ignore

@@ -29,7 +29,7 @@ frontend.extensions.zotero.api/item
 ;; For repl
 frontend.external.roam/reset-state!
 ;; For repl
-frontend.format.mldoc/ast-export-markdown
+logseq.graph-parser.mldoc/ast-export-markdown
 ;; Protocol fn wrapper that could be used
 frontend.fs/readdir
 ;; Referenced in TODO
@@ -72,5 +72,7 @@ frontend.util/trace!
 frontend.util.pool/terminate-pool!
 ;; Repl fn
 frontend.util.property/add-page-properties
-;; Used by shadow
+;; Test runner used by shadow
 frontend.test.node-test-runner/main
+;; Test runner for nbb
+logseq.graph-parser.nbb-test-runner/run-tests

+ 3 - 0
.clj-kondo/config.edn

@@ -20,6 +20,9 @@
              frontend.db.query-react query-react
              frontend.util util
              frontend.config config
+             frontend.format.mldoc mldoc
+             logseq.graph-parser.text text
+             logseq.graph-parser.mldoc gp-mldoc
              logseq.graph-parser.util gp-util
              logseq.graph-parser.config gp-config}}}
 

+ 3 - 1
.github/workflows/build.yml

@@ -74,11 +74,13 @@ jobs:
       - name: Fetch yarn deps
         run: yarn install --frozen-lockfile
 
-      - name: Run ClojureScript test
+      - name: Run ClojureScript tests
         run: |
           yarn cljs:test
           node static/tests.js
 
+      - name: Run nbb tests for graph-parser
+        run: yarn nbb-logseq -cp src/main:src/test -m logseq.graph-parser.nbb-test-runner/run-tests
       # In this job because it depends on an npm package
       - name: Load nbb compatible namespaces
         run: bb test:load-nbb-compatible-namespaces

+ 3 - 3
docs/dev-practices.md

@@ -101,9 +101,9 @@ For this workflow:
   1. Add `^:focus` metadata flags to tests e.g. `(deftest ^:focus test-name ...)`.
   2. In another shell, run `node static/tests.js -i focus` to only run those
   tests. To run all tests except those tests run `node static/tests.js -e focus`.
-3. Or focus namespaces: Using the regex option `-r`, run tests for `frontend.text-test` with `node static/tests.js -r text`.
+3. Or focus namespaces: Using the regex option `-r`, run tests for `frontend.util.page-property-test` with `node static/tests.js -r page-property`.
 
-Multiple options can be specified to AND selections. For example, to run all `frontend.text-test` tests except for the focused one: `node static/tests.js -r text -e focus`
+Multiple options can be specified to AND selections. For example, to run all `frontend.util.page-property-test` tests except for the focused one: `node static/tests.js -r page-property -e focus`
 
 For help on more options, run `node static/tests.js -h`.
 
@@ -114,7 +114,7 @@ shadow-cljs watch test --config-merge '{:autorun true}'`. The test output may
 appear where shadow-cljs was first invoked e.g. where `yarn watch` is running.
 Specific namespace(s) can be auto run with the `:ns-regexp` option e.g. `npx
 shadow-cljs watch test --config-merge '{:autorun true :ns-regexp
-"frontend.text-test"}'`.
+"frontend.util.page-property-test"}'`.
 
 ## Logging
 

+ 1 - 1
package.json

@@ -5,7 +5,7 @@
     "main": "static/electron.js",
     "devDependencies": {
         "@capacitor/cli": "3.2.2",
-        "@logseq/nbb-logseq": "^0.3.10",
+        "@logseq/nbb-logseq": "^0.3.99",
         "@playwright/test": "^1.19.2",
         "@tailwindcss/ui": "0.7.2",
         "@types/gulp": "^4.0.7",

+ 9 - 8
src/main/frontend/components/block.cljs

@@ -46,7 +46,7 @@
             [frontend.security :as security]
             [frontend.state :as state]
             [frontend.template :as template]
-            [frontend.text :as text]
+            [logseq.graph-parser.text :as text]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util.clock :as clock]
@@ -54,6 +54,7 @@
             [frontend.util.drawer :as drawer]
             [logseq.graph-parser.config :as gp-config]
             [logseq.graph-parser.util :as gp-util]
+            [logseq.graph-parser.mldoc :as gp-mldoc]
             [goog.dom :as gdom]
             [goog.object :as gobj]
             [lambdaisland.glogi :as log]
@@ -746,13 +747,13 @@
    (inline-text {} format v))
   ([config format v]
    (when (string? v)
-     (let [inline-list (mldoc/inline->edn v (mldoc/default-config format))]
+     (let [inline-list (gp-mldoc/inline->edn v (gp-mldoc/default-config format))]
        [:div.inline.mr-1 (map-inline config inline-list)]))))
 
 (defn- render-macro
   [config name arguments macro-content format]
   (if macro-content
-    (let [ast (->> (mldoc/->edn macro-content (mldoc/default-config format))
+    (let [ast (->> (mldoc/->edn macro-content (gp-mldoc/default-config format))
                    (map first))
           paragraph? (and (= 1 (count ast))
                           (= "Paragraph" (ffirst ast)))]
@@ -886,7 +887,7 @@
     (not (string/includes? s "."))
     (page-reference (:html-export? config) s config label)
 
-    (util/url? s)
+    (gp-util/url? s)
     (->elem :a {:href s
                 :data-href s
                 :target "_blank"}
@@ -1737,8 +1738,8 @@
          (for [elem elems]
            (rum/with-key elem (str (random-uuid)))))
 
-       (and (string? v) (util/wrapped-by-quotes? v))
-       (util/unquote-string v)
+       (and (string? v) (gp-util/wrapped-by-quotes? v))
+       (gp-util/unquote-string v)
 
        :else
        (inline-text config (:block/format block) (str v)))]))
@@ -2764,11 +2765,11 @@
 ;;     (cond
 ;;       (= lang "quote")
 ;;       (let [content (string/trim (string/join "\n" lines))]
-;;         ["Quote" (first (mldoc/->edn content (mldoc/default-config :markdown)))])
+;;         ["Quote" (first (mldoc/->edn content (gp-mldoc/default-config :markdown)))])
 
 ;;       (contains? #{"query" "note" "tip" "important" "caution" "warning" "pinned"} lang)
 ;;       (let [content (string/trim (string/join "\n" lines))]
-;;         ["Custom" lang nil (first (mldoc/->edn content (mldoc/default-config :markdown))) content])
+;;         ["Custom" lang nil (first (mldoc/->edn content (gp-mldoc/default-config :markdown))) content])
 
 ;;       :else
 ;;       ["Src" options])))

+ 1 - 1
src/main/frontend/components/hierarchy.cljs

@@ -4,7 +4,7 @@
             [frontend.db :as db]
             [frontend.db.model :as db-model]
             [frontend.state :as state]
-            [frontend.text :as text]
+            [logseq.graph-parser.text :as text]
             [frontend.ui :as ui]
             [medley.core :as medley]
             [rum.core :as rum]

+ 1 - 1
src/main/frontend/components/journal.cljs

@@ -8,7 +8,7 @@
             [frontend.db.model :as model]
             [frontend.handler.page :as page-handler]
             [frontend.state :as state]
-            [frontend.text :as text]
+            [logseq.graph-parser.text :as text]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [goog.object :as gobj]

+ 3 - 3
src/main/frontend/components/page.cljs

@@ -26,7 +26,7 @@
             [frontend.handler.route :as route-handler]
             [frontend.mixins :as mixins]
             [frontend.state :as state]
-            [frontend.text :as text]
+            [logseq.graph-parser.text :as text]
             [frontend.search :as search]
             [frontend.ui :as ui]
             [frontend.util :as util]
@@ -222,8 +222,8 @@
                          (reset! *edit? false)
                          (notification/show! "Illegal page name, can not rename!" :warning))
           blur-fn (fn [e]
-                    (when (util/wrapped-by-quotes? @*title-value)
-                      (swap! *title-value util/unquote-string)
+                    (when (gp-util/wrapped-by-quotes? @*title-value)
+                      (swap! *title-value gp-util/unquote-string)
                       (gobj/set (rum/deref input-ref) "value" @*title-value))
                     (state/set-state! :editor/editing-page-title? false)
                     (cond

+ 1 - 1
src/main/frontend/components/repo.cljs

@@ -14,7 +14,7 @@
             [reitit.frontend.easy :as rfe]
             [rum.core :as rum]
             [frontend.mobile.util :as mobile-util]
-            [frontend.text :as text]
+            [logseq.graph-parser.text :as text]
             [promesa.core :as p]
             [electron.ipc :as ipc]
             [goog.object :as gobj]

+ 1 - 1
src/main/frontend/components/select.cljs

@@ -10,7 +10,7 @@
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.db :as db]
-            [frontend.text :as text]
+            [logseq.graph-parser.text :as text]
             [rum.core :as rum]
             [frontend.config :as config]
             [frontend.handler.repo :as repo-handler]

+ 1 - 1
src/main/frontend/db/conn.cljs

@@ -7,7 +7,7 @@
             [frontend.mobile.util :as mobile-util]
             [frontend.state :as state]
             [frontend.config :as config]
-            [frontend.text :as text]
+            [logseq.graph-parser.text :as text]
             [logseq.graph-parser.util :as gp-util]
             [datascript.core :as d]))
 

+ 4 - 3
src/main/frontend/db/query_dsl.cljs

@@ -6,13 +6,14 @@
             [clojure.set :as set]
             [clojure.string :as string]
             [clojure.walk :as walk]
+            [frontend.state :as state]
             [frontend.date :as date]
             [frontend.db.model :as model]
             [frontend.db.query-react :as query-react]
             [frontend.db.utils :as db-utils]
             [frontend.db.rules :as rules]
             [frontend.template :as template]
-            [frontend.text :as text]
+            [logseq.graph-parser.text :as text]
             [frontend.util :as util]
             [logseq.graph-parser.util :as gp-util]))
 
@@ -238,7 +239,7 @@
   (let [k (string/replace (name (nth e 1)) "_" "-")
         v (nth e 2)
         v (if-not (nil? v)
-            (text/parse-property k v)
+            (text/parse-property k v (state/get-config))
             v)
         v (if (coll? v) (first v) v)]
     {:query (list 'property '?b (keyword k) v)
@@ -283,7 +284,7 @@
   (let [[k v] (rest e)
         k (string/replace (name k) "_" "-")]
     (if (some? v)
-      (let [v' (text/parse-property k v)
+      (let [v' (text/parse-property k v (state/get-config))
             val (if (coll? v') (first v') v')]
         {:query (list 'page-property '?p (keyword k) val)
          :rules [:page-property]})

+ 1 - 1
src/main/frontend/db/query_react.cljs

@@ -10,7 +10,7 @@
             [frontend.debug :as debug]
             [frontend.extensions.sci :as sci]
             [frontend.state :as state]
-            [frontend.text :as text]
+            [logseq.graph-parser.text :as text]
             [frontend.util :as util]
             [logseq.graph-parser.util :as gp-util]
             [lambdaisland.glogi :as log]))

+ 1 - 1
src/main/frontend/diff.cljs

@@ -6,7 +6,7 @@
             [cljs-bean.core :as bean]
             [frontend.util :as util]
             [logseq.graph-parser.util :as gp-util]
-            [frontend.text :as text]))
+            [logseq.graph-parser.text :as text]))
 
 (defn diff
   [s1 s2]

+ 1 - 1
src/main/frontend/encrypt.cljs

@@ -1,5 +1,5 @@
 (ns frontend.encrypt
-  (:require [frontend.utf8 :as utf8]
+  (:require [logseq.graph-parser.utf8 :as utf8]
             [frontend.db.utils :as db-utils]
             [frontend.db :as db]
             [promesa.core :as p]

+ 1 - 1
src/main/frontend/extensions/code.cljs

@@ -132,7 +132,7 @@
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.file :as file-handler]
             [frontend.state :as state]
-            [frontend.utf8 :as utf8]
+            [logseq.graph-parser.utf8 :as utf8]
             [frontend.util :as util]
             [frontend.config :as config]
             [goog.dom :as gdom]

+ 1 - 1
src/main/frontend/external/roam.cljs

@@ -7,7 +7,7 @@
             [clojure.string :as string]
             [frontend.util :as util]
             [logseq.graph-parser.util :as gp-util]
-            [frontend.text :as text]))
+            [logseq.graph-parser.text :as text]))
 
 (defonce all-refed-uids (atom #{}))
 (defonce uid->uuid (atom {}))

+ 8 - 5
src/main/frontend/format.cljs

@@ -2,10 +2,13 @@
   (:require [frontend.format.mldoc :refer [->MldocMode] :as mldoc]
             [frontend.format.adoc :refer [->AdocMode]]
             [frontend.format.protocol :as protocol]
-            [frontend.text :as text]
+            [logseq.graph-parser.mldoc :as gp-mldoc]
+            [logseq.graph-parser.text :as text]
             [clojure.string :as string]))
 
-(set! mldoc/parse-property text/parse-property)
+;; TODO: Properly fix this circular dependency:
+;; mldoc/->edn > text/parse-property > mldoc/link? ->mldoc/inline->edn + mldoc/default-config
+(set! gp-mldoc/parse-property text/parse-property)
 
 (defonce mldoc-record (->MldocMode))
 (defonce adoc-record (->AdocMode))
@@ -37,9 +40,9 @@
 ;; html
 (defn get-default-config
   ([format]
-   (mldoc/default-config format))
+   (gp-mldoc/default-config format))
   ([format options]
-   (mldoc/default-config format options)))
+   (gp-mldoc/default-config format options)))
 
 (defn to-html
   ([content format]
@@ -49,7 +52,7 @@
      (if (string/blank? content)
        ""
        (if-let [record (get-format-record format)]
-         (protocol/toHtml record content config mldoc/default-references)
+         (protocol/toHtml record content config gp-mldoc/default-references)
          content)))))
 
 (defn to-edn

+ 9 - 9
src/main/frontend/format/block.cljs

@@ -7,15 +7,15 @@
             [frontend.db :as db]
             [frontend.format :as format]
             [frontend.state :as state]
-            [frontend.text :as text]
-            [frontend.utf8 :as utf8]
+            [logseq.graph-parser.text :as text]
+            [logseq.graph-parser.utf8 :as utf8]
             [frontend.util :as util]
             [frontend.util.property :as property]
             [logseq.graph-parser.util :as gp-util]
             [logseq.graph-parser.config :as gp-config]
+            [logseq.graph-parser.mldoc :as gp-mldoc]
             [lambdaisland.glogi :as log]
-            [medley.core :as medley]
-            [frontend.format.mldoc :as mldoc]))
+            [medley.core :as medley]))
 
 (defn heading-block?
   [block]
@@ -165,7 +165,7 @@
                      (map last)
                      (map (fn [v]
                             (when (and (string? v)
-                                       (not (mldoc/link? format v)))
+                                       (not (gp-mldoc/link? format v)))
                               (let [v (string/trim v)
                                     result (text/split-page-refs-without-brackets v {:un-brackets? false})]
                                 (if (coll? result)
@@ -185,7 +185,7 @@
                                            (remove string/blank? v)
                                            (if (string/blank? v)
                                              nil
-                                             (text/parse-property format k v)))
+                                             (text/parse-property format k v (state/get-config))))
                                        k (keyword k)
                                        v (if (and
                                               (string? v)
@@ -425,11 +425,11 @@
                   (utf8/substring utf8-content
                                   (:start_pos meta)))
         content (when content
-                  (let [content (text/remove-level-spaces content format)]
+                  (let [content (text/remove-level-spaces content format (config/get-block-pattern format))]
                     (if (or (:pre-block? block)
                             (= (:format block) :org))
                       content
-                      (mldoc/remove-indentation-spaces content (inc (:level block)) false))))]
+                      (gp-mldoc/remove-indentation-spaces content (inc (:level block)) false))))]
     (if (= format :org)
       content
       (property/->new-properties content))))
@@ -702,7 +702,7 @@
                        (str (config/get-block-pattern format) " " (string/triml content)))]
        (if-let [result (state/get-block-ast block-uuid content)]
          result
-         (let [ast (->> (format/to-edn content format (mldoc/default-config format))
+         (let [ast (->> (format/to-edn content format (gp-mldoc/default-config format))
                         (map first))
                title (when (heading-block? (first ast))
                        (:title (second (first ast))))

+ 13 - 209
src/main/frontend/format/mldoc.cljs

@@ -1,68 +1,18 @@
 (ns frontend.format.mldoc
-  (:require [cljs-bean.core :as bean]
-            [clojure.string :as string]
+  (:require [clojure.string :as string]
             [frontend.format.protocol :as protocol]
-            [frontend.utf8 :as utf8]
+            [frontend.state :as state]
             [goog.object :as gobj]
             [lambdaisland.glogi :as log]
-            [medley.core :as medley]
             ["mldoc" :as mldoc :refer [Mldoc]]
-            [linked.core :as linked]
-            [logseq.graph-parser.util :as gp-util]
-            [logseq.graph-parser.config :as gp-config]))
+            [logseq.graph-parser.mldoc :as gp-mldoc]
+            [logseq.graph-parser.util :as gp-util]))
 
-(defonce parseJson (gobj/get Mldoc "parseJson"))
-(defonce parseInlineJson (gobj/get Mldoc "parseInlineJson"))
-(defonce parseOPML (gobj/get Mldoc "parseOPML"))
-(defonce export (gobj/get Mldoc "export"))
 (defonce anchorLink (gobj/get Mldoc "anchorLink"))
+(defonce parseOPML (gobj/get Mldoc "parseOPML"))
 (defonce parseAndExportMarkdown (gobj/get Mldoc "parseAndExportMarkdown"))
 (defonce parseAndExportOPML (gobj/get Mldoc "parseAndExportOPML"))
-(defonce astExportMarkdown (gobj/get Mldoc "astExportMarkdown"))
-
-(defn convert-export-md-remove-options [opts]
-  (->>
-   (mapv (fn [opt]
-             (case opt
-               :page-ref ["Page_ref"]
-               :emphasis ["Emphasis"]
-               []))
-         opts)
-   (remove empty?)))
-
-
-(defn default-config
-  ([format]
-   (default-config format {:export-heading-to-list? false}))
-  ([format {:keys [export-heading-to-list? export-keep-properties? export-md-indent-style export-md-remove-options parse_outline_only?]}]
-   (let [format (string/capitalize (name (or format :markdown)))]
-     (->> {:toc false
-           :parse_outline_only (or parse_outline_only? false)
-           :heading_number false
-           :keep_line_break true
-           :format format
-           :heading_to_list (or export-heading-to-list? false)
-           :exporting_keep_properties export-keep-properties?
-           :export_md_indent_style export-md-indent-style
-           :export_md_remove_options
-           (convert-export-md-remove-options export-md-remove-options)}
-          (filter #(not(nil? (second %))))
-          (into {})
-          (bean/->js)
-          (js/JSON.stringify)))))
-
-(def default-references
-  (js/JSON.stringify
-   (clj->js {:embed_blocks []
-             :embed_pages []})))
-
-(defn parse-json
-  [content config]
-  (parseJson content config))
-
-(defn inline-parse-json
-  [text config]
-  (parseInlineJson text config))
+(defonce export (gobj/get Mldoc "export"))
 
 (defn parse-opml
   [content]
@@ -72,118 +22,14 @@
   [content config references]
   (parseAndExportMarkdown content
                           config
-                          (or references default-references)))
+                          (or references gp-mldoc/default-references)))
 
 (defn parse-export-opml
   [content config title references]
   (parseAndExportOPML content
                       config
                       title
-                      (or references default-references)))
-
-(defn ast-export-markdown
-  [ast config references]
-  (astExportMarkdown ast
-                     config
-                     (or references default-references)))
-
-(defn remove-indentation-spaces
-  [s level remove-first-line?]
-  (let [lines (string/split-lines s)
-        [f & r] lines
-        body (map (fn [line]
-                    (if (string/blank? (gp-util/safe-subs line 0 level))
-                      (gp-util/safe-subs line level)
-                      line))
-               (if remove-first-line? lines r))
-        content (if remove-first-line? body (cons f body))]
-    (string/join "\n" content)))
-
-(defn- ->vec
-  [s]
-  (if (string? s) [s] s))
-
-(defn- ->vec-concat
-  [& coll]
-  (->> (map ->vec coll)
-       (remove nil?)
-       (apply concat)
-       (distinct)))
-
-(defn collect-page-properties
-  [ast parse-property]
-  (if (seq ast)
-    (let [original-ast ast
-          ast (map first ast)           ; without position meta
-          directive? (fn [[item _]] (= "directive" (string/lower-case (first item))))
-          grouped-ast (group-by directive? original-ast)
-          directive-ast (take-while directive? original-ast)
-          [properties-ast other-ast] (if (= "Property_Drawer" (ffirst ast))
-                                       [(last (first ast))
-                                        (rest original-ast)]
-                                       [(->> (map first directive-ast)
-                                             (map rest))
-                                        (get grouped-ast false)])
-          properties (->>
-                      properties-ast
-                      (map (fn [[k v]]
-                             (let [k (keyword (string/lower-case k))
-                                   v (if (contains? #{:title :description :filters :macro} k)
-                                       v
-                                       (parse-property k v))]
-                               [k v]))))
-          properties (into (linked/map) properties)
-          macro-properties (filter (fn [x] (= :macro (first x))) properties)
-          macros (if (seq macro-properties)
-                   (->>
-                    (map
-                     (fn [[_ v]]
-                       (let [[k v] (gp-util/split-first " " v)]
-                         (mapv
-                          string/trim
-                          [k v])))
-                     macro-properties)
-                    (into {}))
-                   {})
-          properties (->> (remove (fn [x] (= :macro (first x))) properties)
-                          (into (linked/map)))
-          properties (cond-> properties
-                       (seq macros)
-                       (assoc :macros macros))
-          alias (:alias properties)
-          alias (when alias
-                  (if (coll? alias)
-                    (remove string/blank? alias)
-                    [alias]))
-          filetags (when-let [org-file-tags (:filetags properties)]
-                     (->> (string/split org-file-tags ":")
-                          (remove string/blank?)))
-          tags (:tags properties)
-          tags (->> (->vec-concat tags filetags)
-                    (remove string/blank?))
-          properties (assoc properties :tags tags :alias alias)
-          properties (-> properties
-                         (update :filetags (constantly filetags)))
-          properties (medley/remove-kv (fn [_k v] (or (nil? v) (and (coll? v) (empty? v)))) properties)]
-      (if (seq properties)
-        (cons [["Properties" properties] nil] other-ast)
-        original-ast))
-    ast))
-
-(defn update-src-full-content
-  [ast content]
-  (let [content (utf8/encode content)]
-    (map (fn [[block pos-meta]]
-          (if (and (vector? block)
-                   (= "Src" (first block)))
-            (let [{:keys [start_pos end_pos]} pos-meta
-                  content (utf8/substring content start_pos end_pos)
-                  spaces (re-find #"^[\t ]+" (first (string/split-lines content)))
-                  content (if spaces (remove-indentation-spaces content (count spaces) true)
-                              content)
-                  block ["Src" (assoc (second block) :full_content content)]]
-              [block pos-meta])
-            [block pos-meta])) ast)))
+                      (or references gp-mldoc/default-references)))
 
 (defn block-with-title?
   [type]
@@ -192,45 +38,21 @@
                "Hiccup"
                "Heading"} type))
 
-(def parse-property nil)
-
-(defn ->edn
-  [content config]
-  (if (string? content)
-    (try
-      (if (string/blank? content)
-        []
-        (-> content
-            (parse-json config)
-            (gp-util/json->clj)
-            (update-src-full-content content)
-            (collect-page-properties parse-property)))
-      (catch js/Error e
-        (js/console.error e)
-        []))
-    (log/error :edn/wrong-content-type content)))
-
 (defn opml->edn
   [content]
   (try
     (if (string/blank? content)
       {}
       (let [[headers blocks] (-> content (parse-opml) (gp-util/json->clj))]
-        [headers (collect-page-properties blocks parse-property)]))
+        [headers (gp-mldoc/collect-page-properties blocks gp-mldoc/parse-property (state/get-config))]))
     (catch js/Error e
       (log/error :edn/convert-failed e)
       [])))
 
-(defn inline->edn
-  [text config]
-  (try
-    (if (string/blank? text)
-      {}
-      (-> text
-          (inline-parse-json config)
-          (gp-util/json->clj)))
-    (catch js/Error _e
-      [])))
+(defn ->edn
+  "Wrapper around gp-mldoc/->edn which provides config state"
+  [content config]
+  (gp-mldoc/->edn content config (state/get-config)))
 
 (defrecord MldocMode []
   protocol/Format
@@ -259,21 +81,3 @@
   [ast typ]
   (and (contains? #{"Drawer"} (ffirst ast))
        (= typ (second (first ast)))))
-
-(defn link?
-  [format link]
-  (when (string? link)
-    (let [[type link] (first (inline->edn link (default-config format)))
-          [ref-type ref-value] (:url link)]
-      (and (= "Link" type)
-           (or
-            ;; 1. url
-            (not (contains? #{"Page_ref" "Block_ref"} ref-type))
-
-            (and (contains? #{"Page_ref"} ref-type)
-                 (or
-                  ;; 2. excalidraw link
-                  (gp-config/draw? ref-value)
-
-                  ;; 3. local asset link
-                  (boolean (gp-config/local-asset? ref-value)))))))))

+ 11 - 10
src/main/frontend/handler/editor.cljs

@@ -34,8 +34,8 @@
             [frontend.search :as search]
             [frontend.state :as state]
             [frontend.template :as template]
-            [frontend.text :as text]
-            [frontend.utf8 :as utf8]
+            [logseq.graph-parser.text :as text]
+            [logseq.graph-parser.utf8 :as utf8]
             [frontend.util :as util :refer [profile]]
             [frontend.util.clock :as clock]
             [frontend.util.cursor :as cursor]
@@ -53,6 +53,7 @@
             [promesa.core :as p]
             [frontend.util.keycode :as keycode]
             [logseq.graph-parser.util :as gp-util]
+            [logseq.graph-parser.mldoc :as gp-mldoc]
             ["path" :as path]))
 
 ;; FIXME: should support multiple images concurrently uploading
@@ -356,7 +357,7 @@
         content (drawer/with-logbook block content)
         content (with-timetracking block content)
         first-block? (= left page)
-        ast (mldoc/->edn (string/trim content) (mldoc/default-config format))
+        ast (mldoc/->edn (string/trim content) (gp-mldoc/default-config format))
         first-elem-type (first (ffirst ast))
         first-elem-meta (second (ffirst ast))
         properties? (contains? #{"Property_Drawer" "Properties"} first-elem-type)
@@ -1315,7 +1316,7 @@
 
 (defn- clean-content!
   [format content]
-  (->> (text/remove-level-spaces content format)
+  (->> (text/remove-level-spaces content format (config/get-block-pattern format))
        (drawer/remove-logbook)
        (property/remove-properties format)
        string/trim))
@@ -1943,7 +1944,7 @@
                     props (into [] (:properties block))
                     content* (str (if (= :markdown format) "- " "* ")
                                   (property/insert-properties format content props))
-                    ast (mldoc/->edn content* (mldoc/default-config format))
+                    ast (mldoc/->edn content* (gp-mldoc/default-config format))
                     blocks (block/extract-blocks ast content* true format)
                     fst-block (first blocks)]
                 (assert fst-block "fst-block shouldn't be nil")
@@ -2852,7 +2853,7 @@
   (when-let [editing-block (state/get-edit-block)]
     (let [page-id (:db/id (:block/page editing-block))
           blocks (block/extract-blocks
-                  (mldoc/->edn text (mldoc/default-config format)) text true format)
+                  (mldoc/->edn text (gp-mldoc/default-config format)) text true format)
           blocks' (block/with-parent-and-left page-id blocks)]
       (paste-blocks blocks' {}))))
 
@@ -2905,17 +2906,17 @@
           (state/set-copied-full-blocks! blocks)
           (paste-blocks blocks {})))
 
-      (and (util/url? text)
+      (and (gp-util/url? text)
            (not (string/blank? (util/get-selected-text))))
       (html-link-format! text)
 
-      (and (util/url? text)
+      (and (gp-util/url? text)
            (or (string/includes? text "youtube.com")
                (string/includes? text "youtu.be"))
            (mobile-util/is-native-platform?))
       (commands/simple-insert! (state/get-edit-input-id) (util/format "{{youtube %s}}" text) nil)
 
-      (and (util/url? text)
+      (and (gp-util/url? text)
            (string/includes? text "twitter.com")
            (mobile-util/is-native-platform?))
       (commands/simple-insert! (state/get-edit-input-id) (util/format "{{twitter %s}}" text) nil)
@@ -3172,7 +3173,7 @@
   [format content semantic?]
   (and (string/includes? content "\n")
        (if semantic?
-         (let [ast (mldoc/->edn content (mldoc/default-config format))
+         (let [ast (mldoc/->edn content (gp-mldoc/default-config format))
                first-elem-type (first (ffirst ast))]
            (mldoc/block-with-title? first-elem-type))
          true)))

+ 3 - 2
src/main/frontend/handler/export.cljs

@@ -16,6 +16,7 @@
             [frontend.state :as state]
             [frontend.util :as util]
             [frontend.format.mldoc :as mldoc]
+            [logseq.graph-parser.mldoc :as gp-mldoc]
             [goog.dom :as gdom]
             [promesa.core :as p])
   (:import [goog.string StringBuffer]))
@@ -214,7 +215,7 @@
   (let [block (db/entity [:block/uuid (uuid block-uuid)])
         block-content (get-blocks-contents repo (:block/uuid block))
         format (:block/format block)
-        ast (mldoc/->edn block-content (mldoc/default-config format))
+        ast (mldoc/->edn block-content (gp-mldoc/default-config format))
         embed-pages-new  (get-embed-pages-from-ast ast)
         embed-blocks-new  (get-embed-blocks-from-ast ast)
         block-refs-new (get-block-refs-from-ast ast)
@@ -258,7 +259,7 @@
   (let [page-name* (util/page-name-sanity-lc page-name)
         page-content (get-page-content repo page-name*)
         format (:block/format (db/entity [:block/name page-name*]))
-        ast (mldoc/->edn page-content (mldoc/default-config format))
+        ast (mldoc/->edn page-content (gp-mldoc/default-config format))
         embed-pages-new (get-embed-pages-from-ast ast)
         embed-blocks-new (get-embed-blocks-from-ast ast)
         block-refs-new (get-block-refs-from-ast ast)

+ 4 - 3
src/main/frontend/handler/extract.cljs

@@ -9,9 +9,10 @@
             [frontend.format.block :as block]
             [frontend.format.mldoc :as mldoc]
             [frontend.state :as state]
-            [frontend.text :as text]
+            [logseq.graph-parser.text :as text]
             [frontend.util :as util]
             [logseq.graph-parser.util :as gp-util]
+            [logseq.graph-parser.mldoc :as gp-mldoc]
             [frontend.util.property :as property]
             [lambdaisland.glogi :as log]))
 
@@ -142,7 +143,7 @@
     []
     (let [format (format/get-format file)
           _ (println "Parsing start: " file)
-          ast (mldoc/->edn content (mldoc/default-config format
+          ast (mldoc/->edn content (gp-mldoc/default-config format
                                                          ;; {:parse_outline_only? true}
                                                          ))]
       (println "Parsing finished : " file)
@@ -151,7 +152,7 @@
                                              (->> (last first-block)
                                                   (map (fn [[x y]]
                                                          [x (if (string? y)
-                                                              (text/parse-property format x y)
+                                                              (text/parse-property format x y (state/get-config))
                                                               y)]))
                                                   (into {})
                                                   (walk/keywordize-keys)))]

+ 2 - 2
src/main/frontend/handler/plugin.cljs

@@ -3,7 +3,7 @@
             [rum.core :as rum]
             [frontend.util :as util]
             [clojure.walk :as walk]
-            [frontend.format.mldoc :as mldoc]
+            [logseq.graph-parser.mldoc :as gp-mldoc]
             [frontend.handler.notification :as notifications]
             [camel-snake-kebab.core :as csk]
             [frontend.state :as state]
@@ -403,7 +403,7 @@
                             (string/replace matched link (util/node-path.join url link))
                             matched)))
                       content)]
-        (format/to-html content :markdown (mldoc/default-config :markdown))))
+        (format/to-html content :markdown (gp-mldoc/default-config :markdown))))
     (catch js/Error e
       (log/error :parse-user-md-exception e)
       content)))

+ 3 - 2
src/main/frontend/handler/route.cljs

@@ -1,12 +1,13 @@
 (ns frontend.handler.route
   (:require [clojure.string :as string]
+            [frontend.config :as config]
             [frontend.date :as date]
             [frontend.db :as db]
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.recent :as recent-handler]
             [frontend.handler.search :as search-handler]
             [frontend.state :as state]
-            [frontend.text :as text]
+            [logseq.graph-parser.text :as text]
             [frontend.util :as util]
             [logseq.graph-parser.util :as gp-util]
             [medley.core :as medley]
@@ -82,7 +83,7 @@
       (if block?
         (if-let [block (db/entity [:block/uuid (medley/uuid name)])]
           (let [content (text/remove-level-spaces (:block/content block)
-                                                  (:block/format block))]
+                                                  (:block/format block) (config/get-block-pattern (:block/format block)))]
             (if (> (count content) 48)
               (str (subs content 0 48) "...")
               content))

+ 4 - 3
src/main/frontend/handler/search.cljs

@@ -1,12 +1,13 @@
 (ns frontend.handler.search
   (:require [clojure.string :as string]
+            [frontend.config :as config]
             [frontend.db :as db]
             [frontend.handler.notification :as notification-handler]
             [frontend.search :as search]
             [frontend.state :as state]
             [frontend.util :as util]
             [promesa.core :as p]
-            [frontend.text :as text]
+            [logseq.graph-parser.text :as text]
             [frontend.util.drawer :as drawer]
             [frontend.util.property :as property]))
 
@@ -21,7 +22,7 @@
 (defn sanity-search-content
   "Convert a block to the display contents for searching"
   [format content]
-  (->> (text/remove-level-spaces content format)
+  (->> (text/remove-level-spaces content format (config/get-block-pattern format))
        (drawer/remove-logbook)
        (property/remove-built-in-properties format)))
 
@@ -78,4 +79,4 @@
        (p/let [_ (search/rebuild-indices! repo)]
          (notification-handler/show!
           "Stale search cache detected. Search indices rebuilt successfully!"
-          :success))))))
+          :success))))))

+ 3 - 3
src/main/frontend/mobile/intent.cljs

@@ -10,7 +10,7 @@
             [frontend.date :as date]
             [frontend.util :as util]
             [frontend.config :as config]
-            [frontend.format.mldoc :as mldoc]
+            [logseq.graph-parser.mldoc :as gp-mldoc]
             ["path" :as path]
             [frontend.mobile.util :as mobile-util]
             [frontend.handler.notification :as notification]
@@ -23,11 +23,11 @@
                  (string/lower-case (date/journal-name)))
         format (db/get-page-format page)
         time (date/get-current-time)
-        url (if (and (mldoc/link? format title) (not url))
+        url (if (and (gp-mldoc/link? format title) (not url))
               title
               url)
         text (if (= url title) nil title)
-        [text url] (if (or (mldoc/link? format url) (not url))
+        [text url] (if (or (gp-mldoc/link? format url) (not url))
                      [text url]
                      (string/split url "\"\n"))
         text (some-> text (string/replace #"^\"" ""))

+ 0 - 18
src/main/frontend/util.cljc

@@ -1258,14 +1258,6 @@
    (defn meta-key-name []
      (if mac? "Cmd" "Ctrl")))
 
-(defn wrapped-by-quotes?
-  [v]
-  (and (string? v) (>= (count v) 2) (= "\"" (first v) (last v))))
-
-(defn unquote-string
-  [v]
-  (string/trim (subs v 1 (dec (count v)))))
-
 #?(:cljs
    (defn right-click?
      [e]
@@ -1274,16 +1266,6 @@
        (or (= which 3)
            (= button 2)))))
 
-#?(:cljs
-   (defn url?
-     [s]
-     (and (string? s)
-          (try
-            (js/URL. s)
-            true
-            (catch js/Error _e
-              false)))))
-
 #?(:cljs
    (defn make-el-into-center-viewport
      [^js/HTMLElement el]

+ 3 - 2
src/main/frontend/util/drawer.cljs

@@ -2,6 +2,7 @@
   (:require [clojure.string :as string]
             [frontend.util :as util]
             [logseq.graph-parser.util :as gp-util]
+            [logseq.graph-parser.mldoc :as gp-mldoc]
             [frontend.util.property :as property]
             [frontend.format.mldoc :as mldoc]))
 
@@ -23,7 +24,7 @@
 
 (defn get-drawer-ast
   [format content typ]
-  (let [ast (mldoc/->edn content (mldoc/default-config format))
+  (let [ast (mldoc/->edn content (gp-mldoc/default-config format))
         typ-drawer (ffirst (filter (fn [x]
                                      (mldoc/typ-drawer? x typ)) ast))]
     typ-drawer))
@@ -32,7 +33,7 @@
   [format content typ value]
   (when (string? content)
     (try
-      (let [ast (mldoc/->edn content (mldoc/default-config format))
+      (let [ast (mldoc/->edn content (gp-mldoc/default-config format))
             has-properties? (some (fn [x] (mldoc/properties? x)) ast)
             has-typ-drawer? (some (fn [x] (mldoc/typ-drawer? x typ)) ast)
             lines (string/split-lines content)

+ 4 - 3
src/main/frontend/util/property.cljs

@@ -5,8 +5,9 @@
             [frontend.config :as config]
             [medley.core :as medley]
             [logseq.graph-parser.util :as gp-util]
+            [logseq.graph-parser.mldoc :as gp-mldoc]
             [frontend.format.mldoc :as mldoc]
-            [frontend.text :as text]
+            [logseq.graph-parser.text :as text]
             [frontend.util.cursor :as cursor]))
 
 (defonce properties-start ":PROPERTIES:")
@@ -187,7 +188,7 @@
         properties (filter (fn [[k _v]] ((built-in-properties) k)) properties)]
     (if (seq properties)
       (let [lines (string/split-lines content)
-            ast (mldoc/->edn content (mldoc/default-config format))
+            ast (mldoc/->edn content (gp-mldoc/default-config format))
             [title body] (if (mldoc/block-with-title? (first (ffirst ast)))
                            [(first lines) (rest lines)]
                            [nil lines])
@@ -233,7 +234,7 @@
    (insert-property format content key value false))
   ([format content key value front-matter?]
    (when (string? content)
-     (let [ast (mldoc/->edn content (mldoc/default-config format))
+     (let [ast (mldoc/->edn content (gp-mldoc/default-config format))
            title? (mldoc/block-with-title? (ffirst (map first ast)))
            has-properties? (or (and title?
                                     (or (mldoc/properties? (second ast))

+ 1 - 1
src/main/frontend/util/thingatpt.cljs

@@ -4,7 +4,7 @@
             [frontend.util.property :as property-util]
             [frontend.util.cursor :as cursor]
             [frontend.config :as config]
-            [frontend.text :as text]
+            [logseq.graph-parser.text :as text]
             [cljs.reader :as reader]
             [goog.object :as gobj]))
 

+ 6 - 0
src/main/logseq/graph_parser/log.cljs

@@ -0,0 +1,6 @@
+(ns logseq.graph-parser.log
+  "Minimal logging ns that implements basic lambdaisland.glogi fns. May use
+  glogi later if this ns is used more")
+
+(defn error [& msgs]
+  (apply js/console.error (map clj->js msgs)))

+ 215 - 0
src/main/logseq/graph_parser/mldoc.cljc

@@ -0,0 +1,215 @@
+(ns ^:nbb-compatible logseq.graph-parser.mldoc
+  ;; Disable clj linters since we don't support clj
+  #?(:clj {:clj-kondo/config {:linters {:unresolved-namespace {:level :off}
+                                        :unresolved-symbol {:level :off}}}})
+  (:require #?(:org.babashka/nbb ["mldoc$default" :refer [Mldoc]]
+               :default ["mldoc" :refer [Mldoc]])
+            #?(:org.babashka/nbb [logseq.graph-parser.log :as log]
+               :default [lambdaisland.glogi :as log])
+            [goog.object :as gobj]
+            [cljs-bean.core :as bean]
+            [logseq.graph-parser.utf8 :as utf8]
+            [clojure.string :as string]
+            [linked.core :as linked]
+            [logseq.graph-parser.util :as gp-util]
+            [logseq.graph-parser.config :as gp-config]))
+
+(defonce parseJson (gobj/get Mldoc "parseJson"))
+(defonce parseInlineJson (gobj/get Mldoc "parseInlineJson"))
+(defonce astExportMarkdown (gobj/get Mldoc "astExportMarkdown"))
+
+(def default-references
+  (js/JSON.stringify
+   (clj->js {:embed_blocks []
+             :embed_pages []})))
+
+(defn- convert-export-md-remove-options [opts]
+  (->> opts
+       (mapv (fn [opt]
+               (case opt
+                 :page-ref ["Page_ref"]
+                 :emphasis ["Emphasis"]
+                 [])))
+       (remove empty?)))
+
+(defn parse-json
+  [content config]
+  (parseJson content config))
+
+(defn inline-parse-json
+  [text config]
+  (parseInlineJson text config))
+
+(defn ast-export-markdown
+  [ast config references]
+  (astExportMarkdown ast
+                     config
+                     (or references default-references)))
+
+(defn default-config
+  ([format]
+   (default-config format {:export-heading-to-list? false}))
+  ([format {:keys [export-heading-to-list? export-keep-properties? export-md-indent-style export-md-remove-options parse_outline_only?]}]
+   (let [format (string/capitalize (name (or format :markdown)))]
+     (->> {:toc false
+           :parse_outline_only (or parse_outline_only? false)
+           :heading_number false
+           :keep_line_break true
+           :format format
+           :heading_to_list (or export-heading-to-list? false)
+           :exporting_keep_properties export-keep-properties?
+           :export_md_indent_style export-md-indent-style
+           :export_md_remove_options
+           (convert-export-md-remove-options export-md-remove-options)}
+          (filter #(not (nil? (second %))))
+          (into {})
+          (bean/->js)
+          js/JSON.stringify))))
+
+(defn remove-indentation-spaces
+  [s level remove-first-line?]
+  (let [lines (string/split-lines s)
+        [f & r] lines
+        body (map (fn [line]
+                    (if (string/blank? (gp-util/safe-subs line 0 level))
+                      (gp-util/safe-subs line level)
+                      line))
+               (if remove-first-line? lines r))
+        content (if remove-first-line? body (cons f body))]
+    (string/join "\n" content)))
+
+(defn- update-src-full-content
+  [ast content]
+  (let [content (utf8/encode content)]
+    (map (fn [[block pos-meta]]
+          (if (and (vector? block)
+                   (= "Src" (first block)))
+            (let [{:keys [start_pos end_pos]} pos-meta
+                  content (utf8/substring content start_pos end_pos)
+                  spaces (re-find #"^[\t ]+" (first (string/split-lines content)))
+                  content (if spaces (remove-indentation-spaces content (count spaces) true)
+                              content)
+                  block ["Src" (assoc (second block) :full_content content)]]
+              [block pos-meta])
+            [block pos-meta])) ast)))
+
+(defn- ->vec
+  [s]
+  (if (string? s) [s] s))
+
+(defn- ->vec-concat
+  [& coll]
+  (->> (map ->vec coll)
+       (remove nil?)
+       (apply concat)
+       (distinct)))
+
+(defn collect-page-properties
+  [ast parse-property config-state]
+  (if (seq ast)
+    (let [original-ast ast
+          ast (map first ast)           ; without position meta
+          directive? (fn [[item _]] (= "directive" (string/lower-case (first item))))
+          grouped-ast (group-by directive? original-ast)
+          directive-ast (take-while directive? original-ast)
+          [properties-ast other-ast] (if (= "Property_Drawer" (ffirst ast))
+                                       [(last (first ast))
+                                        (rest original-ast)]
+                                       [(->> (map first directive-ast)
+                                             (map rest))
+                                        (get grouped-ast false)])
+          properties (->>
+                      properties-ast
+                      (map (fn [[k v]]
+                             (let [k (keyword (string/lower-case k))
+                                   v (if (contains? #{:title :description :filters :macro} k)
+                                       v
+                                       (parse-property k v config-state))]
+                               [k v]))))
+          properties (into (linked/map) properties)
+          macro-properties (filter (fn [x] (= :macro (first x))) properties)
+          macros (if (seq macro-properties)
+                   (->>
+                    (map
+                     (fn [[_ v]]
+                       (let [[k v] (gp-util/split-first " " v)]
+                         (mapv
+                          string/trim
+                          [k v])))
+                     macro-properties)
+                    (into {}))
+                   {})
+          properties (->> (remove (fn [x] (= :macro (first x))) properties)
+                          (into (linked/map)))
+          properties (cond-> properties
+                             (seq macros)
+                             (assoc :macros macros))
+          alias (:alias properties)
+          alias (when alias
+                  (if (coll? alias)
+                    (remove string/blank? alias)
+                    [alias]))
+          filetags (when-let [org-file-tags (:filetags properties)]
+                     (->> (string/split org-file-tags ":")
+                          (remove string/blank?)))
+          tags (:tags properties)
+          tags (->> (->vec-concat tags filetags)
+                    (remove string/blank?))
+          properties (assoc properties :tags tags :alias alias)
+          properties (-> properties
+                         (update :filetags (constantly filetags)))
+          properties (into (linked/map)
+                           (remove (fn [[_k v]]
+                                     (or (nil? v) (and (coll? v) (empty? v))))
+                                   properties))]
+      (if (seq properties)
+        (cons [["Properties" properties] nil] other-ast)
+        original-ast))
+    ast))
+
+(def parse-property nil)
+
+(defn ->edn
+  [content config config-state]
+  (if (string? content)
+    (try
+      (if (string/blank? content)
+        []
+        (-> content
+            (parse-json config)
+            (gp-util/json->clj)
+            (update-src-full-content content)
+            (collect-page-properties parse-property config-state)))
+      (catch :default e
+        (log/error :unexpected-error e)
+        []))
+    (log/error :edn/wrong-content-type content)))
+
+(defn inline->edn
+  [text config]
+  (try
+    (if (string/blank? text)
+      {}
+      (-> text
+          (inline-parse-json config)
+          (gp-util/json->clj)))
+    (catch :default _e
+      [])))
+
+(defn link?
+  [format link]
+  (when (string? link)
+    (let [[type link] (first (inline->edn link (default-config format)))
+          [ref-type ref-value] (:url link)]
+      (and (= "Link" type)
+           (or
+            ;; 1. url
+            (not (contains? #{"Page_ref" "Block_ref"} ref-type))
+
+            (and (contains? #{"Page_ref"} ref-type)
+                 (or
+                  ;; 2. excalidraw link
+                  (gp-config/draw? ref-value)
+
+                  ;; 3. local asset link
+                  (boolean (gp-config/local-asset? ref-value)))))))))

+ 29 - 37
src/main/frontend/text.cljs → src/main/logseq/graph_parser/text.cljs

@@ -1,11 +1,10 @@
-(ns frontend.text
-  (:require [frontend.config :as config]
-            [frontend.util :as util]
+(ns ^:nbb-compatible logseq.graph-parser.text
+  (:require ["path" :as path]
+            [goog.string :as gstring]
             [clojure.string :as string]
-            [frontend.format.mldoc :as mldoc]
             [clojure.set :as set]
-            [logseq.graph-parser.util :as gp-util]
-            [frontend.state :as state]))
+            [logseq.graph-parser.mldoc :as gp-mldoc]
+            [logseq.graph-parser.util :as gp-util]))
 
 (def page-ref-re-0 #"\[\[(.*)\]\]")
 (def org-page-ref-re #"\[\[(file:.*)\]\[.+?\]\]")
@@ -14,7 +13,8 @@
 (defn get-file-basename
   [path]
   (when-not (string/blank? path)
-    (util/node-path.name path)))
+    ;; Same as util/node-path.name
+    (.-name (path/parse (string/replace path "+" "/")))))
 
 (defn get-page-name
   [s]
@@ -123,14 +123,15 @@
   (string/split s #"(\"[^\"]*\")"))
 
 (def markdown-link #"\[([^\[]+)\](\(.*\))")
+
 (defn split-page-refs-without-brackets
   ([s]
    (split-page-refs-without-brackets s {}))
   ([s {:keys [un-brackets?]
        :or {un-brackets? true}}]
    (cond
-     (and (string? s) (util/wrapped-by-quotes? s))
-     (util/unquote-string s)
+     (and (string? s) (gp-util/wrapped-by-quotes? s))
+     (gp-util/unquote-string s)
 
      (and (string? s) (re-find markdown-link s))
      s
@@ -142,11 +143,11 @@
      (let [result (->> (sep-by-quotes s)
                        (mapcat
                         (fn [s]
-                          (when-not (util/wrapped-by-quotes? (string/trim s))
+                          (when-not (gp-util/wrapped-by-quotes? (string/trim s))
                             (string/split s page-ref-re-2))))
                        (mapcat (fn [s]
                                  (cond
-                                   (util/wrapped-by-quotes? s)
+                                   (gp-util/wrapped-by-quotes? s)
                                    nil
 
                                    (string/includes? (string/trimr s) "]],")
@@ -166,7 +167,7 @@
                        (remove string/blank?)
                        (mapcat (fn [s]
                                  (cond
-                                   (util/wrapped-by-quotes? s)
+                                   (gp-util/wrapped-by-quotes? s)
                                    nil
 
                                    (page-ref? s)
@@ -187,18 +188,9 @@
      :else
      s)))
 
-(defn extract-level-spaces
-  [text format]
-  (if-not (string/blank? text)
-    (let [pattern (util/format
-                   "^[%s]+\\s?"
-                   (config/get-block-pattern format))]
-      (gp-util/safe-re-find (re-pattern pattern) text))
-    ""))
-
 (defn- remove-level-space-aux!
   [text pattern space? trim-left?]
-  (let [pattern (util/format
+  (let [pattern (gstring/format
                  (if space?
                    "^[%s]+\\s+"
                    "^[%s]+\\s?")
@@ -207,11 +199,11 @@
     (string/replace-first text (re-pattern pattern) "")))
 
 (defn remove-level-spaces
-  ([text format]
-   (remove-level-spaces text format false true))
-  ([text format space?]
-   (remove-level-spaces text format space? true))
-  ([text format space? trim-left?]
+  ([text format block-pattern]
+   (remove-level-spaces text format block-pattern false true))
+  ([text format block-pattern space?]
+   (remove-level-spaces text format block-pattern space? true))
+  ([text format block-pattern space? trim-left?]
    (when format
      (cond
        (string/blank? text)
@@ -222,12 +214,12 @@
        text
 
        :else
-       (remove-level-space-aux! text (config/get-block-pattern format) space? trim-left?)))))
+       (remove-level-space-aux! text block-pattern space? trim-left?)))))
 
 (defn build-data-value
   [col]
   (let [items (map (fn [item] (str "\"" item "\"")) col)]
-    (util/format "[%s]"
+    (gstring/format "[%s]"
                  (string/join ", " items))))
 
 (defn media-link?
@@ -240,7 +232,7 @@
        (string/includes? p "/")
        (not (string/starts-with? p "../"))
        (not (string/starts-with? p "./"))
-       (not (util/url? p))))
+       (not (gp-util/url? p))))
 
 (defn add-timestamp
   [content key value]
@@ -340,16 +332,16 @@
   (atom #{"background-color" "background_color"}))
 
 (defn parse-property
-  ([k v]
-   (parse-property :markdown k v))
-  ([format k v]
+  ([k v config-state]
+   (parse-property :markdown k v config-state))
+  ([format k v config-state]
    (let [k (name k)
          v (if (or (symbol? v) (keyword? v)) (name v) (str v))
          v (string/trim v)]
      (cond
        (contains? (set/union
                    #{"title" "filters"}
-                   (get (state/get-config) :ignored-page-references-keywords)) k)
+                   (get config-state :ignored-page-references-keywords)) k)
        v
 
        (= v "true")
@@ -358,15 +350,15 @@
        false
 
        (and (not= k "alias") (gp-util/safe-re-find #"^\d+$" v))
-       (util/safe-parse-int v)
+       (gp-util/safe-parse-int v)
 
-       (util/wrapped-by-quotes? v) ; wrapped in ""
+       (gp-util/wrapped-by-quotes? v) ; wrapped in ""
        v
 
        (contains? @non-parsing-properties (string/lower-case k))
        v
 
-       (mldoc/link? format v)
+       (gp-mldoc/link? format v)
        v
 
        :else

+ 1 - 1
src/main/frontend/utf8.cljs → src/main/logseq/graph_parser/utf8.cljs

@@ -1,4 +1,4 @@
-(ns frontend.utf8
+(ns ^:nbb-compatible logseq.graph-parser.utf8
   (:require [goog.object :as gobj]))
 
 (defonce encoder

+ 32 - 0
src/main/logseq/graph_parser/util.cljs

@@ -57,6 +57,38 @@
    (let [c (count s)]
      (subs s (min c start) (min c end)))))
 
+(defn unquote-string
+  [v]
+  (string/trim (subs v 1 (dec (count v)))))
+
+(defn wrapped-by-quotes?
+  [v]
+  (and (string? v) (>= (count v) 2) (= "\"" (first v) (last v))))
+
+(defn parse-int
+  "Copy of frontend.util/parse-int. Don't want to couple to main app too much"
+  [x]
+  (if (string? x)
+    (js/parseInt x)
+    x))
+
+(defn safe-parse-int
+  "Copy of frontend.util/safe-parse-int. Don't want to couple to main app too much"
+  [x]
+  (let [result (parse-int x)]
+    (if (js/isNaN result)
+      nil
+      result)))
+
+(defn url?
+  [s]
+  (and (string? s)
+       (try
+         (js/URL. s)
+         true
+         (catch js/Error _e
+           false))))
+
 (defn json->clj
   [json-string]
   (-> json-string

+ 0 - 39
src/test/frontend/format/mldoc_test.cljs

@@ -1,39 +0,0 @@
-(ns frontend.format.mldoc-test
-  (:require [frontend.format.mldoc :as mldoc]
-            [cljs.test :refer [testing deftest are]]))
-
-(deftest test-link
-  (testing "non-link"
-    (are [x y] (= (mldoc/link? :markdown x) y)
-      "google.com" false))
-
-  (testing "plain links"
-    (are [x y] (= (mldoc/link? :markdown x) y)
-      "http://www.google.com" true
-      "http://google.com" true))
-
-  (testing "org links with labels"
-    (are [x y] (= (mldoc/link? :org x) y)
-      "[[http://www.google.com][google]]" true
-      "[[http://google.com][google]]" true
-      "[[https://www.google.com][google]]" true
-      "[[https://google.com][google]]" true))
-
-  (testing "org links without labels"
-    (are [x y] (= (mldoc/link? :org x) y)
-      "[[http://www.google.com]]" true
-      "[[https://www.google.com]]" true
-      "[[draws/2022-03-06-15-00-28.excalidraw]]" true
-      "[[assets/2022-03-06-15-00-28.pdf]]" true))
-
-  (testing "markdown links"
-    (are [x y] (= (mldoc/link? :markdown x) y)
-      "[google](http://www.google.com)" true
-      "[google](https://www.google.com)" true
-      "[[draws/2022-03-06-15-00-28.excalidraw]]" true
-      "![a pdf](assets/2022-03-06-15-00-28.pdf)" true))
-
-  ;; https://github.com/logseq/logseq/issues/4308
-  (testing "parsing links should be finished"
-    (are [x y] (= (mldoc/link? :markdown x) y)
-      "[YouTube](https://www.youtube.com/watch?v=-8ym7pyUs9gL) - [Vimeo](https://vimeo.com/677920303) {{youtube https://www.youtube.com/watch?v=-8ym7pyUs9g}}" true)))

+ 3 - 33
src/test/frontend/handler/repo_test.cljs

@@ -1,44 +1,14 @@
 (ns frontend.handler.repo-test
   (:require [cljs.test :refer [deftest use-fixtures is testing]]
-            [clojure.string :as string]
-            ["fs" :as fs]
-            ["child_process" :as child-process]
             [frontend.handler.repo :as repo-handler]
             [frontend.test.helper :as test-helper]
+            [frontend.test.docs-graph-helper :as docs-graph-helper]
             [datascript.core :as d]
             [frontend.db.conn :as conn]))
 
 (use-fixtures :each {:before test-helper/start-test-db!
                      :after test-helper/destroy-test-db!})
 
-(defn- slurp
-  "Like clojure.core/slurp"
-  [file]
-  (str (fs/readFileSync file)))
-
-(defn- sh
-  "Run shell cmd synchronously and print to inherited streams by default. Aims
-    to be similar to babashka.tasks/shell"
-  [cmd opts]
-  (child-process/spawnSync (first cmd)
-                           (clj->js (rest cmd))
-                           (clj->js (merge {:stdio "inherit"} opts))))
-
-(defn- build-graph-files
-  [dir]
-  (let [files (->> (str (.-stdout (sh ["git" "ls-files"]
-                                      {:cwd dir :stdio nil})))
-                   string/split-lines
-                   (filter #(re-find #"^(pages|journals)" %))
-                   (map #(str dir "/" %)))]
-    (mapv #(hash-map :file/path % :file/content (slurp %)) files)))
-
-(defn- clone-docs-repo-if-not-exists
-  [dir]
-  (when-not (.existsSync fs dir)
-    (sh ["git" "clone" "--depth" "1" "-b" "v0.6.7" "-c" "advice.detachedHead=false"
-         "https://github.com/logseq/docs" dir] {})))
-
 (defn- get-top-block-properties
   [db]
   (->> (d/q '[:find (pull ?b [*])
@@ -67,8 +37,8 @@
 ;; Integration test that test parsing a large graph like docs
 (deftest ^:integration parse-and-load-files-to-db
   (let [graph-dir "src/test/docs"
-        _ (clone-docs-repo-if-not-exists graph-dir)
-        files (build-graph-files graph-dir)
+        _ (docs-graph-helper/clone-docs-repo-if-not-exists graph-dir)
+        files (docs-graph-helper/build-graph-files graph-dir)
         _ (repo-handler/parse-files-and-load-to-db! test-helper/test-db files {:re-render? false})
         db (conn/get-db test-helper/test-db)]
 

+ 0 - 28
src/test/frontend/parser.cljs

@@ -1,28 +0,0 @@
-(ns frontend.parser
-  (:require [cljs.test :refer [is deftest]]
-            [frontend.format.mldoc :as mldoc :refer [->edn]]))
-
-(def md-config (mldoc/default-config :markdown))
-
-(deftest src-test
-  (is (=
-       (first (->edn "```
-: hello
-```" md-config))
-       [["Src"
-         {:lines [": hello" "\n"],
-          :pos_meta {:start_pos 4, :end_pos 12},
-          :full_content "```\n: hello\n```"}]
-        {:start_pos 0, :end_pos 15}])))
-
-(deftest name-definition-test
-  (is (=
-       (first (->edn "term
-: definition" md-config))
-       [["List"
-         [{:content [["Paragraph" [["Plain" "definition"]]]],
-           :items [],
-           :name [["Plain" "term"]],
-           :indent 0,
-           :ordered false}]]
-        {:start_pos 0, :end_pos 17}])))

+ 34 - 0
src/test/frontend/test/docs_graph_helper.cljs

@@ -0,0 +1,34 @@
+(ns ^:nbb-compatible frontend.test.docs-graph-helper
+  "Helper fns for running tests against docs graph"
+  (:require ["fs" :as fs]
+            ["child_process" :as child-process]
+            [clojure.string :as string]))
+
+
+(defn- slurp
+  "Like clojure.core/slurp"
+  [file]
+  (str (fs/readFileSync file)))
+
+(defn- sh
+  "Run shell cmd synchronously and print to inherited streams by default. Aims
+    to be similar to babashka.tasks/shell"
+  [cmd opts]
+  (child-process/spawnSync (first cmd)
+                           (clj->js (rest cmd))
+                           (clj->js (merge {:stdio "inherit"} opts))))
+
+(defn build-graph-files
+  [dir]
+  (let [files (->> (str (.-stdout (sh ["git" "ls-files"]
+                                      {:cwd dir :stdio nil})))
+                   string/split-lines
+                   (filter #(re-find #"^(pages|journals)" %))
+                   (map #(str dir "/" %)))]
+    (mapv #(hash-map :file/path % :file/content (slurp %)) files)))
+
+(defn clone-docs-repo-if-not-exists
+  [dir]
+  (when-not (.existsSync fs dir)
+    (sh ["git" "clone" "--depth" "1" "-b" "v0.6.7" "-c" "advice.detachedHead=false"
+         "https://github.com/logseq/docs" dir] {})))

+ 130 - 0
src/test/logseq/graph_parser/mldoc_test.cljs

@@ -0,0 +1,130 @@
+(ns logseq.graph-parser.mldoc-test
+  (:require [logseq.graph-parser.mldoc :as gp-mldoc]
+            [clojure.string :as string]
+            [frontend.test.docs-graph-helper :as docs-graph-helper]
+            [cljs.test :refer [testing deftest are is]]))
+
+(deftest test-link
+  (testing "non-link"
+    (are [x y] (= (gp-mldoc/link? :markdown x) y)
+      "google.com" false))
+
+  (testing "plain links"
+    (are [x y] (= (gp-mldoc/link? :markdown x) y)
+      "http://www.google.com" true
+      "http://google.com" true))
+
+  (testing "org links with labels"
+    (are [x y] (= (gp-mldoc/link? :org x) y)
+      "[[http://www.google.com][google]]" true
+      "[[http://google.com][google]]" true
+      "[[https://www.google.com][google]]" true
+      "[[https://google.com][google]]" true))
+
+  (testing "org links without labels"
+    (are [x y] (= (gp-mldoc/link? :org x) y)
+      "[[http://www.google.com]]" true
+      "[[https://www.google.com]]" true
+      "[[draws/2022-03-06-15-00-28.excalidraw]]" true
+      "[[assets/2022-03-06-15-00-28.pdf]]" true))
+
+  (testing "markdown links"
+    (are [x y] (= (gp-mldoc/link? :markdown x) y)
+      "[google](http://www.google.com)" true
+      "[google](https://www.google.com)" true
+      "[[draws/2022-03-06-15-00-28.excalidraw]]" true
+      "![a pdf](assets/2022-03-06-15-00-28.pdf)" true))
+
+  ;; https://github.com/logseq/logseq/issues/4308
+  (testing "parsing links should be finished"
+    (are [x y] (= (gp-mldoc/link? :markdown x) y)
+      "[YouTube](https://www.youtube.com/watch?v=-8ym7pyUs9gL) - [Vimeo](https://vimeo.com/677920303) {{youtube https://www.youtube.com/watch?v=-8ym7pyUs9g}}" true)))
+
+(def md-config (gp-mldoc/default-config :markdown))
+
+(deftest src-test
+  (is (= [["Src"
+           {:lines [": hello" "\n"],
+            :pos_meta {:start_pos 4, :end_pos 12},
+            :full_content "```\n: hello\n```"}]
+          {:start_pos 0, :end_pos 15}]
+         (first (gp-mldoc/->edn "```
+: hello
+```" md-config {})))
+      "Basic src example")
+
+  (is (= [["Src"
+            {:lines ["  hello" "\n" "  world" "\n"],
+             :pos_meta {:start_pos 7, :end_pos 25},
+             :full_content "```\nhello\nworld\n```"}]
+           {:start_pos 1, :end_pos 29}]
+         (second (gp-mldoc/->edn "
+  ```
+  hello
+  world
+  ```
+" md-config {})))
+      "Src example with leading whitespace"))
+
+(deftest properties-test
+  (are [x y] (= [["Properties" y] nil]
+                (first (gp-mldoc/->edn x md-config {})))
+
+       ;; comma separates values
+       "property:: foo, bar"
+       {:property #{"foo" "bar"}}
+
+       ;; alias property
+       "alias:: foo,, bar"
+       {:alias ["foo" "bar"]}
+
+       ;; tags property
+       "tags:: foo,bar,foo"
+       {:tags ["foo" "bar"]}
+
+       ;; title property
+       "title:: comma, is ok"
+       {:title "comma, is ok"}))
+
+(deftest name-definition-test
+  (is (= [["List"
+           [{:content [["Paragraph" [["Plain" "definition"]]]],
+             :items [],
+             :name [["Plain" "term"]],
+             :indent 0,
+             :ordered false}]]
+          {:start_pos 0, :end_pos 17}]
+         (first (gp-mldoc/->edn "term
+: definition" md-config {})))))
+
+(deftest ^:integration test->edn
+  (let [graph-dir "src/test/docs"
+        _ (docs-graph-helper/clone-docs-repo-if-not-exists graph-dir)
+        files (docs-graph-helper/build-graph-files graph-dir)
+        asts-by-file (->> files
+                          (map (fn [{:file/keys [path content]}]
+                                 (let [format (if (string/ends-with? path ".org")
+                                                :org :markdown)]
+                                   [path
+                                    (gp-mldoc/->edn content
+                                                    (gp-mldoc/default-config format)
+                                                    {})])))
+                          (into {}))]
+    (is (= {"CommentBlock" 1,
+            "Custom" 41,
+            "Displayed_Math" 1,
+            "Drawer" 1,
+            "Example" 20,
+            "Footnote_Definition" 2,
+            "Heading" 3493,
+            "Hiccup" 15,
+            "List" 36,
+            "Paragraph" 411,
+            "Properties" 104,
+            "Property_Drawer" 188,
+            "Quote" 9,
+            "Raw_Html" 12,
+            "Src" 56,
+            "Table" 4}
+           (->> asts-by-file (mapcat val) (map ffirst) frequencies))
+        "AST node type counts")))

+ 21 - 0
src/test/logseq/graph_parser/nbb_test_runner.cljs

@@ -0,0 +1,21 @@
+(ns logseq.graph-parser.nbb-test-runner
+  "Nbb tests for graph-parser"
+  (:require [cljs.test :as t]
+            [logseq.graph-parser.mldoc :as gp-mldoc]
+            [logseq.graph-parser.text :as text]
+            [logseq.graph-parser.text-test]
+            [logseq.graph-parser.mldoc-test]))
+
+(defmethod cljs.test/report [:cljs.test/default :end-run-tests] [m]
+  (when-not (cljs.test/successful? m)
+    (set! (.-exitCode js/process) 1)))
+
+;; run this function with: nbb-logseq -m logseq.test.nbb-test-runner/run-tests
+(defn run-tests []
+  ;; This hack is the same as the one in frontend.format. This has to be in an nbb only
+  ;; ns since alter-var-root doesn't exist in cljs and nbb doesn't support set! yet
+  #_:clj-kondo/ignore
+  (alter-var-root #'gp-mldoc/parse-property (constantly text/parse-property))
+  (t/run-tests 'logseq.graph-parser.mldoc-test
+               ;; TODO: Enable when https://github.com/babashka/nbb/issues/187 works
+               'logseq.graph-parser.text-test))

+ 27 - 36
src/test/frontend/text_test.cljs → src/test/logseq/graph_parser/text_test.cljs

@@ -1,30 +1,30 @@
-(ns frontend.text-test
+(ns logseq.graph-parser.text-test
   (:require [cljs.test :refer [are deftest testing]]
-            [frontend.text :as text]))
+            [logseq.graph-parser.text :as text]))
 
 (deftest test-get-page-name
   []
   (are [x y] (= (text/get-page-name x) y)
-    "[[page]]" "page"
-    "[[another page]]" "another page"
-    "[single bracket]" nil
-    "no brackets" nil
+         "[[page]]" "page"
+         "[[another page]]" "another page"
+         "[single bracket]" nil
+         "no brackets" nil
 
-    "[[another page]]" "another page"
-    "[[nested [[page]]]]" "nested [[page]]"
+         "[[another page]]" "another page"
+         "[[nested [[page]]]]" "nested [[page]]"
 
-    "[[file:./page.org][page]]" "page"
-    "[[file:./pages/page.org][page]]" "page"
+         "[[file:./page.org][page]]" "page"
+         "[[file:./pages/page.org][page]]" "page"
 
-    "[[file:./namespace.page.org][namespace/page]]" "namespace/page"
-    "[[file:./pages/namespace.page.org][namespace/page]]" "namespace/page"
-    "[[file:./pages/namespace.page.org][please don't change me]]" "namespace/page"
+         "[[file:./namespace.page.org][namespace/page]]" "namespace/page"
+         "[[file:./pages/namespace.page.org][namespace/page]]" "namespace/page"
+         "[[file:./pages/namespace.page.org][please don't change me]]" "namespace/page"
 
-    "[page](file:./page.md)" "page"
-    "[page](file:.pages/page.md)" "page"
+         "[page](file:./page.md)" "page"
+         "[page](file:.pages/page.md)" "page"
 
-    "[logseq/page](file:./logseq.page.md)" "logseq/page"
-    "[logseq/page](file:./pages/logseq.page.md)" "logseq/page"))
+         "[logseq/page](file:./logseq.page.md)" "logseq/page"
+         "[logseq/page](file:./pages/logseq.page.md)" "logseq/page"))
 
 (deftest page-ref?
   []
@@ -74,35 +74,26 @@
     "#tag1,#tag2" #{"tag1" "tag2"}
     "[[Jan 26th, 2021]], hello" #{"hello" "Jan 26th, 2021"}))
 
-(deftest extract-level-spaces
-  []
-  (testing "markdown"
-    (are [x y] (= (text/extract-level-spaces x :markdown) y)
-      "- foobar" "- "
-      "--   foobar" "-- "
-      "---------------------   foobar" "--------------------- "))
-  (testing "org mode"
-    (are [x y] (= (text/extract-level-spaces x :org) y)
-      "* foobar" "* "
-      "**   foobar" "** "
-      "*********************  foobar" "********************* ")))
+(def block-patterns
+  {:markdown "-"
+   :org "*"})
 
 (deftest remove-level-spaces
   []
   (testing "markdown"
-    (are [x y] (= (text/remove-level-spaces x :markdown true) y)
+    (are [x y] (= (text/remove-level-spaces x :markdown (block-patterns :markdown) true) y)
       "- foobar" "foobar"
       " - foobar" "foobar"))
   (testing "markdown without spaces between the `#` and title"
-    (are [x y] (= (text/remove-level-spaces x :markdown) y)
+    (are [x y] (= (text/remove-level-spaces x :markdown (block-patterns :markdown)) y)
       "-foobar" "foobar"))
   (testing "org"
-    (are [x y] (= (text/remove-level-spaces x :org true) y)
+    (are [x y] (= (text/remove-level-spaces x :org (block-patterns :org) true) y)
       "* foobar" "foobar"
       "**   foobar" "foobar"
       "*********************   foobar" "foobar"))
   (testing "org without spaces between the `#` and title"
-    (are [x y] (= (text/remove-level-spaces x :org) y)
+    (are [x y] (= (text/remove-level-spaces x :org (block-patterns :org)) y)
       "*foobar" "foobar"
       "**foobar" "foobar"
       "*********************foobar" "foobar")))
@@ -139,7 +130,7 @@
 
 (deftest test-parse-property
   (testing "parse-property"
-    (are [k v y] (= (text/parse-property k v) y)
+    (are [k v y] (= (text/parse-property k v {}) y)
       :tags "foo" "foo"
       :tags "foo, bar" #{"foo" "bar"}
       :tags "foo,bar" #{"foo" "bar"}
@@ -151,9 +142,9 @@
       :tags "[[foo [[bar]]]]" #{"foo [[bar]]"}
       :tags "[[foo [[bar]]]], baz" #{"baz" "foo [[bar]]"}))
   (testing "parse-property with quoted strings"
-    (are [k v y] (= (text/parse-property k v) y)
+    (are [k v y] (= (text/parse-property k v {}) y)
       :tags "\"foo, bar\"" "\"foo, bar\""
       :tags "\"[[foo]], [[bar]]\"" "\"[[foo]], [[bar]]\""
       :tags "baz, \"[[foo]], [[bar]]\"" #{"baz"})))
 
-#_(cljs.test/test-ns 'frontend.text-test)
+#_(cljs.test/test-ns 'logseq.graph-parser.text-test)

+ 9 - 4
yarn.lock

@@ -721,10 +721,10 @@
   resolved "https://registry.yarnpkg.com/@kanru/rage-wasm/-/rage-wasm-0.2.1.tgz#dd8fdd3133992c42bf68c0086d8cad40a13bc329"
   integrity sha512-sYi4F2mL6Mpcz7zbS4myasw11xLBEbgZkDMRVg9jNxTKt6Ct/LT7/vCHDmEzAFcPcPqixD5De6Ql3bJijAX0/w==
 
-"@logseq/nbb-logseq@^0.3.10":
-  version "0.3.10"
-  resolved "https://registry.yarnpkg.com/@logseq/nbb-logseq/-/nbb-logseq-0.3.10.tgz#74534f9d263eb2105a41143a55425d0910d43eb8"
-  integrity sha512-y7/VJ99WCoNpQMqQOOBobKzNrRz4IcG85HggAkIaUtNwJ/Adb5fA28ZKP5ttEpqWLXTbAyqbBObD5q/xJLLD8w==
+"@logseq/nbb-logseq@^0.3.99":
+  version "0.3.99"
+  resolved "https://registry.yarnpkg.com/@logseq/nbb-logseq/-/nbb-logseq-0.3.99.tgz#cf6c05c559963e4e0fb92f214a63228972ef87d3"
+  integrity sha512-Msa6Ck6wqt7sYGExQZgUT/uUG/z5jGaPlytVdgGkCrVogZb2yaChbWeMOCfCsCTi6kbHo15hC3aOeDAXcfnFzw==
   dependencies:
     import-meta-resolve "^1.1.1"
 
@@ -6817,6 +6817,11 @@ [email protected]:
     react-draggable "3.x"
     react-resizable "1.x"
 
[email protected]:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/react-icon-base/-/react-icon-base-2.1.0.tgz#a196e33fdf1e7aaa1fda3aefbb68bdad9e82a79d"
+  integrity sha1-oZbjP98eeqof2jrvu2i9rZ6Cp50=
+
 react-icon-base@^2.1.2:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/react-icon-base/-/react-icon-base-2.1.2.tgz#a17101dad9c1192652356096860a9ab43a0766c7"