diff --git a/.github/workflows/build-desktop-release.yml b/.github/workflows/build-desktop-release.yml index 008fb95f24..265868d249 100644 --- a/.github/workflows/build-desktop-release.yml +++ b/.github/workflows/build-desktop-release.yml @@ -13,7 +13,7 @@ on: - beta - nightly - non-release - default: "beta" + default: "non-release" git-ref: description: "Release Git Ref (Which branch or tag to build?)" required: true diff --git a/docs/contributing-to-translations.md b/docs/contributing-to-translations.md index 53f095d7a4..355cc6734b 100644 --- a/docs/contributing-to-translations.md +++ b/docs/contributing-to-translations.md @@ -67,9 +67,9 @@ you're hoping to have this list drop to zero. Almost all translations are pretty quick. The only exceptions to this are the keys `:tutorial/text` and `:tutorial/dummy-notes`. These reference files that are part of the onboarding tutorial. Most languages don't have this translated. If you are willing to do this, we would be happy to have this translated. -## Fix Mistakes +## Fix Untranslated -There is a lot to translate and sometimes we make mistakes. For example, we may leave a string untranslated. To see what translation keys are still left in English: +There is a lot to translate and sometimes we forget to translate a string. To see what translation keys are still left in English: ``` $ bb lang:duplicates @@ -82,8 +82,11 @@ Keys with duplicate values found: | :no | No | ``` -Sometimes, we typo the translation key. If that happens, the github CI job will -detect this error and helpfully show you what was typoed. +## Fix Mistakes + +Sometimes, we typo the translation key. If that happens, the github CI step of +`bb lang:invalid-translations` will detect this error and helpfully show you +what was typoed. ## Add a Language diff --git a/docs/dev-practices.md b/docs/dev-practices.md index 764d476446..4879508aa1 100644 --- a/docs/dev-practices.md +++ b/docs/dev-practices.md @@ -5,7 +5,8 @@ This page describes development practices for this codebase. ## Linting Most of our linters require babashka. Before running them, please install -https://github.com/babashka/babashka#installation. +https://github.com/babashka/babashka#installation. To invoke all the linters in +this section, run `bb dev:lint`. ### Clojure code @@ -49,7 +50,7 @@ and understand them. To run this linter: bb lint:large-vars ``` -To configure the linter, see its `config` var. +To configure the linter, see the `[:tasks/config :large-vars]` path of bb.edn. ### Datalog linting @@ -60,6 +61,11 @@ queries and rules. Our queries are linted through clj-kondo and [datalog-parser](https://github.com/lambdaforge/datalog-parser). clj-kondo will error if it detects an invalid query. +### Invalid translations + +Our translations can be configured incorrectly. We can catch some of these +mistakes [as noted here](./contributing-to-translations.md#fix-mistakes). + ## Testing We have unit and end to end tests. diff --git a/libs/src/LSPlugin.ts b/libs/src/LSPlugin.ts index 66dc6d6c9b..1f0cadb3b3 100644 --- a/libs/src/LSPlugin.ts +++ b/libs/src/LSPlugin.ts @@ -228,7 +228,7 @@ export type SimpleCommandKeybinding = { export type SettingSchemaDesc = { key: string - type: 'string' | 'number' | 'boolean' | 'enum' | 'object' + type: 'string' | 'number' | 'boolean' | 'enum' | 'object' | 'heading' default: string | number | boolean | Array | object | null title: string description: string // support markdown diff --git a/src/electron/electron/core.cljs b/src/electron/electron/core.cljs index ebc102466b..bbb2933bee 100644 --- a/src/electron/electron/core.cljs +++ b/src/electron/electron/core.cljs @@ -43,6 +43,8 @@ :win win}))) (defn open-url-handler + "win - the main window instance (first renderer process) + url - the input URL" [win url] (.info logger "open-url" (str {:url url})) diff --git a/src/electron/electron/url.cljs b/src/electron/electron/url.cljs index 10bfe62b15..acbfe692ab 100644 --- a/src/electron/electron/url.cljs +++ b/src/electron/electron/url.cljs @@ -2,7 +2,7 @@ (:require [electron.handler :as handler] [electron.state :as state] [electron.window :as win] - [electron.utils :refer [send-to-renderer] :as utils] + [electron.utils :refer [send-to-renderer send-to-focused-renderer] :as utils] [clojure.string :as string] [promesa.core :as p])) @@ -58,22 +58,24 @@ (graph-identifier-error-handler graph-identifier)))) (defn- x-callback-url-handler - [^js parsed-url] + "win - a window used for fallback (main window is prefered)" + [^js win parsed-url] (let [action (.-pathname parsed-url)] (cond (= action "/quickCapture") (let [[url title content] (get-URL-decoded-params parsed-url ["url" "title" "content"])] - (send-to-renderer "quickCapture" {:url url - :title title - :content content})) + (send-to-focused-renderer "quickCapture" {:url url + :title title + :content content} win)) :else - (send-to-renderer "notification" {:type "error" - :payload (str "Unimplemented x-callback-url action: `" - action - "`.")})))) + (send-to-focused-renderer "notification" {:type "error" + :payload (str "Unimplemented x-callback-url action: `" + action + "`.")} win)))) (defn logseq-url-handler + "win - the main window" [^js win parsed-url] (let [url-host (.-host parsed-url)] ;; return "" when no pathname provided (cond @@ -81,7 +83,7 @@ (send-to-renderer win "loginCallback" (.get (.-searchParams parsed-url) "code")) (= "x-callback-url" url-host) - (x-callback-url-handler parsed-url) + (x-callback-url-handler win parsed-url) ;; identifier of graph in local (= "graph" url-host) diff --git a/src/electron/electron/utils.cljs b/src/electron/electron/utils.cljs index cba23c3eae..babf6be5c2 100644 --- a/src/electron/electron/utils.cljs +++ b/src/electron/electron/utils.cljs @@ -6,7 +6,7 @@ [cljs-bean.core :as bean] ["electron" :refer [app BrowserWindow]])) -(defonce *win (atom nil)) +(defonce *win (atom nil)) ;; The main window (defonce mac? (= (.-platform js/process) "darwin")) (defonce win32? (= (.-platform js/process) "win32")) (defonce linux? (= (.-platform js/process) "linux")) @@ -112,7 +112,8 @@ (defn send-to-renderer "Notice: pass the `window` parameter if you can. Otherwise, the message - will not be received if there's no focused window." + will not be received if there's no focused window. + Use `send-to-focused-renderer` instead if you want to set a window for fallback" ([kind payload] (send-to-renderer (get-focused-window) kind payload)) ([window kind payload] @@ -120,6 +121,13 @@ (.. ^js window -webContents (send (name kind) (bean/->js payload)))))) +(defn send-to-focused-renderer + "Try to send to focused window. If no focused window, fallback to the `fallback-win`" + ([kind payload fallback-win] + (let [focused-win (get-focused-window) + win (if focused-win focused-win fallback-win)] + (send-to-renderer win kind payload)))) + (defn get-graph-dir "required by all internal state in the electron section" [graph-name] diff --git a/src/main/frontend/components/block.cljs b/src/main/frontend/components/block.cljs index bafe190ab9..b2d2d45238 100644 --- a/src/main/frontend/components/block.cljs +++ b/src/main/frontend/components/block.cljs @@ -2727,29 +2727,32 @@ [state] (let [[config query] (:rum/args state) repo (state/get-current-repo) - result-atom (atom nil) - query-atom (if (:dsl-query? config) - (let [q (:query query) - form (safe-read-string q false)] - (cond - ;; Searches like 'foo' or 'foo bar' come back as symbols - ;; and are meant to go directly to full text search - (and (util/electron?) (symbol? form)) ; full-text search - (p/let [blocks (search/block-search repo (string/trim (str form)) {:limit 30})] - (when (seq blocks) - (let [result (db/pull-many (state/get-current-repo) '[*] (map (fn [b] [:block/uuid (uuid (:block/uuid b))]) blocks))] - (reset! result-atom result)))) + result-atom (or (:query-atom state) (atom nil)) + [full-text-search? query-atom] (if (:dsl-query? config) + (let [q (:query query) + form (safe-read-string q false)] + (cond + ;; Searches like 'foo' or 'foo bar' come back as symbols + ;; and are meant to go directly to full text search + (and (util/electron?) (symbol? form)) ; full-text search + [true + (p/let [blocks (search/block-search repo (string/trim (str form)) {:limit 30})] + (when (seq blocks) + (let [result (db/pull-many (state/get-current-repo) '[*] (map (fn [b] [:block/uuid (uuid (:block/uuid b))]) blocks))] + (reset! result-atom result))))] - (symbol? form) - (atom nil) + (symbol? form) + [false (atom nil)] - :else - (query-dsl/query (state/get-current-repo) q))) - (db/custom-query query)) + :else + [false (query-dsl/query (state/get-current-repo) q)])) + [false (db/custom-query query)]) query-atom (if (instance? Atom query-atom) query-atom result-atom)] - (assoc state :query-atom query-atom))) + (assoc state + :query-atom query-atom + :full-text-search? full-text-search?))) (rum/defcs ^:large-vars/cleanup-todo custom-query* < rum/reactive {:will-mount trigger-custom-query! @@ -2809,21 +2812,30 @@ [:div.custom-query.mt-4 (get config :attr {}) (ui/foldable [:div.custom-query-title.flex.justify-between.w-full - [:div [:span.title-text (cond - (vector? title) title - (string? title) (inline-text config - (get-in config [:block :block/format] :markdown) - title) - :else title)] + [:div.flex.items-center + (when (:full-text-search? state) + [:a.control.fade-link.mr-1.inline-flex + {:title "Refresh query result" + :on-mouse-down (fn [e] + (util/stop e) + (trigger-custom-query! state))} + (ui/icon "refresh" {:style {:font-size 20}})]) + [:span.title-text (cond + (vector? title) title + (string? title) (inline-text config + (get-in config [:block :block/format] :markdown) + title) + :else title)] [:span.opacity-60.text-sm.ml-2.results-count (str (count transformed-query-result) " results")]] + ;;insert an "edit" button in the query view (when-not built-in? - [:a.opacity-70.hover:opacity-100.svg-small.inline - {:on-mouse-down (fn [e] - (util/stop e) - (editor-handler/edit-block! current-block :max (:block/uuid current-block)))} - svg/edit])] + [:a.opacity-70.hover:opacity-100.svg-small.inline + {:on-mouse-down (fn [e] + (util/stop e) + (editor-handler/edit-block! current-block :max (:block/uuid current-block)))} + svg/edit])] (fn [] [:div (when (and current-block (not view-f) (nil? table-view?)) diff --git a/src/main/frontend/components/plugins.css b/src/main/frontend/components/plugins.css index 6ffa6772b7..8d28cf3216 100644 --- a/src/main/frontend/components/plugins.css +++ b/src/main/frontend/components/plugins.css @@ -465,6 +465,12 @@ right: 8px; } + .heading-item { + margin: 12px 12px 6px; + font-weight: bold; + border-bottom: 1px solid var(--ls-border-color, #738694); + } + .desc-item { padding: 12px 12px 6px; @@ -783,7 +789,7 @@ max-height: 80vh; overflow-y: auto; } - + .menu-link { padding: 3px 5px; } diff --git a/src/main/frontend/components/plugins_settings.cljs b/src/main/frontend/components/plugins_settings.cljs index 80794b3e76..7a82cd617e 100644 --- a/src/main/frontend/components/plugins_settings.cljs +++ b/src/main/frontend/components/plugins_settings.cljs @@ -74,6 +74,12 @@ [:small.pl-1.flex-1 description] [:div.pl-1 (edit-settings-file pid nil)]]]) +(rum/defc render-item-heading + [{:keys [title]}] + + [:div.heading-item + [:h2 title]]) + (rum/defc settings-container [schema ^js pl] (let [^js _settings (.-settings pl) @@ -107,6 +113,7 @@ #{:boolean} (render-item-toggle val desc update-setting!) #{:enum} (render-item-enum val desc update-setting!) #{:object} (render-item-object val desc pid) + #{:heading} (render-item-heading desc) [:p (str "#Not Handled#" key)]))] diff --git a/src/main/frontend/extensions/calc.cljc b/src/main/frontend/extensions/calc.cljc index 8c30b5cdbf..d20f0d301c 100644 --- a/src/main/frontend/extensions/calc.cljc +++ b/src/main/frontend/extensions/calc.cljc @@ -40,11 +40,18 @@ :sub (fn sub [a b] (-> a (.minus b))) :mul (fn mul [a b] (-> a (.multipliedBy b))) :div (fn div [a b] (-> a (.dividedBy b))) - :pow (fn pow [a b] (-> a (.exponentiatedBy b))) + :pow (fn pow [a b] (if (.isInteger b) + (.exponentiatedBy a b) + #?(:clj (java.lang.Math/pow a b) + :cljs (bn/BigNumber (js/Math.pow a b))))) + :abs (fn abs [a] (.abs a)) + :sqrt (fn abs [a] (.sqrt a)) :log (fn log [a] #?(:clj (java.lang.Math/log10 a) :cljs (bn/BigNumber (js/Math.log10 a)))) :ln (fn ln [a] #?(:clj (java.lang.Math/log a) :cljs (bn/BigNumber (js/Math.log a)))) + :exp (fn ln [a] + #?(:clj (java.lang.Math/exp a) :cljs (bn/BigNumber (js/Math.exp a)))) :sin (fn sin [a] #?(:clj (java.lang.Math/sin a) :cljs (bn/BigNumber(js/Math.sin a)))) :cos (fn cos [a] @@ -61,6 +68,7 @@ (swap! env assoc var val) val) :toassign str/trim + :comment (constantly nil) :variable (fn resolve [var] (let [var (str/trim var)] (or (get @env var) @@ -79,12 +87,17 @@ (catch #?(:clj Exception :cljs js/Error) e e)))) +(defn assign-last-value [env val] + (when-not (nil? val) + (swap! env assoc "last" val)) + val) + (defn eval-lines [s] {:pre [(string? s)]} (let [env (new-env)] (mapv (fn [line] (when-not (str/blank? line) - (eval env (parse line)))) + (assign-last-value env (eval env (parse line))))) (str/split-lines s)))) ;; ====================================================================== diff --git a/src/main/frontend/handler/page.cljs b/src/main/frontend/handler/page.cljs index e75f234cd0..922d7e846d 100644 --- a/src/main/frontend/handler/page.cljs +++ b/src/main/frontend/handler/page.cljs @@ -5,6 +5,7 @@ [datascript.core :as d] [frontend.commands :as commands] [frontend.config :as config] + [frontend.context.i18n :refer [t]] [frontend.date :as date] [frontend.db :as db] [logseq.db.schema :as db-schema] @@ -719,8 +720,9 @@ (fn [chosen _click?] (state/clear-editor-action!) (let [wrapped? (= "[[" (gp-util/safe-subs edit-content (- pos 2) pos)) - chosen (if (string/starts-with? chosen "New page: ") ;; FIXME: What if a page named "New page: XXX"? - (subs chosen 10) + prefix (str (t :new-page) ": ") + chosen (if (string/starts-with? chosen prefix) ;; FIXME: What if a page named "New page: XXX"? + (string/replace-first chosen prefix "") chosen) chosen (if (and (util/safe-re-find #"\s+" chosen) (not wrapped?)) (util/format "[[%s]]" chosen) @@ -740,8 +742,9 @@ :forward-pos forward-pos}))) (fn [chosen _click?] (state/clear-editor-action!) - (let [chosen (if (string/starts-with? chosen "New page: ") - (subs chosen 10) + (let [prefix (str (t :new-page) ": ") + chosen (if (string/starts-with? chosen prefix) + (string/replace-first chosen prefix "") chosen) page-ref-text (get-page-ref-text chosen)] (editor-handler/insert-command! id diff --git a/src/main/grammar/calc.bnf b/src/main/grammar/calc.bnf index 29884e78ba..12466ef6b6 100644 --- a/src/main/grammar/calc.bnf +++ b/src/main/grammar/calc.bnf @@ -1,28 +1,32 @@ - = assignment | expr -expr = add-sub - = pow-term | mul-div | add | sub | variable + = assignment | expr | comment +expr = add-sub comment +comment = <#'\s*(#.*$)?'> + = pow-term | mul-div | add | sub | variable add = add-sub <'+'> mul-div sub = add-sub <'-'> mul-div = pow-term | mul | div mul = mul-div <'*'> pow-term div = mul-div <'/'> pow-term = pow | term -pow = pow-term <'^'> term - = sin | cos | tan | acos | asin | atan +pow = posterm <'^'> pow-term + = log | ln | exp | sqrt | abs | sin | cos | tan | acos | asin | atan log = <#'\s*'> <'log('> expr <')'> <#'\s*'> ln = <#'\s*'> <'ln('> expr <')'> <#'\s*'> +exp = <#'\s*'> <'exp('> expr <')'> <#'\s*'> +sqrt = <#'\s*'> <'sqrt('> expr <')'> <#'\s*'> +abs = <#'\s*'> <'abs('> expr <')'> <#'\s*'> sin = <#'\s*'> <'sin('> expr <')'> <#'\s*'> cos = <#'\s*'> <'cos('> expr <')'> <#'\s*'> tan = <#'\s*'> <'tan('> expr <')'> <#'\s*'> atan = <#'\s*'> <'atan('> expr <')'> <#'\s*'> acos = <#'\s*'> <'acos('> expr <')'> <#'\s*'> asin = <#'\s*'> <'asin('> expr <')'> <#'\s*'> - = log | ln | trig | percent | scientific | number | variable | <#'\s*'> <'('> expr <')'> <#'\s*'> -negterm = <#'\s*'> <'-'> posterm + = function | percent | scientific | number | variable | <#'\s*'> <'('> expr <')'> <#'\s*'> +negterm = <#'\s*'> <'-'> posterm | <#'\s*'> <'-'> pow = negterm | posterm -scientific = #'\s*[0-9]+\.?[0-9]*(e|E)-?[0-9]+()\s*' -number = #'\s*\d+(,\d+)*(\.\d*)?\s*' +scientific = #'\s*[0-9]*\.?[0-9]+(e|E)[\-\+]?[0-9]+()\s*' +number = #'\s*(\d+(,\d+)*(\.\d*)?|\d*\.\d+)\s*' percent = number <'%'> <#'\s*'> -variable = #'\s*[a-zA-Z]+(\_+[a-zA-Z]+)*\s*' -toassign = #'\s*[a-zA-Z]+(\_+[a-zA-Z]+)*\s*' +variable = #'\s*_*[a-zA-Z]+[_a-zA-Z0-9]*\s*' +toassign = #'\s*_*[a-zA-Z]+[_a-zA-Z0-9]*\s*' assignment = toassign <#'\s*'> <'='> <#'\s*'> expr \ No newline at end of file diff --git a/src/test/frontend/extensions/calc_test.cljc b/src/test/frontend/extensions/calc_test.cljc index 53b795a2c8..79ee24e4df 100644 --- a/src/test/frontend/extensions/calc_test.cljc +++ b/src/test/frontend/extensions/calc_test.cljc @@ -19,7 +19,11 @@ 98123 "98123" 1.0 " 1.0 " 22.1124131 "22.1124131" - 100.01231 " 100.01231 ") + 100.01231 " 100.01231 " + 0.01231 " .01231 " + 0.015 ".015 " + -0.2 "-.2" + -0.3 "- .3") (testing "even when they have the commas in the wrong place" (are [value expr] (= value (run expr)) 98123 "9812,3" @@ -62,15 +66,6 @@ 2.0 "2*100%" 0.01 "2%/2" 500e3 "50% * 1e6")) - (testing "power" - (are [value expr] (= value (run expr)) - 1.0 "1 ^ 0" - 4.0 "2^2 " - 27.0 " 3^ 3" - 0.125 " 2^ -3" - 16.0 "2 ^ 2 ^ 2" - 256.0 "4.000 ^ 4.0" - 4096.0 "200% ^ 12")) (testing "operator precedence" (are [value expr] (= value (run expr)) 1 "1 + 0 * 2" @@ -90,9 +85,39 @@ 12.3 "123.0e-1" -12.3 "-123.0e-1" 12.3 "123.0E-1" - 2.0 "1e0 + 1e0")) - (testing "scientific functions" + 12300 "123.0E+2" + 2.0 "1e0 + 1e0" + 10 ".1e2" + 0.001 ".1e-2" + -0.045 "-.45e-1" + -210 "-.21e3")) + (testing "avoiding rounding errors" (are [value expr] (= value (run expr)) + 3.3 "1.1 + 2.2" + 2.2 "3.3 - 1.1" + 0.0001 "1/10000" + 1e-7 "1/10000000"))) + +(deftest scientific-functions + (testing "power" + (are [value expr] (= value (run expr)) + 1.0 "1 ^ 0" + 4.0 "2^2 " + -9.0 "-3^2 " + 9.0 "(-3)^2 " + 27.0 " 3^ 3" + 0.125 " 2^ -3" + 512.0 "2 ^ 3 ^ 2" + 256.0 "4.000 ^ 4.0" + 2.0 "4^0.5" + 0.1 "100^(-0.5)" + 125.0 "25^(3/2)" + 4096.0 "200% ^ 12")) + (testing "functions" + (are [value expr] (= value (run expr)) + 2.0 "sqrt( 4 )" + 3.0 "abs( 3 )" + 3.0 "abs( -3 )" 1.0 "cos( 0 * 1 )" 0.0 "sin( 1 -1 )" 0.0 "atan(tan(0))" @@ -101,14 +126,9 @@ 0.0 "acos(cos(0))" 5.0 "2 * log(10) + 3" 1.0 "-2 * log(10) + 3" - 10.0 "ln(1) + 10")) - (testing "avoiding rounding errors" - (are [value expr] (= value (run expr)) - 3.3 "1.1 + 2.2" - 2.2 "3.3 - 1.1" - 0.0001 "1/10000" - 1e-7 "1/10000000" - ))) + 10.0 "ln(1) + 10" + 1.0 "exp(0)" + 2.0 "ln(exp(2))"))) (deftest variables (testing "variables can be remembered" @@ -116,7 +136,8 @@ (calc/eval env (calc/parse expr)) (= final-env (into {} (for [[k v] @env] [k (convert-bigNum v)])))) {"a" 1} "a = 1" - {"a" -1} "a = -1" + {"a" -1} "a = -1" + {"k9" 27} "k9 = 27" {"variable" 1} "variable = 1 + 0 * 2" {"x" 1} "x= 2 * 1 - 1 " {"y" 4} "y =8 / 4 + 2 * 1 - 25 * 0 / 1" @@ -128,6 +149,7 @@ (calc/eval env (calc/parse expr)) (= final-env (into {} (for [[k v] @env] [k (convert-bigNum v)])))) {"a_a" 1} "a_a = 1" + {"_foo" 1} "_foo = 1" {"x_yy_zzz" 1} "x_yy_zzz= 1" {"foo_bar_baz" 1} "foo_bar_baz = 1 + -0 * 2")) (testing "variables can be reused" @@ -150,15 +172,33 @@ {"a" 2 "b" 2} ["a = 1" "b = a + 1" "a = b"] {"variable" 1 "x" 0} ["variable = 1 + 0 * 2" "x = log(variable)" "x = variable - 1"]))) +(deftest last-value + (testing "last value is set" + (are [values exprs] (let [env (calc/new-env)] + (mapv (fn [expr] + (calc/eval env (calc/parse expr))) + exprs)) + [42 126] ["6*7" "last*3"] + [25 5] ["3^2+4^2" "sqrt(last)"] + [6 12] ["2*3" "# a comment" "" " " "last*2"]))) + +(deftest comments + (testing "comments are ignored" + (are [value expr] (= value (run expr)) + nil "# this comment is ignored" + nil " # this comment is ignored " + 8.0 "2*4# double 4" + 10.0 "2*5 # double 5" + 12.0 "2*6 # double 6" + 14.0 "2*7 # 99"))) + (deftest failure (testing "expressions that don't match the spec fail" (are [expr] (calc/failure? (calc/eval (calc/new-env) (calc/parse expr))) "foo_ =" "foo__ =" "oo___ =" - " " - "bar_2 = 2 + 4" - "bar_2a = 3 + 4" - "foo_ = " - "foo__ =" - "foo_3 = a"))) + " . " + "_ = 2" + "__ = 4" + "foo_3 = _")))