diff --git a/src/main/logseq/cli/command/completion.cljs b/src/main/logseq/cli/command/completion.cljs index 2c32e77d41..4ca0be789b 100644 --- a/src/main/logseq/cli/command/completion.cljs +++ b/src/main/logseq/cli/command/completion.cljs @@ -26,4 +26,6 @@ [(core/command-entry ["completion"] :completion "Generate shell completion script" completion-spec - {:long-desc long-desc})]) + {:long-desc long-desc + :examples ["logseq completion zsh" + "logseq completion bash"]})]) diff --git a/src/main/logseq/cli/command/core.cljs b/src/main/logseq/cli/command/core.cljs index 932792c024..d6d10bcc39 100644 --- a/src/main/logseq/cli/command/core.cljs +++ b/src/main/logseq/cli/command/core.cljs @@ -39,12 +39,13 @@ (defn command-entry ([cmds command desc spec] (command-entry cmds command desc spec nil)) - ([cmds command desc spec {:keys [long-desc]}] + ([cmds command desc spec {:keys [long-desc examples]}] (let [spec* (merge-spec spec)] {:cmds cmds :command command :desc desc :long-desc long-desc + :examples examples :spec spec* :restrict true :fn (fn [{:keys [opts args]}] @@ -52,6 +53,7 @@ :cmds cmds :spec spec* :long-desc long-desc + :examples examples :opts opts :args args})}))) @@ -127,9 +129,21 @@ (str "Command " (style/bold "options") ":") " See `logseq --help`"]))) +(defn- format-examples + [examples] + (->> examples + (keep (fn [example] + (let [line (some-> example str string/trim)] + (when (seq line) + line)))) + (take 5) + (map #(str " " %)) + (string/join "\n"))) + (defn command-summary - [{:keys [cmds spec long-desc]}] - (let [command-spec (apply dissoc spec (keys global-spec*))] + [{:keys [cmds spec long-desc examples]}] + (let [command-spec (apply dissoc spec (keys global-spec*)) + formatted-examples (format-examples examples)] (string/join "\n" (cond-> [(str "Usage: logseq " (command-usage cmds spec))] (seq long-desc) (conj "" long-desc) @@ -138,7 +152,10 @@ (format-opts global-spec*) "" (str "Command " (style/bold "options") ":") - (format-opts command-spec)))))) + (format-opts command-spec)) + (seq formatted-examples) (conj "" + (str (style/bold "Examples") ":") + formatted-examples))))) (defn normalize-opts [opts] diff --git a/src/main/logseq/cli/command/doctor.cljs b/src/main/logseq/cli/command/doctor.cljs index 0a5bad921f..fd5c987de7 100644 --- a/src/main/logseq/cli/command/doctor.cljs +++ b/src/main/logseq/cli/command/doctor.cljs @@ -13,7 +13,8 @@ :doctor "Run runtime diagnostics" {:dev-script {:desc "Check static/db-worker-node.js instead of bundled dist runtime" - :coerce :boolean}})]) + :coerce :boolean}} + {:examples ["logseq doctor --dev-script"]})]) (defn build-action ([] diff --git a/src/main/logseq/cli/command/graph.cljs b/src/main/logseq/cli/command/graph.cljs index 8b211a3ec3..9fc972aee7 100644 --- a/src/main/logseq/cli/command/graph.cljs +++ b/src/main/logseq/cli/command/graph.cljs @@ -28,13 +28,21 @@ (def entries [(core/command-entry ["graph" "list"] :graph-list "List graphs" {}) - (core/command-entry ["graph" "create"] :graph-create "Create graph" {}) - (core/command-entry ["graph" "switch"] :graph-switch "Switch current graph" {}) - (core/command-entry ["graph" "remove"] :graph-remove "Remove graph" {}) - (core/command-entry ["graph" "validate"] :graph-validate "Validate graph" graph-validate-spec) - (core/command-entry ["graph" "info"] :graph-info "Graph metadata" {}) - (core/command-entry ["graph" "export"] :graph-export "Export graph" graph-export-spec) - (core/command-entry ["graph" "import"] :graph-import "Import graph" graph-import-spec)]) + (core/command-entry ["graph" "create"] :graph-create "Create graph" {} + {:examples ["logseq graph create --graph my-graph"]}) + (core/command-entry ["graph" "switch"] :graph-switch "Switch current graph" {} + {:examples ["logseq graph switch --graph my-graph"]}) + (core/command-entry ["graph" "remove"] :graph-remove "Remove graph" {} + {:examples ["logseq graph remove --graph my-graph"]}) + (core/command-entry ["graph" "validate"] :graph-validate "Validate graph" graph-validate-spec + {:examples ["logseq graph validate --graph my-graph" + "logseq graph validate --graph my-graph --fix"]}) + (core/command-entry ["graph" "info"] :graph-info "Graph metadata" {} + {:examples ["logseq graph info --graph my-graph"]}) + (core/command-entry ["graph" "export"] :graph-export "Export graph" graph-export-spec + {:examples ["logseq graph export --graph my-graph --type edn --file /tmp/my-graph.edn"]}) + (core/command-entry ["graph" "import"] :graph-import "Import graph" graph-import-spec + {:examples ["logseq graph import --graph my-graph --type edn --input /tmp/my-graph.edn"]})]) (def ^:private import-export-types* #{"edn" "sqlite"}) diff --git a/src/main/logseq/cli/command/list.cljs b/src/main/logseq/cli/command/list.cljs index aadf72d0ae..5d8770c050 100644 --- a/src/main/logseq/cli/command/list.cljs +++ b/src/main/logseq/cli/command/list.cljs @@ -64,9 +64,13 @@ :fields {:desc "Select output fields (comma separated)"}})) (def entries - [(core/command-entry ["list" "page"] :list-page "List pages" list-page-spec) - (core/command-entry ["list" "tag"] :list-tag "List tags" list-tag-spec) - (core/command-entry ["list" "property"] :list-property "List properties" list-property-spec)]) + [(core/command-entry ["list" "page"] :list-page "List pages" list-page-spec + {:examples ["logseq list page --graph my-graph" + "logseq list page --graph my-graph --journal-only --limit 20"]}) + (core/command-entry ["list" "tag"] :list-tag "List tags" list-tag-spec + {:examples ["logseq list tag --graph my-graph --with-properties"]}) + (core/command-entry ["list" "property"] :list-property "List properties" list-property-spec + {:examples ["logseq list property --graph my-graph --with-type"]})]) (defn invalid-options? [command opts] diff --git a/src/main/logseq/cli/command/query.cljs b/src/main/logseq/cli/command/query.cljs index 3915315699..9d6f15f619 100644 --- a/src/main/logseq/cli/command/query.cljs +++ b/src/main/logseq/cli/command/query.cljs @@ -17,7 +17,9 @@ {}) (def entries - [(core/command-entry ["query"] :query "Run a Datascript query" query-spec) + [(core/command-entry ["query"] :query "Run a Datascript query" query-spec + {:examples ["logseq query --graph my-graph --name block-search --inputs '[\"daily\"]'" + "logseq query --graph my-graph --query '[:find [?e ...] :where [?e :block/name]]'"]}) (core/command-entry ["query" "list"] :query-list "List available queries" query-list-spec)]) (def ^:private built-in-query-specs diff --git a/src/main/logseq/cli/command/remove.cljs b/src/main/logseq/cli/command/remove.cljs index 2099acaa6c..933a541b02 100644 --- a/src/main/logseq/cli/command/remove.cljs +++ b/src/main/logseq/cli/command/remove.cljs @@ -22,10 +22,15 @@ :name {:desc "Entity name"}}) (def entries - [(core/command-entry ["remove" "block"] :remove-block "Remove blocks" remove-block-spec) - (core/command-entry ["remove" "page"] :remove-page "Remove page" remove-page-spec) - (core/command-entry ["remove" "tag"] :remove-tag "Remove tag" remove-entity-spec) - (core/command-entry ["remove" "property"] :remove-property "Remove property" remove-entity-spec)]) + [(core/command-entry ["remove" "block"] :remove-block "Remove blocks" remove-block-spec + {:examples ["logseq remove block --graph my-graph --id 123" + "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 + {:examples ["logseq remove tag --graph my-graph --name project"]}) + (core/command-entry ["remove" "property"] :remove-property "Remove property" remove-entity-spec + {:examples ["logseq remove property --graph my-graph --name status"]})]) (defn invalid-options? [command opts] diff --git a/src/main/logseq/cli/command/server.cljs b/src/main/logseq/cli/command/server.cljs index a7e2360422..13e0877576 100644 --- a/src/main/logseq/cli/command/server.cljs +++ b/src/main/logseq/cli/command/server.cljs @@ -10,10 +10,14 @@ (def entries [(core/command-entry ["server" "list"] :server-list "List db-worker-node servers" {}) - (core/command-entry ["server" "status"] :server-status "Show server status for a graph" server-spec) - (core/command-entry ["server" "start"] :server-start "Start db-worker-node for a graph" server-spec) - (core/command-entry ["server" "stop"] :server-stop "Stop db-worker-node for a graph" server-spec) - (core/command-entry ["server" "restart"] :server-restart "Restart db-worker-node for a graph" server-spec)]) + (core/command-entry ["server" "status"] :server-status "Show server status for a graph" server-spec + {:examples ["logseq server status --graph my-graph"]}) + (core/command-entry ["server" "start"] :server-start "Start db-worker-node for a graph" server-spec + {:examples ["logseq server start --graph my-graph"]}) + (core/command-entry ["server" "stop"] :server-stop "Stop db-worker-node for a graph" server-spec + {:examples ["logseq server stop --graph my-graph"]}) + (core/command-entry ["server" "restart"] :server-restart "Restart db-worker-node for a graph" server-spec + {:examples ["logseq server restart --graph my-graph"]})]) (defn build-action [command repo] diff --git a/src/main/logseq/cli/command/show.cljs b/src/main/logseq/cli/command/show.cljs index 519a545f25..0c9621afc4 100644 --- a/src/main/logseq/cli/command/show.cljs +++ b/src/main/logseq/cli/command/show.cljs @@ -22,7 +22,9 @@ :coerce :long}}) (def entries - [(core/command-entry ["show"] :show "Show tree" show-spec)]) + [(core/command-entry ["show"] :show "Show tree" show-spec + {:examples ["logseq show --graph my-graph --page Home" + "logseq show --graph my-graph --id 123 --level 3"]})]) (def ^:private multi-id-delimiter "\n================================================================\n") diff --git a/src/main/logseq/cli/command/sync.cljs b/src/main/logseq/cli/command/sync.cljs index 896443ef10..daf5a5337d 100644 --- a/src/main/logseq/cli/command/sync.cljs +++ b/src/main/logseq/cli/command/sync.cljs @@ -17,17 +17,32 @@ :coerce :boolean}}) (def entries - [(core/command-entry ["sync" "status"] :sync-status "Show db-sync runtime status" {}) - (core/command-entry ["sync" "start"] :sync-start "Start db-sync client" {}) - (core/command-entry ["sync" "stop"] :sync-stop "Stop db-sync client" {}) - (core/command-entry ["sync" "upload"] :sync-upload "Upload current graph snapshot" {}) - (core/command-entry ["sync" "download"] :sync-download "Download remote graph snapshot" sync-download-spec) + [(core/command-entry ["sync" "status"] :sync-status "Show db-sync runtime status" {} + {:examples ["logseq sync status --graph my-graph"]}) + (core/command-entry ["sync" "start"] :sync-start "Start db-sync client" {} + {:examples ["logseq sync start --graph my-graph"]}) + (core/command-entry ["sync" "stop"] :sync-stop "Stop db-sync client" {} + {:examples ["logseq sync stop --graph my-graph"]}) + (core/command-entry ["sync" "upload"] :sync-upload "Upload current graph snapshot" {} + {:examples ["logseq sync upload --graph my-graph"]}) + (core/command-entry ["sync" "download"] :sync-download "Download remote graph snapshot" sync-download-spec + {:examples ["logseq sync download --graph my-graph" + "logseq sync download --graph my-graph --progress"]}) (core/command-entry ["sync" "remote-graphs"] :sync-remote-graphs "List remote graphs" {}) (core/command-entry ["sync" "ensure-keys"] :sync-ensure-keys "Ensure user RSA keys for sync/e2ee" {}) - (core/command-entry ["sync" "grant-access"] :sync-grant-access "Grant graph access to an email" sync-grant-access-spec) - (core/command-entry ["sync" "config" "set"] :sync-config-set "Set sync config key" {}) - (core/command-entry ["sync" "config" "get"] :sync-config-get "Get sync config key" {}) - (core/command-entry ["sync" "config" "unset"] :sync-config-unset "Unset sync config key" {})]) + (core/command-entry ["sync" "grant-access"] :sync-grant-access "Grant graph access to an email" sync-grant-access-spec + {:examples ["logseq sync grant-access --graph my-graph --graph-id 8b6ecdd0-1fab-4a9f-b3fb-3069c5f76e95 --email teammate@example.com"]}) + (core/command-entry ["sync" "config" "set"] :sync-config-set "Set sync config key" {} + {:examples ["logseq sync config set ws-url wss://sync.logseq.com" + "logseq sync config set http-base https://api.logseq.com" + "logseq sync config set e2ee-password my-secret" + "logseq sync config set ws-url ws://localhost:12315" + "logseq sync config set http-base http://localhost:8080" + "logseq sync config set ws-url wss://example.com/socket"]}) + (core/command-entry ["sync" "config" "get"] :sync-config-get "Get sync config key" {} + {:examples ["logseq sync config get ws-url"]}) + (core/command-entry ["sync" "config" "unset"] :sync-config-unset "Unset sync config key" {} + {:examples ["logseq sync config unset ws-url"]})]) (def ^:private config-key-map {"ws-url" :ws-url diff --git a/src/main/logseq/cli/command/upsert.cljs b/src/main/logseq/cli/command/upsert.cljs index b4583a69e1..d5ee249a1b 100644 --- a/src/main/logseq/cli/command/upsert.cljs +++ b/src/main/logseq/cli/command/upsert.cljs @@ -61,10 +61,15 @@ :coerce :boolean}}) (def entries - [(core/command-entry ["upsert" "block"] :upsert-block "Upsert block" upsert-block-spec) - (core/command-entry ["upsert" "page"] :upsert-page "Upsert page" upsert-page-spec) - (core/command-entry ["upsert" "tag"] :upsert-tag "Upsert tag" upsert-tag-spec) - (core/command-entry ["upsert" "property"] :upsert-property "Upsert property" upsert-property-spec)]) + [(core/command-entry ["upsert" "block"] :upsert-block "Upsert block" upsert-block-spec + {:examples ["logseq upsert block --graph my-graph --target-page Home --content \"New block\"" + "logseq upsert block --graph my-graph --id 123 --content \"Updated content\""]}) + (core/command-entry ["upsert" "page"] :upsert-page "Upsert page" upsert-page-spec + {:examples ["logseq upsert page --graph my-graph --page Home --update-tags '[\"project\"]'"]}) + (core/command-entry ["upsert" "tag"] :upsert-tag "Upsert tag" upsert-tag-spec + {:examples ["logseq upsert tag --graph my-graph --name project"]}) + (core/command-entry ["upsert" "property"] :upsert-property "Upsert property" upsert-property-spec + {:examples ["logseq upsert property --graph my-graph --name status --type default --cardinality one"]})]) (def ^:private property-types #{"default" "number" "date" "datetime" "checkbox" "url" "node" "json" "string"}) diff --git a/src/main/logseq/cli/commands.cljs b/src/main/logseq/cli/commands.cljs index f52ff5f1f4..d189d55979 100644 --- a/src/main/logseq/cli/commands.cljs +++ b/src/main/logseq/cli/commands.cljs @@ -163,10 +163,13 @@ nil))) (defn- ^:large-vars/cleanup-todo finalize-command - [summary {:keys [command opts args cmds spec long-desc]}] + [summary {:keys [command opts args cmds spec long-desc examples]}] (let [opts (command-core/normalize-opts opts) args (vec args) - cmd-summary (command-core/command-summary {:cmds cmds :spec spec :long-desc long-desc}) + cmd-summary (command-core/command-summary {:cmds cmds + :spec spec + :long-desc long-desc + :examples examples}) graph (:graph opts) has-args? (seq args) has-content? (or (seq (:content opts)) diff --git a/src/test/logseq/cli/command/completion_test.cljs b/src/test/logseq/cli/command/completion_test.cljs index 398cfdb57e..f65af1c3ab 100644 --- a/src/test/logseq/cli/command/completion_test.cljs +++ b/src/test/logseq/cli/command/completion_test.cljs @@ -14,7 +14,12 @@ (let [entry (first completion-command/entries)] (is (some? (:long-desc entry))) (is (string/includes? (:long-desc entry) "autoload -Uz compinit")) - (is (string/includes? (:long-desc entry) "eval \"$(logseq completion zsh)\""))))) + (is (string/includes? (:long-desc entry) "eval \"$(logseq completion zsh)\"")))) + (testing "completion entry has examples metadata" + (let [entry (first completion-command/entries)] + (is (= ["logseq completion zsh" + "logseq completion bash"] + (:examples entry)))))) (deftest test-parse-args-completion-shell (testing "parse-args recognizes completion --shell zsh" @@ -27,12 +32,14 @@ (is (= :completion (:command result)))))) (deftest test-parse-args-completion-help - (testing "parse-args completion --help returns help with setup instructions" + (testing "parse-args completion --help returns help with setup instructions and examples" (let [result (commands/parse-args ["completion" "--help"])] (is (false? (:ok? result))) (is (true? (:help? result))) (is (string/includes? (:summary result) "autoload -Uz compinit")) - (is (string/includes? (:summary result) "eval \"$(logseq completion bash)\""))))) + (is (string/includes? (:summary result) "eval \"$(logseq completion bash)\"")) + (is (string/includes? (:summary result) "Examples:")) + (is (string/includes? (:summary result) "logseq completion zsh"))))) (deftest test-build-action-completion (testing "build-action for :completion returns correct action" diff --git a/src/test/logseq/cli/commands_test.cljs b/src/test/logseq/cli/commands_test.cljs index 6206ab6486..7ccdb19c5e 100644 --- a/src/test/logseq/cli/commands_test.cljs +++ b/src/test/logseq/cli/commands_test.cljs @@ -114,7 +114,7 @@ (is (string/includes? plain-summary "Global options:")) (is (string/includes? plain-summary "Command options:"))))) -(deftest test-parse-args-help +(deftest test-parse-args-help-groups (testing "graph group shows subcommands" (let [result (binding [style/*color-enabled?* true] (commands/parse-args ["graph"])) @@ -156,35 +156,6 @@ (is (contains-bold? summary "upsert tag")) (is (contains-bold? summary "upsert property")))) - (testing "remove block command shows help" - (let [result (binding [style/*color-enabled?* true] - (commands/parse-args ["remove" "block" "--help"])) - summary (:summary result) - plain-summary (strip-ansi summary)] - (is (true? (:help? result))) - (is (string/includes? plain-summary "Usage: logseq remove block")) - (is (string/includes? plain-summary "Command options:")) - (is (contains-bold? summary "--id")) - (is (contains-bold? summary "--uuid")))) - - (testing "upsert block command shows help" - (let [result (binding [style/*color-enabled?* true] - (commands/parse-args ["upsert" "block" "--help"])) - summary (:summary result) - plain-summary (strip-ansi summary)] - (is (true? (:help? result))) - (is (string/includes? plain-summary "Usage: logseq upsert block")) - (is (string/includes? plain-summary "Command options:")) - (is (contains-bold? summary "--id")) - (is (contains-bold? summary "--uuid")) - (is (contains-bold? summary "--content")) - (is (contains-bold? summary "--target-id")) - (is (contains-bold? summary "--target-uuid")) - (is (contains-bold? summary "--update-tags")) - (is (contains-bold? summary "--update-properties")) - (is (contains-bold? summary "--remove-tags")) - (is (contains-bold? summary "--remove-properties")))) - (testing "server group shows subcommands" (let [result (binding [style/*color-enabled?* true] (commands/parse-args ["server"])) @@ -214,6 +185,49 @@ (is (seq lines)) (is (every? #(not (string/includes? % "[options]")) lines))))) +(deftest test-parse-args-help-command-examples + (testing "remove block command shows help" + (let [result (binding [style/*color-enabled?* true] + (commands/parse-args ["remove" "block" "--help"])) + summary (:summary result) + plain-summary (strip-ansi summary)] + (is (true? (:help? result))) + (is (string/includes? plain-summary "Usage: logseq remove block")) + (is (string/includes? plain-summary "Command options:")) + (is (string/includes? plain-summary "Examples:")) + (is (string/includes? plain-summary "logseq remove block --graph my-graph --id 123")) + (is (contains-bold? summary "--id")) + (is (contains-bold? summary "--uuid")))) + + (testing "sync config set help limits examples to five lines" + (let [result (binding [style/*color-enabled?* true] + (commands/parse-args ["sync" "config" "set" "--help"])) + plain-summary (strip-ansi (:summary result))] + (is (true? (:help? result))) + (is (string/includes? plain-summary "Examples:")) + (is (string/includes? plain-summary "logseq sync config set ws-url wss://sync.logseq.com")) + (is (string/includes? plain-summary "logseq sync config set http-base http://localhost:8080")) + (is (not (string/includes? plain-summary "logseq sync config set ws-url wss://example.com/socket"))))) + + (testing "upsert block command shows help" + (let [result (binding [style/*color-enabled?* true] + (commands/parse-args ["upsert" "block" "--help"])) + summary (:summary result) + plain-summary (strip-ansi summary)] + (is (true? (:help? result))) + (is (string/includes? plain-summary "Usage: logseq upsert block")) + (is (string/includes? plain-summary "Command options:")) + (is (string/includes? plain-summary "Examples:")) + (is (contains-bold? summary "--id")) + (is (contains-bold? summary "--uuid")) + (is (contains-bold? summary "--content")) + (is (contains-bold? summary "--target-id")) + (is (contains-bold? summary "--target-uuid")) + (is (contains-bold? summary "--update-tags")) + (is (contains-bold? summary "--update-properties")) + (is (contains-bold? summary "--remove-tags")) + (is (contains-bold? summary "--remove-properties"))))) + (deftest test-parse-args-group-help-flags (testing "all groups show group help with -h and --help" (doseq [group ["graph" "server" "list" "upsert" "remove" "query" "sync"] @@ -243,7 +257,8 @@ (is (true? (:help? result))) (is (string/includes? plain-summary "Usage: logseq login")) (is (string/includes? plain-summary "Global options:")) - (is (string/includes? plain-summary "Command options:")))) + (is (string/includes? plain-summary "Command options:")) + (is (not (string/includes? plain-summary "Examples:"))))) (testing "logout command shows help" (let [result (binding [style/*color-enabled?* true] @@ -253,7 +268,8 @@ (is (true? (:help? result))) (is (string/includes? plain-summary "Usage: logseq logout")) (is (string/includes? plain-summary "Global options:")) - (is (string/includes? plain-summary "Command options:"))))) + (is (string/includes? plain-summary "Command options:")) + (is (not (string/includes? plain-summary "Examples:")))))) (deftest test-parse-args-help-sync-group (testing "sync group shows subcommands"