Browse Source

Merge branch 'master' into whiteboards

Peng Xiao 3 years ago
parent
commit
ec4164b044

+ 1 - 1
.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

+ 7 - 4
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
 

+ 8 - 2
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.

+ 1 - 1
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<any> | object | null
   title: string
   description: string // support markdown

+ 2 - 0
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}))
 

+ 12 - 10
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)

+ 10 - 2
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]

+ 43 - 31
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))))
-
-                         (symbol? form)
-                         (atom nil)
-
-                         :else
-                         (query-dsl/query (state/get-current-repo) q)))
-                     (db/custom-query query))
+        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)
+                                             [false (atom nil)]
+
+                                             :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?))

+ 7 - 1
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;
       }

+ 7 - 0
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)]))]
 

+ 15 - 2
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))))
 
 ;; ======================================================================

+ 7 - 4
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

+ 15 - 11
src/main/grammar/calc.bnf

@@ -1,28 +1,32 @@
-<start> = assignment | expr
-expr = add-sub
-<add-sub> = pow-term | mul-div | add | sub |  variable
+<start> = assignment | expr | comment
+expr = add-sub comment
+comment = <#'\s*(#.*$)?'>
+<add-sub> = pow-term | mul-div | add | sub | variable
 add = add-sub <'+'> mul-div
 sub = add-sub <'-'> mul-div
 <mul-div> = pow-term | mul | div
 mul = mul-div <'*'> pow-term
 div = mul-div <'/'> pow-term
 <pow-term> = pow | term
-pow = pow-term <'^'> term
-<trig> = sin | cos | tan | acos | asin | atan
+pow = posterm <'^'> pow-term
+<function> = 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*'>
-<posterm> = log | ln | trig | percent | scientific | number | variable | <#'\s*'> <'('> expr <')'> <#'\s*'>
-negterm = <#'\s*'> <'-'> posterm
+<posterm> = function | percent | scientific | number | variable | <#'\s*'> <'('> expr <')'> <#'\s*'>
+negterm = <#'\s*'> <'-'> posterm | <#'\s*'> <'-'> pow
 <term> = 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

+ 67 - 27
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  = _")))