branch: externals/tomelr commit 7d8d41f15b6d5a2d2325160490482b133c56f845 Author: Kaushal Modi <kaushal.m...@gmail.com> Commit: Kaushal Modi <kaushal.m...@gmail.com>
feat: Encode to multi-line TOML string automatically .. if the string has newlines or quote chars. --- README.org | 20 +++++++++++---- test/tscalar.el | 16 ++++++++++++ tomelr.el | 77 ++++++++++++++++++++++++++++++++++++--------------------- 3 files changed, 80 insertions(+), 33 deletions(-) diff --git a/README.org b/README.org index 176979b7ed..6583e6dcca 100644 --- a/README.org +++ b/README.org @@ -23,7 +23,7 @@ specification defined below. - [X] Boolean - [X] Integer - [X] Float - - [ ] String + - [X] String - [ ] Date + Time with Offset - [ ] Date - [ ] Nil @@ -149,15 +149,24 @@ flt7 = 6.626e-34 : "flt6": -0.02, : "flt7": 6.626e-34 : } -*** String +*** DONE String +CLOSED: [2022-04-28 Thu 22:10] https://toml.io/en/v1.0.0#string **** S-expression #+begin_src emacs-lisp :eval no :noweb-ref scalar-string -'((str . "Roses are red\nViolets are blue")) +'((str1 . "Roses are red") + (str2 . "Roses are red\nViolets are blue")) #+end_src **** TOML +#+begin_src emacs-lisp :noweb yes :exports results :wrap src toml +(tomelr-encode + <<scalar-string>>) +#+end_src + +#+RESULTS: #+begin_src toml -str = """ +str1 = "Roses are red" +str2 = """ Roses are red Violets are blue""" #+end_src @@ -169,7 +178,8 @@ Violets are blue""" #+RESULTS: : { -: "str": "Roses are red\nViolets are blue" +: "str1": "Roses are red", +: "str2": "Roses are red\nViolets are blue" : } *** Date + Time with Offset https://toml.io/en/v1.0.0#offset-date-time diff --git a/test/tscalar.el b/test/tscalar.el index a052eff7db..27fd940d00 100644 --- a/test/tscalar.el +++ b/test/tscalar.el @@ -74,5 +74,21 @@ (push (tomelr-encode el) out)) (should (equal ref (nreverse out))))) +;;;; Scalar - String +(ert-deftest test-scalar-string () + (let ((inp '(((string1 . "Roses are red")) + ((string2 . "Roses are red\nViolets are blue")) ;Newline in string + ((string3 . "\"Hello!\"")))) ;Quote in string + (ref '("string1 = \"Roses are red\"" + "string2 = \"\"\" +Roses are red +Violets are blue\"\"\"" + "string3 = \"\"\" +\"Hello!\"\"\"\"")) + out) + (dolist (el inp) + (push (tomelr-encode el) out)) + (should (equal ref (nreverse out))))) + (provide 'tscalar) diff --git a/tomelr.el b/tomelr.el index 805dcdb8a9..342ba01a5d 100644 --- a/tomelr.el +++ b/tomelr.el @@ -130,35 +130,56 @@ Return nil if KEYWORD is not recognized as a TOML keyword." (and keyword (insert keyword)))) ;;;; Strings -(defconst tomelr-special-chars - '((?\" . ?\") - (?\\ . ?\\) - (?b . ?\b) - (?f . ?\f) - (?n . ?\n) - (?r . ?\r) - (?t . ?\t)) - "Characters which are escaped in TOML, with their Elisp counterparts.") - -(defun tomelr--print-string (string &optional from) +(defun tomelr--print-string (string &optional trim-init-chars) "Insert a TOML representation of STRING at point. -FROM is the index of STRING to start from and defaults to 0." - ;; (message "[tomelr--print-string DBG] string = %s" string) - (insert ?\") - (goto-char (prog1 (point) (princ string))) - (and from (delete-char from)) - ;; Escape only quotation mark, backslash, and the control - ;; characters U+0000 to U+001F (RFC 4627, ECMA-404). - (while (re-search-forward (rx (in ?\" ?\\ cntrl)) nil 'move) - (let ((char (preceding-char))) - (delete-char -1) - (insert ?\\ (or - ;; Special TOML character (\n, \r, etc.). - (car (rassq char tomelr-special-chars)) - ;; Fallback: UCS code point in \uNNNN form. - (format "u%04x" char))))) - (insert ?\") - string) + +If TRIM-INIT-CHARS is positive, those many initial characters +of the STRING are not inserted. + +Return the same STRING passed as input." + (let ((special-chars '((?b . ?\b) ;U+0008 + (?f . ?\f) ;U+000C + (?\\ . ?\\))) + special-chars-re + begin-q end-q) + ;; Use multi-line string quotation if the string contains a " char + ;; or a newline. + (if (string-match-p "\n\\|\"" string) + (progn ;Triple quotation """STRING""" + ;; From https://toml.io/en/v1.0.0#string, Any Unicode + ;; character may be used except those that must be escaped: + ;; backslash and the control characters other than tab, line + ;; feed, and carriage return (U+0000 to U+0008, U+000B, + ;; U+000C, U+000E to U+001F, U+007F). + (setq special-chars-re (rx (in ?\\ + (?\u0000 . ?\u0008) + ?\u000B ?\u000C + (?\u000E . ?\u001F) + ?\u007F))) + (setq begin-q "\"\"\"\n") + (setq end-q "\"\"\"")) + (progn ;Basic quotation "STRING" + (setq special-chars-re (rx (in ?\" ?\\ cntrl ?\u007F))) ;cntrl is same as (?\u0000 . ?\u001F) + (push '(?\" . ?\") special-chars) + (push '(?t . ?\t) special-chars) ;U+0009 + (push '(?n . ?\n) special-chars) ;U+000A + (push '(?r . ?\r) special-chars) ;U+000D + (setq begin-q "\"") + (setq end-q begin-q))) + ;; (message "[tomelr--print-string DBG] string = `%s'" string) + (insert begin-q) + (goto-char (prog1 (point) (princ string))) + (and trim-init-chars (delete-char trim-init-chars)) + (while (re-search-forward special-chars-re nil :noerror) + (let ((char (preceding-char))) + (delete-char -1) + (insert ?\\ (or + ;; Escape special characters + (car (rassq char special-chars)) + ;; Fallback: UCS code point in \uNNNN form. + (format "u%04x" char))))) + (insert end-q) + string)) (defun tomelr-encode-string (string) "Return a TOML representation of STRING."