unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Stefan Monnier <monnier@iro.umontreal.ca>
To: mvar <mvar.40k@gmail.com>
Cc: Lars Ingebrigtsen <larsi@gnus.org>,
	39277@debbugs.gnu.org, Hadrien Lacour <hadrien.lacour@posteo.net>
Subject: bug#39277: 26.3; Tcl font lock does not understand quoting
Date: Tue, 27 Oct 2020 18:48:56 -0400	[thread overview]
Message-ID: <jwv5z6vi9z2.fsf-monnier+emacs@gnu.org> (raw)
In-Reply-To: <87sg9ze6yj.fsf@cnu407c2zx.nsn-intra.net> (mvar's message of "Tue, 27 Oct 2020 22:42:44 +0200")

> thank you Lars for reverting, this didn't feel right anyway. I'll try to come up
> with some more elegant solution or at least find some way to skip
> breaking the other locks - for example moving the tcl-font-lock-keywords
> regexp to the end of that list solves the problem Stefan mentioned but it
> still doesn't address what Andreas pointed out, i.e. proc test (args) will
> have args locked as a string.

How 'bout the patch below?


        Stefan


diff --git a/lisp/progmodes/tcl.el b/lisp/progmodes/tcl.el
index 717008a0a2..d4d51e8b50 100644
--- a/lisp/progmodes/tcl.el
+++ b/lisp/progmodes/tcl.el
@@ -407,10 +407,64 @@ tcl-font-lock-keywords
 `tcl-typeword-list', and `tcl-keyword-list' by the function
 `tcl-set-font-lock-keywords'.")
 
+(eval-and-compile
+  (defconst tcl--word-delimiters "[;{ \t\n"))
+
+(defun tcl--syntax-of-quote (pos)
+  "Decide whether a double quote opens a string or not."
+  ;; This is pretty tricky, because strings can be written as "..."
+  ;; or as {...} or without any quoting at all for some simple and not so
+  ;; simple cases (e.g. `abc' but also `a"b').  To make things more
+  ;; interesting, code is represented as strings, so the content of
+  ;; strings can be later re-lexed to find nested strings.
+  (save-excursion
+    (let ((ppss (syntax-ppss pos)))
+      (cond
+       ((nth 8 ppss) nil) ;; Within a string or a comment.
+       ((not (memq (char-before pos)
+                   (cons nil
+                         (eval-when-compile
+                           (mapcar #'identity tcl--word-delimiters)))))
+        ;; The double quote appears within some other lexical entity.
+        ;; FIXME: Similar treatment should be used for `{' which can appear
+        ;; within non-delimited strings (but only at top-level, so
+        ;; maybe it's not worth worrying about).
+        (string-to-syntax "."))
+       ((zerop (nth 0 ppss))
+        ;; Not within a { ... }, so can't be truncated by a }.
+        ;; FIXME: The syntax-table also considers () and [] as paren
+        ;; delimiters just like {}, even though Tcl treats them differently.
+        ;; Tho I'm not sure it's worth worrying about, either.
+        nil)
+       (t
+        ;; A double quote within a {...}: leave it as a normal string
+        ;; delimiter only if we don't find a closing } before we
+        ;; find a closing ".
+        (let ((type nil)
+              (depth 0))
+          (forward-char 1)
+          (while (and (not type)
+                      (re-search-forward "[\"{}\\]" nil t))
+            (pcase (char-after (match-beginning 0))
+              (?\\ (forward-char 1))
+              (?\" (setq type 'matched))
+              (?\{ (cl-incf depth))
+              (?\} (if (zerop depth) (setq type 'unmatched)
+                     (cl-incf depth)))))
+          (when (> (line-beginning-position) pos)
+            ;; The quote is not on the same line as the deciding
+            ;; factor, so make sure we revisit this choice later.
+            (put-text-property pos (point) 'syntax-multiline t))
+          (when (eq type 'unmatched)
+            ;; The quote has no matching close because a } closes the
+            ;; surrounding string before, so it doesn't really "open a string".
+            (string-to-syntax "."))))))))
+
 (defconst tcl-syntax-propertize-function
   (syntax-propertize-rules
    ;; Mark the few `#' that are not comment-markers.
-   ("[^;[{ \t\n][ \t]*\\(#\\)" (1 ".")))
+   ((concat "[^" tcl--word-delimiters "][ \t]*\\(#\\)") (1 "."))
+   ("\"" (0 (tcl--syntax-of-quote (match-beginning 0)))))
   "Syntactic keywords for `tcl-mode'.")
 
 ;; FIXME need some way to recognize variables because array refs look
@@ -593,6 +647,8 @@ tcl-mode
        '(tcl-font-lock-keywords nil nil nil beginning-of-defun))
   (set (make-local-variable 'syntax-propertize-function)
        tcl-syntax-propertize-function)
+  (add-hook 'syntax-propertize-extend-region-functions
+            #'syntax-propertize-multiline 'append 'local)
 
   (set (make-local-variable 'imenu-generic-expression)
        tcl-imenu-generic-expression)
diff --git a/test/manual/indent/tcl.tcl b/test/manual/indent/tcl.tcl
new file mode 100644
index 0000000000..447b64cf1c
--- /dev/null
+++ b/test/manual/indent/tcl.tcl
@@ -0,0 +1,19 @@
+
+puts "hello}"; # Top-level strings can contain unescaped closing braces!
+
+puts a"b;                  # Non-delimited strings can contain quotes!
+puts a""b;                 # Even several of them!
+
+proc foo1 {} {
+    puts "hello";   # Normal case!
+    puts "hello\};  # This will signal an error when `foo1` is called!
+}
+
+proc foo1 {} {
+    puts "hello; # This will also signal an error when `foo1` is called!
+}
+
+proc foo1 {} {
+    puts a"b;                   # This will not signal an error!
+    puts a""b";                 # And that won't either!
+}






  parent reply	other threads:[~2020-10-27 22:48 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-01-25 10:00 bug#39277: 26.3; Tcl font lock does not understand quoting Hadrien Lacour
2020-01-25 10:12 ` bug#39277: tcl-mode " Hadrien Lacour
2020-01-25 10:53 ` Hadrien Lacour
2020-10-26 20:44 ` bug#39277: 26.3; Tcl font lock " mvar
2020-10-27  8:31   ` bug#39277: (no subject) Lars Ingebrigtsen
2020-10-27  8:51   ` bug#39277: 26.3; Tcl font lock does not understand quoting Andreas Schwab
2020-10-27  8:56     ` Lars Ingebrigtsen
2020-10-27 13:27   ` Stefan Monnier
2020-10-27 17:45     ` Lars Ingebrigtsen
2020-10-27 20:42       ` mvar
2020-10-27 20:47         ` Lars Ingebrigtsen
2020-10-27 22:48         ` Stefan Monnier [this message]
2020-10-29 17:39           ` Stefan Monnier
2020-10-30 12:02             ` Lars Ingebrigtsen
2020-10-31 11:01             ` mvar
2020-10-31 13:20               ` Stefan Monnier
2020-11-03 19:47                 ` mvar
2020-11-03 21:45                   ` Stefan Monnier
2020-11-05 12:38                     ` mvar
2020-10-27 15:24 ` bug#39277: Hadrien Lacour

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=jwv5z6vi9z2.fsf-monnier+emacs@gnu.org \
    --to=monnier@iro.umontreal.ca \
    --cc=39277@debbugs.gnu.org \
    --cc=hadrien.lacour@posteo.net \
    --cc=larsi@gnus.org \
    --cc=mvar.40k@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).