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!
+}
next prev 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).