gnu-emacs-sources
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

n3-mode.el: a major mode for editing Notation3 (and Turtle) data


From: Ivan Shmakov
Subject: n3-mode.el: a major mode for editing Notation3 (and Turtle) data
Date: Thu, 18 Jul 2013 21:50:50 +0000
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/23.4 (gnu/linux)

Archive-name: n3-mode.el-2013-is
Submitted-by: address@hidden
Last-modified: 2013-07-18 21:16:42 +00:00
Copyright-Notice: Both the README and the code are under GNU GPL v3+.

        [Replacing news:alt.sources with news:alt.sources.d within
        Followup-To:, as per the former’s charter.]

README.n3-mode  -*- Text -*-

    Synopsis

        ### foo.n3 — Describing foo in Notation3  -*- N3 -*-

        Load the library with M-x load-file or M-x load-library.

        Add ‘-*- N3 -*-’ to the comment at the topmost line of an N3
        data file (as shown above); use M-x normal-mode RET to turn the
        mode on for the first time.

        Use TAB, C-M-\, M-;, C-j, etc. as needed.


    Summary

        This Emacs major mode provides indentation support for Notation3
        (and thus Turtle) files.  The indentation style is influenced by
        the n3-indent-level (customizable) variable, and follows the
        following general rules:

        • an element immediately (i. e., with no intervening newlines)
          following an opening bracket or parenthesis sets the
          indentation level for its “siblings,” like:

   [       :foo     :bar ;
           :baz  :qux ] .

        • the indentation level is incremented after each “word,” like:

   :foo
       :bar
           :baz
               :qux
                   .

          (even though this example isn’t valid Notation3);

        • the indentation level is decremented by 1 after a comma, and
          by 2 after a semicolon, like:

   :foo
       :bar    :baz ,
           :qux ;
       :hello
           :world .

        • the indentation level of an element immediately after a period
          is assumed to be the same as that of the element immediately
          after the innermost enclosing pair of brackets or parentheses,
          or zero if there’re none.


    Bugs

        The code assumes that a ‘#’ preceded by a whitespace character
        (or beginning of the line) starts a comment; even though it
        doesn’t, should it occur within a URI or a string literal.

        Multiline (or “long”) string literals are not supported.

        The closing brackets and parentheses have the same indentation
        level as the enclosed (child) elements, while the other modes
        generally indent them in line with the sibling elements instead.

        Arguably, an opening curly brace should increment the
        indentation level.  Also, a period within a formula doesn’t seem
        to set the indentation to the one of the preceding “sibling”
        statement.  (This particular issue is not relevant to Turtle.)

        There’s no font-lock-mode (highlighting) support.

        Be sure to check the FIXME: comments within the code itself.

README.n3-mode ends here

;;; n3-mode.el — Major mode for editing Notation3  -*- Emacs-Lisp -*-

;;; Copyright © 2013 Ivan Shmakov

;; Author: Ivan Shmakov
;; Version: 0.1
;; Keywords: languages

;; 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.

;; GNU Emacs 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 GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.

;;; Code:

