Skip to content

Commit 5f556e4

Browse files
committed
Improve file watch functionality
- Add file watch warning threshold - limit the usage of file-truename because it is really slow for deeply nested paths.
1 parent b19c850 commit 5f556e4

File tree

1 file changed

+122
-58
lines changed

1 file changed

+122
-58
lines changed

lsp-mode.el

Lines changed: 122 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -553,9 +553,9 @@ If set to `:none' neither of two will be enabled."
553553
The actual trace output at each level depends on the language server in use.
554554
Changes take effect only when a new session is started."
555555
:type '(choice (const :tag "Disabled" "off")
556-
(const :tag "Messages only" "messages")
557-
(const :tag "Verbose" "verbose")
558-
(const :tag "Default (disabled)" nil))
556+
(const :tag "Messages only" "messages")
557+
(const :tag "Verbose" "verbose")
558+
(const :tag "Default (disabled)" nil))
559559
:group 'lsp-mode
560560
:package-version '(lsp-mode . "6.1"))
561561

@@ -684,6 +684,12 @@ must be used for handling a particular message.")
684684
:type 'number
685685
:group 'lsp-mode)
686686

687+
(defcustom lsp-file-watch-threshold 300
688+
"Show warning if the files to watch are more than.
689+
Set to nil to disable the warning."
690+
:type 'number
691+
:group 'lsp-mode)
692+
687693
(defvar lsp-custom-markup-modes
688694
'((rust-mode "no_run" "rust,no_run" "rust,ignore" "rust,should_panic"))
689695
"Mode to uses with markdown code blocks.
@@ -1203,7 +1209,7 @@ DELETE when `lsp-mode.el' is deleted.")
12031209
(equal 'created event-type)
12041210
(not (lsp--string-match-any lsp-file-watch-ignored file-name)))
12051211

1206-
(lsp-watch-root-folder file-name callback watch)
1212+
(lsp-watch-root-folder (file-truename file-name) callback watch)
12071213

