From f1a45195076a29e72a117e0f7d05fd8339336b55 Mon Sep 17 00:00:00 2001 From: John Doty Date: Mon, 16 Aug 2021 16:30:19 -0700 Subject: [PATCH] Shrug --- .emacs.d/custom.el | 12 +- site-lisp/fb-glass.el | 105 ++++++++++++ site-lisp/thrift.el | 387 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 498 insertions(+), 6 deletions(-) create mode 100644 site-lisp/fb-glass.el create mode 100644 site-lisp/thrift.el diff --git a/.emacs.d/custom.el b/.emacs.d/custom.el index 5c4ba3a..677c6f2 100644 --- a/.emacs.d/custom.el +++ b/.emacs.d/custom.el @@ -17,9 +17,9 @@ '(comint-prompt-read-only t) '(comint-scroll-to-bottom-on-input t) '(company-backends - '(company-bbdb company-nxml company-css company-semantic company-clang company-cmake company-capf company-files - (company-dabbrev-code company-gtags company-etags company-keywords) - company-oddmuse company-dabbrev company-flow)) + '(company-tasks company-reviewers company-bbdb company-nxml company-css company-capf + (company-dabbrev-code company-keywords))) + '(company-minimum-prefix-length 1) '(css-indent-offset 2) '(custom-safe-themes '("3dbb18bf06f41012d4525e6c64c392d6cfef06a2f8fe1bf7b565c4e020255466" "8db4b03b9ae654d4a57804286eb3e332725c84d7cdab38463cb6b97d5762ad26" default)) @@ -58,7 +58,7 @@ '(org-odd-levels-only t) '(org-todo-keywords '((sequence "TODO" "|" "DONE" "ABANDONED" "DEFERRED"))) '(package-selected-packages - '(prettier-js zig-mode modus-operandi-theme esup gnu-elpa-keyring-update lsp-hack hack-mode rust-mode filladapt lsp-ui yaml-mode wgrep fsharp-mode company-lsp cquery mustache-mode clang-format projectile dash-functional mocha add-node-modules-path rjsx-mode xref-js2 js2-refactor company omnisharp geiser cider clojure-mode graphviz-dot-mode multi-term xterm-color thrift markdown-mode tuareg merlin ag use-package flycheck dockerfile-mode js2-mode web-mode tss switch-window python-mode paredit magit lua-mode go-mode go-autocomplete exec-path-from-shell csharp-mode color-theme-monokai auto-complete auto-complete-nxml flymake flyspell json-mode popup ruby-mode company-jedi tide elm-mode monky)) + '(eglot prettier-js zig-mode modus-operandi-theme esup gnu-elpa-keyring-update lsp-hack hack-mode rust-mode filladapt lsp-ui yaml-mode wgrep fsharp-mode company-lsp cquery mustache-mode clang-format projectile dash-functional mocha add-node-modules-path rjsx-mode xref-js2 js2-refactor company omnisharp geiser cider clojure-mode graphviz-dot-mode multi-term xterm-color thrift markdown-mode tuareg merlin ag use-package flycheck dockerfile-mode js2-mode web-mode tss switch-window python-mode paredit magit lua-mode go-mode go-autocomplete exec-path-from-shell csharp-mode color-theme-monokai auto-complete auto-complete-nxml flymake flyspell json-mode popup ruby-mode company-jedi tide elm-mode monky)) '(reb-re-syntax 'string) '(rmail-mail-new-frame t) '(safe-local-variable-values @@ -84,8 +84,8 @@ '(tab-width 4) '(tags-revert-without-query t) '(tramp-completion-reread-directory-timeout nil) - '(tramp-default-method "sshx" nil (tramp)) - '(tramp-use-ssh-controlmaster-options nil nil (tramp)) + '(tramp-default-method "sshx") + '(tramp-use-ssh-controlmaster-options nil) '(transient-mark-mode t) '(truncate-lines t) '(typescript-indent-level 2) diff --git a/site-lisp/fb-glass.el b/site-lisp/fb-glass.el new file mode 100644 index 0000000..1f9a0a7 --- /dev/null +++ b/site-lisp/fb-glass.el @@ -0,0 +1,105 @@ +;;; fb-glass.el --- Emacs integration for the Glass CLI -*- lexical-binding: t; -*- + +;; Keywords: tools,processes +;; Package-Requires: ((emacs "27.1") (f "1.0")) +;; Version: 0.1 + +;;; Commentary: +;; Integrates `glass` into Emacs. Improvements welcome! +;; Note that Glass is designed more for static/"offline" navigation of code, without needing +;; a full LSP to be running. +;; This integration will return stale results if you try to use it on files that have been modified. + +;;; Code: +(require 'compile) +(require 'f) + +(defun glass--call-process-shell-check-success (command) + (with-temp-buffer + (let ((exit-code (call-process-shell-command command nil (current-buffer)))) + (unless (equal exit-code 0) + (error "Error running shell command")) + (buffer-string)))) + +(defun glass--root (filename) + "Gets the root corresponding to the given filename. If none given, the current buffer's filename is used." + (let ((default-directory (file-name-directory (file-truename filename)))) + (string-trim (glass--call-process-shell-check-success "hg root")))) + +(defun glass--repo (filename) + "Gets the Glean repo ID corresponding to the given filename. If none given, the current buffer's filename is used." + (let ((default-directory (file-name-directory (file-truename filename)))) + (string-trim (glass--call-process-shell-check-success "hg config remotefilelog.reponame")))) + +;;; VISIT SYMBOLS + +(defun glass-visit-symbol (symbol) + "Given a Glass symbol, visit the file that defines that symbol" + (interactive) + (let* ((output (glass--call-process-shell-check-success + (format "glass --caller=emacs describe -t %s" (shell-quote-argument symbol)))) + (position (split-string (car (split-string output)) ":")) + (filename (car position)) + (local-filename (concat "~/" filename))) + (if (file-exists-p local-filename) + (progn + (find-file local-filename) + (goto-char (point-min)) + (forward-line (- (string-to-number (cadr position)) 1)) + (forward-char (- (string-to-number (caddr position)) 1))) + (let* ((buffer (generate-new-buffer (concat "glass--" (file-name-nondirectory filename)))) + (repo (car (split-string filename "/"))) + (exit-code (call-process "scsc" nil buffer nil + "cat" + "--repo" (shell-quote-argument repo) + "--path" (shell-quote-argument (string-remove-prefix repo filename)) + "-B" "master"))) + (if (equal exit-code 0) + (progn + (with-current-buffer buffer + (setq-local buffer-read-only t) + (goto-char (point-min)) + (forward-line (- (string-to-number (cadr position)) 1)) + (forward-char (- (string-to-number (caddr position)) 1))) + (pop-to-buffer buffer)) + (progn + (kill-buffer buffer) + (error "Couldn't display source with `scsc`"))))))) + +;;; LIST SYMBOLS + +(defun glass-list-symbols (&optional filename) + "Lists all symbols within the given filesystem path. If none given, the current buffer's filename is used." + (let* ((file (file-truename (or filename (buffer-file-name)))) + (root (glass--root file)) + (repo (glass--repo file)) + (search-path (concat repo "/" (file-relative-name file root)))) + ;; TODO integrate with myles, myles to get a path -> glass to list its symbols + (compilation-start + (format "glass --caller emacs list-symbols %s | sed -e 's|^|~/|'" search-path)))) + +;;; SYMBOL-SEARCH + +(with-eval-after-load "counsel" + (defun glass--symbol-search (input) + (counsel--async-command + (format "glass --caller=emacs symbol-search %s" + (shell-quote-argument input))) + nil) + + (defun glass-symbol-search-counsel () + "Find a symbol by full symbol ID prefix using `glass symbol-search`" + (interactive) + (counsel-require-program "glass") + (let ((default-directory (myles--root))) + ;; TODO allow entering "/" to grab the current candidate and restart from there + ;; similar to how it works for counsel-find-file + (ivy-read + "symbol prefix: " + #'glass--symbol-search + :caller 'glass-counsel + :dynamic-collection t + :action #'glass-visit-symbol)))) + +(provide 'fb-glass) +;;; fb-glass.el ends here diff --git a/site-lisp/thrift.el b/site-lisp/thrift.el new file mode 100644 index 0000000..4e04621 --- /dev/null +++ b/site-lisp/thrift.el @@ -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