Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
[WIP] qmake-mode by SMIE
;;; qmake-mode-el -- Major mode for editing QMAKE files
;; Author: Hyungchan Kim <>
;; Created: 2 Nov 2015
;; Keywords: QMAKE major-mode
;; Copyright (C) 2015 Hyungchan Kim <>
;; 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 2 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
;; 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, write to the Free
;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
;; MA 02111-1307 USA
;;; Commentary:
;;; Code:
(require 'smie)
(defvar qmake-mode-hook nil)
(defvar qmake-mode-map
(let ((qmake-mode-map (make-keymap)))
(define-key qmake-mode-map "\C-j" 'newline-and-indent)
"Keymap for QMAKE major mode.")
(defcustom qmake-indentation 4
"The width for further indentation in qmake mode."
:type 'integer
:group 'qmake-mode)
(put 'qmake-indentation 'safe-local-variable 'integerp)
(defvar qmake-functions-variables
"Qmake function types."
(defvar qmake-variables
"Qmake variables."
(defvar qmake-functions-regexp (regexp-opt qmake-functions-variables 'words))
(defvar qmake-variables-regexp (regexp-opt qmake-variables 'words))
(defvar qmake-font-lock-keywords
'("#.*" . font-lock-comment-face)
`(,qmake-functions-regexp . ,font-lock-function-name-face)
`(,qmake-variables-regexp . ,font-lock-builtin-face))
"Default highlighting expressions for QMAKE mode.")
(defvar qmake-mode-syntax-table
(let ((qmake-mode-syntax-table (make-syntax-table)))
(modify-syntax-entry ?_ "w" qmake-mode-syntax-table)
(modify-syntax-entry ?\n "> b" qmake-mode-syntax-table)
"Syntax table for qmake-mode.")
(defconst qmake-smie-grammar
(exp (var "+=" value)
(var "=" value)
(var "-=" value))
(inst (exp "||" exp)
(exp "&&" exp))
(insts (inst ";" insts)))
'((assoc "+=" "=" "-="))
'((assoc "||" "&&"))
'((assoc ";"))
;; from sh-script.el
(defun qmake-smie--newline-semi-p (&optional tok)
"Return non-nil if a newline should be treated as a semi-colon.
Here we assume that a newline should be treated as a semi-colon unless it
comes right after a special keyword.
This function does not pay attention to line-continuations.
If TOK is nil, point should be before the newline; otherwise, TOK is the token
before the newline and in that case point should be just before the token."
(unless tok
(setq tok (funcall smie-backward-token-function)))
(if (and (zerop (length tok))
(looking-back "\\s(" (1- (point))))
(not (numberp (nth 2 (assoc tok smie-grammar)))))))
(defun qmake-smie--looking-back-at-continuation-p ()
(and (if (eq (char-before) ?\n) (progn (forward-char -1) t) (eolp))
(looking-back "\\(?:^\\|[^\\]\\)\\(?:\\\\\\\\\\)*\\\\"
(defcustom qmake-indent-after-continuation t
"If non-nil, try to make sure text is indented after a line continuation."
:version "24.3"
:type 'boolean
:group 'qmake-indentation)
(defun qmake-smie--continuation-start-indent ()
"Return the initial indentation of a continued line.
May return nil if the line should not be treated as continued."
(forward-line -1)
(unless (qmake-smie--looking-back-at-continuation-p)
(defun qmake-smie-forward-token ()
(if (and (looking-at "[ \t]*\\(?:#\\|\\(\\s|\\)\\|$\\)")
(skip-chars-backward " \t")
(not (bolp))))
(let ((semi (qmake-smie--newline-semi-p)))
(forward-line 1)
(if semi ";"
(forward-comment (point-max))
((looking-at "\\\\\n") (forward-line 1) (qmake-smie-forward-token))
(t (smie-default-forward-token)))))
(defun qmake-smie-backward-token ()
(let ((bol (line-beginning-position))
pos tok)
(forward-comment (- (point)))
((< (point) bol)
(forward-char -1)
(funcall smie-backward-token-function))
((qmake-smie--newline-semi-p) ";")
(t (funcall smie-backward-token-function))))
(t tok))))
(defun qmake-smie-rules (kind token)
(pcase (cons kind token)
(`(:elem . basic) qmake-indentation)
((and `(:before . ,_)
(guard (when qmake-indent-after-continuation
(skip-chars-backward " \t")
;; After a line-continuation, make sure the rest is indented.
(let* ((qmake-indent-after-continuation nil)
(indent (smie-indent-calculate))
(initial (qmake-smie--continuation-start-indent)))
(when (and (numberp indent) (numberp initial)
(<= indent initial))
`(column . ,(+ initial qmake-indentation)))))
(`(:before . ,(or `"(" `"{" `"["))
(if (smie-rule-hanging-p) (smie-rule-parent)))
(define-derived-mode qmake-mode prog-mode "qmake"
"A major mode for qmake."
:syntax-table qmake-mode-syntax-table
(setq-local comment-start "# ")
(setq-local comment-end "")
;; (setq-local comment-start-skip "#+\\s-*")
(setq-local font-lock-defaults
(smie-setup qmake-smie-grammar #'qmake-smie-rules
:forward-token #'qmake-smie-forward-token
:backward-token #'qmake-smie-backward-token)
(run-hooks 'qmake-mode-hook))
(add-to-list 'auto-mode-alist '("\\.pr\\(i\\|o\\|f\\)\\'" . qmake-mode))
(provide 'qmake-mode)
;;; qmake-mode.el ends here
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment