enhance(cli): completion for properties and tags

This commit is contained in:
Gabriel Horner
2026-03-30 14:22:07 -04:00
parent e086d337b5
commit 05fdb7d2ad
4 changed files with 98 additions and 17 deletions

View File

@@ -18,10 +18,17 @@
{:name {:desc "Page name"
:complete :pages}})
(def ^:private remove-entity-spec
(def ^:private remove-tag-spec
{:id {:desc "Entity db/id"
:coerce :long}
:name {:desc "Entity name"}})
:name {:desc "Tag name"
:complete :tags}})
(def ^:private remove-property-spec
{:id {:desc "Entity db/id"
:coerce :long}
:name {:desc "Property name"
:complete :properties}})
(def entries
[(core/command-entry ["remove" "block"] :remove-block "Remove blocks" remove-block-spec
@@ -30,9 +37,9 @@
"logseq remove block --graph my-graph --uuid 7f0f4bb3-2e48-4b46-ae0f-18f52ef0f8be"]})
(core/command-entry ["remove" "page"] :remove-page "Remove page" remove-page-spec
{:examples ["logseq remove page --graph my-graph --name Home"]})
(core/command-entry ["remove" "tag"] :remove-tag "Remove tag" remove-entity-spec
(core/command-entry ["remove" "tag"] :remove-tag "Remove tag" remove-tag-spec
{:examples ["logseq remove tag --graph my-graph --name project"]})
(core/command-entry ["remove" "property"] :remove-property "Remove property" remove-entity-spec
(core/command-entry ["remove" "property"] :remove-property "Remove property" remove-property-spec
{:examples ["logseq remove property --graph my-graph --name owner"
"logseq remove property --graph my-graph --id 321"]})])

View File

