diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8f9713c..9a53b1e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,10 +13,10 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macOS-latest] - ghc: ['9.2', '9.4'] + ghc: ['9.8'] cabal: ['3.10'] include: - - ghc: '9.4' + - ghc: '9.8' executable: true steps: - uses: actions/checkout@v3 diff --git a/cabal.project b/cabal.project index c837fdf..2b0f965 100644 --- a/cabal.project +++ b/cabal.project @@ -8,14 +8,26 @@ package * source-repository-package type: git location: https://github.com/lucasvreis/lvar.git - tag: b5c16f1f48a784bdb0578a663c3bb0d355fe0fc6 + tag: 7d5d776126df8199a7019fe16dc2e6f7fd5255ee source-repository-package type: git - location: https://github.com/lucasvreis/unionmount.git - tag: 5fef2d328b5c15b9feec7ca0dd71d2d403c39941 + location: https://github.com/srid/unionmount.git + tag: fb401ce6030e0f516ea105e1f5c400260b3ade33 + +-- source-repository-package +-- type: git +-- location: https://github.com/lucasvreis/ondim.git +-- tag: e8a1918f979140c7fb72ec7c19315f8d19bd8d60 +-- subdir: . ondim-html ondim-pandoc ondim-json ondim-latex + +source-repository-package + type: git + location: https://github.com/lucasvreis/ix-cat.git + tag: fbacb089e0e8d231f376e63a6f0616c057450c28 source-repository-package type: git - location: https://github.com/lucasvreis/ondim.git - tag: 6bbb553d1e75f1a2f45db915804292e80ce01396 + location: https://gitlab.com/trupill/kind-generics.git + tag: a38287ccab9de3941c1103cc06090009ff965e50 + subdir: kind-generics-th diff --git a/fourmolu.yaml b/fourmolu.yaml new file mode 100644 index 0000000..c21fd78 --- /dev/null +++ b/fourmolu.yaml @@ -0,0 +1,10 @@ +indentation: 2 +function-arrows: trailing +comma-style: leading +import-export-style: leading +indent-wheres: true +record-brace-space: true +newlines-between-decls: 1 +haddock-style: multi-line +respectful: true +fixities: [] diff --git a/org-cli/app/Options.hs b/org-cli/app/Options.hs index 94a909f..070c238 100644 --- a/org-cli/app/Options.hs +++ b/org-cli/app/Options.hs @@ -16,7 +16,7 @@ data AppOptions = AppOptions } data BackendOptions - = AST {pretty :: Bool} + = AST | Ondim {ondimOptions :: OndimOptions} data OndimFormat @@ -92,7 +92,7 @@ backend' = <> command "pandoc" (info pandoc (progDesc "Export to Pandoc JSON using Ondim.")) ) where - ast = AST . not <$> switch (long "no-pretty" <> help "Disable pretty-printing of the parsed AST") + ast = pure AST html = Ondim <$> ondimOptions' HTML latex = Ondim <$> ondimOptions' LaTeX pandoc = Ondim <$> ondimOptions' Pandoc diff --git a/org-cli/app/org-cli.hs b/org-cli/app/org-cli.hs index ac07be9..e9ab2f6 100644 --- a/org-cli/app/org-cli.hs +++ b/org-cli/app/org-cli.hs @@ -5,8 +5,9 @@ module Main where import Control.Exception (try) import Data.Text.IO qualified as T import Ondim +import Ondim.Debug (OndimException) import Ondim.Extra.Exceptions (prettyException) -import Ondim.Extra.Loading (LoadConfig (..), TemplateLoadingError (..), loadTemplates) +import Ondim.Loading (LoadConfig (..), TemplateLoadingException (..), loadTemplates) import Ondim.Targets.HTML qualified as H import Ondim.Targets.LaTeX qualified as L import Ondim.Targets.Pandoc qualified as P @@ -19,18 +20,17 @@ import Org.Exporters.LaTeX qualified as L import Org.Exporters.Pandoc qualified as P import Org.Exporters.Processing (OrgData, processAll) import Org.Parser -import Org.Types (OrgDocument) +import Org.Types.Variants.Annotated (OrgDocument) import System.Directory qualified as D import System.FilePath (isDrive, takeDirectory, ()) -import Text.Pretty.Simple -loadPandoc :: forall m. Monad m => LoadConfig m +loadPandoc :: forall m. (Monad m) => LoadConfig m loadPandoc = (P.loadPandocMd @m) {initialState = templatesEmbed [P.loadPandocMd]} -loadHtml :: forall m. Monad m => LoadConfig m +loadHtml :: forall m. (Monad m) => LoadConfig m loadHtml = (H.loadHtml @m) {initialState = templatesEmbed [H.loadHtml]} -loadLaTeX :: forall m. Monad m => LoadConfig m +loadLaTeX :: forall m. (Monad m) => LoadConfig m loadLaTeX = (L.loadLaTeX @m) {initialState = templatesEmbed [L.loadLaTeX]} renderDoc :: @@ -49,8 +49,8 @@ renderDoc udirs oo datum doc = do if null dirs then return $ initialState cfg else loadTemplates [cfg] dirs - evalOndimTWith tpls $ - callTemplateFold @Rendered (O.layout oo) + evalOndimWith tpls $ + renderTemplateOrError (O.layout oo) `binding` documentExp bk datum doc main :: IO () @@ -86,8 +86,7 @@ main = do (processed, datum) = processAll parsed out <- try case O.backend opts of - O.AST False -> return $ Right $ show processed - O.AST True -> return $ Right $ encodeUtf8 $ pShowNoColor processed + O.AST -> return $ Right $ show processed O.Ondim oo -> renderDoc udirs oo datum processed case out of Right (Right out') -> case O.output opts of diff --git a/org-cli/org-cli.cabal b/org-cli/org-cli.cabal index 6f3a173..a6012e9 100644 --- a/org-cli/org-cli.cabal +++ b/org-cli/org-cli.cabal @@ -26,16 +26,18 @@ executable horg main-is: org-cli.hs other-modules: Options build-depends: - , base >=4.15 && <4.19 + , base >=4.17.2 && <4.19 , directory , filepath , megaparsec , ondim + , ondim-html + , ondim-latex + , ondim-pandoc , optparse-applicative >=0.17.0 && <0.18 , org-exporters , org-parser , pandoc - , pretty-simple >=4.1 , relude ghc-options: -Wall -threaded diff --git a/org-exporters/baseline.csv b/org-exporters/baseline.csv index a67e629..462f67d 100644 --- a/org-exporters/baseline.csv +++ b/org-exporters/baseline.csv @@ -1,17 +1,25 @@ Name,Mean (ps),2*Stdev (ps) -All.config.Parse,39021067600,3031762928 -All.config.Process.process,2722141412,143405412 -All.config.Process.Export.html.export,159614359400,6297960142 -All.config.Process.Export.html.render.render,1730125718,147106578 -All.config.Process.Export.md.export,108419337300,2548007956 -All.config.Process.Export.md.render.render,410768689,28265520 -All.config.Process.Export.tex.export,52783477100,3147149626 -All.config.Process.Export.tex.render.render,106205838500,2512305106 -All.diagramas.Parse,19252348600,1912887730 -All.diagramas.Process.process,1275592303,82426622 -All.diagramas.Process.Export.html.export,33021328950,1155194530 -All.diagramas.Process.Export.html.render.render,319007339,21307638 -All.diagramas.Process.Export.md.export,47099573600,3057184338 -All.diagramas.Process.Export.md.render.render,181065551,12232328 -All.diagramas.Process.Export.tex.export,21078764800,1330461452 -All.diagramas.Process.Export.tex.render.render,4106936100,191300142 +All.config.parsing.bench,32842970800,1732831486 +All.config.parsing.processing.bench,2258442325,84087782 +All.config.parsing.processing.export.html.export,114053682700,3056975936 +All.config.parsing.processing.export.html.render.render,1748998950,93245682 +All.config.parsing.processing.export.md.export,73876882400,5872259756 +All.config.parsing.processing.export.md.render.render,463439748,36056730 +All.config.parsing.processing.export.tex.export,41567682500,2909580596 +All.config.parsing.processing.export.tex.render.render,89240492900,2237442264 +All.diagramas.parsing.bench,13953423700,1060804512 +All.diagramas.parsing.processing.bench,1095703409,82110818 +All.diagramas.parsing.processing.export.html.export,23569743650,885944890 +All.diagramas.parsing.processing.export.html.render.render,325906117,26743512 +All.diagramas.parsing.processing.export.md.export,31710374300,2403231500 +All.diagramas.parsing.processing.export.md.render.render,192354867,10728048 +All.diagramas.parsing.processing.export.tex.export,18296192350,990760128 +All.diagramas.parsing.processing.export.tex.render.render,5496583775,465287396 +All.config-tecosaur.parsing.bench,273457942600,14085829374 +All.config-tecosaur.parsing.processing.bench,18877885050,1184465778 +All.config-tecosaur.parsing.processing.export.html.export,564520696200,9637435540 +All.config-tecosaur.parsing.processing.export.html.render.render,8354891500,689611190 +All.config-tecosaur.parsing.processing.export.md.export,426179064400,39130553668 +All.config-tecosaur.parsing.processing.export.md.render.render,2891376687,223670524 +All.config-tecosaur.parsing.processing.export.tex.export,303972845600,15033282680 +All.config-tecosaur.parsing.processing.export.tex.render.render,2528484429400,35206187284 diff --git a/org-exporters/data/templates/extra/toc.html b/org-exporters/data/templates/extra/toc.html index d13df58..ba5b6da 100644 --- a/org-exporters/data/templates/extra/toc.html +++ b/org-exporters/data/templates/extra/toc.html @@ -7,7 +7,7 @@ -
-
;;; -*- lexical-binding: t; -*-
-
+Doom
+
;;; -*- lexical-binding: t; -*-
(setq user-full-name "Lucas V. R."
user-mail-address "redacted")

-Disables and unpins

-
+Disables and unpins
;; -*- no-byte-compile: t; -*-
;;; $DOOMDIR/packages.el
(package! flycheck-popup-tip :disable t)
(package! writegood-mode :disable t)
(package! hl-line :disable t)
(package! revealjs :disable t)
(package! org-re-reveal :disable t)
(package! company :disable t)
(unpin! doom-themes)
(unpin! vertico)
(unpin! treemacs)
(unpin! evil-tex)
(unpin! all-the-icons)

@@ -699,14 +696,11 @@

Table of Contents

Non-interactive

-Advices

-

Para desabilitar as mensagens chatas:

-
+Advices

Para desabilitar as mensagens chatas:

(defun advice--inhibit-message (f &rest r) (let ((inhibit-message t)) (apply f r)))

-List of strings predicate

-
+List of strings predicate
(defun string-list-p (x) (and (listp x) (--all? (stringp it) x)))

@@ -722,17 +716,14 @@

Table of Contents

  • -Boolean variable toggle (setq-toggle)

    -
    +Boolean variable toggle (setq-toggle)

    (defmacro setq-toggle (s)
    `(setq ,s (not ,s)))
  • Interactive

    -Remove all advice from a symbol

    -

    This was taken from StackExchange long ago.

    -
    +Remove all advice from a symbol

    This was taken from StackExchange long ago.

    (defun advice-unadvice (sym)
    "Remove all advices from symbol SYM."
    (interactive "aFunction symbol: ")
    (advice-mapc (lambda (advice _props) (advice-remove sym advice)) sym))

    @@ -747,14 +738,12 @@

    Table of Contents

  • -AAS (auto expanding snippets)

    -
    +AAS (auto expanding snippets)

    (package! aas :recipe (:host github :repo "ymarco/auto-activating-snippets"))
  • -Abbrev

    -
    +Abbrev

    (add-hook! 'text-mode-hook
    (abbrev-mode +1))

    (setq abbrev-file-name (concat doom-private-dir "abbrev_defs"))
  • @@ -764,14 +753,12 @@

    Table of Contents

  • -Installation

    -
    +Installation

    (package! cape)
  • -Configuration

    -
    +Configuration

    (use-package cape
    :after corfu
    ;; Bind dedicated completion commands
    :bind (("C-c c p" . completion-at-point) ;; capf
    ("C-c c t" . complete-tag) ;; etags
    ("C-c c d" . cape-dabbrev) ;; or dabbrev-completion
    ("C-c c f" . cape-file)
    ("C-c c k" . cape-keyword)
    ("C-c c s" . cape-symbol)
    ("C-c c a" . cape-abbrev)
    ("C-c c i" . cape-ispell)
    ("C-c c l" . cape-line)
    ("C-c c w" . cape-dict)
    ("C-c c \\" . cape-tex)
    ("C-c c &" . cape-sgml)
    ("C-c c r" . cape-rfc1345))
    :init
    ;; Add `completion-at-point-functions', used by `completion-at-point'.
    (setq cape-dabbrev-check-other-buffers nil
    cape-dabbrev-min-length 3
    dabbrev-case-fold-search t)
    (add-to-list 'completion-at-point-functions #'cape-file)
    (add-to-list 'completion-at-point-functions #'cape-tex)
    (add-to-list 'completion-at-point-functions #'cape-keyword))
    ;;(add-to-list 'completion-at-point-functions #'cape-sgml)
    ;;(add-to-list 'completion-at-point-functions #'cape-rfc1345)
    ;;(add-to-list 'completion-at-point-functions #'cape-abbrev)
    ;;(add-to-list 'completion-at-point-functions #'cape-ispell)
    ;;(add-to-list 'completion-at-point-functions #'cape-dict)
    ;;(add-to-list 'completion-at-point-functions #'cape-symbol)
    ;;(add-to-list 'completion-at-point-functions #'cape-line)
  • @@ -781,50 +768,37 @@

    Table of Contents

  • -Installation

    -
    +Installation

    (package! corfu :recipe (:host github :repo "minad/corfu" :files ("*.el" "extensions/*.el")))
    (package! popon :recipe (:type git :repo "https://codeberg.org/akib/emacs-popon.git"))
    (package! corfu-terminal :recipe (:type git :repo "https://codeberg.org/akib/emacs-corfu-terminal.git"))
  • -Configuration

    -
    +Configuration

    (use-package corfu
    :bind (:map corfu-map
    ("\\" . corfu-quit)
    ("TAB" . corfu-next)
    ("S-TAB" . corfu-previous)
    ("<tab>" . corfu-next)
    ("<backtab>" . corfu-previous)
    ("M-s" . corfu-insert-separator))
    :hook (doom-first-input . global-corfu-mode)

    ;; Optional customizations
    :custom
    (corfu-cycle t) ;; Enable cycling for `corfu-next/previous'
    (corfu-auto t)
    (corfu-auto-delay 0)
    (corfu-auto-prefix 3)
    (corfu-preselect 'prompt)
    ;; (corfu-commit-predicate nil) ;; Do not commit selected candidates on next input
    ;; (corfu-quit-at-boundary t) ;; Automatically quit at word boundary
    (corfu-quit-no-match 'separator)) ;; Automatically quit if there is no match
    ;; (corfu-echo-documentation nil) ;; Do not show documentation in the echo area
    ;; You may want to enable Corfu only for certain modes.
    ;; :hook ((prog-mode . corfu-mode)
    ;; (shell-mode . corfu-mode)
    ;; (eshell-mode . corfu-mode))

    ;; Dabbrev works with Corfu
    (use-package dabbrev
    :custom (dabbrev-search-all-buffers nil)
    ;; Swap M-/ and C-M-/
    :bind (("M-/" . dabbrev-completion)
    ("C-M-/" . dabbrev-expand)))

    ;; A few more useful configurations...
    (use-package emacs
    :init
    ;; TAB cycle if there are only few candidates
    (setq completion-cycle-threshold 3)

    ;; Emacs 28: Hide commands in M-x which do not work in the current mode.
    ;; Corfu commands are hidden, since they are not supposed to be used via M-x.
    (setq read-extended-command-predicate
    #'command-completion-default-include-p)

    ;; Enable indentation+completion using the TAB key.
    ;; `completion-at-point' is often bound to M-TAB.
    (setq tab-always-indent 'complete))
  • -Color picker (Zenity)

    -
    -
    (package! zenity-color-picker)
    -
    +Color picker (Zenity)

    +
    (package! zenity-color-picker)
    (map! :leader :n "e c" #'zenity-cp-color-at-point-dwim)
  • -Yasnippet

    -

    Fixes the issue where when a placeholder appeared on an empty line, no -indentation was applied. For instance, this:

    -
    -
    1. bla
    quote|
    -

    would be expanded to this:

    -
    -
     1. bla
    ,#+begin_quote
    |
    ,#+end_quote
    -

    quite annoing…

    -
    +Yasnippet

    Fixes the issue where when a placeholder appeared on an empty line, no +indentation was applied. For instance, this:

    +
    1. bla
    quote|

    would be expanded to this:

    +
     1. bla
    ,#+begin_quote
    |
    ,#+end_quote

    quite annoing…

    (setq yas-also-indent-empty-lines t)
    1. -Movimentos entre campos

      -

      Uma dessas coisas que se imagina, por que não fizeram assim?

      -
      +Movimentos entre campos

      Uma dessas coisas que se imagina, por que não fizeram assim?

      (defadvice! my-yas--maybe-move-to-active-field (snippet)
      "Try to move to SNIPPET's active (or first) field and return it if found."

      :override #'yas--maybe-move-to-active-field

      (let ((target-field (or (yas--snippet-active-field snippet)
      (car (yas--snippet-fields snippet)))))
      (when target-field
      (yas--move-to-field snippet target-field)
      (goto-char (yas--field-end target-field))
      target-field)))
    2. -Interaction with Corfu

      -
      +Interaction with Corfu

      (defadvice! yas-expand-filter-corfu-a (&rest r)
      :before-while #'yas-maybe-expand-abbrev-key-filter
      (not (and (frame-live-p corfu--frame) (frame-visible-p corfu--frame))))
  • @@ -833,10 +807,8 @@

    Table of Contents

  • -Kind-icon

    -
    -
    (package! kind-icon)
    -
    +Kind-icon

    +
    (package! kind-icon)
    (use-package kind-icon
    :after corfu
    :custom
    (kind-icon-default-face 'corfu-default) ; to compute blended backgrounds correctly
    :config
    (setq kind-icon-default-style '(:padding 0 :stroke 0 :margin 0 :radius 0 :height 0.8 :scale 1.0))
    (add-hook! 'doom-load-theme-hook #'kind-icon-reset-cache)
    (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter))
  • @@ -850,36 +822,29 @@

    Table of Contents

  • -Installation

    -
    +Installation

    (unpin! consult)
  • -Configuration

    -
    +Configuration

    (use-package consult
    :bind (:map doom-leader-map
    ("," . consult-buffer)))

    (after! consult
    (map! :n "M-y" #'consult-yank-replace
    :leader "h I" #'consult-info
    "r r" #'consult-register
    "r s" #'consult-register-store
    "r l" #'consult-register-load))
  • -Orderless

    -
    -
    (use-package corfu
    :config
    (setq completion-styles '(basic-limited orderless basic)))
    -

    Também quero que tenha inicialismos (por exemplo, hmlm -> hide-mode-line-mode):

    -
    +Orderless

    +
    (use-package corfu
    :config
    (setq completion-styles '(basic-limited orderless basic)))

    Também quero que tenha inicialismos (por exemplo, hmlm -> hide-mode-line-mode):

    (setq orderless-matching-styles
    '(orderless-initialism
    orderless-literal
    orderless-regexp))
    1. -Orderless fast dispatch

      -
      +Orderless fast dispatch

      (defun basic-limited-all-completions (string table pred point)
      (when (length< string 4)
      (completion-emacs21-all-completions string table pred point)))

      (defun basic-limited-try-completion (string table pred point)
      (when (length< string 4)
      (completion-emacs21-try-completion string table pred point)))

      (add-to-list 'completion-styles-alist
      '(basic-limited
      basic-limited-try-completion
      basic-limited-all-completions
      "Limited basic completion."))
  • -Register interaction with Evil

    -
    +Register interaction with Evil

    (after! (consult evil)
    (defadvice! evil-paste--pretend-to-be-yank-a (&rest _r)
    :after #'evil-paste-after
    :after #'evil-paste-before
    (setq this-command 'yank
    yank-undo-function (lambda (_ _) (evil-undo-pop)))))
  • @@ -889,23 +854,19 @@

    Table of Contents

  • -Configuration

    -
    +Configuration

    (use-package vertico
    :bind (:map vertico-map
    ("M-k" . vertico-next)
    ("M-j" . vertico-previous))
    :config
    ;; (vertico-reverse-mode +1)
    (setq vertico-resize nil
    vertico-count 8))
  • Help and error system

    -Eldoc-box

    -
    +Eldoc-box
    (package! eldoc-box)

    -Jinx (spell)

    -
    -
    (package! jinx)
    -
    +Jinx (spell)
    +
    (package! jinx)
    (use-package jinx
    :config
    (setq jinx-languages "pt_BR en_US")
    (dolist (hook '(text-mode-hook conf-mode-hook))
    (add-hook hook #'jinx-mode))
    (define-key evil-visual-state-map "z=" 'jinx-correct)
    (define-key evil-normal-state-map "z=" 'jinx-correct))

    @@ -915,10 +876,8 @@

    Table of Contents

    Posframe -ARCHIVE

    -
    -
    (package! which-key-posframe)
    -
    +ARCHIVE

    +
    (package! which-key-posframe)
    (use-package which-key-posframe
    :hook (which-key-mode . which-key-posframe-mode)
    :config
    (add-hook 'doom-after-reload-hook #'posframe-delete-all)
    (setq which-key-posframe-poshandler #'posframe-poshandler-frame-bottom-center))

    @@ -930,18 +889,13 @@

    Table of Contents

  • -Variables

    -
    -
    (setq evil-shift-round nil
    evil-cross-lines t
    evil-move-cursor-back nil
    evil-want-fine-undo t
    evil-snipe-spillover-scope 'visible
    evil-respect-visual-line-mode t

    ;; Substitui vários matches por linha no evil-ex
    evil-ex-substitute-global t)
    -

    This is very important. The newline at the end of a line is a character too!

    -
    +Variables

    +
    (setq evil-shift-round nil
    evil-cross-lines t
    evil-move-cursor-back nil
    evil-want-fine-undo t
    evil-snipe-spillover-scope 'visible
    evil-respect-visual-line-mode t

    ;; Substitui vários matches por linha no evil-ex
    evil-ex-substitute-global t)

    This is very important. The newline at the end of a line is a character too!

    (setq evil-move-beyond-eol t)
  • -Mouse bindings for multicursor (evil-mc)

    -

    Toggle multicursors at mouse pointer with C-<mouse-1>.

    -
    +Mouse bindings for multicursor (evil-mc)

    Toggle multicursors at mouse pointer with C-<mouse-1>.

    (defun evil-mc/toggle-cursor-on-click (event)
    "Add a cursor where you click, or remove a fake cursor that is
    already there."
    (interactive "e")
    (mouse-minibuffer-check event)
    (require 'evil-mc)
    ;; Use event-end in case called from mouse-drag-region.
    ;; If EVENT is a click, event-end and event-start give same value.
    (let ((position (event-end event)))
    (if (not (windowp (posn-window position)))
    (error "Position not in text area of window"))
    (select-window (posn-window position))
    (let ((pt (posn-point position)))
    (if (numberp pt)
    ;; is there a fake cursor with the actual *point* right where we are?
    (unless (evil-mc-undo-cursor-at-pos pt)
    (save-excursion
    (goto-char pt)
    (evil-mc-make-cursor-here)))))))

    (after! evil
    (map! "C-<down-mouse-1>" nil)
    (map! "C-<mouse-1>" #'evil-mc/toggle-cursor-on-click))
  • @@ -951,79 +905,62 @@

    Table of Contents

  • -Org headlines

    -
    +Org headlines

    (defun evil-org--parse-headline ()
    (save-excursion
    (end-of-line)
    (outline-previous-heading)
    (skip-chars-forward "* \t")
    (let* ((todo-start (point))
    (todo-end1 (and org-todo-regexp
    (let (case-fold-search) (looking-at (concat org-todo-regexp " ")))
    (goto-char (1- (match-end 0)))))
    (todo-end2 (when todo-end1 (skip-chars-forward " \t") (point)))
    (priority-start (point))
    (priority-end (when (looking-at "\\[#.\\][ \t]*") (goto-char (match-end 0))))
    (_ (and (let (case-fold-search) (looking-at org-comment-string))
    (goto-char (match-end 0))))
    (title-start (point))
    (tags-start (when (re-search-forward "[ \t]+\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$"
    (line-end-position) 'move)
    (goto-char (match-beginning 0))
    (match-beginning 1)))
    (title-end (point)))
    (list todo-start todo-end1 todo-end2 priority-start
    priority-end title-start title-end
    tags-start (line-end-position)))))

    (after! evil
    (evil-define-text-object evil-org-headline (count &optional beg end type)
    "Select the current org heading" :jump t
    (save-excursion
    (end-of-line)
    (outline-previous-heading)
    (list (line-beginning-position) (line-end-position))))

    (evil-define-text-object evil-org-headline-title (c &rest _)
    "Select the title text in the current org heading" :jump t
    (let ((parse (evil-org--parse-headline)))
    (list (nth 5 parse) (nth 6 parse))))

    (evil-define-text-object evil-org-headline-todo (c &rest _)
    "Select the todo entry in the current org heading" :jump t
    (let ((parse (evil-org--parse-headline)))
    (list (nth 0 parse) (nth 2 parse))))

    (evil-define-text-object evil-org-headline-inner-todo (c &rest _)
    "Select the inner todo entry in the current org heading" :jump t
    (let ((parse (evil-org--parse-headline)))
    (list (nth 0 parse) (nth 1 parse))))

    (evil-define-text-object evil-org-headline-priority (c &rest _)
    "Select the priority entry in the current org heading" :jump t
    (let ((parse (evil-org--parse-headline)))
    (list (nth 3 parse) (nth 4 parse))))

    (evil-define-text-object evil-org-headline-tags (c &rest _)
    "Select the tags in the current org heading" :jump t
    (let ((parse (evil-org--parse-headline)))
    (list (nth 6 parse) (nth 8 parse))))

    (evil-define-text-object evil-org-headline-inner-priority (c &rest r)
    "Select the inner part of priority in the current org heading" :jump t
    (let ((parse (evil-org--parse-headline)))
    (when (nth 4 parse)
    (let ((p (+ 2 (nth 3 parse)))) (list p (1+ p))))))

    (evil-define-text-object evil-org-headline-inner-tags (c &rest _)
    "Select the inner part of tags in the current org heading" :jump t
    (let ((parse (evil-org--parse-headline)))
    (when (nth 7 parse)
    (list (1+ (nth 7 parse)) (1- (nth 8 parse))))))

    (map! :map 'evil-inner-text-objects-map
    "h h" #'evil-org-headline-title
    "h t" #'evil-org-headline-inner-todo
    "h p" #'evil-org-headline-inner-priority
    "h a" #'evil-org-headline-inner-tags)

    (map! :map 'evil-outer-text-objects-map
    "h h" #'evil-org-headline
    "h t" #'evil-org-headline-todo
    "h p" #'evil-org-headline-priority
    "h a" #'evil-org-headline-tags))
  • -CameL case

    -
    +CameL case

    (after! evil
    (evil-define-text-object evil-prog-camelcase (c &rest _)
    "Select a camelCase \"word\". For instance, if cursor is at | in
    camelCase|dWord, then it selects \"Cased\"." :jump t
    (let ((case-fold-search nil))
    (if-let* ((_ (looking-at-p "[[:lower:]]"))
    (begin (save-excursion (re-search-backward "[[:upper:]]\\|[^[:alpha:]].")
    (match-end 0)))
    (end (save-excursion (re-search-forward "[^[:lower:]]"))))
    (list (1- begin) (1- end))
    (if-let* ((_ (looking-at-p "[[:upper:]]"))
    (end (save-excursion (re-search-forward "[[:alpha:]][[:upper:]]\\|[^[:alpha:]]")
    (match-end 0))))
    (list (point) (1- end)))))))

    (map! :mode 'prog-mode
    :map 'evil-inner-text-objects-map
    "l" #'evil-prog-camelcase)
  • -Scroll

    -
    +Scroll
    (setq mouse-wheel-scroll-amount '(3 ((shift) . 6)) ;; one line at a time
    mouse-wheel-progressive-speed nil ;; don't accelerate scrolling
    scroll-margin 0
    scroll-step 1) ;; keyboard scroll one line at a time

    (when (fboundp 'pixel-scroll-precision-mode)
    (pixel-scroll-precision-mode +1)
    (setq pixel-scroll-precision-interpolate-mice nil))

    -Variables

    -
    -
    (setq-default fill-column 80)

    (setq amalgamating-undo-limit 3)

    (setq tab-always-indent t)

    ;; (setq company-idle-delay 0.1
    ;; company-minimum-prefix-length 1)

    (setq mouse-drag-and-drop-region t
    mouse-drag-and-drop-region-cut-when-buffers-differ t
    mouse-drag-and-drop-region-show-tooltip nil)

    (setq default-input-method "TeX")
    -

    Deixa o text-scale-mode mais devagar.

    -
    +Variables
    +
    (setq-default fill-column 80)

    (setq amalgamating-undo-limit 3)

    (setq tab-always-indent t)

    ;; (setq company-idle-delay 0.1
    ;; company-minimum-prefix-length 1)

    (setq mouse-drag-and-drop-region t
    mouse-drag-and-drop-region-cut-when-buffers-differ t
    mouse-drag-and-drop-region-show-tooltip nil)

    (setq default-input-method "TeX")

    Deixa o text-scale-mode mais devagar.

    (setq text-scale-mode-step 1.05)

    Shrink whitespace -bindings

    -
    +bindings
    (setq doom-leader-alt-key "M-SPC")
    (map! :i "C-SPC" #'cycle-spacing)

    -Highlighting (Tree-sitter)

    -
    +Highlighting (Tree-sitter)
    (global-tree-sitter-mode)
    (add-hook 'tree-sitter-after-on-hook #'tree-sitter-hl-mode)

    -Saving

    -

    Desabilita a mensagem de salvamento.

    -
    +Saving

    Desabilita a mensagem de salvamento.

    (advice-add 'save-buffer :around #'advice--inhibit-message)

    -Popups

    -
    +Popups
    (setq +popup-defaults
    '(:side bottom
    :height 0.3
    :width 130
    :quit t
    :select ignore
    :ttl 5))

    (setq +popup-default-alist
    '((window-height . 0.3)
    (reusable-frames . visible)))

    -Others

    -
    +Others
    (remove-hook! '(org-mode-hook text-mode-hook) #'flyspell-mode)
    (remove-hook! 'org-mode-hook #'org-cdlatex-mode)

    (setq vterm-shell "zsh"
    delete-by-moving-to-trash t
    mouse-autoselect-window nil)

    Formatting

    -Apheleia

    -
    -
    (package! apheleia)
    -
    +Apheleia
    +
    (package! apheleia)
    (use-package apheleia
    :config
    (push '(fourmolu . ("fourmolu" "--stdin-input-file" (or (buffer-file-name) (buffer-name)))) apheleia-formatters)
    (setf (alist-get 'latexindent apheleia-formatters) '("latexindent" "-y=defaultIndent:' '" "--logfile=/dev/null"))
    (setf (alist-get 'haskell-mode apheleia-mode-alist) 'fourmolu))

    -WS butler

    -
    +WS butler
    (after! ws-butler
    (setq ws-butler-global-exempt-modes
    '(special-mode
    comint-mode
    term-mode
    eshell-mode
    diff-mode
    markdown-mode
    org-mode
    latex-mode)))

    @@ -1031,24 +968,20 @@

    Table of Contents

    Expand-region -bindings

    -
    +bindings

    (use-package expand-region
    :config
    (map! :n "C-a" #'er/expand-region
    "C-S-a" #'er/contract-region))

    Overall UI

    -Doom dashboard

    -

    Small dashboard changes:

    -
    +Doom dashboard

    Small dashboard changes:

    ;; Disables "benchmark" echo message
    (remove-hook 'window-setup-hook #'doom-display-benchmark-h)

    @@ -1057,60 +990,44 @@

    Table of Contents

  • -Fonts

    -

    Note: the twemoji font is the CBDT/CBLC variant from Fedora, and in AUR it is named ttf-twemoji. The SVG-in-OTF variant will not work!

    -

    If it works, you will see a twemoji smile: 🙂

    -
    +Fonts

    Note: the twemoji font is the CBDT/CBLC variant from Fedora, and in AUR it is named ttf-twemoji. The SVG-in-OTF variant will not work!

    If it works, you will see a twemoji smile: 🙂

    (setq doom-font                (font-spec :family "Victor Mono" :size 19 :weight 'medium)
    doom-variable-pitch-font (font-spec :family "IBM Plex Sans" :size 19 :weight 'normal)
    doom-serif-font (font-spec :family "IBM Plex Mono" :weight 'light))
    ;; doom-unicode-font (font-spec :family "JuliaMono" :weight 'normal))

    ;; Colocamos uma ordem de prioridade para tentar ter todos os unicodes e emojis.
    (setq use-default-font-for-symbols t)
    (defun my/adjust-fonts ()
    ;; (set-fontset-font t 'unicode (font-spec :family "Concrete Math"))
    (set-fontset-font t 'unicode (font-spec :family "Julia Mono") nil 'append)
    (set-fontset-font t 'emoji "Twemoji" nil 'prepend))

    (add-hook! 'after-setting-font-hook #'my/adjust-fonts)
  • -Comments and keywords

    -

    Deixamos os comentários itálicas, e os keywords oblíquos.

    -
    +Comments and keywords

    Deixamos os comentários itálicas, e os keywords oblíquos.

    (custom-set-faces!
    '(font-lock-comment-face :slant italic :weight normal)
    '(font-lock-keyword-face :slant italic :weight normal))
  • -Child frames

    -
    +Child frames

    (custom-set-faces!
    `(child-frame-border :inherit default))
  • -Echo area

    -
    +Echo area

    (defun customize-echo ()
    (with-current-buffer " *Echo Area 0*"
    (face-remap-add-relative 'default '(:family "Julia Mono"))
    (face-remap-add-relative 'default '(:height 140 :inherit shadow)))
    (with-current-buffer " *Echo Area 1*"
    (face-remap-add-relative 'default '(:family "Julia Mono"))
    (face-remap-add-relative 'default '(:height 140 :inherit shadow))))

    ;; (add-hook 'doom-load-theme-hook #'customize-echo 40)
  • -Icons

    -

    Adjusts the icon sizes so they are a bit smaller.

    -
    +Icons

    Adjusts the icon sizes so they are a bit smaller.

    (setq all-the-icons-scale-factor 0.88)
  • -Ligatures

    -
    -
    (package! ligature :recipe (:host github :repo "mickeynp/ligature.el"))
    -
    +Ligatures

    +
    (package! ligature :recipe (:host github :repo "mickeynp/ligature.el"))
    (use-package ligature
    :config
    (ligature-set-ligatures
    't '("</" "</>" "/>" "~-" "-~" "~@" "<~" "<~>" "<~~" "~>" "~~"
    "~~>" ">=" "<=" "<!--" "##" "###" "####" "|-" "-|" "|->"
    "<-|" ">-|" "|-<" "|=" "|=>" "<-" "<--" "-->" "->" "-<"
    ">->" ">>-" "<<-" "<->" "->>" "-<<" "<-<" "==>" "=>" "=/="
    "!==" "!=" "<==" ">>=" "=>>" ">=>" "<=>" "<=<" "<<=" "=<<"
    ".-" ".=" "=:=" "=!=" "==" "===" "::" ":=" ":>" ":<" ">:"
    "<|" "<|>" "|>" "<>" "<$" "<$>" "$>" "<+" "<+>" "+>"
    "?=" "/=" "/==" "/\\" "\\/" "__" "&&" "++" "+++"))
    ;; Enables ligature checks globally in all buffers. You can also do it
    ;; per mode with `ligature-mode'.
    (global-ligature-mode t))
  • -Mixed-pitch

    -
    -
    (defface my-mixed-pitch-face '((t . nil))
    "Face for `mixed-pitch-mode'")
    (custom-set-faces!
    '(my-mixed-pitch-face :family "Alegreya Sans Scaled" :height 1.1))

    (setq mixed-pitch-face 'my-mixed-pitch-face
    mixed-pitch-set-height nil)
    -

    No modeline pode aparecer um trecho com fonte font-lock-string-face. Como fica feio, vamos removê-lo.

    -

    Além disso, org-drawer não está na lista por padrão.

    -
    +Mixed-pitch

    +
    (defface my-mixed-pitch-face '((t . nil))
    "Face for `mixed-pitch-mode'")
    (custom-set-faces!
    '(my-mixed-pitch-face :family "Alegreya Sans Scaled" :height 1.1))

    (setq mixed-pitch-face 'my-mixed-pitch-face
    mixed-pitch-set-height nil)

    No modeline pode aparecer um trecho com fonte font-lock-string-face. Como fica feio, vamos removê-lo.

    Além disso, org-drawer não está na lista por padrão.

    (after! mixed-pitch
    (setq mixed-pitch-fixed-pitch-faces
    (seq-difference
    (seq-union mixed-pitch-fixed-pitch-faces
    '(org-drawer))
    '(font-lock-string-face diff-added diff-removed))))
  • -Yasnippet

    -
    +Yasnippet

    (custom-set-faces!
    `(yas-field-highlight-face
    :inherit nil
    :background ,(doom-blend "#b315b3" (face-attribute 'default :background) 0.2)
    :foreground "undefined"))
  • @@ -1119,21 +1036,17 @@

    Table of Contents

  • -Faces

    -
    +Faces

    (custom-set-faces!
    '(mode-line :height 105 :family "Julia Mono")
    '(mode-line-inactive :height 105 :family "Julia Mono")
    '(doom-modeline-buffer-modified :underline t :inherit nil)
    '(doom-modeline-info :foreground "white"))
    (setq! doom-modeline-height 22
    doom-modeline-bar-width 1)
  • -Doom mode-line

    -
    -
    (setq doom-modeline-irc nil
    doom-modeline-icon nil)
    -
    +Doom mode-line

    +
    (setq doom-modeline-irc nil
    doom-modeline-icon nil)
    (after! doom-modeline
    (doom-modeline-def-segment buffer-name
    "Display the current buffer's name, without any other information."
    (concat
    (doom-modeline-spc)
    (doom-modeline--buffer-name)))

    (doom-modeline-def-segment pdf-icon
    "PDF icon from all-the-icons."
    (concat
    (doom-modeline-spc)
    (doom-modeline-icon 'octicon "file-pdf" nil nil
    :face (if (doom-modeline--active)
    'all-the-icons-red
    'mode-line-inactive)
    :v-adjust 0.02)))

    (defun doom-modeline-update-pdf-pages ()
    "Update PDF pages."
    (setq doom-modeline--pdf-pages
    (let ((current-page-str (number-to-string (eval `(pdf-view-current-page))))
    (total-page-str (number-to-string (pdf-cache-number-of-pages))))
    (concat
    (propertize
    (concat (make-string (- (length total-page-str) (length current-page-str)) 32)
    " P" current-page-str)
    'face 'mode-line)
    (propertize (concat "/" total-page-str) 'face 'doom-modeline-buffer-minor-mode)))))

    (doom-modeline-def-segment pdf-pages
    "Display PDF pages."
    (if (doom-modeline--active) doom-modeline--pdf-pages
    (propertize doom-modeline--pdf-pages 'face 'mode-line-inactive)))

    (doom-modeline-def-modeline 'pdf
    '(bar window-number pdf-pages pdf-icon buffer-name)
    '(misc-info matches major-mode process vcs)))

  • -Tab bar

    -
    +Tab bar
    (map! :n "M-z" #'toggle-frame-tab-bar)

    @@ -1142,50 +1055,39 @@

    Table of Contents

  • -Esconder algumas coisas

    -

    Roubado do tecosaur.

    -
    +Esconder algumas coisas

    Roubado do tecosaur.

    (defcustom treemacs-file-ignore-extensions
    '("aux" "ptc" "fdb_latexmk" "fls" "synctex.gz" "toc" ;; LaTeX
    "glg" "glo" "gls" "glsdefs" "ist" "acn" "acr" "alg" ;; LaTeX - glossary
    "mw" ;; LaTeX - pgfplots
    "pdfa.xmpi") ;; LaTeX - pdfx
    "File extension which `treemacs-ignore-filter' will ensure are ignored"
    :safe #'string-list-p)

    (defcustom treemacs-file-ignore-globs
    '("*/_minted-*" ;; LaTeX
    "*/.auctex-auto" "*/_region_.log" "*/_region_.tex") ;; AucTeX
    "Globs which will are transformed to `treemacs-file-ignore-regexps'
    which `treemacs-ignore-filter' will ensure are ignored"
    :safe #'string-list-p)
  • -Make treemacs fringes appear

    -

    They only appear if this variable is set to a value \(\ge\) 7.

    -
    +Make treemacs fringes appear

    They only appear if this variable is set to a value \(\ge\) 7.

    (setq doom-themes-treemacs-bitmap-indicator-width 7)
  • -Theme

    -
    +Theme
    (setq doom-theme 'ef-cherie)
    1. -ef-themes

      -
      +ef-themes

      (package! ef-themes)

    -Window divisors

    -
    +Window divisors
    (setq window-divider-default-bottom-width 2   ; default is 1
    window-divider-default-right-width 2) ; default is 1

    Living in Emacs

    -LSP

    -
    -
    (unpin! lsp-mode)
    -
    -
    (use-package lsp
    :custom
    (lsp-completion-provider :none)
    (lsp-lens-enable t)
    (lsp-enable-snippet nil)
    (lsp-use-plists "true")
    :init
    (defun my/lsp-mode-setup-completion ()
    (setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults))
    '(orderless)))
    :hook
    (lsp-completion-mode . my/lsp-mode-setup-completion))
    -
    +LSP
    +
    (unpin! lsp-mode)
    +
    (use-package lsp
    :custom
    (lsp-completion-provider :none)
    (lsp-lens-enable t)
    (lsp-enable-snippet nil)
    (lsp-use-plists "true")
    :init
    (defun my/lsp-mode-setup-completion ()
    (setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults))
    '(orderless)))
    :hook
    (lsp-completion-mode . my/lsp-mode-setup-completion))
    (map! :map 'lsp-mode-map :leader :n "c f" #'lsp-format-buffer)

    -LSP-UI

    -
    +LSP-UI
    (setq lsp-ui-sideline-diagnostic-max-lines 10)

    @@ -1197,164 +1099,127 @@

    Table of Contents

  • -Installation

    -
    +Installation

    (package! eat)
  • -Calibre

    -
    +Calibre
    (package! calibredb)

    VC/Git/Magit

    -Magit Todos

    -
    +Magit Todos
    (put 'magit-todos-exclude-globs 'safe-local-variable #'listp)

    -Git Auto Commit Mode

    -
    -
    (package! git-auto-commit-mode)
    -
    +Git Auto Commit Mode
    +
    (package! git-auto-commit-mode)
    (pushnew! safe-local-variable-values '(gac-automatically-push-p . t))

    ORGANIZE Features

    -Benchmark init

    -
    +Benchmark init
    (package! benchmark-init :recipe (:host github :repo "kekeimiku/benchmark-init-el"))

    -Citations and Citar

    -
    +Citations and Citar
    (use-package citar
    :custom
    (citar-file-open-functions '(("pdf" . citar-file-open-external)))
    (citar-bibliography '("/home/lucas/Zotero/bibs/all.bib"))
    (org-cite-csl-styles-dir "/home/lucas/Zotero/styles")
    (citar-symbols `((file ,(all-the-icons-faicon "file-o" :face 'all-the-icons-green :v-adjust -0.1) . " ")
    (note ,(all-the-icons-material "speaker_notes" :face 'all-the-icons-blue :v-adjust -0.3) . " ")
    (link ,(all-the-icons-octicon "link" :face 'all-the-icons-orange :v-adjust 0.01) . " ")))
    (citar-symbol-separator " "))

    -citar-org-roam templates

    -
    +citar-org-roam templates
    (setq citar-org-roam-note-title-template "${author editor}: ${title}")

    -Org-cite-csl-activate

    -
    -
    (unpin! citar) 
    (package! oc-csl-activate :recipe (:host github :repo "andras-simonyi/org-cite-csl-activate"))
    -
    +Org-cite-csl-activate
    +
    (unpin! citar) 
    (package! oc-csl-activate :recipe (:host github :repo "andras-simonyi/org-cite-csl-activate"))
    (use-package oc-csl-activate
    :after (org citar)
    :config
    (setq org-cite-activate-processor 'csl-activate
    org-cite-csl-activate-use-document-style t)
    (add-hook 'org-font-lock-hook (lambda (&rest _) (org-cite-csl-activate-render-all)))
    (add-hook! org-mode
    (cursor-sensor-mode +1)))

    -CRDT

    -
    +CRDT
    (package! crdt)

    -Elfeed

    -
    -
    (after! elfeed
    (setq elfeed-search-filter "@3-year-old #200"))
    -
    +Elfeed
    +
    (after! elfeed
    (setq elfeed-search-filter "@3-year-old #200"))
    (package! elfeed-goodies :disable t)

    -Elfeed-tube

    -
    -
    (package! elfeed-tube)
    (package! elfeed-tube-mpv)
    -
    +Elfeed-tube
    +
    (package! elfeed-tube)
    (package! elfeed-tube-mpv)
    (use-package elfeed-tube
    :after elfeed
    :demand t
    :config
    ;; (setq elfeed-tube-auto-save-p nil) ; default value
    ;; (setq elfeed-tube-auto-fetch-p t) ; default value
    (elfeed-tube-setup)

    :bind (:map elfeed-show-mode-map
    ("F" . elfeed-tube-fetch)
    ([remap save-buffer] . elfeed-tube-save)
    :map elfeed-search-mode-map
    ("F" . elfeed-tube-fetch)
    ([remap save-buffer] . elfeed-tube-save)))

    (use-package elfeed-tube-mpv
    :bind (:map elfeed-show-mode-map
    ("C-c C-f" . elfeed-tube-mpv-follow-mode)
    ("C-c C-w" . elfeed-tube-mpv-where)))

    -Esxml

    -
    +Esxml
    (package! esxml)

    -Engrave Faces

    -
    +Engrave Faces
    (package! engrave-faces)

    -Google translate

    -
    -
    (package! google-translate)
    -
    +Google translate
    +
    (package! google-translate)
    (use-package google-translate
    :commands (google-translate-version)
    :custom
    (google-translate-backend-method 'curl)
    :config
    (defun google-translate--search-tkk () "Search TKK." (list 430675 2721866130)))

    TODO -Olivetti

    -
    -
    (package! olivetti)
    -
    +Olivetti
    +
    (package! olivetti)
    (use-package olivetti
    :init
    (add-hook! olivetti-mode
    (if olivetti-mode
    (progn
    (remove-hook! lsp-mode #'lsp-ui-mode)
    (when (bound-and-true-p lsp-mode) (lsp-ui-mode -1)))
    (add-hook! lsp-mode #'lsp-ui-mode)
    (when (bound-and-true-p lsp-mode) (lsp-ui-mode +1))))

    :commands #'olivetti-mode
    :hook (org-mode . olivetti-mode)
    :config
    (map! :leader :desc "Centered mode" "t e" #'olivetti-mode)
    (map! :map 'olivetti-mode-map
    "C-c \\" nil
    "C-c |" nil)

    (setq-default olivetti-body-width 80
    olivetti-recall-visual-line-mode-entry-state nil)

    (after! persp-mode
    (defvar persp--olivetti-buffers-backup nil)

    (defun persp--olivetti-deactivate (fow)
    (dolist (b (mapcar #'window-buffer
    (window-list (selected-frame)
    'no-minibuf)))
    (with-current-buffer b
    (when (eq 'olivetti-split-window-sensibly
    split-window-preferred-function)
    (push b persp--olivetti-buffers-backup)
    (setq-local split-window-preferred-function nil)
    (olivetti-reset-all-windows)))))

    (defun persp--olivetti-activate (fow)
    (dolist (b persp--olivetti-buffers-backup)
    (with-current-buffer b
    (setq-local split-window-preferred-function
    'olivetti-split-window-sensibly)))
    (setq persp--olivetti-buffers-backup nil))

    (add-hook 'persp-before-deactivate-functions #'persp--olivetti-deactivate)
    (add-hook 'persp-activated-functions #'persp--olivetti-activate)))

    -PDF Tools

    -
    -
    (unpin! pdf-tools)
    -
    +PDF Tools
    +
    (unpin! pdf-tools)
    (after! pdf-tools
    (defvar pdf-scroll-multiplier 2)

    (defun pdf-tools--scroll-mul (l)
    (mapcar (lambda (x) (* pdf-scroll-multiplier x)) l))

    (advice-add 'pdf-view-next-line-or-next-page :filter-args #'pdf-tools--scroll-mul)
    (advice-add 'pdf-view-previous-line-or-previous-page :filter-args #'pdf-tools--scroll-mul)
    (advice-add 'image-forward-hscroll :filter-args #'pdf-tools--scroll-mul)
    (advice-add 'image-backward-hscroll :filter-args #'pdf-tools--scroll-mul)

    (defun pdf-tools-center-page ()
    (interactive)
    (let* ((image (image-get-display-property))
    (edges (window-inside-edges))
    (win-width (- (nth 2 edges) (nth 0 edges)))
    (img-width (ceiling (car (image-display-size image)))))
    (image-set-window-hscroll (max 0 (/ (- img-width win-width -1) 2)))))

    (advice-add 'pdf-view-shrink :after (lambda (_) (pdf-tools-center-page)))
    (advice-add 'pdf-view-enlarge :after (lambda (_) (pdf-tools-center-page)))

    (add-hook! '(doom-load-theme-hook ef-themes-post-load-hook)
    (setq pdf-view-midnight-colors (cons (face-attribute 'default :foreground) (face-attribute 'default :background)))))

    -Perps-mode

    -

    The following advice disables the annoying, big and ugly messages when auto-saving.

    -
    +Perps-mode

    The following advice disables the annoying, big and ugly messages when auto-saving.

    (advice-add 'persp-parameters-to-savelist :around #'advice--inhibit-message)

    -Real auto-save

    -
    -
    (package! real-auto-save)
    -
    +Real auto-save
    +
    (package! real-auto-save)
    (use-package real-auto-save
    :after doom-first-file-hook
    :commands (real-auto-save-mode))

    (pushnew! safe-local-variable-values '(real-auto-save-interval . 0.5))

    Minor modes

    -Focus

    -
    -
    (package! focus :type 'local :recipe (:local-repo "lisp/lib/focus.el"))
    -
    +Focus
    +
    (package! focus :type 'local :recipe (:local-repo "lisp/lib/focus.el"))
    (setq focus-fraction 0.7)
    ;; (custom-set-faces!
    ;; '(focus-unfocused :inherit custom-comment-tag :foreground "gray"))

    -Iedit

    -

    Desativa uma mensagem chata quando apertamos M-d

    -
    +Iedit

    Desativa uma mensagem chata quando apertamos M-d

    (setq iedit-toggle-key-default nil)

    Languages

    -Dart (flutter)

    -
    +Dart (flutter)
    (setq flutter-sdk-path "/opt/flutter")

    -F#

    -
    +F#
    (setq inferior-fsharp-program "dotnet fsi --readline-")

    -Haskell

    -
    -
    (setq lsp-haskell-server-path "haskell-language-server-wrapper"
    lsp-haskell-formatting-provider "fourmolu"
    lsp-haskell-plugin-eval-global-on t
    lsp-haskell-plugin-class-global-on nil
    lsp-haskell-plugin-ghcide-type-lenses-global-on nil
    lsp-haskell-plugin-ghcide-completions-config-auto-extend-on nil
    lsp-haskell-plugin-import-lens-code-lens-on nil
    lsp-haskell-plugin-import-lens-code-actions-on nil)
    -

    Work around bad doom-emacs use of :prelude.

    -
    +Haskell
    +
    (setq lsp-haskell-server-path "haskell-language-server-wrapper"
    lsp-haskell-formatting-provider "fourmolu"
    lsp-haskell-plugin-eval-global-on t
    lsp-haskell-plugin-class-global-on nil
    lsp-haskell-plugin-ghcide-type-lenses-global-on nil
    lsp-haskell-plugin-ghcide-completions-config-auto-extend-on nil
    lsp-haskell-plugin-import-lens-code-lens-on nil
    lsp-haskell-plugin-import-lens-code-actions-on nil)

    Work around bad doom-emacs use of :prelude.

    (remove-hook 'haskell-mode-local-vars-hook #'lsp!)

    Lisps

    -Parinfer

    -
    +Parinfer
    (use-package parinfer-rust-mode
    :when (bound-and-true-p module-file-suffix)
    :hook (emacs-lisp-mode . parinfer-rust-mode)
    :init
    (setq parinfer-rust-library
    (concat doom-data-dir "parinfer-rust/"
    (cond (IS-MAC "parinfer-rust-darwin.so")
    (IS-LINUX "parinfer-rust-linux.so")
    (IS-WINDOWS "parinfer-rust-windows.dll")
    (IS-BSD "libparinfer_rust.so"))))
    :config
    (map! :map parinfer-rust-mode-map
    :localleader
    "P" #'parinfer-rust-switch-mode
    "p" #'parinfer-rust-toggle-disable))
    • paren Mode gives you full control of parens, while Parinfer corrects @@ -1363,77 +1228,57 @@

      Table of Contents

    • indent Mode gives you full control of indentation, while Parinfer corrects or inserts close-parens where appropriate. Specifically, it only touches the groups of close-parens at the end of each line.
    • -
    • smart Mode is like Indent Mode, but it tries to preserve the structure too.
    -

    NOTE TO SELF: smart and indent won’t allow inserting unmached }

    -
    +
  • smart Mode is like Indent Mode, but it tries to preserve the structure too.
  • NOTE TO SELF: smart and indent won’t allow inserting unmached }

    (setq parinfer-rust-preferred-mode "smart")
    1. -Hooks

      -

      I don’t want to run it in all lisps, just elisp. Had some issues with kbd-mode.

      -
      +Hooks

      I don’t want to run it in all lisps, just elisp. Had some issues with kbd-mode.

      (remove-hook 'lisp-mode-hook #'parinfer-rust-mode)
      (add-hook! 'kbd-mode-hook (parinfer-rust-mode -1))

    -kmonad

    -
    -
    (package! kbd-mode :recipe (:host github :repo "kmonad/kbd-mode"))
    -
    +kmonad
    +
    (package! kbd-mode :recipe (:host github :repo "kmonad/kbd-mode"))
    (use-package kbd-mode)

    -Lean

    -
    -
    (package! lean4-mode :recipe
    (:host github
    :repo "leanprover/lean4-mode"
    :files ("*.el" "data")))
    -
    -
    (use-package lean4-mode
    :commands (lean4-mode))
    -
    +Lean
    +
    (package! lean4-mode :recipe
    (:host github
    :repo "leanprover/lean4-mode"
    :files ("*.el" "data")))
    +
    (use-package lean4-mode
    :commands (lean4-mode))
    (set-popup-rule! "^\\*Lean Goal\\*"
    :side 'right
    :ttl 10
    :quit 'current
    :width 50
    :select nil
    :modeline nil)

    -LaTeX

    -
    -
    (package! latex-preview-pane :disable t)
    -
    +LaTeX
    +
    (package! latex-preview-pane :disable t)
    (after! tex
    (setq TeX-save-query nil
    TeX-view-evince-keep-focus t
    TeX-indent-open-delimiters "["
    TeX-indent-close-delimiters "]"
    TeX-view-program-selection '((output-pdf "Zathura"))
    TeX-view-program-list
    '(("Sioyek-flatpak"
    ("flatpak run --file-forwarding com.github.ahrm.sioyek @@ %o @@"
    (mode-io-correlate " --forward-search-file \"%b\" --forward-search-line %n --inverse-search \"emacsclient -n +%2 %1\""))))

    font-latex-fontify-script 'multi-level
    font-latex-fontify-script-max-level 3
    font-latex-script-display '((raise -0.4) . (raise 0.4)))

    (custom-set-faces!
    '(font-latex-subscript-face :height 0.8)
    '(font-latex-superscript-face :height 0.8)))

    (add-hook! (LaTeX-mode latex-mode)
    (display-line-numbers-mode -1)
    (setq fill-nobreak-predicate nil
    fill-column 9999999999))

    -Do not auto fill my text when I wrap it inside an environment

    -
    +Do not auto fill my text when I wrap it inside an environment
    (defadvice! latex-environment-do-not-justify (f &rest r)
    :around 'LaTeX-environment
    (let ((auto-fill-function nil))
    (apply f r)))

    Org

    -Installation

    -
    +Installation
    (unpin! org)

    -Variáveis

    -
    -
    (setq org-directory "~/dados/org"
    org-attach-id-dir "data/"
    org-fold-core-style 'overlays
    org-startup-folded nil
    org-startup-indented t
    org-support-shift-select t
    org-hide-emphasis-markers nil
    org-src-window-setup 'plain
    org-highlight-latex-and-related '(latex script)
    org-emphasis-regexp-components '("-[:space:]('\"{" "-[:space:].,:!?;'\")}\\[" "{}*[:space:]" "." 1)
    org-indent-indentation-per-level 2)
    -
    +Variáveis
    +
    (setq org-directory "~/dados/org"
    org-attach-id-dir "data/"
    org-fold-core-style 'overlays
    org-startup-folded nil
    org-startup-indented t
    org-support-shift-select t
    org-hide-emphasis-markers nil
    org-src-window-setup 'plain
    org-highlight-latex-and-related '(latex script)
    org-emphasis-regexp-components '("-[:space:]('\"{" "-[:space:].,:!?;'\")}\\[" "{}*[:space:]" "." 1)
    org-indent-indentation-per-level 2)
    (after! org-src
    (pushnew! org-src-lang-modes
    '("html" . web)
    '("lean4" . lean4)))

    -Attach & Download

    -

    We can make org-attach work before the first headline:

    -
    -
    (after! org-attach
    (setq org-attach-auto-tag nil
    org-attach-id-to-path-function-list
    '(org-attach-id-ts-folder-format org-attach-id-uuid-folder-format identity)))
    -
    +Attach & Download

    We can make org-attach work before the first headline:

    +
    (after! org-attach
    (setq org-attach-auto-tag nil
    org-attach-id-to-path-function-list
    '(org-attach-id-ts-folder-format org-attach-id-uuid-folder-format identity)))
    (after! org-download
    (setq org-download-image-org-width 300))
    1. -Org-attach-extra

      -
      -
      (add-load-path! "/home/lucas/.doom.d/lisp/lib/org-attach-extra/")
      -
      +Org-attach-extra

      +
      (add-load-path! "/home/lucas/.doom.d/lisp/lib/org-attach-extra/")
      (use-package org-attach-extra
      :after org)

    @@ -1448,81 +1293,64 @@

    Table of Contents

    Hide fragment delimiters -monkey

    -
    +monkey

    (defadvice! my-org-do-latex-and-related (limit)
    "Highlight LaTeX snippets and environments, entities and sub/superscript.
    Stop at first highlighted object, if any. Return t if some
    highlighting was done, nil otherwise."
    :override #'org-do-latex-and-related
    (when (org-string-nw-p org-latex-and-related-regexp)
    (let ((latex-prefix-re (rx (or "$" "\\(" "\\[")))
    (blank-line-re (rx (and "\n" (zero-or-more (or " " "\t")) "\n"))))
    (catch 'found
    (while (and (< (point) limit)
    (re-search-forward org-latex-and-related-regexp nil t))
    (cond
    ((>= (match-beginning 0) limit)
    (throw 'found nil))
    ((cl-some (lambda (f)
    (memq f '(org-code org-verbatim underline
    org-special-keyword)))
    (save-excursion
    (goto-char (1+ (match-beginning 0)))
    (face-at-point nil t))))
    ;; Try to limit false positives. In this case, ignore
    ;; $$...$$, \(...\), and \[...\] LaTeX constructs if they
    ;; contain an empty line.
    ((save-excursion
    (goto-char (match-beginning 0))
    (and (looking-at-p latex-prefix-re)
    (save-match-data
    (re-search-forward blank-line-re (1- (match-end 0)) t)))))
    (t
    (let* ((offset (if (memq (char-after (1+ (match-beginning 0)))
    '(?_ ?^))
    1
    0))
    (start (+ offset (match-beginning 0)))
    (end (match-end 0)))
    (if (memq 'native org-highlight-latex-and-related)
    (org-src-font-lock-fontify-block "latex" start end)
    (font-lock-prepend-text-property start end
    'face 'org-latex-and-related))
    ;; my code starts here
    (when (and org-hide-emphasis-markers (< (+ start 4) end))
    (cond ((member (buffer-substring start (+ start 2)) '("$$" "\\("))
    (add-text-properties start (+ start 2) '(invisible org-link)))
    ((string= (buffer-substring (1+ start) (+ start 2)) "$")
    (add-text-properties (1+ start) (+ start 2) '(invisible org-link))))
    (cond ((member (buffer-substring end (- end 2)) '("$$" "\\)"))
    (add-text-properties end (- end 2) '(invisible org-link)))
    ((string= (buffer-substring (1- end) (- end 2)) "$")
    (add-text-properties (1- end) (- end 2) '(invisible org-link)))))
    ;; my code ends here
    (add-text-properties (+ offset (match-beginning 0)) (match-end 0)
    '(font-lock-multiline t))
    (throw 'found t)))))
    nil))))
  • -Fragment fontification without org-block

    -

    Org reuses the org-src-font-lock-fontify-block function to fontify LaTeX fragments natively. But this function adds the very inappropiate face org-block to everything. Let’s remove it when the native block is one of our fragments.

    -
    +Fragment fontification without org-block

    Org reuses the org-src-font-lock-fontify-block function to fontify LaTeX fragments natively. But this function adds the very inappropiate face org-block to everything. Let’s remove it when the native block is one of our fragments.

    (defvar org--font-locking-latex-fragment nil)

    (undefadvice! signal-font-locking-latex (orig-fun &rest args)
    :around #'org-do-latex-and-related
    (let ((org--font-locking-latex-fragment t))
    (apply orig-fun args)))

    (undefadvice! do-not-org-block-my-latex-advice (_ start end)
    :after #'org-src-font-lock-fontify-block
    (when org--font-locking-latex-fragment
    (alter-text-property start end 'face (lambda (l) (remove 'org-block l)))))
  • Better alignment for mixed-pitch

    -
    -
    +

    -attachment:Captura de tela de 2022-04-21 12-12-49.png -

    Before

    -
    +attachment:Captura de tela de 2022-04-21 12-12-49.png +
    Before

    -attachment:Captura de tela de 2022-04-21 12-13-01.png -

    After

    -
    -
    (defun org-add-indent-face-to-prespace ()
    (setq
    org-font-lock-extra-keywords
    (append (delete
    '("^ *\\([-+]\\|\\(?:[0-9]+\\|[a-zA-Z]\\)[).]\\)[ \t]" 1 'org-list-dt append)
    org-font-lock-extra-keywords)
    ;; Add org-indent face to all spaces at line starts
    '(("^\\( +\\)"
    (1 'org-indent append))
    ;; Also fontify * bullets
    ("^ +\\(\\*\\)\\([ \t]\\)"
    (1 'org-list-dt append)
    (2 'org-indent append))
    ;; This is modified from user @psii
    ;; https://github.com/doomemacs/themes/pull/716
    ("^ *\\([-+]\\|\\(?:[0-9]+\\|[a-zA-Z]\\)[).]\\)\\([ \t]\\)"
    (1 'org-list-dt append)
    (2 'org-indent append))))))

    (add-hook 'org-font-lock-set-keywords-hook #'org-add-indent-face-to-prespace)
    -

    We can also make list bullets fixed-pitch, so they are even more aligned.

    -
    +attachment:Captura de tela de 2022-04-21 12-13-01.png +
    After

    +
    (defun org-add-indent-face-to-prespace ()
    (setq
    org-font-lock-extra-keywords
    (append (delete
    '("^ *\\([-+]\\|\\(?:[0-9]+\\|[a-zA-Z]\\)[).]\\)[ \t]" 1 'org-list-dt append)
    org-font-lock-extra-keywords)
    ;; Add org-indent face to all spaces at line starts
    '(("^\\( +\\)"
    (1 'org-indent append))
    ;; Also fontify * bullets
    ("^ +\\(\\*\\)\\([ \t]\\)"
    (1 'org-list-dt append)
    (2 'org-indent append))
    ;; This is modified from user @psii
    ;; https://github.com/doomemacs/themes/pull/716
    ("^ *\\([-+]\\|\\(?:[0-9]+\\|[a-zA-Z]\\)[).]\\)\\([ \t]\\)"
    (1 'org-list-dt append)
    (2 'org-indent append))))))

    (add-hook 'org-font-lock-set-keywords-hook #'org-add-indent-face-to-prespace)

    We can also make list bullets fixed-pitch, so they are even more aligned.

    (after! mixed-pitch
    (add-to-list 'mixed-pitch-fixed-pitch-faces 'org-list-dt))
  • -Fontify counter cookies

    -
    +Fontify counter cookies

    (defun org-fontify-counter-cookies ()
    (setq
    org-font-lock-extra-keywords
    (append org-font-lock-extra-keywords
    '(("^[ \t]*\\(?:[-+*]\\|\\(?:[0-9]+\\|[a-zA-Z]\\)[.)]\\)[ \t]+\\(\\[@\\(?:start:\\)?\\(?:[0-9]+\\|[a-zA-Z]\\)\\]\\)"
    (1 'org-property-value prepend))))))

    (add-hook 'org-font-lock-set-keywords-hook #'org-fontify-counter-cookies)
  • -Faces

    -
    +Faces

    (custom-set-faces!
    '(font-latex-math-face :foreground unspecified)
    '(org-indent :inherit org-hide)
    '(org-headline-done :foreground unspecified)
    '(org-verse :inherit nil)
    '(font-latex-sedate-face :inherit nil))

    (custom-set-faces!
    '(org-link :weight normal))
    (custom-set-faces!
    `(outline-1 :weight bold :inherit nil)
    `(outline-2 :weight bold :inherit nil)
    `(outline-3 :weight bold :inherit nil)
    `(outline-4 :weight bold :inherit nil)
    `(outline-5 :weight bold :inherit nil)
    '(outline-6 :weight bold :inherit nil)
    '(outline-8 :weight bold :inherit nil)
    '(outline-9 :weight bold :inherit nil))
  • Bindings -bindings

    -
    +bindings
    (map! :mode 'org-mode
    :g "C-S-s" (cmd! (org-latex-export-to-pdf nil))
    :i "C-i" (cmd! (org-emphasize ?/))
    :i "C-b" (cmd! (org-emphasize ?*))
    :n "\\" #'org-edit-special
    :localleader "s p" #'org-paste-subtree)
    1. Better emphasis toggle -advice

      -

      If the point is before the emphasis marker, then org-emphasize should exit it.

      -
      +advice

      If the point is before the emphasis marker, then org-emphasize should exit it.

      (defadvice! org-emphasize-emph-exit-ad (char)
      :before-until #'org-emphasize
      (when (eq (char-after) char)
      (forward-char)
      t))
    2. -SPC s I to open subtree with narrowing

      -
      +SPC s I to open subtree with narrowing

      (map! :mode 'org-mode
      :map 'doom-leader-search-map
      "I" (cmd!
      (let ((this-buffer (current-buffer))
      (pos (point)))
      (consult-imenu)
      (org-tree-to-indirect-buffer)
      (setq indirect-buffer (current-buffer))
      ;; with-current-buffer does not save pos for some reason
      (switch-to-buffer this-buffer)
      (goto-char pos)
      (evil-scroll-line-to-center nil)
      (switch-to-buffer indirect-buffer))))

    -General config

    -
    +General config
    (add-hook! org-mode
    <<org-mode-hook>>
    nil)

    (set-popup-rule! "\*Org Src .+\*"
    :size 0.5)

    (after! org
    (require 'org-src))
    ;; (add-to-list 'org-src-block-faces '("latex" (:inherit default :extend t)))

    -Linking

    -
    +Linking
    (setq +org-roam-link-to-org-use-id 'use-existing
    org-id-link-to-org-use-id 'use-existing)

    -Logging

    -
    +Logging
    (setq org-log-states-order-reversed t
    org-log-done 'note
    org-log-into-drawer t)

    @@ -1531,82 +1359,63 @@

    Table of Contents

  • -Org-appear

    -
    -
    (package! org-appear)
    -
    +Org-appear

    +
    (package! org-appear)
    (use-package org-appear
    :after org
    :init
    (defun org-appear-toggle ()
    (interactive)
    (if org-appear-mode
    (progn (setq org-hide-emphasis-markers nil)
    (org-restart-font-lock)
    (org-appear-mode -1))
    (setq org-hide-emphasis-markers t)
    (org-restart-font-lock)
    (org-appear-mode 1)))
    :config
    (setq org-appear-autolinks nil
    org-appear-inside-latex t
    org-appear-autosubmarkers t)
    (map! :mode 'org-mode :leader "t a" #'org-appear-toggle))
  • -Org-noter

    -
    +Org-noter

    (setq org-noter-notes-search-path (list (expand-file-name "org-noter" org-roam-directory)))
  • -Org-remark

    -
    -
    (package! org-remark)
    -
    +Org-remark

    +
    (package! org-remark)
    (use-package org-remark
    :after org
    :config)
  • Org-roam-timestamps -ARCHIVE

    -
    -
    (package! org-roam-timestamps
    :recipe (:host github :repo "stites/org-roam-timestamps")
    :pin "fbbe57a7d6283624e567bd1ee46ebea3d179a321")
    -
    +ARCHIVE

    +
    (package! org-roam-timestamps
    :recipe (:host github :repo "stites/org-roam-timestamps")
    :pin "fbbe57a7d6283624e567bd1ee46ebea3d179a321")
    (use-package! org-roam-timestamps
    :after org-roam
    :config (org-roam-timestamps-mode))
  • -Org-superstar

    -
    -
    (package! org-superstar)
    -
    +Org-superstar

    +
    (package! org-superstar)
    (use-package org-superstar
    :after (org)
    :hook (org-mode . org-superstar-mode)
    :config
    (setq org-superstar-headline-bullets-list '(?◆ ?❉ ?🞱 ?🞽 ?✺)))
    ;; org-superstar-headline-bullets-list '("🙐" "🙑" "🙒" "🙓" "🙔" "🙕" "🙖" "🙗")))
  • Org-transclusion -monkey

    -
    -
    (package! org-transclusion)
    -
    -
    (use-package! org-transclusion
    :after org
    :init
    (map!
    :map global-map "<f12>" #'org-transclusion-add
    :leader
    :prefix "n"
    :desc "Org Transclusion Mode" "t" #'org-transclusion-mode)
    (setq org-transclusion-exclude-elements '(property-drawer keyword)))
    -
    -
    (after! org
    (defadvice! +org--recenter-after-follow-link-a (&rest _args)
    "Recenter after following a link, but only internal or file links."
    :after '(org-footnote-action
    org-follow-timestamp-link
    org-link-open-as-file
    org-link-search)
    (if-let* ((window (get-buffer-window)))
    (with-selected-window window
    (recenter)))))
    -
    +monkey

    +
    (package! org-transclusion)
    +
    (use-package! org-transclusion
    :after org
    :init
    (map!
    :map global-map "<f12>" #'org-transclusion-add
    :leader
    :prefix "n"
    :desc "Org Transclusion Mode" "t" #'org-transclusion-mode)
    (setq org-transclusion-exclude-elements '(property-drawer keyword)))
    +
    (after! org
    (defadvice! +org--recenter-after-follow-link-a (&rest _args)
    "Recenter after following a link, but only internal or file links."
    :after '(org-footnote-action
    org-follow-timestamp-link
    org-link-open-as-file
    org-link-search)
    (if-let* ((window (get-buffer-window)))
    (with-selected-window window
    (recenter)))))
    (defun org-transclusion-add-better-id (link plist)
    "Return a list for Org-ID LINK object and PLIST.
    Return nil if not found."
    (when (string= "id" (org-element-property :type link))
    ;; when type is id, the value of path is the id
    (let* ((both (split-string (org-element-property :path link) "::"))
    (id (cl-first both))
    (search (cl-second both))
    (mkr (ignore-errors (org-id-find id t)))
    (payload '(:tc-type "org-id")))
    (if mkr
    (append payload (org-transclusion-content-better-marker mkr search plist))
    (message
    (format "No transclusion done for this ID. Ensure it works at point %d, line %d"
    (point) (org-current-line)))
    nil))))

    (defun org-transclusion-content-better-marker (marker search plist)
    "Return a list of payload from MARKER and PLIST.
    This function is intended to be used for Org-ID. It delates the
    work to
    `org-transclusion-content-org-buffer-or-element'."
    (if (and marker (marker-buffer marker)
    (buffer-live-p (marker-buffer marker)))
    (progn
    (with-current-buffer (marker-buffer marker)
    (org-with-wide-buffer
    (goto-char marker)
    (when search
    (org-link-search search))
    (if (and (not search) (org-before-first-heading-p))
    (org-transclusion-content-org-buffer-or-element
    nil plist)
    (org-transclusion-content-org-buffer-or-element
    'only-element plist)))))
    (message "Nothing done. Cannot find marker for the ID.")))

    (defun org-transclusion-add-org-attach (link plist)
    "Return a list for attached file LINK object and PLIST.
    Return nil if not found."
    (when (string= "attachment" (org-element-property :type link))
    (let* ((both (split-string (org-element-property :path link) "::"))
    (path (org-attach-expand (cl-first both)))
    (search (cl-second both))
    (link_ (org-element-put-property link :path path))
    (link__ (org-element-put-property link_ :search-string search)))
    (or (org-transclusion-add-org-file link__ plist)
    (org-transclusion-add-other-file link__ plist)))))

    (after! org-transclusion
    ;; (defadvice! org-transclusion-no-fringe ()
    ;; :override #'org-transclusion-propertize-source
    ;; nil)
    (setq! org-transclusion-add-functions
    '(org-transclusion-add-src-lines
    org-transclusion-add-better-id
    org-transclusion-add-org-attach
    org-transclusion-add-org-file
    org-transclusion-add-other-file)))
  • -Gutter

    -

    O git-gutter não funciona bem com o org-indent-mode:

    -
    +Gutter

    O git-gutter não funciona bem com o org-indent-mode:

    (push 'org-mode git-gutter:disabled-modes)

    -Hook

    -
    +Hook
    (setq-local auto-save-visited-interval 0.2
    display-line-numbers nil)
    (setq line-spacing 5)
    (add-to-list 'completion-at-point-functions #'cape-dabbrev)

    -Organon

    -
    +Organon
    (define-minor-mode organon-follow-mode
    "Set whether organon should follow your every move in Emacs."
    :lighter " organon"
    :global t
    :group 'organon
    :init-value nil
    (if organon-follow-mode
    (progn
    (add-hook 'post-command-hook #'organon--update-position)
    (message "organon will now follow you around."))
    (remove-hook 'post-command-hook #'organon--update-position)
    (message "organon will now leave you alone.")))

    (defvar organon--last-pos nil)
    (defvar organon--conn nil)

    (defun organon--connect ()
    (require 'websocket)
    (unless organon--conn
    (websocket-open
    "ws://127.0.0.1:9160"
    :on-open (lambda (ws) (message "organon: connected") (setq organon--conn ws))
    :on-close (lambda (ws) (message "organon: disconnected") (setq organon--conn nil)))))

    (defun organon--get-info ()
    (list :id (org-entry-get nil "ID" t)
    :file (buffer-file-name)
    :anchor (or (org-entry-get nil "CUSTOM_ID")
    (condition-case nil
    (concat "h-" (nth 4 (org-heading-components)))
    (user-error nil)))))

    (defun organon--update-position ()
    (when-let ((_ (eq major-mode 'org-mode))
    (cur-pos (organon--get-info))
    (_ (not (equal cur-pos organon--last-pos))))
    (setq organon--last-pos cur-pos)
    (send-to-organon)))

    (defun send-to-organon ()
    (interactive)
    (organon--connect)
    (when organon--conn
    (let ((cur-info (organon--get-info)))
    (websocket-send-text organon--conn (json-encode cur-info)))))

    -Exporting

    -
    +Exporting
    (after! ox
    (add-to-list
    'org-export-smart-quotes-alist
    '("pt-br"
    (primary-opening :utf-8 "“" :html "&ldquo;" :latex "``" :texinfo "``")
    (primary-closing :utf-8 "”" :html "&rdquo;" :latex "''" :texinfo "''")
    (secondary-opening :utf-8 "‘" :html "&lsquo;" :latex "`" :texinfo "`")
    (secondary-closing :utf-8 "’" :html "&rsquo;" :latex "'" :texinfo "'")
    (apostrophe :utf-8 "’" :html "&rsquo;"))))
    1. -LaTeX

      -
      +LaTeX

      (after! org
      ;; Note to self: NEVER change this!!! You have had problems with uncompilable documents when you changed the default preamble in the past!!
      (setq org-latex-packages-alist nil
      org-latex-preview-default-process 'dvisvgm)

      (after! ox-latex
      (setq org-latex-pdf-process '("latexmk -f -pdf -%latex -interaction=nonstopmode -output-directory=%o %f")
      org-latex-compilers '("tectonic" "pdflatex" "xelatex" "lualatex")
      org-latex-compiler "xelatex"))

      (custom-reevaluate-setting 'org-latex-preview-process-alist)

      ;; To avoid issues with pdfcrop
      (plist-put! org-format-latex-options
      :background "Transparent"
      :scale 2.0))

    @@ -1620,28 +1429,22 @@

    Table of Contents

  • -Common

    -

    Shared with desktop and termux.

    -
    +Common

    Shared with desktop and termux.

    (after! org-roam
    (setq org-id-method 'ts
    org-roam-completion-everywhere nil))
  • -Desktop-specific

    -

    Those aren’t shared with termux.

    -
    +Desktop-specific

    Those aren’t shared with termux.

    (after! org-roam
    (setq org-roam-directory "~/dados/notas"
    org-roam-node-display-template
    #("${doom-hierarchy} ${doom-type} ${doom-tags}" 18 30
    (face font-lock-comment-face)
    31 43
    (face font-lock-comment-face))))
  • -Protocol

    -
    +Protocol

    (after! org-roam
    (require 'org-roam-protocol))
  • -Switch workspace

    -
    +Switch workspace

    (defadvice! yeet/org-roam-in-own-workspace-a (&rest _)
    "Open all roam buffers in their own workspace."
    :before #'org-roam-node-find
    :before #'org-roam-node-random
    :before #'org-roam-buffer-display-dedicated
    :before #'org-roam-buffer-toggle
    (when (modulep! :ui workspaces)
    (+workspace-switch "notas" t))
    (when (functionp 'tabspaces-switch-or-create-workspace)
    (tabspaces-switch-or-create-workspace "notas")))
  • @@ -1651,153 +1454,117 @@

    Table of Contents

  • -Roam

    -
    +Roam

    (after! org-roam
    (setq org-roam-capture-templates
    '(("d" "default" plain "%?"
    :target (file+head "%<%Y%m%d%H%M%S>.org" "#+title: ${title}
    #+language: pt
    ")
    :unnarrowed t)
    ("m" "math" plain "%?"
    :target (file+head "math/%<%Y%m%d%H%M%S>.org" "#+title: ${title}
    #+language: pt
    ")
    :unnarrowed t))))

  • -Roam-ref

    -
    +Roam-ref

    (setq org-roam-capture-ref-templates
    '(("m" "math" plain "%?"
    :target (file+head "math/%<%Y%m%d%H%M%S>.org" "#+title: ${title}\n\n${body}")
    :unnarrowed t)
    ("fr" "Add to my future-read list" entry "* ${title}\n%?"
    :target (file+olp "to-read.org" ("${title}"))
    :empty-lines-before 1 nil nil)
    ("r" "ref" plain "%?" :target
    (file+head "${slug}.org" "#+title: ${title}")
    :unnarrowed t)))
  • -Workarounds

    -
    -
    (defadvice! inhibit-redisplay-on-roam-autosync (fn)
    "Inhibit redisplay when syncing roam database on saves."
    :around #'org-roam-db-autosync--try-update-on-save-h
    (let ((inhibit-redisplay t)) (funcall fn)))
    -
    +Workarounds

    +
    (defadvice! inhibit-redisplay-on-roam-autosync (fn)
    "Inhibit redisplay when syncing roam database on saves."
    :around #'org-roam-db-autosync--try-update-on-save-h
    (let ((inhibit-redisplay t)) (funcall fn)))
    (defadvice! org-roam-db-insert-link--remove-search-a (f link)
    :around #'org-roam-db-insert-link
    (let ((newpath (car (split-string (org-element-property :path link) "::"))))
    (funcall f (org-element-put-property link :path newpath))))
  • -md-roam

    -
    +md-roam

    (package! md-roam :recipe (:host github :repo "nobiot/md-roam"))
  • -Org-roam-ui

    -
    -
    (unpin! org-roam)
    (package! org-roam-ui)
    -
    +Org-roam-ui
    +
    (unpin! org-roam)
    (package! org-roam-ui)
    (use-package! websocket
    :after org-roam-ui)

    (use-package! org-roam-ui
    :after org-roam
    :commands (org-roam-ui-mode)
    :config
    (setq org-roam-ui-sync-theme t
    org-roam-ui-follow t
    org-roam-ui-update-on-save t))

    -Org-protocol

    -
    +Org-protocol
    (after! org-protocol
    (add-to-list 'org-protocol-protocol-alist
    '("org-file" :protocol "org-file"
    :function org-protocol-goto-org-file)))

    (defun org-protocol-goto-org-file (info)
    (if-let ((id (plist-get info :id)))
    (org-id-goto id)
    (when-let ((file (plist-get info :file)))
    (org-open-file file)))
    nil)
    1. -Raise frame

      -

      Requires this extension to be installed.

      -
      +Raise frame

      Requires this extension to be installed.

      (defun org-protocol/select-current-frame ()
      ;; gnome stuff
      ;; (shell-command-to-string
      ;; (format "gdbus call \
      ;; --session \
      ;; --dest org.gnome.Shell \
      ;; --object-path /org/gnome/Shell/Extensions/Windows \
      ;; --method org.gnome.Shell.Extensions.Windows.List \
      ;; | cut -c 3- | rev | cut -c4- | rev \
      ;; | jq '.[] | select(.pid == %s) .id'" (emacs-pid))))))
      ;; (dbus-call-method
      ;; :session
      ;; "org.gnome.Shell"
      ;; "/org/gnome/Shell/Extensions/Windows"
      ;; "org.gnome.Shell.Extensions.Windows"
      ;; "Activate"
      ;; wid))
      ;; this should be compatible with other WMs
      (select-frame-set-input-focus (selected-frame)))
      (add-hook 'org-capture-mode-hook 'org-protocol/select-current-frame)

    -Unfill instead of fill

    -

    With time, I came to the conclusion that having no line breaks inside paragraphs works better in most Org documents. So instead of org-fill-paragraph, I’ll bind M-q to some sort of org-unfill-paragraph.

    -
    +Unfill instead of fill

    With time, I came to the conclusion that having no line breaks inside paragraphs works better in most Org documents. So instead of org-fill-paragraph, I’ll bind M-q to some sort of org-unfill-paragraph.

    (defun org-unfill-paragraph ()
    (interactive)
    (let ((fill-column most-positive-fixnum))
    (org-fill-paragraph)))

    (map! :map 'org-mode-map "M-q" #'org-unfill-paragraph)

    -Vulpea

    -
    -
    (package! vulpea)
    -
    +Vulpea
    +
    (package! vulpea)
    (use-package! vulpea
    :hook ((org-roam-db-autosync-mode . vulpea-db-autosync-enable)))

    -Writing mode

    -
    +Writing mode
    (defvar-local org-writing-mode--previous-mixed-pitch nil)
    (defvar-local org-writing-mode--previous-line-numbers nil)
    (defvar-local org-writing-mode--previous-line-spacing 0)
    (defvar-local org-writing-mode--frame nil)

    (define-minor-mode org-writing-mode
    "Minor mode for writing in org."
    :init-value nil
    :lighter nil
    (require 'mixed-pitch)
    (unless (frame-live-p org-writing-mode--frame)
    (setq-local org-writing-mode--frame (selected-frame)))
    (if org-writing-mode
    (progn (setq-local
    org-writing-mode--previous-mixed-pitch mixed-pitch-mode
    org-writing-mode--previous-line-numbers display-line-numbers
    org-writing-mode--previous-line-spacing line-spacing
    display-line-numbers nil
    line-spacing 5)
    (mixed-pitch-mode +1)
    ;; (set-frame-parameter org-writing-mode--frame 'internal-border-width 30)
    (evil-normal-state))
    (mixed-pitch-mode (if org-writing-mode--previous-mixed-pitch 1 -1))
    ;; (set-frame-parameter org-writing-mode--frame 'internal-border-width 0)
    (setq display-line-numbers org-writing-mode--previous-line-numbers
    line-spacing org-writing-mode--previous-line-spacing)))

    (map! :mode 'org-mode :leader "t z" #'org-writing-mode)

    -Web

    -
    +Web
    (setq-default web-mode-code-indent-offset 2
    web-mode-markup-indent-offset 2)

    -Yuck mode

    -
    +Yuck mode
    (package! yuck-mode)

    Scientific writing (mostly mathematics)

    -Mamimo

    -
    -
    (package! mamimo
    :type 'local
    :recipe (:local-repo "lisp/lib/mamimo"
    :files ("*.el" "snippets")))
    -
    +Mamimo
    +
    (package! mamimo
    :type 'local
    :recipe (:local-repo "lisp/lib/mamimo"
    :files ("*.el" "snippets")))
    (use-package mamimo
    :commands (mamimo-mode
    mamimo-yas-mode
    mamimo-smartkeys-mode)
    :config
    (add-hook! 'mamimo-mode-hook (evil-tex-mode +1)))

    Abbrev

    -Language & math predicate

    -
    +Language & math predicate
    (defsubst abbrev/math-text-lang-p (lang)
    (and (mamimo-notmathp)
    (-any (lambda (k) (string= lang (cadr k)))
    (org-collect-keywords '("language")))))

    (defun abbrev/math-text-pt-p () (abbrev/math-text-lang-p "pt"))
    (defun abbrev/math-text-en-p () (abbrev/math-text-lang-p "en"))

    -Textual abbrevs

    -
    +Textual abbrevs
    (setq abbrev/math-text-abbrevs-pt
    '(("pa" "podemos assumir")
    ("pd" "por definição")
    ("ie" "i.e.")
    ("tq" "tal que")
    ("spg" "sem perda de generalidade")
    ("qtp" "q.t.p.")
    ("sss" "se, e somente se,")
    ("li" "linearmente independentes")))

    (setq abbrev/math-text-abbrevs-en
    '(("wlog" "without loss of generality")
    ("iff" "if and only if")
    ("ie" "i.e.")
    ("st" "such that")
    ("ae" "a.e.")
    ("bd" "by definition")
    ("li" "linearly independent")))

    -Variable abbrevs

    -
    +Variable abbrevs
    (setq abbrev/var-abbrevs-pt '(b c d f g h i j k l m n p q r s t u v w x y z))
    (setq abbrev/var-abbrevs-en '(b c d e f g h j k l m n o p q r s t u v w x y z))

    (defun abbrev/compile-var-abbrevs (abbrevs)
    (mapcar (lambda (s) (list (symbol-name s) (format "\\(%s\\)" s) nil :system t))
    abbrevs))

    -Tables and mode-local tables

    -
    +Tables and mode-local tables
    (setq abbrev/tables
    `((abbrev/math-text-pt-table
    ,(append
    abbrev/math-text-abbrevs-pt
    (abbrev/compile-var-abbrevs abbrev/var-abbrevs-pt))
    abbrev/math-text-pt-p)
    (abbrev/math-text-en-table
    ,(append
    abbrev/math-text-abbrevs-en
    (abbrev/compile-var-abbrevs abbrev/var-abbrevs-en))
    abbrev/math-text-en-p)))

    (defun abbrev/setup ()
    (require 'abbrev)
    (setq-local local-abbrev-table nil)
    (pcase-dolist (`(,name ,defs ,cond) abbrev/tables)
    (define-abbrev-table name defs :enable-function cond)
    (push (symbol-value name) local-abbrev-table))
    (abbrev-mode +1))

    (add-hook 'mamimo-mode-hook #'abbrev/setup)

    ORGANIZE Bindings

    -Comandos familiares

    -

    Porque ninguém merece tantos atalhos diferentes.

    -
    +Comandos familiares

    Porque ninguém merece tantos atalhos diferentes.

    (map! :g "C-s" 'save-buffer)
    ;; (map! :g "C-/" 'evilnc-comment-or-uncomment-lines)

    (map! :i "C-v" 'yank)
    (map! :i "C-z" 'evil-undo)
    (map! :i "C-S-z" 'evil-redo)
    (map! :i "C-x" 'evil-delete)
    (map! :g "C-<backspace>" 'evil-delete-backward-word)

    -Linhas visuais

    -
    +Linhas visuais
    (map! :map evil-motion-state-map
    "j" 'evil-next-visual-line
    "k" 'evil-previous-visual-line
    "<down>" 'evil-next-visual-line
    "<up>" 'evil-previous-visual-line)

    -Hydras

    -

    Uma história antiga.

    -

    Não gosto do estilo do pop up

    -
    -
    (setq hydra-is-helpful nil)
    -

    Tamanho da janela

    -
    +Hydras

    Uma história antiga.

    Não gosto do estilo do pop up

    +
    (setq hydra-is-helpful nil)

    Tamanho da janela

    (defhydra window-height-hydra (evil-window-map)
    "window height"
    ("=" evil-window-increase-height "")
    ("-" evil-window-decrease-height "")
    (">" evil-window-increase-width "")
    ("<" evil-window-decrease-width ""))

    ;; (defhydra workspace-hydra (doom-leader-workspace-map)
    ;; "workspace"
    ;; ("]" +workspace/switch-right "")
    ;; ("[" +workspace/switch-left "")
    ;; ("}" +workspace/swap-right "")
    ;; ("{" +workspace/swap-left ""))

    -Kitty (Terminal)

    -
    +Kitty (Terminal)
    (map! :prefix-map ("\x80" . "kitty C map")
    :map 'key-translation-map
    "/" "C-/")

    (map! :prefix-map ("\x81" . "kitty C-S map")
    :map 'key-translation-map
    "z" (kbd "C-S-z"))

    -Leader edit key

    -
    +Leader edit key
    (map! :leader
    :prefix ("e" . "edit")
    :desc "New snipet" "s" #'+snippets/new
    :desc "New alias" "a" #'+snippets/new-alias)

    (map! :i "C-M-x" ctl-x-map)

    Workarounds

    -Doom

    -

    Those may become obsolete in the future.

    -
    -
    (unpin! straight)
    -
    +Doom

    Those may become obsolete in the future.

    +
    (unpin! straight)
    ;; for some reason consult preview is not working in +default/p-s 
    (map! :leader "/" #'+vertico/project-search)
    \ No newline at end of file diff --git a/org-exporters/test/files/diagramas/out.html b/org-exporters/test/files/diagramas/out.html index f1945a1..269ab2a 100644 --- a/org-exporters/test/files/diagramas/out.html +++ b/org-exporters/test/files/diagramas/out.html @@ -214,33 +214,23 @@

    Table of Contents

    Calculando com os diagramas

    -O que são tensores?

    -

    Em várias partes da ciência e da matemática, precisamos lidar com um conjunto de vetores e obter um outro vetor ou escalar como resultado; um dos exemplos mais simples é o de calcular o volume de paralelogramos, paralelepípedos e seus correspondentes em dimensões maiores. Por conta da estrutura linear dos espaços vetoriais, é natural considerar que a transformação envolvida seja multilinear, agindo como uma transformação linear quando são fixadas todas menos uma das entradas. Por exemplo, uma função \(f:\R^2\times\R^2\to\R\) que calcula o volume do paralelogramo gerado por dois vetores em \(\R^2\) é 2-linear, e na segunda entrada isso significa que \(f(u,v+\lambda w)=f(u,v)+\lambda f(u,w)\).

    -

    A ideia dos tensores é construir uma correspondência entre as funções multilineares, que têm domínios em produtos cartesianos de espaços vetoriais, e funções lineares, com domínio no produto tensorial desses espaços. Com isso em mente, definimos este produto especificando as propriedades que ele deve ter: se \(V_1,\dots,V_k\) são espaços vetoriais, o seu produto tensorial constitui uma dupla \((\otimes,V)\) de um espaço vetorial \(V\) e uma função \(k~\)-linear \(\otimes:\prod_{i=1}^k V_i\to V\) que satisfazem uma propriedade universal: para toda função \(k~\)-linear \(\psi:\prod_{i=1}^k V_i\to W\), existe uma única função linear \(\tilde\psi:V\to W\) tal que \(\psi=\tilde\psi\circ\otimes\). Podemos mostrar que a imagem de \(\otimes\) gera \(V\) (mesmo sendo, em geral, menor que \(V\)), os elementos de \(V\) são chamados de tensores, e \(V\) é denotado por \(V_1\otimes...\otimes V_k\). A correspondência entre formas multilineares e mapas lineares com domínio no produto tensorial fica condensada na seguinte preposição:

    -
    -

    Para todo espaço vetorial \(W\), a função

    +O que são tensores?

    Em várias partes da ciência e da matemática, precisamos lidar com um conjunto de vetores e obter um outro vetor ou escalar como resultado; um dos exemplos mais simples é o de calcular o volume de paralelogramos, paralelepípedos e seus correspondentes em dimensões maiores. Por conta da estrutura linear dos espaços vetoriais, é natural considerar que a transformação envolvida seja multilinear, agindo como uma transformação linear quando são fixadas todas menos uma das entradas. Por exemplo, uma função \(f:\R^2\times\R^2\to\R\) que calcula o volume do paralelogramo gerado por dois vetores em \(\R^2\) é 2-linear, e na segunda entrada isso significa que \(f(u,v+\lambda w)=f(u,v)+\lambda f(u,w)\).

    A ideia dos tensores é construir uma correspondência entre as funções multilineares, que têm domínios em produtos cartesianos de espaços vetoriais, e funções lineares, com domínio no produto tensorial desses espaços. Com isso em mente, definimos este produto especificando as propriedades que ele deve ter: se \(V_1,\dots,V_k\) são espaços vetoriais, o seu produto tensorial constitui uma dupla \((\otimes,V)\) de um espaço vetorial \(V\) e uma função \(k~\)-linear \(\otimes:\prod_{i=1}^k V_i\to V\) que satisfazem uma propriedade universal: para toda função \(k~\)-linear \(\psi:\prod_{i=1}^k V_i\to W\), existe uma única função linear \(\tilde\psi:V\to W\) tal que \(\psi=\tilde\psi\circ\otimes\). Podemos mostrar que a imagem de \(\otimes\) gera \(V\) (mesmo sendo, em geral, menor que \(V\)), os elementos de \(V\) são chamados de tensores, e \(V\) é denotado por \(V_1\otimes...\otimes V_k\). A correspondência entre formas multilineares e mapas lineares com domínio no produto tensorial fica condensada na seguinte preposição:

    +

    Para todo espaço vetorial \(W\), a função

    \[\begin{equation*} \Gamma:\Hom_{\F}^k(V_1,...,V_k, W)\to\Hom_{\F}(V_1\otimes...\otimes V_k,W) \end{equation*}\] -
    -

    dada por \(\Gamma(\psi)=\tilde\psi\) é um isomorfismo linear entre os dois espaços, com inversa \(\Gamma^{-1}(f)=f\circ\otimes\).

    +

    dada por \(\Gamma(\psi)=\tilde\psi\) é um isomorfismo linear entre os dois espaços, com inversa \(\Gamma^{-1}(f)=f\circ\otimes\).

    -Uma pequena motivação: teoria quântica

    -

    É bem sabido que o produto tensorial tem um papel bastante relevante na teoria quântica. Na teoria, os estados de um sistema são vetores em um espaço vetorial complexo com produto interno (um espaço de Hilbert). Quando queremos juntar vários sistemas quânticos para formar um novo sistema, o sistema resultante tem como espaço de estados o produto tensorial dos espaços originais. Nesse espaço produto, aparecem os tensores não puros, tensores que não estão na imagem da função \(\otimes\): vistos assim, eles são justamente os estados que não podem ser formados pelo produto de um estado de cada componente, e são chamados de estados emaranhados.

    -

    Transformações em um sistema quântico são representadas por operadores lineares unitários (operadores sobrejetivos que preservam o produto interno). Se temos dois sistemas \(\cat{H}_1,\cat{H}_2\) com operatores \(f:\cat{H}_1\to\cat{H}_1\) e \(g:\cat{H}_2\to\cat{H}_2\), podemos nos perguntar se faz sentido existir uma transformação em \(\cat{H}_1\otimes\cat{H}_2\) que representa as duas transformações sendo feitas em paralelo, ao mesmo tempo:

    -
    -

    Sejam \(V_1,V_2,W_1,W_2\) espaços vetoriais. Então existe e é única uma transformação linear injetora

    +Uma pequena motivação: teoria quântica

    É bem sabido que o produto tensorial tem um papel bastante relevante na teoria quântica. Na teoria, os estados de um sistema são vetores em um espaço vetorial complexo com produto interno (um espaço de Hilbert). Quando queremos juntar vários sistemas quânticos para formar um novo sistema, o sistema resultante tem como espaço de estados o produto tensorial dos espaços originais. Nesse espaço produto, aparecem os tensores não puros, tensores que não estão na imagem da função \(\otimes\): vistos assim, eles são justamente os estados que não podem ser formados pelo produto de um estado de cada componente, e são chamados de estados emaranhados.

    Transformações em um sistema quântico são representadas por operadores lineares unitários (operadores sobrejetivos que preservam o produto interno). Se temos dois sistemas \(\cat{H}_1,\cat{H}_2\) com operatores \(f:\cat{H}_1\to\cat{H}_1\) e \(g:\cat{H}_2\to\cat{H}_2\), podemos nos perguntar se faz sentido existir uma transformação em \(\cat{H}_1\otimes\cat{H}_2\) que representa as duas transformações sendo feitas em paralelo, ao mesmo tempo:

    +

    Sejam \(V_1,V_2,W_1,W_2\) espaços vetoriais. Então existe e é única uma transformação linear injetora

    \[\begin{equation*} \Omega :\Hom_{\F}(V_1,W_1) \otimes \Hom_{\F}(V_2,W_2)\to\Hom_{\F}(V_1\otimes W_1,V_2\otimes W_2) \end{equation*}\] -
    -

    tal que para todas as transformações lineares \(S:V_1\to W_1\), \(T:V_2\to W_2\) e quaisquer vetores \(v_1\in V_1\) e \(v_2\in V_2\), vale \(\Gamma(S \otimes T)(v_1 \otimes v_2)=S(v_1)\otimes T(v_2)\). Para simplificar a notação, passaremos a chamar \(\Gamma(S \otimes T)\) simplesmente de \(S\otimes T\).

    -

    É intuitivo pensar em processos que agem sobre os sistemas compondo operadores em paralelo ou sequencialmente. Como podemos usar desenhos de maneira formal para pensar sobre processos que envolvem tensores?

    +

    tal que para todas as transformações lineares \(S:V_1\to W_1\), \(T:V_2\to W_2\) e quaisquer vetores \(v_1\in V_1\) e \(v_2\in V_2\), vale \(\Gamma(S \otimes T)(v_1 \otimes v_2)=S(v_1)\otimes T(v_2)\). Para simplificar a notação, passaremos a chamar \(\Gamma(S \otimes T)\) simplesmente de \(S\otimes T\).

    É intuitivo pensar em processos que agem sobre os sistemas compondo operadores em paralelo ou sequencialmente. Como podemos usar desenhos de maneira formal para pensar sobre processos que envolvem tensores?

    -Diagramas

    -

    Os diagramas são compostos de caixas e fios, onde as caixas representam funções lineares e os fios servem como forma compor essas transformações. Para deixar a notação mais clara, podemos etiquetar as caixas com os nomes das transformações e os fios com os respectivos espaços vetoriais que são domínios ou contradomínios dessas transformações. Como exemplo, veja a transformação \(f:V\to W\) abaixo. Quando as funções envolvidas agem sobre produtos tensoriais, como \(g:V_1\otimes\dots\otimes V_n\to W_1\otimes\dots\otimes W_m\), desenhamos as caixas com vários fios que entram ou saem, um para cada fator do produto:

    +Diagramas

    Os diagramas são compostos de caixas e fios, onde as caixas representam funções lineares e os fios servem como forma compor essas transformações. Para deixar a notação mais clara, podemos etiquetar as caixas com os nomes das transformações e os fios com os respectivos espaços vetoriais que são domínios ou contradomínios dessas transformações. Como exemplo, veja a transformação \(f:V\to W\) abaixo. Quando as funções envolvidas agem sobre produtos tensoriais, como \(g:V_1\otimes\dots\otimes V_n\to W_1\otimes\dots\otimes W_m\), desenhamos as caixas com vários fios que entram ou saem, um para cada fator do produto:

    \[\begin{tikzpicture}[disp] \matrix (c) { \coordinate (s); \\ @@ -265,8 +255,7 @@

    Table of Contents

    \node[vs,between= f-ts-1 and f-ts-2,yshift=1mm,xshift=.7mm] {$...$}; \end{tikzpicture}\] -
    -

    Conhecemos duas receitas de bolo para produzir uma transformação linear a partir de outras duas, \(f\) e \(g\): o produto tensorial \(f \otimes g\), e, se \(f\) e \(g\) concordarem domínio com contradomínio, a composta \(f\circ g\). Nos diagramas, a composição de duas funções é ilustrada como na figura abaixo. Note que se tivermos mais caixas empilhadas, a associatividade da composição garante que não precisamos nos importar com parênteses.

    +

    Conhecemos duas receitas de bolo para produzir uma transformação linear a partir de outras duas, \(f\) e \(g\): o produto tensorial \(f \otimes g\), e, se \(f\) e \(g\) concordarem domínio com contradomínio, a composta \(f\circ g\). Nos diagramas, a composição de duas funções é ilustrada como na figura abaixo. Note que se tivermos mais caixas empilhadas, a associatividade da composição garante que não precisamos nos importar com parênteses.

    \[\begin{tikzpicture}[disp] \matrix (c2) { \node (s) {}; @@ -290,8 +279,7 @@

    Table of Contents

    -- node[vs] {$V$} (gof) -- node[vs] {$U$} (e); \end{tikzpicture}\] -
    -

    Representamos produtos tensoriais de funções ao colocar as suas caixas lado a lado:

    +

    Representamos produtos tensoriais de funções ao colocar as suas caixas lado a lado:

    \[\begin{tikzpicture}[disp] \matrix (c) { \node (s1) {}; @@ -346,9 +334,7 @@

    Table of Contents

    to[out=-90,in=60] node[vs] {$X$} (c) to[out=-60,in=90] node[vs] {$U$} (e2); \end{tikzpicture}\] -
    -

    Em geral, os diagramas sempre corresponderão a transformações lineares que saem de um produto tensorial de espaços e vão a outro produto tensorial. De fato, os diagramas podem ser convertidos para a notação algébrica usual ao serem lidos linha por linha como uma composição de funções, e vice-versa. Mesmo sendo equivalentes à notação usual, a mágica dos diagramas é que a representação gráfica deixa algumas das regras para manipular tensores mais intuitivas.

    -

    Seguindo as definições que vimos acima, os diagramas de tensores têm uma estrutura formada por linhas, onde cada linha corresponde a um produto tensorial de funções e os fios indicam como compor as linhas verticalmente. Como as linhas dos diagramas são lidas de cima para baixo, em muitas passagens ficará mais claro para a leitura introduzir a notação \(g\comp f\comp...\comp h\) para significar a composição \(h\circ...\circ f\circ g\) de funções escrita na ordem reversa (assim, a leitura da esquerda para direita acompanha a leitura de cima para baixo do diagrama), e usaremos ela no decorrer do texto. A função identidade é ilustrada por um fio vazio e vertical . Note que pela igualdade \((g\comp f)\otimes(h\comp w)=(g\otimes h)\comp(f\otimes w)\), não precisamos nos preocupar com a ordem que interpretamos as operações composição e produto:

    +

    Em geral, os diagramas sempre corresponderão a transformações lineares que saem de um produto tensorial de espaços e vão a outro produto tensorial. De fato, os diagramas podem ser convertidos para a notação algébrica usual ao serem lidos linha por linha como uma composição de funções, e vice-versa. Mesmo sendo equivalentes à notação usual, a mágica dos diagramas é que a representação gráfica deixa algumas das regras para manipular tensores mais intuitivas.

    Seguindo as definições que vimos acima, os diagramas de tensores têm uma estrutura formada por linhas, onde cada linha corresponde a um produto tensorial de funções e os fios indicam como compor as linhas verticalmente. Como as linhas dos diagramas são lidas de cima para baixo, em muitas passagens ficará mais claro para a leitura introduzir a notação \(g\comp f\comp...\comp h\) para significar a composição \(h\circ...\circ f\circ g\) de funções escrita na ordem reversa (assim, a leitura da esquerda para direita acompanha a leitura de cima para baixo do diagrama), e usaremos ela no decorrer do texto. A função identidade é ilustrada por um fio vazio e vertical . Note que pela igualdade \((g\comp f)\otimes(h\comp w)=(g\otimes h)\comp(f\otimes w)\), não precisamos nos preocupar com a ordem que interpretamos as operações composição e produto:

    \[\begin{tikzpicture}[disp] \matrix (c) { @@ -398,9 +384,7 @@

    Table of Contents

    \node[box,fit=(g) (h)] {}; \node[box,fit=(f) (w)] {}; \end{tikzpicture}\] -
    -

    Mas basta considerar diagramas com mais de duas colunas que vemos que outros detalhes do formalismo também ficam escondidos na maneira que representamos os diagramas; por exemplo, quando colocamos três funções \(f,g\) e \(h\) lado a lado, subentende-se ser possível identificar as funções \(f\ot (g\ot h)\) e \((f\ot g)\ot h\) entre si de maneira natural, mesmo que elas atuem em espaços diferentes. Por enquanto, vamos subentender que sempre que preciso, aplicamos os isomorfismos canônicos entre esses espaços para que se adequem aos domínios e contradomínios das funções – é possível abordar essas noções de maneira um pouco mais formal a partir de transformações naturais e outros conceitos da teoria das categorias, mas não faremos isso aqui.

    -

    Podemos representar um escalar \(\lambda\in\F\) ou um vetor \(v\in V\) como na figura abaixo:

    +

    Mas basta considerar diagramas com mais de duas colunas que vemos que outros detalhes do formalismo também ficam escondidos na maneira que representamos os diagramas; por exemplo, quando colocamos três funções \(f,g\) e \(h\) lado a lado, subentende-se ser possível identificar as funções \(f\ot (g\ot h)\) e \((f\ot g)\ot h\) entre si de maneira natural, mesmo que elas atuem em espaços diferentes. Por enquanto, vamos subentender que sempre que preciso, aplicamos os isomorfismos canônicos entre esses espaços para que se adequem aos domínios e contradomínios das funções – é possível abordar essas noções de maneira um pouco mais formal a partir de transformações naturais e outros conceitos da teoria das categorias, mas não faremos isso aqui.

    Podemos representar um escalar \(\lambda\in\F\) ou um vetor \(v\in V\) como na figura abaixo:

    \[\begin{tikzpicture}[disp] \centering @@ -412,9 +396,7 @@

    Table of Contents

    \draw[oes={->-}] (v) -- node[vs] {$V$} (e); \end{tikzpicture}\] -
    -

    Por que falamos de transformações antes de escalares ou vetores? A razão para isso é que do ponto de vista formal, as coisas ficam um pouco mais simples se pensarmos que escalares e vetores também são funções lineares: cada escalar \(\lambda\) pode ser visto como a transformação \(\lambda:\F\to\F\) dada por \(t\mapsto\lambda t\), e cada vetor \(v\) pode ser representado pela função \(v:\F\to V\) definida por \(t\mapsto tv\). Com essa mudança, todos os elementos dos diagramas serão transformações lineares, e os fios que carregam escalares ficam invisíveis. Não precisamos nos preocupar com eles pois sempre que preciso, lançamos mão do isomorfismo canônico \(\F\otimes V\cong V\) dado por \(\lambda\otimes v\mapsto\lambda v\).

    -

    No mundo dos produtos tensoriais, existe um único isomorfismo \(B_{V,W}:V\ot W\to W\ot V\) que troca os vetores \(v\ot w\mapsto w\ot v\). Nos diagramas, ele é desenhado como um par de fios trocados. Assim, o diagrama

    +

    Por que falamos de transformações antes de escalares ou vetores? A razão para isso é que do ponto de vista formal, as coisas ficam um pouco mais simples se pensarmos que escalares e vetores também são funções lineares: cada escalar \(\lambda\) pode ser visto como a transformação \(\lambda:\F\to\F\) dada por \(t\mapsto\lambda t\), e cada vetor \(v\) pode ser representado pela função \(v:\F\to V\) definida por \(t\mapsto tv\). Com essa mudança, todos os elementos dos diagramas serão transformações lineares, e os fios que carregam escalares ficam invisíveis. Não precisamos nos preocupar com eles pois sempre que preciso, lançamos mão do isomorfismo canônico \(\F\otimes V\cong V\) dado por \(\lambda\otimes v\mapsto\lambda v\).

    No mundo dos produtos tensoriais, existe um único isomorfismo \(B_{V,W}:V\ot W\to W\ot V\) que troca os vetores \(v\ot w\mapsto w\ot v\). Nos diagramas, ele é desenhado como um par de fios trocados. Assim, o diagrama

    \[\begin{tikzpicture}[disp] \matrix (c) { @@ -430,9 +412,7 @@

    Table of Contents

    (8mm,0)--(8mm,-1)--(8mm,-2);\\ }; \end{tikzpicture}\] -
    -

    expressa a igualdade \(B_{V,W}\comp{}B_{W,V}=\id_{V\ot{}W}\). Como os diagramas são em si funções lineares, também podemos fazer com eles as operações usuais de um espaço vetorial. Por exemplo, se \(\car(\F)\neq2\), o diagrama abaixo é uma função linear:

    -

    e o leitor curioso pode checar que \(s_2\comp s_2=s_2\) usando a identidade que vimos logo acima. Outra transformação interessante é o funcional avaliação \(\ev{V}: V\ot{}V^*\to\F\) definido por \(\ev{V}(v\ot{f})=f(v)\). Nos inspirando na existência desse funcional, introduziremos a partir daqui uma notação para fios que carregam o espaço dual \(V^*\) de um espaço \(V\) ao representá-los como um fio também etiquetado por \(V\), mas com a orientação (a seta) invertida. Com essa notação e com a justificativa de que \(\ev{V}\) é o funcional canônico nesse espaço, passaremos a escrever o mapa \(\ev{V}\) como um simples fio dobrado:

    +

    expressa a igualdade \(B_{V,W}\comp{}B_{W,V}=\id_{V\ot{}W}\). Como os diagramas são em si funções lineares, também podemos fazer com eles as operações usuais de um espaço vetorial. Por exemplo, se \(\car(\F)\neq2\), o diagrama abaixo é uma função linear:

    e o leitor curioso pode checar que \(s_2\comp s_2=s_2\) usando a identidade que vimos logo acima. Outra transformação interessante é o funcional avaliação \(\ev{V}: V\ot{}V^*\to\F\) definido por \(\ev{V}(v\ot{f})=f(v)\). Nos inspirando na existência desse funcional, introduziremos a partir daqui uma notação para fios que carregam o espaço dual \(V^*\) de um espaço \(V\) ao representá-los como um fio também etiquetado por \(V\), mas com a orientação (a seta) invertida. Com essa notação e com a justificativa de que \(\ev{V}\) é o funcional canônico nesse espaço, passaremos a escrever o mapa \(\ev{V}\) como um simples fio dobrado:

    \[\begin{tikzpicture}[disp] \matrix (c0) { @@ -456,34 +436,27 @@

    Table of Contents

    \fheads[2]{ev}{1}\\ }; \end{tikzpicture}\] -
    -

    Assim, também fica simples descobrir algumas correspondências entre tensores e mapas lineares, por exemplo, \(T:V^*\ot W\to\Hom_{\F}(V,W)\) dada por:

    -

    Se interpretamos o diagrama na linguagem usual, obtemos:

    +

    Assim, também fica simples descobrir algumas correspondências entre tensores e mapas lineares, por exemplo, \(T:V^*\ot W\to\Hom_{\F}(V,W)\) dada por:

    Se interpretamos o diagrama na linguagem usual, obtemos:

    \[\begin{equation*} T(u)=(\id_V\ot\, u) \comp (\ev{V}\ot\id_W). \end{equation*}\] -
    -

    Mais explicitamente, \(T(f\ot w)(v)\) pode ser calculado passo a passo como:

    +

    Mais explicitamente, \(T(f\ot w)(v)\) pode ser calculado passo a passo como:

    \[\begin{equation*} v\xmapsto{\id_V\ot\, f\,\ot\, w} v\ot f\ot w \xmapsto{\ev{V}\ot\id_W} f(v)\ot w \xmapsto{\sim} f(v)w, \end{equation*}\] -
    -

    ou seja, \(T(f\ot w)(v)=f(v)w\).

    +

    ou seja, \(T(f\ot w)(v)=f(v)w\).

    -Dimensão finita

    -

    Diagramas de tensores envolvendo espaços de dimensões finitas são especiais. Nesse contexto, conseguimos definir um dual \(\coev{V}\) do funcional \(\ev{V}\) que tem o efeito de dobrar os fios para baixo, chamado de coavaliação. Explicitamente, em dimensão finita o morfismo \(T\) visto acima é um isomorfismo, e definiremos \(\coev{V}=T^{-1}(\id_V)\).

    -

    Note que essa definição não depende de uma escolha da base, mas se tomarmos uma base \(\{e_i\}_{i=1}^n\) de \(V\), podemos mostrar que \(\coev{V}=\sum_{i=1}^n e_i^*\ot e_i\):

    +Dimensão finita

    Diagramas de tensores envolvendo espaços de dimensões finitas são especiais. Nesse contexto, conseguimos definir um dual \(\coev{V}\) do funcional \(\ev{V}\) que tem o efeito de dobrar os fios para baixo, chamado de coavaliação. Explicitamente, em dimensão finita o morfismo \(T\) visto acima é um isomorfismo, e definiremos \(\coev{V}=T^{-1}(\id_V)\).

    Note que essa definição não depende de uma escolha da base, mas se tomarmos uma base \(\{e_i\}_{i=1}^n\) de \(V\), podemos mostrar que \(\coev{V}=\sum_{i=1}^n e_i^*\ot e_i\):

    \[\begin{equation*} T(\coev{V})(v)=T\left(\sum_{i=1}^n e_i^*\ot e_i\right)(v) =\sum_{i=1}^nT(e_i^*\ot e_i)(v) =\sum_{i=1}^n e_i^*(v)e_i=v. \end{equation*}\] -
    -

    Com isso em mente, passaremos a representar \(\coev{V}\) nos diagramas da seguinte forma:

    +

    Com isso em mente, passaremos a representar \(\coev{V}\) nos diagramas da seguinte forma:

    \[\begin{tikzpicture}[disp] \matrix (c) { @@ -497,8 +470,7 @@

    Table of Contents

    \eq{c}; \node[rofeq] {$\displaystyle1\mapsto\sum_{i=1}^{n}e^*_i\otimes e_i$}; \end{tikzpicture}\] -
    -

    As identidades mais importantes, que tanto justificam chamar \(\coev{V}\) de dual de \(\ev{V}\) como mostram uma interpretação topológica dos diagramas, são as identidades zig-zag:

    +

    As identidades mais importantes, que tanto justificam chamar \(\coev{V}\) de dual de \(\ev{V}\) como mostram uma interpretação topológica dos diagramas, são as identidades zig-zag:

    \[\begin{tikzpicture}[disp] \matrix[matd] (m) { &&\\&&\\&&\\ @@ -528,15 +500,13 @@

    Table of Contents

    \draw[->-] (0,-1.4) to (0,0);\\ }; \end{tikzpicture}\] -
    -

    Podemos provar essas identidades: a identidade da esquerda é, por definição, o fato que \(T(\coev{V})=\id_V\), e na direita podemos mostrar a igualdade aplicando uma \(f\in V^*\) ao diagrama, e assim obtendo:

    +

    Podemos provar essas identidades: a identidade da esquerda é, por definição, o fato que \(T(\coev{V})=\id_V\), e na direita podemos mostrar a igualdade aplicando uma \(f\in V^*\) ao diagrama, e assim obtendo:

    \[\begin{equation*} f\xmapsto{\coev{V}\ot\,\id_{V^*}} \sum_{i=1}^n e_i^*\ot e_i\ot f \xmapsto{\id_{V^*}\ot\,\ev{V}} \sum_{i=1}^n f(e_i)e_i^* = f. \end{equation*}\] -
    -

    Usando o morfismo \(B_{V^*,V}\), também podemos definir \(\ev{V^*}\) e \(\coev{V^*}\), respectivamente:

    +

    Usando o morfismo \(B_{V^*,V}\), também podemos definir \(\ev{V^*}\) e \(\coev{V^*}\), respectivamente:

    \[\begin{tikzpicture}[disp] \matrix (c) { \tev[1,0]{0,0} @@ -564,9 +534,7 @@

    Table of Contents

    (1,0) to[in=90,out=-90] (0,-.8);\\ }; \end{tikzpicture}\] -
    -

    e similarmente, podemos obter outras duas identidades zig-zag com essas versões. Observe que a notação também sugere uma identificação do bidual \((V^*)^*\) com o espaço \(V\). Isso se justifica pela existência de um isomorfismo \(\Phi:V\to (V^*)^*\) dado por \(\Phi(v)(g)=g(v)\), que é canônico no sentido que não depende de uma escolha de base do espaço. O mesmo não vale para \(V^*\), entretanto. Na linguagem categórica, os espaços vetoriais de dimensão finita formam uma categoria fechada compacta.

    -

    Poderíamos provar algumas igualdades apenas substituindo pedaços do diagrama por identidades anteriores, por exemplo:

    +

    e similarmente, podemos obter outras duas identidades zig-zag com essas versões. Observe que a notação também sugere uma identificação do bidual \((V^*)^*\) com o espaço \(V\). Isso se justifica pela existência de um isomorfismo \(\Phi:V\to (V^*)^*\) dado por \(\Phi(v)(g)=g(v)\), que é canônico no sentido que não depende de uma escolha de base do espaço. O mesmo não vale para \(V^*\), entretanto. Na linguagem categórica, os espaços vetoriais de dimensão finita formam uma categoria fechada compacta.

    Poderíamos provar algumas igualdades apenas substituindo pedaços do diagrama por identidades anteriores, por exemplo:

    \[\begin{tikzpicture}[disp] \matrix[matd,column sep=7mm] (m) { @@ -630,8 +598,7 @@

    Table of Contents

    \draw[->-] (0,1.4) to (0,0);\\ }; \end{tikzpicture}\] -
    -

    Mas de forma mais geral, é possível demonstrar que é sempre permitido +

    Mas de forma mais geral, é possível demonstrar que é sempre permitido desembaraçar os diagramas. Faremos isso com algums lemas que mostram como uma função \(f\) comuta com os fios dobrados. Por exemplo, é simples ver que:

    @@ -660,8 +627,7 @@

    Table of Contents

    \draw[oes={->-l}] (m-1-1 |- fs) -- (m-2-1 |- f.south) to[out=-90,in=90] (m-2-2); \end{tikzpicture}\] -
    -

    Definimos a transposta de funções lineares \(f:V\to W\), \(g:W^*\to V^*\) pelos diagramas:

    +

    Definimos a transposta de funções lineares \(f:V\to W\), \(g:W^*\to V^*\) pelos diagramas:

    \[\begin{tikzpicture}[disp] \matrix (c) { @@ -696,8 +662,7 @@

    Table of Contents

    (m-2-1 |- f.north) --(m-3-1) (m-1-3)--(m-2-3 |- f.south); \end{tikzpicture}\] -
    -

    Podemos checar que a primeira transposta \(f^t\) concorda com a transposta usual +

    Podemos checar que a primeira transposta \(f^t\) concorda com a transposta usual ao aplicá-la a um vetor \(w\in W^*\). Pelas definições do diagrama acima e de \(\ev{V}\), sabemos que para todo \(v\in V\), \((f^t(w))(v)\) é igual a:

    @@ -734,8 +699,7 @@

    Table of Contents

    \eq{m}; \node[rofeq,xshift=-3mm] {$(w\circ f)(v)$}; \end{tikzpicture}\] -
    -

    Como isso vale para todos \(v\) e \(w\), \(f^t\) concorda com a transposta usual. Para \(g^t\), isso é apenas parcialmente verdade; ela é a transposta usual a menos do isomorfismo canônico \(\Phi\). Com a transposta, andamos pelos fios com os lemas do deslizamento:

    +

    Como isso vale para todos \(v\) e \(w\), \(f^t\) concorda com a transposta usual. Para \(g^t\), isso é apenas parcialmente verdade; ela é a transposta usual a menos do isomorfismo canônico \(\Phi\). Com a transposta, andamos pelos fios com os lemas do deslizamento:

    \[\begin{tikzpicture}[disp] \matrix[matd] (m) { @@ -768,8 +732,7 @@

    Table of Contents

    (f.north) -- (m-1-2) (m-1-1) -- (m-2-1 |- f.south); \end{tikzpicture}\] -
    -

    Usando o mesmo argumento,

    +

    Usando o mesmo argumento,

    \[\begin{tikzpicture}[disp] \matrix[matd] (m) { @@ -802,8 +765,7 @@

    Table of Contents

    (f) -- (m-3-2) (m-3-1) -- (m-2-1 |- f.north); \end{tikzpicture}\] -
    -

    e também:

    +

    e também:

    \[\begin{tikzpicture}[disp] \matrix[matd] (m) { @@ -865,8 +827,7 @@

    Table of Contents

    (m-1-2) -- (f) (m-2-1 |- f.south) -- (m-1-1); \end{tikzpicture}\] -
    -

    Os mesmos lemas valem para uma função \(g:W^*\to V^*\) pelas mesmas provas, basta +

    Os mesmos lemas valem para uma função \(g:W^*\to V^*\) pelas mesmas provas, basta inverter as setas. Mas também note que poderíamos ter definido as transpostas de uma forma diferente, dobrando os fios na direção oposta. Porém, usando os lemas anteriores, provar que essa transposta alternativa é igual a que já foi definida @@ -904,16 +865,14 @@

    Table of Contents

    \ftails{ft}{0}\\ }; \end{tikzpicture}\] - -

    Se compormos as duas versões da transposta e aplicarmos a identidade zig-zag, +

    Se compormos as duas versões da transposta e aplicarmos a identidade zig-zag, mostramos também que \((f^t)^t=f\) (para a transposta que definimos, claro). Juntos, esses fatos garantem que podemos desembaraçar fios soltos nos diagramas simplesmente deslizando as funções ao longo dos fios; no máximo, os fios trocam a função para sua transposta ou vice-versa.

    -Calculando com os diagramas

    -

    Até então, nós aprendemos um formalismo relativamente abstrato para os diagramas, mas ainda não é fácil calcular valores numéricos. Para nos ajudar nisso, vamos escolher uma base \((e_i)_{i=1}^n\) do espaço \(V\), e denotar \(e^i=e_i^*\) para todo \(i\in\{0,...,n\}\). O truque principal é uma decomposição da função identidade:

    +Calculando com os diagramas

    Até então, nós aprendemos um formalismo relativamente abstrato para os diagramas, mas ainda não é fácil calcular valores numéricos. Para nos ajudar nisso, vamos escolher uma base \((e_i)_{i=1}^n\) do espaço \(V\), e denotar \(e^i=e_i^*\) para todo \(i\in\{0,...,n\}\). O truque principal é uma decomposição da função identidade:

    \[\begin{tikzpicture}[disp] \matrix (m) { @@ -929,8 +888,7 @@

    Table of Contents

    \ftails[1]{dv}{1} \fheads[1]{v}{0} \end{tikzpicture}\] -
    -

    Estaremos sempre identificando \(\F^*\cong\F\), de forma que \((e_i)^t=\Phi(e_i): V^*\to\F\) e \((e^i)^t=e^i : \F\to V^*\) (lembrando: os vetores estão sendo vistos como funções). As transpostas desses elementos serão representadas com a mesma etiqueta, mas com um fio na direção oposta (já que são essencialmente os mesmos). Por exemplo, um diagrama curioso:

    +

    Estaremos sempre identificando \(\F^*\cong\F\), de forma que \((e_i)^t=\Phi(e_i): V^*\to\F\) e \((e^i)^t=e^i : \F\to V^*\) (lembrando: os vetores estão sendo vistos como funções). As transpostas desses elementos serão representadas com a mesma etiqueta, mas com um fio na direção oposta (já que são essencialmente os mesmos). Por exemplo, um diagrama curioso:

    \[\begin{tikzpicture}[disp] \matrix (c) { @@ -968,8 +926,7 @@

    Table of Contents

    \eq[.3]{m}; \node[right=of eq] {$\tr(f)$}; \end{tikzpicture}\] -
    -

    Com isso, fica bastante simples mostrar que \(\tr(f\circ g)=\tr(g\circ f)\):

    +

    Com isso, fica bastante simples mostrar que \(\tr(f\circ g)=\tr(g\circ f)\):

    \[\begin{tikzpicture}[disp] \matrix[matd] (m) { diff --git a/org-exporters/test/test-org-exporters.hs b/org-exporters/test/test-org-exporters.hs index 90e8177..a09ab94 100644 --- a/org-exporters/test/test-org-exporters.hs +++ b/org-exporters/test/test-org-exporters.hs @@ -1,11 +1,17 @@ {-# LANGUAGE TypeApplications #-} +{-# OPTIONS_GHC -Wno-orphans #-} module Main where import Data.Aeson (Value (..), toJSON) import Data.Aeson.Encode.Pretty (encodePretty) import Data.Aeson.KeyMap qualified as KM -import Ondim (OndimNode, OndimState, binding, callTemplate, evalOndimTWith) +import Data.Ix.RecursionSchemes (Fix (..)) +import Data.TreeDiff.Class (ToExpr (..)) +import Data.TreeDiff.Expr (Expr (..)) +import Data.TreeDiff.Golden (ediffGolden) +import Data.TreeDiff.OMap qualified as OM +import Ondim (OndimNode, OndimState, binding, callTemplate, evalOndimWith) import Ondim.Extra.Exceptions (prettyException) import Ondim.Targets.HTML qualified as H import Ondim.Targets.LaTeX qualified as L @@ -15,16 +21,28 @@ import Org.Exporters.Data.Templates (templatesEmbed) import Org.Exporters.HTML qualified as H import Org.Exporters.LaTeX qualified as L import Org.Exporters.Pandoc qualified as P -import Org.Exporters.Processing (OrgData, processAll) -import Org.Parser (defaultOrgOptions, parseOrgDoc) -import Org.Types (OrgDocument) +import Org.Exporters.Processing (OrgData, processAll, ExporterSettings) +import Org.Parser (defaultOrgOptions, parseOrgDoc, OrgOptions) +import Org.Types.Data.Element +import Org.Types.Data.Object +import Org.Types.Data.Section +import Org.Types.Data.StandardProperties +import Org.Types.Data.Timestamp +import Org.Types.Ix (AllOrgIx, ComposeIx (..)) +import Org.Types.Variants.Annotated (OrgDocumentData) +import Org.Types.Variants.Annotated qualified as A +import Org.Types.Variants.ParseInfo qualified as PI +import Org.Types.Variants.Plain qualified as P import Relude.Unsafe (fromJust) import System.Directory qualified as D -import System.FilePath (takeBaseName, ()) +import System.FilePath (takeBaseName, (<.>), ()) import Test.Tasty (testGroup, withResource) import Test.Tasty.Bench import Test.Tasty.Golden (goldenVsStringDiff) +import Test.Tasty.Golden.Advanced (goldenTest) +import Test.Tasty.Providers (TestName, TestTree) import Text.Pandoc.Definition (Pandoc) +import Citeproc.CslJson (CslJson) tplsPandoc :: OndimState IO tplsPandoc = templatesEmbed [P.loadPandocMd] @@ -42,7 +60,7 @@ benchsFor :: Text -> String -> ExportBackend IO -> - IO (OrgDocument, OrgData) -> + IO (A.OrgDocument, OrgData) -> (a -> LByteString) -> Benchmark benchsFor tpls ext out bk bundle pf = @@ -60,10 +78,10 @@ benchsFor tpls ext out bk bundle pf = diffTest = goldenVsStringDiff "golden" (\ref new -> ["git", "diff", "--color", "--word-diff=plain", ref, new]) out expanded = do (doc, datum) <- bundle - toRight $ - evalOndimTWith tpls $ - callTemplate @a ("document." <> ext) - `binding` documentExp bk datum doc + toRight + $ evalOndimWith tpls + $ callTemplate @a ("document." <> ext) + `binding` documentExp bk datum doc f = fromJust renderNode toRight = (either (error . prettyException) return =<<) @@ -72,27 +90,34 @@ testFile dir = do withResource loadOrg (const pass) \textIO -> testGroup (takeBaseName dir) - [ bench "Parse" $ nfIO $ parseOrg <$> textIO - , withResource (parseOrg <$> textIO) (const pass) \parsed -> + [ withResource (parseOrg <$> textIO) (const pass) \parsed -> testGroup - "Process" - [ bench "process" $ nfIO $ processAll <$> parsed + "parsing" + [ bench "bench" $ nfIO $ parseOrg <$> textIO + , diffGolden "parsed" parsed , withResource (processOrg <$> parsed) (const pass) \stuff -> testGroup - "Export" - [ benchsFor @H.HtmlDocument tplsHtml "html" (out "html") H.defBackend stuff f - , benchsFor @Pandoc tplsPandoc "md" (out "json") P.defBackend stuff json - , benchsFor @[L.Node] tplsLaTeX "tex" (out "tex") L.defBackend stuff f + "processing" + [ bench "bench" $ nfIO $ processAll <$> parsed + , diffGolden "processed" stuff + , testGroup + "export" + [ benchsFor @H.HtmlDocument tplsHtml "html" (out "html") H.defBackend stuff f + , benchsFor @Pandoc tplsPandoc "md" (out "json") P.defBackend stuff json + , benchsFor @[L.Node] tplsLaTeX "tex" (out "tex") L.defBackend stuff f + ] ] ] ] where + diffGolden :: (Eq a, ToExpr a) => TestName -> IO a -> TestTree + diffGolden name = ediffGolden goldenTest "golden" (dir name) file = dir "in.org" - out ext = dir "out." ++ ext + out ext = dir "out" <.> ext loadOrg :: IO Text = decodeUtf8 <$> readFileBS file parseOrg = parseOrgDoc defaultOrgOptions file processOrg = processAll - f :: OndimNode a => a -> LByteString + f :: (OndimNode a) => a -> LByteString f = fromJust renderNode json = encodePretty . filt . toJSON where @@ -104,3 +129,54 @@ main :: IO () main = do tests <- D.listDirectory "test/files" defaultMain (testFile . ("test/files" ) <$> tests) + +deriving instance (ToExpr o) => (ToExpr (KeywordValue o)) +deriving instance (ToExpr QuoteType) +deriving instance (ToExpr TimestampData) +deriving instance (ToExpr OrgDate) +deriving instance ToExpr OrgTime +deriving instance (ToExpr FragmentType) +deriving instance (ToExpr o) => ToExpr (FootnoteRefData o) +deriving instance (ToExpr o) => ToExpr (Citation o) +deriving instance (ToExpr o) => ToExpr (CiteReference o) +deriving instance (ToExpr BabelCall) +deriving instance (ToExpr LinkTarget) +deriving instance (AllOrgIx ToExpr k) => ToExpr (OrgElementData k ix) +deriving instance (AllOrgIx ToExpr k) => ToExpr (OrgObjectData k ix) +deriving instance (AllOrgIx ToExpr k) => ToExpr (OrgSectionData k ix) +deriving instance (AllOrgIx ToExpr k) => ToExpr (OrgDocumentData k ix) +deriving instance (ToExpr GreaterBlockType) +deriving instance (ToExpr ListType) +deriving instance (ToExpr OrderedStyle) +deriving instance (AllOrgIx ToExpr k) => ToExpr (ListItem k ix) +deriving instance (ToExpr Bullet) +deriving instance (ToExpr Checkbox) +deriving instance (ToExpr o) => (ToExpr (TableRow o)) +deriving instance (ToExpr ColumnAlignment) +deriving instance (AllOrgIx ToExpr k) => (ToExpr (PI.OrgF k ix)) +deriving instance (AllOrgIx ToExpr k) => (ToExpr (A.OrgF k ix)) +deriving instance (ToExpr StandardProperties) +deriving instance (ToExpr TodoKeyword) +deriving instance (ToExpr TodoState) +deriving instance (ToExpr Priority) +deriving instance (ToExpr PlanningInfo) +deriving instance (ToExpr OrgData) +deriving instance (ToExpr (CslJson Text)) +deriving instance (ToExpr ExporterSettings) +deriving instance (ToExpr OrgOptions) +deriving instance (ToExpr (InternalLink Text)) + +instance (AllOrgIx ToExpr k) => ToExpr (P.OrgF k ix) where + toExpr = \case + P.OrgObjectF d -> toExpr d + P.OrgElementF a d -> Rec "OrgElement" (OM.fromList [("affiliated", toExpr a), ("data", toExpr d)]) + P.OrgSectionF d -> toExpr d + +instance ToExpr (P.Org ix) where + toExpr (Fix (ComposeIx x)) = Lst $ map toExpr $ toList x + +instance ToExpr (PI.Org ix) where + toExpr (Fix (ComposeIx x)) = Lst $ map toExpr $ toList x + +instance ToExpr (A.Org ix) where + toExpr (Fix (ComposeIx x)) = Lst $ map toExpr x diff --git a/org-parser/org-parser.cabal b/org-parser/org-parser.cabal index f79cb07..81fa9ec 100644 --- a/org-parser/org-parser.cabal +++ b/org-parser/org-parser.cabal @@ -29,16 +29,16 @@ source-repository head location: git://github.com/lucasvreis/org-mode-hs.git common common-options - default-language: Haskell2010 + default-language: GHC2021 build-depends: - , aeson >=2.1.2 && <2.2 - , base >=4.15 && <4.19 - , containers >=0.6.5 && <0.7 - , megaparsec >=9.3.0 && <9.4 - , multiwalk >=0.3.0 && <0.4 - , relude >=1.2.0 && <1.3 - , replace-megaparsec >=1.4.5 && <1.5 - , text >=1.2.5 && <2.1 + , aeson >=2.1.2 && <2.3 + , base >=4.17.2 && <4.20 + , containers >=0.6.5 && <0.7 + , ix-cat + , kind-generics-th + , megaparsec >=9.3.0 && <9.7 + , relude >=1.2.0 && <1.3 + , text >=1.2.5 && <2.1 mixins: base hiding (Prelude), @@ -48,30 +48,47 @@ common common-options ghc-options: -Wall default-extensions: BlockArguments - ConstraintKinds - DeriveGeneric - FlexibleContexts - ImportQualifiedPost + DataKinds + DeriveAnyClass + DerivingStrategies + DerivingVia + DuplicateRecordFields LambdaCase MultiWayIf + NoFieldSelectors + OverloadedLabels + OverloadedRecordDot OverloadedStrings - ScopedTypeVariables - TupleSections + PatternSynonyms + TypeFamilies ViewPatterns library import: common-options hs-source-dirs: src exposed-modules: - Org.Builder Org.Compare Org.Data.Entities Org.Parser Org.Parser.Document Org.Parser.Elements Org.Parser.Objects - Org.Types - Org.Walk + Org.Types.Aeson + Org.Types.Data.Cite + Org.Types.Data.Document + Org.Types.Data.Element + Org.Types.Data.Footnote + Org.Types.Data.Object + Org.Types.Data.Section + Org.Types.Data.StandardProperties + Org.Types.Data.Timestamp + Org.Types.Ix + Org.Types.Utils + Org.Types.Variants.Basic + Org.Types.Variants.ParseInfo + Org.Types.Variants.Plain + Org.Types.Walk + Org.Types.Wrapping other-modules: Org.Parser.Common @@ -85,11 +102,12 @@ test-suite test hs-source-dirs: test main-is: test-org-parser.hs build-depends: - , neat-interpolation >=0.5 && <0.6 + , filepath >=1.4.2 && <1.5 + , neat-interpolation >=0.5 && <0.6 , org-parser - , tasty >=1.4 && <1.5 - , tasty-hunit >=0.10 && <0.11 - , tree-diff >=0.3 && <0.4 + , tasty >=1.4 && <1.5 + , tasty-golden >=2.3 && <2.4 + , tree-diff >=0.3 && <0.4 other-modules: Tests.Document diff --git a/org-parser/src/Org/Builder.hs b/org-parser/src/Org/Builder.hs deleted file mode 100644 index 064f7f1..0000000 --- a/org-parser/src/Org/Builder.hs +++ /dev/null @@ -1,273 +0,0 @@ -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE DeriveTraversable #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE StandaloneDeriving #-} -{-# LANGUAGE TypeFamilies #-} - -module Org.Builder where - -import Data.Sequence (ViewL (..), ViewR (..), viewl, viewr, (|>)) -import Data.Text qualified as T -import GHC.Exts qualified -import Org.Types - -newtype Many a = Many {unMany :: Seq a} - deriving (Ord, Eq, Typeable, Foldable, Traversable, Functor, Show, Read) - -instance One (Many a) where - type OneItem (Many a) = a - one = Many . one - -instance IsList (Many a) where - type Item (Many a) = a - fromList = Many . fromList - toList = toList . unMany - -deriving instance Generic (Many a) - -type OrgObjects = Many OrgObject - -type OrgElements = Many OrgElement - -deriving instance Semigroup OrgElements - -deriving instance Monoid OrgElements - -instance Semigroup OrgObjects where - (Many xs) <> (Many ys) = - case (viewr xs, viewl ys) of - (EmptyR, _) -> Many ys - (_, EmptyL) -> Many xs - (xs' :> x, y :< ys') -> Many (meld <> ys') - where - meld = - case (x, y) of - (Plain t1, Plain t2) -> xs' |> Plain (t1 <> t2) - (Plain t1, LineBreak) -> xs' |> Plain (T.stripEnd t1) |> LineBreak - (LineBreak, Plain t2) -> xs' |> LineBreak |> Plain (T.stripStart t2) - (Italic i1, Italic i2) -> xs' |> Italic (i1 <> i2) - (Underline i1, Underline i2) -> xs' |> Underline (i1 <> i2) - (Bold i1, Bold i2) -> xs' |> Bold (i1 <> i2) - (Subscript i1, Subscript i2) -> xs' |> Subscript (i1 <> i2) - (Superscript i1, Superscript i2) -> xs' |> Superscript (i1 <> i2) - (Strikethrough i1, Strikethrough i2) -> xs' |> Strikethrough (i1 <> i2) - (Code i1, Code i2) -> xs' |> Code (i1 <> i2) - (Verbatim i1, Verbatim i2) -> xs' |> Verbatim (i1 <> i2) - _ -> xs' |> x |> y - -instance Monoid OrgObjects where - mempty = Many mempty - mappend = (<>) - -instance IsString OrgObjects where - fromString = plain . T.pack - -instance IsString OrgElements where - fromString = element . para . plain . T.pack - --- * Element builders - -element :: OrgElementData -> OrgElements -element = one . OrgElement mempty - -element' :: [(Text, KeywordValue)] -> OrgElementData -> OrgElements -element' aff = one . OrgElement (fromList aff) - -para :: OrgObjects -> OrgElementData -para = Paragraph . toList - -export :: Text -> Text -> OrgElementData -export = ExportBlock - -example :: - Map Text Text -> - [SrcLine] -> - OrgElementData -example = ExampleBlock - -srcBlock :: - Text -> - Map Text Text -> - [(Text, Text)] -> - [SrcLine] -> - OrgElementData -srcBlock = SrcBlock - -greaterBlock :: - GreaterBlockType -> - OrgElements -> - OrgElementData -greaterBlock btype = GreaterBlock btype . toList - -drawer :: - Text -> - OrgElements -> - OrgElementData -drawer name = Drawer name . toList - -latexEnvironment :: - Text -> - Text -> - OrgElementData -latexEnvironment = LaTeXEnvironment - -listItemUnord :: Char -> OrgElements -> ListItem -listItemUnord s = ListItem (Bullet s) Nothing Nothing [] . toList - -list :: - ListType -> - [ListItem] -> - OrgElementData -list = PlainList - -orderedList :: - OrderedStyle -> - Char -> - [OrgElements] -> - OrgElementData -orderedList style separator = - PlainList (Ordered style) - . zipWith (\b -> ListItem b Nothing Nothing [] . toList) bullets - where - bullets = case style of - OrderedNum -> [Counter (show i) separator | i :: Int <- [1 ..]] - OrderedAlpha -> [Counter (one a) separator | a <- ['a' ..]] - -descriptiveList :: - [(OrgObjects, OrgElements)] -> - OrgElementData -descriptiveList = - PlainList Descriptive - . map (\(tag, els) -> ListItem (Bullet '-') Nothing Nothing (toList tag) (toList els)) - -parsedKeyword :: - OrgObjects -> - KeywordValue -parsedKeyword = ParsedKeyword . toList - -valueKeyword :: - Text -> - KeywordValue -valueKeyword = ValueKeyword - -attrKeyword :: - [(Text, Text)] -> - KeywordValue -attrKeyword = BackendKeyword - -keyword :: - Text -> - KeywordValue -> - OrgElementData -keyword = Keyword - -clock :: TimestampData -> Maybe Time -> OrgElementData -clock = Clock - -footnoteDef :: Text -> OrgElements -> OrgElementData -footnoteDef l = FootnoteDef l . toList - -horizontalRule :: OrgElementData -horizontalRule = HorizontalRule - -table :: [TableRow] -> OrgElementData -table = Table - -standardRow :: [OrgObjects] -> TableRow -standardRow = StandardRow . map toList - --- * Object builders - -plain :: Text -> OrgObjects -plain = one . Plain - -italic :: OrgObjects -> OrgObjects -italic = one . Italic . toList - -underline :: OrgObjects -> OrgObjects -underline = one . Underline . toList - -bold :: OrgObjects -> OrgObjects -bold = one . Bold . toList - -strikethrough :: OrgObjects -> OrgObjects -strikethrough = one . Strikethrough . toList - -superscript :: OrgObjects -> OrgObjects -superscript = one . Superscript . toList - -subscript :: OrgObjects -> OrgObjects -subscript = one . Subscript . toList - -singleQuoted :: OrgObjects -> OrgObjects -singleQuoted = quoted SingleQuote - -doubleQuoted :: OrgObjects -> OrgObjects -doubleQuoted = quoted DoubleQuote - -quoted :: QuoteType -> OrgObjects -> OrgObjects -quoted qt = one . Quoted qt . toList - -citation :: Citation -> OrgObjects -citation = one . Cite - -citation' :: Text -> Text -> OrgObjects -> OrgObjects -> [CiteReference] -> OrgObjects -citation' style variant prefix suffix = one . Cite . Citation style variant (toList prefix) (toList suffix) - -timestamp :: TimestampData -> OrgObjects -timestamp = one . Timestamp - --- | Plain inline code. -code :: Text -> OrgObjects -code = one . Code - --- | Inline verbatim. -verbatim :: Text -> OrgObjects -verbatim = one . Verbatim - -linebreak :: OrgObjects -linebreak = one LineBreak - -entity :: Text -> OrgObjects -entity = one . Entity - -fragment :: Text -> OrgObjects -fragment = one . LaTeXFragment RawFragment - -inlMath :: Text -> OrgObjects -inlMath = one . LaTeXFragment InlMathFragment - -dispMath :: Text -> OrgObjects -dispMath = one . LaTeXFragment DispMathFragment - -exportSnippet :: Text -> Text -> OrgObjects -exportSnippet backend = one . ExportSnippet backend - -inlBabel :: Text -> Text -> Text -> Text -> OrgObjects -inlBabel name h1 h2 args = one $ InlBabelCall (BabelCall name h1 h2 args) - -macro :: Text -> [Text] -> OrgObjects -macro = (one .) . Macro - -inlSrc :: Text -> Text -> Text -> OrgObjects -inlSrc name headers = one . Src name headers - -link :: LinkTarget -> OrgObjects -> OrgObjects -link tgt = one . Link tgt . toList - -uriLink :: Text -> Text -> OrgObjects -> OrgObjects -uriLink protocol tgt = one . Link (URILink protocol tgt) . toList - -target :: Id -> Text -> OrgObjects -target a = one . Target a - -footnoteLabel :: Text -> OrgObjects -footnoteLabel = one . FootnoteRef . FootnoteRefLabel - -footnoteInlDef :: Maybe Text -> OrgObjects -> OrgObjects -footnoteInlDef l = one . FootnoteRef . FootnoteRefDef l . toList - -statisticCookie :: Either (Int, Int) Int -> OrgObjects -statisticCookie = one . StatisticCookie diff --git a/org-parser/src/Org/Compare.hs b/org-parser/src/Org/Compare.hs index 9ec990f..b421b4c 100644 --- a/org-parser/src/Org/Compare.hs +++ b/org-parser/src/Org/Compare.hs @@ -1,59 +1,64 @@ +{-# LANGUAGE DataKinds #-} {-# LANGUAGE RankNTypes #-} -{-# LANGUAGE RecordWildCards #-} -{- | This module implements comparasion of Org contents, using multiwalk. You - should use this instead of the default Ord instances when you want to compare - Org content semantically. +{- | This module implements semantic comparasion of Org contents. You should use + this instead of the default Ord instances when you want to order Org content + semantically. -} -module Org.Compare (compareContent, compareContents, toAtoms, Atom) where +module Org.Compare (compareContents, toAtoms, Atom) where +import Data.Ix.Foldable (IFoldable (..)) +import Data.Ix.Functor (IFunctor) import Data.Text qualified as T -import Org.Types -import Org.Walk +import Org.Types.Variants.Plain +import Org.Types.Walk (queryTopDown) + +type Res = Endo [Atom] + +tellOne :: Atom -> Res +tellOne = Endo . (:) + +tellMany :: [Atom] -> Res +tellMany = Endo . (++) data Atom = Separator - | Word Text - | Literal Text - | Time DateTime + | Word !Text + | Literal !Text + | Time !OrgDateTime deriving (Eq, Ord) -compareContent :: MultiWalk MWTag a => a -> a -> Ordering -compareContent = comparing toAtoms - -compareContents :: MultiWalk MWTag a => [a] -> [a] -> Ordering -compareContents = comparing (foldMap toAtoms) - -toAtoms :: MultiWalk MWTag a => a -> [Atom] -toAtoms = buildMultiQ \f l -> - l ?> objToAtoms f ?> elmToAtoms f - -elmToAtoms :: Query [Atom] -> OrgElementData -> [Atom] -elmToAtoms f = (Separator :) . \case - ExportBlock _ t -> [Literal t] - ExampleBlock _ t -> [Literal $ srcLinesToText t] - SrcBlock {..} -> [Literal $ srcLinesToText srcBlkLines] - LaTeXEnvironment _ t -> [Literal t] - x -> f x - -objToAtoms :: Query [Atom] -> OrgObject -> [Atom] -objToAtoms f = \case - Plain t -> Word <$> T.words t - Code t -> [Literal t] - Verbatim t -> [Literal t] - Timestamp (TimestampData _ t) -> [Time t] - Timestamp (TimestampRange _ t s) -> [Time t, Time s] - Entity t -> [Literal t] - LaTeXFragment _ t -> [Literal t] - ExportSnippet _ t -> [Literal t] - FootnoteRef {} -> [] - Cite (Citation {..}) -> - (citationPrefix >>= toAtoms) - ++ ( citationReferences >>= \CiteReference {..} -> - (refPrefix >>= toAtoms) - ++ [Literal refId] - ++ (refSuffix >>= toAtoms) - ) - ++ (citationSuffix >>= toAtoms) - Src _ _ t -> [Literal t] - x -> f x +compareContents :: (IFunctor f, IFoldable f) => Org f ix -> Org f ix -> Ordering +compareContents = comparing toAtoms + +toAtoms :: (IFunctor f, IFoldable f) => Org f ix -> [Atom] +toAtoms x = appEndo (queryTopDown f x) [] + where + f :: OrgF k jx -> Res + f = \case + OrgObjectF d -> objToAtoms d + OrgElementF _k d -> elmToAtoms d + OrgSectionF {} -> mempty + +elmToAtoms :: OrgElementData k ix -> Res +elmToAtoms el = do + tellOne Separator + <> case el of + ExportBlock _ t -> tellOne $ Literal t + ExampleBlock _ t -> tellOne $ Literal $ srcLinesToText t + SrcBlock {} -> tellOne $ Literal $ srcLinesToText el.srcLines + LaTeXEnvironment _ t -> tellOne $ Literal t + _other -> mempty + +objToAtoms :: OrgObjectData k ix -> Res +objToAtoms = \case + Plain t -> tellMany (Word <$> T.words t) + Code t -> tellOne $ Literal t + Verbatim t -> tellOne $ Literal t + Timestamp ts@TimestampData {} -> tellOne $ Time ts.time + Timestamp ts@TimestampRange {} -> tellMany [Time ts.start, Time ts.end] + Entity t -> tellOne $ Literal t + LaTeXFragment _ t -> tellOne $ Literal t + ExportSnippet _ t -> tellOne $ Literal t + Src _ _ t -> tellOne $ Literal t + _other -> mempty diff --git a/org-parser/src/Org/Data/Entities.hs b/org-parser/src/Org/Data/Entities.hs index c72b4f5..524b809 100644 --- a/org-parser/src/Org/Data/Entities.hs +++ b/org-parser/src/Org/Data/Entities.hs @@ -26,10 +26,10 @@ Generated by the elisp function: -} defaultEntitiesNames :: [Text] -defaultEntitiesNames = map entityName defaultEntities +defaultEntitiesNames = map (.entityName) defaultEntities defaultEntitiesMap :: Map Text Entity -defaultEntitiesMap = fromList $ map (\e -> (entityName e, e)) defaultEntities +defaultEntitiesMap = fromList $ map ((.entityName) &&& id) defaultEntities defaultEntities :: [Entity] defaultEntities = diff --git a/org-parser/src/Org/Parser.hs b/org-parser/src/Org/Parser.hs index a7c25e1..b1e7946 100644 --- a/org-parser/src/Org/Parser.hs +++ b/org-parser/src/Org/Parser.hs @@ -29,21 +29,22 @@ parseOrg :: OrgOptions -> OrgParser a -> FilePath -> Text -> Either OrgParseErro parseOrg opt (OrgParser x) = parse $ x - `runReaderT` defaultEnv {orgEnvOptions = opt} + `runReaderT` defaultEnv {options = opt} `evalStateT` defaultState newtype OrgParserException = OrgParserException String - deriving (Show, Exception) + deriving (Show) + deriving anyclass (Exception) -- | Parse an Org document fully, with given options, and a filepath for error messages. -parseOrgDoc :: OrgOptions -> FilePath -> Text -> OrgDocument +parseOrgDoc :: OrgOptions -> FilePath -> Text -> OrgDocumentData OrgParsed i parseOrgDoc opt fp txt = case parseOrg opt orgDocument fp txt of Left e -> throw $ OrgParserException (errorBundlePretty e) Right d -> d -- | Parse an Org document in a UTF8 file, with given options. -parseOrgDocIO :: MonadIO m => OrgOptions -> FilePath -> m OrgDocument +parseOrgDocIO :: MonadIO m => OrgOptions -> FilePath -> m (OrgDocumentData OrgParsed i) parseOrgDocIO opt fp = do text <- readFileBS fp return $ parseOrgDoc opt fp $ decodeUtf8 text diff --git a/org-parser/src/Org/Parser/Common.hs b/org-parser/src/Org/Parser/Common.hs index 3992a1c..a4e53fe 100644 --- a/org-parser/src/Org/Parser/Common.hs +++ b/org-parser/src/Org/Parser/Common.hs @@ -8,32 +8,32 @@ import Prelude hiding (State, many, some) -- | Read the start of a header line, return the header level headingStart :: OrgParser Int headingStart = - try $ - (T.length <$> takeWhile1P (Just "heading bullets") (== '*')) - <* char ' ' - <* skipSpaces + try + $ (T.length <$> takeWhile1P (Just "heading bullets") (== '*')) + <* char ' ' + <* skipSpaces -parseTime :: OrgParser Time +parseTime :: OrgParser OrgTime parseTime = do hour <- (number 2 <|> number 1) <* char ':' minute <- number 2 - pure (hour, minute) + pure $ OrgTime hour minute -- | The same as 'string'', but cheaper (?) -string'' :: MonadParser m => Text -> m Text +string'' :: (MonadParser m) => Text -> m Text string'' = tokens ((==) `on` T.toLower) {-# INLINE string'' #-} -digitIntChar :: MonadParser m => m Int +digitIntChar :: (MonadParser m) => m Int digitIntChar = digitToInt <$> digitChar -digits :: MonadParser m => m Text +digits :: (MonadParser m) => m Text digits = takeWhileP (Just "digits") isDigit -digits1 :: MonadParser m => m Text +digits1 :: (MonadParser m) => m Text digits1 = takeWhile1P (Just "digits") isDigit -integer :: MonadParser m => m Int +integer :: (MonadParser m) => m Int integer = try $ do digits' <- reverse <$> some digitIntChar let toInt (x : xs) = 10 * toInt xs + x @@ -54,30 +54,30 @@ number _ = error "Number of digits to parse must be positive!" isAsciiAlpha :: Char -> Bool isAsciiAlpha c = isAsciiLower c || isAsciiUpper c -upperAscii' :: MonadParser m => m Int +upperAscii' :: (MonadParser m) => m Int upperAscii' = do c <- upperAscii pure $ ord c - ord 'A' + 1 -lowerAscii' :: MonadParser m => m Int +lowerAscii' :: (MonadParser m) => m Int lowerAscii' = do c <- lowerAscii pure $ ord c - ord 'a' + 1 -asciiAlpha' :: MonadParser m => m Int +asciiAlpha' :: (MonadParser m) => m Int asciiAlpha' = lowerAscii' <|> upperAscii' -upperAscii :: MonadParser m => m Char +upperAscii :: (MonadParser m) => m Char upperAscii = satisfy isAsciiUpper "uppercase A-Z character" -lowerAscii :: MonadParser m => m Char +lowerAscii :: (MonadParser m) => m Char lowerAscii = satisfy isAsciiLower "lowercase a-z character" -asciiAlpha :: MonadParser m => m Char +asciiAlpha :: (MonadParser m) => m Char asciiAlpha = satisfy isAsciiAlpha "a-z or A-Z character" @@ -88,7 +88,7 @@ manyAsciiAlpha = (Just "a-z or A-Z characters") isAsciiAlpha -someAsciiAlpha :: MonadParser m => m Text +someAsciiAlpha :: (MonadParser m) => m Text someAsciiAlpha = takeWhile1P (Just "a-z or A-Z characters") @@ -112,65 +112,68 @@ countSpaces tabWidth = T.foldr go 0 spacesOrTabs :: OrgParser Int spacesOrTabs = do - tw <- getsO orgSrcTabWidth + tw <- getsO (.orgSrcTabWidth) countSpaces tw <$> skipSpaces spacesOrTabs1 :: OrgParser Int spacesOrTabs1 = do - tw <- getsO orgSrcTabWidth + tw <- getsO (.orgSrcTabWidth) countSpaces tw <$> skipSpaces1 -- | Skips one or more spaces or tabs. -skipSpaces1 :: MonadParser m => m Text +skipSpaces1 :: (MonadParser m) => m Text skipSpaces1 = takeWhile1P (Just "at least one space or tab whitespace") isSpaceOrTab -- | Skips zero or more spaces or tabs. -skipSpaces :: MonadParser m => m Text +skipSpaces :: (MonadParser m) => m Text skipSpaces = takeWhileP (Just "spaces or tabs") isSpaceOrTab --- | Makes sure a value is Just, else fail with a custom --- error message. +{- | Makes sure a value is Just, else fail with a custom +error message. +-} guardMaybe :: (MonadFail m, MonadParser m) => String -> Maybe a -> m a guardMaybe _ (Just x) = pure x guardMaybe err _ = fail err -- | Parse a newline or EOF. Consumes no input at EOF! -newline' :: MonadParser m => m () +newline' :: (MonadParser m) => m () newline' = void newline <|> eof -- | Parse the rest of line, returning the contents without the final newline. -anyLine :: MonadParser m => m (Tokens Text) +anyLine :: (MonadParser m) => m (Tokens Text) anyLine = takeWhileP (Just "rest of line") (/= '\n') <* newline {-# INLINE anyLine #-} --- | Parse the rest of line, returning the contents without the final newline or EOF. --- Consumes no input at EOF! -anyLine' :: MonadParser m => m (Tokens Text) +{- | Parse the rest of line, returning the contents without the final newline or EOF. +Consumes no input at EOF! +-} +anyLine' :: (MonadParser m) => m (Tokens Text) anyLine' = takeWhileP (Just "rest of line") (/= '\n') <* newline' -- | Consumes the rest of input -takeInput :: MonadParser m => m Text +takeInput :: (MonadParser m) => m Text takeInput = takeWhileP Nothing (const True) -- | Parse a line with whitespace contents, and consume a newline at the end. -blankline :: MonadParser m => m () +blankline :: (MonadParser m) => m () blankline = try $ hspace <* newline --- | Parse a line with whitespace contents, line may end with EOF. CAUTION: this --- function may consume NO INPUT! Be mindful of infinite loops! -blankline' :: MonadParser m => m () +{- | Parse a line with whitespace contents, line may end with EOF. CAUTION: this +function may consume NO INPUT! Be mindful of infinite loops! +-} +blankline' :: (MonadParser m) => m () blankline' = try $ hspace <* newline' parseFromText :: FullState -> Text -> OrgParser b -> OrgParser b parseFromText (prevPS, prevOS) txt parser = do (cPS, cOS) <- getFullState setFullState - ( prevPS {stateInput = txt}, - -- NOTE: using cOS instead of prevOS + ( prevPS {stateInput = txt} + , -- NOTE: using cOS instead of prevOS -- is an implementation quirk. We -- don't want neither the changes of -- state done by the end parser in @@ -188,21 +191,16 @@ parseFromText (prevPS, prevOS) txt parser = do -- order in which title keywords are -- concatenated) or must be handled -- manually like affiliated keywords. - cOS - { orgStateLastChar = orgStateLastChar prevOS - } + prevOS ) result <- parser - (aPS, aOS) <- getFullState + aPS <- getParserState setFullState ( cPS { stateParseErrors = stateParseErrors cPS ++ stateParseErrors aPS - }, - aOS - { orgStateLastChar = - orgStateLastChar cOS } + , cOS ) pure result diff --git a/org-parser/src/Org/Parser/Definitions.hs b/org-parser/src/Org/Parser/Definitions.hs index c929359..1d78c28 100644 --- a/org-parser/src/Org/Parser/Definitions.hs +++ b/org-parser/src/Org/Parser/Definitions.hs @@ -5,8 +5,7 @@ module Org.Parser.Definitions ( module Org.Parser.Definitions - , module Org.Types - , module Org.Builder + , module Org.Types.Variants.ParseInfo , module Org.Parser.State , module Text.Megaparsec , module Text.Megaparsec.Char @@ -16,9 +15,8 @@ module Org.Parser.Definitions where import Data.Char (isAlphaNum, isAscii, isDigit, isLetter, isPunctuation, isSpace) -import Org.Builder (OrgElements, OrgObjects) import Org.Parser.State -import Org.Types +import Org.Types.Variants.ParseInfo import Text.Megaparsec import Text.Megaparsec.Char import Text.Megaparsec.Debug @@ -47,16 +45,16 @@ type OrgParseError = ParseErrorBundle Text Void setLastChar :: Maybe Char -> OrgParser () setLastChar lchar = - modify (\c -> c {orgStateLastChar = lchar <|> orgStateLastChar c}) + modify (\c -> c {lastChar = lchar <|> c.lastChar}) clearLastChar :: OrgParser () -clearLastChar = modify (\c -> c {orgStateLastChar = Nothing}) +clearLastChar = modify (\c -> c {lastChar = Nothing}) putLastChar :: Char -> OrgParser () -putLastChar lchar = modify (\c -> c {orgStateLastChar = Just lchar}) +putLastChar lchar = modify (\c -> c {lastChar = Just lchar}) withIndentLevel :: Int -> OrgParser a -> OrgParser a -withIndentLevel i = local \s -> s {orgEnvIndentLevel = i} +withIndentLevel i = local \s -> s {indentLevel = i} -- * State and Environment convenience functions @@ -69,26 +67,27 @@ setFullState :: FullState -> OrgParser () setFullState (pS, oS) = setParserState pS >> put oS getsO :: (OrgOptions -> a) -> OrgParser a -getsO f = asks (f . orgEnvOptions) +getsO f = asks (f . (.options)) -- * Marked parsers data Marked m a = Marked - { getMarks :: String - , getParser :: m a + { marks :: String + , parser :: m a } + deriving (Functor) -instance Functor m => Functor (Marked m) where - fmap f x@(Marked _ p) = x {getParser = fmap f p} +mapMarkedP :: (m a -> n b) -> Marked m a -> Marked n b +mapMarkedP f m = m { parser = f m.parser } -instance Alternative m => Semigroup (Marked m a) where +instance (Alternative m) => Semigroup (Marked m a) where Marked s1 p1 <> Marked s2 p2 = Marked (s1 ++ s2) (p1 <|> p2) -instance Alternative m => Monoid (Marked m a) where +instance (Alternative m) => Monoid (Marked m a) where mempty = Marked [] empty mconcat ms = Marked - (foldMap getMarks ms) - (choice $ map getParser ms) + (foldMap (.marks) ms) + (choice $ map (.parser) ms) {-# INLINE mconcat #-} diff --git a/org-parser/src/Org/Parser/Document.hs b/org-parser/src/Org/Parser/Document.hs index 3c584ec..7b2170d 100644 --- a/org-parser/src/Org/Parser/Document.hs +++ b/org-parser/src/Org/Parser/Document.hs @@ -10,31 +10,40 @@ module Org.Parser.Document import Data.Text qualified as T import Org.Parser.Common -import Org.Parser.Definitions +import Org.Parser.Definitions hiding (section) +import Org.Parser.Definitions qualified as D import Org.Parser.Elements import Org.Parser.MarkupContexts import Org.Parser.Objects import Prelude hiding (many, some) -- | Parse an Org document. -orgDocument :: OrgParser OrgDocument +orgDocument :: OrgParser (OrgDocumentData OrgParsed i) orgDocument = do skipMany commentLine properties <- option mempty propertyDrawer topLevel <- elements - sections <- many (section 1) + sections' <- sections 1 eof return $ - OrgDocument - { documentProperties = properties - , documentChildren = toList topLevel - , documentSections = sections + OrgDocumentData + { properties = properties + , children = topLevel + , sections = sections' } +sections :: Int -> OrgParser OrgSections +sections lvl = + mconcat <$> many do + begin <- getOffset + datum <- section lvl + end <- getOffset + return $ D.section begin end datum + {- | Parse an Org section and its contents. @lvl@ gives the minimum acceptable level of the heading. -} -section :: Int -> OrgParser OrgSection +section :: Int -> OrgParser OrgSectionD section lvl = try $ do level <- headingStart guard (lvl <= level) @@ -45,21 +54,20 @@ section lvl = try $ do planning <- option emptyPlanning planningInfo properties <- option mempty propertyDrawer contents <- elements - children <- many (section (level + 1)) + children <- sections (level + 1) return - OrgSection - { sectionLevel = level - , sectionProperties = properties - , sectionTodo = todoKw - , sectionIsComment = isComment - , sectionPriority = priority - , sectionTitle = toList title - , sectionRawTitle = titleTxt - , sectionAnchor = "" -- Dealt with later - , sectionTags = tags - , sectionPlanning = planning - , sectionChildren = toList contents - , sectionSubsections = children + OrgSectionData + { level = level + , properties = properties + , todo = todoKw + , comment = isComment + , priority = priority + , title = title + , rawTitle = titleTxt + , tags = tags + , planning = planning + , children = contents + , subsections = children } where titleObjects :: OrgParser (OrgObjects, [Tag], Text) @@ -85,13 +93,13 @@ section lvl = try $ do -- | Parse a to-do keyword that is registered in the state. todoKeyword :: OrgParser TodoKeyword todoKeyword = try $ do - taskStates <- getsO orgTodoKeywords + taskStates <- getsO (.orgTodoKeywords) choice (map kwParser taskStates) where kwParser :: TodoKeyword -> OrgParser TodoKeyword kwParser tdm = -- NOTE to self: space placement - "TO" is subset of "TODOKEY" - try (string (todoName tdm) *> hspace1 $> tdm) + try (string tdm.name *> hspace1 $> tdm) -- | Parse a priority cookie like @[#A]@. priorityCookie :: OrgParser Priority @@ -103,8 +111,10 @@ priorityCookie = where priorityFromChar :: OrgParser Priority priorityFromChar = - NumericPriority <$> digitIntChar - <|> LetterPriority <$> upperAscii + NumericPriority + <$> digitIntChar + <|> LetterPriority + <$> upperAscii orgTagWord :: OrgParser Text orgTagWord = @@ -125,9 +135,9 @@ planningInfo = try $ do planningDatum = skipSpaces *> choice - [ updateWith (\s p -> p {planningScheduled = Just s}) "SCHEDULED" - , updateWith (\d p -> p {planningDeadline = Just d}) "DEADLINE" - , updateWith (\c p -> p {planningClosed = Just c}) "CLOSED" + [ updateWith (\s p -> p {scheduled = Just s}) "SCHEDULED" + , updateWith (\d p -> p {deadline = Just d}) "DEADLINE" + , updateWith (\c p -> p {closed = Just c}) "CLOSED" ] updateWith fn cs = fn <$> (string cs *> char ':' *> skipSpaces *> parseTimestamp) @@ -143,7 +153,9 @@ propertyDrawer = try $ do endOfDrawer :: OrgParser Text endOfDrawer = try $ - hspace *> string' ":end:" <* blankline' + hspace + *> string' ":end:" + <* blankline' nodeProperty :: OrgParser (Text, Text) nodeProperty = try $ liftA2 (,) name value @@ -153,9 +165,9 @@ propertyDrawer = try $ do skipSpaces *> char ':' *> takeWhile1P (Just "node property name") (not . isSpace) - <&> T.stripSuffix ":" + <&> T.stripSuffix ":" >>= guardMaybe "expecting ':' at end of node property name" - <&> T.toLower + <&> T.toLower value :: OrgParser Text value = diff --git a/org-parser/src/Org/Parser/Elements.hs b/org-parser/src/Org/Parser/Elements.hs index e34599e..7659a53 100644 --- a/org-parser/src/Org/Parser/Elements.hs +++ b/org-parser/src/Org/Parser/Elements.hs @@ -24,15 +24,21 @@ module Org.Parser.Elements ) where import Data.Text qualified as T -import Org.Builder qualified as B import Org.Parser.Common import Org.Parser.Definitions import Org.Parser.MarkupContexts import Org.Parser.Objects import Relude.Extra hiding (elems, next) -import Replace.Megaparsec import Prelude hiding (many, some) +withPos' :: OrgParser a -> OrgParser (Int, Int, Int, a) +withPos' p = do + s <- getOffset + r <- p + e <- getOffset + b <- length <$> many blankline + return (s, e, b, r) + -- * General -- | Parse zero or more Org elements. @@ -55,62 +61,83 @@ elementIndented :: Int -> Bool -> OrgParser OrgElements -elementIndented minI paraEnd = try (goKws []) +elementIndented minI paraEnd = do + begin <- getOffset + try (goKws begin []) where - goKws kws = do + goKws begin kws = do notFollowedBy headingStart i <- spacesOrTabs blank kws <|> do guard (i >= minI) - optional affiliatedKeyword >>= \case - Just akw -> goKws (akw : kws) <|> return (affToKws (akw : kws)) - Nothing -> withIndentLevel i $ finalize kws - - finalize kws = do - B.element' kws <$> nonParaElement <|> do + optional (withPos' affiliatedKeyword) >>= \case + Just akw -> do + let newKws = akw : kws + goKws begin newKws <|> return (affToKws newKws) + Nothing -> withIndentLevel i $ finalize begin kws + + finalize begin kws = do + let kws' = keywordsFromList $ map (\(_, _, _, a) -> a) kws + nonParaElement begin kws' <|> do guard (not (paraEnd && null kws)) - paraIndented minI kws + paraIndented begin minI kws' blank kws = do blankline' $> affToKws kws - affToKws kws = mconcat (B.element . uncurry B.keyword <$> kws) - - nonParaElement = - choice - [ clock - , commentLine - , exampleBlock - , srcBlock - , exportBlock - , commentBlock - , greaterBlock - , plainList - , latexEnvironment - , drawer - , fixedWidth - , keyword - , horizontalRule - , table - , footnoteDef - ] - -paraIndented :: Int -> [(Text, KeywordValue)] -> OrgParser OrgElements -paraIndented minI kws = + affToKws [] = mempty + affToKws ((start, end, postBlank, (name, value)) : kws) = + affToKws kws <> element start end postBlank mempty (Keyword name value) + + nonParaElement begin kws = do + el <- + choice + [ clock + , commentLine + , exampleBlock + , srcBlock + , exportBlock + , commentBlock + , greaterBlock + , plainList + , latexEnvironment + , drawer + , fixedWidth + , keyword + , horizontalRule + , table + , footnoteDef + ] + end <- getOffset + postBlank <- length <$> many blankline + return $ element begin end postBlank kws el + +paraIndented :: Int -> Int -> Keywords (OrgParsed ObjIx) -> OrgParser OrgElements +paraIndented begin minI kws = blankline' $> mempty <|> do - (inls, next) <- withContext_ skip end (plainMarkupContext standardSet) - return $ B.element' kws (B.para inls) <> next + (inls, (e, postBlank, next)) <- withContext_ skip end (plainMarkupContext standardSet) + return $ element begin e postBlank kws (Paragraph inls) <> next where skip = anySingle >> takeWhileP Nothing (/= '\n') - end :: OrgParser OrgElements + + eofEnd = eof >> (,0,) <$> getOffset ?? mempty + + blanklineEnd = lookAhead blankline' + + indentEnd = try do + -- rest of line can't be blank, otherwise blanklineEnd would succeed + void $ lookAhead (guard . (< minI) =<< spacesOrTabs) + + headingEnd = void $ lookAhead headingStart + + end :: OrgParser (Int, Int, OrgElements) end = - (eof $> mempty) <|> try do + eofEnd <|> try do _ <- newline - lookAhead blankline' $> mempty - <|> elementIndented minI True - <|> lookAhead headingStart $> mempty - -- rest of line can't be blank, otherwise elementIndented would succeed - <|> lookAhead (try $ guard . (< minI) =<< spacesOrTabs) $> mempty + postBlank <- length <$> lookAhead (many blankline) + liftA2 (,postBlank,) getOffset do + ((blanklineEnd <|> indentEnd <|> headingEnd) $> mempty) + <|> elementIndented minI True {-# INLINEABLE paraIndented #-} -- traceWithPos :: String -> OrgParser () @@ -126,34 +153,35 @@ paraIndented minI kws = -- ** Lists -- | Parse a plain list. -plainList :: OrgParser OrgElementData +plainList :: OrgParser OrgElementD plainList = try do fstItem <- listItem rest <- many itemIndented let kind = listItemType fstItem items = fstItem : rest - return $ B.list kind items + return $ PlainList kind items where itemIndented = try do notFollowedBy headingStart - i <- asks orgEnvIndentLevel + i <- asks (.indentLevel) j <- spacesOrTabs guard (j == i) listItem -listItem :: OrgParser ListItem +listItem :: OrgParser (ListItem OrgParsed ElmIx) listItem = try do - indent <- asks orgEnvIndentLevel + indent <- asks (.indentLevel) bullet <- unorderedBullet <|> counterBullet hspace1 <|> lookAhead newline' cookie <- optional counterSet box <- optional checkbox -- for the tag, previous horizontal space must have been consumed tag <- case bullet of - Bullet _ -> option [] (toList <$> itemTag) - _ -> return [] - els <- liftA2 (<>) (paraIndented (indent + 1) []) (elementsIndented (indent + 1)) - return (ListItem bullet cookie box tag (toList els)) + Bullet _ -> optional itemTag + _ -> return Nothing + pos <- getOffset + els <- liftA2 (<>) (paraIndented pos (indent + 1) mempty) (elementsIndented (indent + 1)) + return (ListItem bullet cookie box tag els) where unorderedBullet = try $ Bullet <$> satisfy \c -> c == '+' || c == '-' || c == '*' counterBullet = try do @@ -163,26 +191,29 @@ listItem = try do counterSet :: OrgParser Int counterSet = - try $ - string "[@" - *> parseNum - <* char ']' - <* hspace + try + $ string "[@" + *> parseNum + <* char ']' + <* hspace where parseNum = integer <|> asciiAlpha' checkbox :: OrgParser Checkbox checkbox = - try $ - char '[' - *> tick - <* char ']' - <* (hspace1 <|> lookAhead newline') + try + $ char '[' + *> tick + <* char ']' + <* (hspace1 <|> lookAhead newline') where tick = - char ' ' $> BoolBox False - <|> char 'X' $> BoolBox True - <|> char '-' $> PartialBox + char ' ' + $> BoolBox False + <|> char 'X' + $> BoolBox True + <|> char '-' + $> PartialBox itemTag :: OrgParser OrgObjects itemTag = withMContext (/= '\n') (not . isSpace) end (plainMarkupContext standardSet) @@ -195,12 +226,12 @@ itemTag = withMContext (/= '\n') (not . isSpace) end (plainMarkupContext standar -- ** Greater blocks -- | Parse a greater block. -greaterBlock :: OrgParser OrgElementData +greaterBlock :: OrgParser OrgElementD greaterBlock = try do _ <- string'' "#+begin_" bname <- someNonSpace <* anyLine els <- withContext anyLine (end bname) elements - return $ B.greaterBlock (blockType bname) els + return $ GreaterBlock (blockType bname) els where blockType = \case (T.toLower -> "center") -> Center @@ -220,13 +251,13 @@ greaterBlock = try do -- ** Drawers -- | Parse a drawer. -drawer :: OrgParser OrgElementData +drawer :: OrgParser OrgElementD drawer = try do _ <- char ':' dname <- takeWhile1P (Just "drawer name") (\c -> c /= ':' && c /= '\n') char ':' >> blankline els <- withContext anyLine end elements - return $ B.drawer dname els + return $ Drawer dname els where end :: OrgParser () end = try $ hspace <* string'' ":end:" <* blankline' @@ -234,21 +265,21 @@ drawer = try do -- ** Footnote definitions -- | Parse a footnote definition. -footnoteDef :: OrgParser OrgElementData +footnoteDef :: OrgParser OrgElementD footnoteDef = try do - guard . (== 0) =<< asks orgEnvIndentLevel + guard . (== 0) =<< asks (.indentLevel) lbl <- start _ <- optional blankline' def <- withContext anyLine - ( lookAhead $ - void headingStart - <|> try (blankline' *> blankline') - <|> void (try start) + ( lookAhead + $ void headingStart + <|> try (blankline' *> blankline') + <|> void (try start) ) elements - return $ B.footnoteDef lbl def + return $ FootnoteDef lbl def where start = string "[fn:" @@ -260,13 +291,13 @@ footnoteDef = try do -- ** Tables -- | Parse a table. -table :: OrgParser OrgElementData +table :: OrgParser OrgElementD table = try do _ <- lookAhead $ char '|' rows <- some tableRow - return $ B.table rows + return $ Table rows where - tableRow :: OrgParser TableRow + tableRow :: OrgParser (TableRow (OrgParsed ObjIx)) tableRow = ruleRow <|> columnPropRow <|> standardRow ruleRow = try $ RuleRow <$ (hspace >> string "|-" >> anyLine') @@ -283,9 +314,12 @@ table = try do Just <$> cookie <|> Nothing <$ void (char '|') cookie = try do a <- - string " AlignLeft - <|> string " AlignCenter - <|> string " AlignRight + string " AlignLeft + <|> string " AlignCenter + <|> string " AlignRight _ <- digits _ <- char '>' hspace @@ -295,18 +329,12 @@ table = try do standardRow = try do hspace _ <- char '|' - B.standardRow - <$> some cell - <* blankline' + StandardRow <$> some cell <* blankline' where cell = do hspace char '|' $> mempty - <|> withMContext - (const True) - (\c -> not $ isSpace c || c == '|') - end - (plainMarkupContext standardSet) + <|> withMContext (const True) (\c -> not $ isSpace c || c == '|') end (plainMarkupContext standardSet) end = try $ hspace >> void (char '|') <|> lookAhead newline' -- * Lesser elements @@ -314,37 +342,37 @@ table = try do -- ** Code -- | Parse an example block. -exampleBlock :: OrgParser OrgElementData +exampleBlock :: OrgParser OrgElementD exampleBlock = try do _ <- string'' "#+begin_example" switches <- blockSwitches _ <- anyLine contents <- rawBlockContents end switches - pure $ B.example switches contents + pure $ ExampleBlock switches contents where end = try $ hspace *> string'' "#+end_example" <* blankline' -- | Parse a fixed width block. -fixedWidth :: OrgParser OrgElementData +fixedWidth :: OrgParser OrgElementD fixedWidth = try do - contents <- SrcLine <<$>> some (hspace *> string ": " *> anyLine') - tabWidth <- getsO orgSrcTabWidth - preserveIndent <- getsO orgSrcPreserveIndentation + contents <- some (hspace *> string ": " *> anyLine') + tabWidth <- getsO (.orgSrcTabWidth) + preserveIndent <- getsO (.orgSrcPreserveIndentation) let lines' = if preserveIndent - then map (srcLineMap (tabsToSpaces tabWidth)) contents + then map (tabsToSpaces tabWidth) contents else indentContents tabWidth contents - pure $ B.example mempty lines' + pure $ ExampleBlock mempty lines' -- | Parse a source block. -srcBlock :: OrgParser OrgElementData +srcBlock :: OrgParser OrgElementD srcBlock = try do _ <- string'' "#+begin_src" lang <- option "" $ hspace1 *> someNonSpace switches <- blockSwitches args <- headerArgs contents <- rawBlockContents end switches - pure $ B.srcBlock lang switches args contents + pure $ SrcBlock lang switches args contents where end = try $ hspace *> string'' "#+end_src" <* blankline' @@ -360,33 +388,35 @@ headerArgs = do liftA2 (,) (char ':' *> someNonSpace) - ( T.strip . fst + ( T.strip + . fst <$> findSkipping (not . isSpace) - ( try $ - lookAhead + ( try + $ lookAhead ( newline' - <|> hspace1 <* char ':' + <|> hspace1 + <* char ':' ) ) ) -- | Parse an export block. -exportBlock :: OrgParser OrgElementData +exportBlock :: OrgParser OrgElementD exportBlock = try do _ <- string'' "#+begin_export" format <- option "" $ hspace1 *> someNonSpace _ <- anyLine contents <- T.unlines <$> manyTill anyLine end - return $ B.export format contents + return $ ExportBlock format contents where end = try $ hspace *> string'' "#+end_export" <* blankline' indentContents :: Int -> [SrcLine] -> [SrcLine] -indentContents tabWidth (map (srcLineMap $ tabsToSpaces tabWidth) -> lins) = - map (srcLineMap $ T.drop minIndent) lins +indentContents tabWidth (map (tabsToSpaces tabWidth) -> lins) = + map (T.drop minIndent) lins where - minIndent = maybe 0 minimum1 (nonEmpty $ map (indentSize . srcLineContent) lins) + minIndent = maybe 0 minimum1 (nonEmpty $ map indentSize lins) indentSize = T.length . T.takeWhile (== ' ') tabsToSpaces :: Int -> Text -> Text @@ -402,12 +432,12 @@ tabsToSpaces tabWidth txt = rawBlockContents :: OrgParser void -> Map Text Text -> OrgParser [SrcLine] rawBlockContents end switches = do - contents <- manyTill (rawBlockLine switches) end - tabWidth <- getsO orgSrcTabWidth - preserveIndent <- getsO orgSrcPreserveIndentation - pure $ - if preserveIndent || "-i" `member` switches - then map (srcLineMap (tabsToSpaces tabWidth)) contents + contents <- manyTill (try quotedLine) end + tabWidth <- getsO (.orgSrcTabWidth) + preserveIndent <- getsO (.orgSrcPreserveIndentation) + pure + $ if preserveIndent || "-i" `member` switches + then map (tabsToSpaces tabWidth) contents else indentContents tabWidth contents quotedLine :: OrgParser Text @@ -416,27 +446,6 @@ quotedLine = do <$> option "" (try $ char ',' *> (string "*" <|> string "#+")) <*> anyLine -rawBlockLine :: Map Text Text -> OrgParser SrcLine -rawBlockLine switches = - try $ applyRef =<< quotedLine - where - (refpre, refpos) = - maybe - ("(ref:", ")") - (second (T.drop 2) . T.breakOn "%s") - $ lookup "-l" switches - applyRef txt - | Just (content, ref, _) <- breakCap refCookie txt = - pure $ RefLine "" ref content - | otherwise = pure $ SrcLine txt - refCookie :: Parser Text - refCookie = do - space1 <* string refpre - toText - <$> someTill - (satisfy $ \c -> isAsciiAlpha c || isDigit c || c == '-' || c == ' ') - (string refpos) - blockSwitches :: OrgParser (Map Text Text) blockSwitches = fromList <$> many (linum <|> switch <|> fmt) where @@ -444,7 +453,8 @@ blockSwitches = fromList <$> many (linum <|> switch <|> fmt) linum = try $ do hspace1 s <- - T.snoc . one + T.snoc + . one <$> oneOf ['+', '-'] <*> char 'n' num <- option "" $ try $ hspace1 *> takeWhileP Nothing isDigit @@ -457,8 +467,8 @@ blockSwitches = fromList <$> many (linum <|> switch <|> fmt) s <- string "-l" hspace1 str <- - between (char '"') (char '"') $ - takeWhileP Nothing (\c -> c /= '"' && c /= '\n') + between (char '"') (char '"') + $ takeWhileP Nothing (\c -> c /= '"' && c /= '\n') _ <- lookAhead spaceChar return (s, str) @@ -466,7 +476,8 @@ blockSwitches = fromList <$> many (linum <|> switch <|> fmt) switch = try $ do hspace1 s <- - T.snoc . one + T.snoc + . one <$> char '-' <*> oneOf ['i', 'k', 'r'] _ <- lookAhead spaceChar @@ -475,7 +486,7 @@ blockSwitches = fromList <$> many (linum <|> switch <|> fmt) -- ** LaTeX -- | Parse a LaTeX environment. -latexEnvironment :: OrgParser OrgElementData +latexEnvironment :: OrgParser OrgElementD latexEnvironment = try do _ <- string "\\begin{" ename <- @@ -484,27 +495,27 @@ latexEnvironment = try do (\c -> isAsciiAlpha c || isDigit c || c == '*') _ <- char '}' (str, _) <- findSkipping (/= '\\') (end ename) - return $ B.latexEnvironment ename $ "\\begin{" <> ename <> "}" <> str <> "\\end{" <> ename <> "}" + return $ LaTeXEnvironment ename $ "\\begin{" <> ename <> "}" <> str <> "\\end{" <> ename <> "}" where end :: Text -> OrgParser () end name = try $ string ("\\end{" <> name <> "}") *> blankline' -- ** Keywords -affiliatedKeyword :: OrgParser (Text, KeywordValue) +affiliatedKeyword :: OrgParser (Text, KeywordValue (OrgParsed ObjIx)) affiliatedKeyword = try do v <- keywordData let name = fst v unless ("attr_" `T.isPrefixOf` name) do - akws <- getsO orgElementAffiliatedKeywords + akws <- getsO (.orgElementAffiliatedKeywords) guard $ name `member` akws return v -- | Parse a keyword. -keyword :: OrgParser OrgElementData -keyword = uncurry B.keyword <$> keywordData +keyword :: OrgParser OrgElementD +keyword = uncurry Keyword <$> keywordData -keywordData :: OrgParser (Text, KeywordValue) +keywordData :: OrgParser (Text, KeywordValue (OrgParsed ObjIx)) keywordData = try do _ <- string "#+" -- This is one of the places where it is convoluted to replicate org-element @@ -512,49 +523,49 @@ keywordData = try do name <- T.toLower . fst <$> fix \me -> do res@(name, _) <- - skipManyTill' (satisfy (not . isSpace)) $ - try $ - char ':' *> notFollowedBy me + skipManyTill' (satisfy (not . isSpace)) + $ try + $ char ':' + *> notFollowedBy me guard (not $ T.null name) pure res hspace if "attr_" `T.isPrefixOf` name then do - args <- B.attrKeyword <$> headerArgs + args <- BackendKeyword <$> headerArgs return (name, args) else do text <- T.stripEnd <$> anyLine' - parsedKws <- getsO orgElementParsedKeywords + parsedKws <- getsO (.orgElementParsedKeywords) value <- if name `member` parsedKws then do st <- getFullState - ParsedKeyword . toList - <$> parseFromText st text (plainMarkupContext standardSet) + ParsedKeyword <$> parseFromText st text (plainMarkupContext standardSet) else return $ ValueKeyword text return (name, value) -- ** Horizontal Rules -- | Parse a horizontal rule. -horizontalRule :: OrgParser OrgElementData +horizontalRule :: OrgParser OrgElementD horizontalRule = try do l <- T.length <$> takeWhile1P (Just "hrule dashes") (== '-') guard (l >= 5) blankline' - return B.horizontalRule + return HorizontalRule -- ** Comments -- | Parse a comment. -commentLine :: OrgParser OrgElementData +commentLine :: OrgParser OrgElementD commentLine = try do _ <- char '#' blankline' <|> (char ' ' <|> fail "If this was meant as a comment, a space is missing here.") *> void anyLine' pure Comment -- | Parse a comment block. -commentBlock :: OrgParser OrgElementData +commentBlock :: OrgParser OrgElementD commentBlock = try do _ <- string'' "#+begin_comment" _ <- anyLine @@ -563,14 +574,14 @@ commentBlock = try do where end = try $ hspace *> string'' "#+end_comment" <* blankline' -clock :: OrgParser OrgElementData +clock :: OrgParser OrgElementD clock = try do _ <- string'' "clock: " ts <- parseTimestamp case ts of TimestampData False _ -> do blankline' - return $ B.clock ts Nothing + return $ Clock ts Nothing TimestampRange False _ _ -> do t <- optional $ do _ <- try $ do @@ -579,5 +590,5 @@ clock = try do hspace1 parseTime blankline' - return $ B.clock ts t + return $ Clock ts t _ -> fail "Clock timestamp must be inactive." diff --git a/org-parser/src/Org/Parser/MarkupContexts.hs b/org-parser/src/Org/Parser/MarkupContexts.hs index b2dbcff..8862d59 100644 --- a/org-parser/src/Org/Parser/MarkupContexts.hs +++ b/org-parser/src/Org/Parser/MarkupContexts.hs @@ -119,31 +119,34 @@ withBalancedContext lchar rchar allowed p = try do -} markupContext :: Monoid k => - (Text -> k) -> + (Int -> Int -> Text -> k) -> Marked OrgParser k -> OrgParser k markupContext f elems = go where go = try $ do - let specials :: Set Char = fromList $ getMarks elems + let specials :: Set Char = fromList elems.marks + s <- getOffset str <- optional $ takeWhile1P Nothing (`notMember` specials) + e <- getOffset -- traceM $ "consumed: " ++ show str - let self = maybe mempty f str + let self = maybe mempty (f s e) str setLastChar (T.last <$> str) (self <>) <$> (finishSelf <|> anotherEl <|> nextChar) where finishSelf = eof $> mempty anotherEl = try $ do - el <- getParser elems + el <- elems.parser rest <- go pure $ el <> rest nextChar = try $ do + s <- getOffset c <- anySingle -- traceM $ "parsed char: " ++ show c putLastChar c rest <- go - pure $ f (one c) <> rest + pure $ f s (s + 1) (T.singleton c) <> rest diff --git a/org-parser/src/Org/Parser/Objects.hs b/org-parser/src/Org/Parser/Objects.hs index 0cde5b6..b507ac3 100644 --- a/org-parser/src/Org/Parser/Objects.hs +++ b/org-parser/src/Org/Parser/Objects.hs @@ -8,6 +8,7 @@ module Org.Parser.Objects , Marked (..) , markupContext , plainMarkupContext + , mapMarkedP -- * General purpose parsers , markup @@ -40,57 +41,65 @@ module Org.Parser.Objects , statisticCookie -- * Auxiliary - , linkToTarget , parseTimestamp + , withPos ) where import Data.Set qualified as Set import Data.Text qualified as T -import Org.Builder qualified as B import Org.Data.Entities (defaultEntitiesNames) import Org.Parser.Common import Org.Parser.Definitions import Org.Parser.MarkupContexts import Prelude hiding (many, some) +withPos :: OrgParser OrgObjectD -> OrgParser OrgObjects +withPos p = do + s <- getOffset + r <- p + e <- getOffset + return $ object s e r + -- * Sets of objects minimalSet :: Marked OrgParser OrgObjects minimalSet = - mconcat - [ endline - , code - , verbatim - , italic - , underline - , bold - , strikethrough - , entity - , latexFragment - , texMathFragment - , singleQuoted - , doubleQuoted - , suscript - , statisticCookie - , macro - ] + mapMarkedP withPos + $ mconcat + [ endline + , code + , verbatim + , italic + , underline + , bold + , strikethrough + , entity + , latexFragment + , texMathFragment + , singleQuoted + , doubleQuoted + , suscript + , statisticCookie + , macro + ] standardSet :: Marked OrgParser OrgObjects standardSet = - mconcat - [ minimalSet - , regularLink - , footnoteReference - , timestamp - , exportSnippet - , inlBabel - , inlSrc - , linebreak - , target - , angleLink - , citation - ] + (minimalSet <>) + . mapMarkedP withPos + $ mconcat + [ regularLink + , footnoteReference + , timestamp + , exportSnippet + , inlBabel + , inlSrc + , linebreak + , target + , angleLink + , citation + ] {- | Parse inside a "plain context", i.e., plain text not matched by any parsers gets converted to 'Plain' objects. @@ -100,7 +109,7 @@ standardSet = @ -} plainMarkupContext :: Marked OrgParser OrgObjects -> OrgParser OrgObjects -plainMarkupContext = markupContext B.plain +plainMarkupContext = markupContext (\s e t -> object s e (Plain t)) newlineAndClear :: OrgParser Char newlineAndClear = newline <* clearLastChar @@ -113,7 +122,7 @@ emphPreChar c = isSpace c || c `Set.member` emphasisPreChars emphasisPre :: Char -> OrgParser () emphasisPre s = try $ do - lchar <- gets orgStateLastChar + lchar <- gets (.lastChar) for_ lchar $ guard . emphPreChar _ <- char s notFollowedBy spaceChar @@ -126,7 +135,7 @@ emphPostChar c = isSpace c || c `Set.member` emphasisPostChars emphasisPost :: Char -> OrgParser () emphasisPost e = try $ do - lchar <- gets orgStateLastChar + lchar <- gets (.lastChar) for_ lchar $ guard . not . isSpace _ <- char e putLastChar e @@ -139,103 +148,98 @@ emphasisSkip s = try $ do setLastChar (snd <$> T.unsnoc t) markup :: - (OrgObjects -> OrgObjects) -> + (OrgObjects -> OrgObjectD) -> Char -> - Marked OrgParser OrgObjects -markup f c = Marked [c] $ - try $ do - emphasisPre c - st <- getFullState - s <- anySingle - (t, _) <- skipManyTill' (emphasisSkip c) (emphasisPost c) - f <$> parseFromText st (T.cons s t) (plainMarkupContext standardSet) + Marked OrgParser OrgObjectD +markup f c = Marked [c] $ try do + emphasisPre c + st <- getFullState + s <- anySingle + (t, _) <- skipManyTill' (emphasisSkip c) (emphasisPost c) + f <$> parseFromText st (T.cons s t) (plainMarkupContext standardSet) rawMarkup :: - (Text -> OrgObjects) -> + (Text -> OrgObjectData OrgParsed ObjIx) -> Char -> - Marked OrgParser OrgObjects -rawMarkup f d = Marked [d] $ - try $ do - emphasisPre d - f . fst <$> skipManyTill' (emphasisSkip d) (emphasisPost d) + Marked OrgParser OrgObjectD +rawMarkup f d = Marked [d] $ try do + emphasisPre d + f . fst <$> skipManyTill' (emphasisSkip d) (emphasisPost d) -- | Parse a code object. -code :: Marked OrgParser OrgObjects -code = rawMarkup B.code '~' +code :: Marked OrgParser OrgObjectD +code = rawMarkup Code '~' -- | Parse a verbatim object. -verbatim :: Marked OrgParser OrgObjects -verbatim = rawMarkup B.verbatim '=' +verbatim :: Marked OrgParser OrgObjectD +verbatim = rawMarkup Verbatim '=' -- | Parse an italic object. -italic :: Marked OrgParser OrgObjects -italic = markup B.italic '/' +italic :: Marked OrgParser OrgObjectD +italic = markup Italic '/' -- | Parse an underline object. -underline :: Marked OrgParser OrgObjects -underline = markup B.underline '_' +underline :: Marked OrgParser OrgObjectD +underline = markup Underline '_' -- | Parse a bold object. -bold :: Marked OrgParser OrgObjects -bold = markup B.bold '*' +bold :: Marked OrgParser OrgObjectD +bold = markup Bold '*' -- | Parse a strikethrough object. -strikethrough :: Marked OrgParser OrgObjects -strikethrough = markup B.strikethrough '+' +strikethrough :: Marked OrgParser OrgObjectD +strikethrough = markup Strikethrough '+' -- | Parse a single-quoted object. -singleQuoted :: Marked OrgParser OrgObjects -singleQuoted = markup B.singleQuoted '\'' +singleQuoted :: Marked OrgParser OrgObjectD +singleQuoted = markup (Quoted SingleQuote) '\'' -- | Parse a double-quoted object. -doubleQuoted :: Marked OrgParser OrgObjects -doubleQuoted = markup B.doubleQuoted '"' +doubleQuoted :: Marked OrgParser OrgObjectD +doubleQuoted = markup (Quoted DoubleQuote) '"' + +-- TODO why is this parsed? can't it live inside plain?? -- | An endline character that can be treated as a space, not a line break. -endline :: Marked OrgParser OrgObjects -endline = - Marked "\n" $ - try $ - newlineAndClear - *> hspace - $> B.plain "\n" +endline :: Marked OrgParser OrgObjectD +endline = Marked "\n" $ try $ newlineAndClear *> hspace $> Plain "\n" -- * Entities and LaTeX fragments -- | Parse an entity object. -entity :: Marked OrgParser OrgObjects -entity = Marked "\\" $ try $ do +entity :: Marked OrgParser OrgObjectD +entity = Marked "\\" $ try do _ <- char '\\' name <- choice (map string defaultEntitiesNames) void (string "{}") <|> notFollowedBy asciiAlpha - pure $ B.entity name + pure $ Entity name -- | Parse a LaTeX fragment object. -latexFragment :: Marked OrgParser OrgObjects +latexFragment :: Marked OrgParser OrgObjectD latexFragment = Marked "\\" $ try do _ <- char '\\' mathFragment <|> rawFragment where mathFragment = try do inline <- - char '(' $> True - <|> char '[' $> False + char '(' + $> True + <|> char '[' + $> False (str, _) <- findSkipping (/= '\\') (try $ char '\\' *> char if inline then ')' else ']') - pure $ - if inline - then B.inlMath str - else B.dispMath str + pure + $ if inline + then LaTeXFragment InlMathFragment str + else LaTeXFragment DispMathFragment str - rawFragment :: MonadParser m => m OrgObjects - rawFragment = try $ do + rawFragment = try do name <- someAsciiAlpha text <- (name <>) <$> option "" brackets - pure $ B.fragment ("\\" <> text) + pure $ LaTeXFragment RawFragment ("\\" <> text) - brackets :: MonadParser m => m Text brackets = try $ do open <- satisfy (\c -> c == '{' || c == '[') let close = if open == '{' then '}' else ']' @@ -244,7 +248,7 @@ latexFragment = Marked "\\" $ try do pure $ open `T.cons` str `T.snoc` close -- | Parse a TeX math fragment object. -texMathFragment :: Marked OrgParser OrgObjects +texMathFragment :: Marked OrgParser OrgObjectD texMathFragment = Marked "$" $ try $ display <|> inline where display = try $ do @@ -253,21 +257,22 @@ texMathFragment = Marked "$" $ try $ display <|> inline findSkipping (/= '$') (string "$$") - pure $ B.dispMath str + pure $ LaTeXFragment DispMathFragment str post = do _ <- char '$' eof - <|> ( void . lookAhead $ - satisfy (\x -> isPunctuation x || isSpace x || x == '"') + <|> ( void + . lookAhead + $ satisfy (\x -> isPunctuation x || isSpace x || x == '"') ) inline = try $ do - lchar <- gets orgStateLastChar + lchar <- gets (.lastChar) for_ lchar $ guard . (/= '$') _ <- char '$' str <- singleChar <|> moreChars - pure $ B.inlMath str + pure $ LaTeXFragment InlMathFragment str moreChars = try $ do str <- takeWhile1P (Just "inside of inline math") (/= '$') @@ -289,47 +294,44 @@ texMathFragment = Marked "$" $ try $ display <|> inline -- * Export snippets -- | Parse an export snippet object. -exportSnippet :: Marked OrgParser OrgObjects -exportSnippet = Marked "@" $ - try $ do - _ <- string "@@" - backend <- - takeWhile1P - (Just "export snippet backend") - (\c -> isAsciiAlpha c || isDigit c || c == '-') - _ <- char ':' - B.exportSnippet backend . fst - <$> findSkipping (/= '@') (string "@@") +exportSnippet :: Marked OrgParser OrgObjectD +exportSnippet = Marked "@" $ try do + _ <- string "@@" + backend <- + takeWhile1P + (Just "export snippet backend") + (\c -> isAsciiAlpha c || isDigit c || c == '-') + _ <- char ':' + ExportSnippet backend . fst <$> findSkipping (/= '@') (string "@@") -- * Citations -- The following code for org-cite citations was adapted and improved upon pandoc's. -- | Parse a citation object. -citation :: Marked OrgParser OrgObjects -citation = - Marked "[" $ - B.citation <$> withBalancedContext '[' ']' (const True) orgCite +citation :: Marked OrgParser OrgObjectD +citation = Marked "[" do + Cite <$> withBalancedContext '[' ']' (const True) orgCite -- | A citation in org-cite style -orgCite :: OrgParser Citation +orgCite :: OrgParser (Citation OrgObjects) orgCite = try $ do _ <- string "cite" (style, variant) <- citeStyle _ <- char ':' space - globalPrefix <- option mempty (try (citeSuffix <* char ';')) + globalPrefix <- optional (try (citeSuffix <* char ';')) items <- citeItems - globalSuffix <- option mempty (try (char ';' *> citePrefix)) + globalSuffix <- optional (try (char ';' *> citePrefix)) space eof return Citation - { citationStyle = style - , citationVariant = variant - , citationPrefix = toList globalPrefix - , citationSuffix = toList globalSuffix - , citationReferences = items + { style = style + , variant = variant + , prefix = globalPrefix + , suffix = globalSuffix + , references = items } citeStyle :: OrgParser (Tokens Text, Tokens Text) @@ -349,21 +351,21 @@ citeStyle = do (Just "alphaNum, '_', '-' or '/' characters") (\c -> isAlphaNum c || c == '_' || c == '-' || c == '/') -citeItems :: OrgParser [CiteReference] +citeItems :: OrgParser [CiteReference OrgObjects] citeItems = citeItem `sepBy1'` char ';' where sepBy1' p sep = (:) <$> p <*> many (try $ sep >> p) -citeItem :: OrgParser CiteReference +citeItem :: OrgParser (CiteReference OrgObjects) citeItem = do - pref <- option mempty citePrefix + pref <- optional citePrefix itemKey <- orgCiteKey - suff <- option mempty citeSuffix + suff <- optional citeSuffix return CiteReference - { refId = itemKey - , refPrefix = toList pref - , refSuffix = toList suff + { id = itemKey + , prefix = pref + , suffix = suff } citePrefix :: OrgParser OrgObjects @@ -394,8 +396,8 @@ orgCiteKeyChar c = -- * Inline Babel calls -- | Parse an inline babel call object. -inlBabel :: Marked OrgParser OrgObjects -inlBabel = Marked "c" . try $ do +inlBabel :: Marked OrgParser OrgObjectD +inlBabel = Marked "c" $ try do _ <- string "call_" name <- takeWhile1P @@ -404,7 +406,7 @@ inlBabel = Marked "c" . try $ do header1 <- option "" header args <- arguments header2 <- option "" header - return $ B.inlBabel name header1 header2 args + return $ InlBabelCall $ BabelCall name header1 header2 args where header = withBalancedContext '[' ']' (/= '\n') getInput arguments = withBalancedContext '(' ')' (/= '\n') getInput @@ -412,15 +414,15 @@ inlBabel = Marked "c" . try $ do -- * Inline source blocks -- | Parse an inline source object. -inlSrc :: Marked OrgParser OrgObjects -inlSrc = Marked "s" . try $ do +inlSrc :: Marked OrgParser OrgObjectD +inlSrc = Marked "s" $ try do _ <- string "src_" name <- takeWhile1P (Just "babel call name") (\c -> not (isSpace c) && c /= '{' && c /= '[') headers <- option "" header - B.inlSrc name headers <$> body + Src name headers <$> body where header = withBalancedContext '[' ']' (/= '\n') getInput body = withBalancedContext '{' '}' (/= '\n') getInput @@ -428,17 +430,17 @@ inlSrc = Marked "s" . try $ do -- * Line breaks -- | Parse a linebreak object. -linebreak :: Marked OrgParser OrgObjects -linebreak = - Marked "\\" . try $ - B.linebreak <$ string "\\\\" <* blankline' <* clearLastChar +linebreak :: Marked OrgParser OrgObjectD +linebreak = Marked "\\" $ try do + LineBreak <$ string "\\\\" <* blankline' <* clearLastChar -- * Links -- | Parse a angle link object. -angleLink :: Marked OrgParser OrgObjects -angleLink = Marked "<" . try $ do +angleLink :: Marked OrgParser OrgObjectD +angleLink = Marked "<" $ try do _ <- char '<' + s <- getOffset protocol <- manyAsciiAlpha _ <- char ':' tgt <- fix $ \search -> do @@ -446,30 +448,32 @@ angleLink = Marked "<" . try $ do takeWhile1P (Just "angle link target") (\c -> c /= '\n' && c /= '>') - char '>' $> partial - <|> newline *> hspace *> ((T.stripEnd partial <>) <$> search) - return $ B.uriLink protocol tgt (B.plain $ protocol <> ":" <> tgt) + char '>' $> partial <|> newline *> hspace *> ((T.stripEnd partial <>) <$> search) + e <- getOffset + return $ UnresolvedLink (protocol <> ":" <> tgt) (object s (e - 1) $ Plain $ protocol <> ":" <> tgt) -- | Parse a regular link object. -regularLink :: Marked OrgParser OrgObjects -regularLink = - Marked "[" . try $ - do - _ <- string "[[" - str <- linkTarget - descr <- linkDescr <|> char ']' $> mempty - putLastChar ']' - return $ B.link (linkToTarget str) descr +regularLink :: Marked OrgParser OrgObjectD +regularLink = Marked "[" $ try do + _ <- string "[[" + str <- linkTarget + descr <- linkDescr <|> char ']' $> mempty + putLastChar ']' + return $ UnresolvedLink str descr where - linkTarget :: MonadParser m => m Text + linkTarget :: (MonadParser m) => m Text linkTarget = fix $ \rest -> do partial <- takeWhileP (Just "link target") (\c -> c /= ']' && c /= '[' && c /= '\\' && c /= '\n') - oneOf ['[', ']'] $> partial - <|> char '\\' *> liftA2 T.cons (option '\\' $ oneOf ['[', ']']) rest - <|> newline *> hspace *> ((T.stripEnd partial `T.snoc` ' ' <>) <$> rest) + oneOf ['[', ']'] + $> partial + <|> char '\\' + *> liftA2 T.cons (option '\\' $ oneOf ['[', ']']) rest + <|> newline + *> hspace + *> ((T.stripEnd partial `T.snoc` ' ' <>) <$> rest) linkDescr :: OrgParser OrgObjects linkDescr = try $ do @@ -479,69 +483,56 @@ regularLink = where skip = anySingle >> takeWhileP Nothing (/= ']') --- TODO this will probably be replaced by the AST annotations. - --- | Transform the link text into a link target. -linkToTarget :: Text -> LinkTarget -linkToTarget link - | any (`T.isPrefixOf` link) ["/", "./", "../"] = - let link' = toText (toString link) - in URILink "file" link' - | (prot, rest) <- T.break (== ':') link - , Just (_, uri) <- T.uncons rest = - URILink prot uri - | otherwise = UnresolvedLink link - -- * Targets and radio targets -- | Parse a target object. -target :: Marked OrgParser OrgObjects +target :: Marked OrgParser OrgObjectD target = Marked "<" $ try do _ <- string "<<" str <- takeWhile1P (Just "dedicated target") (\c -> c /= '<' && c /= '>' && c /= '\n') guard (not (isSpace $ T.head str)) guard (not (isSpace $ T.last str)) _ <- string ">>" - return $ B.target "" str + return $ Target str -- * Subscripts and superscripts -- | Parse a subscript or a superscript object. -suscript :: Marked OrgParser OrgObjects +suscript :: Marked OrgParser OrgObjectD suscript = Marked "_^" $ try do - lchar <- gets orgStateLastChar + lchar <- gets (.lastChar) for_ lchar $ guard . not . isSpace start <- satisfy \c -> c == '_' || c == '^' contents <- asterisk <|> balanced <|> plain - pure $ - if start == '_' - then B.subscript contents - else B.superscript contents + pure + $ if start == '_' + then Subscript contents + else Superscript contents where - asterisk = B.plain . one <$> char '*' + asterisk = withPos $ Plain . T.singleton <$> char '*' balanced = - withBalancedContext '{' '}' (const True) $ - plainMarkupContext minimalSet + withBalancedContext '{' '}' (const True) + $ plainMarkupContext minimalSet - sign = option mempty (B.plain . one <$> oneOf ['+', '-']) + sign = option mempty (withPos $ Plain . one <$> oneOf ['+', '-']) plain = - liftA2 (<>) sign $ - withMContext (const True) isAlphaNum plainEnd $ - plainMarkupContext (entity <> latexFragment) + liftA2 (<>) sign + $ withMContext (const True) isAlphaNum plainEnd + $ plainMarkupContext (mapMarkedP withPos $ entity <> latexFragment) plainEnd :: OrgParser () plainEnd = try do - lookAhead $ - eof - <|> try (some (oneOf [',', '.', '\\']) *> notFollowedBy (satisfy isAlphaNum)) - <|> void (noneOf [',', '.', '\\']) + lookAhead + $ eof + <|> try (some (oneOf [',', '.', '\\']) *> notFollowedBy (satisfy isAlphaNum)) + <|> void (noneOf [',', '.', '\\']) -- * Macros -- | Parse a macro object. -macro :: Marked OrgParser OrgObjects +macro :: Marked OrgParser OrgObjectD macro = Marked "{" $ try do _ <- string "{{{" _ <- lookAhead $ satisfy isAsciiAlpha @@ -551,38 +542,37 @@ macro = Marked "{" $ try do _ <- char '(' t <- fst <$> findSkipping (/= ')') (string ")}}}") return $ T.split (== ',') t - return $ B.macro key args + return $ Macro key args where allowedKeyChar c = isAsciiAlpha c || isDigit c || c == '-' || c == '_' -- * Footnote references -- | Parse a footnote reference object. -footnoteReference :: Marked OrgParser OrgObjects -footnoteReference = Marked "[" $ - withBalancedContext '[' ']' (const True) do - _ <- string "fn:" - lbl <- - optional $ - takeWhile1P - (Just "footnote ref label") - (\c -> isAlphaNum c || c == '-' || c == '_') - def <- - optional $ try do - _ <- char ':' - plainMarkupContext standardSet - case (lbl, def) of - (Nothing, Nothing) -> empty - (Just lbl', Nothing) -> - return $ B.footnoteLabel lbl' - (_, Just def') -> - return $ B.footnoteInlDef lbl def' +footnoteReference :: Marked OrgParser OrgObjectD +footnoteReference = Marked "[" $ withBalancedContext '[' ']' (const True) do + _ <- string "fn:" + lbl <- + optional + $ takeWhile1P + (Just "footnote ref label") + (\c -> isAlphaNum c || c == '-' || c == '_') + def <- + optional $ try do + _ <- char ':' + plainMarkupContext standardSet + case (lbl, def) of + (Nothing, Nothing) -> empty + (Just lbl', Nothing) -> + return $ FootnoteRef (FootnoteRefLabel lbl') + (_, Just def') -> + return $ FootnoteRef (FootnoteRefDef lbl def') -- * Timestamps -- | Parse a timestamp object. -timestamp :: Marked OrgParser OrgObjects -timestamp = Marked "<[" $ B.timestamp <$> parseTimestamp +timestamp :: Marked OrgParser OrgObjectD +timestamp = Marked "<[" $ Timestamp <$> parseTimestamp -- | Parse a timestamp. parseTimestamp :: OrgParser TimestampData @@ -616,7 +606,7 @@ parseTimestamp = try $ do _ <- char (snd delims) pure (date, time, repeater, warning) - parseDate :: OrgParser Date + parseDate :: OrgParser OrgDate parseDate = do year <- number 4 <* char '-' month <- number 2 <* char '-' @@ -624,7 +614,7 @@ parseTimestamp = try $ do dayName <- optional $ try do hspace1 takeWhile1P (Just "dayname characters") isLetter - pure (year, month, day, dayName) + pure $ OrgDate year month day dayName repeaterMark = tsmark ["++", ".+", "+"] @@ -638,12 +628,12 @@ parseTimestamp = try $ do -- * Statistic Cookies -- | Parse a statistic cookie object. -statisticCookie :: Marked OrgParser OrgObjects +statisticCookie :: Marked OrgParser OrgObjectD statisticCookie = Marked "[" $ try do _ <- char '[' res <- Left <$> fra <|> Right <$> pct _ <- char ']' - return $ B.statisticCookie res + return $ StatisticCookie res where fra = try $ liftA2 (,) integer (char '/' *> integer) pct = try $ integer <* char '%' diff --git a/org-parser/src/Org/Parser/State.hs b/org-parser/src/Org/Parser/State.hs index 930759f..76aae1e 100644 --- a/org-parser/src/Org/Parser/State.hs +++ b/org-parser/src/Org/Parser/State.hs @@ -3,7 +3,8 @@ module Org.Parser.State where import Data.Aeson qualified as Aeson -import Org.Types +import Org.Types.Aeson (aesonOptions) +import Org.Types.Data.Section (TodoKeyword (..), TodoState (..)) -- | Collection of todo markers in the order in which items should progress type TodoSequence = [TodoKeyword] @@ -40,30 +41,24 @@ defaultOrgOptions = -- | Org-mode parser state data OrgParserEnv = OrgParserEnv - { orgEnvOptions :: OrgOptions - , orgEnvIndentLevel :: Int + { options :: OrgOptions + , indentLevel :: Int } -- | Org-mode parser state newtype OrgParserState = OrgParserState - { orgStateLastChar :: Maybe Char + { lastChar :: Maybe Char } defaultState :: OrgParserState defaultState = OrgParserState - { orgStateLastChar = Nothing + { lastChar = Nothing } defaultEnv :: OrgParserEnv defaultEnv = OrgParserEnv - { orgEnvOptions = defaultOrgOptions - , orgEnvIndentLevel = 0 - } - -aesonOptions :: Aeson.Options -aesonOptions = - Aeson.defaultOptions - { Aeson.fieldLabelModifier = Aeson.camelTo2 '-' + { options = defaultOrgOptions + , indentLevel = 0 } diff --git a/org-parser/src/Org/Types.hs b/org-parser/src/Org/Types.hs deleted file mode 100644 index ccd2cea..0000000 --- a/org-parser/src/Org/Types.hs +++ /dev/null @@ -1,569 +0,0 @@ -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE DeriveDataTypeable #-} -{-# LANGUAGE DerivingStrategies #-} -{-# LANGUAGE FlexibleContexts #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} - -module Org.Types - ( -- * Document - OrgDocument (..) - , Properties - - -- ** Helpers - , lookupProperty - - -- * Sections - , OrgSection (..) - , TodoKeyword (..) - , TodoState (..) - , Tag - , Priority (..) - , PlanningInfo (..) - - -- ** Helpers - , lookupSectionProperty - - -- * OrgContent - , OrgContent - , documentContent - , mapContentM - , mapContent - , sectionContent - , mapSectionContentM - , mapSectionContent - - -- * Elements - , OrgElement (..) - , OrgElementData (..) - - -- ** Greater blocks - , GreaterBlockType (..) - - -- ** Source blocks - , SrcLine (..) - , srcLineContent - , srcLinesToText - , srcLineMap - - -- ** Lists - , ListType (..) - , OrderedStyle (..) - , orderedStyle - , ListItem (..) - , Bullet (..) - , Checkbox (..) - , listItemType - - -- ** Keywords - , Keywords - , KeywordValue (..) - , lookupValueKeyword - , lookupParsedKeyword - , lookupBackendKeyword - , keywordsFromList - - -- ** Tables - , TableRow (..) - , TableCell - , ColumnAlignment (..) - - -- * Objects - , OrgObject (..) - - -- ** Links - , LinkTarget (..) - , Protocol - , Id - , linkTargetToText - - -- ** LaTeX fragments - , FragmentType (..) - - -- ** Citations - , Citation (..) - , CiteReference (..) - - -- ** Footnote references - , FootnoteRefData (..) - - -- ** Timestamps - , TimestampData (..) - , DateTime - , TimestampMark - , Date - , Time - - -- * Quotes - , QuoteType (..) - - -- * Babel - , BabelCall (..) - ) where - -import Data.Aeson -import Data.Aeson.Encoding (text) -import Data.Char (isDigit, toLower) -import Data.Data (Data) -import Data.Map qualified as M -import Data.Text qualified as T - --- * Document, Sections and Headings - -data OrgDocument = OrgDocument - { documentProperties :: Properties - , documentChildren :: [OrgElement] - , documentSections :: [OrgSection] - } - deriving (Eq, Ord, Read, Show, Generic) - deriving anyclass (NFData) - -lookupProperty :: Text -> OrgDocument -> Maybe Text -lookupProperty k = M.lookup k . documentProperties - -data OrgSection = OrgSection - { sectionLevel :: Int - , sectionProperties :: Properties - , sectionTodo :: Maybe TodoKeyword - , sectionIsComment :: Bool - , sectionPriority :: Maybe Priority - , sectionTitle :: [OrgObject] - , sectionRawTitle :: Text - , sectionAnchor :: Id - -- ^ Section custom ID (Warning: this field is not populated by the parser! in - -- the near future, fields like this one and the 'Id' type will be removed in - -- favor of AST extensibility). See also the documentation for 'LinkTarget' - , sectionTags :: [Tag] - , sectionPlanning :: PlanningInfo - , sectionChildren :: [OrgElement] - , sectionSubsections :: [OrgSection] - } - deriving (Eq, Ord, Read, Show, Typeable, Data, Generic) - deriving anyclass (NFData) - -lookupSectionProperty :: Text -> OrgSection -> Maybe Text -lookupSectionProperty k = M.lookup k . sectionProperties - -type OrgContent = ([OrgElement], [OrgSection]) - -documentContent :: OrgDocument -> OrgContent -documentContent doc = (documentChildren doc, documentSections doc) - -mapContentM :: Monad m => (OrgContent -> m OrgContent) -> OrgDocument -> m OrgDocument -mapContentM f d = do - (c', s') <- f (documentContent d) - pure $ d {documentChildren = c', documentSections = s'} - -mapContent :: (OrgContent -> OrgContent) -> OrgDocument -> OrgDocument -mapContent f = runIdentity . mapContentM (Identity . f) - -sectionContent :: OrgSection -> OrgContent -sectionContent sec = (sectionChildren sec, sectionSubsections sec) - -mapSectionContentM :: Monad m => (OrgContent -> m OrgContent) -> OrgSection -> m OrgSection -mapSectionContentM f d = do - (c', s') <- f (sectionContent d) - pure $ d {sectionChildren = c', sectionSubsections = s'} - -mapSectionContent :: (OrgContent -> OrgContent) -> OrgSection -> OrgSection -mapSectionContent f = runIdentity . mapSectionContentM (Identity . f) - -type Tag = Text - --- | The states in which a todo item can be -data TodoState = Todo | Done - deriving (Eq, Ord, Show, Read, Typeable, Data, Generic) - deriving anyclass (NFData) - -instance ToJSON TodoState where - toJSON Todo = "todo" - toJSON Done = "done" - toEncoding Todo = text "todo" - toEncoding Done = text "done" - -instance FromJSON TodoState where - parseJSON = - genericParseJSON - defaultOptions - { constructorTagModifier = map toLower - } - --- | A to-do keyword like @TODO@ or @DONE@. -data TodoKeyword = TodoKeyword - { todoState :: TodoState - , todoName :: Text - } - deriving (Show, Eq, Ord, Read, Typeable, Data, Generic) - deriving anyclass (NFData) - -instance ToJSON TodoKeyword where - toJSON (TodoKeyword s n) = object ["state" .= s, "name" .= n] - toEncoding (TodoKeyword s n) = pairs ("state" .= s <> "name" .= n) - -instance FromJSON TodoKeyword where - parseJSON = withObject "Todo Keyword" $ \v -> - TodoKeyword <$> v .: "state" <*> v .: "name" - -data Priority - = LetterPriority Char - | NumericPriority Int - deriving (Show, Eq, Ord, Read, Typeable, Data, Generic) - deriving anyclass (NFData) - -type Date = (Int, Int, Int, Maybe Text) - -type Time = (Int, Int) - -type TimestampMark = (Text, Int, Char) - -type DateTime = (Date, Maybe Time, Maybe TimestampMark, Maybe TimestampMark) - --- | An Org timestamp, including repetition marks. -data TimestampData - = TimestampData Bool DateTime - | TimestampRange Bool DateTime DateTime - deriving (Show, Eq, Ord, Read, Typeable, Data, Generic) - deriving anyclass (NFData) - --- | Planning information for a subtree/headline. -data PlanningInfo = PlanningInfo - { planningClosed :: Maybe TimestampData - , planningDeadline :: Maybe TimestampData - , planningScheduled :: Maybe TimestampData - } - deriving (Show, Eq, Ord, Read, Typeable, Data, Generic) - deriving anyclass (NFData) - -type Properties = Map Text Text - --- * Elements - --- | Org element. Like a Pandoc Block. -data OrgElement = OrgElement {affiliatedKeywords :: Keywords, elementData :: OrgElementData} - deriving (Eq, Ord, Read, Show, Typeable, Data, Generic) - deriving anyclass (NFData) - -data OrgElementData - = -- | Clock - Clock - TimestampData - -- ^ Clock timestamp - (Maybe Time) - -- ^ Duration - | -- | Greater block - GreaterBlock - { blkType :: GreaterBlockType - -- ^ Greater block type - , blkElements :: [OrgElement] - -- ^ Greater block elements - } - | -- | Drawer - Drawer - { drawerName :: Text - -- ^ Drawer name - , drawerElements :: [OrgElement] - -- ^ Drawer elements - } - | -- | Plain list - PlainList - { listType :: ListType - -- ^ List types - , listItems :: [ListItem] - -- ^ List items - } - | -- | Export block - ExportBlock - Text - -- ^ Format - Text - -- ^ Contents - | -- | Example block - ExampleBlock - (Map Text Text) - -- ^ Switches - [SrcLine] - -- ^ Contents - | -- | Source blocks - SrcBlock - { srcBlkLang :: Text - -- ^ Language - , srcBlkSwitches :: Map Text Text - -- ^ Switches - , srcBlkArguments :: [(Text, Text)] - -- ^ Header arguments - , srcBlkLines :: [SrcLine] - -- ^ Contents - } - | VerseBlock [[OrgObject]] - | HorizontalRule - | Keyword - { keywordKey :: Text - , keywordValue :: KeywordValue - } - | LaTeXEnvironment - Text - -- ^ Environment name - Text - -- ^ Environment contents - | Paragraph [OrgObject] - | Table [TableRow] - | FootnoteDef - Text - -- ^ Footnote name - [OrgElement] - -- ^ Footnote content - | Comment - deriving (Eq, Ord, Read, Show, Typeable, Data, Generic) - deriving anyclass (NFData) - -data QuoteType = SingleQuote | DoubleQuote - deriving (Eq, Ord, Read, Show, Typeable, Data, Generic) - deriving anyclass (NFData) - -data SrcLine - = SrcLine Text - | RefLine - Id - -- ^ Reference id (its anchor) - Text - -- ^ Reference name (how it appears) - Text - -- ^ Line contents - deriving (Eq, Ord, Read, Show, Typeable, Data, Generic) - deriving anyclass (NFData) - -srcLineContent :: SrcLine -> Text -srcLineContent (SrcLine c) = c -srcLineContent (RefLine _ _ c) = c - -srcLinesToText :: [SrcLine] -> Text -srcLinesToText = T.unlines . map srcLineContent - -srcLineMap :: (Text -> Text) -> SrcLine -> SrcLine -srcLineMap f (SrcLine c) = SrcLine (f c) -srcLineMap f (RefLine i t c) = RefLine i t (f c) - --- Keywords - -data KeywordValue - = ValueKeyword Text - | ParsedKeyword [OrgObject] - | BackendKeyword [(Text, Text)] - deriving (Eq, Ord, Read, Show, Typeable, Data, Generic) - deriving anyclass (NFData) - -instance Semigroup KeywordValue where - (ValueKeyword t1) <> (ValueKeyword t2) = ValueKeyword (t1 <> "\n" <> t2) - (ParsedKeyword t1) <> (ParsedKeyword t2) = ParsedKeyword (t1 <> t2) - (BackendKeyword b1) <> (BackendKeyword b2) = BackendKeyword (b1 <> b2) - _ <> x = x - -type Keywords = Map Text KeywordValue - -lookupValueKeyword :: Text -> Keywords -> Text -lookupValueKeyword key kws = fromMaybe mempty do - ValueKeyword x <- M.lookup key kws - return x - -lookupParsedKeyword :: Text -> Keywords -> [OrgObject] -lookupParsedKeyword key kws = fromMaybe mempty do - ParsedKeyword x <- M.lookup key kws - return x - -lookupBackendKeyword :: Text -> Keywords -> [(Text, Text)] -lookupBackendKeyword key kws = fromMaybe mempty do - BackendKeyword x <- M.lookup key kws - return x - -keywordsFromList :: [(Text, KeywordValue)] -> Keywords -keywordsFromList = M.fromListWith (flip (<>)) - --- Greater Blocks - -data GreaterBlockType = Center | Quote | Special Text - deriving (Eq, Ord, Read, Show, Typeable, Data, Generic) - deriving anyclass (NFData) - --- Lists - -data ListType = Ordered OrderedStyle | Descriptive | Unordered Char - deriving (Eq, Ord, Read, Show, Typeable, Data, Generic) - deriving anyclass (NFData) - -data OrderedStyle = OrderedNum | OrderedAlpha - deriving (Eq, Ord, Read, Show, Typeable, Data, Generic) - deriving anyclass (NFData) - -orderedStyle :: Text -> OrderedStyle -orderedStyle (T.any isDigit -> True) = OrderedNum -orderedStyle _ = OrderedAlpha - -{- | One item of a list. Parameters are bullet, counter cookie, checkbox and -tag. --} -data ListItem = ListItem Bullet (Maybe Int) (Maybe Checkbox) [OrgObject] [OrgElement] - deriving (Eq, Ord, Read, Show, Typeable, Data, Generic) - deriving anyclass (NFData) - -data Bullet = Bullet Char | Counter Text Char - deriving (Eq, Ord, Read, Show, Typeable, Data, Generic) - deriving anyclass (NFData) - -data Checkbox = BoolBox Bool | PartialBox - deriving (Eq, Ord, Read, Show, Typeable, Data, Generic) - deriving anyclass (NFData) - -listItemType :: ListItem -> ListType -listItemType (ListItem (Counter t _) _ _ _ _) = Ordered (orderedStyle t) -listItemType (ListItem (Bullet _) _ _ (_ : _) _) = Descriptive -listItemType (ListItem (Bullet c) _ _ _ _) = Unordered c - --- Babel call - -data BabelCall = BabelCall - { babelCallName :: Text - , babelCallHeader1 :: Text - , babelCallHeader2 :: Text - , babelCallArguments :: Text - } - deriving (Eq, Ord, Read, Show, Typeable, Data, Generic) - deriving anyclass (NFData) - --- Tables - -data TableRow - = StandardRow [TableCell] - | ColumnPropsRow [Maybe ColumnAlignment] - | RuleRow - deriving (Eq, Ord, Read, Show, Typeable, Data, Generic) - deriving anyclass (NFData) - -type TableCell = [OrgObject] - -data ColumnAlignment = AlignLeft | AlignCenter | AlignRight - deriving (Eq, Ord, Read, Show, Typeable, Data, Generic) - deriving anyclass (NFData) - --- * Objects (inline elements) - --- | Objects (inline elements). -data OrgObject - = Plain Text - | LineBreak - | Italic [OrgObject] - | Underline [OrgObject] - | Bold [OrgObject] - | Strikethrough [OrgObject] - | Superscript [OrgObject] - | Subscript [OrgObject] - | Quoted QuoteType [OrgObject] - | Code Text - | Verbatim Text - | Timestamp TimestampData - | -- | Entity (e.g. @\\alpha{}@) - Entity - Text - -- ^ Name (e.g. @alpha@) - | LaTeXFragment FragmentType Text - | -- | Inline export snippet (e.g. @\@\@html:\
    \@\@@) - ExportSnippet - Text - -- ^ Back-end (e.g. @html@) - Text - -- ^ Value (e.g. @\
    @) - | -- | Footnote reference. - FootnoteRef FootnoteRefData - | Cite Citation - | InlBabelCall BabelCall - | -- | Inline source (e.g. @src_html[:foo bar]{\
    }@) - Src - Text - -- ^ Language (e.g. @html@) - Text - -- ^ Parameters (e.g. @:foo bar@) - Text - -- ^ Value (e.g. @\
    @) - | Link LinkTarget [OrgObject] - | -- | Inline target (e.g. @\<\<\\>\>@) - Target - Id - -- ^ Anchor (Warning: this field is not populated by the parser! --- in - -- the near future, fields like this one and the 'Id' type will be removed - -- in favor of AST extensibility). See also the documentation for - -- 'LinkTarget' - Text - -- ^ Name - | -- | Org inline macro (e.g. @{{{poem(red,blue)}}}@) - Macro - Text - -- ^ Macro name (e.g. @"poem"@) - [Text] - -- ^ Arguments (e.g. @["red", "blue"]@) - | -- | Statistic cookies. - StatisticCookie - (Either (Int, Int) Int) - -- ^ Either @[num1/num2]@ or @[percent%]@. - deriving (Show, Eq, Ord, Read, Typeable, Data, Generic) - deriving anyclass (NFData) - --- | Data for a footnote reference. -data FootnoteRefData - = -- | Label-only footnote reference (e.g. @[fn:foo]@) - FootnoteRefLabel - Text - -- ^ Label (e.g. @foo@) - | -- | Inline footnote definition (e.g. @[fn:foo::bar]@) - FootnoteRefDef - (Maybe Text) - -- ^ Label (if present, e.g. @foo@) - [OrgObject] - -- ^ Content (e.g. @bar@) - deriving (Show, Eq, Ord, Read, Typeable, Data, Generic) - deriving anyclass (NFData) - -type Protocol = Text - -type Id = Text - -{- | Link target. Note that the parser does not resolve internal links. Instead, -they should be resolved using the functions in [@org-exporters@ -package](https://github.com/lucasvreis/org-mode-hs). In the near future, the -'InternalLink' constructor and 'Id' type will be removed in favor of AST -extensibility. See also the documentation for 'Target'. --} -data LinkTarget - = URILink Protocol Text - | InternalLink Id - | UnresolvedLink Text - deriving (Show, Eq, Ord, Read, Typeable, Data, Generic) - deriving anyclass (NFData) - -linkTargetToText :: LinkTarget -> Text -linkTargetToText = \case - URILink prot l -> prot <> ":" <> l - InternalLink l -> l - UnresolvedLink l -> l - -data FragmentType - = RawFragment - | InlMathFragment - | DispMathFragment - deriving (Show, Eq, Ord, Read, Typeable, Data, Generic) - deriving anyclass (NFData) - -data Citation = Citation - { citationStyle :: Text - , citationVariant :: Text - , citationPrefix :: [OrgObject] - , citationSuffix :: [OrgObject] - , citationReferences :: [CiteReference] - } - deriving (Show, Eq, Ord, Read, Typeable, Data, Generic) - deriving anyclass (NFData) - -data CiteReference = CiteReference - { refId :: Text - , refPrefix :: [OrgObject] - , refSuffix :: [OrgObject] - } - deriving (Show, Eq, Ord, Read, Typeable, Data, Generic) - deriving anyclass (NFData) diff --git a/org-parser/src/Org/Types/Aeson.hs b/org-parser/src/Org/Types/Aeson.hs new file mode 100644 index 0000000..712df02 --- /dev/null +++ b/org-parser/src/Org/Types/Aeson.hs @@ -0,0 +1,8 @@ +module Org.Types.Aeson where +import qualified Data.Aeson as Aeson + +aesonOptions :: Aeson.Options +aesonOptions = + Aeson.defaultOptions + { Aeson.fieldLabelModifier = Aeson.camelTo2 '-' + } diff --git a/org-parser/src/Org/Types/Data/Cite.hs b/org-parser/src/Org/Types/Data/Cite.hs new file mode 100644 index 0000000..2002269 --- /dev/null +++ b/org-parser/src/Org/Types/Data/Cite.hs @@ -0,0 +1,3 @@ +-- | + +module Org.Types.Data.Cite where diff --git a/org-parser/src/Org/Types/Data/Document.hs b/org-parser/src/Org/Types/Data/Document.hs new file mode 100644 index 0000000..e06331a --- /dev/null +++ b/org-parser/src/Org/Types/Data/Document.hs @@ -0,0 +1,32 @@ +{-# LANGUAGE TemplateHaskell #-} + +module Org.Types.Data.Document + ( OrgDocumentData (..) + ) where + +import Control.Category.Endofunctor (Endofunctor) +import Control.Category.Natural (type (~>)) +import Data.Ix.Foldable (IFoldable) +import Data.Ix.Instances +import Data.Ix.Traversable (ITraversable) +import Generics.Kind.TH (deriveGenericK) +import Org.Types.Data.Section (Properties) +import Org.Types.Ix + +data OrgDocumentData k (_i :: OrgIx) = OrgDocumentData + { properties :: Properties + , children :: k ElmIx + , sections :: k SecIx + } + deriving (Typeable, Generic) + +deriving instance (AllOrgIx Show k) => Show (OrgDocumentData k ix) +deriving instance (AllOrgIx Read k) => Read (OrgDocumentData k ix) +deriving instance (AllOrgIx Eq k) => Eq (OrgDocumentData k ix) +deriving instance (AllOrgIx Ord k) => Ord (OrgDocumentData k ix) +deriving instance (AllOrgIx NFData k) => NFData (OrgDocumentData k ix) + +$(deriveGenericK ''OrgDocumentData) +deriving via (Generically OrgDocumentData) instance (Endofunctor (~>) OrgDocumentData) +deriving via (Generically OrgDocumentData) instance (IFoldable OrgDocumentData) +deriving via (Generically OrgDocumentData) instance (ITraversable OrgDocumentData) diff --git a/org-parser/src/Org/Types/Data/Element.hs b/org-parser/src/Org/Types/Data/Element.hs new file mode 100644 index 0000000..3458f77 --- /dev/null +++ b/org-parser/src/Org/Types/Data/Element.hs @@ -0,0 +1,259 @@ +{-# LANGUAGE TemplateHaskell #-} + +module Org.Types.Data.Element + ( OrgElementData (..) + + -- ** Greater blocks + , GreaterBlockType (..) + + -- ** Source blocks + , SrcLine + , srcLinesToText + + -- ** Lists + , ListType (..) + , OrderedStyle (..) + , orderedStyle + , ListItem (..) + , Bullet (..) + , Checkbox (..) + , listItemType + + -- * Constructors + , listItemUnord + , orderedList + , descriptiveList + + -- ** Keywords + , Keywords + , KeywordValue (..) + , lookupValueKeyword + , lookupParsedKeyword + , lookupBackendKeyword + , keywordsFromList + + -- ** Tables + , TableRow (..) + , ColumnAlignment (..) + ) where + +import Control.Category.Endofunctor (Endofunctor) +import Control.Category.Natural (type (~>)) +import Data.Char (isDigit) +import Data.Ix.Foldable (IFoldable) +import Data.Ix.Instances +import Data.Ix.Traversable (ITraversable) +import Data.Map qualified as M +import Data.Text qualified as T +import Generics.Kind.TH (deriveGenericK) +import Org.Types.Data.Timestamp (OrgTime, TimestampData) +import Org.Types.Ix + +data OrgElementData k (_i :: OrgIx) + = -- | Clock + Clock + -- | Clock timestamp + TimestampData + -- | Duration + (Maybe OrgTime) + | -- | Greater block + GreaterBlock + -- | Greater block type + GreaterBlockType + -- | Greater block elements + (k ElmIx) + | -- | Drawer + Drawer + -- | Drawer name + Text + -- | Drawer elements + (k ElmIx) + | -- | Plain list + PlainList + -- | List types + ListType + -- | List items + [ListItem k _i] + | -- | Export block + ExportBlock + -- | Format + Text + -- | Contents + Text + | -- | Example block + ExampleBlock + -- | Switches + (Map Text Text) + -- | Contents + [SrcLine] + | -- | Source blocks + SrcBlock + { language :: Text + -- ^ Language + , switches :: Map Text Text + -- ^ Switches + , header :: [(Text, Text)] + -- ^ Header arguments + , srcLines :: [SrcLine] + -- ^ Contents + } + | VerseBlock (k ObjIx) + | HorizontalRule + | Keyword + Text + (KeywordValue (k ObjIx)) + | LaTeXEnvironment + -- | Environment name + Text + -- | Environment contents + Text + | Paragraph (k ObjIx) + | Table [TableRow (k ObjIx)] + | FootnoteDef + -- | Footnote name + Text + -- | Footnote content + (k ElmIx) + | Comment + deriving (Typeable, Generic) + +deriving instance (AllOrgIx Show k) => Show (OrgElementData k ix) +deriving instance (AllOrgIx Read k) => Read (OrgElementData k ix) +deriving instance (AllOrgIx Eq k) => Eq (OrgElementData k ix) +deriving instance (AllOrgIx Ord k) => Ord (OrgElementData k ix) +deriving instance (AllOrgIx NFData k) => NFData (OrgElementData k ix) + +type SrcLine = Text + +srcLinesToText :: [SrcLine] -> Text +srcLinesToText = T.unlines + +-- Keywords + +data KeywordValue o + = ValueKeyword Text + | ParsedKeyword o + | BackendKeyword [(Text, Text)] + deriving (Eq, Ord, Read, Show, Typeable, Generic, Functor, Foldable, Traversable) + deriving anyclass (NFData) + +instance (Semigroup o) => Semigroup (KeywordValue o) where + (ValueKeyword t1) <> (ValueKeyword t2) = ValueKeyword (t1 <> "\n" <> t2) + (ParsedKeyword t1) <> (ParsedKeyword t2) = ParsedKeyword (t1 <> t2) + (BackendKeyword b1) <> (BackendKeyword b2) = BackendKeyword (b1 <> b2) + _ <> x = x + +type Keywords o = Map Text (KeywordValue o) + +lookupValueKeyword :: Text -> Keywords o -> Text +lookupValueKeyword key kws = fromMaybe mempty do + ValueKeyword x <- M.lookup key kws + return x + +lookupParsedKeyword :: (Monoid o) => Text -> Keywords o -> o +lookupParsedKeyword key kws = fromMaybe mempty do + ParsedKeyword x <- M.lookup key kws + return x + +lookupBackendKeyword :: Text -> Keywords o -> [(Text, Text)] +lookupBackendKeyword key kws = fromMaybe mempty do + BackendKeyword x <- M.lookup key kws + return x + +keywordsFromList :: (Semigroup o) => [(Text, KeywordValue o)] -> Keywords o +keywordsFromList = M.fromListWith (flip (<>)) + +-- Greater Blocks + +data GreaterBlockType = Center | Quote | Special Text + deriving (Eq, Ord, Read, Show, Typeable, Generic) + deriving anyclass (NFData) + +-- Lists + +data ListType = Ordered OrderedStyle | Descriptive | Unordered Char + deriving (Eq, Ord, Read, Show, Typeable, Generic) + deriving anyclass (NFData) + +data OrderedStyle = OrderedNum | OrderedAlpha + deriving (Eq, Ord, Read, Show, Typeable, Generic) + deriving anyclass (NFData) + +orderedStyle :: Text -> OrderedStyle +orderedStyle (T.any isDigit -> True) = OrderedNum +orderedStyle _ = OrderedAlpha + +{- | One item of a list. Parameters are bullet, counter cookie, checkbox and +tag. +-} +data ListItem k (i :: OrgIx) = ListItem + { bullet :: Bullet + , counter :: Maybe Int + , checkbox :: Maybe Checkbox + , tag :: Maybe (k ObjIx) + , content :: k ElmIx + } + deriving (Typeable, Generic) + +deriving instance (AllOrgIx Show k) => Show (ListItem k ix) +deriving instance (AllOrgIx Read k) => Read (ListItem k ix) +deriving instance (AllOrgIx Eq k) => Eq (ListItem k ix) +deriving instance (AllOrgIx Ord k) => Ord (ListItem k ix) +deriving instance (AllOrgIx NFData k) => NFData (ListItem k ix) + +data Bullet = Bullet Char | Counter Text Char + deriving (Eq, Ord, Read, Show, Typeable, Generic) + deriving anyclass (NFData) + +data Checkbox = BoolBox Bool | PartialBox + deriving (Eq, Ord, Read, Show, Typeable, Generic) + deriving anyclass (NFData) + +listItemType :: ListItem k i -> ListType +listItemType (ListItem (Counter t _) _ _ _ _) = Ordered (orderedStyle t) +listItemType (ListItem Bullet {} _ _ Just {} _) = Descriptive +listItemType (ListItem (Bullet c) _ _ _ _) = Unordered c + +listItemUnord :: Char -> k ElmIx -> ListItem k ix +listItemUnord s = ListItem (Bullet s) Nothing Nothing Nothing + +orderedList :: + OrderedStyle -> + Char -> + [k ElmIx] -> + OrgElementData k ix +orderedList style separator = + PlainList (Ordered style) + . zipWith (\b -> ListItem b Nothing Nothing Nothing) bullets + where + bullets = case style of + OrderedNum -> [Counter (show i) separator | i :: Int <- [1 ..]] + OrderedAlpha -> [Counter (one a) separator | a <- ['a' ..]] + +descriptiveList :: [(k ObjIx, k ElmIx)] -> OrgElementData k ix +descriptiveList = + PlainList Descriptive + . map (\(tag, els) -> ListItem (Bullet '-') Nothing Nothing (Just tag) els) + +-- Tables + +data TableRow o + = StandardRow [o] + | ColumnPropsRow [Maybe ColumnAlignment] + | RuleRow + deriving (Eq, Ord, Read, Show, Typeable, Generic, Functor, Foldable, Traversable) + deriving anyclass (NFData) + +data ColumnAlignment = AlignLeft | AlignCenter | AlignRight + deriving (Eq, Ord, Read, Show, Typeable, Generic) + deriving anyclass (NFData) + +$(deriveGenericK ''ListItem) +deriving via (Generically ListItem) instance (Endofunctor (~>) ListItem) +deriving via (Generically ListItem) instance (IFoldable ListItem) +deriving via (Generically ListItem) instance (ITraversable ListItem) + +$(deriveGenericK ''OrgElementData) +deriving via (Generically OrgElementData) instance (Endofunctor (~>) OrgElementData) +deriving via (Generically OrgElementData) instance (IFoldable OrgElementData) +deriving via (Generically OrgElementData) instance (ITraversable OrgElementData) diff --git a/org-parser/src/Org/Types/Data/Footnote.hs b/org-parser/src/Org/Types/Data/Footnote.hs new file mode 100644 index 0000000..c9c6027 --- /dev/null +++ b/org-parser/src/Org/Types/Data/Footnote.hs @@ -0,0 +1,3 @@ +-- | + +module Org.Types.Data.Footnote where diff --git a/org-parser/src/Org/Types/Data/Object.hs b/org-parser/src/Org/Types/Data/Object.hs new file mode 100644 index 0000000..45b1d41 --- /dev/null +++ b/org-parser/src/Org/Types/Data/Object.hs @@ -0,0 +1,157 @@ +{-# LANGUAGE TemplateHaskell #-} + +module Org.Types.Data.Object + ( -- * Objects + OrgObjectData (..) + + -- ** Links + , Protocol + + -- ** LaTeX fragments + , FragmentType (..) + + -- ** Citations + , Citation (..) + , CiteReference (..) + + -- ** Footnote references + , FootnoteRefData (..) + + -- * Quotes + , QuoteType (..) + + -- * Babel + , BabelCall (..) + ) where + +import Control.Category.Endofunctor (Endofunctor) +import Control.Category.Natural (type (~>)) +import Data.Ix.Foldable (IFoldable) +import Data.Ix.Instances +import Data.Ix.Traversable (ITraversable) +import Generics.Kind.TH +import Org.Types.Data.Timestamp (TimestampData) +import Org.Types.Ix + +-- | Objects (inline elements). +data OrgObjectData k (_i :: OrgIx) + = Plain Text + | LineBreak + | Italic (k ObjIx) + | Underline (k ObjIx) + | Bold (k ObjIx) + | Strikethrough (k ObjIx) + | Superscript (k ObjIx) + | Subscript (k ObjIx) + | Quoted QuoteType (k ObjIx) + | Code Text + | Verbatim Text + | Timestamp TimestampData + | -- | Entity (e.g. @\\alpha{}@) + Entity + -- | Name (e.g. @alpha@) + Text + | LaTeXFragment FragmentType Text + | -- | Inline export snippet (e.g. @\@\@html:\
    \@\@@) + ExportSnippet + -- | Back-end (e.g. @html@) + Text + -- | Value (e.g. @\
    @) + Text + | -- | Footnote reference. + FootnoteRef (FootnoteRefData (k ObjIx)) + | Cite (Citation (k ObjIx)) + | InlBabelCall BabelCall + | -- | Inline source (e.g. @src_html[:foo bar]{\
    }@) + Src + -- | Language (e.g. @html@) + Text + -- | Parameters (e.g. @:foo bar@) + Text + -- | Value (e.g. @\
    @) + Text + | UnresolvedLink Text (k ObjIx) + | -- | Inline target (e.g. @\<\<\\>\>@) + Target + -- | Name + Text + | -- | Org inline macro (e.g. @{{{poem(red,blue)}}}@) + Macro + -- | Macro name (e.g. @"poem"@) + Text + -- | Arguments (e.g. @["red", "blue"]@) + [Text] + | -- | Statistic cookies. + StatisticCookie + -- | Either @[num1/num2]@ or @[percent%]@. + (Either (Int, Int) Int) + deriving (Typeable, Generic) + +deriving instance (AllOrgIx Show k) => Show (OrgObjectData k ix) +deriving instance (AllOrgIx Read k) => Read (OrgObjectData k ix) +deriving instance (AllOrgIx Eq k) => Eq (OrgObjectData k ix) +deriving instance (AllOrgIx Ord k) => Ord (OrgObjectData k ix) +deriving instance (AllOrgIx NFData k) => NFData (OrgObjectData k ix) + +-- | Data for a footnote reference. +data FootnoteRefData o + = -- | Label-only footnote reference (e.g. @[fn:foo]@) + FootnoteRefLabel + -- | Label (e.g. @foo@) + Text + | -- | Inline footnote definition (e.g. @[fn:foo::bar]@) + FootnoteRefDef + -- | Label (if present, e.g. @foo@) + (Maybe Text) + -- | Content (e.g. @bar@) + o + deriving (Show, Eq, Ord, Read, Typeable, Generic, Functor, Foldable, Traversable) + deriving anyclass (NFData) + +data QuoteType = SingleQuote | DoubleQuote + deriving (Eq, Ord, Read, Show, Typeable, Generic) + deriving anyclass (NFData) + +type Protocol = Text + +-- * Links + +data FragmentType + = RawFragment + | InlMathFragment + | DispMathFragment + deriving (Show, Eq, Ord, Read, Typeable, Generic) + deriving anyclass (NFData) + +data Citation o = Citation + { style :: Text + , variant :: Text + , prefix :: Maybe o + , suffix :: Maybe o + , references :: [CiteReference o] + } + deriving (Show, Eq, Ord, Read, Typeable, Generic, Functor, Foldable, Traversable) + deriving anyclass (NFData) + +data CiteReference o = CiteReference + { id :: Text + , prefix :: Maybe o + , suffix :: Maybe o + } + deriving (Show, Eq, Ord, Read, Typeable, Generic, Functor, Foldable, Traversable) + deriving anyclass (NFData) + +-- | Babel call +data BabelCall = BabelCall + { name :: Text + , header1 :: Text + , header2 :: Text + , arguments :: Text + } + deriving (Eq, Ord, Read, Show, Typeable, Generic) + deriving anyclass (NFData) + +$(deriveGenericK ''OrgObjectData) +deriving via (Generically OrgObjectData) instance (Endofunctor (~>) OrgObjectData) +deriving via (Generically OrgObjectData) instance (IFoldable OrgObjectData) +deriving via (Generically OrgObjectData) instance (ITraversable OrgObjectData) diff --git a/org-parser/src/Org/Types/Data/Section.hs b/org-parser/src/Org/Types/Data/Section.hs new file mode 100644 index 0000000..5cc38ee --- /dev/null +++ b/org-parser/src/Org/Types/Data/Section.hs @@ -0,0 +1,91 @@ +{-# LANGUAGE TemplateHaskell #-} + +module Org.Types.Data.Section + ( OrgSectionData (..) + , TodoKeyword (..) + , Properties + , TodoState (..) + , Tag + , Priority (..) + , PlanningInfo (..) + ) where + +import Control.Category.Endofunctor (Endofunctor) +import Control.Category.Natural (type (~>)) +import Data.Aeson qualified as Aeson +import Data.Ix.Foldable (IFoldable) +import Data.Ix.Instances +import Data.Ix.Traversable (ITraversable) +import Generics.Kind.TH (deriveGenericK) +import Org.Types.Aeson (aesonOptions) +import Org.Types.Data.Timestamp (TimestampData) +import Org.Types.Ix + +data OrgSectionData k (_i :: OrgIx) = OrgSectionData + { level :: Int + , properties :: Properties + , todo :: Maybe TodoKeyword + , comment :: Bool + , priority :: Maybe Priority + , title :: k ObjIx + , rawTitle :: Text + , tags :: [Tag] + , planning :: PlanningInfo + , children :: k ElmIx + , subsections :: k SecIx + } + deriving (Typeable, Generic) + +deriving instance (AllOrgIx Show k) => Show (OrgSectionData k ix) +deriving instance (AllOrgIx Read k) => Read (OrgSectionData k ix) +deriving instance (AllOrgIx Eq k) => Eq (OrgSectionData k ix) +deriving instance (AllOrgIx Ord k) => Ord (OrgSectionData k ix) +deriving instance (AllOrgIx NFData k) => NFData (OrgSectionData k ix) + +type Tag = Text + +-- | The states in which a todo item can be +data TodoState = Todo | Done + deriving (Eq, Ord, Show, Read, Typeable, Generic) + deriving anyclass (NFData) + +instance Aeson.ToJSON TodoState where + toJSON = Aeson.genericToJSON aesonOptions + toEncoding = Aeson.genericToEncoding aesonOptions + +instance Aeson.FromJSON TodoState where + parseJSON = Aeson.genericParseJSON aesonOptions + +-- | A to-do keyword like @TODO@ or @DONE@. +data TodoKeyword = TodoKeyword {state :: TodoState, name :: Text} + deriving (Eq, Ord, Read, Show, Typeable, Generic) + deriving anyclass (NFData) + +instance Aeson.ToJSON TodoKeyword where + toJSON = Aeson.genericToJSON aesonOptions + toEncoding = Aeson.genericToEncoding aesonOptions + +instance Aeson.FromJSON TodoKeyword where + parseJSON = Aeson.genericParseJSON aesonOptions + +data Priority + = LetterPriority Char + | NumericPriority Int + deriving (Eq, Ord, Read, Show, Typeable, Generic) + deriving anyclass (NFData) + +-- | Planning information for a subtree/headline. +data PlanningInfo = PlanningInfo + { closed :: Maybe TimestampData + , deadline :: Maybe TimestampData + , scheduled :: Maybe TimestampData + } + deriving (Show, Eq, Ord, Read, Typeable, Generic) + deriving anyclass (NFData) + +type Properties = Map Text Text + +$(deriveGenericK ''OrgSectionData) +deriving via (Generically OrgSectionData) instance (Endofunctor (~>) OrgSectionData) +deriving via (Generically OrgSectionData) instance (IFoldable OrgSectionData) +deriving via (Generically OrgSectionData) instance (ITraversable OrgSectionData) diff --git a/org-parser/src/Org/Types/Data/StandardProperties.hs b/org-parser/src/Org/Types/Data/StandardProperties.hs new file mode 100644 index 0000000..55b0d45 --- /dev/null +++ b/org-parser/src/Org/Types/Data/StandardProperties.hs @@ -0,0 +1,9 @@ +module Org.Types.Data.StandardProperties where + +data StandardProperties = StandardProperties + { begin :: !Int + , end :: !Int + , postBlank :: !Int + } + deriving (Eq, Ord, Read, Show, Typeable, Generic) + deriving anyclass (NFData) diff --git a/org-parser/src/Org/Types/Data/Timestamp.hs b/org-parser/src/Org/Types/Data/Timestamp.hs new file mode 100644 index 0000000..dd0fb5d --- /dev/null +++ b/org-parser/src/Org/Types/Data/Timestamp.hs @@ -0,0 +1,26 @@ +module Org.Types.Data.Timestamp + ( TimestampData (..) + , OrgDateTime + , TimestampMark + , OrgDate (..) + , OrgTime (..) + ) where + +-- | An Org timestamp, including repetition marks. +data TimestampData + = TimestampData {active :: Bool, time :: OrgDateTime} + | TimestampRange {active :: Bool, start :: OrgDateTime, end :: OrgDateTime} + deriving (Eq, Ord, Read, Show, Typeable, Generic) + deriving anyclass (NFData) + +data OrgDate = OrgDate {year :: Int, month :: Int, day :: Int, weekday :: Maybe Text} + deriving (Eq, Ord, Read, Show, Typeable, Generic) + deriving anyclass (NFData) + +data OrgTime = OrgTime {hour :: Int, minute :: Int} + deriving (Eq, Ord, Read, Show, Typeable, Generic) + deriving anyclass (NFData) + +type TimestampMark = (Text, Int, Char) + +type OrgDateTime = (OrgDate, Maybe OrgTime, Maybe TimestampMark, Maybe TimestampMark) diff --git a/org-parser/src/Org/Types/Ix.hs b/org-parser/src/Org/Types/Ix.hs new file mode 100644 index 0000000..61dbd7d --- /dev/null +++ b/org-parser/src/Org/Types/Ix.hs @@ -0,0 +1,56 @@ +{-# LANGUAGE TemplateHaskell #-} + +module Org.Types.Ix where + +import Control.Category.Natural (type (~~>)) +import Data.Type.Equality (type (:~:) (..)) + +data family S (a :: ix) + +class SingI ix where sing :: S ix + +data OrgIx = ObjIx | ElmIx | SecIx + +data instance S (a :: OrgIx) where + SObjIx :: S ObjIx + SElmIx :: S ElmIx + SSecIx :: S SecIx + +instance SingI ObjIx where sing = SObjIx +instance SingI ElmIx where sing = SElmIx +instance SingI SecIx where sing = SSecIx + +class TestIxEq (c :: k -> Type) where + testIxEq :: S ix -> c jx -> Maybe (ix :~: jx) + +-- instance (TestIxEq (c k), Foldable f, Functor f) => TestIxEq (ComposeIx f c k) where +-- testIxEq s (ComposeIx x) = msum (testIxEq s <$> x) + +mapIx :: + forall ix k s. + (SingI ix, TestIxEq k) => + (k ~~> s) -> + (k ix -> s ix) -> + k ~~> s +mapIx def f x = + case testIxEq (sing @ix) x of + Just Refl -> f x + Nothing -> def x + +getsIx :: + forall ix k b. + (SingI ix, TestIxEq k) => + (forall jx. k jx -> b) -> + (k ix -> b) -> + forall jx. + k jx -> + b +getsIx def f x = + case testIxEq (sing @ix) x of + Just Refl -> f x + Nothing -> def x + +type AllOrgIx :: (Type -> Constraint) -> (OrgIx -> Type) -> Constraint +type AllOrgIx c k = (c (k ObjIx), c (k ElmIx), c (k SecIx)) + +type (:+:) = Compose diff --git a/org-parser/src/Org/Types/Utils.hs b/org-parser/src/Org/Types/Utils.hs new file mode 100644 index 0000000..33d76f3 --- /dev/null +++ b/org-parser/src/Org/Types/Utils.hs @@ -0,0 +1,28 @@ +module Org.Types.Utils where + +import Data.Ix.Functor (IFunctor) +import Org.Types.Data.Document (OrgDocumentData (..)) +import Org.Types.Data.Section (OrgSectionData (..)) +import Org.Types.Variants.Plain (Org, OrgF, OrgIx (..), mapIx, secData) +import Org.Types.Walk (walk) +import Relude.Extra.Lens (over) + +-- * Sections + +isolateSection :: + (IFunctor f) => + OrgSectionData (Org f) SecIx -> + OrgDocumentData (Org f) ix +isolateSection section = + OrgDocumentData + { properties = section.properties + , children = section.children + , sections = walk (mapIx id (shiftSection (-section.level))) section.subsections + } + +shiftSection :: Int -> OrgF k SecIx -> OrgF k SecIx +shiftSection i = + over secData \sec@(OrgSectionData {level = level}) -> + if level + i > 0 + then sec {level = level + i} + else sec {level = 1} diff --git a/org-parser/src/Org/Types/Variants/Basic.hs b/org-parser/src/Org/Types/Variants/Basic.hs new file mode 100644 index 0000000..1b96669 --- /dev/null +++ b/org-parser/src/Org/Types/Variants/Basic.hs @@ -0,0 +1,84 @@ +{-# LANGUAGE TemplateHaskell #-} +-- | + +module Org.Types.Variants.Basic + ( OrgF (..) + , secData + , OrgNodes (..) + , Org + , pattern Org + + -- * Re-exports + , module Org.Types.Data.Document + , module Org.Types.Data.Section + , module Org.Types.Data.Element + , module Org.Types.Data.Object + , module Org.Types.Data.Timestamp + , module Org.Types.Ix + ) +where + +import Control.Category.Endofunctor (Endofunctor) +import Control.Category.Natural (type (~>)) +import Data.Ix.Foldable (IFoldable) +import Data.Ix.Instances +import Data.Ix.RecursionSchemes (Fix (..)) +import Data.Ix.Traversable (ITraversable) +import Data.Type.Equality ((:~:) (..)) +import Generics.Kind.TH (deriveGenericK) +import Org.Types.Data.Document +import Org.Types.Data.Element +import Org.Types.Data.Object +import Org.Types.Data.Section +import Org.Types.Data.Timestamp +import Org.Types.Ix +import Relude.Extra.Lens (Lens') +import Data.Ix.Functor (IFunctor) + +data OrgF k ix where + OrgObjectF :: OrgObjectData k ObjIx -> OrgF k ObjIx + OrgElementF :: Keywords (k ObjIx) -> OrgElementData k ElmIx -> OrgF k ElmIx + OrgSectionF :: OrgSectionData k SecIx -> OrgF k SecIx + +secData :: Lens' (OrgF k SecIx) (OrgSectionData k SecIx) +secData f (OrgSectionF d) = OrgSectionF <$> f d + +instance TestIxEq (OrgF k) where + testIxEq SObjIx OrgObjectF {} = Just Refl + testIxEq SElmIx OrgElementF {} = Just Refl + testIxEq SSecIx OrgSectionF {} = Just Refl + testIxEq _ _ = Nothing + +deriving instance (AllOrgIx Eq k) => (Eq (OrgF k a)) +deriving instance (AllOrgIx Ord k) => (Ord (OrgF k a)) +deriving instance (AllOrgIx Show k) => (Show (OrgF k a)) +instance (AllOrgIx NFData k) => NFData (OrgF k a) where + rnf (OrgObjectF x) = rnf x + rnf (OrgElementF x y) = rnf x `seq` rnf y + rnf (OrgSectionF x) = rnf x + +$(deriveGenericK ''OrgF) +deriving via (Generically OrgF) instance (Endofunctor (~>) OrgF) +deriving via (Generically OrgF) instance (IFoldable OrgF) +deriving via (Generically OrgF) instance (ITraversable OrgF) + +newtype OrgNodes f k i = OrgNodes (f (OrgF k) i) + deriving (Generic) + +deriving instance (Eq (f (OrgF k) i)) => Eq (OrgNodes f k i) +deriving instance (Ord (f (OrgF k) i)) => Ord (OrgNodes f k i) +deriving instance (Show (f (OrgF k) i)) => Show (OrgNodes f k i) +deriving newtype instance (NFData (f (OrgF k) i)) => NFData (OrgNodes f k i) +deriving newtype instance (Semigroup (f (OrgF k) i)) => Semigroup (OrgNodes f k i) +deriving newtype instance (Monoid (f (OrgF k) i)) => Monoid (OrgNodes f k i) + +$(deriveGenericK ''OrgNodes) +deriving via (Generically (OrgNodes f)) instance (IFunctor f) => (Endofunctor (~>) (OrgNodes f)) +deriving via (Generically (OrgNodes f)) instance (IFoldable f) => (IFoldable (OrgNodes f)) +deriving via (Generically (OrgNodes f)) instance (ITraversable f) => (ITraversable (OrgNodes f)) + +type Org f = Fix (OrgNodes f) +{-# COMPLETE Org #-} + +pattern Org :: f (OrgF (Org f)) ix -> Org f ix +pattern Org x = Fix (OrgNodes x) diff --git a/org-parser/src/Org/Types/Variants/ParseInfo.hs b/org-parser/src/Org/Types/Variants/ParseInfo.hs new file mode 100644 index 0000000..5db316d --- /dev/null +++ b/org-parser/src/Org/Types/Variants/ParseInfo.hs @@ -0,0 +1,98 @@ +{-# LANGUAGE RecordWildCards #-} + +module Org.Types.Variants.ParseInfo + ( PropW (..) + , OrgParsedF + , OrgParsed + , pattern OrgParsed + , OrgObjectD + , OrgObjects + , OrgElementD + , OrgElements + , OrgSectionD + , OrgSections + + -- * Constructors and patterns + , object + , pattern OrgObject + , element + , pattern OrgElement + , section + , pattern OrgSection + + -- * Re-exports + , module Org.Types.Variants.Basic + , module Org.Types.Data.StandardProperties + ) where + +import Data.Ix.RecursionSchemes (Fix (..)) +import Data.Sequence (ViewL (..), ViewR (..), singleton, viewl, viewr, (|>)) +import Org.Types.Data.StandardProperties +import Org.Types.Variants.Basic + +data PropW t = PropW {props :: !StandardProperties, get :: !t} + deriving (Eq, Ord, Show, Generic, Functor, Foldable, Traversable, NFData) + +type OrgParsedF k ix = PropW (OrgF k ix) + +type OrgParsed = Org (Compose (Seq :+: PropW)) + +pattern OrgParsed :: Seq (OrgParsedF OrgParsed i) -> OrgParsed i +pattern OrgParsed x = Org (Compose (Compose x)) +{-# COMPLETE OrgParsed #-} + +-- * Objects + +pattern OrgObject :: Int -> Int -> OrgObjectData k ObjIx -> OrgParsedF k ObjIx +pattern OrgObject {begin, end, datum} = PropW (StandardProperties {postBlank = 0, ..}) (OrgObjectF datum) + +object :: Int -> Int -> OrgObjectD -> OrgParsed ObjIx +object begin end datum = OrgParsed $ singleton $ OrgObject {..} + +type OrgObjectD = OrgObjectData OrgParsed ObjIx +type OrgObjects = OrgParsed ObjIx + +instance Semigroup OrgObjects where + (OrgParsed xs) <> (OrgParsed ys) = + OrgParsed $ + case (viewr xs, viewl ys) of + (EmptyR, _) -> ys + (_, EmptyL) -> xs + (xs' :> x'@(PropW posx x), y'@(PropW posy y) :< ys') -> meld <> ys' + where + merged = PropW (StandardProperties posx.begin posy.end 0) + meld = + case (x, y) of + (OrgObjectF (Plain t1), OrgObjectF (Plain t2)) + | posx.end == posy.begin -> + xs' |> merged (OrgObjectF (Plain (t1 <> t2))) + _other -> xs' |> x' |> y' + +instance Monoid OrgObjects where + mempty = coerce $ mempty @(Seq (OrgParsedF OrgParsed ObjIx)) + +-- * Elements + +pattern OrgElement :: Int -> Int -> Int -> Keywords (k ObjIx) -> OrgElementData k ElmIx -> OrgParsedF k ElmIx +pattern OrgElement {begin, end, postBlank, keywords, datum} = PropW (StandardProperties {..}) (OrgElementF keywords datum) + +element :: Int -> Int -> Int -> Keywords OrgObjects -> OrgElementD -> OrgElements +element begin end postBlank keywords datum = coerce $ singleton $ OrgElement {..} + +type OrgElementD = OrgElementData OrgParsed ElmIx +type OrgElements = OrgParsed ElmIx +deriving newtype instance Semigroup OrgElements +deriving newtype instance Monoid OrgElements + +-- * Sections + +pattern OrgSection :: Int -> Int -> OrgSectionData k SecIx -> OrgParsedF k SecIx +pattern OrgSection {begin, end, datum} = PropW (StandardProperties {postBlank = 0, ..}) (OrgSectionF datum) + +section :: Int -> Int -> OrgSectionD -> OrgSections +section begin end datum = coerce $ singleton $ OrgSection {..} + +type OrgSectionD = OrgSectionData OrgParsed SecIx +type OrgSections = OrgParsed SecIx +deriving newtype instance Semigroup OrgSections +deriving newtype instance Monoid OrgSections diff --git a/org-parser/src/Org/Types/Variants/Plain.hs b/org-parser/src/Org/Types/Variants/Plain.hs new file mode 100644 index 0000000..22b8442 --- /dev/null +++ b/org-parser/src/Org/Types/Variants/Plain.hs @@ -0,0 +1,28 @@ +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE UndecidableInstances #-} + +module Org.Types.Variants.Plain + ( OrgObjects + , OrgElements + , OrgSections + , OrgDocument + + -- * Constructors and patterns + , element + + -- * Re-exports + , module Org.Types.Variants.Basic + ) +where + +import Org.Types.Variants.Basic + +type OrgPlain = Org (Compose []) + +type OrgObjects = OrgPlain ObjIx +type OrgElements = OrgPlain ElmIx +type OrgSections = OrgPlain SecIx +type OrgDocument = OrgDocumentData OrgPlain ElmIx + +element :: [(Text, KeywordValue (k ObjIx))] -> OrgElementData k ElmIx -> OrgF k ElmIx +element aff = OrgElementF (fromList aff) diff --git a/org-parser/src/Org/Types/Walk.hs b/org-parser/src/Org/Types/Walk.hs new file mode 100644 index 0000000..c47527f --- /dev/null +++ b/org-parser/src/Org/Types/Walk.hs @@ -0,0 +1,50 @@ +-- | Some convenience functions built around the recusion-schemes API. +module Org.Types.Walk where + +import Control.Category.Natural (NProd (..), type (~>) (..), type (~~>)) +import Data.Ix.Foldable (IFoldable (..)) +import Data.Ix.Functor (IFunctor, ifmap) +import Data.Ix.RecursionSchemes qualified as R +import Org.Types.Variants.Basic +import Data.Ix.Traversable (ITraversable, itraverse) + +walk :: (IFunctor f) => (OrgF (Org f) ~~> OrgF (Org f)) -> Org f ~~> Org f +walk f = (R.hoist g #) + where + g = NT \(OrgNodes x) -> OrgNodes (ifmap f x) + +walkF :: forall f t. (ITraversable f, Applicative t) => (forall i. OrgF (Compose t (Org f)) i -> t (OrgF (Org f) i)) -> forall i. Org f i -> t (Org f i) +walkF f = getCompose . (R.transverse g #) + where + g :: OrgNodes f (Compose t (Org f)) ~> Compose t (OrgNodes f (Org f)) + g = NT \(OrgNodes x) -> Compose $ OrgNodes <$> itraverse f x + +query :: (IFoldable f, IFunctor f, Monoid m) => (forall jx. OrgF (Const m) jx -> m) -> Org f ix -> m +query f = getConst . (R.fold g #) + where + g = NT $ \(OrgNodes nodes) -> Const $ ifoldMap f nodes + +queryTopDown :: (IFoldable f, IFunctor f, Monoid m) => (forall jx. OrgF (Org f) jx -> m) -> Org f ix -> m +queryTopDown f = getConst . (R.para' g #) + where + g = NT $ \(Org original :.: child) -> Const $ ifoldMap f original <> ifold child + +queryBottomUp :: (IFoldable f, IFunctor f, Monoid m) => (forall jx. OrgF (Org f) jx -> m) -> Org f ix -> m +queryBottomUp f = getDual . queryTopDown (Dual . f) + +-- * Query + +-- queryTopDown :: forall k m a. (R.Recursive (~>) k, Monoid m, IFoldable (R.Base k), GeneralizeQuery k a) => (forall jx. k jx -> m) -> a -> m +-- queryTopDown f = generalizeQuery $ getConst . (R.para' (NT $ \(original :.: child) -> Const $ f original <> ifold child) #) + +-- queryBottomUp :: forall k m a. (R.Recursive (~>) k, Monoid m, IFoldable (R.Base k), GeneralizeQuery k a) => (forall jx. k jx -> m) -> a -> m +-- queryBottomUp f = generalizeQuery $ getConst . (R.para' (NT $ \(original :.: child) -> Const $ ifold child <> f original) #) + +-- class GeneralizeQuery k a where +-- generalizeQuery :: (Monoid m) => (forall jx. k jx -> m) -> a -> m + +-- instance GeneralizeQuery k (k ix) where +-- generalizeQuery f = f + +-- instance (IFoldable f) => GeneralizeQuery (OrgF (Org f)) (Org f ix) where +-- generalizeQuery f (Org x) = ifoldMap f x diff --git a/org-parser/src/Org/Types/Wrapping.hs b/org-parser/src/Org/Types/Wrapping.hs new file mode 100644 index 0000000..2f2753d --- /dev/null +++ b/org-parser/src/Org/Types/Wrapping.hs @@ -0,0 +1,32 @@ +{-# LANGUAGE FunctionalDependencies #-} +{-# LANGUAGE UndecidableInstances #-} + +-- | How to handle onions without tears. 🧅 +module Org.Types.Wrapping where +import Data.Ix.Traversable (ITraversable, itraverse) +import Data.Ix.RecursionSchemes (Fix (..)) +import Control.Category.Natural (type (~~>)) + +type ITraversal w t = forall f. Applicative f => (forall i. t i -> f (t i)) -> (forall i. w i -> f (w i)) + +-- | Like traversable but with specified types. +class Wrapper w t | w -> t where + wrapped :: ITraversal w t + +instance (ITraversable f) => Wrapper (f t) t where + wrapped = itraverse + +instance (ITraversable f) => Wrapper (Fix f) (f (Fix f)) where + wrapped f (Fix x) = Fix <$> f x + +class HasInside w t where + insideF :: ITraversal w t + +inside :: HasInside w t => (t ~~> t) -> w ~~> w +inside f = runIdentity . insideF (pure . f) + +instance {-# OVERLAPPABLE #-} HasInside t t where + insideF f = f + +instance (Wrapper w s, HasInside s t) => HasInside w t where + insideF f = wrapped $ insideF f diff --git a/org-parser/src/Org/Walk.hs b/org-parser/src/Org/Walk.hs deleted file mode 100644 index 94ab9d6..0000000 --- a/org-parser/src/Org/Walk.hs +++ /dev/null @@ -1,124 +0,0 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE UndecidableInstances #-} - -module Org.Walk - ( module Org.Walk - , MultiWalk - , (.>) - , (?>) - ) -where - -import Control.MultiWalk -import Org.Types - -data MWTag - -type Walk = Control.MultiWalk.Walk MWTag Identity - -type WalkM m = Control.MultiWalk.Walk MWTag m - -type Query m = Control.MultiWalk.Query MWTag m - -query :: (MultiWalk MWTag c, MultiWalk MWTag t, Monoid m) => (t -> m) -> c -> m -query = Control.MultiWalk.query @MWTag - -walkM :: (MultiWalk MWTag c, MultiWalk MWTag t, Monad m) => (t -> m t) -> c -> m c -walkM = Control.MultiWalk.walkM @MWTag - -walk :: (MultiWalk MWTag c, MultiWalk MWTag t) => (t -> t) -> c -> c -walk = Control.MultiWalk.walk @MWTag - -buildMultiQ :: - (MultiWalk MWTag a, Monoid m) => - (Org.Walk.Query m -> QList m (MultiTypes MWTag) -> QList m (MultiTypes MWTag)) -> - a -> - m -buildMultiQ = Control.MultiWalk.buildMultiQ @MWTag - -buildMultiW :: - (MultiWalk MWTag a, Applicative m) => - (Org.Walk.WalkM m -> FList m (MultiTypes MWTag) -> FList m (MultiTypes MWTag)) -> - a -> - m a -buildMultiW = Control.MultiWalk.buildMultiW @MWTag - -instance MultiTag MWTag where - type - MultiTypes MWTag = - '[ OrgDocument - , OrgSection - , -- Element stuff - OrgElement - , OrgElementData - , ListItem - , -- Object stuff - OrgObject - , Citation - ] - -type List a = Trav [] a - -type DoubleList a = MatchWith [[a]] (Trav (Compose [] []) a) - -instance MultiSub MWTag OrgDocument where - type SubTypes MWTag OrgDocument = 'SpecList '[ToSpec (List OrgElement), ToSpec (List OrgSection)] - -instance MultiSub MWTag OrgElement where - type - SubTypes MWTag OrgElement = - 'SpecList - '[ ToSpec OrgElementData - , ToSpec (Trav (Map Text) (Under KeywordValue 'NoSel (List OrgObject))) -- Objects under affiliated keywords - ] - -instance MultiSub MWTag OrgElementData where - type - SubTypes MWTag OrgElementData = - 'SpecList - '[ ToSpec (List OrgElement) - , ToSpec (List OrgObject) - , ToSpec (Under KeywordValue 'NoSel (List OrgObject)) -- Objects under keywords - , ToSpec (List ListItem) - , ToSpec (List (Under TableRow 'NoSel (DoubleList OrgObject))) -- Objects under table rows - , ToSpec (DoubleList OrgObject) -- Objects under verse blocks - ] - -instance MultiSub MWTag ListItem where - type - SubTypes MWTag ListItem = - 'SpecList - '[ ToSpec (List OrgObject) - , ToSpec (List OrgElement) - ] - -instance MultiSub MWTag OrgSection where - type - SubTypes MWTag OrgSection = - 'SpecList - '[ ToSpec (List OrgObject) - , ToSpec (List OrgElement) - , ToSpec (List OrgSection) - ] - -instance MultiSub MWTag OrgObject where - type - SubTypes MWTag OrgObject = - 'SpecList - '[ ToSpec (List OrgObject) - , ToSpec (Under FootnoteRefData 'NoSel (List OrgElement)) - , ToSpec Citation - ] - -instance MultiSub MWTag Citation where - type - SubTypes MWTag Citation = - 'SpecList - '[ ToSpec (List OrgObject) - , ToSpec (List (Under CiteReference 'NoSel (List OrgObject))) - ] diff --git a/org-parser/test/Tests/Document.hs b/org-parser/test/Tests/Document.hs index 72424e1..52e0f45 100644 --- a/org-parser/test/Tests/Document.hs +++ b/org-parser/test/Tests/Document.hs @@ -1,28 +1,24 @@ module Tests.Document where -import NeatInterpolation import Org.Parser.Document (propertyDrawer) import Tests.Helpers testDocument :: TestTree testDocument = - testGroup - "Document" - [ "Property drawer" ~: propertyDrawer $ - [ [text| :pRoPerTieS: - :Fo^o3': bar - :foobar: - :fooBARbar: bla bla - :ENd: - |] - =?> fromList - [ ("fo^o3'", "bar"), - ("foobar", ""), - ("foobarbar", "bla bla") - ], - [text|:properties: - :end: - |] - =?> mempty - ] + goldenGroup + "document" + [ "property drawer" + ~: propertyDrawer + $ [ unlines + [ " :pRoPerTieS:" + , ":Fo^o3': \t bar" + , " :foobar:" + , ":fooBARbar: bla bla" + , " :ENd:" + ] + , unlines + [ ":properties" + , ":end:" + ] + ] ] diff --git a/org-parser/test/Tests/Elements.hs b/org-parser/test/Tests/Elements.hs index 0e3bfe0..d9312d8 100644 --- a/org-parser/test/Tests/Elements.hs +++ b/org-parser/test/Tests/Elements.hs @@ -1,108 +1,61 @@ module Tests.Elements where import NeatInterpolation -import Org.Builder qualified as B import Org.Parser.Elements -import Org.Types import Tests.Helpers testElements :: TestTree testElements = - testGroup - "Elements" - [ "Clock" ~: clock $ + goldenGroup + "elements" + [ "clock" ~: clock $ [ "CLOCK: [2012-11-18 Sun 19:26]--[2012-11-18 Sun 19:33] => 0:07\n" - =?> let dt1 = ((2012, 11, 18, Just "Sun"), Just (19, 26), Nothing, Nothing) - dt2 = ((2012, 11, 18, Just "Sun"), Just (19, 33), Nothing, Nothing) - in B.clock (TimestampRange False dt1 dt2) (Just (0, 7)) ] - , "Clocks in context" ~: elements $ + , "clocks in context" ~: elements $ [ [text| foo CLOCK: [2012-11-18 Sun 19:26]--[2012-11-18 Sun 19:33] => 0:07 bar |] - =?> let dt1 = ((2012, 11, 18, Just "Sun"), Just (19, 26), Nothing, Nothing) - dt2 = ((2012, 11, 18, Just "Sun"), Just (19, 33), Nothing, Nothing) - in "foo" - <> B.element (B.clock (TimestampRange False dt1 dt2) (Just (0, 7))) - <> "bar" ] - , "Comment line" ~: commentLine $ - [ "# this is a comment" =?> Comment - , "#this line is not a comment" =!> () + , "comment line" ~: commentLine $ + [ "# this is a comment" + , "#this line is not a comment" ] - , "Paragraph" ~: elements $ + , "paragraph" ~: elements $ [ -- [text| foobar baz |] - =?> B.element (B.para ("foobar" <> "\n" <> "baz")) , [text| with /wrapped markup/ and markup *at end* =at start= but not~here~ and not _here_right. |] - =?> B.element - ( B.para - ( "with " - <> B.italic "wrapped\nmarkup" - <> " and markup " - <> B.bold "at end" - <> "\n" - <> B.verbatim "at start" - <> " but not~here~ and\nnot _here" - <> B.subscript (B.plain "right") - <> B.plain "." - ) - ) ] - , "Affiliated Keywords in Context" ~: elements $ + , "affiliated keywords in context" ~: elements $ [ -- [text| #+attr_html: :width 40px :foo bar:joined space :liz buuz Hi |] - =?> let kw = - BackendKeyword - [ ("width", "40px") - , ("foo", "bar:joined space") - , ("liz", "buuz") - ] - in B.element' [("attr_html", kw)] (B.para "Hi") , [text| Some para #+caption: hi /guys/ Hi |] - =?> foldMap - B.element - [ B.para "Some para" - , B.keyword "caption" (B.parsedKeyword $ "hi " <> B.italic "guys") - , B.para "Hi" - ] , [text| #+attr_html: :style color: red - foo |] - =?> let kw = BackendKeyword [("style", "color: red")] - in B.element' - [("attr_html", kw)] - ( B.list - (Unordered '-') - [B.listItemUnord '-' $ B.element $ B.para "foo"] - ) , [text| Some para #+caption: hi /guys/ Hi |] - =?> let kw = B.parsedKeyword ("hi " <> B.italic "guys") - in B.element (B.para "Some para") - <> B.element' [("caption", kw)] (B.para "Hi") , [text| #+attr_org: :foo bar #+begin_center @@ -111,43 +64,27 @@ testElements = #+end_center I don't have a caption |] - =?> let kw1 = BackendKeyword [("foo", "bar")] - kw2 = B.parsedKeyword ("hi " <> B.italic "guys") - in B.element' - [("attr_org", kw1)] - ( B.greaterBlock - Center - ( foldMap B.element [B.para "Some para", B.keyword "caption" kw2] - ) - ) - <> B.element (B.para "I don't have a caption") ] - , "Ordered Lists" ~: plainList $ + , "ordered lists" ~: plainList $ [ -- unlines [ "1. our" , "2. moment's" , "3. else's" ] - =?> B.orderedList OrderedNum '.' ["our", "moment's", "else's"] ] - , "Descriptive Lists" ~: plainList $ + , "descriptive lists" ~: plainList $ [ "- foo :: bar" - =?> B.descriptiveList [("foo", "bar")] , "- foo bar :: baz" - =?> B.descriptiveList [("foo bar", "baz")] - , "- :: ::" =?> B.descriptiveList [("::", mempty)] - , "- :: foo ::" =?> B.descriptiveList [(":: foo", mempty)] - , "- :: :: bar" =?> B.descriptiveList [("::", "bar")] - , "- :: :::" =?> B.list (Unordered '-') [B.listItemUnord '-' ":: :::"] + , "- :: ::" + , "- :: foo ::" + , "- :: :: bar" + , "- :: :::" , "- /foo/ :: bar" - =?> B.descriptiveList [(B.italic "foo", "bar")] , "- [[foo][bar]] :: bar" - =?> B.descriptiveList [(B.link (UnresolvedLink "foo") "bar", "bar")] , "- [[foo:prot.co][bar baz]] :: bla :: ble" - =?> B.descriptiveList [(B.link (URILink "foo" "prot.co") "bar baz", "bla :: ble")] ] - , "Lists in context" ~: elements $ + , "lists in context" ~: elements $ [ -- unlines [ "- foo bar" @@ -155,26 +92,11 @@ testElements = , " #+caption: foo" , "bla" ] - =?> B.element - ( B.list - (Unordered '-') - [ B.listItemUnord '-' $ - "foo bar" <> B.element (B.keyword "caption" $ B.parsedKeyword "foo") - ] - ) - <> "bla" , unlines [ "- foo bar" , "#+caption: foo" , " bla" ] - =?> B.element - ( B.list - (Unordered '-') - [ B.listItemUnord '-' "foo bar" - ] - ) - <> B.element' [("caption", B.parsedKeyword "foo")] (B.para "bla") , unlines [ "- " , " * " @@ -183,21 +105,6 @@ testElements = , " + " , "+" ] - =?> B.element - ( B.list - (Unordered '-') - [ B.listItemUnord '-' $ - B.element $ - B.list - (Unordered '*') - [ B.listItemUnord '*' mempty - , B.listItemUnord '-' "foo" - , B.listItemUnord '-' mempty - , B.listItemUnord '+' mempty - ] - , B.listItemUnord '+' mempty - ] - ) , unlines [ "- " , "" @@ -210,69 +117,34 @@ testElements = , "" , " - doo" ] - =?> B.element - ( B.list - (Unordered '-') - [ B.listItemUnord '-' mempty - , B.listItemUnord '-' "foo" - ] - ) - <> B.element - ( B.list - (Unordered '*') - [ B.listItemUnord '*' "bar" - , B.listItemUnord '*' mempty - ] - ) - <> B.element - ( B.list - (Unordered '-') - [ B.listItemUnord '-' "doo" - ] - ) , unlines [ " " , " 1. our" , " 2. moment's" , " 3. else's" ] - =?> B.element - ( B.orderedList - OrderedNum - '.' - (map (B.element . B.para) ["our", "moment's", "else's"]) - ) ] - , "Greater Blocks" ~: greaterBlock $ + , "greater blocks" ~: greaterBlock $ [ -- unlines [ "#+begin_fun" , " " , "#+end_fun" ] - =?> B.greaterBlock (Special "fun") mempty ] - , "Fixed width" ~: fixedWidth $ + , "fixed width" ~: fixedWidth $ [ -- [text| : fooblabla boo : foooo : booo |] - =?> B.example - mempty - [ SrcLine " fooblabla boo" - , SrcLine "foooo" - , SrcLine " booo" - ] ] - , "Horizontal Rules" ~: horizontalRule $ + , "horizontal rules" ~: horizontalRule $ [ "---------------- " - =?> B.horizontalRule , "-- " - =!> () ] - , "Tables" ~: table $ + , "tables" ~: table $ [ -- [text| | foo | bar | baz | @@ -282,19 +154,11 @@ testElements = | | foo /bar/ | *ba* | baz | foo || bar | | |] - =?> B.table - [ B.standardRow ["foo", "bar", "baz"] - , B.standardRow ["foo bar", "baz"] - , RuleRow - , ColumnPropsRow [Just AlignRight, Nothing, Just AlignLeft, Just AlignCenter] - , B.standardRow ["", "foo " <> B.italic "bar", B.bold "ba", "baz"] - , B.standardRow ["foo", mempty, "bar", mempty] - ] ] - , "Tricky whitespace" ~: elements $ - [ "\n " =?> mempty - , "" =?> mempty - , "\n" =?> mempty - , "\n\n a" =?> B.element (B.para "a") + , "tricky whitespace" ~: elements $ + [ "\n " + , "" + , "\n" + , "\n\n a" ] ] diff --git a/org-parser/test/Tests/Helpers.hs b/org-parser/test/Tests/Helpers.hs index c78f509..d133215 100644 --- a/org-parser/test/Tests/Helpers.hs +++ b/org-parser/test/Tests/Helpers.hs @@ -10,13 +10,18 @@ module Tests.Helpers ) where +import Data.Ix.RecursionSchemes (Fix (..)) import Data.TreeDiff -import Org.Builder (Many) +import Data.TreeDiff.Golden (ediffGolden) +import Data.TreeDiff.OMap qualified as OM import Org.Parser import Org.Parser.Objects (Marked (..)) -import Org.Types +import Org.Types.Variants.ParseInfo +import Org.Types.Variants.Plain qualified as P +import System.FilePath ((<.>), ()) import Test.Tasty -import Test.Tasty.HUnit +import Test.Tasty.Golden.Advanced +import Test.Tasty.Options import Text.Megaparsec (eof) import Text.Megaparsec.Error (errorBundlePretty) @@ -31,33 +36,13 @@ instance Parsable OrgParser where parse' p = parseOrg defaultOrgOptions (p <* eof) "" instance Parsable (Marked OrgParser) where - parse' p = parse' (getParser p) + parse' p = parse' p.parser -instance PrettyFormable Properties where - type PrettyForm Properties = Properties - prettyForm = id - -instance PrettyFormable OrgDocument where - type PrettyForm OrgDocument = OrgDocument - prettyForm = id - -class PrettyFormable a where - type PrettyForm a - prettyForm :: a -> PrettyForm a - -instance PrettyFormable (Many a) where - type PrettyForm (Many a) = [a] - prettyForm = toList - -instance PrettyFormable OrgElementData where - type PrettyForm OrgElementData = OrgElementData - prettyForm = id - -prettyParse :: (Parsable m, PrettyFormable a, ToExpr (PrettyForm a)) => m a -> Text -> IO () +prettyParse :: (Parsable m, ToExpr a) => m a -> Text -> IO () prettyParse parser txt = case parse' parser txt of Left e -> putStrLn $ errorBundlePretty e - Right x -> print $ ansiWlBgExpr $ toExpr $ prettyForm x + Right x -> print $ ansiWlBgExpr $ toExpr x infix 1 =?> @@ -69,65 +54,71 @@ infix 1 =!> (=!>) :: a -> () -> (a, Either () c) x =!> y = (x, Left y) -infix 4 =: - -(=:) :: (Eq a, Show a) => TestName -> (a, a) -> TestTree -(=:) name (x, y) = testCase name (x @?= y) - -infix 4 ~: - (~:) :: - HasCallStack => - (Parsable m, PrettyFormable a, Eq a, ToExpr (PrettyForm a)) => + (HasCallStack) => + (Parsable m, Eq a, ToExpr a) => TestName -> m a -> - [(Text, Either () a)] -> + [Text] -> TestTree (~:) name parser cases = - testGroup name $ - flip (`zipWith` [1 ..]) cases $ \(i :: Int) (txt, ref) -> - testCase (name <> " " <> show i) $ - case parse' parser txt of - Left e - | isRight ref -> assertFailure $ errorBundlePretty e - | otherwise -> pure () - Right got - | Right reference <- ref -> - unless (got == reference) $ - assertFailure (diffExpr got reference) - | otherwise -> - assertFailure $ - "Should not parse, but parsed as:\n" <> renderExpr got + testGroup name + $ flip (`zipWith` [1 ..]) cases + $ \(i :: Int) txt -> + askOption \(GoldenPath outDir) -> + ediffGolden goldenTest (name <> " " <> show i) (outDir name <.> show i) + $ return + $ first errorBundlePretty + $ parse' parser txt + +newtype GoldenPath = GoldenPath String + +instance IsOption GoldenPath where + defaultValue = GoldenPath "test/files/golden" + parseValue = Just . GoldenPath + optionName = "golden-path" + optionHelp = "Path to golden test files." + +goldenGroup :: TestName -> [TestTree] -> TestTree +goldenGroup tn = adjustOption f . testGroup tn where - renderExpr :: (PrettyFormable a, ToExpr (PrettyForm a)) => a -> String - renderExpr = show . ansiWlBgExpr . toExpr . prettyForm - diffExpr :: (PrettyFormable a, ToExpr (PrettyForm a)) => a -> a -> String - diffExpr a b = show $ ansiWlBgEditExpr $ ediff (toExpr $ prettyForm a) (toExpr $ prettyForm b) + f (GoldenPath p) = GoldenPath (p tn) deriving instance (ToExpr OrgDocument) -deriving instance (ToExpr KeywordValue) -deriving instance (ToExpr OrgObject) +deriving instance (ToExpr (KeywordValue OrgObjects)) deriving instance (ToExpr QuoteType) deriving instance (ToExpr TimestampData) +deriving instance (ToExpr OrgDate) +deriving instance (ToExpr OrgTime) deriving instance (ToExpr FragmentType) -deriving instance (ToExpr FootnoteRefData) -deriving instance (ToExpr Citation) -deriving instance (ToExpr CiteReference) +deriving instance (ToExpr (FootnoteRefData OrgObjects)) +deriving instance (ToExpr (Citation OrgObjects)) +deriving instance (ToExpr (CiteReference OrgObjects)) deriving instance (ToExpr BabelCall) deriving instance (ToExpr LinkTarget) -deriving instance (ToExpr OrgElement) -deriving instance (ToExpr OrgElementData) +deriving instance (ToExpr (OrgElementData Org ix)) +deriving instance (ToExpr (OrgObjectData Org ix)) +deriving instance (ToExpr (OrgSectionData Org ix)) deriving instance (ToExpr GreaterBlockType) deriving instance (ToExpr ListType) deriving instance (ToExpr OrderedStyle) -deriving instance (ToExpr ListItem) +deriving instance (ToExpr (ListItem Org ix)) deriving instance (ToExpr Bullet) deriving instance (ToExpr Checkbox) -deriving instance (ToExpr SrcLine) -deriving instance (ToExpr TableRow) +deriving instance (ToExpr (TableRow OrgObjects)) deriving instance (ToExpr ColumnAlignment) -deriving instance (ToExpr OrgSection) +deriving instance (ToExpr (OrgF Org ix)) +deriving instance (ToExpr StandardProperties) deriving instance (ToExpr TodoKeyword) deriving instance (ToExpr TodoState) deriving instance (ToExpr Priority) deriving instance (ToExpr PlanningInfo) + +instance ToExpr (P.OrgF Org ix) where + toExpr = \case + P.OrgObjectF d -> toExpr d + P.OrgElementF a d -> Rec "OrgElement" (OM.fromList [("affiliated", toExpr a), ("data", toExpr d)]) + P.OrgSectionF d -> toExpr d + +instance ToExpr (Org ix) where + toExpr (Fix (ComposeIx x)) = Lst $ map toExpr $ toList x diff --git a/org-parser/test/Tests/Objects.hs b/org-parser/test/Tests/Objects.hs index b6d4419..3d62007 100644 --- a/org-parser/test/Tests/Objects.hs +++ b/org-parser/test/Tests/Objects.hs @@ -1,103 +1,78 @@ module Tests.Objects where -import Org.Builder qualified as B import Org.Parser.Objects -import Org.Types import Tests.Helpers testObjects :: TestTree testObjects = - testGroup - "Objects" - [ "Timestamp" ~: timestamp $ + goldenGroup + "objects" + [ "timestamp" ~: timestamp $ [ "<1997-11-03 Mon 19:15>" - =?> B.timestamp - (TimestampData True ((1997, 11, 3, Just "Mon"), Just (19, 15), Nothing, Nothing)) , "[2020-03-04 20:20]" - =?> B.timestamp - (TimestampData False ((2020, 03, 04, Nothing), Just (20, 20), Nothing, Nothing)) , "[2020-03-04 0:20]" - =?> B.timestamp - (TimestampData False ((2020, 03, 04, Nothing), Just (0, 20), Nothing, Nothing)) ] - , "Citations" ~: citation $ + , "citations" ~: citation $ [ "[cite:/foo/;/bar/@bef=bof=;/baz/]" - =?> let ref = - CiteReference - { refId = "bef" - , refPrefix = [Italic [Plain "bar"]] - , refSuffix = [Verbatim "bof"] - } - in B.citation - Citation - { citationStyle = "" - , citationVariant = "" - , citationPrefix = [Italic [Plain "foo"]] - , citationSuffix = [Italic [Plain "baz"]] - , citationReferences = [ref] - } ] - , "Targets" ~: target $ - [ "<>" =?> B.target "" "this is a target" - , "<< not a target>>" =!> () - , "<>" =!> () - , "<>" =!> () - , "<>" =!> () - , "< is not a target>>" =!> () + , "targets" ~: target $ + [ "<>" + , "<< not a target>>" + , "<>" + , "<>" + , "<>" + , "< is not a target>>" ] - , "Math fragment" ~: latexFragment $ - [ "\\(\\LaTeX + 2\\)" =?> B.inlMath "\\LaTeX + 2" - , "\\[\\LaTeX + 2\\]" =?> B.dispMath "\\LaTeX + 2" + , "math fragment" ~: latexFragment $ + [ "\\(\\LaTeX + 2\\)" + , "\\[\\LaTeX + 2\\]" ] - , "TeX Math Fragments" ~: plainMarkupContext texMathFragment $ - [ "$e = mc^2$" =?> B.inlMath "e = mc^2" - , "$$foo bar$" =?> "$$foo bar$" - , "$foo bar$a" =?> "$foo bar$a" - , "($foo bar$)" =?> "(" <> B.inlMath "foo bar" <> ")" - , "This is $1 buck, not math ($1! so cheap!)" =?> "This is $1 buck, not math ($1! so cheap!)" - , "two$$always means$$math" =?> "two" <> B.dispMath "always means" <> "math" + , "tex math fragments" ~: plainMarkupContext (mapMarkedP withPos texMathFragment) $ + [ "$e = mc^2$" + , "$$foo bar$" + , "$foo bar$a" + , "($foo bar$)" + , "This is $1 buck, not math ($1! so cheap!)" + , "two$$always means$$math" ] - , "Subscripts and superscripts" ~: plainMarkupContext suscript $ - [ "not a _suscript" =?> "not a _suscript" - , "not_{{suscript}" =?> "not_{{suscript}" - , "a_{balanced^{crazy} ok}" =?> "a" <> B.subscript ("balanced" <> B.superscript "crazy" <> " ok") - , "a_{balanced {suscript} ok}" =?> "a" <> B.subscript "balanced {suscript} ok" - , "a_{bala\nnced {sus\ncript} ok}" =?> "a" <> B.subscript "bala\nnced {sus\ncript} ok" - , "a^+strange,suscript," =?> "a" <> B.superscript "+strange,suscript" <> "," - , "a^*suspicious suscript" =?> "a" <> B.superscript "*" <> "suspicious suscript" - , "a_bad,.,.,maleficent, one" =?> "a" <> B.subscript "bad,.,.,maleficent" <> ", one" - , "a_some\\LaTeX" =?> "a" <> B.subscript ("some" <> B.fragment "\\LaTeX") + , "subscripts and superscripts" ~: plainMarkupContext (mapMarkedP withPos suscript) $ + [ "not a _suscript" + , "not_{{suscript}" + , "a_{balanced^{crazy} ok}" + , "a_{balanced {suscript} ok}" + , "a_{bala\nnced {sus\ncript} ok}" + , "a^+strange,suscript," + , "a^*suspicious suscript" + , "a_bad,.,.,maleficent, one" + , "a_some\\LaTeX" ] - , "Line breaks" ~: plainMarkupContext linebreak $ + , "line breaks" ~: plainMarkupContext (mapMarkedP withPos linebreak) $ [ "this is a \\\\ \t\n\ \line break" - =?> "this is a " - <> B.linebreak - <> "line break" - , "also linebreak \\\\" =?> "also linebreak " <> B.linebreak + , "also linebreak \\\\" ] - , "Image or links" ~: regularLink $ - [ "[[http://blablebli.com]]" =?> B.link (URILink "http" "//blablebli.com") mempty - , "[[http://blablebli.com][/uh/ duh! *foo*]]" =?> B.link (URILink "http" "//blablebli.com") (B.italic "uh" <> " duh! " <> B.bold "foo") + , "image or links" ~: regularLink $ + [ "[[http://blablebli.com]]" + , "[[http://blablebli.com][/uh/ duh! *foo*]]" ] - , "Statistic Cookies" ~: statisticCookie $ - [ "[13/18]" =?> B.statisticCookie (Left (13, 18)) - , "[33%]" =?> B.statisticCookie (Right 33) + , "statistic cookies" ~: statisticCookie $ + [ "[13/18]" + , "[33%]" ] - , "Footnote references" ~: footnoteReference $ - [ "[fn::simple]" =?> B.footnoteInlDef Nothing "simple" - , "[fn::s[imple]" =!> () - , "[fn:mydef:s[imp]le]" =?> B.footnoteInlDef (Just "mydef") "s[imp]le" + , "footnote references" ~: footnoteReference $ + [ "[fn::simple]" + , "[fn::s[imple]" + , "[fn:mydef:s[imp]le]" ] - , "Macros" ~: macro $ - [ "{{{fooo()}}}" =?> B.macro "fooo" [""] - , "{{{função()}}}" =!> () - , "{{{2fun()}}}" =!> () - , "{{{fun-2_3(bar,(bar,baz){a})}}}" =?> B.macro "fun-2_3" ["bar", "(bar", "baz){a}"] + , "macros" ~: macro $ + [ "{{{fooo()}}}" + , "{{{função()}}}" + , "{{{2fun()}}}" + , "{{{fun-2_3(bar,(bar,baz){a})}}}" ] - , "Italic" ~: italic $ - [ "// foo/" =?> B.italic "/ foo" - , "/foo //" =?> B.italic "foo /" - , "/foo / f/" =?> B.italic "foo / f" + , "italic" ~: italic $ + [ "// foo/" + , "/foo //" + , "/foo / f/" ] ] diff --git a/org-parser/test/files/golden/document/property drawer.1 b/org-parser/test/files/golden/document/property drawer.1 new file mode 100644 index 0000000..f7fc297 --- /dev/null +++ b/org-parser/test/files/golden/document/property drawer.1 @@ -0,0 +1,6 @@ +Right + (Map.fromList + [ + _×_ "fo^o3'" "bar", + _×_ "foobar" "", + _×_ "foobarbar" "bla bla"]) diff --git a/org-parser/test/files/golden/document/property drawer.2 b/org-parser/test/files/golden/document/property drawer.2 new file mode 100644 index 0000000..d84a103 --- /dev/null +++ b/org-parser/test/files/golden/document/property drawer.2 @@ -0,0 +1,9 @@ +Left + (concat + [ + "1:1:\n", + " |\n", + "1 | :properties\n", + " | ^^^^^^^^^^^^\n", + "unexpected \":properties\"\n", + "expecting \":properties:\" or spaces or tabs\n"]) diff --git a/org-parser/test/files/golden/elements/affiliated keywords in context.1 b/org-parser/test/files/golden/elements/affiliated keywords in context.1 new file mode 100644 index 0000000..c9b0538 --- /dev/null +++ b/org-parser/test/files/golden/elements/affiliated keywords in context.1 @@ -0,0 +1,25 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 59, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList + [ + _×_ + "attr_html" + (BackendKeyword + [ + _×_ "width" "40px", + _×_ "foo" "bar:joined space", + _×_ "liz" "buuz"])], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 57, + end = 59, + postBlank = 0}, + datum = Plain "Hi"}]}}] diff --git a/org-parser/test/files/golden/elements/affiliated keywords in context.2 b/org-parser/test/files/golden/elements/affiliated keywords in context.2 new file mode 100644 index 0000000..4a1a1db --- /dev/null +++ b/org-parser/test/files/golden/elements/affiliated keywords in context.2 @@ -0,0 +1,56 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 10, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 9, + postBlank = 0}, + datum = Plain "Some para"}]}}, + OrgF { + props = StandardProperties { + begin = 10, + end = 34, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList + [ + _×_ + "caption" + (ParsedKeyword + [ + OrgF { + props = StandardProperties { + begin = 31, + end = 34, + postBlank = 0}, + datum = Plain "hi "}, + OrgF { + props = StandardProperties { + begin = 34, + end = 40, + postBlank = 0}, + datum = Italic + [ + OrgF { + props = StandardProperties { + begin = 35, + end = 39, + postBlank = 0}, + datum = Plain "guys"}]}])], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 32, + end = 34, + postBlank = 0}, + datum = Plain "Hi"}]}}] diff --git a/org-parser/test/files/golden/elements/affiliated keywords in context.3 b/org-parser/test/files/golden/elements/affiliated keywords in context.3 new file mode 100644 index 0000000..20b8b37 --- /dev/null +++ b/org-parser/test/files/golden/elements/affiliated keywords in context.3 @@ -0,0 +1,38 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 38, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList + [ + _×_ + "attr_html" + (BackendKeyword + [_×_ "style" "color: red"])], + data = PlainList + (Unordered '-') + [ + ListItem { + bullet = Bullet '-', + counter = Nothing, + checkbox = Nothing, + tag = Nothing, + content = [ + OrgF { + props = StandardProperties { + begin = 35, + end = 38, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 35, + end = 38, + postBlank = 0}, + datum = Plain "foo"}]}}]}]}}] diff --git a/org-parser/test/files/golden/elements/affiliated keywords in context.4 b/org-parser/test/files/golden/elements/affiliated keywords in context.4 new file mode 100644 index 0000000..0ef858a --- /dev/null +++ b/org-parser/test/files/golden/elements/affiliated keywords in context.4 @@ -0,0 +1,56 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 10, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 9, + postBlank = 0}, + datum = Plain "Some para"}]}}, + OrgF { + props = StandardProperties { + begin = 10, + end = 33, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList + [ + _×_ + "caption" + (ParsedKeyword + [ + OrgF { + props = StandardProperties { + begin = 31, + end = 34, + postBlank = 0}, + datum = Plain "hi "}, + OrgF { + props = StandardProperties { + begin = 34, + end = 40, + postBlank = 0}, + datum = Italic + [ + OrgF { + props = StandardProperties { + begin = 35, + end = 39, + postBlank = 0}, + datum = Plain "guys"}]}])], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 31, + end = 33, + postBlank = 0}, + datum = Plain "Hi"}]}}] diff --git a/org-parser/test/files/golden/elements/affiliated keywords in context.5 b/org-parser/test/files/golden/elements/affiliated keywords in context.5 new file mode 100644 index 0000000..bdac0df --- /dev/null +++ b/org-parser/test/files/golden/elements/affiliated keywords in context.5 @@ -0,0 +1,78 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 80, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList + [ + _×_ + "attr_org" + (BackendKeyword + [_×_ "foo" "bar"])], + data = GreaterBlock + Center + [ + OrgF { + props = StandardProperties { + begin = 36, + end = 46, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 36, + end = 45, + postBlank = 0}, + datum = Plain "Some para"}]}}, + OrgF { + props = StandardProperties { + begin = 46, + end = 67, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Keyword + "caption" + (ParsedKeyword + [ + OrgF { + props = StandardProperties { + begin = 67, + end = 70, + postBlank = 0}, + datum = Plain "hi "}, + OrgF { + props = StandardProperties { + begin = 70, + end = 76, + postBlank = 0}, + datum = Italic + [ + OrgF { + props = StandardProperties { + begin = 71, + end = 75, + postBlank = 0}, + datum = Plain "guys"}]}])}}]}}, + OrgF { + props = StandardProperties { + begin = 80, + end = 102, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 80, + end = 102, + postBlank = 0}, + datum = Plain + "I don't have a caption"}]}}] diff --git a/org-parser/test/files/golden/elements/clock.1 b/org-parser/test/files/golden/elements/clock.1 new file mode 100644 index 0000000..3a592b1 --- /dev/null +++ b/org-parser/test/files/golden/elements/clock.1 @@ -0,0 +1,30 @@ +Right + (Clock + TimestampRange { + active = False, + start = _×_×_×_ + OrgDate { + year = 2012, + month = 11, + day = 18, + weekday = Just "Sun"} + (Just + OrgTime { + hour = 19, + minute = 26}) + Nothing + Nothing, + end = _×_×_×_ + OrgDate { + year = 2012, + month = 11, + day = 18, + weekday = Just "Sun"} + (Just + OrgTime { + hour = 19, + minute = 33}) + Nothing + Nothing} + (Just + OrgTime {hour = 0, minute = 7})) diff --git a/org-parser/test/files/golden/elements/clocks in context.1 b/org-parser/test/files/golden/elements/clocks in context.1 new file mode 100644 index 0000000..84c96f7 --- /dev/null +++ b/org-parser/test/files/golden/elements/clocks in context.1 @@ -0,0 +1,70 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 4, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 3, + postBlank = 0}, + datum = Plain "foo"}]}}, + OrgF { + props = StandardProperties { + begin = 4, + end = 67, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Clock + TimestampRange { + active = False, + start = _×_×_×_ + OrgDate { + year = 2012, + month = 11, + day = 18, + weekday = Just "Sun"} + (Just + OrgTime { + hour = 19, + minute = 26}) + Nothing + Nothing, + end = _×_×_×_ + OrgDate { + year = 2012, + month = 11, + day = 18, + weekday = Just "Sun"} + (Just + OrgTime { + hour = 19, + minute = 33}) + Nothing + Nothing} + (Just + OrgTime { + hour = 0, + minute = 7})}}, + OrgF { + props = StandardProperties { + begin = 67, + end = 70, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 67, + end = 70, + postBlank = 0}, + datum = Plain "bar"}]}}] diff --git a/org-parser/test/files/golden/elements/comment line.1 b/org-parser/test/files/golden/elements/comment line.1 new file mode 100644 index 0000000..6c4a43d --- /dev/null +++ b/org-parser/test/files/golden/elements/comment line.1 @@ -0,0 +1 @@ +Right Comment diff --git a/org-parser/test/files/golden/elements/comment line.2 b/org-parser/test/files/golden/elements/comment line.2 new file mode 100644 index 0000000..ea5e30b --- /dev/null +++ b/org-parser/test/files/golden/elements/comment line.2 @@ -0,0 +1,8 @@ +Left + (concat + [ + "1:2:\n", + " |\n", + "1 | #this line is not a comment\n", + " | ^\n", + "If this was meant as a comment, a space is missing here.\n"]) diff --git a/org-parser/test/files/golden/elements/descriptive lists.1 b/org-parser/test/files/golden/elements/descriptive lists.1 new file mode 100644 index 0000000..262e28b --- /dev/null +++ b/org-parser/test/files/golden/elements/descriptive lists.1 @@ -0,0 +1,32 @@ +Right + (PlainList + Descriptive + [ + ListItem { + bullet = Bullet '-', + counter = Nothing, + checkbox = Nothing, + tag = Just + [ + OrgF { + props = StandardProperties { + begin = 2, + end = 5, + postBlank = 0}, + datum = Plain "foo"}], + content = [ + OrgF { + props = StandardProperties { + begin = 11, + end = 14, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 11, + end = 14, + postBlank = 0}, + datum = Plain "bar"}]}}]}]) diff --git a/org-parser/test/files/golden/elements/descriptive lists.2 b/org-parser/test/files/golden/elements/descriptive lists.2 new file mode 100644 index 0000000..c24b429 --- /dev/null +++ b/org-parser/test/files/golden/elements/descriptive lists.2 @@ -0,0 +1,32 @@ +Right + (PlainList + Descriptive + [ + ListItem { + bullet = Bullet '-', + counter = Nothing, + checkbox = Nothing, + tag = Just + [ + OrgF { + props = StandardProperties { + begin = 2, + end = 9, + postBlank = 0}, + datum = Plain "foo bar"}], + content = [ + OrgF { + props = StandardProperties { + begin = 14, + end = 17, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 14, + end = 17, + postBlank = 0}, + datum = Plain "baz"}]}}]}]) diff --git a/org-parser/test/files/golden/elements/descriptive lists.3 b/org-parser/test/files/golden/elements/descriptive lists.3 new file mode 100644 index 0000000..8c07693 --- /dev/null +++ b/org-parser/test/files/golden/elements/descriptive lists.3 @@ -0,0 +1,17 @@ +Right + (PlainList + Descriptive + [ + ListItem { + bullet = Bullet '-', + counter = Nothing, + checkbox = Nothing, + tag = Just + [ + OrgF { + props = StandardProperties { + begin = 4, + end = 6, + postBlank = 0}, + datum = Plain "::"}], + content = []}]) diff --git a/org-parser/test/files/golden/elements/descriptive lists.4 b/org-parser/test/files/golden/elements/descriptive lists.4 new file mode 100644 index 0000000..042f7c2 --- /dev/null +++ b/org-parser/test/files/golden/elements/descriptive lists.4 @@ -0,0 +1,17 @@ +Right + (PlainList + Descriptive + [ + ListItem { + bullet = Bullet '-', + counter = Nothing, + checkbox = Nothing, + tag = Just + [ + OrgF { + props = StandardProperties { + begin = 4, + end = 10, + postBlank = 0}, + datum = Plain ":: foo"}], + content = []}]) diff --git a/org-parser/test/files/golden/elements/descriptive lists.5 b/org-parser/test/files/golden/elements/descriptive lists.5 new file mode 100644 index 0000000..88b1d21 --- /dev/null +++ b/org-parser/test/files/golden/elements/descriptive lists.5 @@ -0,0 +1,32 @@ +Right + (PlainList + Descriptive + [ + ListItem { + bullet = Bullet '-', + counter = Nothing, + checkbox = Nothing, + tag = Just + [ + OrgF { + props = StandardProperties { + begin = 4, + end = 6, + postBlank = 0}, + datum = Plain "::"}], + content = [ + OrgF { + props = StandardProperties { + begin = 10, + end = 13, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 10, + end = 13, + postBlank = 0}, + datum = Plain "bar"}]}}]}]) diff --git a/org-parser/test/files/golden/elements/descriptive lists.6 b/org-parser/test/files/golden/elements/descriptive lists.6 new file mode 100644 index 0000000..d65bcb4 --- /dev/null +++ b/org-parser/test/files/golden/elements/descriptive lists.6 @@ -0,0 +1,25 @@ +Right + (PlainList + (Unordered '-') + [ + ListItem { + bullet = Bullet '-', + counter = Nothing, + checkbox = Nothing, + tag = Nothing, + content = [ + OrgF { + props = StandardProperties { + begin = 3, + end = 10, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 3, + end = 10, + postBlank = 0}, + datum = Plain ":: :::"}]}}]}]) diff --git a/org-parser/test/files/golden/elements/descriptive lists.7 b/org-parser/test/files/golden/elements/descriptive lists.7 new file mode 100644 index 0000000..66d7541 --- /dev/null +++ b/org-parser/test/files/golden/elements/descriptive lists.7 @@ -0,0 +1,39 @@ +Right + (PlainList + Descriptive + [ + ListItem { + bullet = Bullet '-', + counter = Nothing, + checkbox = Nothing, + tag = Just + [ + OrgF { + props = StandardProperties { + begin = 2, + end = 7, + postBlank = 0}, + datum = Italic + [ + OrgF { + props = StandardProperties { + begin = 3, + end = 6, + postBlank = 0}, + datum = Plain "foo"}]}], + content = [ + OrgF { + props = StandardProperties { + begin = 11, + end = 14, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 11, + end = 14, + postBlank = 0}, + datum = Plain "bar"}]}}]}]) diff --git a/org-parser/test/files/golden/elements/descriptive lists.8 b/org-parser/test/files/golden/elements/descriptive lists.8 new file mode 100644 index 0000000..ffada61 --- /dev/null +++ b/org-parser/test/files/golden/elements/descriptive lists.8 @@ -0,0 +1,40 @@ +Right + (PlainList + Descriptive + [ + ListItem { + bullet = Bullet '-', + counter = Nothing, + checkbox = Nothing, + tag = Just + [ + OrgF { + props = StandardProperties { + begin = 2, + end = 14, + postBlank = 0}, + datum = Link + (UnresolvedLink "foo") + [ + OrgF { + props = StandardProperties { + begin = 9, + end = 12, + postBlank = 0}, + datum = Plain "bar"}]}], + content = [ + OrgF { + props = StandardProperties { + begin = 18, + end = 21, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 18, + end = 21, + postBlank = 0}, + datum = Plain "bar"}]}}]}]) diff --git a/org-parser/test/files/golden/elements/descriptive lists.9 b/org-parser/test/files/golden/elements/descriptive lists.9 new file mode 100644 index 0000000..a79a1d5 --- /dev/null +++ b/org-parser/test/files/golden/elements/descriptive lists.9 @@ -0,0 +1,41 @@ +Right + (PlainList + Descriptive + [ + ListItem { + bullet = Bullet '-', + counter = Nothing, + checkbox = Nothing, + tag = Just + [ + OrgF { + props = StandardProperties { + begin = 2, + end = 26, + postBlank = 0}, + datum = Link + (URILink "foo" "prot.co") + [ + OrgF { + props = StandardProperties { + begin = 17, + end = 24, + postBlank = 0}, + datum = Plain "bar baz"}]}], + content = [ + OrgF { + props = StandardProperties { + begin = 30, + end = 40, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 30, + end = 40, + postBlank = 0}, + datum = Plain + "bla :: ble"}]}}]}]) diff --git a/org-parser/test/files/golden/elements/fixed width.1 b/org-parser/test/files/golden/elements/fixed width.1 new file mode 100644 index 0000000..e7ca500 --- /dev/null +++ b/org-parser/test/files/golden/elements/fixed width.1 @@ -0,0 +1,7 @@ +Right + (ExampleBlock + (Map.fromList []) + [ + " fooblabla boo", + "foooo", + " booo"]) diff --git a/org-parser/test/files/golden/elements/greater blocks.1 b/org-parser/test/files/golden/elements/greater blocks.1 new file mode 100644 index 0000000..9fd3018 --- /dev/null +++ b/org-parser/test/files/golden/elements/greater blocks.1 @@ -0,0 +1,4 @@ +Right + (GreaterBlock + (Special "fun") + []) diff --git a/org-parser/test/files/golden/elements/horizontal rules.1 b/org-parser/test/files/golden/elements/horizontal rules.1 new file mode 100644 index 0000000..2574d0e --- /dev/null +++ b/org-parser/test/files/golden/elements/horizontal rules.1 @@ -0,0 +1 @@ +Right HorizontalRule diff --git a/org-parser/test/files/golden/elements/horizontal rules.2 b/org-parser/test/files/golden/elements/horizontal rules.2 new file mode 100644 index 0000000..5fcfbf8 --- /dev/null +++ b/org-parser/test/files/golden/elements/horizontal rules.2 @@ -0,0 +1,8 @@ +Left + (concat + [ + "1:3:\n", + " |\n", + "1 | -- \n", + " | ^\n", + "expecting hrule dashes\n"]) diff --git a/org-parser/test/files/golden/elements/lists in context.1 b/org-parser/test/files/golden/elements/lists in context.1 new file mode 100644 index 0000000..18a3640 --- /dev/null +++ b/org-parser/test/files/golden/elements/lists in context.1 @@ -0,0 +1,65 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 28, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = PlainList + (Unordered '-') + [ + ListItem { + bullet = Bullet '-', + counter = Nothing, + checkbox = Nothing, + tag = Nothing, + content = [ + OrgF { + props = StandardProperties { + begin = 2, + end = 10, + postBlank = 1}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 2, + end = 9, + postBlank = 0}, + datum = Plain "foo bar"}]}}, + OrgF { + props = StandardProperties { + begin = 13, + end = 28, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Keyword + "caption" + (ParsedKeyword + [ + OrgF { + props = StandardProperties { + begin = 28, + end = 31, + postBlank = 0}, + datum = Plain "foo"}])}}]}]}}, + OrgF { + props = StandardProperties { + begin = 28, + end = 32, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 28, + end = 31, + postBlank = 0}, + datum = Plain "bla"}]}}] diff --git a/org-parser/test/files/golden/elements/lists in context.2 b/org-parser/test/files/golden/elements/lists in context.2 new file mode 100644 index 0000000..1411dc6 --- /dev/null +++ b/org-parser/test/files/golden/elements/lists in context.2 @@ -0,0 +1,60 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 10, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = PlainList + (Unordered '-') + [ + ListItem { + bullet = Bullet '-', + counter = Nothing, + checkbox = Nothing, + tag = Nothing, + content = [ + OrgF { + props = StandardProperties { + begin = 2, + end = 10, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 2, + end = 9, + postBlank = 0}, + datum = Plain + "foo bar"}]}}]}]}}, + OrgF { + props = StandardProperties { + begin = 10, + end = 31, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList + [ + _×_ + "caption" + (ParsedKeyword + [ + OrgF { + props = StandardProperties { + begin = 25, + end = 28, + postBlank = 0}, + datum = Plain "foo"}])], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 27, + end = 30, + postBlank = 0}, + datum = Plain "bla"}]}}] diff --git a/org-parser/test/files/golden/elements/lists in context.3 b/org-parser/test/files/golden/elements/lists in context.3 new file mode 100644 index 0000000..ac8a826 --- /dev/null +++ b/org-parser/test/files/golden/elements/lists in context.3 @@ -0,0 +1,73 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 23, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = PlainList + (Unordered '-') + [ + ListItem { + bullet = Bullet '-', + counter = Nothing, + checkbox = Nothing, + tag = Nothing, + content = [ + OrgF { + props = StandardProperties { + begin = 3, + end = 21, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = PlainList + (Unordered '*') + [ + ListItem { + bullet = Bullet '*', + counter = Nothing, + checkbox = Nothing, + tag = Nothing, + content = []}, + ListItem { + bullet = Bullet '-', + counter = Nothing, + checkbox = Nothing, + tag = Nothing, + content = [ + OrgF { + props = StandardProperties { + begin = 10, + end = 14, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 10, + end = 13, + postBlank = 0}, + datum = Plain "foo"}]}}]}, + ListItem { + bullet = Bullet '-', + counter = Nothing, + checkbox = Nothing, + tag = Nothing, + content = []}, + ListItem { + bullet = Bullet '+', + counter = Nothing, + checkbox = Nothing, + tag = Nothing, + content = []}]}}]}, + ListItem { + bullet = Bullet '+', + counter = Nothing, + checkbox = Nothing, + tag = Nothing, + content = []}]}}] diff --git a/org-parser/test/files/golden/elements/lists in context.4 b/org-parser/test/files/golden/elements/lists in context.4 new file mode 100644 index 0000000..84fff5c --- /dev/null +++ b/org-parser/test/files/golden/elements/lists in context.4 @@ -0,0 +1,107 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 10, + postBlank = 2}, + datum = OrgElement { + affiliated = Map.fromList [], + data = PlainList + (Unordered '-') + [ + ListItem { + bullet = Bullet '-', + counter = Nothing, + checkbox = Nothing, + tag = Nothing, + content = []}, + ListItem { + bullet = Bullet '-', + counter = Nothing, + checkbox = Nothing, + tag = Nothing, + content = [ + OrgF { + props = StandardProperties { + begin = 6, + end = 10, + postBlank = 2}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 6, + end = 9, + postBlank = 0}, + datum = Plain "foo"}]}}]}]}}, + OrgF { + props = StandardProperties { + begin = 16, + end = 26, + postBlank = 2}, + datum = OrgElement { + affiliated = Map.fromList [], + data = PlainList + (Unordered '*') + [ + ListItem { + bullet = Bullet '*', + counter = Nothing, + checkbox = Nothing, + tag = Nothing, + content = [ + OrgF { + props = StandardProperties { + begin = 19, + end = 23, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 19, + end = 22, + postBlank = 0}, + datum = Plain "bar"}]}}]}, + ListItem { + bullet = Bullet '*', + counter = Nothing, + checkbox = Nothing, + tag = Nothing, + content = []}]}}, + OrgF { + props = StandardProperties { + begin = 28, + end = 35, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = PlainList + (Unordered '-') + [ + ListItem { + bullet = Bullet '-', + counter = Nothing, + checkbox = Nothing, + tag = Nothing, + content = [ + OrgF { + props = StandardProperties { + begin = 31, + end = 35, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 31, + end = 34, + postBlank = 0}, + datum = Plain "doo"}]}}]}]}}] diff --git a/org-parser/test/files/golden/elements/lists in context.5 b/org-parser/test/files/golden/elements/lists in context.5 new file mode 100644 index 0000000..40a3099 --- /dev/null +++ b/org-parser/test/files/golden/elements/lists in context.5 @@ -0,0 +1,75 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 2, + end = 34, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = PlainList + (Ordered OrderedNum) + [ + ListItem { + bullet = Counter "1" '.', + counter = Nothing, + checkbox = Nothing, + tag = Nothing, + content = [ + OrgF { + props = StandardProperties { + begin = 6, + end = 10, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 6, + end = 9, + postBlank = 0}, + datum = Plain "our"}]}}]}, + ListItem { + bullet = Counter "2" '.', + counter = Nothing, + checkbox = Nothing, + tag = Nothing, + content = [ + OrgF { + props = StandardProperties { + begin = 14, + end = 23, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 14, + end = 22, + postBlank = 0}, + datum = Plain "moment's"}]}}]}, + ListItem { + bullet = Counter "3" '.', + counter = Nothing, + checkbox = Nothing, + tag = Nothing, + content = [ + OrgF { + props = StandardProperties { + begin = 27, + end = 34, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 27, + end = 33, + postBlank = 0}, + datum = Plain "else's"}]}}]}]}}] diff --git a/org-parser/test/files/golden/elements/ordered lists.1 b/org-parser/test/files/golden/elements/ordered lists.1 new file mode 100644 index 0000000..7706335 --- /dev/null +++ b/org-parser/test/files/golden/elements/ordered lists.1 @@ -0,0 +1,67 @@ +Right + (PlainList + (Ordered OrderedNum) + [ + ListItem { + bullet = Counter "1" '.', + counter = Nothing, + checkbox = Nothing, + tag = Nothing, + content = [ + OrgF { + props = StandardProperties { + begin = 3, + end = 7, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 3, + end = 6, + postBlank = 0}, + datum = Plain "our"}]}}]}, + ListItem { + bullet = Counter "2" '.', + counter = Nothing, + checkbox = Nothing, + tag = Nothing, + content = [ + OrgF { + props = StandardProperties { + begin = 10, + end = 19, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 10, + end = 18, + postBlank = 0}, + datum = Plain "moment's"}]}}]}, + ListItem { + bullet = Counter "3" '.', + counter = Nothing, + checkbox = Nothing, + tag = Nothing, + content = [ + OrgF { + props = StandardProperties { + begin = 22, + end = 29, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 22, + end = 28, + postBlank = 0}, + datum = Plain "else's"}]}}]}]) diff --git a/org-parser/test/files/golden/elements/paragraph.1 b/org-parser/test/files/golden/elements/paragraph.1 new file mode 100644 index 0000000..330b750 --- /dev/null +++ b/org-parser/test/files/golden/elements/paragraph.1 @@ -0,0 +1,19 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 10, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 10, + postBlank = 0}, + datum = Plain + (T.concat + ["foobar\n", "baz"])}]}}] diff --git a/org-parser/test/files/golden/elements/paragraph.2 b/org-parser/test/files/golden/elements/paragraph.2 new file mode 100644 index 0000000..0eefecf --- /dev/null +++ b/org-parser/test/files/golden/elements/paragraph.2 @@ -0,0 +1,92 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 87, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 5, + postBlank = 0}, + datum = Plain "with "}, + OrgF { + props = StandardProperties { + begin = 5, + end = 21, + postBlank = 0}, + datum = Italic + [ + OrgF { + props = StandardProperties { + begin = 6, + end = 20, + postBlank = 0}, + datum = Plain + (T.concat + ["wrapped\n", "markup"])}]}, + OrgF { + props = StandardProperties { + begin = 21, + end = 33, + postBlank = 0}, + datum = Plain " and markup "}, + OrgF { + props = StandardProperties { + begin = 33, + end = 41, + postBlank = 0}, + datum = Bold + [ + OrgF { + props = StandardProperties { + begin = 34, + end = 40, + postBlank = 0}, + datum = Plain "at end"}]}, + OrgF { + props = StandardProperties { + begin = 41, + end = 42, + postBlank = 0}, + datum = Plain "\n"}, + OrgF { + props = StandardProperties { + begin = 42, + end = 52, + postBlank = 0}, + datum = Verbatim "at start"}, + OrgF { + props = StandardProperties { + begin = 52, + end = 80, + postBlank = 0}, + datum = Plain + (T.concat + [ + " but not~here~ and\n", + "not _here"])}, + OrgF { + props = StandardProperties { + begin = 80, + end = 86, + postBlank = 0}, + datum = Subscript + [ + OrgF { + props = StandardProperties { + begin = 81, + end = 86, + postBlank = 0}, + datum = Plain "right"}]}, + OrgF { + props = StandardProperties { + begin = 86, + end = 87, + postBlank = 0}, + datum = Plain "."}]}}] diff --git a/org-parser/test/files/golden/elements/tables.1 b/org-parser/test/files/golden/elements/tables.1 new file mode 100644 index 0000000..3657fef --- /dev/null +++ b/org-parser/test/files/golden/elements/tables.1 @@ -0,0 +1,117 @@ +Right + (Table + [ + StandardRow + [ + [ + OrgF { + props = StandardProperties { + begin = 2, + end = 5, + postBlank = 0}, + datum = Plain "foo"}], + [ + OrgF { + props = StandardProperties { + begin = 8, + end = 11, + postBlank = 0}, + datum = Plain "bar"}], + [ + OrgF { + props = StandardProperties { + begin = 14, + end = 17, + postBlank = 0}, + datum = Plain "baz"}]], + StandardRow + [ + [ + OrgF { + props = StandardProperties { + begin = 25, + end = 32, + postBlank = 0}, + datum = Plain "foo bar"}], + [ + OrgF { + props = StandardProperties { + begin = 35, + end = 38, + postBlank = 0}, + datum = Plain "baz"}]], + RuleRow, + ColumnPropsRow + [ + Just AlignRight, + Nothing, + Just AlignLeft, + Just AlignCenter], + StandardRow + [ + [ + OrgF { + props = StandardProperties { + begin = 65, + end = 68, + postBlank = 0}, + datum = Plain ""}], + [ + OrgF { + props = StandardProperties { + begin = 71, + end = 75, + postBlank = 0}, + datum = Plain "foo "}, + OrgF { + props = StandardProperties { + begin = 75, + end = 80, + postBlank = 0}, + datum = Italic + [ + OrgF { + props = StandardProperties { + begin = 76, + end = 79, + postBlank = 0}, + datum = Plain "bar"}]}], + [ + OrgF { + props = StandardProperties { + begin = 83, + end = 87, + postBlank = 0}, + datum = Bold + [ + OrgF { + props = StandardProperties { + begin = 84, + end = 86, + postBlank = 0}, + datum = Plain "ba"}]}], + [ + OrgF { + props = StandardProperties { + begin = 90, + end = 93, + postBlank = 0}, + datum = Plain "baz"}]], + StandardRow + [ + [ + OrgF { + props = StandardProperties { + begin = 96, + end = 99, + postBlank = 0}, + datum = Plain "foo"}], + [], + [ + OrgF { + props = StandardProperties { + begin = 103, + end = 106, + postBlank = 0}, + datum = Plain "bar"}], + []]]) diff --git a/org-parser/test/files/golden/elements/tricky whitespace.1 b/org-parser/test/files/golden/elements/tricky whitespace.1 new file mode 100644 index 0000000..9fd3ca7 --- /dev/null +++ b/org-parser/test/files/golden/elements/tricky whitespace.1 @@ -0,0 +1 @@ +Right [] diff --git a/org-parser/test/files/golden/elements/tricky whitespace.2 b/org-parser/test/files/golden/elements/tricky whitespace.2 new file mode 100644 index 0000000..9fd3ca7 --- /dev/null +++ b/org-parser/test/files/golden/elements/tricky whitespace.2 @@ -0,0 +1 @@ +Right [] diff --git a/org-parser/test/files/golden/elements/tricky whitespace.3 b/org-parser/test/files/golden/elements/tricky whitespace.3 new file mode 100644 index 0000000..9fd3ca7 --- /dev/null +++ b/org-parser/test/files/golden/elements/tricky whitespace.3 @@ -0,0 +1 @@ +Right [] diff --git a/org-parser/test/files/golden/elements/tricky whitespace.4 b/org-parser/test/files/golden/elements/tricky whitespace.4 new file mode 100644 index 0000000..2073b97 --- /dev/null +++ b/org-parser/test/files/golden/elements/tricky whitespace.4 @@ -0,0 +1,17 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 2, + end = 4, + postBlank = 0}, + datum = OrgElement { + affiliated = Map.fromList [], + data = Paragraph + [ + OrgF { + props = StandardProperties { + begin = 3, + end = 4, + postBlank = 0}, + datum = Plain "a"}]}}] diff --git a/org-parser/test/files/golden/objects/citations.1 b/org-parser/test/files/golden/objects/citations.1 new file mode 100644 index 0000000..80a5420 --- /dev/null +++ b/org-parser/test/files/golden/objects/citations.1 @@ -0,0 +1,61 @@ +Right + (Cite + Citation { + style = "", + variant = "", + prefix = Just + [ + OrgF { + props = StandardProperties { + begin = 6, + end = 11, + postBlank = 0}, + datum = Italic + [ + OrgF { + props = StandardProperties { + begin = 7, + end = 10, + postBlank = 0}, + datum = Plain "foo"}]}], + suffix = Just + [ + OrgF { + props = StandardProperties { + begin = 27, + end = 32, + postBlank = 0}, + datum = Italic + [ + OrgF { + props = StandardProperties { + begin = 28, + end = 31, + postBlank = 0}, + datum = Plain "baz"}]}], + references = [ + CiteReference { + id = "bef", + prefix = Just + [ + OrgF { + props = StandardProperties { + begin = 12, + end = 17, + postBlank = 0}, + datum = Italic + [ + OrgF { + props = StandardProperties { + begin = 13, + end = 16, + postBlank = 0}, + datum = Plain "bar"}]}], + suffix = Just + [ + OrgF { + props = StandardProperties { + begin = 21, + end = 26, + postBlank = 0}, + datum = Verbatim "bof"}]}]}) diff --git a/org-parser/test/files/golden/objects/footnote references.1 b/org-parser/test/files/golden/objects/footnote references.1 new file mode 100644 index 0000000..eba4fdf --- /dev/null +++ b/org-parser/test/files/golden/objects/footnote references.1 @@ -0,0 +1,11 @@ +Right + (FootnoteRef + (FootnoteRefDef + Nothing + [ + OrgF { + props = StandardProperties { + begin = 5, + end = 11, + postBlank = 0}, + datum = Plain "simple"}])) diff --git a/org-parser/test/files/golden/objects/footnote references.2 b/org-parser/test/files/golden/objects/footnote references.2 new file mode 100644 index 0000000..a1216b8 --- /dev/null +++ b/org-parser/test/files/golden/objects/footnote references.2 @@ -0,0 +1,9 @@ +Left + (concat + [ + "1:14:\n", + " |\n", + "1 | [fn::s[imple]\n", + " | ^\n", + "unexpected end of input\n", + "expecting balanced delimiters or insides of markup\n"]) diff --git a/org-parser/test/files/golden/objects/footnote references.3 b/org-parser/test/files/golden/objects/footnote references.3 new file mode 100644 index 0000000..120f4d3 --- /dev/null +++ b/org-parser/test/files/golden/objects/footnote references.3 @@ -0,0 +1,11 @@ +Right + (FootnoteRef + (FootnoteRefDef + (Just "mydef") + [ + OrgF { + props = StandardProperties { + begin = 10, + end = 18, + postBlank = 0}, + datum = Plain "s[imp]le"}])) diff --git a/org-parser/test/files/golden/objects/image or links.1 b/org-parser/test/files/golden/objects/image or links.1 new file mode 100644 index 0000000..0c56ef3 --- /dev/null +++ b/org-parser/test/files/golden/objects/image or links.1 @@ -0,0 +1,6 @@ +Right + (Link + (URILink + "http" + "//blablebli.com") + []) diff --git a/org-parser/test/files/golden/objects/image or links.2 b/org-parser/test/files/golden/objects/image or links.2 new file mode 100644 index 0000000..5f746fc --- /dev/null +++ b/org-parser/test/files/golden/objects/image or links.2 @@ -0,0 +1,38 @@ +Right + (Link + (URILink + "http" + "//blablebli.com") + [ + OrgF { + props = StandardProperties { + begin = 24, + end = 28, + postBlank = 0}, + datum = Italic + [ + OrgF { + props = StandardProperties { + begin = 25, + end = 27, + postBlank = 0}, + datum = Plain "uh"}]}, + OrgF { + props = StandardProperties { + begin = 28, + end = 34, + postBlank = 0}, + datum = Plain " duh! "}, + OrgF { + props = StandardProperties { + begin = 34, + end = 39, + postBlank = 0}, + datum = Bold + [ + OrgF { + props = StandardProperties { + begin = 35, + end = 38, + postBlank = 0}, + datum = Plain "foo"}]}]) diff --git a/org-parser/test/files/golden/objects/italic.1 b/org-parser/test/files/golden/objects/italic.1 new file mode 100644 index 0000000..845e853 --- /dev/null +++ b/org-parser/test/files/golden/objects/italic.1 @@ -0,0 +1,9 @@ +Right + (Italic + [ + OrgF { + props = StandardProperties { + begin = 1, + end = 6, + postBlank = 0}, + datum = Plain "/ foo"}]) diff --git a/org-parser/test/files/golden/objects/italic.2 b/org-parser/test/files/golden/objects/italic.2 new file mode 100644 index 0000000..d176195 --- /dev/null +++ b/org-parser/test/files/golden/objects/italic.2 @@ -0,0 +1,9 @@ +Right + (Italic + [ + OrgF { + props = StandardProperties { + begin = 1, + end = 6, + postBlank = 0}, + datum = Plain "foo /"}]) diff --git a/org-parser/test/files/golden/objects/italic.3 b/org-parser/test/files/golden/objects/italic.3 new file mode 100644 index 0000000..59cc49f --- /dev/null +++ b/org-parser/test/files/golden/objects/italic.3 @@ -0,0 +1,9 @@ +Right + (Italic + [ + OrgF { + props = StandardProperties { + begin = 1, + end = 8, + postBlank = 0}, + datum = Plain "foo / f"}]) diff --git a/org-parser/test/files/golden/objects/line breaks.1 b/org-parser/test/files/golden/objects/line breaks.1 new file mode 100644 index 0000000..763c35a --- /dev/null +++ b/org-parser/test/files/golden/objects/line breaks.1 @@ -0,0 +1,20 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 10, + postBlank = 0}, + datum = Plain "this is a "}, + OrgF { + props = StandardProperties { + begin = 10, + end = 16, + postBlank = 0}, + datum = LineBreak}, + OrgF { + props = StandardProperties { + begin = 16, + end = 26, + postBlank = 0}, + datum = Plain "line break"}] diff --git a/org-parser/test/files/golden/objects/line breaks.2 b/org-parser/test/files/golden/objects/line breaks.2 new file mode 100644 index 0000000..4114efc --- /dev/null +++ b/org-parser/test/files/golden/objects/line breaks.2 @@ -0,0 +1,15 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 15, + postBlank = 0}, + datum = Plain + "also linebreak "}, + OrgF { + props = StandardProperties { + begin = 15, + end = 17, + postBlank = 0}, + datum = LineBreak}] diff --git a/org-parser/test/files/golden/objects/macros.1 b/org-parser/test/files/golden/objects/macros.1 new file mode 100644 index 0000000..d7c45d5 --- /dev/null +++ b/org-parser/test/files/golden/objects/macros.1 @@ -0,0 +1 @@ +Right (Macro "fooo" [""]) diff --git a/org-parser/test/files/golden/objects/macros.2 b/org-parser/test/files/golden/objects/macros.2 new file mode 100644 index 0000000..175701a --- /dev/null +++ b/org-parser/test/files/golden/objects/macros.2 @@ -0,0 +1,9 @@ +Left + (concat + [ + "1:7:\n", + " |\n", + "1 | {{{fun\231\227o()}}}\n", + " | ^^^\n", + "unexpected \"\231\227o\"\n", + "expecting \"}}}\" or '('\n"]) diff --git a/org-parser/test/files/golden/objects/macros.3 b/org-parser/test/files/golden/objects/macros.3 new file mode 100644 index 0000000..52e50db --- /dev/null +++ b/org-parser/test/files/golden/objects/macros.3 @@ -0,0 +1,8 @@ +Left + (concat + [ + "1:4:\n", + " |\n", + "1 | {{{2fun()}}}\n", + " | ^\n", + "unexpected '2'\n"]) diff --git a/org-parser/test/files/golden/objects/macros.4 b/org-parser/test/files/golden/objects/macros.4 new file mode 100644 index 0000000..7cfe8ee --- /dev/null +++ b/org-parser/test/files/golden/objects/macros.4 @@ -0,0 +1,4 @@ +Right + (Macro + "fun-2_3" + ["bar", "(bar", "baz){a}"]) diff --git a/org-parser/test/files/golden/objects/math fragment.1 b/org-parser/test/files/golden/objects/math fragment.1 new file mode 100644 index 0000000..849a54d --- /dev/null +++ b/org-parser/test/files/golden/objects/math fragment.1 @@ -0,0 +1,4 @@ +Right + (LaTeXFragment + InlMathFragment + "\\LaTeX + 2") diff --git a/org-parser/test/files/golden/objects/math fragment.2 b/org-parser/test/files/golden/objects/math fragment.2 new file mode 100644 index 0000000..2bb8a83 --- /dev/null +++ b/org-parser/test/files/golden/objects/math fragment.2 @@ -0,0 +1,4 @@ +Right + (LaTeXFragment + DispMathFragment + "\\LaTeX + 2") diff --git a/org-parser/test/files/golden/objects/statistic cookies.1 b/org-parser/test/files/golden/objects/statistic cookies.1 new file mode 100644 index 0000000..ef6013d --- /dev/null +++ b/org-parser/test/files/golden/objects/statistic cookies.1 @@ -0,0 +1,3 @@ +Right + (StatisticCookie + (Left (_×_ 13 18))) diff --git a/org-parser/test/files/golden/objects/statistic cookies.2 b/org-parser/test/files/golden/objects/statistic cookies.2 new file mode 100644 index 0000000..a879609 --- /dev/null +++ b/org-parser/test/files/golden/objects/statistic cookies.2 @@ -0,0 +1,2 @@ +Right + (StatisticCookie (Right 33)) diff --git a/org-parser/test/files/golden/objects/subscripts and superscripts.1 b/org-parser/test/files/golden/objects/subscripts and superscripts.1 new file mode 100644 index 0000000..700a5ff --- /dev/null +++ b/org-parser/test/files/golden/objects/subscripts and superscripts.1 @@ -0,0 +1,9 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 15, + postBlank = 0}, + datum = Plain + "not a _suscript"}] diff --git a/org-parser/test/files/golden/objects/subscripts and superscripts.2 b/org-parser/test/files/golden/objects/subscripts and superscripts.2 new file mode 100644 index 0000000..4ca0b08 --- /dev/null +++ b/org-parser/test/files/golden/objects/subscripts and superscripts.2 @@ -0,0 +1,9 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 15, + postBlank = 0}, + datum = Plain + "not_{{suscript}"}] diff --git a/org-parser/test/files/golden/objects/subscripts and superscripts.3 b/org-parser/test/files/golden/objects/subscripts and superscripts.3 new file mode 100644 index 0000000..de05e12 --- /dev/null +++ b/org-parser/test/files/golden/objects/subscripts and superscripts.3 @@ -0,0 +1,40 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 1, + postBlank = 0}, + datum = Plain "a"}, + OrgF { + props = StandardProperties { + begin = 1, + end = 23, + postBlank = 0}, + datum = Subscript + [ + OrgF { + props = StandardProperties { + begin = 3, + end = 11, + postBlank = 0}, + datum = Plain "balanced"}, + OrgF { + props = StandardProperties { + begin = 11, + end = 19, + postBlank = 0}, + datum = Superscript + [ + OrgF { + props = StandardProperties { + begin = 13, + end = 18, + postBlank = 0}, + datum = Plain "crazy"}]}, + OrgF { + props = StandardProperties { + begin = 19, + end = 22, + postBlank = 0}, + datum = Plain " ok"}]}] diff --git a/org-parser/test/files/golden/objects/subscripts and superscripts.4 b/org-parser/test/files/golden/objects/subscripts and superscripts.4 new file mode 100644 index 0000000..bbe1e51 --- /dev/null +++ b/org-parser/test/files/golden/objects/subscripts and superscripts.4 @@ -0,0 +1,22 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 1, + postBlank = 0}, + datum = Plain "a"}, + OrgF { + props = StandardProperties { + begin = 1, + end = 26, + postBlank = 0}, + datum = Subscript + [ + OrgF { + props = StandardProperties { + begin = 3, + end = 25, + postBlank = 0}, + datum = Plain + "balanced {suscript} ok"}]}] diff --git a/org-parser/test/files/golden/objects/subscripts and superscripts.5 b/org-parser/test/files/golden/objects/subscripts and superscripts.5 new file mode 100644 index 0000000..07314ca --- /dev/null +++ b/org-parser/test/files/golden/objects/subscripts and superscripts.5 @@ -0,0 +1,26 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 1, + postBlank = 0}, + datum = Plain "a"}, + OrgF { + props = StandardProperties { + begin = 1, + end = 28, + postBlank = 0}, + datum = Subscript + [ + OrgF { + props = StandardProperties { + begin = 3, + end = 27, + postBlank = 0}, + datum = Plain + (T.concat + [ + "bala\n", + "nced {sus\n", + "cript} ok"])}]}] diff --git a/org-parser/test/files/golden/objects/subscripts and superscripts.6 b/org-parser/test/files/golden/objects/subscripts and superscripts.6 new file mode 100644 index 0000000..ca5aa49 --- /dev/null +++ b/org-parser/test/files/golden/objects/subscripts and superscripts.6 @@ -0,0 +1,28 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 1, + postBlank = 0}, + datum = Plain "a"}, + OrgF { + props = StandardProperties { + begin = 1, + end = 19, + postBlank = 0}, + datum = Superscript + [ + OrgF { + props = StandardProperties { + begin = 2, + end = 19, + postBlank = 0}, + datum = Plain + "+strange,suscript"}]}, + OrgF { + props = StandardProperties { + begin = 19, + end = 20, + postBlank = 0}, + datum = Plain ","}] diff --git a/org-parser/test/files/golden/objects/subscripts and superscripts.7 b/org-parser/test/files/golden/objects/subscripts and superscripts.7 new file mode 100644 index 0000000..7ed11be --- /dev/null +++ b/org-parser/test/files/golden/objects/subscripts and superscripts.7 @@ -0,0 +1,28 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 1, + postBlank = 0}, + datum = Plain "a"}, + OrgF { + props = StandardProperties { + begin = 1, + end = 3, + postBlank = 0}, + datum = Superscript + [ + OrgF { + props = StandardProperties { + begin = 2, + end = 3, + postBlank = 0}, + datum = Plain "*"}]}, + OrgF { + props = StandardProperties { + begin = 3, + end = 22, + postBlank = 0}, + datum = Plain + "suspicious suscript"}] diff --git a/org-parser/test/files/golden/objects/subscripts and superscripts.8 b/org-parser/test/files/golden/objects/subscripts and superscripts.8 new file mode 100644 index 0000000..ed93460 --- /dev/null +++ b/org-parser/test/files/golden/objects/subscripts and superscripts.8 @@ -0,0 +1,28 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 1, + postBlank = 0}, + datum = Plain "a"}, + OrgF { + props = StandardProperties { + begin = 1, + end = 20, + postBlank = 0}, + datum = Subscript + [ + OrgF { + props = StandardProperties { + begin = 2, + end = 20, + postBlank = 0}, + datum = Plain + "bad,.,.,maleficent"}]}, + OrgF { + props = StandardProperties { + begin = 20, + end = 25, + postBlank = 0}, + datum = Plain ", one"}] diff --git a/org-parser/test/files/golden/objects/subscripts and superscripts.9 b/org-parser/test/files/golden/objects/subscripts and superscripts.9 new file mode 100644 index 0000000..43d09ad --- /dev/null +++ b/org-parser/test/files/golden/objects/subscripts and superscripts.9 @@ -0,0 +1,29 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 1, + postBlank = 0}, + datum = Plain "a"}, + OrgF { + props = StandardProperties { + begin = 1, + end = 12, + postBlank = 0}, + datum = Subscript + [ + OrgF { + props = StandardProperties { + begin = 2, + end = 6, + postBlank = 0}, + datum = Plain "some"}, + OrgF { + props = StandardProperties { + begin = 6, + end = 12, + postBlank = 0}, + datum = LaTeXFragment + RawFragment + "\\LaTeX"}]}] diff --git a/org-parser/test/files/golden/objects/targets.1 b/org-parser/test/files/golden/objects/targets.1 new file mode 100644 index 0000000..714f372 --- /dev/null +++ b/org-parser/test/files/golden/objects/targets.1 @@ -0,0 +1,2 @@ +Right + (Target "this is a target") diff --git a/org-parser/test/files/golden/objects/targets.2 b/org-parser/test/files/golden/objects/targets.2 new file mode 100644 index 0000000..b8846a6 --- /dev/null +++ b/org-parser/test/files/golden/objects/targets.2 @@ -0,0 +1,8 @@ +Left + (concat + [ + "1:16:\n", + " |\n", + "1 | << not a target>>\n", + " | ^\n", + "expecting dedicated target\n"]) diff --git a/org-parser/test/files/golden/objects/targets.3 b/org-parser/test/files/golden/objects/targets.3 new file mode 100644 index 0000000..2913aac --- /dev/null +++ b/org-parser/test/files/golden/objects/targets.3 @@ -0,0 +1,8 @@ +Left + (concat + [ + "1:16:\n", + " |\n", + "1 | <>\n", + " | ^\n", + "expecting dedicated target\n"]) diff --git a/org-parser/test/files/golden/objects/targets.4 b/org-parser/test/files/golden/objects/targets.4 new file mode 100644 index 0000000..cc99679 --- /dev/null +++ b/org-parser/test/files/golden/objects/targets.4 @@ -0,0 +1,8 @@ +Left + (concat + [ + "1:8:\n", + " |\n", + "1 | <>\n", + " | ^\n", + "expecting dedicated target\n"]) diff --git a/org-parser/test/files/golden/objects/targets.5 b/org-parser/test/files/golden/objects/targets.5 new file mode 100644 index 0000000..b9804d5 --- /dev/null +++ b/org-parser/test/files/golden/objects/targets.5 @@ -0,0 +1,8 @@ +Left + (concat + [ + "1:8:\n", + " |\n", + "1 | < is not a target>>\n", + " | ^\n", + "expecting dedicated target\n"]) diff --git a/org-parser/test/files/golden/objects/tex math fragments.1 b/org-parser/test/files/golden/objects/tex math fragments.1 new file mode 100644 index 0000000..1a382e2 --- /dev/null +++ b/org-parser/test/files/golden/objects/tex math fragments.1 @@ -0,0 +1,10 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 10, + postBlank = 0}, + datum = LaTeXFragment + InlMathFragment + "e = mc^2"}] diff --git a/org-parser/test/files/golden/objects/tex math fragments.2 b/org-parser/test/files/golden/objects/tex math fragments.2 new file mode 100644 index 0000000..cf67cf7 --- /dev/null +++ b/org-parser/test/files/golden/objects/tex math fragments.2 @@ -0,0 +1,8 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 10, + postBlank = 0}, + datum = Plain "$$foo bar$"}] diff --git a/org-parser/test/files/golden/objects/tex math fragments.3 b/org-parser/test/files/golden/objects/tex math fragments.3 new file mode 100644 index 0000000..fd862ce --- /dev/null +++ b/org-parser/test/files/golden/objects/tex math fragments.3 @@ -0,0 +1,8 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 10, + postBlank = 0}, + datum = Plain "$foo bar$a"}] diff --git a/org-parser/test/files/golden/objects/tex math fragments.4 b/org-parser/test/files/golden/objects/tex math fragments.4 new file mode 100644 index 0000000..10a78ca --- /dev/null +++ b/org-parser/test/files/golden/objects/tex math fragments.4 @@ -0,0 +1,22 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 1, + postBlank = 0}, + datum = Plain "("}, + OrgF { + props = StandardProperties { + begin = 1, + end = 10, + postBlank = 0}, + datum = LaTeXFragment + InlMathFragment + "foo bar"}, + OrgF { + props = StandardProperties { + begin = 10, + end = 11, + postBlank = 0}, + datum = Plain ")"}] diff --git a/org-parser/test/files/golden/objects/tex math fragments.5 b/org-parser/test/files/golden/objects/tex math fragments.5 new file mode 100644 index 0000000..0c6a15c --- /dev/null +++ b/org-parser/test/files/golden/objects/tex math fragments.5 @@ -0,0 +1,9 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 41, + postBlank = 0}, + datum = Plain + "This is $1 buck, not math ($1! so cheap!)"}] diff --git a/org-parser/test/files/golden/objects/tex math fragments.6 b/org-parser/test/files/golden/objects/tex math fragments.6 new file mode 100644 index 0000000..7f9c297 --- /dev/null +++ b/org-parser/test/files/golden/objects/tex math fragments.6 @@ -0,0 +1,22 @@ +Right + [ + OrgF { + props = StandardProperties { + begin = 0, + end = 3, + postBlank = 0}, + datum = Plain "two"}, + OrgF { + props = StandardProperties { + begin = 3, + end = 19, + postBlank = 0}, + datum = LaTeXFragment + DispMathFragment + "always means"}, + OrgF { + props = StandardProperties { + begin = 19, + end = 23, + postBlank = 0}, + datum = Plain "math"}] diff --git a/org-parser/test/files/golden/objects/timestamp.1 b/org-parser/test/files/golden/objects/timestamp.1 new file mode 100644 index 0000000..311ff31 --- /dev/null +++ b/org-parser/test/files/golden/objects/timestamp.1 @@ -0,0 +1,16 @@ +Right + (Timestamp + TimestampData { + active = True, + time = _×_×_×_ + OrgDate { + year = 1997, + month = 11, + day = 3, + weekday = Just "Mon"} + (Just + OrgTime { + hour = 19, + minute = 15}) + Nothing + Nothing}) diff --git a/org-parser/test/files/golden/objects/timestamp.2 b/org-parser/test/files/golden/objects/timestamp.2 new file mode 100644 index 0000000..0b157a4 --- /dev/null +++ b/org-parser/test/files/golden/objects/timestamp.2 @@ -0,0 +1,16 @@ +Right + (Timestamp + TimestampData { + active = False, + time = _×_×_×_ + OrgDate { + year = 2020, + month = 3, + day = 4, + weekday = Nothing} + (Just + OrgTime { + hour = 20, + minute = 20}) + Nothing + Nothing}) diff --git a/org-parser/test/files/golden/objects/timestamp.3 b/org-parser/test/files/golden/objects/timestamp.3 new file mode 100644 index 0000000..30b9290 --- /dev/null +++ b/org-parser/test/files/golden/objects/timestamp.3 @@ -0,0 +1,14 @@ +Right + (Timestamp + TimestampData { + active = False, + time = _×_×_×_ + OrgDate { + year = 2020, + month = 3, + day = 4, + weekday = Nothing} + (Just + OrgTime {hour = 0, minute = 20}) + Nothing + Nothing})