(defcustom n3-indent-level 4
  "*Indentation of Notation3 statements with respect to containing block."
  :type 'integer
  :group 'n3)

(defvar n3-mode-syntax-table
  (let ((s (make-syntax-table)))
    (modify-syntax-entry ?\n ">   " s)
    (modify-syntax-entry ?\r ">   " s)
    (modify-syntax-entry ?\f ">   " s)
    (modify-syntax-entry ?\# "<   " s)
    (modify-syntax-entry ?\\ "\\" s)
    (modify-syntax-entry ?<  "(>" s)
    (modify-syntax-entry ?>  ")<" s)
    ;; FIXME: shouldn’t ?: be a symbol constituent (_) instead?
    (modify-syntax-entry ?:  "w" s)
    (modify-syntax-entry ?!  "." s)
    (modify-syntax-entry ?^  "." s)
    s))

(defconst n3-token-re
  (concat "\\([.,;]"
          "\\|\\s(\\|\\s)"
          "\\|\\S\\\"\\(?:.*\\S\\\\)?\""
          "\\|\\<\\sw+\\>"
          "\\|\\s<.*?\\s>"
          "\\)")
  "A regular expression for matching Notation3 tokens.")

(defconst n3-token-start-re
  (concat "\\(\"\\|\\s>\\)\\|" n3-token-re)
  "A regular expression for matching either \", \\s>, or an N3 token.")

;;;###autoload
(defun n3-mode ()
  "Major mode for editing Notation3 data.
Turning on Notation3 mode runs the `n3-mode-hook' hook."
  (interactive)
  (kill-all-local-variables)
  ; (use-local-map n3-mode-map)
  (setq major-mode  'n3-mode
        mode-name   "N3")
  (set-syntax-table n3-mode-syntax-table)
  (set  (make-local-variable 'indent-line-function)
        'n3-indent-line)
  (set  (make-local-variable 'comment-start)    "# ")
  ;; FIXME: fails to handle ‘ #’ in strings and URIs properly
  (set  (make-local-variable 'comment-start-skip)
        "\\(^\\|\\s-\\)#+ *")
  (set  (make-local-variable 'comment-end)      "")
  ;; FIXME: with comment-use-syntax set to nil, ‘ #’ within a string
  ;; will be confused for a comment
  (set  (make-local-variable 'comment-use-syntax)       nil)
  ;; however, with comment-use-syntax being non-nil, so will be ‘#’
  ;; within < >; (alas, ‘ #’ within < > will be confused either way)
  ; (set  (make-local-variable 'comment-use-syntax)       t)
  (run-mode-hooks 'n3-mode-hook))

(defun n3-compute-indentation nil
  "Compute the current line indentation according to the syntactic context."
  (save-excursion
    ;; FIXME: assumes no token can contain a newline
    ;; FIXME: thus disallowing multi-line string literals
    (forward-line 0)
    (let (indent
          (depth 0))
      (while (and (not indent)
                  (> (point) (point-min)))
        (if (re-search-backward n3-token-start-re (point-min) t)
            (let* ((c (aref (match-string 0) 0))
                   (s (char-syntax c)))
              ; (message "@ %S (%S %S) %S: %S"
              ;          (point) c (string s) depth (match-string 0))
              (cond ((equal ?> s)
                     ;; skip the comment, if any
                     (let ((p (point)))
                       (forward-line 0)
                       (save-match-data
                         ;; NB: or use comment-search-forward here
                         (unless (re-search-forward comment-start-skip p t)
                           (goto-char p)))))
                    ((or (equal ?\" c) (equal ?\) s))
                     ;; skip this string or URI literal or a group
                     (goto-char (match-end 0))
                     ;; FIXME: really using backward-sexp?
                     (backward-sexp)
                     (setq depth (+ 1 depth)))
                    ((equal ?  s))     ; ignore the whitespace
                    ((equal ?w s)
                     ;; increment the depth
                     (setq depth (+ 1 depth)))
                    ((equal ?\( s)
                     ;; check if this same line has a non-whitespace
                     (save-match-data
                       (when (looking-at "\\(\\s(\\s *\\)\\sw")
                         (goto-char (match-end 1))
                         ;; we can compute the indentation right now
                         (setq indent
                               (+ (current-column)
                                  (* (max 0 depth) n3-indent-level))))))
                    ((equal ?. c)
                     ;; we can compute the indentation right now
                     (setq indent (* (max 0 depth) n3-indent-level)))
                    ((equal ?, c)
                     (setq depth (+ -1 depth)))
                    ((equal ?\; c)
                     (setq depth (+ -2 depth)))))
          ;; no tokens found?
          (setq indent 0)))
      (or indent 0))))

(defun n3-indent-line nil
  "Indent the current line according to the syntactic context."
  (let ((c (n3-compute-indentation)))
    (unless (= (current-indentation) c)
      (save-excursion
        (indent-line-to c)))
    (when (< (current-column) c)
      (forward-to-indentation 0))))

;;; The trailer
;; Local variables:
;; coding: utf-8
;; indent-tabs-mode: nil
;; End:
;;; n3-mode.el ends here

-- 
FSF associate member #7257


reply via email to

[Prev in Thread] Current Thread [Next in Thread]