12081214
;; process the files that are already present in
12091215
;; the directory.
@@ -1215,33 +1221,82 @@ DELETE when `lsp-mode.el' is deleted.")
12151221
(memq event-type '(created deleted changed)))
12161222
(funcall callback event)))))
12171223

1218-
(defun lsp-watch-root-folder (dir callback &optional watch)
1224+
(defun lsp--directory-files-recursively (dir regexp &optional include-directories)
1225+
"Copy of `directory-files-recursively' but it skips `lsp-file-watch-ignored'."
1226+
(let* ((result nil)
1227+
(files nil)
1228+
(dir (directory-file-name dir))
1229+
;; When DIR is "/", remote file names like "/method:" could
1230+
;; also be offered. We shall suppress them.
1231+
(tramp-mode (and tramp-mode (file-remote-p (expand-file-name dir)))))
1232+
(dolist (file (sort (file-name-all-completions "" dir)
1233+
'string<))
1234+
(unless (member file '("./" "../"))
1235+
(if (and (directory-name-p file)
1236+
(not (lsp--string-match-any lsp-file-watch-ignored (f-join dir (f-filename file)))))
1237+
(let* ((leaf (substring file 0 (1- (length file))))
1238+
(full-file (concat dir "/" leaf)))
1239+
;; Don't follow symlinks to other directories.
1240+
(unless (file-symlink-p full-file)
1241+
(setq result
1242+
(nconc result (lsp--directory-files-recursively
1243+
full-file regexp include-directories))))
1244+
(when (and include-directories
1245+
(string-match regexp leaf))
1246+
(setq result (nconc result (list full-file)))))
1247+
(when (string-match regexp file)
1248+
(push (concat dir "/" file) files)))))
1249+
(nconc result (nreverse files))))
1250+
1251+
(defun lsp-watch-root-folder (dir callback &optional watch warn-big-repo?)
12191252
"Create recursive file notificaton watch in DIR.
12201253
CALLBACK will be called when there are changes in any of
12211254
the monitored files. WATCHES is a hash table directory->file
12221255
notification handle which contains all of the watch that
12231256
already have been created."
1224-
(lsp-log "Creating watch for %s" dir)
1225-
(let ((watch (or watch (make-lsp-watch :root-directory dir))))
1226-
(condition-case err
1227-
(progn
1228-
(puthash
1229-
(file-truename dir)
1230-
(file-notify-add-watch
1231-
dir
1232-
'(change)
1233-
(lambda (event) (lsp--folder-watch-callback event callback watch)))
1234-
(lsp-watch-descriptors watch))
1235-
(seq-do
1236-
(-rpartial #'lsp-watch-root-folder callback watch)
1237-
(seq-filter (lambda (f)
1238-
(and (file-directory-p f)
1239-
(not (gethash (file-truename f) (lsp-watch-descriptors watch)))
1240-
(not (lsp--string-match-any lsp-file-watch-ignored f))
1241-
(not (-contains? '("." "..") (f-filename f)))))
1242-
(directory-files dir t))))
1243-
(error (lsp-log "Failed to create a watch for %s: message" (error-message-string err)))
1244-
(file-missing (lsp-log "Failed to create a watch for %s: message" (error-message-string err))))
1257+
(let* ((dir (if (f-symlink? dir)
1258+
(file-truename dir)
1259+
dir))
1260+
(watch (or watch (make-lsp-watch :root-directory dir))))
1261+
(lsp-log "Creating watch for %s" dir)
1262+
(when (or
1263+
(not warn-big-repo?)
1264+
(not lsp-file-watch-threshold)
1265+
(let ((number-of-files (length (lsp--directory-files-recursively dir ".*" t))))
1266+
(or
1267+
(< number-of-files lsp-file-watch-threshold)
1268+
(condition-case _err
1269+
(yes-or-no-p
1270+
(format
1271+
"There are %s files in folder %s and watching the repo which may slow Emacs down. To configure:
1272+
1. Use `lsp-enable-file-watchers' to disable file watchers globally or for the project(via .dir-local).
1273+
2. Increase/set to nil `lsp-file-watch-threshold' to remove the warning.
1274+
Do you want to continue?"
1275+
number-of-files
1276+
dir))
1277+
('quit)))))
1278+
(condition-case err
1279+
(progn
1280+
(puthash
1281+
dir
1282+
(file-notify-add-watch dir
1283+
'(change)
1284+
(lambda (event)
1285+
(lsp--folder-watch-callback event callback watch)))
1286+
(lsp-watch-descriptors watch))
1287+
(seq-do
1288+
(-rpartial #'lsp-watch-root-folder callback watch)
1289+
(seq-filter (lambda (f)
1290+
(and (file-directory-p f)
1291+
(not (gethash (if (f-symlink? f)
1292+
(file-truename f)
1293+
f)
1294+
(lsp-watch-descriptors watch)))
1295+
(not (lsp--string-match-any lsp-file-watch-ignored f))
1296+
(not (-contains? '("." "..") (f-filename f)))))
1297+
(directory-files dir t))))
1298+
(error (lsp-log "Failed to create a watch for %s: message" (error-message-string err)))
1299+
(file-missing (lsp-log "Failed to create a watch for %s: message" (error-message-string err)))))
12451300
watch))
12461301

12471302
(defun lsp-kill-watch (watch)
@@ -2447,27 +2502,33 @@ disappearing, unset all the variables related to it."
24472502
(defun lsp--file-process-event (session root-folder event)
24482503
"Process file event."
24492504
(let ((changed-file (cl-caddr event)))
2450-
(->> session
2451-
lsp-session-folder->servers
2452-
(gethash root-folder)
2453-
(seq-do (lambda (workspace)
2454-
(when (->> workspace
2455-
lsp--workspace-registered-server-capabilities
2456-
(-any? (lambda (capability)
2457-
(and (string= (lsp--registered-capability-method capability)
2458-
"workspace/didChangeWatchedFiles")
2459-
(->> capability
2460-
lsp--registered-capability-options
2461-
(gethash "watchers")
2462-
(seq-find (-lambda ((&hash "globPattern" glob-pattern))
2463-
(-let [glob-regex (eshell-glob-regexp glob-pattern)]
2464-
(or (string-match glob-regex changed-file)
2465-
(string-match glob-regex (f-relative changed-file root-folder)))))))))))
2466-
(with-lsp-workspace workspace
2467-
(lsp-notify
2468-
"workspace/didChangeWatchedFiles"
2469-
`((changes . [((type . ,(alist-get (cadr event) lsp--file-change-type))
2470-
(uri . ,(lsp--path-to-uri changed-file)))]))))))))))
2505+
(->>
2506+
session
2507+
lsp-session-folder->servers
2508+
(gethash root-folder)
2509+
(seq-do (lambda (workspace)
2510+
(when (->>
2511+
workspace
2512+
lsp--workspace-registered-server-capabilities
2513+
(-any?
2514+
(lambda (capability)
2515+
(and
2516+
(string= (lsp--registered-capability-method capability)
2517+
"workspace/didChangeWatchedFiles")
2518+
(->>
2519+
capability
2520+
lsp--registered-capability-options
2521+
(gethash "watchers")
2522+
(seq-find
2523+
(-lambda ((&hash "globPattern" glob-pattern))
2524+
(-let [glob-regex (eshell-glob-regexp glob-pattern)]
2525+
(or (string-match glob-regex changed-file)
2526+
(string-match glob-regex (f-relative changed-file root-folder)))))))))))
2527+
(with-lsp-workspace workspace
2528+
(lsp-notify
2529+
"workspace/didChangeWatchedFiles"
2530+
`((changes . [((type . ,(alist-get (cadr event) lsp--file-change-type))
2531+
(uri . ,(lsp--path-to-uri changed-file)))]))))))))))
24712532

24722533
(defun lsp--server-register-capability (reg)
24732534
"Register capability REG."
@@ -2476,14 +2537,17 @@ disappearing, unset all the variables related to it."
24762537
(when (and lsp-enable-file-watchers
24772538
(string= method "workspace/didChangeWatchedFiles"))
24782539
(-let* ((created-watches (lsp-session-watches session))
2479-
(root-folders (cl-set-difference (lsp-find-roots-for-workspace lsp--cur-workspace session)
2480-
(ht-keys created-watches))))
2540+
(root-folders (cl-set-difference
2541+
(lsp-find-roots-for-workspace lsp--cur-workspace session)
2542+
(ht-keys created-watches))))
24812543
;; create watch for each root folder withtout such
24822544
(dolist (folder root-folders)
2483-
(puthash folder (lsp-watch-root-folder
2484-
folder
2485-
(-partial #'lsp--file-process-event session folder))
2486-
created-watches))))
2545+
(let ((watch (make-lsp-watch :root-directory folder)))
2546+
(puthash folder watch created-watches)
2547+
(lsp-watch-root-folder (file-truename folder)
2548+
(-partial #'lsp--file-process-event session folder)
2549+
watch
2550+
t)))))
24872551

24882552
(push
24892553
(make-lsp--registered-capability :id id :method method :options registerOptions)
@@ -4036,7 +4100,7 @@ unless overriden by a more specific face association."
40364100
:group 'lsp-faces)
40374101

40384102
(defvar lsp-semantic-highlighting-faces
4039-
'(("^variable\\.parameter\\(\\..*\\)?$" . lsp-face-semhl-variable-parameter)
4103+
'(("^variable\\.parameter\\(\\..*\\)?$" . lsp-face-semhl-variable-parameter)
40404104
("^variable\\.other\\.local\\(\\..*\\)?$" . lsp-face-semhl-variable-local)
40414105
("^variable\\.other\\.field\\.static\\(\\..*\\)?$" . lsp-face-semhl-field-static)
40424106
("^variable\\.other\\.field\\(\\..*\\)?$" . lsp-face-semhl-field)
@@ -4070,10 +4134,10 @@ unless overriden by a more specific face association."
40704134
(when (s-matches-p (car regexp-and-face) scope-name)
40714135
(cdr regexp-and-face)))
40724136
lsp-semantic-highlighting-faces))
4073-
scope-names)))
4137+
scope-names)))
40744138
(unless maybe-face
40754139
(lsp--warn "Unknown scopes [%s], please amend lsp-semantic-highlighting-faces"
4076-
(s-join ", " scope-names)))
4140+
(s-join ", " scope-names)))
40774141
maybe-face))
40784142

40794143
(defvar-local lsp--facemap nil)
@@ -5188,8 +5252,8 @@ SESSION is the active session."
51885252
:rootUri (lsp--path-to-uri root)
51895253
:capabilities (lsp--client-capabilities)
51905254
:initializationOptions initialization-options)
5191-
(when lsp-server-trace
5192-
(list :trace lsp-server-trace))
5255+
(when lsp-server-trace
5256+
(list :trace lsp-server-trace))
51935257
(when (lsp--client-multi-root client)
51945258
(->> workspace-folders
51955259
(-map (lambda (folder)

0 commit comments

Comments
 (0)