Created
November 2, 2015 12:45
-
-
Save inlinechan/2ff5194d06cfcfb34016 to your computer and use it in GitHub Desktop.
[WIP] qmake-mode by SMIE
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;; 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