├── README.org └── org-logseq.el /README.org: -------------------------------------------------------------------------------- 1 | #+TITLE:Org-logseq 2 | 3 | Org-logseq supports to open [[https://github.com/logseq/logseq][logseq]]-style link (page or block reference), transclusion of referred and embedded blocks using overlays, along with Org-Mode. 4 | 5 | * Features 6 | 7 | + Open logseq-style links by native Org-Mode binding (=C-c C-o=, =Enter=, etc). Org-logseq supports: 8 | - Page link: =[[a logseq page]]= 9 | - Block reference: =((6064f6c6-440a-46ca-b8df-59131adab5a1))= 10 | - Block Embed and Reference overlays. 11 | + Create a blank excalidraw file if necessary, while inserting it at point. 12 | + Display logseq block references in buffer as overlays: 13 | - =((6064f6c6-440a-46ca-b8df-59131adab5a1))= to =[[id:6064f6c6-440a-46ca-b8df-59131adab5a1][Block heading]]= 14 | - ={{embed ((6064f6c6-440a-46ca-b8df-59131adab5a1))}}= to the content of the referred block. 15 | + Download images stored in the logseq server to local. 16 | =curl= needs to be installed first. 17 | + Toggle =contents.org= as a sidebar. 18 | 19 | * Installation 20 | 21 | Org-logseq requires =grep=. Install it using your system tool at first. 22 | 23 | Org-logseq package is not on MELPA yet. Currently, the easiest way to install org-logseq is through [[https://github.com/quelpa/quelpa][quelpa]] or [[https://github.com/raxod502/straight.el][straight]] system, or use the =:quelpa= or =:straight= keyword by using [[https://github.com/jwiegley/use-package][use-package]]. In order to make =:quelpa= work well with =use-package=, you need to install [[https://github.com/quelpa/quelpa-use-package][quelpa-use-package]]. 24 | 25 | - quelpa 26 | #+begin_src emacs-lisp 27 | (use-package org-logseq 28 | :quelpa (org-logseq :fetcher github :repo "llcc/org-logseq" :files ("*")) 29 | :custom (org-logseq-dir "~/logseq")) 30 | #+end_src 31 | 32 | - straight 33 | #+begin_src emacs-lisp :tangle yes 34 | (use-package org-logseq 35 | :straight (org-logseq :fetcher github :repo "llcc/org-logseq" :files ("*")) 36 | :custom (org-logseq-dir "~/logseq")) 37 | #+end_src 38 | 39 | * Getting started 40 | 41 | 1. Set the variable =org-logseq-dir= to your logseq path. 42 | 2. =M-x org-logseq-mode= in your current buffer. 43 | Creating a directory-local varible in your logseq directory could be the best way to get it worked in all org files. 44 | 45 | #+begin_src emacs-lisp 46 | ((org-mode . ((eval org-logseq-mode 1)))) 47 | #+end_src 48 | 49 | 3. Use your native Org-mode bindings to open pages or block references. 50 | 51 | * Customization 52 | 53 | - =org-logseq-create-page-p= :: 54 | A boolean value indicating whether or not org-logseq should try to new a page file at point if it does not exist. The varible defaults to =nil=. 55 | - *org-logseq-block-ref-overlay-p* :: 56 | A boolean value indicating whether or not to display the overlays of =block references= when initializing the Org-Mode buffer. 57 | - *org-logseq-block-embed-overlay-p* :: 58 | A boolean value indicating whether or not to display the overlays of =block embed= when initializing the Org-Mode buffer. 59 | 60 | 61 | * Changelog 62 | 63 | ** <2021-04-08 Thu> 64 | - Add a =org-logseq-toggle-contents-sidebar= command, to toggle your =contents.org= as a sidebar. 65 | - Fix path quote bug in macOS, [[https://github.com/llcc/org-logseq/issues/1][#issue1]] 66 | 67 | * Thanks 68 | 69 | All to [[https://github.com/logseq/logseq][logseq]]. 70 | 71 | -------------------------------------------------------------------------------- /org-logseq.el: -------------------------------------------------------------------------------- 1 | ;;; org-logseq.el --- for logseq -*- lexical-binding: t; -*- 2 | 3 | ;; Author: Zhe Lei 4 | ;; URL: https://github.com/llcc/org-logseq 5 | ;; Package-Version: 20210402.2237 6 | ;; Version: 0.0.4 7 | ;; Package-Requires: ((dash "2.11.0") (org "9.0.0")) 8 | 9 | 10 | ;; This program is free software; you can redistribute it and/or modify 11 | ;; it under the terms of the GNU General Public License as published by 12 | ;; the Free Software Foundation, either version 3 of the License, or 13 | ;; (at your option) any later version. 14 | 15 | ;; This program is distributed in the hope that it will be useful, 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ;; GNU General Public License for more details. 19 | 20 | ;; You should have received a copy of the GNU General Public License 21 | ;; along with this program. If not, see . 22 | 23 | ;;; Commentary: 24 | 25 | ;; Make logseq work easier in Emacs 26 | 27 | ;;; Code: 28 | 29 | (require 'ol) 30 | (require 'org-element) 31 | (require 'dash) 32 | 33 | (defgroup org-logseq nil 34 | "Logseq capbility in Org Mode" 35 | :group 'org) 36 | 37 | (defcustom org-logseq-dir nil 38 | "Path of logseq notes" 39 | :group 'org-logseq) 40 | 41 | (defcustom org-logseq-new-page-p nil 42 | "Non-nil means creating a page if not exist." 43 | :group 'org-logseq) 44 | 45 | (defcustom org-logseq-block-ref-overlay-p nil 46 | "Non-nil means to enable block ref by default" 47 | :group 'org-logseq) 48 | (make-variable-buffer-local 'org-logseq-block-ref-overlay-p) 49 | 50 | (defcustom org-logseq-block-embed-overlay-p nil 51 | "Non-nil means to enable block ref by default" 52 | :group 'org-logseq) 53 | (make-variable-buffer-local 'org-logseq-block-embed-overlay-p) 54 | 55 | (defun org-logseq-get-block-id (&optional beg end embed) 56 | "Return a cons: \"('id . id)\" at point." 57 | (save-excursion 58 | (when-let* ((prev-bracket (or beg (search-backward-regexp 59 | "((" (line-beginning-position) t)))) 60 | (let* ((next-bracket (or end (search-forward-regexp 61 | "))" (line-end-position) t))) 62 | (id (buffer-substring-no-properties 63 | (+ prev-bracket 2) (- next-bracket 2)))) 64 | (cons 'id id))))) 65 | 66 | (defun org-logseq-get-link () 67 | "Return a cons: \"('type . link)\" at point. 68 | The type can be 'url, 'draw and 'page, denoting the link type." 69 | (save-excursion 70 | (let ((context (org-element-context)) link) 71 | (when (eq 'link (car context)) 72 | (setq link (org-element-property :raw-link context)) 73 | (cond ((string-match "\\(?:https?\\)" link) 74 | (cons 'url link)) 75 | ((string-suffix-p ".excalidraw" link) 76 | (cons 'draw link)) 77 | (t (cons 'page link))))))) 78 | 79 | (defun org-logseq-grep-query (page-or-id) 80 | "Return grep result for searching PAGE-OR-ID in `org-logseq-dir'." 81 | (let ((type (car page-or-id)) 82 | (query (cdr page-or-id))) 83 | (format (pcase type 84 | ('page "grep -niR \"^#+\\(TITLE\\|ALIAS\\): *%s\" %s --exclude-dir=.git" ) 85 | ('id "grep -niR \":id: *%s\" %s --exclude-dir=.git")) 86 | query (shell-quote-argument (f-expand org-logseq-dir))))) 87 | 88 | (defun org-logseq-get-block-ref-or-embed-link () 89 | (when-let ((ovs (overlays-at (point))) 90 | (ov (--first (eq (overlay-get it 'parent) 'block) ovs))) 91 | (cons 'id (overlay-get ov 'block-uuid)))) 92 | 93 | ;;;###autoload 94 | (defun org-logseq-open-link () 95 | "Open link at point. Supports url, id and page. 96 | or Block Ref or Embed overlays." 97 | (interactive) 98 | (when-let* ((t-l (or (org-logseq-get-block-ref-or-embed-link) 99 | (org-logseq-get-link) 100 | (org-logseq-get-block-id)))) 101 | (let ((type (car t-l)) 102 | (link (cdr t-l))) 103 | (pcase type 104 | ('url (browse-url link)) 105 | ('draw (org-logseq-open-excalidraw link)) 106 | (_ (let ((result (shell-command-to-string 107 | (org-logseq-grep-query t-l)))) 108 | (when (string-prefix-p "grep" result) 109 | (error "grep searching error")) 110 | (if (string= result "") 111 | (org-logseq-new-page link) 112 | (let* ((f-n (split-string result ":" nil)) 113 | (fname (car f-n)) 114 | (lineno (string-to-number (cadr f-n)))) 115 | (org-open-file fname t lineno))))))))) 116 | 117 | (defun org-logseq-new-page (page) 118 | "Create a new PAGE org file in pages directory if setting 119 | `org-logseq-new-page-p' to non-nil." 120 | (if org-logseq-new-page-p 121 | (find-file-other-window 122 | (expand-file-name (concat "pages/" page) github-dir)) 123 | (user-error "No page found; Check `org-logseq-new-page-p` variable"))) 124 | 125 | ;; todo: looking for a way to open draw file by post 126 | (defun org-logseq-open-excalidraw nil) 127 | 128 | (defvar org-logseq-excalidraw 129 | "{ 130 | \"type\": \"excalidraw\", 131 | \"version\": 2, 132 | \"source\": \"https://excalidraw.com\", 133 | \"elements\": [], 134 | \"appState\": { 135 | \"gridSize\": null, 136 | \"viewBackgroundColor\": \"#ffffff\" 137 | } 138 | }" 139 | "Blank excalidraw file") 140 | 141 | (defun org-logseq-new-excalidraw (&optional name) 142 | "Create an excalidraw file and insert it at point." 143 | (interactive "P") 144 | (let ((excalidraw-fname (format "%s.excalidraw" 145 | (or name (format-time-string "%Y-%m-%d_%H_%M_%S"))))) 146 | (insert (format "[[draws/%s]]" excalidraw-fname)) 147 | (with-current-buffer 148 | (find-file-noselect 149 | (expand-file-name (format "draws/%s" excalidraw-fname) 150 | org-logseq-dir)) 151 | (insert org-logseq-excalidraw) 152 | (save-buffer)))) 153 | 154 | ;;; Contents sidebar 155 | (defcustom org-logseq-sidebar-width 30 156 | "Sidebar window width") 157 | 158 | (defcustom org-logseq-sidebar-side 'left 159 | "Sidebar position") 160 | 161 | ;;;###autoload 162 | (defun org-logseq-toggle-contents-sidebar () 163 | "Display contents.org as sidebar left side." 164 | (interactive) 165 | (if-let ((contents-window 166 | (--first 167 | (and (string= "contents.org" (buffer-name (window-buffer it))) 168 | (window-dedicated-p it)) 169 | (window-list (selected-frame))))) 170 | (delete-window contents-window) 171 | (let ((window-parameters (list (cons 'no-other-window t) 172 | (cons 'no-delete-other-windows t)))) 173 | (display-buffer-in-side-window 174 | (find-file-noselect (expand-file-name "pages/contents.org" org-logseq-dir)) 175 | (list (cons 'side org-logseq-sidebar-side) 176 | (cons 'window-width org-logseq-sidebar-width) 177 | (cons 'window-parameters window-parameters)))))) 178 | 179 | ;;; Logseq id overlays 180 | (defvar-local org-logseq-block-ref-overlays nil) 181 | (defvar-local org-logseq-block-embed-overlays nil) 182 | (defvar-local org-logseq-buffer-modified-p nil) 183 | 184 | (defvar org-logseq-block-ref-re "((\\([a-zA-Z0-9-]+\\)))") 185 | (defvar org-logseq-block-embed-re "{{embed +((\\([a-zA-Z0-9-]+\\))) *}}") 186 | 187 | (defface org-logseq-ref-block 188 | '((((class color) (min-colors 88) (background light)) 189 | :background "#fff3da" :extend t) 190 | (((class color) (min-colors 88) (background dark)) 191 | :background "#fff3da" :extend t)) 192 | "Face for ref block." 193 | :group 'org-logseq) 194 | 195 | (defface org-logseq-embed-block 196 | '((((class color) (min-colors 88) (background light)) 197 | :background "#f3f3ff" :extend t) 198 | (((class color) (min-colors 88) (background dark)) 199 | :background "#f3f3ff" :extend t)) 200 | "Face for embed block." 201 | :group 'org-logseq) 202 | 203 | ;;;###autoload 204 | (defun org-logseq-toggle-block-ref-overlays () 205 | (interactive) 206 | (if org-logseq-block-ref-overlays 207 | (org-logseq-block-ref-deactivate) 208 | (org-logseq-block-ref-activate))) 209 | 210 | ;;;###autoload 211 | (defun org-logseq-toggle-block-embed-overlays () 212 | (interactive) 213 | (if org-logseq-block-embed-overlays 214 | (org-logseq-block-embed-deactivate) 215 | (org-logseq-block-embed-activate))) 216 | 217 | (defun org-logseq--make-block-ref-overlays () 218 | (org-logseq-make-block-overlays 'ref)) 219 | (defun org-logseq--remove-block-ref-overlays () 220 | (org-logseq-remove-block-overlays 'ref)) 221 | 222 | (defun org-logseq-block-ref-activate () 223 | (org-logseq--make-block-ref-overlays) 224 | (add-hook 'before-save-hook #'org-logseq--remove-block-ref-overlays nil t) 225 | (add-hook 'after-save-hook #'org-logseq--make-block-ref-overlays nil t) 226 | (add-hook 'kill-buffer-hook #'org-logseq--remove-block-ref-overlays nil t)) 227 | (defun org-logseq-block-ref-deactivate () 228 | (org-logseq--remove-block-ref-overlays) 229 | (remove-hook 'before-save-hook #'org-logseq--remove-block-ref-overlays t) 230 | (remove-hook 'after-save-hook #'org-logseq--make-block-ref-overlays t) 231 | (remove-hook 'kill-buffer-hook #'org-logseq--make-block-ref-overlays t)) 232 | 233 | (add-hook 'org-mode-hook 234 | #'(lambda () (when org-logseq-block-ref-overlay-p 235 | (org-logseq-block-ref-activate)))) 236 | 237 | (defun org-logseq--make-block-embed-overlays () 238 | (org-logseq-make-block-overlays 'embed)) 239 | (defun org-logseq--remove-block-embed-overlays () 240 | (org-logseq-remove-block-overlays 'embed)) 241 | 242 | (defun org-logseq-block-embed-activate () 243 | (org-logseq--make-block-embed-overlays) 244 | (add-hook 'before-save-hook #'org-logseq--remove-block-embed-overlays nil t) 245 | (add-hook 'after-save-hook #'org-logseq--make-block-embed-overlays nil t)) 246 | (defun org-logseq-block-embed-deactivate () 247 | (org-logseq--remove-block-embed-overlays) 248 | (remove-hook 'before-save-hook #'org-logseq--remove-block-embed-overlays t) 249 | (remove-hook 'after-save-hook #'org-logseq--make-block-embed-overlays t)) 250 | 251 | (add-hook 'org-mode-hook 252 | #'(lambda () (when org-logseq-block-embed-overlay-p 253 | (org-logseq-block-embed-activate)))) 254 | 255 | (defun org-logseq-make-block-overlays (type &optional beg end) 256 | (setq org-logseq-buffer-modified-p (buffer-modified-p)) 257 | (save-excursion 258 | (goto-char (or beg (point-min))) 259 | (let ((re (pcase type 260 | ('ref org-logseq-block-ref-re) 261 | ('embed org-logseq-block-embed-re)))) 262 | (while (and (re-search-forward re end t) 263 | (pcase type 264 | ('ref (not (looking-at " *}}"))) 265 | ('embed t))) 266 | (let* ((uuid (match-string-no-properties 1)) 267 | (tuuid (cons 'id uuid)) 268 | (overlay-end (point)) 269 | (overlay-beg (re-search-backward 270 | (pcase type ('ref "((") ('embed "{{")) 271 | (line-beginning-position) t)) 272 | (file-type-block (org-logseq-get-block-content tuuid type))) 273 | (org-logseq-create-block-overlay overlay-beg overlay-end file-type-block))))) 274 | (set-buffer-modified-p org-logseq-buffer-modified-p)) 275 | 276 | (defun org-logseq-prepare-embed-content (content) 277 | (let ((content-list (split-string content "\n")) 278 | headline-spaces line-spaces) 279 | (concat "​" (mapconcat 280 | #'(lambda (str) 281 | (if (string-match "\\(\*+\\)" str) 282 | (progn 283 | (setq line-spaces (make-string (* (length (match-string 1 str)) 2) ? )) 284 | (setq headline-spaces (make-string (* (1- (length (match-string 1 str))) 2) ? )) 285 | (setq heading 286 | (concat headline-spaces (replace-match "\*" nil nil str 1)))) 287 | (concat line-spaces str))) 288 | content-list "\n") 289 | "\n"))) 290 | 291 | (defun org-logseq-hide-block-embed-drawer-all (beg end) 292 | "Fold all drawers in the block embed overlays." 293 | (save-excursion 294 | (goto-char beg) 295 | (while (re-search-forward org-drawer-regexp end t) 296 | (let* ((pair (get-char-property-and-overlay (line-beginning-position) 297 | 'invisible)) 298 | (o (cdr-safe pair))) 299 | (if (overlayp o) (goto-char (overlay-end o)) ;invisible drawer 300 | (pcase (get-char-property-and-overlay (point) 'invisible) 301 | (`(outline . ,o) (goto-char (overlay-end o))) ;already folded 302 | (_ 303 | (let* ((drawer (org-element-at-point)) 304 | (type (org-element-type drawer))) 305 | (when (memq type '(drawer property-drawer)) 306 | (org-hide-drawer-toggle t nil drawer) 307 | ;; Make sure to skip drawer entirely or we might flag it 308 | ;; another time when matching its ending line with 309 | ;; `org-drawer-regexp'. 310 | (goto-char end)))))))))) 311 | 312 | (defun org-logseq-create-block-overlay (beg end file-type-block) 313 | (pcase-let ((`((,file . ,uuid) . (,type . ,content)) 314 | file-type-block)) 315 | (delete-region beg end) 316 | (insert (pcase type 317 | ('ref content) 318 | ('embed (org-logseq-prepare-embed-content content)))) 319 | (let* ((end (point)) 320 | (ov (make-overlay beg end)) 321 | (face (pcase type 322 | ('ref 'org-logseq-ref-block) 323 | ('embed 'org-logseq-embed-block)))) 324 | (overlay-put ov 'parent 'block) 325 | (overlay-put ov 'type type) 326 | (overlay-put ov 'evaporate t) 327 | (overlay-put ov 'file file) 328 | (overlay-put ov 'block-uuid uuid) 329 | (overlay-put ov 'target content) 330 | (overlay-put ov 'help-echo 331 | (format "Original page: %s.org" (file-name-base file))) 332 | (overlay-put ov 'face face) 333 | ;; (overlay-put ov 'keymap org-logseq-overlay-map) 334 | (add-text-properties beg end '(read-only t)) 335 | (org-logseq-hide-block-embed-drawer-all beg end) 336 | (pcase type 337 | ('ref (push ov org-logseq-block-ref-overlays)) 338 | ('embed (push ov org-logseq-block-embed-overlays)))))) 339 | 340 | (defvar org-logseq-overlay-map 341 | (let ((map (make-sparse-keymap))) 342 | (define-key map "RET" 'org-logseq-open-link) 343 | map)) 344 | 345 | (defun org-logseq-get-block-content (tuuid type) 346 | (when-let ((to-marker (point-marker)) 347 | (result (shell-command-to-string 348 | (org-logseq-grep-query tuuid)))) 349 | (let* ((f-n (split-string result ":" nil)) 350 | (file (car f-n)) 351 | (line (string-to-number (cadr f-n)))) 352 | (cons (cons file (cdr tuuid)) 353 | (with-current-buffer (find-file-noselect file) 354 | (goto-line line) 355 | (cons type 356 | (pcase type 357 | ('ref (format "[[id:%s][%s]]" (cdr tuuid) 358 | (let ((result (org-no-properties (org-get-heading)))) 359 | (dolist (func org-logseq-block-ref-heading-hook) 360 | (setq result (funcall func result))) 361 | result))) 362 | ('embed 363 | (save-restriction 364 | (org-narrow-to-subtree) 365 | (buffer-substring-no-properties (point-min) (point-max))))))))))) 366 | 367 | (defun olih-link (heading) 368 | (if (string-match "\\[\\[.+\\]\\[\\(.+\\)\\]\\]" heading) 369 | (match-string 1 heading) 370 | heading)) 371 | 372 | (defcustom org-logseq-block-ref-heading-hook 373 | '(olih-link) 374 | "Hook for cleaning up id heading") 375 | 376 | (defun org-logseq-remove-block-overlays (type) 377 | (setq org-logseq-buffer-modified-p (buffer-modified-p)) 378 | (when-let ((overlays (pcase type 379 | ('ref org-logseq-block-ref-overlays) 380 | ('embed org-logseq-block-embed-overlays)))) 381 | (save-excursion 382 | (dolist (ov overlays) 383 | (let* ((beg (overlay-start ov)) 384 | (end (overlay-end ov)) 385 | (block-uuid (overlay-get ov 'block-uuid)) 386 | (file (overlay-get ov 'file)) 387 | (inhibit-read-only t)) 388 | (remove-text-properties beg end '(read-only t)) 389 | (delete-region beg end) 390 | (delete-overlay ov) 391 | (goto-char beg) 392 | (insert (pcase type 393 | ('ref (concat "((" block-uuid "))")) 394 | ('embed (format "{{embed ((%s))}}" block-uuid)))))) 395 | (pcase type 396 | ('ref (setq org-logseq-block-ref-overlays nil)) 397 | ('embed (setq org-logseq-block-embed-overlays nil))))) 398 | (set-buffer-modified-p org-logseq-buffer-modified-p)) 399 | 400 | (defun org-logseq-return (&optional indent arg interactive) 401 | "Goto next table row or insert a newline. 402 | 403 | Calls `org-table-next-row' or `newline', depending on context. 404 | 405 | When optional INDENT argument is non-nil, call 406 | `newline-and-indent' with ARG, otherwise call `newline' with ARG 407 | and INTERACTIVE. 408 | 409 | When `org-return-follows-link' is non-nil and point is on 410 | a timestamp or a link, call `org-open-at-point'. However, it 411 | will not happen if point is in a table or on a \"dead\" 412 | object (e.g., within a comment). In these case, you need to use 413 | `org-open-at-point' directly." 414 | (interactive "i\nP\np") 415 | (let ((context (if org-return-follows-link (org-element-context) 416 | (org-element-at-point)))) 417 | (cond 418 | ;; In a table, call `org-table-next-row'. However, before first 419 | ;; column or after last one, split the table. 420 | ((or (and (eq 'table (org-element-type context)) 421 | (not (eq 'table.el (org-element-property :type context))) 422 | (>= (point) (org-element-property :contents-begin context)) 423 | (< (point) (org-element-property :contents-end context))) 424 | (org-element-lineage context '(table-row table-cell) t)) 425 | (if (or (looking-at-p "[ \t]*$") 426 | (save-excursion (skip-chars-backward " \t") (bolp))) 427 | (insert "\n") 428 | (org-table-justify-field-maybe) 429 | (call-interactively #'org-table-next-row))) 430 | ;; On a link or a timestamp, call `org-open-at-point' if 431 | ;; `org-return-follows-link' allows it. Tolerate fuzzy 432 | ;; locations, e.g., in a comment, as `org-open-at-point'. 433 | ((and org-return-follows-link 434 | (or (and (eq 'link (org-element-type context)) 435 | ;; Ensure point is not on the white spaces after 436 | ;; the link. 437 | (let ((origin (point))) 438 | (org-with-point-at (org-element-property :end context) 439 | (skip-chars-backward " \t") 440 | (> (point) origin)))) 441 | (org-in-regexp org-ts-regexp-both nil t) 442 | (org-in-regexp org-tsr-regexp-both nil t) 443 | (org-in-regexp org-link-any-re nil t) 444 | (org-logseq-get-block-id) 445 | (org-logseq-get-block-ref-or-embed-link))) 446 | (org-logseq-open-link)) 447 | ;; Insert newline in heading, but preserve tags. 448 | ((and (not (bolp)) 449 | (let ((case-fold-search nil)) 450 | (org-match-line org-complex-heading-regexp))) 451 | ;; At headline. Split line. However, if point is on keyword, 452 | ;; priority cookie or tags, do not break any of them: add 453 | ;; a newline after the headline instead. 454 | (let ((tags-column (and (match-beginning 5) 455 | (save-excursion (goto-char (match-beginning 5)) 456 | (current-column)))) 457 | (string 458 | (when (and (match-end 4) (org-point-in-group (point) 4)) 459 | (delete-and-extract-region (point) (match-end 4))))) 460 | ;; Adjust tag alignment. 461 | (cond 462 | ((not (and tags-column string))) 463 | (org-auto-align-tags (org-align-tags)) 464 | (t (org--align-tags-here tags-column))) ;preserve tags column 465 | (end-of-line) 466 | (org-show-entry) 467 | (org--newline indent arg interactive) 468 | (when string (save-excursion (insert (org-trim string)))))) 469 | ;; In a list, make sure indenting keeps trailing text within. 470 | ((and (not (eolp)) 471 | (org-element-lineage context '(item))) 472 | (let ((trailing-data 473 | (delete-and-extract-region (point) (line-end-position)))) 474 | (org--newline indent arg interactive) 475 | (save-excursion (insert trailing-data)))) 476 | (t 477 | ;; Do not auto-fill when point is in an Org property drawer. 478 | (let ((auto-fill-function (and (not (org-at-property-p)) 479 | auto-fill-function))) 480 | (org--newline indent arg interactive)))))) 481 | 482 | (defun org-logseq-activate () 483 | (advice-add 'org-return :override #'org-logseq-return) 484 | (advice-add 'org-open-at-point :override #'org-logseq-open-link) 485 | (advice-add 'org-open-at-mouse :override #'org-logseq-open-link)) 486 | 487 | (defun org-logseq-deactivate () 488 | (advice-remove 'org-return #'org-logseq-return) 489 | (advice-remove 'org-open-at-point #'org-logseq-open-link) 490 | (advice-remove 'org-open-at-mouse #'org-logseq-open-link)) 491 | 492 | (defvar org-logseq-map 493 | (let ((map (make-sparse-keymap))) 494 | (define-key map [remap org-return] 'org-logseq-return) 495 | (define-key map [remap org-open-at-point] 'org-logseq-open-link) 496 | (define-key map [remap org-open-at-mouse] 'org-logseq-open-link) 497 | map) 498 | "Org-logseq map") 499 | 500 | (define-minor-mode org-logseq-mode 501 | "Org-logseq minor mode" 502 | :init-value nil 503 | :global nil 504 | :keymap org-logseq-map) 505 | 506 | ;; (if org-logseq-mode 507 | ;; (org-logseq-activate) 508 | ;; (org-logseq-deactivate)) 509 | 510 | ;;;###autoload 511 | (defun org-logseq-download-images () 512 | (interactive) 513 | (save-excursion 514 | (goto-char (point-min)) 515 | (while (re-search-forward "https://cdn.logseq.com" nil t) 516 | (let* ((context (org-element-context))) 517 | (when (eq (car context) 'link) 518 | (let ((link-begin (org-element-property :begin context)) 519 | (link-end (org-element-property :end context)) 520 | (path (org-element-property :raw-link context)) 521 | (image-path (concat "images/" 522 | (make-temp-name "org-logseq-") (format-time-string "-%Y%m%d.png")))) 523 | (start-process "org logseq" nil "curl" path "--output" image-path) 524 | (setf (buffer-substring link-begin link-end) (format "[[./%s]]" image-path)))))))) 525 | 526 | (provide 'org-logseq) 527 | ;;; org-logseq.el ends here 528 | --------------------------------------------------------------------------------