Skip to content

Instantly share code, notes, and snippets.

@inlinechan
Created November 2, 2015 12:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save inlinechan/2ff5194d06cfcfb34016 to your computer and use it in GitHub Desktop.
Save inlinechan/2ff5194d06cfcfb34016 to your computer and use it in GitHub Desktop.
[WIP] qmake-mode by SMIE
;;; qmake-mode-el -- Major mode for editing QMAKE files
;; Author: Hyungchan Kim <inlinechan@gmail.com>
;; Created: 2 Nov 2015
;; Keywords: QMAKE major-mode
;; Copyright (C) 2015 Hyungchan Kim <inlinechan@gmail.com>
;; 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
;; 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, 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)
qmake-mode-map)
"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
'("basename"
"CONFIG"
"contains"
"count"
"dirname"
"error"
"eval"
"exists"
"find"
"for"
"include"
"infile"
"isEmpty"
"join"
"member"
"message"
"prompt"
"qoute"
"sprintf"
"system"
"unique"
"warning"
"else"
"unix"
"win32"
"mac")
"Qmake function types."
)
(defvar qmake-variables
'("CONFIG"
"DEFINES"
"DEF_FILE"
"DEPENDPATH"
"DESTDIR"
"DESTDIR_TARGET"
"DLLDESTDIR"
"DISTFILES"
"DSP_TEMPLATE"
"FORMS"
"FORMS3"
"HEADERS"
"INCLUDEPATH"
"INSTALLS"
"LEXIMPLS"
"LEXOBJECTS"
"LEXSOURCES"
"LIBS"
"LITERAL_HASH"
"MAKEFILE"
"MAKEFILE_GENERATOR"
"MOC_DIR"
"OBJECTS"
"OBJECTS_DIR"
"OBJMOC"
"POST_TARGETDEPS"
"PRE_TARGETDEPS"
"PRECOMPILED_HEADER"
"QMAKE"
"QMAKESPEC"
"QMAKE_APP_FLAG"
"QMAKE_APP_OR_DLL"
"QMAKE_AR_CMD"
"QMAKE_BUNDLE_DATA"
"QMAKE_BUNDLE_EXTENSION"
"QMAKE_CC"
"QMAKE_CFLAGS_DEBUG"
"QMAKE_CFLAGS_MT"
"QMAKE_CFLAGS_MT_DBG"
"QMAKE_CFLAGS_MT_DLL"
"QMAKE_CFLAGS_MT_DLLDBG"
"QMAKE_CFLAGS_RELEASE"
"QMAKE_CFLAGS_SHLIB"
"QMAKE_CFLAGS_THREAD"
"QMAKE_CFLAGS_WARN_OFF"
"QMAKE_CFLAGS_WARN_ON"
"QMAKE_CLEAN"
"QMAKE_CXX"
"QMAKE_CXXFLAGS"
"QMAKE_CXXFLAGS_DEBUG"
"QMAKE_CXXFLAGS_MT"
"QMAKE_CXXFLAGS_MT_DBG"
"QMAKE_CXXFLAGS_MT_DLL"
"QMAKE_CXXFLAGS_MT_DLLDBG"
"QMAKE_CXXFLAGS_RELEASE"
"QMAKE_CXXFLAGS_SHLIB"
"QMAKE_CXXFLAGS_THREAD"
"QMAKE_CXXFLAGS_WARN_OFF"
"QMAKE_CXXFLAGS_WARN_ON"
"QMAKE_EXTENSION_SHLIB"
"QMAKE_EXT_MOC"
"QMAKE_EXT_UI"
"QMAKE_EXT_PRL"
"QMAKE_EXT_LEX"
"QMAKE_EXT_YACC"
"QMAKE_EXT_OBJ"
"QMAKE_EXT_CPP"
"QMAKE_EXT_H"
"QMAKE_FAILED_REQUIREMENTS"
"QMAKE_FILETAGS"
"QMAKE_FRAMEWORK_BUNDLE_NAME"
"QMAKE_FRAMEWORK_VERSION"
"QMAKE_INCDIR"
"QMAKE_INCDIR_OPENGL"
"QMAKE_INCDIR_QT"
"QMAKE_INCDIR_THREAD"
"QMAKE_INCDIR_X11"
"QMAKE_LFLAGS"
"QMAKE_LFLAGS_CONSOLE"
"QMAKE_LFLAGS_CONSOLE_DLL"
"QMAKE_LFLAGS_DEBUG"
"QMAKE_LFLAGS_PLUGIN"
"QMAKE_LFLAGS_QT_DLL"
"QMAKE_LFLAGS_RELEASE"
"QMAKE_LFLAGS_SHAPP"
"QMAKE_LFLAGS_SHLIB"
"QMAKE_LFLAGS_SONAME"
"QMAKE_LFLAGS_THREAD"
"QMAKE_LFLAGS_WINDOWS"
"QMAKE_LFLAGS_WINDOWS_DLL"
"QMAKE_LIBDIR"
"QMAKE_LIBDIR_FLAGS"
"QMAKE_LIBDIR_OPENGL"
"QMAKE_LIBDIR_QT"
"QMAKE_LIBDIR_X11"
"QMAKE_LIBS"
"QMAKE_LIBS_CONSOLE"
"QMAKE_LIBS_OPENGL"
"QMAKE_LIBS_OPENGL_QT"
"QMAKE_LIBS_QT"
"QMAKE_LIBS_QT_DLL"
"QMAKE_LIBS_QT_OPENGL"
"QMAKE_LIBS_QT_THREAD"
"QMAKE_LIBS_RT"
"QMAKE_LIBS_RTMT"
"QMAKE_LIBS_THREAD"
"QMAKE_LIBS_WINDOWS"
"QMAKE_LIBS_X11"
"QMAKE_LIBS_X11SM"
"QMAKE_LIB_FLAG"
"QMAKE_LINK_SHLIB_CMD"
"QMAKE_POST_LINK"
"QMAKE_PRE_LINK"
"QMAKE_LN_SHLIB"
"QMAKE_MAC_SDK"
"QMAKE_MAKEFILE"
"QMAKE_MOC_SRC"
"QMAKE_QMAKE"
"QMAKE_QT_DLL"
"QMAKE_RESOURCE_FLAGS"
"QMAKE_RUN_CC"
"QMAKE_RUN_CC_IMP"
"QMAKE_RUN_CXX"
"QMAKE_RUN_CXX_IMP"
"QMAKE_TARGET"
"QMAKE_UIC"
"QT"
"QTPLUGIN"
"RC_FILE"
"RCC_DIR"
"REQUIRES"
"RES_FILE"
"SOURCES"
"SRCMOC"
"SUBDIRS"
"TARGET"
"TARGET_EXT"
"TARGET_x"
"TARGET_x.y.z"
"TEMPLATE"
"TRANSLATIONS"
"UICIMPLS"
"UICOBJECTS"
"UI_DIR"
"UI_HEADERS_DIR"
"UI_SOURCES_DIR"
"VERSION"
"VER_MAJ"
"VER_MIN"
"VER_PAT"
"VPATH"
"YACCIMPLS"
"YACCOBJECTS"
"YACCSOURCES")
"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
(list
'("#.*" . 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)
qmake-mode-syntax-table)
"Syntax table for qmake-mode.")
(defconst qmake-smie-grammar
(smie-prec2->grammar
(smie-bnf->prec2
'((var)
(value)
(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."
(save-excursion
(unless tok
(setq tok (funcall smie-backward-token-function)))
(if (and (zerop (length tok))
(looking-back "\\s(" (1- (point))))
nil
(not (numberp (nth 2 (assoc tok smie-grammar)))))))
(defun qmake-smie--looking-back-at-continuation-p ()
(save-excursion
(and (if (eq (char-before) ?\n) (progn (forward-char -1) t) (eolp))
(looking-back "\\(?:^\\|[^\\]\\)\\(?:\\\\\\\\\\)*\\\\"
(line-beginning-position)))))
(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."
(save-excursion
(forward-line -1)
(unless (qmake-smie--looking-back-at-continuation-p)
(current-indentation))))
(defun qmake-smie-forward-token ()
(if (and (looking-at "[ \t]*\\(?:#\\|\\(\\s|\\)\\|$\\)")
(save-excursion
(skip-chars-backward " \t")
(not (bolp))))
(let ((semi (qmake-smie--newline-semi-p)))
(forward-line 1)
(if semi ";"
(qmake-smie-forward-token)))
(forward-comment (point-max))
(cond
((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)))
(cond
((< (point) bol)
(cond
((qmake-smie--looking-back-at-continuation-p)
(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
(save-excursion
(ignore-errors
(skip-chars-backward " \t")
(qmake-smie--looking-back-at-continuation-p))))))
;; 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)))
))
;;;###autoload
(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
'(qmake-font-lock-keywords))
(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