with grammar = '((:** "\\*\\*/?" eglot--glob-emit-**)
(:* "\\*" eglot--glob-emit-*)
(:? "\\?" eglot--glob-emit-?)
- (:{} "{[^][*{}]+}" eglot--glob-emit-{})
+ (:{} "{[^{}]+}" eglot--glob-emit-{})
(:range "\\[\\^?[^][/,*{}]+\\]" eglot--glob-emit-range)
(:literal "[^][,*?{}]+" eglot--glob-emit-self))
until (eobp)
(list (cl-gensym "state-") emitter (match-string 0)))
finally (error "Glob '%s' invalid at %s" (buffer-string) (point))))))
+(cl-defun eglot--glob-fsm (states &key (exit 'eobp) noerror)
+ `(cl-labels ,(cl-loop for (this that) on states
+ for (self emit text) = this
+ for next = (or (car that) exit)
+ collect (funcall emit text self next))
+ ,(if noerror
+ `(,(caar states))
+ `(or (,(caar states))
+ (error "Glob done but more unmatched text: '%s'"
+ (buffer-substring (point) (point-max)))))))
+
(defun eglot--glob-compile (glob &optional byte-compile noerror)
"Convert GLOB into Elisp function. Maybe BYTE-COMPILE it.
If NOERROR, return predicate, else erroring function."
- (let* ((states (eglot--glob-parse glob))
+ (let* ((states (eglot--glob-parse glob))
(body `(with-current-buffer (get-buffer-create " *eglot-glob-matcher*")
(erase-buffer)
(save-excursion (insert string))
- (cl-labels ,(cl-loop for (this that) on states
- for (self emit text) = this
- for next = (or (car that) 'eobp)
- collect (funcall emit text self next))
- (or (,(caar states))
- (error "Glob done but more unmatched text: '%s'"
- (buffer-substring (point) (point-max)))))))
+ ,(eglot--glob-fsm states)))
(form `(lambda (string) ,(if noerror `(ignore-errors ,body) body))))
(if byte-compile (byte-compile form) form)))
(defun eglot--glob-emit-{} (arg self next)
(let ((alternatives (split-string (substring arg 1 (1- (length arg))) ",")))
- `(,self ()
- (or (re-search-forward ,(concat "\\=" (regexp-opt alternatives)) nil t)
- (error "Failed matching any of %s" ',alternatives))
- (,next))))
+ (if (cl-notany (lambda (a) (string-match "\\*" a)) alternatives)
+ `(,self ()
+ (or (re-search-forward ,(concat "\\=" (regexp-opt alternatives)) nil t)
+ (error "No alternatives match: %s" ',alternatives))
+ (,next))
+ (let ((fsms (mapcar (lambda (a)
+ `(save-excursion
+ (ignore-errors
+ ,(eglot--glob-fsm (eglot--glob-parse a)
+ :exit next :noerror t))))
+ alternatives)))
+ `(,self ()
+ (or ,@fsms
+ (error "Glob match fail after alternatives %s" ',alternatives)))))))
(defun eglot--glob-emit-range (arg self next)
(when (eq ?! (aref arg 1)) (aset arg 1 ?^))
;; (should (eglot--glob-match "{foo,bar}/**" "foo"))
;; (should (eglot--glob-match "{foo,bar}/**" "bar"))
- ;; VSCode also supports nested blobs. Do we care?
+ ;; VSCode also supports nested blobs. Do we care? Apparently yes:
+ ;; github#1403
;;
- ;; (should (eglot--glob-match "{**/*.d.ts,**/*.js}" "/testing/foo.js"))
- ;; (should (eglot--glob-match "{**/*.d.ts,**/*.js}" "testing/foo.d.ts"))
- ;; (should (eglot--glob-match "{**/*.d.ts,**/*.js,foo.[0-9]}" "foo.5"))
- ;; (should (eglot--glob-match "prefix/{**/*.d.ts,**/*.js,foo.[0-9]}" "prefix/foo.8"))
- )
+ (should (eglot--glob-match "{**/*.d.ts,**/*.js}" "/testing/foo.js"))
+ (should (eglot--glob-match "{**/*.d.ts,**/*.js}" "testing/foo.d.ts"))
+ (should (eglot--glob-match "{**/*.d.ts,**/*.js,foo.[0-9]}" "foo.5"))
+ (should-not (eglot--glob-match "{**/*.d.ts,**/*.js,foo.[0-4]}" "foo.5"))
+ (should (eglot--glob-match "prefix/{**/*.d.ts,**/*.js,foo.[0-9]}"
+ "prefix/foo.8"))
+ (should (eglot--glob-match "prefix/{**/*.js,**/foo.[0-9]}.suffix"
+ "prefix/a/b/c/d/foo.5.suffix"))
+ (should (eglot--glob-match "prefix/{**/*.js,**/foo.[0-9]}.suffix"
+ "prefix/a/b/c/d/foo.js.suffix")))
(defvar tramp-histfile-override)
(defun eglot--call-with-tramp-test (fn)