From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Stefan Monnier Newsgroups: gmane.emacs.devel Subject: Re: html, css, and js modes working together Date: Thu, 02 Feb 2017 09:19:08 -0500 Message-ID: References: <87o9ynarz3.fsf@tromey.com> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: 8bit X-Trace: blaine.gmane.org 1486045167 21661 195.159.176.226 (2 Feb 2017 14:19:27 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Thu, 2 Feb 2017 14:19:27 +0000 (UTC) User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/26.0.50 (gnu/linux) To: emacs-devel@gnu.org Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Thu Feb 02 15:19:23 2017 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1cZIEM-0005Ru-0H for ged-emacs-devel@m.gmane.org; Thu, 02 Feb 2017 15:19:22 +0100 Original-Received: from localhost ([::1]:56911 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cZIER-0003zE-LP for ged-emacs-devel@m.gmane.org; Thu, 02 Feb 2017 09:19:27 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:58894) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cZIEL-0003z9-U6 for emacs-devel@gnu.org; Thu, 02 Feb 2017 09:19:23 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1cZIEI-00078z-NF for emacs-devel@gnu.org; Thu, 02 Feb 2017 09:19:21 -0500 Original-Received: from [195.159.176.226] (port=51814 helo=blaine.gmane.org) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1cZIEI-00077W-D5 for emacs-devel@gnu.org; Thu, 02 Feb 2017 09:19:18 -0500 Original-Received: from list by blaine.gmane.org with local (Exim 4.84_2) (envelope-from ) id 1cZIEA-00053A-9F for emacs-devel@gnu.org; Thu, 02 Feb 2017 15:19:10 +0100 X-Injected-Via-Gmane: http://gmane.org/ Original-Lines: 154 Original-X-Complaints-To: usenet@blaine.gmane.org Cancel-Lock: sha1:lR/DygiNDwupwQMnxSCGRDY/WEU= X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 195.159.176.226 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.org gmane.emacs.devel:211869 Archived-At: > * I didn't read SMIE deeply enough to see if the indentation parts > relied heavily on the forward/backward-a-token rules. If so then the > new macro will need some additional work. In most major modes, the tokenizer needs to be adjusted (IOW, the default values of smie-*ward-token-functions only exists so it's easier to get a new major mode half-working): a quick "grep '(smie-setup' **/*.el" indicates that only ps-mode relies on the default tokenizer. So yes, it's important for smie-with-rules to set the for/backward-token-function. > - (if (bobp) 0))) > + (if (bobp) (prog-first-column)))) Hmm.. if we want to obey prog-indentation-context, don't we want something like (if (and prog-indentation-context (<= (point) (caadr prog-indentation-context))) (car prog-indentation-context) 0) > +(defmacro with-smie-rules (spec &rest body) > + "Temporarily set up SMIE indentation and evaluate BODY. > +SPEC is of the form (GRAMMAR RULES-FUNCTION); see `smie-setup'. > +BODY is evaluated with the relevant SMIE variables temporarily bound." > + (declare (indent 1)) > + `(let ((smie-grammar ,(car spec)) > + (smie-rules-function ,(cadr spec)) > + (indent-line-function #'smie-indent-line)) > + ,@body)) I think it's likely that the implementation of this macro will change over time, even if it's external appearance doesn't. So I think we should make it expand to a code that calls an smie function that does the actual work. E.g. (defmacro smie-with-rules (spec &rest body) (smie-funcall-with-rules (list ,@spec) (lambda () . ,body))) (defun smie-funcall-with-rules (spec fun) (let ((smie-grammar (car spec)) (smie-rules-function (cadr spec)) (indent-line-function #'smie-indent-line)) (funcall fun))) Then again, maybe we want something even more generic that's not specific to SMIE (see below). > + (t (prog-first-column))))) I suspect here as well, we should obey (caadr prog-indentation-context). > +(defconst html-syntax-propertize-function > + (syntax-propertize-rules > + ("\\(\\(\n\\|.\\)*?\\)" > + (1 > + (prog1 css-mode-syntax-table > + (let ((start (nth 2 (match-data))) > + (end (nth 3 (match-data)))) > + (funcall css-syntax-propertize-function start end) > + (add-text-properties start end '(syntax-multiline t)))))) > + ("\\(\\(\n\\|.\\)*?\\)" > + (1 > + (prog1 js-mode-syntax-table > + (let ((start (nth 2 (match-data))) > + (end (nth 3 (match-data)))) > + (js-syntax-propertize start end) > + (add-text-properties start end '(syntax-multiline t)))))) > + sgml-syntax-propertize-rules) > + "Syntactic keywords for `html-mode'.") As pointed out by Clément, these regexps are a bad idea. Better do something like: (defun html-syntax-propertize (start end) (goto-char start) (when (get-text-property (point) 'html-submode) (html--syntax-propertize-submode (get-text-property (point) 'html-submode) end)) (funcall (syntax-propertize-rules ("" (0 (ignore (goto-char (match-end 0)) (html--syntax-propertize-submode 'css-mode end)))) ("" (0 (ignore (goto-char (match-end 0)) (html--syntax-propertize-submode 'js-mode end))))) (point) end)) > + (cond > + ((equal tag "style") > + ;; CSS. > + (save-restriction > + (let ((base-indent (save-excursion > + (goto-char (sgml-tag-end context)) > + (sgml-calculate-indent)))) > + (narrow-to-region (sgml-tag-end context) (point-max)) > + (let ((prog-indentation-context (list base-indent > + (cons (point-min) nil) > + nil))) > + (with-smie-rules (css-smie-grammar #'css-smie-rules) > + (smie-indent-line)))))) > + ((equal tag "script") > + ;; Javascript. > + (save-restriction > + (let ((base-indent (save-excursion > + (goto-char (sgml-tag-end context)) > + (sgml-calculate-indent)))) > + (narrow-to-region (sgml-tag-end context) (point-max)) > + (let ((prog-indentation-context (list base-indent > + (cons (point-min) nil) > + nil))) > + (js-indent-line))))) > + (t > + ;; HTML. > + (sgml-indent-line))))) How 'bout instead doing something like: (defun html--get-mode-vars (mode) (with-temp-buffer (funcall mode) (buffer-local-variables))) (defconst html--css-vars (html--get-mode-vars 'css-mode)) (defconst html--js-vars (html--get-mode-vars js-mode)) (defun html--funcall-with-mode-var (vars fun) (cl-progv (mapcar #'car vars) (mapcar #'cdr vars) (funcall fun))) and then do ((member tag '("style" "script")) (let* ((start (sgml-tag-end context)) (base-indent (save-excursion (goto-char start) (sgml-calculate-indent))) (prog-indentation-context (list base-indent (cons start nil) nil)) (vars (if (equal tag "style") html--css-vars html--js-vars))) (cl-progv (mapcar #'car vars) (mapcar #'cdr vars) (indent-according-to-mode)))) I'm sure the above will break miserably, because you'll need to tweak the list of vars that really matter (most likely some of the buffer-local-variables should not be in html--*-vars), but such an approach should also help break the cyclic dependencies. Stefan