Pārlūkot izejas kodu

feat: add properties as linked references

Tienson Qin 3 gadi atpakaļ
vecāks
revīzija
49acb54178

+ 36 - 23
deps/graph-parser/src/logseq/graph_parser/block.cljs

@@ -139,26 +139,47 @@
    (vector? block)
    (= "Timestamp" (first block))))
 
-;; TODO: we should move this to mldoc
-(defn extract-properties
-  [format properties user-config]
-  (when (seq properties)
-    (let [properties (seq properties)
-          page-refs (->>
+(defn- get-page-ref-names-from-properties
+  [format properties]
+  (let [page-refs (->>
                      properties
                      (remove (fn [[k _]]
                                (contains? #{:background-color :background_color} (keyword k))))
                      (map last)
                      (map (fn [v]
-                            (when (and (string? v)
-                                       (not (gp-mldoc/link? format v)))
+                            (cond
+                              (and (string? 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)
                                   (map text/page-ref-un-brackets! result)
-                                  [])))))
-                     (apply concat)
-                     (remove string/blank?))
+                                  []))
+
+                              (coll? v)
+                              (map (fn [s]
+                                     (when-not (and (string? v)
+                                                    (gp-mldoc/link? format v))
+                                       (text/page-ref-un-brackets! s))) v)
+
+                              :else
+                              nil)))
+                     (apply concat))
+        property-keys-page-refs (some->> properties
+                                         (map (comp name first))
+                                         (remove string/blank?)
+                                         (distinct))]
+    (->> (concat page-refs property-keys-page-refs)
+         (remove string/blank?)
+         distinct)))
+
+;; TODO: we should move this to mldoc
+(defn extract-properties
+  [format properties user-config]
+  (when (seq properties)
+    (let [properties (seq properties)
+          properties (into {} properties)
+          page-refs (get-page-ref-names-from-properties format properties)
           properties (->> properties
                           (map (fn [[k v]]
                                  (let [k (-> (string/lower-case (name k))
@@ -437,17 +458,8 @@
       (d/squuid)))
 
 (defn get-page-refs-from-properties
-  [properties db date-formatter]
-  (let [page-refs (mapcat (fn [v] (cond
-                                   (coll? v)
-                                   v
-
-                                   (text/page-ref? v)
-                                   [(text/page-ref-un-brackets! v)]
-
-                                   :else
-                                   nil)) (vals properties))
-        page-refs (remove string/blank? page-refs)]
+  [format properties db date-formatter]
+  (let [page-refs (get-page-ref-names-from-properties format properties)]
     (map (fn [page] (page-name->map page true db true date-formatter)) page-refs)))
 
 (defn- with-page-block-refs
@@ -461,6 +473,7 @@
 (defn- with-pre-block-if-exists
   [blocks body pre-block-properties encoded-content {:keys [supported-formats db date-formatter]}]
   (let [first-block (first blocks)
+        format (or (:block/format first-block) :markdown)
         first-block-start-pos (get-in first-block [:block/meta :start_pos])
 
         ;; Add pre-block
@@ -471,7 +484,7 @@
                    (let [content (utf8/substring encoded-content 0 first-block-start-pos)
                          {:keys [properties properties-order]} pre-block-properties
                          id (get-custom-id-or-new-id {:properties properties})
-                         property-refs (->> (get-page-refs-from-properties properties db date-formatter)
+                         property-refs (->> (get-page-refs-from-properties format properties db date-formatter)
                                             (map :block/original-name))
                          block {:uuid id
                                 :content content

+ 1 - 1
deps/graph-parser/src/logseq/graph_parser/test/docs_graph_helper.cljs

@@ -142,7 +142,7 @@
   ;; only increase over time as the docs graph rarely has deletions
   (testing "Counts"
     (is (= 211 (count files)) "Correct file count")
-    (is (= 40945 (count (d/datoms db :eavt))) "Correct datoms count")
+    (is (= 44212 (count (d/datoms db :eavt))) "Correct datoms count")
 
     (is (= 3600
            (ffirst

+ 1 - 0
src/main/frontend/commands.cljs

@@ -24,6 +24,7 @@
 ;; TODO: move to frontend.handler.editor.commands
 
 (defonce angle-bracket "<")
+(defonce colon ":")
 (defonce *current-command (atom nil))
 
 (def query-doc

+ 3 - 1
src/main/frontend/components/block.cljs

@@ -1805,7 +1805,9 @@
   [config block k v]
   (let [date (and (= k :date) (date/get-locale-string (str v)))]
     [:div
-     [:span.page-property-key.font-medium (name k)]
+     [:a.page-property-key
+      {:href (rfe/href :page {:name (name k)})}
+      (name k)]
      [:span.mr-1 ":"]
      (cond
        (int? v)

+ 6 - 1
src/main/frontend/components/block.css

@@ -188,7 +188,7 @@
 .block-left-menu {
     background-color: var(--ls-secondary-background-color);
     background: linear-gradient(90deg, var(--ls-primary-background-color) 0%, var(--ls-secondary-background-color) 100%);
-    
+
     .commands-button {
         overflow: hidden;
         max-width: 40px;
@@ -613,9 +613,14 @@ a.cloze-revealed {
 }
 
 .page-property-key {
+  @apply font-medium;
   color: var(--ls-secondary-text-color);
 }
 
+.page-property-key:hover {
+    background-color: var(--ls-selection-background-color);
+}
+
 .block-parents a {
   color: var(--ls-primary-text-color);
 }

+ 28 - 0
src/main/frontend/components/editor.cljs

@@ -229,6 +229,30 @@
                          template)
           :class       "black"})))))
 
+(rum/defc property-search < rum/reactive
+  {:will-unmount (fn [state] (reset! editor-handler/*selected-text nil) state)}
+  [id]
+  (let [pos (:pos (:pos (state/get-editor-action-data)))
+        input (gdom/getElement id)]
+    (when input
+      (let [current-pos (cursor/pos input)
+            edit-content (state/sub [:editor/content id])
+            q (or
+               (when (>= (count edit-content) current-pos)
+                 (subs edit-content pos current-pos))
+               "")
+            matched-properties (editor-handler/get-matched-properties q)
+            non-exist-handler (fn [_state]
+                                ((editor-handler/property-on-chosen-handler id q) nil))]
+        (ui/auto-complete
+         matched-properties
+         {:on-chosen (editor-handler/property-on-chosen-handler id q)
+          :on-enter non-exist-handler
+          :empty-placeholder [:div.text-gray-500.px-4.py-2.text-sm "Search for a property"]
+          :header [:div.px-4.py-2.text-sm.font-medium "Matched properties: "]
+          :item-render (fn [property] property)
+          :class       "black"})))))
+
 (rum/defcs input < rum/reactive
   (rum/local {} ::input-value)
   (mixins/event-mixin
@@ -488,6 +512,10 @@
       (= :template-search action)
       (animated-modal "template-search" (template-search id format) true)
 
+      (= :property-search action)
+      (animated-modal "property-search" (property-search id) true)
+
+
       (= :datepicker action)
       (animated-modal "date-picker" (datetime-comp/date-picker id format nil) false)
 

+ 15 - 0
src/main/frontend/db/model.cljs

@@ -1341,6 +1341,21 @@
                 [(get m :template) e]))
          (into {}))))
 
+(defn get-all-properties
+  []
+  (let [properties (d/q
+                     '[:find [?p ...]
+                       :where
+                       [_ :block/properties ?p]]
+                     (conn/get-db))
+        properties (remove (fn [m] (or (empty? m)
+                                       (and (= 1 (count m))
+                                            (= :id (first (keys m)))))) properties)]
+    (->> (map keys properties)
+         (apply concat)
+         distinct
+         sort)))
+
 (defn get-template-by-name
   [name]
   (when (string? name)

+ 30 - 2
src/main/frontend/handler/editor.cljs

@@ -661,7 +661,7 @@
 (defn properties-block
   [properties format page]
   (let [content (property/insert-properties format "" properties)
-        refs (gp-block/get-page-refs-from-properties properties
+        refs (gp-block/get-page-refs-from-properties format properties
                                                      (db/get-db (state/get-current-repo))
                                                      (state/get-date-formatter))]
     {:block/pre-block? true
@@ -1616,6 +1616,10 @@
   [q]
   (search/template-search q))
 
+(defn get-matched-properties
+  [q]
+  (search/property-search q))
+
 (defn get-matched-commands
   [input]
   (try
@@ -1825,7 +1829,9 @@
 (defn handle-last-input []
   (let [input           (state/get-input)
         pos             (cursor/pos input)
-        last-input-char (util/nth-safe (.-value input) (dec pos))]
+        last-input-char (util/nth-safe (.-value input) (dec pos))
+        last-prev-input-char (util/nth-safe (.-value input) (dec (dec pos)))
+        prev-prev-input-char (util/nth-safe (.-value input) (- pos 3))]
 
     ;; TODO: is it cross-browser compatible?
     ;; (not= (gobj/get native-e "inputType") "insertFromPaste")
@@ -1842,6 +1848,16 @@
         (commands/reinit-matched-block-commands!)
         (state/set-editor-show-block-commands!))
 
+      (and (= last-input-char last-prev-input-char commands/colon)
+           (or (nil? prev-prev-input-char)
+               (= prev-prev-input-char "\n")))
+      (do
+        (state/set-editor-action-data! {:pos (cursor/get-caret-pos input)})
+        (state/set-editor-show-property-search! true))
+
+      (and (= last-input-char commands/colon) (= :property-search (state/get-editor-action)))
+      (state/clear-editor-action!)
+
       :else
       nil)))
 
@@ -2041,6 +2057,18 @@
     (insert-template! element-id db-id
                       {:replace-empty-target? true})))
 
+(defn property-on-chosen-handler
+  [element-id q]
+  (fn [property]
+    (when-let [input (gdom/getElement element-id)]
+      (let [value (.-value input)
+            pos (util/get-selection-start input)
+            last-index (string/last-index-of (subs value 0 pos) "::")
+            last-pattern (subs value last-index pos)]
+        (commands/insert! element-id (str (or property q) ":: ")
+                          {:last-pattern last-pattern})
+        (state/clear-editor-action!)))))
+
 (defn parent-is-page?
   [{{:block/keys [parent page]} :data :as node}]
   {:pre [(tree/satisfied-inode? node)]}

+ 1 - 1
src/main/frontend/handler/page.cljs

@@ -73,7 +73,7 @@
    (let [p (common-handler/get-page-default-properties title)
          ps (merge p properties)
          content (page-property/insert-properties format "" ps)
-         refs (gp-block/get-page-refs-from-properties properties
+         refs (gp-block/get-page-refs-from-properties format properties
                                                       (db/get-db (state/get-current-repo))
                                                       (state/get-date-formatter))]
      {:block/uuid (db/new-block-id)

+ 13 - 0
src/main/frontend/search.cljs

@@ -3,6 +3,7 @@
             [clojure.string :as string]
             [logseq.graph-parser.config :as gp-config]
             [frontend.db :as db]
+            [frontend.db.model :as db-model]
             [frontend.regex :as regex]
             [frontend.search.browser :as search-browser]
             [frontend.search.db :as search-db :refer [indices]]
@@ -161,6 +162,18 @@
        (let [result (fuzzy-search (keys templates) q :limit limit)]
          (vec (select-keys templates result)))))))
 
+(defn property-search
+  ([q]
+   (property-search q 10))
+  ([q limit]
+   (let [q (clean-str q)
+         properties (map name (db-model/get-all-properties))]
+     (when (seq properties)
+       (if (string/blank? q)
+         properties
+         (let [result (fuzzy-search properties q :limit limit)]
+          (vec result)))))))
+
 (defn sync-search-indice!
   [repo tx-report]
   (let [data (:tx-data tx-report)

+ 6 - 0
src/main/frontend/state.cljs

@@ -629,6 +629,12 @@
 (defn get-editor-show-template-search?
   []
   (= (get-editor-action) :template-search))
+(defn set-editor-show-property-search!
+  [value]
+  (set-editor-action! (when value :property-search)))
+(defn get-editor-show-property-search?
+  []
+  (= (get-editor-action) :property-search))
 (defn set-editor-show-date-picker!
   [value]
   (set-editor-action! (when value :datepicker)))

+ 3 - 1
src/main/frontend/ui.cljs

@@ -397,11 +397,13 @@
            get-group-name
            empty-placeholder
            item-render
-           class]}]
+           class
+           header]}]
   (let [current-idx (get state ::current-idx)]
     [:div#ui__ac {:class class}
      (if (seq matched)
        [:div#ui__ac-inner.hide-scrollbar
+        (when header header)
         (for [[idx item] (medley/indexed matched)]
           [:<>
            {:key idx}

+ 1 - 1
src/test/frontend/db/query_dsl_test.cljs

@@ -394,7 +394,7 @@ tags: other
                 (dsl-query "(or [[tag2]] [[page 3]])")))
         "OR query with nonexistent page should return meaningful results")
 
-    (is (= ["foo:: bar\n" "b1 [[page 1]] #tag2" "b3"]
+    (is (= ["b1 [[page 1]] #tag2" "foo:: bar\n" "b3"]
            (->> (dsl-query "(not [[page 2]])")
                 ;; Only filter to page1 to get meaningful results
                 (filter #(= "page1" (get-in % [:block/page :block/name])))