From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Mark Oteiza Newsgroups: gmane.emacs.help Subject: On multi-line font lock Date: Wed, 13 Sep 2017 20:54:18 -0400 Message-ID: <87o9qedrr9.fsf@holos> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: blaine.gmane.org 1505396549 13558 195.159.176.226 (14 Sep 2017 13:42:29 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Thu, 14 Sep 2017 13:42:29 +0000 (UTC) To: help-gnu-emacs@gnu.org Original-X-From: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Thu Sep 14 15:42:23 2017 Return-path: Envelope-to: geh-help-gnu-emacs@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 1dsUPM-0003Lk-W3 for geh-help-gnu-emacs@m.gmane.org; Thu, 14 Sep 2017 15:42:21 +0200 Original-Received: from localhost ([::1]:47864 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dsUPU-0006RS-7h for geh-help-gnu-emacs@m.gmane.org; Thu, 14 Sep 2017 09:42:28 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:41233) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dsIQG-0007yP-0G for help-gnu-emacs@gnu.org; Wed, 13 Sep 2017 20:54:29 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dsIQB-00043e-C7 for help-gnu-emacs@gnu.org; Wed, 13 Sep 2017 20:54:28 -0400 Original-Received: from mail-qt0-x244.google.com ([2607:f8b0:400d:c0d::244]:38468) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1dsIQB-000430-2k for help-gnu-emacs@gnu.org; Wed, 13 Sep 2017 20:54:23 -0400 Original-Received: by mail-qt0-x244.google.com with SMTP id f24so1116142qte.5 for ; Wed, 13 Sep 2017 17:54:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=udel-edu.20150623.gappssmtp.com; s=20150623; h=from:to:subject:date:message-id:mime-version; bh=G0YodTei9bCHm1KlUBNncLBhptqJuTC35Y8mDUvCSko=; b=r8CfQ0szXJ0fMbdCH43oChOyqQR8O+WGTTJ38xCr9n3yTLs/6HjAB3rfoYG3pSqHVO r6Enil/zqahe/HE4+pF3PfQ6+ef8gTHe80zh56/VbSqw4ElGDoSraRB+0kLG8NjVzBYz 5wywVq5YXw4fDSVTauNxGsj8zQ/f5au66bhTx3995mCaNlDH7NRXs4n8+SXkU3pLLWh9 qL5BiXlg/6cjLcegEf20mUhQtisG50GgbaioAhWOLfzMPlpUg4Bn/dlbdRfOvLH2zYte uec8H9KeVqmM+0xBrJLyZKDkTEk6HzqAHEAD2MCS7oHCokCgdrAiyC8gLHSlNy5CAmmh lfnw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:mime-version; bh=G0YodTei9bCHm1KlUBNncLBhptqJuTC35Y8mDUvCSko=; b=BuUHC+GrgyhIMTD5FjZEDJZHuHCUOqHl5BizsDvSJEe9FIIAEpSHFQm41MNgKwW4SN +4wJXwSj0qk+MniKrX1L08PkfItRch7BPX+QP76wUjt0s71lzmeZRwoIgt46Ie6WB/Ce Eey3l7ioGHd19zGPYaU+a/Y9x1hQsmHcPJGGrDG73ujaT+Okpa6Addj7aIfPKCZVKHYk EeYPl5nTSRdZkV9jSuw5tW8xbiZe8bUyw+7yXsS+hQxiUZQ2d58J6Rfw97aI46xnzgOc kO1Q+EoAAH4jN/GcEy+QeVoevgU0rjJpetvx2kMzIdjhcAa02MWHSdpXCvbBX/2lgxlL S16A== X-Gm-Message-State: AHPjjUiUcqWC7Dq0VCRsMAg7dQwfiDTWtdsuvcmjtUjZKiej5dy9KG14 VIERobhWiJYCUS+3r/9RIg== X-Google-Smtp-Source: AOwi7QAVhHZqiokuCuN4JCl835xE+VpZRHbKSjJKMDH/fHcy6fXhFPstgpWbHiAShGAHMyr8u3Twrg== X-Received: by 10.237.53.23 with SMTP id a23mr31293964qte.125.1505350461801; Wed, 13 Sep 2017 17:54:21 -0700 (PDT) Original-Received: from holos.localdomain (pool-173-67-36-61.bltmmd.fios.verizon.net. [173.67.36.61]) by smtp.gmail.com with ESMTPSA id a50sm133873qtc.87.2017.09.13.17.54.19 for (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Wed, 13 Sep 2017 17:54:19 -0700 (PDT) Original-Received: by holos.localdomain (Postfix, from userid 1000) id 033AB69FB0; Wed, 13 Sep 2017 20:54:18 -0400 (EDT) X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2607:f8b0:400d:c0d::244 X-Mailman-Approved-At: Thu, 14 Sep 2017 09:41:40 -0400 X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: Users list for the GNU Emacs text editor List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Original-Sender: "help-gnu-emacs" Xref: news.gmane.org gmane.emacs.help:114276 Archived-At: --=-=-= Content-Type: text/plain Hi, I'm writing to ask about correct use of font-lock-multiline and font-lock-extend-* hooks, and possibly for a working example if possible. I was writing a mode for mailcap files and was struggling to write working elisp for handling multi-line constructs. In mailcap, comments begin with a # at BOL, and lines can be broken by a trailing \ in a non-comment line: application/foobar; baz; copiousoutput; \ # not a comment \ # a comment I discovered an existing mailcap-mode here: http://user42.tuxfamily.org/mailcap-mode/index.html which appears to take advantage of jit-lock-contextually. I also managed to figure out syntax-propertize to a degree, with the help of some blog examples. However, I'm not fond of jit-lock's delay (which is configurable, but defaults are defaults are defaults), so I tried to get the non-jit-lock option to work. The attached package somehow appears to be a working example of using font-lock-extend-region-functions. I'm puzzled because the function I've added to the hook always returns a truthy value AFAICT, and the documentation suggests the hook is run until all of its items return nil. What is the correct way to use font-lock-extend-foo hooks? Please Cc:, thanks. --=-=-= Content-Type: application/emacs-lisp Content-Disposition: attachment; filename=mailcap-mode.el Content-Transfer-Encoding: quoted-printable Content-Description: mailcap-mode package ;;; mailcap-mode.el --- Major mode for editing mailcap files -*- lexical-bi= nding: t -*- ;; Copyright (C) 2017 Mark Oteiza ;; Author: Mark Oteiza ;; Created: 02 September 2017 ;; Keywords: wp, mail, multimedia, ;; This program is free software; you can redistribute it and/or ;; modify it under the terms of the GNU General Public License ;; as published by the Free Software Foundation; either version 3 ;; of the License, or (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; Major mode for editing mailcap files. ;; References: ;; https://tools.ietf.org/html/rfc1524 ;; https://tools.ietf.org/html/rfc1521 ;; https://www.iana.org/assignments/media-types/media-types.xhtml ;; https://www.ch.ic.ac.uk/chemime/ ;;; Code: (require 'conf-mode) (defun mailcap-entry-start-p () "Return non-nil if the current line is the first in a mailcap entry." (let ((flag t)) (save-excursion (while (and (zerop (forward-line -1)) (save-excursion (goto-char (line-end-position)) (eq (preceding-char) ?\\)) (skip-chars-forward " \t") (setq flag (eq (following-char) ?#))))) flag)) (defun mailcap-comment-anchored-matcher (limit) "Matcher for comments, which must be of the form ^#[...]. See https://tools.ietf.org/html/rfc1524 for details." (let ((pos (car (match-data))) res) (when (mailcap-entry-start-p) (goto-char limit) (prog1 (setq res (list pos (point-marker))) (message "%S" res) (set-match-data res))))) (defun mailcap-typefield-anchored-matcher (limit) "Matcher for the MIME type form TYPE/SUBTYPE. See https://tools.ietf.org/html/rfc1521 for details." (let ((pos (car (match-data))) (token "[:alnum:]!#$%&'*+-.^_`{|}~") res) (when (and (mailcap-entry-start-p) (< 0 (skip-chars-forward token limit)) (=3D (following-char) ?\/)) (forward-char 1) (when (and (< 0 (skip-chars-forward token limit)) (skip-chars-forward " \t" limit) (=3D (following-char) ?\;)) (prog1 (setq res (list pos (point-marker))) (set-match-data res)))))) (defun mailcap-limit-field () (when (not (eolp)) (let ((limit (line-end-position))) (save-excursion (save-match-data (when (search-forward ";" limit t 1) (point))))))) (defun mailcap-field-anchored-matcher (limit) "Matcher for the optional field names in the form FIELD[=3DARG]. See https://tools.ietf.org/html/rfc1524 for details." (let ((fields (eval-when-compile (regexp-opt '("compose" "composetyped" "edit" "print" "test" "needsterminal" "copiousoutput" "description" "textualnewlines" "x11-bitmap" "nametemplate") 'symbols))) res) (let ((case-fold-search t)) (setq res (re-search-forward fields limit t 1))) (when (null res) (re-search-forward "x-[[:alnum:]!#$%&'*+-.^_`{|}~]+" limit t 1)) (when res (prog1 (setq res (list (car (match-data)) (point-marker))) (set-match-data res))))) ;; Font lock multiline (defvar font-lock-beg) (defvar font-lock-end) (defun mailcap-entry-start-index () "Return a number for `forward-line' to the beginning of a construct." (let ((count 0)) (save-excursion (while (and (zerop (forward-line -1)) (save-excursion (goto-char (line-end-position)) (eq (preceding-char) ?\\)) (skip-chars-forward " \t") (/=3D (following-char) ?#)) (cl-decf count))) count)) (defun mailcap-entry-end-index () "Return the number for `forward-line' to the end of construct." (let ((count 0)) (save-excursion (while (and (save-excursion (goto-char (line-end-position)) (eq (preceding-char) ?\\)) (skip-chars-forward " \t") (/=3D (following-char) ?#) (zerop (forward-line))) (cl-incf count))) count)) (defun mailcap-font-lock-extend-region () "Function for `font-lock-extend-region-functions'." ;; This always returns non-nil--why does this work? (let (idx1 idx2 changed) (goto-char font-lock-beg) (when (setq idx1 (mailcap-entry-start-index)) (forward-line idx1) (setq font-lock-beg (point) changed t)) (goto-char font-lock-end) (when (setq idx2 (mailcap-entry-end-index)) (forward-line idx2) (setq font-lock-end (line-end-position) changed t)) changed)) (defvar mailcap-mode-syntax-table (let ((table (make-syntax-table))) (modify-syntax-entry ?\\ "/" table) (modify-syntax-entry ?% "'" table) (modify-syntax-entry ?\; "." table) (modify-syntax-entry ?# "<" table) table) "Syntax table used in `mailcap-mode' buffers.") (defconst mailcap-font-lock-keywords-1 '(("^#" (mailcap-comment-anchored-matcher nil nil (0 'font-lock-comment-face))) ("^[aAcCfFeEiImMtTvV]" ; type/subtype; (mailcap-typefield-anchored-matcher nil nil (0 'font-lock-keyword-face= )))) "Minimal keywords to highlight in `mailcap-mode'.") (defconst mailcap-font-lock-keywords-2 `(,@mailcap-font-lock-keywords-1 ("\\\\$" 0 'font-lock-warning-face) ; line break ("%{\\(?:boundary\\|charset\\|media-type\\)}" 0 'font-lock-variable-name-face) ("%[stFn]" 0 'font-lock-constant-face) (";[ \t]*?" (mailcap-field-anchored-matcher (mailcap-limit-field) nil (0 'font-lock-builtin-face)))) "Accurate normal keywords to highlight in `mailcap-mode'.") (defvar mailcap-font-lock-keywords 'mailcap-font-lock-keywords-1 "Default expressions to highlight in `mailcap-mode'.") ;;;###autoload (add-to-list 'auto-mode-alist '("\\.mailcap\\'" . mailcap-mo= de)) (define-derived-mode mailcap-mode conf-mode "Mailcap" "Major mode for editing mailcap files." (set-keymap-parent mailcap-mode-map nil) (conf-mode-initialize "#") (setq-local comment-style 'plain) (add-hook 'font-lock-extend-region-functions 'mailcap-font-lock-extend-region nil t) (setq font-lock-defaults '((mailcap-font-lock-keywords mailcap-font-lock-keywords-1 mailcap-font-lock-keywords-2)))) (provide 'mailcap-mode) ;;; mailcap-mode.el ends here --=-=-=--