├── .github └── workflows │ └── test.yml ├── .gitignore ├── Eask ├── README.md ├── org-pretty-table.el └── tests └── test-pretty-table.el /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | test: 16 | runs-on: ${{ matrix.os }} 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | os: [ubuntu-latest, macos-latest, windows-latest] 21 | emacs-version: 22 | - 26.3 23 | - 27.2 24 | - 28.2 25 | - snapshot 26 | 27 | steps: 28 | - uses: actions/checkout@v3 29 | 30 | - uses: jcs090218/setup-emacs@master 31 | with: 32 | version: ${{ matrix.emacs-version }} 33 | 34 | - uses: emacs-eask/setup-eask@master 35 | with: 36 | version: 'snapshot' 37 | 38 | - name: Install dependencies 39 | run: 'eask install-deps --dev' 40 | 41 | - name: Run buttercup 42 | run: | 43 | eask test buttercup 44 | 45 | - name: Run tests 46 | run: | 47 | eask clean all 48 | eask package 49 | eask install 50 | eask compile 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.eask 2 | *.elc 3 | *-autoloads.el 4 | -------------------------------------------------------------------------------- /Eask: -------------------------------------------------------------------------------- 1 | (package "org-pretty-table" 2 | "1.0.0" 3 | "Replace org-table characters with box-drawing unicode glyphs") 4 | 5 | (website-url "https://github.com/Fuco1/org-pretty-table") 6 | (keywords "faces") 7 | 8 | (package-file "org-pretty-table.el") 9 | 10 | (script "test" "echo \"Error: no test specified\" && exit 1") 11 | 12 | (source 'gnu) 13 | (source 'melpa) 14 | 15 | (depends-on "org" "9") 16 | (depends-on "emacs" "24.1") 17 | 18 | (development 19 | (depends-on "elsa") 20 | (depends-on "undercover") 21 | (depends-on "buttercup")) 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # org-pretty-table 2 | 3 | Draw pretty unicode tables in `org-mode` and `orgtbl-mode`. 4 | 5 | Very experimental. 6 | 7 | ![example](https://pbs.twimg.com/media/FqexCG-WYAA9x_X?format=png&name=large) 8 | 9 | # Usage 10 | 11 | Enable the `org-pretty-table-mode` in an org buffer and it will 12 | replace the ASCII table borders with beautiful unicode ones. The 13 | replacement is *only visual* (using [display text properties][props]), 14 | nothing in your file is actually changed. The buffer and the file 15 | saved to disc still contains the original ASCII characters (which it 16 | must since that's what org uses for table detection). 17 | 18 | You can customize `org-pretty-table-charset` to change the border 19 | characters. This mode comes with two built-in "themes", single 20 | horizontal lines and double horizontal lines. See `M-x 21 | customize-variable RET org-pretty-table-charset RET`. 22 | 23 | # Installation 24 | 25 | This package is not yet distributed through package archives and you have to install it manually. 26 | 27 | 1. Download the `org-pretty-table.el` file into some folder in your hard drive (for example `~/.emacs.d/site-lisp`). 28 | 29 | 2. Put the following code into your `~/.emacs.d/init.el` or `~/.emacs` file: 30 | 31 | ``` emacs-lisp 32 | (progn 33 | (add-to-list 'load-path "~/.emacs.d/site-lisp") 34 | (require 'org-pretty-table) 35 | (add-hook 'org-mode-hook (lambda () (org-pretty-table-mode)))) 36 | ``` 37 | 38 | 3. Evaluate the `progn` block by putting the point on the `progn` and calling `C-M-x` (`M-x eval-defun`). If unsure, you can also simply restart Emacs. 39 | 40 | 4. Open an org-mode file. Calling `M-: org-pretty-table-mode RET` should print `1` in the minibuffer. You're done! 41 | 42 | # Development 43 | 44 | Use Eask to install dependencies: 45 | 46 | ``` shell 47 | eask install-deps --dev 48 | ``` 49 | 50 | Run tests with 51 | 52 | ``` shell 53 | eask test buttercup 54 | ``` 55 | 56 | [props]: https://www.gnu.org/software/emacs/manual/html_node/elisp/Special-Properties.html 57 | -------------------------------------------------------------------------------- /org-pretty-table.el: -------------------------------------------------------------------------------- 1 | ;;; org-pretty-table.el --- Replace org-table characters with box-drawing unicode glyphs -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2013, 2023 Matus Goljer 4 | 5 | ;; Author: Matus Goljer 6 | ;; Maintainer: Matus Goljer 7 | ;; Keywords: faces 8 | ;; URL: https://github.com/Fuco1/org-pretty-table 9 | ;; Package-Requires: ((org "9") (emacs "24.1")) 10 | ;; Version: 1.0.0 11 | ;; Created: 29th November 2013 12 | 13 | ;; This program is free software; you can redistribute it and/or modify 14 | ;; it under the terms of the GNU General Public License as published by 15 | ;; the Free Software Foundation, either version 3 of the License, or 16 | ;; (at your option) any later version. 17 | 18 | ;; This program is distributed in the hope that it will be useful, 19 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | ;; GNU General Public License for more details. 22 | 23 | ;; You should have received a copy of the GNU General Public License 24 | ;; along with this program. If not, see . 25 | 26 | ;;; Commentary: 27 | 28 | ;; This replaces the characters - | and + in `org-mode' tables with 29 | ;; appropriate unicode box-drawing glyphs, see 30 | ;; http://en.wikipedia.org/wiki/Box-drawing_character 31 | 32 | ;;; Code: 33 | 34 | (require 'org) 35 | 36 | (defconst org-pretty-table-regexp (regexp-opt '("-" "+" "|"))) 37 | 38 | (defgroup org-pretty-table () 39 | "Replace org-table characters with box-drawing unicode glyphs." 40 | :group 'org) 41 | 42 | (defcustom org-pretty-table-charset "┌┐└┘┬┤┴├┼─│" 43 | "Charset to draw the table. 44 | 45 | The value is a string of length 11 with the characters used to 46 | draw the table borders. 47 | 48 | The order of the blocks is: 49 | 50 | - upper left corner 51 | - upper right corner 52 | - lower left corner 53 | - lower right corner 54 | - down-facing T 55 | - left-facing T 56 | - up-facing T 57 | - right-facing T 58 | - cross 59 | - horizontal bar 60 | - vertical bar" 61 | :group 'org-pretty-table 62 | :type '(choice (const :tag "Single horizontal lines" "┌┐└┘┬┤┴├┼─│") 63 | (const :tag "Double horizontal lines" "╒╕╘╛╤╡╧╞╪═│") 64 | (string :tag "Custom"))) 65 | 66 | (defsubst org-pretty-table-ul-corner () 67 | "Return upper left corner character as a string." 68 | (declare (pure t)) 69 | (make-string 1 (aref org-pretty-table-charset 0))) 70 | 71 | (defsubst org-pretty-table-ur-corner () 72 | "Return upper right corner character as a string." 73 | (declare (pure t)) 74 | (make-string 1 (aref org-pretty-table-charset 1))) 75 | 76 | (defsubst org-pretty-table-ll-corner () 77 | "Return lower left corner character as a string." 78 | (declare (pure t)) 79 | (make-string 1 (aref org-pretty-table-charset 2))) 80 | 81 | (defsubst org-pretty-table-lr-corner () 82 | "Return lower right corner character as a string." 83 | (declare (pure t)) 84 | (make-string 1 (aref org-pretty-table-charset 3))) 85 | 86 | (defsubst org-pretty-table-df-t () 87 | "Return down facing T character as a string." 88 | (declare (pure t)) 89 | (make-string 1 (aref org-pretty-table-charset 4))) 90 | 91 | (defsubst org-pretty-table-lf-t () 92 | "Return left facing T character as a string." 93 | (declare (pure t)) 94 | (make-string 1 (aref org-pretty-table-charset 5))) 95 | 96 | (defsubst org-pretty-table-uf-t () 97 | "Return up facing T character as a string." 98 | (declare (pure t)) 99 | (make-string 1 (aref org-pretty-table-charset 6))) 100 | 101 | (defsubst org-pretty-table-rf-t () 102 | "Return right facing T character as a string." 103 | (declare (pure t)) 104 | (make-string 1 (aref org-pretty-table-charset 7))) 105 | 106 | (defsubst org-pretty-table-cross () 107 | "Return cross character as a string." 108 | (declare (pure t)) 109 | (make-string 1 (aref org-pretty-table-charset 8))) 110 | 111 | (defsubst org-pretty-table-hb () 112 | "Return horizontal bar character as a string." 113 | (declare (pure t)) 114 | (make-string 1 (aref org-pretty-table-charset 9))) 115 | 116 | (defsubst org-pretty-table-vb () 117 | "Return vertical bar character as a string." 118 | (declare (pure t)) 119 | (make-string 1 (aref org-pretty-table-charset 10))) 120 | 121 | (defun org-pretty-table-at-table-p () 122 | "Check if point is at table." 123 | (save-excursion 124 | (skip-syntax-forward " " (line-end-position)) 125 | (eq (following-char) ?|))) 126 | 127 | (defun org-pretty-table-propertize-region (start end) 128 | "Replace org-table characters with box-drawing glyphs between START and END. 129 | 130 | Used by jit-lock for dynamic highlighting." 131 | (save-excursion 132 | (goto-char start) 133 | (let (table-end) 134 | (while (re-search-forward org-pretty-table-regexp end t) 135 | ;; reached the end of the current table 136 | (if (and table-end 137 | (> (point) table-end)) 138 | (setq table-end nil)) 139 | 140 | ;; check if the current match is a table if we are not in a 141 | ;; table right now 142 | (unless (and (not table-end) 143 | (not (save-match-data 144 | (org-at-table-p)))) 145 | 146 | ;; get the end of the table if we found a new table, so we 147 | ;; don't have to check (org-at-table-p) again until then 148 | (unless table-end 149 | (save-match-data 150 | (setq table-end (org-table-end)))) 151 | 152 | ;; determine the context of the character 153 | (let ((match (match-string 0))) 154 | (cond 155 | ((equal "-" match) 156 | (backward-char 1) 157 | (re-search-forward "-+") 158 | (when (looking-at-p "[+|]") 159 | (put-text-property 160 | (match-beginning 0) (match-end 0) 161 | 'display 162 | (make-string (- (match-end 0) (match-beginning 0)) 163 | (aref (org-pretty-table-hb) 0)))) 164 | t) 165 | ((equal "|" match) 166 | (cond 167 | ((and (eq (following-char) ?-) 168 | (save-excursion 169 | (forward-line 1) 170 | (org-pretty-table-at-table-p)) 171 | (save-excursion 172 | (backward-char 1) 173 | (not (bobp))) 174 | (save-excursion 175 | (forward-line -1) 176 | (and (not (bobp)) 177 | (org-pretty-table-at-table-p)))) 178 | (put-text-property (match-beginning 0) (match-end 0) 'display (org-pretty-table-rf-t)) 179 | t) 180 | ((and (save-excursion 181 | (backward-char 1) 182 | (eq (preceding-char) ?-)) 183 | (save-excursion 184 | (forward-line 1) 185 | (org-pretty-table-at-table-p)) 186 | (save-excursion 187 | (forward-line -1) 188 | (and (not (bobp)) 189 | (org-pretty-table-at-table-p)))) 190 | (put-text-property (match-beginning 0) (match-end 0) 'display (org-pretty-table-lf-t)) 191 | t) 192 | ((and (save-excursion 193 | (backward-char 1) 194 | (eq (preceding-char) ?-)) 195 | (save-excursion 196 | (forward-line -1) 197 | (or (bobp) 198 | (not (org-pretty-table-at-table-p))))) 199 | (put-text-property (match-beginning 0) (match-end 0) 'display (org-pretty-table-ur-corner)) 200 | t) 201 | ((and (save-excursion 202 | (backward-char 1) 203 | (eq (preceding-char) ?-)) 204 | (save-excursion 205 | (forward-line 1) 206 | (not (org-pretty-table-at-table-p)))) 207 | (put-text-property (match-beginning 0) (match-end 0) 'display (org-pretty-table-lr-corner)) 208 | t) 209 | ((and (eq (following-char) ?-) 210 | (save-excursion 211 | (forward-line -1) 212 | (or (bobp) 213 | (not (org-pretty-table-at-table-p))))) 214 | (put-text-property (match-beginning 0) (match-end 0) 'display (org-pretty-table-ul-corner)) 215 | t) 216 | ((and (eq (following-char) ?-) 217 | (save-excursion 218 | (forward-line 1) 219 | (not (org-pretty-table-at-table-p)))) 220 | (put-text-property (match-beginning 0) (match-end 0) 'display (org-pretty-table-ll-corner)) 221 | t) 222 | (t 223 | (put-text-property (match-beginning 0) (match-end 0) 'display (org-pretty-table-vb)) 224 | t))) 225 | ((equal "+" match) 226 | (cond 227 | ((and (eq (following-char) ?-) 228 | (save-excursion 229 | (backward-char 1) 230 | (eq (preceding-char) ?-)) 231 | (save-excursion 232 | (forward-line -1) 233 | (and (not (bobp)) 234 | (org-pretty-table-at-table-p))) 235 | (save-excursion 236 | (forward-line 1) 237 | (org-pretty-table-at-table-p))) 238 | (put-text-property (match-beginning 0) (match-end 0) 'display (org-pretty-table-cross)) 239 | t) 240 | ((and (eq (following-char) ?-) 241 | (save-excursion 242 | (backward-char 1) 243 | (eq (preceding-char) ?-)) 244 | (save-excursion 245 | (forward-line -1) 246 | (or (bobp) 247 | (not (org-pretty-table-at-table-p)))) 248 | (save-excursion 249 | (forward-line 1) 250 | (org-pretty-table-at-table-p))) 251 | (put-text-property (match-beginning 0) (match-end 0) 'display (org-pretty-table-df-t)) 252 | t) 253 | ((and (eq (following-char) ?-) 254 | (save-excursion 255 | (backward-char 1) 256 | (eq (preceding-char) ?-)) 257 | (save-excursion 258 | (let ((char-pos (- (point) (line-beginning-position) 1))) 259 | (forward-line -1) 260 | (beginning-of-line) 261 | (forward-char char-pos)) 262 | (eq (following-char) ?|)) 263 | (save-excursion 264 | (backward-char 1) 265 | (forward-line) 266 | (not (eq (following-char) ?|)))) 267 | (put-text-property (match-beginning 0) (match-end 0) 'display (org-pretty-table-uf-t)) 268 | t)))))))))) 269 | 270 | (defun org-pretty-table-unpropertize-region (start end) 271 | "Remove box-drawing compositions between START and END." 272 | (remove-text-properties start end '(display))) 273 | 274 | (defun org-pretty-table-unpropertize-table () 275 | "Remove box-drawing compositions from table at point." 276 | (org-pretty-table-unpropertize-region (org-table-begin) (org-table-end))) 277 | 278 | (defun org-pretty-table-align (oldfun &rest args) 279 | (unwind-protect 280 | (progn 281 | (org-pretty-table-mode -1) 282 | (org-pretty-table-unpropertize-table) 283 | (apply oldfun args)) 284 | (org-pretty-table-mode 1))) 285 | 286 | ;;; Minor mode: 287 | 288 | ;;;###autoload 289 | (define-minor-mode org-pretty-table-mode 290 | "Replace org-table characters with box-drawing unicode glyphs." 291 | :lighter " OPT" 292 | (if org-pretty-table-mode 293 | (progn 294 | (jit-lock-register 'org-pretty-table-propertize-region t) 295 | (advice-add 'org-table-align :around #'org-pretty-table-align)) 296 | (jit-lock-unregister 'org-pretty-table-propertize-region) 297 | (advice-remove 'org-table-align #'org-pretty-table-align) 298 | (org-pretty-table-unpropertize-region (point-min) (point-max)))) 299 | 300 | ;;;###autoload 301 | (defun turn-on-org-pretty-table-mode () 302 | "Turn on `org-pretty-table-mode'." 303 | (org-pretty-table-mode 1)) 304 | 305 | ;;;###autoload 306 | (defun turn-off-org-pretty-table-mode () 307 | "Turn off `org-pretty-table-mode'." 308 | (org-pretty-table-mode 0)) 309 | 310 | ;;;###autoload 311 | (define-globalized-minor-mode global-org-pretty-table-mode 312 | org-pretty-table-mode turn-on-org-pretty-table-mode) 313 | 314 | (provide 'org-pretty-table) 315 | ;;; org-pretty-table.el ends here 316 | -------------------------------------------------------------------------------- /tests/test-pretty-table.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t -*- 2 | 3 | (require 'buttercup) 4 | (require 'org-pretty-table) 5 | 6 | (defun opt-assert-display (expected) 7 | (string-match-p expected (get-text-property (point) 'display))) 8 | 9 | (defun opt-search-and-backward (str) 10 | (re-search-forward str) 11 | (backward-char 1)) 12 | 13 | (defmacro with-org-table (&rest body) 14 | (declare (indent 0)) 15 | (let ((prefix (plist-get body :prefix)) 16 | (suffix (plist-get body :suffix))) 17 | `(with-temp-buffer 18 | (org-mode) 19 | (insert "|------+-----| 20 | | name | age | 21 | |------+-----| 22 | | bob | 10 | 23 | |------+-----|") 24 | (when ,prefix 25 | (goto-char (point-min)) 26 | (insert ,prefix)) 27 | (when ,suffix 28 | (goto-char (point-max)) 29 | (insert ,suffix)) 30 | 31 | (org-pretty-table-mode 1) 32 | (jit-lock-refontify) 33 | (jit-lock-fontify-now) 34 | ,@body))) 35 | 36 | (describe "org-pretty-table" 37 | 38 | (before-each 39 | (setq org-pretty-table-charset "┌┐└┘┬┤┴├┼─│")) 40 | 41 | (describe "horizontal bar" 42 | 43 | (it "should not prettify segments not ending in + or |" 44 | (with-org-table 45 | :prefix "|------+-----|\n| ---- | --- |\n" 46 | (goto-char (point-min)) 47 | (forward-line 1) 48 | (re-search-forward "----") 49 | (backward-char 4) 50 | (expect (get-text-property (point) 'display) :to-be nil)))) 51 | 52 | (describe "top row" 53 | 54 | (describe "at beginning of buffer" 55 | 56 | (it "should prettify up-left corner" 57 | (with-org-table 58 | (goto-char (point-min)) 59 | (expect (opt-assert-display "┌") :to-be-truthy))) 60 | 61 | (it "should prettify up-right corner" 62 | (with-org-table 63 | (goto-char (point-min)) 64 | (end-of-line) 65 | (backward-char 1) 66 | (expect (opt-assert-display "┐") :to-be-truthy))) 67 | 68 | (it "should prettify down facing T" 69 | (with-org-table 70 | (goto-char (point-min)) 71 | (opt-search-and-backward "+") 72 | (expect (opt-assert-display "┬") :to-be-truthy)))) 73 | 74 | (describe "with empty line above" 75 | 76 | (it "should prettify up-left corner" 77 | (with-org-table 78 | :prefix "hello world \n\n" 79 | (goto-char (point-min)) 80 | (opt-search-and-backward "|") 81 | (expect (opt-assert-display "┌") :to-be-truthy))) 82 | 83 | (it "should prettify up-right corner" 84 | (with-org-table 85 | :prefix "hello world \n\n" 86 | (goto-char (point-min)) 87 | (re-search-forward "+") 88 | (end-of-line) 89 | (backward-char 1) 90 | (expect (opt-assert-display "┐") :to-be-truthy))) 91 | 92 | (it "should prettify down facing T" 93 | (with-org-table 94 | :prefix "hello world \n\n" 95 | (goto-char (point-min)) 96 | (opt-search-and-backward "+") 97 | (expect (opt-assert-display "┬") :to-be-truthy)))) 98 | 99 | (describe "with content line above" 100 | 101 | (it "should prettify up-left corner" 102 | (with-org-table 103 | :prefix "** headline\n" 104 | (goto-char (point-min)) 105 | (opt-search-and-backward "|") 106 | (expect (opt-assert-display "┌") :to-be-truthy))) 107 | 108 | (it "should prettify up-right corner" 109 | (with-org-table 110 | :prefix "** headline\n" 111 | (goto-char (point-min)) 112 | (re-search-forward "+") 113 | (end-of-line) 114 | (backward-char 1) 115 | (expect (opt-assert-display "┐") :to-be-truthy))) 116 | 117 | (it "should prettify down facing T" 118 | (with-org-table 119 | :prefix "** headline\n" 120 | (goto-char (point-min)) 121 | (opt-search-and-backward "+") 122 | (expect (opt-assert-display "┬") :to-be-truthy))))) 123 | 124 | (describe "middle row" 125 | 126 | (it "should prettify left facing T" 127 | (with-org-table 128 | (goto-char (point-min)) 129 | (re-search-forward "age") 130 | (forward-line 1) 131 | (end-of-line) 132 | (backward-char 1) 133 | (expect (opt-assert-display "┤") :to-be-truthy))) 134 | 135 | (it "should prettify right facing T" 136 | (with-org-table 137 | (goto-char (point-min)) 138 | (re-search-forward "age") 139 | (forward-line 1) 140 | (beginning-of-line) 141 | (expect (opt-assert-display "├") :to-be-truthy))) 142 | 143 | (it "should prettify cross" 144 | (with-org-table 145 | (goto-char (point-min)) 146 | (re-search-forward "+") 147 | (opt-search-and-backward "+") 148 | (expect (opt-assert-display "┼") :to-be-truthy)))) 149 | 150 | (describe "bottom row" 151 | 152 | (describe "at the end of buffer" 153 | 154 | (it "should render bottom left corner" 155 | (with-org-table 156 | (goto-char (point-max)) 157 | (re-search-backward "|") 158 | (beginning-of-line) 159 | (expect (opt-assert-display "└") :to-be-truthy))) 160 | 161 | (it "should render bottom right corner" 162 | (with-org-table 163 | (goto-char (point-max)) 164 | (re-search-backward "|") 165 | (expect (opt-assert-display "┘") :to-be-truthy))) 166 | 167 | (it "should render up facing T" 168 | (with-org-table 169 | (goto-char (point-max)) 170 | (re-search-backward "+") 171 | (expect (opt-assert-display "┴") :to-be-truthy)))) 172 | 173 | (describe "with empty line below" 174 | 175 | (it "should render bottom left corner" 176 | (with-org-table 177 | :suffix "\n\nhello" 178 | (goto-char (point-max)) 179 | (re-search-backward "|") 180 | (beginning-of-line) 181 | (expect (opt-assert-display "└") :to-be-truthy))) 182 | 183 | (it "should render bottom right corner" 184 | (with-org-table 185 | :suffix "\n\nhello" 186 | (goto-char (point-max)) 187 | (re-search-backward "|") 188 | (expect (opt-assert-display "┘") :to-be-truthy))) 189 | 190 | (it "should render up facing T" 191 | (with-org-table 192 | :suffix "\n\nhello" 193 | (goto-char (point-max)) 194 | (re-search-backward "+") 195 | (expect (opt-assert-display "┴") :to-be-truthy)))) 196 | 197 | (describe "with content line below" 198 | 199 | (it "should render bottom left corner" 200 | (with-org-table 201 | :suffix "\n** headline\n" 202 | (goto-char (point-max)) 203 | (re-search-backward "|") 204 | (beginning-of-line) 205 | (expect (opt-assert-display "└") :to-be-truthy))) 206 | 207 | (it "should render bottom right corner" 208 | (with-org-table 209 | :suffix "\n** headline\n" 210 | (goto-char (point-max)) 211 | (re-search-backward "|") 212 | (expect (opt-assert-display "┘") :to-be-truthy))) 213 | 214 | (it "should render up facing T" 215 | (with-org-table 216 | :suffix "\n** headline\n" 217 | (goto-char (point-max)) 218 | (re-search-backward "+") 219 | (expect (opt-assert-display "┴") :to-be-truthy)))))) 220 | --------------------------------------------------------------------------------