mirror of
https://github.com/logseq/logseq.git
synced 2026-05-15 16:32:21 +00:00
Merge remote-tracking branch 'origin/feat/cliable' into feat/cliable
This commit is contained in:
@@ -19,13 +19,13 @@ Use `logseq` to inspect and edit graph entities, run Datascript queries, and con
|
||||
## Command groups (from `logseq --help`)
|
||||
|
||||
- Graph inspect/edit:
|
||||
- `list page`, `list tag`, `list property`
|
||||
- `upsert block`, `upsert page`, `upsert tag`, `upsert property`
|
||||
- `list node`, `list page`, `list tag`, `list property`, `list task`, `list asset`
|
||||
- `upsert block`, `upsert page`, `upsert tag`, `upsert property`, `upsert task`, `upsert assert`
|
||||
- `remove block`, `remove page`, `remove tag`, `remove property`
|
||||
- `query`, `query list`, `show`
|
||||
- Graph management: `graph list|create|switch|remove|validate|info|export|import`
|
||||
- Server management: `server list|status|start|stop|restart`
|
||||
- Diagnostics: `doctor`
|
||||
- `query`, `query list`, `show`, `search`
|
||||
- Graph management: `graph list|create|switch|remove|validate|info|export|import|backup`
|
||||
- Server management: `server list|cleanup|start|stop|restart`
|
||||
- Diagnostics: `doctor`, `debug`
|
||||
|
||||
## Global options
|
||||
|
||||
@@ -84,8 +84,4 @@ Use `logseq` to inspect and edit graph entities, run Datascript queries, and con
|
||||
- `upsert block` enters update mode when `--id` or `--uuid` is provided.
|
||||
- Always verify command flags with `logseq --help` and `logseq <...> --help` before execution.
|
||||
- If `logseq` reports that it doesn’t have read/write permission for data-dir, then add read/write permission for data-dir in the agent’s config.
|
||||
- In sandboxed environments, `graph create` may print a process-scan warning to stderr; if command status is `ok`, the graph is still created.
|
||||
|
||||
## References
|
||||
|
||||
- Built-in tags and properties: See `references/logseq-builtins.md` when you need canonical built-ins for `list ... --include-built-in` or for tag/property upsert fields.
|
||||
- In sandboxed environments, `graph create` may print a process-scan warning to stderr; if command status is `ok`, the graph is still created.
|
||||
@@ -406,9 +406,8 @@
|
||||
:close-db (fn [db] (.close db))
|
||||
:exec (fn [db sql-or-opts] (.exec db sql-or-opts))
|
||||
:transaction (fn [db f] (.transaction db f))
|
||||
:backup-db (fn [db path]
|
||||
(let [backup-fn (gobj/get db "backup")]
|
||||
(backup-fn path)))}
|
||||
:backup-db (fn [^js db path]
|
||||
(.backup db path))}
|
||||
:crypto {:save-secret-text! (fn [key text]
|
||||
((:set! kv) (secret-key key) text))
|
||||
:read-secret-text (fn [key]
|
||||
|
||||
@@ -69,22 +69,6 @@
|
||||
(transport/invoke config :thread-api/pull false
|
||||
[repo [:db/id :block/uuid :block/name :block/title] [:block/name page-name-lc]]))))))
|
||||
|
||||
;; TODO: Replace uses of this fn with ensure-page! when users are able to specify ids for contexts this
|
||||
;; is used in
|
||||
(defn- ensure-first-page!
|
||||
"Unlike ensure-page!, chooses the first random page and doesn't ensure the page is unique. Only use this
|
||||
when the user unable to specificy the specific page e.g. page refs in a block/title"
|
||||
[config repo page-name]
|
||||
(let [page-name-lc (common-util/page-name-sanity-lc page-name)]
|
||||
(p/let [page (transport/invoke config :thread-api/pull false
|
||||
[repo [:db/id :block/uuid :block/name :block/title] [:block/name page-name-lc]])]
|
||||
(if (:db/id page)
|
||||
page
|
||||
(p/let [_ (transport/invoke config :thread-api/apply-outliner-ops false
|
||||
[repo [[:create-page [page-name {}]]] {}])]
|
||||
(transport/invoke config :thread-api/pull false
|
||||
[repo [:db/id :block/uuid :block/name :block/title] [:block/name page-name-lc]]))))))
|
||||
|
||||
(defn pull-tag-by-name
|
||||
"Look up a tag by name, constrained to entities tagged with :logseq.class/Tag."
|
||||
[config repo tag-name selector]
|
||||
@@ -266,6 +250,10 @@
|
||||
(remove string/blank?)
|
||||
vec))
|
||||
|
||||
(defn- integer-string?
|
||||
[s]
|
||||
(boolean (re-matches #"-?\d+" s)))
|
||||
|
||||
(defn- partition-ref-values
|
||||
[refs]
|
||||
(reduce
|
||||
@@ -278,9 +266,12 @@
|
||||
(common-util/uuid-string? value)
|
||||
(update acc :uuid-refs conj value)
|
||||
|
||||
(integer-string? value)
|
||||
(update acc :id-refs conj value)
|
||||
|
||||
:else
|
||||
(update acc :page-refs conj value))))
|
||||
{:uuid-refs [] :page-refs []}
|
||||
{:uuid-refs [] :page-refs [] :id-refs []}
|
||||
refs))
|
||||
|
||||
(defn- resolve-page-ref-entities
|
||||
@@ -295,7 +286,7 @@
|
||||
page-refs)]
|
||||
(p/let [resolved (p/all
|
||||
(map (fn [[_ page-name]]
|
||||
(p/let [page (ensure-first-page! config repo page-name)
|
||||
(p/let [page (ensure-page! config repo page-name)
|
||||
page-uuid (:block/uuid page)]
|
||||
(when-not page-uuid
|
||||
(throw (ex-info "page not found"
|
||||
@@ -320,6 +311,26 @@
|
||||
:uuid uuid-ref})))))
|
||||
(distinct uuid-refs)))))
|
||||
|
||||
(defn- resolve-id-ref-entities
|
||||
"Resolve integer id refs (db/id values) to entity maps with :block/uuid and
|
||||
:block/title so they can be normalized like page-name refs."
|
||||
[config repo id-refs]
|
||||
(if (seq id-refs)
|
||||
(p/let [entities (p/all
|
||||
(map (fn [id-str]
|
||||
(let [id (parse-long id-str)]
|
||||
(p/let [entity (transport/invoke config :thread-api/pull false
|
||||
[repo [:db/id :block/uuid :block/title] id])]
|
||||
(when-not (:db/id entity)
|
||||
(throw (ex-info (str "id ref not found: " id-str)
|
||||
{:code :id-ref-not-found
|
||||
:id id-str})))
|
||||
{:block/uuid (:block/uuid entity)
|
||||
:block/title id-str})))
|
||||
(distinct id-refs)))]
|
||||
(vec entities))
|
||||
(p/resolved nil)))
|
||||
|
||||
(defn- normalize-block-title-refs
|
||||
[blocks refs]
|
||||
(mapv (fn update-block [block]
|
||||
@@ -754,7 +765,7 @@
|
||||
(and (string? value) (common-util/uuid-string? (string/trim value)))
|
||||
(resolve-entity-id config repo [:block/uuid (uuid (string/trim value))])
|
||||
(string? value)
|
||||
(p/let [page (ensure-first-page! config repo value)]
|
||||
(p/let [page (ensure-page! config repo value)]
|
||||
(or (:db/id page)
|
||||
(throw (ex-info "page not found" {:code :page-not-found :value value}))))
|
||||
:else
|
||||
@@ -1138,9 +1149,11 @@
|
||||
(-> (p/let [cfg (cli-server/ensure-server! config (:repo action))
|
||||
target-block-uuid (resolve-add-target cfg action)
|
||||
ref-values (collect-page-refs (:blocks action))
|
||||
{:keys [uuid-refs page-refs]} (partition-ref-values ref-values)
|
||||
{:keys [uuid-refs page-refs id-refs]} (partition-ref-values ref-values)
|
||||
_ (ensure-block-refs-exist! cfg (:repo action) uuid-refs)
|
||||
refs (or (resolve-page-ref-entities cfg (:repo action) page-refs) [])
|
||||
page-refs' (or (resolve-page-ref-entities cfg (:repo action) page-refs) [])
|
||||
id-refs' (or (resolve-id-ref-entities cfg (:repo action) id-refs) [])
|
||||
refs (into page-refs' id-refs')
|
||||
blocks (if (seq refs)
|
||||
(normalize-block-title-refs (:blocks action) refs)
|
||||
(:blocks action))
|
||||
|
||||
@@ -358,41 +358,30 @@ _logseq_multi_values() {
|
||||
[cmds]
|
||||
(str "_logseq_" (string/join "_" cmds)))
|
||||
|
||||
(defn- zsh-group-function
|
||||
"Generate a group dispatcher function.
|
||||
Handles groups that have both a root command (e.g. [\"query\"]) and
|
||||
subcommands (e.g. [\"query\" \"list\"])."
|
||||
[group-name subentries global-spec]
|
||||
(let [func-name (str "_logseq_" group-name)
|
||||
;; Separate root entry from subcommand entries
|
||||
root-entry (first (filter #(= 1 (count (:cmds %))) subentries))
|
||||
sub-entries (filter #(> (count (:cmds %)) 1) subentries)
|
||||
;; Root-level options (global + root command's own spec if present)
|
||||
root-spec (if root-entry
|
||||
(:spec root-entry)
|
||||
global-spec)
|
||||
(defn- zsh-subgroup-function
|
||||
"Generate a dispatcher for a 3-level subgroup (e.g. graph backup)."
|
||||
[parent-name subgroup-name sub-entries global-spec]
|
||||
(let [func-name (str "_logseq_" parent-name "_" subgroup-name)
|
||||
global-keys (set (keys global-spec))
|
||||
root-tokens (zsh-arguments-tokens root-spec global-keys)
|
||||
root-lines (string/join " \\\n " root-tokens)
|
||||
tokens (zsh-arguments-tokens global-spec global-keys)
|
||||
options-lines (string/join " \\\n " tokens)
|
||||
subcmds (->> sub-entries
|
||||
(mapv (fn [entry]
|
||||
(let [subcmd (second (:cmds entry))
|
||||
(let [sub-subcmd (nth (:cmds entry) 2)
|
||||
desc (or (:desc entry) "")]
|
||||
(str " '" subcmd ":" desc "'")))))
|
||||
(str " '" sub-subcmd ":" desc "'")))))
|
||||
subcmd-lines (string/join "\n" subcmds)
|
||||
dispatches (->> sub-entries
|
||||
(mapv (fn [entry]
|
||||
(let [subcmd (second (:cmds entry))
|
||||
sub-func (cmd->func-name (:cmds entry))]
|
||||
(str " " subcmd ") " sub-func " ;;"))))
|
||||
)
|
||||
dispatch-lines (string/join "\n" dispatches)]
|
||||
(let [sub-subcmd (nth (:cmds entry) 2)
|
||||
leaf-func (cmd->func-name (:cmds entry))]
|
||||
(str " " sub-subcmd ") " leaf-func " ;;")))))]
|
||||
(str func-name "() {\n"
|
||||
" local curcontext=\"$curcontext\" state line\n"
|
||||
" typeset -A opt_args\n"
|
||||
"\n"
|
||||
" _arguments -C -s \\\n"
|
||||
" " root-lines " \\\n"
|
||||
" " options-lines " \\\n"
|
||||
" '1:subcommand:->subcmd' \\\n"
|
||||
" '*::args:->args'\n"
|
||||
"\n"
|
||||
@@ -406,12 +395,90 @@ _logseq_multi_values() {
|
||||
" ;;\n"
|
||||
" args)\n"
|
||||
" case $line[1] in\n"
|
||||
dispatch-lines "\n"
|
||||
(string/join "\n" dispatches) "\n"
|
||||
" esac\n"
|
||||
" ;;\n"
|
||||
" esac\n"
|
||||
"}\n")))
|
||||
|
||||
(defn- zsh-group-function
|
||||
"Generate a group dispatcher function.
|
||||
Handles groups that have both a root command (e.g. [\"query\"]) and
|
||||
subcommands (e.g. [\"query\" \"list\"]).
|
||||
Also handles nested subgroups (e.g. [\"graph\" \"backup\" \"list\"])."
|
||||
[group-name subentries global-spec]
|
||||
(let [func-name (str "_logseq_" group-name)
|
||||
;; Separate root entry from subcommand entries
|
||||
root-entry (first (filter #(= 1 (count (:cmds %))) subentries))
|
||||
sub-entries (filter #(> (count (:cmds %)) 1) subentries)
|
||||
;; Partition into leaf subcmds (2 elements) and nested (3+ elements)
|
||||
leaf-entries (filter #(= 2 (count (:cmds %))) sub-entries)
|
||||
nested-entries (filter #(> (count (:cmds %)) 2) sub-entries)
|
||||
;; Group nested entries by their 2nd element to find subgroups
|
||||
nested-groups (group-by #(second (:cmds %)) nested-entries)
|
||||
;; Generate subgroup dispatcher functions
|
||||
subgroup-fns (when (seq nested-groups)
|
||||
(->> nested-groups
|
||||
(mapv (fn [[sg-name sg-entries]]
|
||||
(zsh-subgroup-function group-name sg-name sg-entries global-spec)))
|
||||
(string/join "\n")))
|
||||
;; Root-level options (global + root command's own spec if present)
|
||||
root-spec (if root-entry
|
||||
(:spec root-entry)
|
||||
global-spec)
|
||||
global-keys (set (keys global-spec))
|
||||
root-tokens (zsh-arguments-tokens root-spec global-keys)
|
||||
root-lines (string/join " \\\n " root-tokens)
|
||||
;; Build subcmd descriptions: leaf entries + subgroup names
|
||||
leaf-subcmds (->> leaf-entries
|
||||
(mapv (fn [entry]
|
||||
(let [subcmd (second (:cmds entry))
|
||||
desc (or (:desc entry) "")]
|
||||
(str " '" subcmd ":" desc "'")))))
|
||||
subgroup-subcmds (->> (keys nested-groups)
|
||||
sort
|
||||
(mapv (fn [sg-name]
|
||||
(str " '" sg-name ":" sg-name " commands'"))))
|
||||
subcmd-lines (string/join "\n" (concat leaf-subcmds subgroup-subcmds))
|
||||
;; Build dispatch: leaf entries dispatch to leaf fns, subgroups to subgroup fns
|
||||
leaf-dispatches (->> leaf-entries
|
||||
(mapv (fn [entry]
|
||||
(let [subcmd (second (:cmds entry))
|
||||
sub-func (cmd->func-name (:cmds entry))]
|
||||
(str " " subcmd ") " sub-func " ;;")))))
|
||||
subgroup-dispatches (->> (keys nested-groups)
|
||||
sort
|
||||
(mapv (fn [sg-name]
|
||||
(str " " sg-name ") _logseq_" group-name "_" sg-name " ;;"))))
|
||||
dispatch-lines (string/join "\n" (concat leaf-dispatches subgroup-dispatches))
|
||||
group-fn (str func-name "() {\n"
|
||||
" local curcontext=\"$curcontext\" state line\n"
|
||||
" typeset -A opt_args\n"
|
||||
"\n"
|
||||
" _arguments -C -s \\\n"
|
||||
" " root-lines " \\\n"
|
||||
" '1:subcommand:->subcmd' \\\n"
|
||||
" '*::args:->args'\n"
|
||||
"\n"
|
||||
" case $state in\n"
|
||||
" subcmd)\n"
|
||||
" local -a subcmds\n"
|
||||
" subcmds=(\n"
|
||||
subcmd-lines "\n"
|
||||
" )\n"
|
||||
" _describe 'subcommand' subcmds\n"
|
||||
" ;;\n"
|
||||
" args)\n"
|
||||
" case $line[1] in\n"
|
||||
dispatch-lines "\n"
|
||||
" esac\n"
|
||||
" ;;\n"
|
||||
" esac\n"
|
||||
"}\n")]
|
||||
(if subgroup-fns
|
||||
(str subgroup-fns "\n" group-fn)
|
||||
group-fn)))
|
||||
|
||||
(defn- zsh-toplevel-function
|
||||
"Generate the _logseq() root dispatcher."
|
||||
[table global-spec]
|
||||
@@ -434,8 +501,7 @@ _logseq_multi_values() {
|
||||
(= 1 (count (:cmds (first entries)))))
|
||||
(cmd->func-name (:cmds (first entries)))
|
||||
(str "_logseq_" g))]
|
||||
(str " " g ") " func " ;;"))))
|
||||
)
|
||||
(str " " g ") " func " ;;")))))
|
||||
dispatch-lines (string/join "\n" dispatches)
|
||||
global-keys (set (keys global-spec))
|
||||
global-tokens (zsh-arguments-tokens global-spec global-keys)
|
||||
@@ -686,7 +752,7 @@ _logseq_multi_values_bash() {
|
||||
[]
|
||||
"_logseq_cmd_and_subcmd() {
|
||||
local i skip=0
|
||||
__cmd='' __subcmd=''
|
||||
__cmd='' __subcmd='' __subsubcmd=''
|
||||
for (( i = 1; i < COMP_CWORD; i++ )); do
|
||||
local w=\"${COMP_WORDS[i]}\"
|
||||
if (( skip )); then skip=0; continue; fi
|
||||
@@ -698,6 +764,8 @@ _logseq_multi_values_bash() {
|
||||
__cmd=\"$w\"
|
||||
elif [[ -z \"$__subcmd\" ]]; then
|
||||
__subcmd=\"$w\"
|
||||
elif [[ -z \"$__subsubcmd\" ]]; then
|
||||
__subsubcmd=\"$w\"
|
||||
fi
|
||||
done
|
||||
}\n")
|
||||
@@ -739,31 +807,52 @@ _logseq_multi_values_bash() {
|
||||
(let [;; Root entry opts go at group level, sub-entries get case branches
|
||||
root-entry (first (filter #(= 1 (count (:cmds %))) entries))
|
||||
sub-entries (filter #(> (count (:cmds %)) 1) entries)
|
||||
leaf-subs (filter #(= 2 (count (:cmds %))) sub-entries)
|
||||
nested-subs (filter #(> (count (:cmds %)) 2) sub-entries)
|
||||
nested-groups (group-by #(second (:cmds %)) nested-subs)
|
||||
root-opts (when root-entry
|
||||
(let [cmd-spec (apply dissoc (:spec root-entry) (keys global-spec))]
|
||||
(->> (keys cmd-spec)
|
||||
(mapcat (fn [k] (bash-option-names k (get cmd-spec k))))
|
||||
(string/join " "))))
|
||||
sub-branches
|
||||
(->> sub-entries
|
||||
leaf-branches
|
||||
(->> leaf-subs
|
||||
(mapv (fn [entry]
|
||||
(let [subcmd (second (:cmds entry))
|
||||
cmd-spec (apply dissoc (:spec entry) (keys global-spec))
|
||||
cmd-opts (->> (keys cmd-spec)
|
||||
(mapcat (fn [k] (bash-option-names k (get cmd-spec k))))
|
||||
(string/join " "))]
|
||||
(str " " subcmd ") opts+=' " cmd-opts "' ;;"))))
|
||||
(string/join "\n"))]
|
||||
(str " " subcmd ") opts+=' " cmd-opts "' ;;")))))
|
||||
;; For nested subgroups, dispatch on subsubcmd
|
||||
nested-branches
|
||||
(->> (sort-by first nested-groups)
|
||||
(mapv (fn [[sg-name sg-entries]]
|
||||
(let [inner (->> sg-entries
|
||||
(mapv (fn [entry]
|
||||
(let [sub-subcmd (nth (:cmds entry) 2)
|
||||
cmd-spec (apply dissoc (:spec entry) (keys global-spec))
|
||||
cmd-opts (->> (keys cmd-spec)
|
||||
(mapcat (fn [k] (bash-option-names k (get cmd-spec k))))
|
||||
(string/join " "))]
|
||||
(str " " sub-subcmd ") opts+=' " cmd-opts "' ;;"))))
|
||||
(string/join "\n"))]
|
||||
(str " " sg-name ")\n"
|
||||
" case \"$subsubcmd\" in\n"
|
||||
inner "\n"
|
||||
" esac\n"
|
||||
" ;;")))))
|
||||
all-branches (concat leaf-branches nested-branches)
|
||||
sub-branches (string/join "\n" all-branches)]
|
||||
(str " " group-name ")\n"
|
||||
(when (seq root-opts)
|
||||
(str " opts+=' " root-opts "'\n"))
|
||||
" case \"$subcmd\" in\n"
|
||||
sub-branches "\n"
|
||||
" esac\n"
|
||||
" ;;"))))
|
||||
))]
|
||||
" ;;"))))))]
|
||||
(str "_logseq_opts_for() {\n"
|
||||
" local cmd=\"$1\" subcmd=\"$2\"\n"
|
||||
" local cmd=\"$1\" subcmd=\"$2\" subsubcmd=\"$3\"\n"
|
||||
" local opts=\"" global-str "\"\n"
|
||||
"\n"
|
||||
" case \"$cmd\" in\n"
|
||||
@@ -841,7 +930,9 @@ _logseq_multi_values_bash() {
|
||||
nil)))
|
||||
|
||||
(defn- bash-subcommand-cases
|
||||
"Generate subcommand completion for each group."
|
||||
"Generate subcommand completion for each group.
|
||||
Deduplicates subcmd names so that 3-level commands like
|
||||
[\"graph\" \"backup\" \"list\"] produce a single \"backup\" entry."
|
||||
[table]
|
||||
(let [groups (extract-groups table)
|
||||
cases (->> (sort-by first groups)
|
||||
@@ -850,12 +941,35 @@ _logseq_multi_values_bash() {
|
||||
(> (count (:cmds (first entries))) 1))
|
||||
(let [subcmds (->> entries
|
||||
(keep #(second (:cmds %)))
|
||||
distinct
|
||||
(string/join " "))]
|
||||
(when (seq subcmds)
|
||||
(str " " group-name ") COMPREPLY=( $(compgen -W '"
|
||||
subcmds "' -- \"$cur\") ) ;;")))))))]
|
||||
(string/join "\n" cases)))
|
||||
|
||||
(defn- bash-sub-subcommand-cases
|
||||
"Generate sub-subcommand completion for 3-level command groups.
|
||||
Produces cases like: graph:backup) COMPREPLY=( $(compgen -W 'list create ...' ...) ) ;;"
|
||||
[table]
|
||||
(let [groups (extract-groups table)
|
||||
cases (->> (sort-by first groups)
|
||||
(mapcat (fn [[group-name entries]]
|
||||
(let [nested (filter #(> (count (:cmds %)) 2) entries)
|
||||
nested-groups (group-by #(second (:cmds %)) nested)]
|
||||
(->> (sort-by first nested-groups)
|
||||
(mapv (fn [[sg-name sg-entries]]
|
||||
(let [sub-subcmds (->> sg-entries
|
||||
(map #(nth (:cmds %) 2))
|
||||
distinct
|
||||
(string/join " "))]
|
||||
(str " " group-name ":" sg-name
|
||||
") COMPREPLY=( $(compgen -W '"
|
||||
sub-subcmds "' -- \"$cur\") ) ;;")))))))))
|
||||
cases (remove nil? cases)]
|
||||
(when (seq cases)
|
||||
(string/join "\n" cases))))
|
||||
|
||||
(defn- bash-toplevel-commands
|
||||
"Get all top-level command names."
|
||||
[table]
|
||||
@@ -1003,6 +1117,8 @@ _logseq_multi_values_bash() {
|
||||
varied-cases (bash-varied-prev-cases table varied-keys global-keys)
|
||||
;; Subcommand completion
|
||||
subcmd-cases (bash-subcommand-cases table)
|
||||
;; Sub-subcommand completion for 3-level commands
|
||||
sub-subcmd-cases (bash-sub-subcommand-cases table)
|
||||
;; Top-level commands
|
||||
top-cmds (bash-toplevel-commands table)]
|
||||
(str "_logseq() {\n"
|
||||
@@ -1011,7 +1127,7 @@ _logseq_multi_values_bash() {
|
||||
" prev=\"${COMP_WORDS[COMP_CWORD-1]}\"\n"
|
||||
" COMPREPLY=()\n"
|
||||
"\n"
|
||||
" local __cmd __subcmd\n"
|
||||
" local __cmd __subcmd __subsubcmd\n"
|
||||
" _logseq_cmd_and_subcmd\n"
|
||||
"\n"
|
||||
" # --- Option value completion ---\n"
|
||||
@@ -1024,7 +1140,7 @@ _logseq_multi_values_bash() {
|
||||
" # --- Flag / positional completion ---\n"
|
||||
" if [[ \"$cur\" == -* ]]; then\n"
|
||||
" # shellcheck disable=SC2046\n"
|
||||
" COMPREPLY=( $(compgen -W \"$(_logseq_opts_for \"$__cmd\" \"$__subcmd\")\" -- \"$cur\") )\n"
|
||||
" COMPREPLY=( $(compgen -W \"$(_logseq_opts_for \"$__cmd\" \"$__subcmd\" \"$__subsubcmd\")\" -- \"$cur\") )\n"
|
||||
" return\n"
|
||||
" fi\n"
|
||||
"\n"
|
||||
@@ -1039,6 +1155,14 @@ _logseq_multi_values_bash() {
|
||||
" esac\n"
|
||||
" return\n"
|
||||
" fi\n"
|
||||
"\n"
|
||||
(when (seq sub-subcmd-cases)
|
||||
(str " if [[ -z \"$__subsubcmd\" ]]; then\n"
|
||||
" case \"$__cmd:$__subcmd\" in\n"
|
||||
sub-subcmd-cases "\n"
|
||||
" esac\n"
|
||||
" return\n"
|
||||
" fi\n"))
|
||||
"}\n")))
|
||||
|
||||
(defn generate-bash
|
||||
|
||||
@@ -47,6 +47,30 @@
|
||||
(is (= :add-id-resolution-failed (-> error ex-data :code)))
|
||||
(is (= [uuid-b] (-> error ex-data :missing-uuids))))))
|
||||
|
||||
(deftest test-partition-ref-values
|
||||
(testing "partitions uuid, integer id, and page-name refs"
|
||||
(let [result (#'add-command/partition-ref-values
|
||||
["some page"
|
||||
"550e8400-e29b-41d4-a716-446655440000"
|
||||
"101"
|
||||
" 42 "
|
||||
"another page"
|
||||
""
|
||||
" "])]
|
||||
(is (= ["550e8400-e29b-41d4-a716-446655440000"] (:uuid-refs result)))
|
||||
(is (= ["101" "42"] (:id-refs result)))
|
||||
(is (= ["some page" "another page"] (:page-refs result)))))
|
||||
|
||||
(testing "negative integers are recognized as id refs"
|
||||
(let [result (#'add-command/partition-ref-values ["-5"])]
|
||||
(is (= ["-5"] (:id-refs result)))
|
||||
(is (empty? (:page-refs result)))))
|
||||
|
||||
(testing "non-integer numbers stay as page refs"
|
||||
(let [result (#'add-command/partition-ref-values ["3.14" "1e5"])]
|
||||
(is (empty? (:id-refs result)))
|
||||
(is (= ["3.14" "1e5"] (:page-refs result))))))
|
||||
|
||||
(def ^:private mock-transport-invoke
|
||||
(fn [_ method _ args]
|
||||
(case method
|
||||
@@ -77,6 +101,26 @@
|
||||
|
||||
(p/rejected (ex-info "unexpected method" {:method method :args args})))))
|
||||
|
||||
(deftest test-resolve-id-ref-entities
|
||||
(testing "resolves integer id refs to uuid+title maps"
|
||||
(async done
|
||||
(let [page-uuid (random-uuid)
|
||||
mock-invoke (fn [_ _ _ args]
|
||||
(let [[_ _ lookup] args]
|
||||
(p/resolved
|
||||
(cond
|
||||
(= lookup 101)
|
||||
{:db/id 101 :block/uuid page-uuid :block/title "My Page"}
|
||||
:else {}))))]
|
||||
(-> (p/with-redefs [transport/invoke mock-invoke]
|
||||
(p/let [result (#'add-command/resolve-id-ref-entities {} "demo" ["101"])]
|
||||
(is (= 1 (count result)))
|
||||
(is (= page-uuid (:block/uuid (first result))))
|
||||
(is (= "101" (:block/title (first result)))
|
||||
"title is the original id string for title-ref->id-ref replacement")))
|
||||
(p/catch (fn [e] (is false (str "unexpected error: " e))))
|
||||
(p/finally done))))))
|
||||
|
||||
(deftest test-resolve-tags-accepts-valid-tag
|
||||
(async done
|
||||
(-> (p/with-redefs [transport/invoke mock-transport-invoke]
|
||||
|
||||
@@ -563,6 +563,45 @@
|
||||
(testing "uniform options like --cardinality are not varied"
|
||||
(is (not (contains? varied :cardinality))))))
|
||||
|
||||
(deftest test-zsh-nested-subcommand-completion
|
||||
(let [output (gen/generate-completions "zsh" full-table)]
|
||||
(testing "zsh generates subgroup dispatcher for graph backup"
|
||||
(is (string/includes? output "_logseq_graph_backup()"))
|
||||
(is (re-find #"(?s)_logseq_graph_backup\(\).*?'list:" output)
|
||||
"graph backup dispatcher lists 'list' subcommand")
|
||||
(is (re-find #"(?s)_logseq_graph_backup\(\).*?'create:" output)
|
||||
"graph backup dispatcher lists 'create' subcommand")
|
||||
(is (re-find #"(?s)_logseq_graph_backup\(\).*?'restore:" output)
|
||||
"graph backup dispatcher lists 'restore' subcommand")
|
||||
(is (re-find #"(?s)_logseq_graph_backup\(\).*?'remove:" output)
|
||||
"graph backup dispatcher lists 'remove' subcommand"))
|
||||
(testing "graph dispatcher dispatches backup to subgroup function"
|
||||
(is (re-find #"(?s)_logseq_graph\(\).*?backup\) _logseq_graph_backup" output)))
|
||||
(testing "graph backup remove leaf has its own function with --src option"
|
||||
(is (string/includes? output "_logseq_graph_backup_remove()"))
|
||||
(is (re-find #"(?s)_logseq_graph_backup\(\).*?remove\) _logseq_graph_backup_remove" output)))))
|
||||
|
||||
(deftest test-bash-nested-subcommand-completion
|
||||
(let [output (gen/generate-completions "bash" full-table)]
|
||||
(testing "bash subcmd completion for graph includes backup (deduplicated)"
|
||||
(is (re-find #"graph\) COMPREPLY=.*backup" output))
|
||||
;; backup should appear only once, not repeated per sub-subcommand
|
||||
(let [graph-case (re-find #"graph\) COMPREPLY=\( \$\(compgen -W '([^']*)'" output)
|
||||
subcmds (when graph-case (string/split (second graph-case) #" "))]
|
||||
(is (= (count (filter #(= "backup" %) subcmds)) 1)
|
||||
"backup appears exactly once in graph subcmd list")))
|
||||
(testing "bash sub-subcommand dispatch for graph:backup"
|
||||
(is (string/includes? output "graph:backup)")))
|
||||
(testing "bash sub-subcommand completions for graph backup include list, create, restore, remove"
|
||||
(let [case-match (re-find #"graph:backup\) COMPREPLY=\( \$\(compgen -W '([^']*)'" output)]
|
||||
(is (some? case-match) "graph:backup case exists")
|
||||
(when case-match
|
||||
(let [sub-subcmds (set (string/split (second case-match) #" "))]
|
||||
(is (contains? sub-subcmds "list"))
|
||||
(is (contains? sub-subcmds "create"))
|
||||
(is (contains? sub-subcmds "restore"))
|
||||
(is (contains? sub-subcmds "remove"))))))))
|
||||
|
||||
(deftest test-e2e-generated-header
|
||||
(testing "zsh output includes do-not-edit header"
|
||||
(let [output (gen/generate-completions "zsh" full-table)]
|
||||
|
||||
Reference in New Issue
Block a user