diff --git a/README.md b/README.md index 9c482b1..77511ad 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,6 @@ This mode needs GNU Emacs 24.4. * Better indentation support * Help system * REPL interaction -* imenu support * ETags support * `find-file-at-point` for module names * Electricity (`electric-pair-mode` needs some context-sensitive help) diff --git a/README.tmp-imenu-notes b/README.tmp-imenu-notes new file mode 100644 index 0000000..60d3374 --- /dev/null +++ b/README.tmp-imenu-notes @@ -0,0 +1,53 @@ +major modes: + +The mode should specify how Imenu should find the definitions or +sections of a buffer, by setting up a buffer-local value for the +variable imenu-generic-expression, for the two variables +imenu-prev-index-position-function and +imenu-extract-index-name-function, or for the variable +imenu-create-index-function (see Imenu). + +Macro: defvar-local variable value &optional docstring + + This macro defines variable as a variable with initial value value + and docstring, and marks it as automatically buffer-local. It is + equivalent to calling defvar followed by + make-variable-buffer-local. variable should be an unquoted symbol. + +22.5 Imenu + +Imenu is a feature that lets users select a definition or section in +the buffer, from a menu which lists all of them, to go directly to +that location in the buffer. Imenu works by constructing a buffer +index which lists the names and buffer positions of the definitions, +or other named portions of the buffer; then the user can choose one of +them and move point to it. Major modes can add a menu bar item to use +Imenu using imenu-add-to-menubar. + +Command: imenu-add-to-menubar name + + This function defines a local menu bar item named name to run Imenu. + +The usual and simplest way is to set the variable imenu-generic-expression: + +Variable: imenu-generic-expression + + This variable, if non-nil, is a list that specifies regular + expressions for finding definitions for Imenu. Simple elements of + imenu-generic-expression look like this: + + (menu-title regexp index) + + Here, if menu-title is non-nil, it says that the matches for this + element should go in a submenu of the buffer index; menu-title + itself specifies the name for the submenu. If menu-title is nil, the + matches for this element go directly in the top level of the buffer + index. + + The second item in the list, regexp, is a regular expression (see + Regular Expressions); anything in the buffer that it matches is + considered a definition, something to mention in the buffer + index. + + The third item, index, is a non-negative integer that indicates + which subexpression in regexp matches the definition’s name. diff --git a/nqp-mode.el b/nqp-mode.el new file mode 100644 index 0000000..d5b2d24 --- /dev/null +++ b/nqp-mode.el @@ -0,0 +1,76 @@ +;;; perl6-mode.el --- Major mode for editing Perl 6 code -*- lexical-binding: t; -*- + +;; Copyright (C) 2015 Hinrik Örn Sigurðsson + +;; Author: Hinrik Örn Sigurðsson +;; URL: https://github.com/hinrik/perl6-mode +;; Keywords: languages +;; Version: 0.1-git +;; Package-Requires: ((emacs "24.4") (pkg-info "0.1")) + +;; This file is not part of GNU Emacs. + +;; 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: + +;; GNU Emacs 24 major mode for editing Perl 6 code. + +;; Currently only provides very basic syntax highlighting. + +;;; Code: + +(declare-function pkg-info-version-info "pkg-info" (library)) + +(defgroup perl6 nil + "Major mode for editing Perl 6 code." + :prefix "perl6-" + :group 'language) + +(require 'perl6-detect) +(require 'perl6-font-lock) +(require 'perl6-indent) +(require 'perl6-imenu) + +;;;###autoload +(define-derived-mode perl6-mode prog-mode "Perl6" + "Major mode for editing Perl 6 code." + ;; Syntaxification and font locking + (setq-local syntax-propertize-function #'perl6-syntax-propertize) + (add-hook 'syntax-propertize-extend-region-functions #'syntax-propertize-multiline nil 'local) + (setq-local font-lock-syntactic-face-function #'perl6-font-lock-syntactic-face) + (setq-local font-lock-defaults '(perl6-font-lock-keywords nil nil)) + ;; Add imenu support for perl6-mode. Note that imenu-generic-expression + ;; is buffer-local, so we don't need a local-variable for it. + (add-hook 'perl6-mode-hook 'imenu-add-menubar-index) + (setq imenu-generic-expression perl6-imenu-generic-expression + imenu-case-fold-search nil) + ;; Comments + (setq-local comment-start "#") + (setq-local comment-start-skip "#+ *") + (setq-local comment-use-syntax t) + (setq-local comment-end "") + ;; Indentation + (smie-setup perl6-smie-grammar #'perl6-smie-rules + :forward-token #'perl6-smie--forward-token + :backward-token #'perl6-smie--backward-token)) + +(provide 'perl6-mode) + +;; Local Variables: +;; coding: utf-8 +;; indent-tabs-mode: nil +;; End: + +;;; perl6-mode.el ends here diff --git a/perl6-detect.el b/perl6-detect.el index 575ba64..5753144 100644 --- a/perl6-detect.el +++ b/perl6-detect.el @@ -14,6 +14,7 @@ ;;;###autoload (add-to-list 'auto-mode-alist '("\\.p[lm]?6\\'" . perl6-mode)) +(add-to-list 'auto-mode-alist '("\\.nqp\\'" . perl6-mode)) ;;;###autoload (defconst perl6-magic-pattern diff --git a/perl6-imenu.el b/perl6-imenu.el new file mode 100644 index 0000000..075b634 --- /dev/null +++ b/perl6-imenu.el @@ -0,0 +1,93 @@ +;;; perl6-imenu.el --- Imenu support Perl 6 -*- lexical-binding: t; -*- + +;; Imenu functions and variables are defined here. + +;; Definition of "identifiers" (names) from +;; https://docs.perl6.org/language/syntax#Identifiers +;; +;; Identifiers are a grammatical building block that occur in several +;; places. An identifier is a primitive name, and must start with an +;; alphabetic character (or an underscore), followed by zero or more +;; word characters (alphabetic, underscore or number). You can also +;; embed dashes - or single quotes ' in the middle, but not two in a +;; row, and only if followed immediately by an alphabetic character. +;; +;; For NQP names, no embedded hyphens or single quotes are allowed. + +;; Regex definitions: +(defvar perl6-name-regex + (concat + "[_[:alpha:]]" ; mandatory leading character + "\\(?:[-']?[[:alpha:]]\\|[_[:alnum:]]\\)*" ; rest of the name allowing embedded hyphens or single quotes + )) + +(defvar nqp-name-regex + (concat + "[_[:alpha:]]" ; mandatory leading character + "[_[:alnum:]]*" ; rest of the name (stricter than Perl 6 name) + )) + +(defvar perl6-vars-regex + (concat + "^\\s-*" ; leading ws allowed + "\\(?:my\\|our\\)\\s-+" ; scope of var, followed by mandatory ws + "\\(" ; start capture group 1 for the var name + "\\(?:\\$\\|@\\|%\\)" ; sigil for type of var + "\\(?:" ; start shy group for choice of one type name + perl6-name-regex + "\\|" + nqp-name-regex + "\\)" ; end shy group + "\\)" ; end of capture group 1 + )) + +(defvar perl6-subs-regex + (concat + "^\\s-*" ; leading ws allowed + "\\(?:my\\s-+\\|our\\s-+\\)?" ; optional specific scope followed by at least one space + ; must have one of the five type identifiers + ; followed by at least one space: + "\\(?:multi\\s-+sub\\|multi\\s-+method\\|sub\\|method\\|multi\\|proto\\)\\s-+" + "\\!?" ; optional private marker + "\\(" ; start capture group 1 for the sub name + perl6-name-regex + "\\|" + nqp-name-regex + "\\)" ; end of capture group 1 + )) + +(defvar perl6-classes-regex + (concat + "^\\s-*" ; leading ws allowed + ; must have one of the four type identifiers followed by at least one space: + "class\\s-+" + "\\(" ; start capture group 1 of the class name + perl6-name-regex + "\\|" + nqp-name-regex + "\\)" ; end of capture group 1 + )) + +(defvar perl6-imenu-generic-expression + `( + ;; the names are in reverse desired order since they are evaluated here last first + ("Classes" ,perl6-classes-regex 1) + ("Variables" ,perl6-vars-regex 1) + ("Subs/Methods" ,perl6-subs-regex 1) + ) + "Define interesting points in the Perl 6 buffer for `imenu'. + +This is used to set `imenu-generic-expression' when Perl 6 mode is +entered. Subsequent changes to `perl6-imenu-generic-expression' will +not affect existing Perl 6 buffers because imenu-generic-expression is +a local variable.") + +;;=========================== +(provide 'perl6-imenu) + +;; Local Variables: +;; coding: utf-8 +;; indent-tabs-mode: nil +;; End: + +;;; perl6-imenu.el ends here diff --git a/perl6-mode.el b/perl6-mode.el index 67c5853..d5b2d24 100644 --- a/perl6-mode.el +++ b/perl6-mode.el @@ -41,6 +41,7 @@ (require 'perl6-detect) (require 'perl6-font-lock) (require 'perl6-indent) +(require 'perl6-imenu) ;;;###autoload (define-derived-mode perl6-mode prog-mode "Perl6" @@ -50,6 +51,11 @@ (add-hook 'syntax-propertize-extend-region-functions #'syntax-propertize-multiline nil 'local) (setq-local font-lock-syntactic-face-function #'perl6-font-lock-syntactic-face) (setq-local font-lock-defaults '(perl6-font-lock-keywords nil nil)) + ;; Add imenu support for perl6-mode. Note that imenu-generic-expression + ;; is buffer-local, so we don't need a local-variable for it. + (add-hook 'perl6-mode-hook 'imenu-add-menubar-index) + (setq imenu-generic-expression perl6-imenu-generic-expression + imenu-case-fold-search nil) ;; Comments (setq-local comment-start "#") (setq-local comment-start-skip "#+ *") diff --git a/perl6-unicode-menu.el b/perl6-unicode-menu.el new file mode 100644 index 0000000..b4a87e7 --- /dev/null +++ b/perl6-unicode-menu.el @@ -0,0 +1,30 @@ +;; Provide a menu bar item to ease insertion of Unicode characters. + + +;; Make a menu keymap (with a prompt string) +;; and make it the menu bar item’s definition. +;; put it at the end +(define-key global-map [menu-bar unicode] + (cons "Unicode" (make-sparse-keymap "Unicode"))) +;; Define specific subcommands in this menu. +(define-key global-map + [menu-bar unicode forward] + '("Forward word" . forward-word)) +(define-key global-map + [menu-bar unicode backward] + '("Backward word" . backward-word)) +(defvar menu-bar-final-items '(help-menu unicode-menu)) ;; doesn't work?? + + + + + +;;=========================== +(provide 'perl6-unicode-menu) + +;; Local Variables: +;; coding: utf-8 +;; indent-tabs-mode: nil +;; End: + +;;; perl6-imenu.el ends here diff --git a/test/test-imenu.nqp b/test/test-imenu.nqp new file mode 100644 index 0000000..307bd42 --- /dev/null +++ b/test/test-imenu.nqp @@ -0,0 +1,24 @@ +# file: test-imenu.p6 + +# Perl 6 syntax file for testing perl6-mode with imenu support, which is located at: +# +# https://github.com/tbrowder/perl6-mode [branch: "my-branch"] + +my $a; +my @b; +our %c; + +my $a-a; +my $a'a_3-z; + + sub a(){my @ze} + multi sub x() {} +method d() {} +my multi method z() {} +multi c() {} + +proto xx() {} + +class My-class1 {} +class My-class2{ + class doit () {} diff --git a/test/test-imenu.p6 b/test/test-imenu.p6 new file mode 100644 index 0000000..5ec9e41 --- /dev/null +++ b/test/test-imenu.p6 @@ -0,0 +1,24 @@ +# file: test-imenu.p6 + +# Perl 6 syntax file for testing perl6-mode with imenu support, which is located at: +# +# https://github.com/tbrowder/perl6-mode [branch: "my-branch"] + +my $a; +my @b; +our %c; + +my $a-a; +my $a'a_3-z; + + sub a(){my @ze} + multi sub x() {} +method d() {} +my multi method z() {} +multi c() {} +proto xx() {} +multi method !z-private() {} + +class My-class1 {} +class My-class2{ + class doit () {}