@@ -53,12 +53,14 @@
(def ^:private upsert-tag-spec
{:id {:desc "Target tag db/id (forces update mode)"
:coerce :long}
:name {:desc "Tag name"}})
:name {:desc "Tag name"
:complete :tags}})
(def ^:private upsert-property-spec
{:id {:desc "Target property db/id (forces update mode)"
:coerce :long}
:name {:desc "Property name"}
:name {:desc "Property name"
:complete :properties}
:type {:desc "Property type"
:validate (into (set (map name db-property-type/user-built-in-property-types))
(set (map name db-property-type/user-allowed-internal-property-types)))}

View File

@@ -58,6 +58,8 @@
(and (not= coerce :boolean) (nil? multiple-values) (seq values)) (assoc :type :enum :values (sort values))
(and (not= coerce :boolean) (nil? multiple-values) (nil? values) (= complete :graphs)) (assoc :type :dynamic :complete :graphs)
(and (not= coerce :boolean) (nil? multiple-values) (nil? values) (= complete :pages)) (assoc :type :dynamic :complete :pages)
(and (not= coerce :boolean) (nil? multiple-values) (nil? values) (= complete :tags)) (assoc :type :dynamic :complete :tags)
(and (not= coerce :boolean) (nil? multiple-values) (nil? values) (= complete :properties)) (assoc :type :dynamic :complete :properties)
(and (not= coerce :boolean) (nil? multiple-values) (nil? values) (= complete :queries)) (assoc :type :dynamic :complete :queries)
(and (not= coerce :boolean) (nil? multiple-values) (nil? values) (= complete :file)) (assoc :type :file)
(and (not= coerce :boolean) (nil? multiple-values) (nil? values) (= complete :dir)) (assoc :type :dir)
@@ -140,6 +142,26 @@ _logseq_pages() {
fi
}
_logseq_tags() {
local graph
graph=$(_logseq_current_graph)
if [[ -n \"$graph\" ]]; then
local -a tags
tags=( ${(f)\"$(logseq list tag --graph \"$graph\" --output json 2>/dev/null | _logseq_json_names data items block/title)\"} )
compadd -a tags
fi
}
_logseq_properties() {
local graph
graph=$(_logseq_current_graph)
if [[ -n \"$graph\" ]]; then
local -a properties
properties=( ${(f)\"$(logseq list property --graph \"$graph\" --output json 2>/dev/null | _logseq_json_names data items block/title)\"} )
compadd -a properties
fi
}
_logseq_queries() {
local graph
graph=$(_logseq_current_graph)
@@ -232,6 +254,8 @@ _logseq_multi_values() {
(let [action (case complete
:graphs "{_logseq_graphs}"
:pages "{_logseq_pages}"
:tags "{_logseq_tags}"
:properties "{_logseq_properties}"
:queries "{_logseq_queries}")]
(if alias
[(str "'" excl long-opt "=[" desc* "]:value:" action "'")
@@ -508,6 +532,14 @@ _logseq_pages_bash() {
logseq list page --graph \"$1\" --output json 2>/dev/null | _logseq_json_names_bash data items block/title
}
_logseq_tags_bash() {
logseq list tag --graph \"$1\" --output json 2>/dev/null | _logseq_json_names_bash data items block/title
}
_logseq_properties_bash() {
logseq list property --graph \"$1\" --output json 2>/dev/null | _logseq_json_names_bash data items block/title
}
_logseq_queries_bash() {
logseq query list --graph \"$1\" --output json 2>/dev/null | _logseq_json_names_bash data queries name
}
@@ -722,6 +754,18 @@ _logseq_multi_values_bash() {
" graph=\"$(_logseq_current_graph_bash)\"\n"
" [[ -n \"$graph\" ]] && _logseq_compadd_lines \"$cur\" _logseq_pages_bash \"$graph\"\n"
" return ;;")
:tags
(str " " pattern ")\n"
" local graph\n"
" graph=\"$(_logseq_current_graph_bash)\"\n"
" [[ -n \"$graph\" ]] && _logseq_compadd_lines \"$cur\" _logseq_tags_bash \"$graph\"\n"
" return ;;")
:properties
(str " " pattern ")\n"
" local graph\n"
" graph=\"$(_logseq_current_graph_bash)\"\n"
" [[ -n \"$graph\" ]] && _logseq_compadd_lines \"$cur\" _logseq_properties_bash \"$graph\"\n"
" return ;;")
:queries
(str " " pattern ")\n"
" local graph\n"
@@ -814,6 +858,18 @@ _logseq_multi_values_bash() {
" graph=\"$(_logseq_current_graph_bash)\"\n"
" [[ -n \"$graph\" ]] && _logseq_compadd_lines \"$cur\" _logseq_pages_bash \"$graph\"\n"
" fi")
:tags
(str " if " condition "; then\n"
" local graph\n"
" graph=\"$(_logseq_current_graph_bash)\"\n"
" [[ -n \"$graph\" ]] && _logseq_compadd_lines \"$cur\" _logseq_tags_bash \"$graph\"\n"
" fi")
:properties
(str " if " condition "; then\n"
" local graph\n"
" graph=\"$(_logseq_current_graph_bash)\"\n"
" [[ -n \"$graph\" ]] && _logseq_compadd_lines \"$cur\" _logseq_properties_bash \"$graph\"\n"
" fi")
:queries
(str " if " condition "; then\n"
" local graph\n"

View File

@@ -72,6 +72,7 @@
(let [entries upsert-command/entries
block-entry (first (filter #(= :upsert-block (:command %)) entries))
page-entry (first (filter #(= :upsert-page (:command %)) entries))
tag-entry (first (filter #(= :upsert-tag (:command %)) entries))
property-entry (first (filter #(= :upsert-property (:command %)) entries))]
(testing "block-spec :pos has :validate set"
(is (= #{"first-child" "last-child" "sibling"}
@@ -84,9 +85,16 @@
(is (= :file (get-in block-entry [:spec :blocks-file :complete]))))
(testing "page-spec :page has :complete :pages"
(is (= :pages (get-in page-entry [:spec :page :complete]))))
(testing "tag-spec :name has :complete :tags"
(is (= :tags (get-in tag-entry [:spec :name :complete]))))
(testing "property-spec :name has :complete :properties"
(is (= :properties (get-in property-entry [:spec :name :complete]))))
(testing "property-spec :type has :validate set"
(is (= #{"default" "number" "date" "datetime" "checkbox" "url" "node" "json" "string"}
(get-in property-entry [:spec :type :validate]))))
(let [types (get-in property-entry [:spec :type :validate])]
(is (set? types))
(is (contains? types "default"))
(is (contains? types "node"))
(is (contains? types "checkbox"))))
(testing "property-spec :cardinality has :validate set"
(is (= #{"one" "many"}
(get-in property-entry [:spec :cardinality :validate]))))))
@@ -123,10 +131,10 @@
property-entry (first (filter #(= :remove-property (:command %)) entries))]
(testing "remove-page :name has :complete :pages"
(is (= :pages (get-in page-entry [:spec :name :complete]))))
(testing "remove-tag :name does NOT have :complete"
(is (nil? (get-in tag-entry [:spec :name :complete]))))
(testing "remove-property :name does NOT have :complete"
(is (nil? (get-in property-entry [:spec :name :complete]))))))
(testing "remove-tag :name has :complete :tags"
(is (= :tags (get-in tag-entry [:spec :name :complete]))))
(testing "remove-property :name has :complete :properties"
(is (= :properties (get-in property-entry [:spec :name :complete]))))))
(deftest test-search-spec-metadata
(let [entries search-command/entries]
@@ -408,12 +416,18 @@
(testing "remove page spec has :name with :complete :pages"
(let [rm-page (first (filter #(= :remove-page (:command %)) entries))]
(is (= :pages (get-in rm-page [:spec :name :complete])))))
(testing "upsert tag spec does NOT have :complete on :name"
(testing "upsert tag spec has :complete :tags on :name"
(let [tag (first (filter #(= :upsert-tag (:command %)) entries))]
(is (nil? (get-in tag [:spec :name :complete])))))
(testing "remove tag spec does NOT have :complete on :name"
(is (= :tags (get-in tag [:spec :name :complete])))))
(testing "remove tag spec has :complete :tags on :name"
(let [tag (first (filter #(= :remove-tag (:command %)) entries))]
(is (nil? (get-in tag [:spec :name :complete])))))))
(is (= :tags (get-in tag [:spec :name :complete])))))
(testing "upsert property spec has :complete :properties on :name"
(let [prop (first (filter #(= :upsert-property (:command %)) entries))]
(is (= :properties (get-in prop [:spec :name :complete])))))
(testing "remove property spec has :complete :properties on :name"
(let [prop (first (filter #(= :remove-property (:command %)) entries))]
(is (= :properties (get-in prop [:spec :name :complete])))))))
(deftest test-bash-context-dependent-type
(let [output (gen/generate-completions "bash" full-table)]
@@ -423,7 +437,9 @@
(is (string/includes? output "compgen -W 'edn sqlite'")))
(testing "--type completes with property types under upsert property context"
(is (string/includes? output "upsert' && \"$__subcmd\" == 'property'"))
(is (string/includes? output "compgen -W 'checkbox date datetime default json node number string url'")))
(is (string/includes? output "compgen -W '"))
(is (string/includes? output "default"))
(is (string/includes? output "node")))
(testing "--type does NOT have a context-free case (simple COMPREPLY after --type)"
;; --type should be in context-dependent if-blocks, not a simple case