Search code examples
emacscommentselisp

How to define whole line comment syntax in Emacs?


I want the sequence // to start a comment when it is at the beginning of a line. But inside of a line it should not start any comments.

// this is a comment
This is a URL: http://example.com

Is it possible?


Solution

  • You can do this by writing a syntax-propertize-function

    I have written an example major mode that shows this below. Emacs built in parsing will call your syntax-propertize-function so that it can manually set the syntax-table text property on lines starting with //.

    (define-derived-mode my-syntax-test-mode fundamental-mode
      "A major mode where // denotes a comment but only if it is at the beginning of a line."
      :syntax-table (make-syntax-table) 
      (setq mode-name "my syntax test")
      ;; our mode will use `apply-my-custom-syntax-table-appropriately' to manually set
      ;; the syntax-table text property on lines starting with //"
      (setq syntax-propertize-function 'apply-my-custom-syntax-table-appropriately)
      ;; change `comment-dwim` to handle this type of comments correctly
      (local-set-key [remap comment-dwim] 'my-comment-dwim))
    
    (defvar my-custom-syntax-table
      ;; syntax table where // starts a comment and \n ends it
      (let ((table (make-syntax-table)))
        (modify-syntax-entry ?/ "< 1" table)
        (modify-syntax-entry ?/ "< 2" table)
        (modify-syntax-entry ?\n "> " table)
        table))
    
    (defun apply-my-custom-syntax-table-appropriately (beg end)
      (save-excursion
        (save-restriction
          (widen)
          (goto-char beg)
          ;; for every line between points BEG and END
          (while (and (not (eobp)) (< (point) end))
            (beginning-of-line)
            ;; if it starts with a //
            (when (looking-at "^//")
              ;; remove current syntax-table property
              (remove-text-properties (1- (line-beginning-position))
                                      (1+ (line-end-position))
                                      '(syntax-table))
              ;; set syntax-table property to our custom one
              ;; for the whole line including the beginning and ending newlines
              (add-text-properties (1- (line-beginning-position))
                                   (1+ (line-end-position))
                                   (list 'syntax-table my-custom-syntax-table)))
            (forward-line 1)))))
    
    (defun my-comment-dwim (arg)
      (interactive "*P")
      (require 'newcomment)
      (save-excursion
        (let ((comment-start "//") (comment-end "")
              (comment-column 0)
              ;; don't indent comments
              (comment-style 'plain))
          ;; create the region containing current line if there is no active region
          (unless (use-region-p)
            (end-of-line)
            (push-mark (line-beginning-position))
            (setq mark-active t))
          (comment-dwim nil))))