Skip to content

Commit 614d6ce

Browse files
committed
hl: Add region-restricted parsing and highlighting
1 parent 3cfab8a commit 614d6ce

File tree

3 files changed

+91
-7
lines changed

3 files changed

+91
-7
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
55

66
## [Unreleased]
7+
- Added support for parsing and highlighting a single region, to enable `jupyter-repl` [integration](https://github.com/emacs-tree-sitter/elisp-tree-sitter/issues/78).
78

89
## [0.18.0] - 2022-02-12
910
- Added APIs to traverse the syntax tree: `tsc-traverse-do`, `tsc-traverse-mapc`, `tsc-traverse-iter`. The traversal is depth-first pre-order.

lisp/tree-sitter-hl.el

+41-6
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,21 @@ See `tree-sitter-hl-add-patterns'."
364364
(setf (map-elt tree-sitter-hl--patterns-alist lang-symbol)
365365
(append (list patterns) (remove patterns old-list))))))
366366

367+
(defun tree-sitter-hl-dry-up-region (beg end)
368+
"Make the highlighting in the region between BEG and END 'permanent'."
369+
(let ((pos beg) next)
370+
(while (< pos end)
371+
;; Determine the end of the current contiguous block...
372+
(setq next (next-single-property-change pos 'face))
373+
;; ... which should be capped to END.
374+
(when (or (null next)
375+
(> next end))
376+
(setq next end))
377+
;; Convert `face' to `font-lock-face'.
378+
(when-let ((face (get-text-property pos 'face)))
379+
(put-text-property pos next 'font-lock-face face))
380+
(setq pos next))))
381+
367382
;;; ----------------------------------------------------------------------------
368383
;;; Internal workings.
369384

@@ -524,12 +539,32 @@ If LOUDLY is non-nil, print debug messages."
524539
"Highlight the region (BEG . END).
525540
526541
This is a wrapper around `tree-sitter-hl--highlight-region' that falls back to
527-
OLD-FONTIFY-FN when the current buffer doesn't have `tree-sitter-hl-mode'
528-
enabled. An example is `jupyter-repl-mode', which copies and uses other major
529-
modes' fontification functions to highlight its input cells. See
530-
https://github.com/emacs-tree-sitter/elisp-tree-sitter/issues/78#issuecomment-1005987817."
531-
(if tree-sitter-hl--query
532-
(tree-sitter-hl--highlight-region beg end loudly)
542+
OLD-FONTIFY-FN in certain situations:
543+
544+
1. When the current buffer doesn't have `tree-sitter-hl-mode' enabled. An
545+
example is `jupyter-repl-mode', which copies and uses other major modes'
546+
fontification functions to highlight its input cells. See
547+
https://github.com/emacs-tree-sitter/elisp-tree-sitter/issues/78#issuecomment-1005987817.
548+
549+
2. When the code to parse and highlight is confined to a single region. The
550+
motivating use case parsing and highlighting the current input cell in
551+
`jupyter-repl-mode'. See `tree-sitter-get-parse-region-function'."
552+
(if (and tree-sitter-hl--query tree-sitter-tree)
553+
(if tree-sitter--parse-region
554+
;; Highlight only the region that `tree-sitter' parsed. Use the
555+
;; underlying fontification function for the rest.
556+
(pcase-let ((`(,code-beg . ,code-end) tree-sitter--parse-region))
557+
(let ((tree-hl-beg (max beg code-beg))
558+
(tree-hl-end (min end code-end)))
559+
(if (<= tree-hl-end tree-hl-beg)
560+
(funcall old-fontify-fn beg end loudly)
561+
(when (< beg tree-hl-beg)
562+
(funcall old-fontify-fn beg tree-hl-beg loudly))
563+
(when (< tree-hl-end end)
564+
(funcall old-fontify-fn tree-hl-end end loudly))
565+
(tree-sitter-hl--highlight-region beg end loudly)
566+
`(jit-lock-bounds ,beg . ,end))))
567+
(tree-sitter-hl--highlight-region beg end loudly))
533568
(funcall old-fontify-fn beg end loudly)))
534569

535570
(defun tree-sitter-hl--invalidate (&optional old-tree)

lisp/tree-sitter.el

+49-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ syntax tree. It is run after `tree-sitter-mode-hook'."
4545

4646
(defcustom tree-sitter-after-on-hook nil
4747
"Functions to call after enabling `tree-sitter-mode'.
48-
Use this to enable other minor modes that depends on the syntax tree."
48+
Use this to enable other minor modes that depend on the syntax tree."
4949
:type 'hook
5050
:group 'tree-sitter)
5151

@@ -64,6 +64,22 @@ Use this to enable other minor modes that depends on the syntax tree."
6464
(defvar-local tree-sitter-language nil
6565
"Tree-sitter language.")
6666

67+
(defvar-local tree-sitter-get-parse-region-function nil
68+
"The function that provides the region to parse.
69+
70+
This is intended to be used by major modes that want to parse code chunks, one
71+
at a time. An example is `jupyter-repl-mode'.
72+
73+
This function should return a (BEG . END) cons cell. If it returns nil, the
74+
whole buffer will be parsed. If there is no code chunk to parse, the major mode
75+
should call `tree-sitter-pause' instead.")
76+
77+
(defvar-local tree-sitter--parse-region nil
78+
"The region that corresponds to the current syntax tree.
79+
Normally this is nil, which means the whole buffer was parsed.
80+
81+
This is non-nil only if `tree-sitter-get-parse-region-function' was set.")
82+
6783
(defvar-local tree-sitter--text-before-change nil)
6884

6985
(defvar-local tree-sitter--beg-before-change nil)
@@ -140,6 +156,19 @@ OLD-LEN is the char length of the old text."
140156
(defun tree-sitter--do-parse ()
141157
"Parse the current buffer and update the syntax tree."
142158
(let ((old-tree tree-sitter-tree))
159+
;; Check and set range restriction (should be provided by the major mode).
160+
(when (functionp tree-sitter-get-parse-region-function)
161+
(when-let ((region (funcall tree-sitter-get-parse-region-function)))
162+
;; TODO: Check validity.
163+
(setq tree-sitter--parse-region region)
164+
(tsc-set-included-ranges
165+
tree-sitter-parser
166+
(pcase-let ((`(,beg . ,end) region))
167+
(tsc--save-context
168+
(vector (vector (position-bytes beg)
169+
(position-bytes end)
170+
(tsc--point-from-position beg)
171+
(tsc--point-from-position end))))))))
143172
(setq tree-sitter-tree
144173
;; https://github.com/emacs-tree-sitter/elisp-tree-sitter/issues/3
145174
(tsc--without-restriction
@@ -180,6 +209,25 @@ signal an error."
180209
(when err
181210
,@error-forms))))
182211

212+
(defun tree-sitter-pause ()
213+
"Pause incremental parsing in the current bufer.
214+
This should only be called by major modes that use
215+
`tree-sitter-get-parse-region-function'.
216+
217+
Functions that depend on the syntax tree will stop working until
218+
`tree-sitter-resume' is called."
219+
(setq tree-sitter-tree nil))
220+
221+
(defun tree-sitter-resume ()
222+
"Resume incremental parsing. If it was paused before, do a full parse first."
223+
(tree-sitter--do-parse))
224+
225+
;;;###autoload
226+
(defun tree-sitter-enable (lang-symbol)
227+
"Turn on `tree-sitter-mode' for LANG-SYMBOL in the current buffer."
228+
(setq tree-sitter-language (tree-sitter-require lang-symbol))
229+
(tree-sitter-mode))
230+
183231
;;;###autoload
184232
(define-minor-mode tree-sitter-mode
185233
"Minor mode that keeps an up-to-date syntax tree using incremental parsing."

0 commit comments

Comments
 (0)