Shrug
This commit is contained in:
parent
3e1c7b565b
commit
f1a4519507
3 changed files with 498 additions and 6 deletions
387
site-lisp/thrift.el
Normal file
387
site-lisp/thrift.el
Normal file
|
|
@ -0,0 +1,387 @@
|
|||
;;; 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue