387 lines
11 KiB
EmacsLisp
387 lines
11 KiB
EmacsLisp
;;; thrift.el --- major mode for fbthrift and Apache Thrift files -*- lexical-binding: t; -*-
|
|
|
|
;; Keywords: languages
|
|
;; Package-Version: 20180905.1050
|
|
;; Package-Requires: ((emacs "24"))
|
|
|
|
;; Licensed to the Apache Software Foundation (ASF) under one
|
|
;; or more contributor license agreements. See the NOTICE file
|
|
;; distributed with this work for additional information
|
|
;; regarding copyright ownership. The ASF licenses this file
|
|
;; to you under the Apache License, Version 2.0 (the
|
|
;; "License"); you may not use this file except in compliance
|
|
;; with the License. You may obtain a copy of the License at
|
|
;;
|
|
;; http://www.apache.org/licenses/LICENSE-2.0
|
|
;;
|
|
;; Unless required by applicable law or agreed to in writing,
|
|
;; software distributed under the License is distributed on an
|
|
;; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
;; KIND, either express or implied. See the License for the
|
|
;; specific language governing permissions and limitations
|
|
;; under the License.
|
|
|
|
;;; Commentary:
|
|
|
|
;; This is a major mode for syntax highlighting .thrift files.
|
|
;;
|
|
;; Features:
|
|
;;
|
|
;; * Precise syntax highlighting based on the thrift parser code.
|
|
;; * Highlighting of method definitions
|
|
;; * Custom face for struct/argument indexes
|
|
;; * Highlighting of doxygen keywords
|
|
;; * Indentation
|
|
;;
|
|
;; This mode works for both fbthrift and Apache Thrift files.
|
|
;;
|
|
;; Hacked by doty for more modern thrift.
|
|
|
|
;;; Code:
|
|
|
|
(defvar thrift-mode-syntax-table
|
|
(let ((table (make-syntax-table)))
|
|
;; Both " and ' are strings.
|
|
(modify-syntax-entry ?\" "\"" table)
|
|
(modify-syntax-entry ?\\ "\\" table)
|
|
(modify-syntax-entry ?' "\"" table)
|
|
|
|
;; Treat < and > as paired delimiters.
|
|
(modify-syntax-entry ?< "(<" table)
|
|
(modify-syntax-entry ?> ")>" table)
|
|
|
|
;; Comments can start with //, /* or # characters.
|
|
(modify-syntax-entry ?/ ". 124" table)
|
|
(modify-syntax-entry ?* ". 23b" table)
|
|
(modify-syntax-entry ?# "<" table)
|
|
(modify-syntax-entry ?\n ">" table)
|
|
|
|
table))
|
|
|
|
(defgroup thrift nil
|
|
"A major mode for editing .thrift files."
|
|
:group 'languages)
|
|
|
|
(defface thrift-ordinal-face
|
|
'((t :foreground "orange"))
|
|
"Face used to highlight Thrift indexes."
|
|
:group 'thrift)
|
|
|
|
(defface thrift-doxygen-key-face
|
|
'((t :foreground "SlateGray"))
|
|
"Face used to highlight @foo in doxygen style comments."
|
|
:group 'thrift)
|
|
|
|
(defun thrift--doxygen-search (pattern limit)
|
|
"Search for PATTERN in /** comments, up to position LIMIT.
|
|
If we find a match, set match-data and put point on the final
|
|
location, and return point. Otherwise, return nil and don't move point.
|
|
|
|
This is intended to be used with `font-lock-keywords'."
|
|
(let (res
|
|
match-data)
|
|
(save-match-data
|
|
;; Search forward for the first @foo..
|
|
(while (and
|
|
(not res)
|
|
(re-search-forward pattern limit t))
|
|
;; Set match data to the @foo we found, before we call `looking-at'.
|
|
(setq match-data (match-data))
|
|
|
|
(let* ((ppss (syntax-ppss))
|
|
(in-comment-p (nth 4 ppss))
|
|
(comment-start (nth 8 ppss))
|
|
in-doxygen-comment-p)
|
|
;; Go to the start of the comment, and confirm this is a /** comment.
|
|
(when in-comment-p
|
|
(save-excursion
|
|
(goto-char comment-start)
|
|
(when (looking-at (rx "/**"))
|
|
(setq in-doxygen-comment-p t))))
|
|
|
|
(when in-doxygen-comment-p
|
|
(setq res (point))))))
|
|
;; Set match data and return point, so Emacs knows we found something.
|
|
(when res
|
|
(set-match-data match-data)
|
|
res)))
|
|
|
|
(defvar thrift-font-lock-keywords
|
|
`((,(regexp-opt
|
|
;; Useful keywords in thriftl.ll.
|
|
'("namespace"
|
|
"cpp_namespace"
|
|
"cpp_include"
|
|
"hs_include"
|
|
"java_package"
|
|
"cocoa_prefix"
|
|
"csharp_namespace"
|
|
"php_namespace"
|
|
"py_module"
|
|
"perl_package"
|
|
"ruby_namespace"
|
|
"smalltalk_category"
|
|
"smalltalk_prefix"
|
|
"include"
|
|
"oneway"
|
|
"typedef"
|
|
"struct"
|
|
"union"
|
|
"exception"
|
|
"extends"
|
|
"throws"
|
|
"service"
|
|
"enum"
|
|
"const"
|
|
"interaction" ;; doty
|
|
;; Deprecated
|
|
"cpp_type"
|
|
"async")
|
|
'symbols)
|
|
. font-lock-keyword-face)
|
|
;; Keywords in thriftl.ll that correspond to types.
|
|
(,(regexp-opt
|
|
'("binary"
|
|
"bool"
|
|
"byte"
|
|
"double"
|
|
"float"
|
|
"hash_map"
|
|
"hash_set"
|
|
"i16"
|
|
"i32"
|
|
"i64"
|
|
"list"
|
|
"map"
|
|
"set"
|
|
"slist"
|
|
"stream"
|
|
"string"
|
|
"void"
|
|
;; Treat optional/required as type keywords, as they occur in that context.
|
|
"optional"
|
|
"required"
|
|
)
|
|
'symbols)
|
|
. font-lock-type-face)
|
|
;; Reserved words in thirftl.ll that don't currently do anything.
|
|
(,(regexp-opt
|
|
'("abstract"
|
|
"and"
|
|
"args"
|
|
"as"
|
|
"assert"
|
|
"auto"
|
|
"break"
|
|
"case"
|
|
"char"
|
|
"class"
|
|
"continue"
|
|
"declare"
|
|
"def"
|
|
"default"
|
|
"del"
|
|
"delete"
|
|
"do"
|
|
"elif"
|
|
"else"
|
|
"elseif"
|
|
"except"
|
|
"exec"
|
|
"extern"
|
|
"finally"
|
|
"for"
|
|
"foreach"
|
|
"function"
|
|
"global"
|
|
"goto"
|
|
"if"
|
|
"implements"
|
|
"import"
|
|
"in"
|
|
"int"
|
|
"inline"
|
|
"instanceof"
|
|
"interface"
|
|
"is"
|
|
"lambda"
|
|
"long"
|
|
"native"
|
|
"new"
|
|
"not"
|
|
"or"
|
|
"pass"
|
|
"public"
|
|
"print"
|
|
"private"
|
|
"protected"
|
|
"raise"
|
|
"register"
|
|
"return"
|
|
"short"
|
|
"signed"
|
|
"sizeof"
|
|
"static"
|
|
"switch"
|
|
"synchronized"
|
|
"template"
|
|
"this"
|
|
"throw"
|
|
"transient"
|
|
"try"
|
|
"unsigned"
|
|
"var"
|
|
"virtual"
|
|
"volatile"
|
|
"while"
|
|
"with"
|
|
"yield"
|
|
"Object"
|
|
"Client"
|
|
"IFace"
|
|
"Processor")
|
|
'symbols)
|
|
. font-lock-type-face)
|
|
(,(regexp-opt
|
|
'("true" "false")
|
|
'symbols)
|
|
. font-lock-constant-face)
|
|
;; Constants are all in upper case, and cannot start with a
|
|
;; digit. We use font-lock-variable-name-face for consistence with
|
|
;; c-mode.
|
|
(,(rx symbol-start
|
|
(any upper "_")
|
|
(+ (any upper "_" digit))
|
|
symbol-end)
|
|
. font-lock-variable-name-face)
|
|
;; Highlight type declarations.
|
|
(,(rx symbol-start
|
|
(or "enum" "service" "struct" "interaction")
|
|
symbol-end
|
|
(+ space)
|
|
symbol-start
|
|
(*
|
|
(seq (syntax word) "."))
|
|
(group (+ (or (syntax word) (syntax symbol))))
|
|
symbol-end)
|
|
1 font-lock-type-face)
|
|
;; Highlight method declarations
|
|
(,(rx symbol-start
|
|
(group (+ (or (syntax word) (syntax symbol))))
|
|
symbol-end
|
|
"(")
|
|
1 font-lock-function-name-face)
|
|
;; Highlight other PascalCase symbols as types.
|
|
(,(rx symbol-start
|
|
(group
|
|
(any upper)
|
|
(+ (or (syntax word) (syntax symbol))))
|
|
symbol-end)
|
|
1 font-lock-type-face)
|
|
|
|
;; Highlight struct indexes.
|
|
(,(rx symbol-start
|
|
(group (+ digit))
|
|
symbol-end
|
|
":")
|
|
1 'thrift-ordinal-face)
|
|
;; Highlight doxygen items that are followed by a symbol, such as '@param foo_bar'.
|
|
(,(lambda (limit)
|
|
(thrift--doxygen-search
|
|
(rx (or "@param" "@throws") symbol-end
|
|
;; Highlight @param even before we've started typing the name of the param.
|
|
(zero-or-one
|
|
(1+ space)
|
|
(1+ (or (syntax word) (syntax symbol) ".")))
|
|
symbol-end)
|
|
limit))
|
|
0 'thrift-doxygen-key-face t)
|
|
;; Highlight standalone doxygen items.
|
|
(,(lambda (limit)
|
|
(thrift--doxygen-search
|
|
(rx (or "@return" "@see") symbol-end)
|
|
limit))
|
|
0 'thrift-doxygen-key-face t)))
|
|
|
|
(defvar thrift-indent-level 2
|
|
"Indentation amount used in Thrift files.")
|
|
|
|
(defun thrift-indent-line ()
|
|
"Indent the current line of Thrift code.
|
|
Preserves point position in the line where possible."
|
|
(interactive)
|
|
(let* ((point-offset (- (current-column) (current-indentation)))
|
|
(ppss (syntax-ppss (line-beginning-position)))
|
|
(paren-depth (nth 0 ppss))
|
|
(current-paren-pos (nth 1 ppss))
|
|
(text-after-paren
|
|
(when current-paren-pos
|
|
(save-excursion
|
|
(goto-char current-paren-pos)
|
|
(buffer-substring
|
|
(1+ current-paren-pos)
|
|
(line-end-position)))))
|
|
(in-multiline-comment-p (nth 4 ppss))
|
|
(current-line (buffer-substring (line-beginning-position) (line-end-position))))
|
|
;; If the current line is just a closing paren, unindent by one level.
|
|
(when (and
|
|
(not in-multiline-comment-p)
|
|
(string-match-p (rx bol (0+ space) (or ")" "}")) current-line))
|
|
(setq paren-depth (1- paren-depth)))
|
|
(cond
|
|
;; In multiline comments, ensure the leading * is indented by one
|
|
;; more space. For example:
|
|
;; /*
|
|
;; * <- this line
|
|
;; */
|
|
(in-multiline-comment-p
|
|
;; Don't modify lines that don't start with *, to avoid changing the indentation of commented-out code.
|
|
(when (or (string-match-p (rx bol (0+ space) "*") current-line)
|
|
(string= "" current-line))
|
|
(indent-line-to (1+ (* thrift-indent-level paren-depth)))))
|
|
;; Indent 'throws' lines by one extra level. For example:
|
|
;; void foo ()
|
|
;; throws (1: FooError fe)
|
|
((string-match-p (rx bol (0+ space) "throws" symbol-end) current-line)
|
|
(indent-line-to (* thrift-indent-level (1+ paren-depth))))
|
|
;; Indent according to the last paren position, if there is text
|
|
;; after the paren. For example:
|
|
;; throws (1: FooError fe,
|
|
;; 2: BarError be, <- this line
|
|
((and
|
|
text-after-paren
|
|
(not (string-match-p (rx bol (0+ space) eol) text-after-paren)))
|
|
(let (open-paren-column)
|
|
(save-excursion
|
|
(goto-char current-paren-pos)
|
|
(setq open-paren-column (current-column)))
|
|
(indent-line-to (1+ open-paren-column))))
|
|
;; Indent parameters by an extra level, so they're visually distinct from 'throws'.
|
|
;; void foo(
|
|
;; 1: int bar) <- this line
|
|
;; throws (1: FooError fe)
|
|
((and current-paren-pos
|
|
(eq (char-after current-paren-pos) ?\())
|
|
(indent-line-to (* thrift-indent-level (1+ paren-depth))))
|
|
;; Indent according to the amount of nesting.
|
|
(t
|
|
(indent-line-to (* thrift-indent-level paren-depth))))
|
|
|
|
;; Point is now at the beginning of indentation, restore it
|
|
;; to its original position (relative to indentation).
|
|
(when (>= point-offset 0)
|
|
(move-to-column (+ (current-indentation) point-offset)))))
|
|
|
|
;;;###autoload
|
|
(add-to-list 'auto-mode-alist '("\\.thrift$" . thrift-mode))
|
|
|
|
;;;###autoload
|
|
(define-derived-mode thrift-mode prog-mode "Thrift"
|
|
"Major mode for editing Thrift files."
|
|
(setq-local font-lock-defaults '(thrift-font-lock-keywords))
|
|
(setq-local indent-line-function 'thrift-indent-line)
|
|
|
|
(setq-local comment-start "// "))
|
|
|
|
(provide 'thrift)
|
|
;;; thrift.el ends here
|