Emit an emacs major mode
With coloring! Next up: formatting but that might be hard.
This commit is contained in:
parent
4941cd049c
commit
d7dfd556ec
2 changed files with 120 additions and 2 deletions
|
|
@ -439,4 +439,4 @@ if __name__ == "__main__":
|
|||
ts_path = Path(__file__).parent / "tree-sitter-fine"
|
||||
emit_tree_sitter_grammar(grammar, ts_path)
|
||||
emit_tree_sitter_queries(grammar, ts_path)
|
||||
emit_emacs_major_mode(grammar, ts_path)
|
||||
emit_emacs_major_mode(grammar, ts_path / "fine.el")
|
||||
|
|
|
|||
120
parser/emacs.py
120
parser/emacs.py
|
|
@ -5,6 +5,13 @@ import pathlib
|
|||
import textwrap
|
||||
|
||||
from parser.tree_sitter import terminal_name
|
||||
from parser.generated_source import (
|
||||
begin_manual_section,
|
||||
end_manual_section,
|
||||
merge_existing,
|
||||
sign_generated_source,
|
||||
signature_token,
|
||||
)
|
||||
|
||||
from . import parser
|
||||
|
||||
|
|
@ -116,5 +123,116 @@ def gather_faces(grammar: parser.Grammar):
|
|||
return feature_string
|
||||
|
||||
|
||||
def emit_emacs_major_mode(grammar: parser.Grammar, path: pathlib.Path | str):
|
||||
def emit_emacs_major_mode(grammar: parser.Grammar, file_path: pathlib.Path | str):
|
||||
if isinstance(file_path, str):
|
||||
file_path = pathlib.Path(file_path)
|
||||
|
||||
face_var = gather_faces(grammar)
|
||||
|
||||
contents = f"""
|
||||
;;; {file_path.name} --- Major mode for editing {grammar.name} --- -*- lexical-binding: t -*-
|
||||
|
||||
;; NOTE: This file is partially generated.
|
||||
;; Only modify marked sections, or your modifications will be lost!
|
||||
;; {signature_token()}
|
||||
|
||||
;; {begin_manual_section('commentary')}
|
||||
|
||||
;; This is free and unencumbered software released into the public domain.
|
||||
;; Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
;; software, either in source code form or as a compiled binary, for any purpose,
|
||||
;; commercial or non-commercial, and by any means.
|
||||
;;
|
||||
;; In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
;; software dedicate any and all copyright interest in the software to the public
|
||||
;; domain. We make this dedication for the benefit of the public at large and to
|
||||
;; the detriment of our heirs and successors. We intend this dedication to be an
|
||||
;; overt act of relinquishment in perpetuity of all present and future rights to
|
||||
;; this software under copyright law.
|
||||
;;
|
||||
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
;; AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
;; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
;;; Commentary:
|
||||
;; (Nobody has written anything about the major mode yet.)
|
||||
|
||||
;; {end_manual_section()}
|
||||
|
||||
;;; Code:
|
||||
(require 'treesit)
|
||||
|
||||
;; {begin_manual_section('prologue')}
|
||||
|
||||
;; {end_manual_section()}
|
||||
|
||||
{face_var}
|
||||
|
||||
(defun {grammar.name}-ts-setup ()
|
||||
"Setup for {grammar.name}-mode."
|
||||
|
||||
;; {begin_manual_section('setup_prologue')}
|
||||
;; {end_manual_section()}
|
||||
|
||||
;; Set up the font-lock rules.
|
||||
(setq-local treesit-font-lock-settings
|
||||
(apply #'treesit-font-lock-rules
|
||||
{grammar.name}-font-lock-rules))
|
||||
|
||||
;; {begin_manual_section('feature_list')}
|
||||
;; NOTE: This list is just to get you started; these are some of the standard
|
||||
;; features and somewhat standard positions in the feature list. You can
|
||||
;; edit this to more closely match your grammar's output. (The info page
|
||||
;; for treesit-font-lock-feature-list describes what it does nicely.)
|
||||
(setq-local treesit-font-lock-feature-list
|
||||
'((comment definition)
|
||||
(keyword string)
|
||||
(assignment attribute builtin constant escape-sequence number type)
|
||||
(bracket delimiter error function operator property variable)))
|
||||
;; {end_manual_section()}
|
||||
|
||||
;; {begin_manual_section('setup_epilogue')}
|
||||
;; If you want to set up more do it here.
|
||||
;; {end_manual_section()}
|
||||
|
||||
(treesit-major-mode-setup))
|
||||
|
||||
;;;###autoload
|
||||
(define-derived-mode {grammar.name}-mode prog-mode "{grammar.name}"
|
||||
"Major mode for editing {grammar.name} files."
|
||||
|
||||
(setq-local font-lock-defaults nil)
|
||||
(when (treesit-ready-p '{grammar.name})
|
||||
(treesit-parser-create '{grammar.name})
|
||||
({grammar.name}-ts-setup)))
|
||||
|
||||
|
||||
;; {begin_manual_section('eplogue')}
|
||||
|
||||
;; {end_manual_section()}
|
||||
;;; {file_path.name} ends here
|
||||
""".lstrip()
|
||||
|
||||
# Sign the contents to give folks a way to check that they haven't been
|
||||
# messed with.
|
||||
contents = sign_generated_source(contents)
|
||||
|
||||
# Try to pull existing file contents out and merge them with the
|
||||
# generated code. This preserves hand-editing in approved areas.
|
||||
try:
|
||||
with open(file_path, "r", encoding="utf-8") as file:
|
||||
existing_contents = file.read()
|
||||
contents = merge_existing(existing_contents, contents)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Ensure that parent directories are created as necessary for the output.
|
||||
if not file_path.parent.exists():
|
||||
file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# And write the file!
|
||||
with open(file_path, "w", encoding="utf-8") as file:
|
||||
file.write(contents)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue