unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#70036: 30.0.50; Move file-truename to the C level
@ 2024-03-27 19:08 Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-27 19:44 ` Eli Zaretskii
                   ` (2 more replies)
  0 siblings, 3 replies; 28+ messages in thread
From: Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-27 19:08 UTC (permalink / raw)
  To: 70036

[-- Attachment #1: Type: text/plain, Size: 2405 bytes --]



Hi, all!

During the last couple of weeks I've been studying Eglots performance
and have been noticing a couple of things that I find very
interesting. It seems like `file-truename` is in the hot path due to the
fact that every request to the lsp server has to create the source file
location, and in every response we have to parse the location the
relevant file. `file-truename` is used for this, and its performance
isn't really up to snuff for what it provides, afaict.

Below I've supplied some benchmarks and profile reports along with the
actual patch. Before we discuss the patch itself, I want to get some
answers to the following:

 - Is there a reason that this function should be supplied at the lisp
   level?
 - Does it have to be recursive? It seems to eat up a lot of stack, and
   the comments in the file suggest that has been an issue before.

Firstly, I'll show some benchmarks

```
;; Emacs 29 branch

(benchmark-run 10000
  (file-truename "~/Work/some/long/path/to/parse/that/is/very/deep/deep/deep/super/duper/deep/deep.el"))
;; (1.810892642 1 0.051070616)


;; With new C implementation

(benchmark-run 10000
  (file-truename "~/Work/some/long/path/to/parse/that/is/very/deep/deep/deep/super/duper/deep/deep.el"))
;; (0.018811035 0 0.0)
```

As you can see, the C implementation, though naive for now is two orders
of magnitude faster, and makes a noticeable difference when running an
lsp server in emacs.

As for the patch - it now relies on wordexp to resolve the paths, and I
believe there is no real feature parity with the old variant as for now,
but I haven't seen any issues thus far. If this approach is accepted I
will of course make sure we have feature parity, unless that isn't
wanted.

As for the profiles - it is very clear the performance is better with my
version, as it doesn't really show up in the profiles, but in the
current state `file-truename` seems to eat up around 10-20% of the total
samplings.

And lastly - I've noticed that `redisplay_internal (C function)` shows
up as a _huge_ chunk in emacs 30, but not in emacs 29. Is this a known
issue, or something to look out for? I could open a different bug report
for this if needed.

Below are the profiles and the patch. On my system I needed to `ln -s
lisp/loadup.el .` to make it compile. Not sure if that is due to
differences between old and new `file-truename`, or something else.

Thanks,

Theo


[-- Attachment #2: emacs-29-before-everything --]
[-- Type: application/octet-stream, Size: 11650 bytes --]

          65  31% - jsonrpc--process-filter
          61  29%  - jsonrpc-connection-receive
          49  24%   - #<compiled -0xbf50d04bcfd4f4b>
          29  14%    - eglot--hover-info
          29  14%     - mapconcat
          29  14%      - eglot--format-markup
          14   6%       - gfm-view-mode
          10   4%        - byte-code
           4   1%         - require
           1   0%          - defvar
           1   0%           - define-keymap
           1   0%            - keymap-set
           1   0%             - keymap--check
           1   0%                key-valid-p
           6   2%       - font-lock-ensure
           3   1%        - #<compiled -0x19e2d10a955dad87>
           3   1%         - font-lock-fontify-region
           3   1%          - c-font-lock-fontify-region
           3   1%           - font-lock-default-fontify-region
           3   1%            - font-lock-fontify-keywords-region
           2   0%             - c-font-lock-declarations
           2   0%              - c-find-decl-spots
           2   0%               - #<compiled -0xe9c9c536033cf90>
           2   0%                - c-forward-decl-or-cast-1
           2   0%                 - c-forward-type
           2   0%                  - c-forward-name
           1   0%                   - c-forward-<>-arglist
           1   0%                    - c-forward-<>-arglist-recur
           1   0%                     - c-forward-type
           1   0%                        c-forward-keyword-clause
           1   0%             - #<compiled 0xfe79bfcdb7014df>
           1   0%                c-beginning-of-decl-1
           3   1%        - font-lock-set-defaults
           3   1%         - font-lock-compile-keywords
           3   1%          - mapcar
           3   1%           - font-lock-compile-keyword
           3   1%            - eval
           3   1%             - list
           3   1%              - progn
           2   0%               - unless
           2   0%                - if
           2   0%                 - c-face-name-p
           2   0%                  - face-list
           2   0%                     maphash
           4   1%       - java-mode
           3   1%        - c-common-init
           2   0%         - c-basic-common-init
           2   0%          - c-set-style
           2   0%           - mapc
           2   0%            - #<compiled 0x1170c390a66532e1>
           2   0%             - c-set-style-1
           2   0%              - mapcar
           2   0%                 #<compiled 0x1892a0c8da397cc8>
           1   0%         - c-font-lock-init
           1   0%          - mapcar
           1   0%             c-mode-symbol
           1   0%        - c-init-language-vars-for
           1   0%         - c-make-keywords-re
           1   0%            regexp-opt
          20   9%    - #<compiled 0x138508d9b11649a6>
          20   9%     - #<compiled 0x3532fdd8350189a>
          20   9%      - run-hook-with-args
          20   9%       - eldoc-display-in-echo-area
          18   8%        - eldoc--message
          18   8%         - eldoc-minibuffer-message
          18   8%          - apply
          13   6%             message
           2   0%        - eldoc--echo-area-substring
           2   0%           substitute-command-keys
          10   4%   - jsonrpc--log-event
           8   3%    - pp-to-string
           8   3%     - pp-buffer
           5   2%      - down-list
           5   2%         syntax-ppss
           2   0%      - indent-sexp
           2   0%       - lisp-indent-calc-next
           1   0%          calculate-lisp-indent
           1   0%        up-list
           1   0%    - jsonrpc--events-buffer-scrollback-size
           1   0%     - apply
           1   0%        #<compiled -0x455ec6e2c68d407>
           1   0%   - #<compiled -0x22ca92508a0e1dc>
           1   0%    - mapcar
           1   0%     - #<compiled 0xda6c796629db7f7>
           1   0%      - eglot--range-region
           1   0%         eglot--lsp-position-to-point
           2   0%  - #<compiled -0x1c74af7641681def>
           2   0%   - kill-buffer
           2   0%      replace-buffer-in-windows
           1   0%    generate-new-buffer
          60  29% - command-execute
          60  29%  - call-interactively
          59  28%   - byte-code
          59  28%    - read-extended-command
          59  28%     - read-extended-command-1
          59  28%      - completing-read
          59  28%       - completing-read-default
          38  18%        - read-from-minibuffer
          13   6%         - command-execute
           9   4%          - call-interactively
           9   4%           - funcall-interactively
           9   4%            - minibuffer-complete-and-exit
           9   4%             - completion-complete-and-exit
           9   4%              - completion--complete-and-exit
           8   3%               - try-completion
           8   3%                - #<compiled 0x1bf3e3a951e865d9>
           8   3%                   complete-with-action
           1   0%                 test-completion
           1   0%         - redisplay_internal (C function)
           1   0%          - eval
           1   0%             flymake--mode-line-counter
           1   0%   - funcall-interactively
           1   0%    - execute-extended-command
           1   0%     - command-execute
           1   0%      - call-interactively
           1   0%       - funcall-interactively
           1   0%          profiler-stop
          55  26% - timer-event-handler
          55  26%  - apply
          51  25%   - #<compiled -0xdafdfebeede7b62>
          51  25%    - eldoc-print-current-symbol-info
          51  25%     - eldoc--invoke-strategy
          51  25%      - eldoc-documentation-compose
          50  24%       - eldoc--documentation-compose-1
          50  24%        - run-hook-wrapped
          50  24%         - #<compiled 0xf55a170ff80dfc9>
          25  12%          - eglot-signature-eldoc-function
          18   8%           - eglot--TextDocumentPositionParams
          18   8%            - eglot--TextDocumentIdentifier
          18   8%             - eglot--path-to-uri
          15   7%              - file-truename
          14   6%               - file-truename
          14   6%                - file-truename
          11   5%                 - file-truename
          11   5%                  - file-truename
          11   5%                   - file-truename
          10   4%                    - file-truename
          10   4%                     - file-truename
           8   3%                      - file-truename
           8   3%                       - file-truename
           8   3%                        - file-truename
           5   2%                         - file-truename
           3   1%                          - file-truename
           2   0%                           - file-truename
           1   0%                              file-truename
           1   0%                url-hexify-string
           1   0%              - file-local-name
           1   0%                 file-remote-p
           7   3%           - jsonrpc-async-request
           7   3%            - apply
           7   3%             - jsonrpc--async-request-1
           7   3%              - jsonrpc-connection-send
           7   3%               - apply
           7   3%                - #<compiled -0x4abc9b84fa7c2a4>
           6   2%                 - jsonrpc--log-event
           6   2%                  - pp-to-string
           4   1%                   - pp-buffer
           3   1%                    - down-list
           2   0%                       syntax-ppss
           1   0%                    - indent-sexp
           1   0%                       lisp-indent-calc-next
          25  12%          - eglot-hover-eldoc-function
          11   5%           - eglot--highlight-piggyback
           7   3%            - eglot--TextDocumentPositionParams
           7   3%             - eglot--TextDocumentIdentifier
           7   3%              - eglot--path-to-uri
           6   2%               - file-truename
           5   2%                - file-truename
           4   1%                 - file-truename
           4   1%                  - file-truename
           4   1%                   - file-truename
           3   1%                    - file-truename
           3   1%                     - file-truename
           3   1%                      - file-truename
           2   0%                       - file-truename
           2   0%                        - file-truename
           1   0%                           file-truename
           4   1%            - jsonrpc-async-request
           4   1%             - apply
           4   1%              - jsonrpc--async-request-1
           2   0%               - jsonrpc-connection-send
           2   0%                - apply
           2   0%                 - #<compiled -0x4abc9b84fa7c2a4>
           2   0%                  - jsonrpc--log-event
           2   0%                   - pp-to-string
           1   0%                      pp-buffer
           1   0%               - #<compiled 0x1547f373ce8e51b2>
           1   0%                - run-with-timer
           1   0%                 - apply
           1   0%                  - run-at-time
           1   0%                     timer-relative-time
           1   0%               - jsonrpc--next-request-id
           1   0%                - apply
           1   0%                   #<compiled -0xdff803ba46c02e5>
          10   4%           - eglot--TextDocumentPositionParams
          10   4%            - eglot--TextDocumentIdentifier
          10   4%             - eglot--path-to-uri
           8   3%              - file-truename
           8   3%               - file-truename
           8   3%                - file-truename
           8   3%                 - file-truename
           7   3%                  - file-truename
           7   3%                   - file-truename
           6   2%                    - file-truename
           4   1%                     - file-truename
           3   1%                      - file-truename
           1   0%                         file-truename
           2   0%              - url-hexify-string
           2   0%               - mapconcat
           1   0%                  #<compiled 0x1e6ca0cb85a458bc>
           4   1%           - jsonrpc-async-request
           4   1%            - apply
           4   1%             - jsonrpc--async-request-1
           4   1%              - jsonrpc-connection-send
           4   1%               - apply
           4   1%                - #<compiled -0x4abc9b84fa7c2a4>
           3   1%                 - jsonrpc--log-event
           3   1%                  - pp-to-string
           2   0%                   - pp-buffer
           2   0%                    - indent-sexp
           1   0%                       indent-line-to
           1   0%                       lisp-indent-calc-next
           1   0%                   jsonrpc--json-encode
           3   1%     #<compiled 0x12c6d7b54a1d6d26>
           1   0%   - show-paren-function
           1   0%    - show-paren--default
           1   0%       syntax-ppss
          21  10% - eldoc-pre-command-refresh-echo-area
          21  10%  - eldoc--message
          21  10%   - eldoc-minibuffer-message
          21  10%      apply
           3   1% - redisplay_internal (C function)
           3   1%  - eval
           1   0%     flymake--mode-line-counter
           0   0% + ...

[-- Attachment #3: emacs-30-before --]
[-- Type: application/octet-stream, Size: 16375 bytes --]

         209  43% - timer-event-handler
         209  43%  - apply
         187  39%   - #<compiled-function 978>
         179  37%    - jsonrpc-connection-receive
         104  21%     - #<compiled-function F7B>
         104  21%      - apply
         104  21%       - eglot-handle-notification
         104  21%        - apply
         104  21%         - #<compiled-function 604>
          90  18%          - find-buffer-visiting
          73  15%           - file-truename
          61  12%            - file-truename
          58  12%             - file-truename
          51  10%              - file-truename
          46   9%               - file-truename
          43   9%                - file-truename
          41   8%                 - file-truename
          36   7%                  - file-truename
          34   7%                   - file-truename
          28   5%                    - file-truename
          22   4%                     - file-truename
          15   3%                      - file-truename
          10   2%                       - file-truename
           5   1%                        - file-truename
           1   0%                           file-truename
           5   1%             abbreviate-file-name
           8   1%          - eglot-uri-to-path
           7   1%           - url-generic-parse-url
           2   0%            - #<compiled-function 548>
           2   0%               kill-buffer
           1   0%              match-string
           1   0%           - eglot--trampish-p
           1   0%            - project-root
           1   0%             - apply
           1   0%              - #<compiled-function 5D8>
           1   0%                 gethash
           5   1%            eglot--make-diag
           1   0%            expand-file-name
          65  13%     - jsonrpc--continue
          63  13%      - #<compiled-function A70>
          56  11%       - eglot--hover-info
          56  11%        - eglot--format-markup
          40   8%         - gfm-view-mode
          13   2%          - defalias
           8   1%           - file-name-sans-extension
           1   0%              file-name-sans-versions
           9   1%          - byte-code
           8   1%           - require
           2   0%            - defalias
           2   0%               file-name-sans-extension
           1   0%            - custom-declare-face
           1   0%             - face-spec-set
           1   0%              - face-spec-recalc
           1   0%               - face-attribute
           1   0%                  face-attribute-relative-p
           1   0%           - custom-declare-variable
           1   0%            - custom-initialize-reset
           1   0%             - eval
           1   0%              - funcall
           1   0%               - #<compiled-function DF3>
           1   0%                - executable-find
           1   0%                   locate-file
           4   0%            require
           4   0%          - read-only-mode
           4   0%           - view-mode-enter
           1   0%            - defalias
           1   0%               file-name-sans-extension
           3   0%            read
           3   0%          - gfm-mode
           3   0%           - markdown-mode
           2   0%            - syntax-propertize
           2   0%             - markdown-syntax-propertize
           1   0%              - markdown-syntax-propertize-list-items
           1   0%               - markdown--cur-list-item-bounds
           1   0%                - markdown-cur-list-item-end
           1   0%                 - markdown-prev-line-blank
           1   0%                    looking-at
           1   0%              - markdown-syntax-propertize-pre-blocks
           1   0%                 format
           1   0%              add-hook
           1   0%          - custom-declare-face
           1   0%           - face-spec-set
           1   0%            - make-empty-face
           1   0%               make-face
          13   2%         - font-lock-ensure
          13   2%          - #<compiled-function 46D>
          13   2%           - font-lock-fontify-region
          13   2%            - font-lock-default-fontify-region
           8   1%             - font-lock-fontify-keywords-region
           2   0%              - markdown-fontify-inline-links
           2   0%               - markdown-match-generic-links
           1   0%                - markdown-range-property-any
           1   0%                   number-sequence
           1   0%                  re-search-forward
           1   0%              - markdown-match-pandoc-metadata
           1   0%               - markdown-match-generic-metadata
           1   0%                  re-search-forward
           1   0%              - markdown-match-bold
           1   0%               - markdown-match-inline-generic
           1   0%                  re-search-forward
           1   0%              - markdown-fontify-sub-superscripts
           1   0%               - markdown-search-until-condition
           1   0%                - apply
           1   0%                   re-search-forward
           1   0%              - markdown-fontify-plain-uris
           1   0%               - markdown-match-plain-uris
           1   0%                - markdown-match-inline-generic
           1   0%                 - markdown-code-block-at-pos
           1   0%                  - markdown-get-enclosing-fenced-block-construct
           1   0%                     cl-find-if
           1   0%              - markdown-match-code
           1   0%               - markdown-search-until-condition
           1   0%                - apply
           1   0%                   re-search-forward
           5   1%             - font-lock-fontify-syntactically-region
           5   1%              - treesit-font-lock-fontify-region
           4   0%                 treesit-parser-root-node
           1   0%                 treesit--font-lock-fontify-region-1
           3   0%         - java-ts-mode
           2   0%          - treesit-major-mode-setup
           2   0%           - keymap-set
           2   0%            - keymap--check
           2   0%             - key-valid-p
           2   0%                string-match
           1   0%          - treesit-ready-p
           1   0%             treesit-language-available-p
           7   1%       - #<compiled-function 260>
           6   1%        - #<compiled-function AF0>
           6   1%         - run-hook-with-args
           5   1%          - eldoc-display-in-echo-area
           3   0%           - eldoc--message
           3   0%            - eldoc-minibuffer-message
           3   0%             - message
           3   0%                redisplay_internal (C function)
           1   0%          - eldoc-display-in-buffer
           1   0%             eldoc--format-doc-buffer
           1   0%      - #<compiled-function C3C>
           1   0%       - eglot--check-object
           1   0%          eglot--interface
           1   0%      - (setf jsonrpc--sync-request-alist)
           1   0%         gethash
           8   1%     - apply
           6   1%      - jsonrpc--event
           6   1%       - #<compiled-function FBD>
           6   1%        - apply
           6   1%         - jsonrpc--log-event
           1   0%          - jsonrpc--sync-request-alist
           1   0%             apply
           1   0%      - jsonrpc--reply
           1   0%       - jsonrpc-connection-send
           1   0%        - apply
           1   0%         - #<compiled-function 201>
           1   0%          - jsonrpc--json-encode
           1   0%             json-serialize
           1   0%     - jsonrpc-convert-from-endpoint
           1   0%        cl-type-of
           1   0%       cl--do-remf
           4   0%    - #<compiled-function 157>
           4   0%     - kill-buffer
           2   0%      - replace-buffer-in-windows
           1   0%         unrecord-window-buffer
           1   0%      generate-new-buffer
          21   4%   - #<subr-native-elisp F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_12>
          21   4%    - eldoc-print-current-symbol-info
          21   4%     - eldoc--invoke-strategy
          21   4%      - eldoc-documentation-compose
          11   2%       - eglot-hover-eldoc-function
           5   1%        - eglot--TextDocumentPositionParams
           5   1%         - eglot--TextDocumentIdentifier
           5   1%          - eglot-path-to-uri
           5   1%           - file-truename
           5   1%            - file-truename
           5   1%             - file-truename
           4   0%              - file-truename
           3   0%               - file-truename
           3   0%                - file-truename
           3   0%                 - file-truename
           1   0%                  - file-truename
           1   0%                   - file-truename
           1   0%                    - file-truename
           1   0%                     - file-truename
           1   0%                        file-truename
           5   1%        - eglot--highlight-piggyback
           5   1%         - eglot--TextDocumentPositionParams
           5   1%          - eglot--TextDocumentIdentifier
           5   1%           - eglot-path-to-uri
           5   1%            - file-truename
           5   1%             - file-truename
           4   0%              - file-truename
           4   0%               - file-truename
           3   0%                - file-truename
           2   0%                 - file-truename
           2   0%                  - file-truename
           2   0%                   - file-truename
           1   0%                      file-truename
           1   0%        - jsonrpc-async-request
           1   0%         - jsonrpc--async-request-1
           1   0%          - jsonrpc-connection-send
           1   0%           - apply
           1   0%            - #<compiled-function 201>
           1   0%               format
          10   2%       - eglot-signature-eldoc-function
          10   2%        - eglot--TextDocumentPositionParams
          10   2%         - eglot--TextDocumentIdentifier
          10   2%          - eglot-path-to-uri
          10   2%           - file-truename
          10   2%            - file-truename
           8   1%             - file-truename
           7   1%              - file-truename
           7   1%               - file-truename
           7   1%                - file-truename
           7   1%                 - file-truename
           7   1%                  - file-truename
           6   1%                   - file-truename
           6   1%                    - file-truename
           6   1%                     - file-truename
           5   1%                      - file-truename
           4   0%                       - file-truename
           3   0%                        - file-truename
           2   0%                         - file-truename
           1   0%                            tramp-completion-file-name-handler
           1   0%                         - tramp-completion-file-name-handler
           1   0%                            tramp-run-real-handler
           1   0%   - #<subr-native-elisp F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_9>
           1   0%      jit-lock-context-fontify
          94  19% - redisplay_internal (C function)
           8   1%  - eval
           5   1%   - eglot--mode-line-format
           3   0%    - eglot-project-nickname
           1   0%       gethash
           1   0%       cl-type-of
           1   0%       apply
           1   0%    - eglot--progress-reporters
           1   0%     - apply
           1   0%        #<compiled-function CC3>
           2   0%   - if
           2   0%      frame-parameter
           1   0%   - flymake--mode-line-exception
           1   0%    - flymake-reporting-backends
           1   0%       called-interactively-p
          85  17% - #<compiled-function FB8>
          74  15%    native-elisp-load
           7   1%  - comp-run-async-workers
           3   0%   - mapcar
           3   0%      prin1-to-string
           2   0%   - file-name-base
           1   0%      file-name-sans-extension
           1   0%   - write-region
           1   0%    - select-safe-coding-system
           1   0%     - find-auto-coding
           1   0%        auto-coding-alist-lookup
           1   0%     make-process
           2   0%    comp-el-to-eln-filename
           1   0%    delete-file
           1   0%    comp-accept-and-process-async-output
          67  14% - command-execute
          65  13%  - byte-code
          65  13%   - read-extended-command
          65  13%    - read-extended-command-1
          65  13%     - completing-read-default
          65  13%      - apply
          65  13%       - vertico--advice
          65  13%        - apply
          65  13%         - #<subr-native-elisp completing-read-default>
          19   3%          - vertico--exhibit
          13   2%           - vertico--update
          13   2%            - vertico--recompute
          11   2%             - vertico--filter-completions
          11   2%              - completion-all-completions
          11   2%               - completion--nth-completion
          11   2%                - seq-some
          11   2%                 - seq-do
          11   2%                  - mapc
          11   2%                   - #<compiled-function D88>
          11   2%                    - #<compiled-function DBB>
          11   2%                     - orderless-all-completions
          11   2%                      - orderless--filter
          11   2%                       - #<subr-native-elisp F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_56>
          11   2%                        - complete-with-action
          11   2%                         - all-completions
           4   0%                          - #<compiled-function DE6>
           2   0%                           - #<compiled-function 84E>
           1   0%                              commandp
           2   0%             - vertico-sort-history-length-alpha
           1   0%              - #<subr-native-elisp F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_14>
           1   0%                 #<primitive-function string-lessp>
           3   0%           - vertico--arrange-candidates
           3   0%            - vertico--affixate
           3   0%             - read-extended-command--affixation
           1   0%                #<subr-native-elisp F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_61>
           3   0%           - vertico--display-candidates
           2   0%            - vertico--resize-window
           2   0%               window-text-pixel-size
           1   0%              move-overlay
          19   3%          - #<compiled-function 12A>
          19   3%             native-elisp-load
          18   3%            redisplay_internal (C function)
           2   0%  - funcall-interactively
           2   0%   - file-notify-handle-event
           2   0%    - file-notify--callback-inotify
           2   0%     - file-notify--handle-event
           2   0%      - file-notify--call-handler
           2   0%       - #<compiled-function 79F>
           1   0%        - #<compiled-function 266>
           1   0%         - #<compiled-function 08F>
           1   0%            re-search-forward
           1   0%        - eglot-path-to-uri
           1   0%         - file-truename
           1   0%          - file-truename
           1   0%           - file-truename
           1   0%              file-truename
          11   2% - apply
          11   2%    native--compile-async
           6   1% - jsonrpc--process-filter
           4   0%  - jsonrpc--json-read
           4   0%     json-parse-buffer
           1   0%  - #<compiled-function 4D6>
           1   0%   - timer-set-time
           1   0%    - timer--time-setter
           1   0%       timerp
           1   0%  - jsonrpc--expected-bytes
           1   0%     gethash
           2   0%   internal-default-process-filter
           1   0% + ...
           1   0% + eldoc-pre-command-refresh-echo-area

[-- Attachment #4: emacs-30-new-file-truename --]
[-- Type: application/octet-stream, Size: 10595 bytes --]

         105  47% - redisplay_internal (C function)
           2   0%  - redisplay--pre-redisplay-functions
           1   0%     run-hook-with-args
           1   0%  - eval
           1   0%   - if
           1   0%      frame-parameter
          55  25% - command-execute
          54  24%  - byte-code
          54  24%   - read-extended-command
          54  24%    - read-extended-command-1
          54  24%     - completing-read-default
          54  24%      - apply
          54  24%       - vertico--advice
          54  24%        - apply
          54  24%         - #<subr-native-elisp completing-read-default>
          19   8%          - vertico--exhibit
          12   5%           - vertico--update
          12   5%            - vertico--recompute
          11   5%             - vertico--filter-completions
          11   5%              - completion-all-completions
          11   5%               - completion--nth-completion
          11   5%                - seq-some
          11   5%                 - seq-do
          11   5%                  - mapc
          11   5%                   - #<compiled-function 210>
          11   5%                    - #<compiled-function 279>
          11   5%                     - orderless-all-completions
          11   5%                      - orderless--filter
          11   5%                       - #<subr-native-elisp F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_56>
          11   5%                        - complete-with-action
          11   5%                         - all-completions
           8   3%                          - #<compiled-function 9CB>
           4   1%                           - #<compiled-function 5C0>
           3   1%                              commandp
           1   0%                             version-to-list
           1   0%             - vertico-sort-history-length-alpha
           1   0%              - #<subr-native-elisp F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_14>
           1   0%                 #<primitive-function string-lessp>
           5   2%           - vertico--arrange-candidates
           4   1%            - vertico--affixate
           4   1%             - read-extended-command--affixation
           3   1%                #<subr-native-elisp F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_61>
           1   0%            - vertico--window-width
           1   0%               get-buffer-window-list
           1   0%           - vertico--display-count
           1   0%              vertico--format-count
           1   0%           - vertico--display-candidates
           1   0%            - vertico--resize-window
           1   0%               window-text-pixel-size
          18   8%          - redisplay_internal (C function)
           1   0%             redisplay--pre-redisplay-functions
           1   0%          - timer-event-handler
           1   0%           - apply
           1   0%            - show-paren-function
           1   0%             - show-paren--default
           1   0%                show-paren--locate-near-paren
           1   0%  - funcall-interactively
           1   0%     execute-extended-command
          53  24% - timer-event-handler
          53  24%  - apply
          37  16%   - #<compiled-function A34>
          36  16%    - jsonrpc-connection-receive
          36  16%     - jsonrpc--continue
          34  15%      - #<compiled-function 1A6>
          31  14%       - eglot--hover-info
          31  14%        - eglot--format-markup
          20   9%         - gfm-view-mode
           5   2%          - byte-code
           4   1%             require
           1   0%           - custom-declare-variable
           1   0%            - custom-handle-keyword
           1   0%               custom-add-to-group
           4   1%          - gfm-mode
           4   1%           - markdown-mode
           4   1%            - syntax-propertize
           2   0%             - markdown-syntax-propertize
           1   0%              - markdown-syntax-propertize-list-items
           1   0%               - markdown--cur-list-item-bounds
           1   0%                  markdown-cur-list-item-end
           1   0%              - markdown-syntax-propertize-pre-blocks
           1   0%               - markdown-calculate-list-levels
           1   0%                  markdown-search-backward-baseline
           2   0%               #<compiled-function BA2>
           3   1%            require
           3   1%          - read-only-mode
           3   1%             view-mode-enter
           1   0%          - custom-declare-face
           1   0%           - face-spec-set
           1   0%            - make-empty-face
           1   0%             - make-face
           1   0%              - make-face-x-resource-internal
           1   0%               - set-face-attributes-from-resources
           1   0%                - set-face-attribute-from-resource
           1   0%                   internal-face-x-get-resource
           6   2%         - font-lock-ensure
           6   2%          - #<compiled-function C5E>
           6   2%           - font-lock-fontify-region
           6   2%            - font-lock-default-fontify-region
           5   2%             - font-lock-fontify-keywords-region
           1   0%              - markdown-match-code
           1   0%               - markdown-search-until-condition
           1   0%                - #<compiled-function ECA>
           1   0%                 - markdown-code-block-at-pos
           1   0%                    markdown-get-enclosing-fenced-block-construct
           1   0%              - markdown-fontify-plain-uris
           1   0%               - markdown-match-plain-uris
           1   0%                - markdown-match-inline-generic
           1   0%                   markdown-match-inline-generic
           1   0%              - markdown-fontify-inline-links
           1   0%               - markdown-match-generic-links
           1   0%                  markdown-end-of-text-block
           1   0%              - markdown-match-bold
           1   0%                 markdown-match-inline-generic
           1   0%             - font-lock-fontify-syntactically-region
           1   0%              - treesit-font-lock-fontify-region
           1   0%                 treesit--font-lock-fontify-region-1
           2   0%         - java-ts-mode
           1   0%          - prog-mode
           1   0%           - magit-auto-revert-mode-cmhh
           1   0%              add-hook
           1   0%         - #<compiled-function 867>
           1   0%            kill-buffer
           1   0%         - string-trim
           1   0%            string-trim-right
           3   1%       - #<compiled-function 150>
           3   1%        - #<compiled-function B0C>
           3   1%         - run-hook-with-args
           2   0%          - eldoc-display-in-echo-area
           2   0%           - eldoc--message
           2   0%            - eldoc-minibuffer-message
           2   0%             - apply
           2   0%              - message
           2   0%                 redisplay_internal (C function)
           1   0%          - eldoc-display-in-buffer
           1   0%           - eldoc--format-doc-buffer
           1   0%            - special-mode
           1   0%             - global-prettify-symbols-mode-cmhh
           1   0%                add-hook
           2   0%      - #<compiled-function 752>
           1   0%         mapc
           1   0%       - mapcar
           1   0%        - #<compiled-function 505>
           1   0%         - eglot-range-region
           1   0%          - eglot--lsp-position-to-point
           1   0%             eglot-move-to-utf-16-linepos
           1   0%      generate-new-buffer
          16   7%   - #<compiled-function F1D>
          16   7%    - eldoc-print-current-symbol-info
          16   7%     - eldoc--invoke-strategy
          16   7%      - eldoc-documentation-compose
          13   5%       - eglot-hover-eldoc-function
           5   2%        - eglot--highlight-piggyback
           4   1%         - jsonrpc-async-request
           4   1%          - jsonrpc--async-request-1
           4   1%           - jsonrpc-connection-send
           4   1%            - apply
           4   1%             - #<compiled-function 835>
           2   0%              - jsonrpc--event
           2   0%               - #<compiled-function 1E3>
           2   0%                - apply
           2   0%                   jsonrpc--log-event
           1   0%                process-send-string
           1   0%         - eglot--TextDocumentPositionParams
           1   0%          - eglot--TextDocumentIdentifier
           1   0%             eglot-path-to-uri
           4   1%        - jsonrpc-async-request
           4   1%         - jsonrpc--async-request-1
           2   0%          - jsonrpc-connection-send
           2   0%           - apply
           2   0%            - #<compiled-function 835>
           2   0%             - jsonrpc--event
           2   0%              - #<compiled-function 2A0>
           2   0%               - apply
           2   0%                  jsonrpc--log-event
           4   1%        - eglot--TextDocumentPositionParams
           4   1%         - eglot--TextDocumentIdentifier
           4   1%          - eglot-path-to-uri
           3   1%             url-generic-parse-url
           3   1%       - eglot-signature-eldoc-function
           2   0%        - jsonrpc-async-request
           2   0%         - jsonrpc--async-request-1
           1   0%          - jsonrpc-connection-send
           1   0%           - apply
           1   0%            - #<compiled-function 835>
           1   0%             - jsonrpc--json-encode
           1   0%                json-serialize
           1   0%          - #<compiled-function FE8>
           1   0%           - run-with-timer
           1   0%            - run-at-time
           1   0%             - timer-activate
           1   0%                timer--activate
           1   0%        - eglot--TextDocumentPositionParams
           1   0%         - eglot--TextDocumentIdentifier
           1   0%            eglot-path-to-uri
           6   2% - eldoc-pre-command-refresh-echo-area
           6   2%  - eldoc--message
           6   2%   - eldoc-minibuffer-message
           6   2%    - apply
           6   2%       message
           1   0%   corfu--auto-post-command
           0   0%   ...

[-- Attachment #5: emacs-Q-30-after-everything --]
[-- Type: application/octet-stream, Size: 10484 bytes --]

         122  39% - redisplay_internal (C function)
           1   0%    file-remote-p
          97  31% - command-execute
          96  30%  - byte-code
          96  30%   - read-extended-command
          96  30%    - read-extended-command-1
          96  30%     - completing-read-default
          46  14%        redisplay_internal (C function)
          16   5%      - command-execute
          16   5%       - funcall-interactively
          16   5%        - minibuffer-complete
          15   4%         - completion-in-region
          15   4%          - completion--in-region
          15   4%           - #<compiled-function E31>
          15   4%            - apply
          15   4%             - #<compiled-function 982>
          15   4%              - completion--in-region-1
          15   4%               - completion--do-completion
          10   3%                - completion-try-completion
          10   3%                 - completion--nth-completion
          10   3%                  - seq-some
          10   3%                   - seq-do
          10   3%                    - mapc
          10   3%                     - #<compiled-function 373>
          10   3%                      - #<compiled-function 1B9>
          10   3%                       - completion-basic-try-completion
          10   3%                        - #<subr-native-elisp F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_56>
          10   3%                         - complete-with-action
          10   3%                          - try-completion
           1   0%                             #<compiled-function 317>
           4   1%                - minibuffer-completion-help
           2   0%                 - completion-all-completions
           2   0%                  - completion--nth-completion
           2   0%                   - seq-some
           2   0%                    - seq-do
           2   0%                     - mapc
           2   0%                      - #<compiled-function 7FD>
           2   0%                       - #<compiled-function 79E>
           2   0%                        - completion-basic-all-completions
           2   0%                         - completion-pcm--all-completions
           2   0%                          - #<subr-native-elisp F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_56>
           2   0%                           - complete-with-action
           2   0%                              all-completions
           2   0%                 - temp-buffer-window-show
           2   0%                  - display-buffer
           2   0%                   - display-buffer-at-bottom
           1   0%                    - walk-window-tree
           1   0%                     - walk-window-tree-1
           1   0%                      - #<compiled-function 365>
           1   0%                         window-in-direction
           1   0%                    - window--display-buffer
           1   0%                     - #<compiled-function 38B>
           1   0%                      - read-extended-command--affixation
           1   0%                         #<subr-native-elisp F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_61>
           1   0%                - minibuffer-hide-completions
           1   0%                 - bury-buffer
           1   0%                  - window--delete
           1   0%                   - delete-window
           1   0%                    - window-sizable-p
           1   0%                     - window-sizable
           1   0%                      - window-size-fixed-p
           1   0%                         window--size-fixed-1
           1   0%           minibuffer--completion-prompt-end
           1   0%      - timer-event-handler
           1   0%       - apply
           1   0%        - show-paren-function
           1   0%         - show-paren--default
           1   0%          - show-paren--locate-near-paren
           1   0%             back-to-indentation
           1   0%  - funcall-interactively
           1   0%     execute-extended-command
          43  13% - timer-event-handler
          43  13%  - apply
          21   6%   - #<compiled-function E78>
          21   6%    - jsonrpc-connection-receive
          19   6%     - jsonrpc--continue
          17   5%      - #<compiled-function A37>
          10   3%       - #<compiled-function D56>
          10   3%        - #<compiled-function D92>
          10   3%         - run-hook-with-args
           9   2%          - eldoc-display-in-echo-area
           9   2%           - eldoc--message
           9   2%            - eldoc-minibuffer-message
           9   2%             - apply
           9   2%              - message
           7   2%                 redisplay_internal (C function)
           1   0%          - eldoc-display-in-buffer
           1   0%           - eldoc--format-doc-buffer
           1   0%            - special-mode
           1   0%               c-leave-cc-mode-mode
           7   2%       - eglot--hover-info
           7   2%        - eglot--format-markup
           5   1%         - font-lock-ensure
           5   1%          - #<compiled-function C5E>
           5   1%           - font-lock-fontify-region
           5   1%            - c-font-lock-fontify-region
           5   1%             - font-lock-default-fontify-region
           4   1%              - font-lock-fontify-keywords-region
           3   0%               - c-font-lock-declarations
           3   0%                - c-find-decl-spots
           3   0%                 - #<compiled-function F1B>
           3   0%                  - c-forward-decl-or-cast-1
           2   0%                   - c-forward-type
           1   0%                    - c-check-qualified-type
           1   0%                     - c-forward-over-compound-identifier
           1   0%                        c-forward-sws
           1   0%                    - c-forward-name
           1   0%                     - c-forward-<>-arglist
           1   0%                      - c-forward-<>-arglist-recur
           1   0%                       - c-forward-sws
           1   0%                          looking-at
           1   0%                     looking-at
           1   0%               - c-font-lock-enclosing-decls
           1   0%                - c-parse-state
           1   0%                   c-beginning-of-macro
           1   0%              - font-lock-fontify-syntactically-region
           1   0%               - font-lock-default-fontify-syntactically
           1   0%                - comment-normalize-vars
           1   0%                 - #<compiled-function A87>
           1   0%                  - kill-buffer
           1   0%                     replace-buffer-in-windows
           2   0%         - java-mode
           1   0%            c-init-language-vars-for
           1   0%          - c-common-init
           1   0%           - #<subr-native-elisp F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_35>
           1   0%            - c-unmark-<>-around-region
           1   0%               #<compiled-function D6E>
           2   0%        #<compiled-function 3C6>
           1   0%     - jsonrpc--remove
           1   0%        slot-value
           1   0%     - apply
           1   0%      - jsonrpc--event
           1   0%       - #<compiled-function 0A3>
           1   0%        - apply
           1   0%           jsonrpc--log-event
          12   3%   - #<compiled-function 4BB>
          12   3%    - eldoc-print-current-symbol-info
          12   3%     - eldoc--invoke-strategy
          12   3%      - eldoc-documentation-compose
          12   3%       - eglot-hover-eldoc-function
           6   1%        - eglot--TextDocumentPositionParams
           5   1%         - eglot--TextDocumentIdentifier
           5   1%          - eglot-path-to-uri
           3   0%             url-generic-parse-url
           1   0%         - eglot--pos-to-lsp-position
           1   0%          - eglot-utf-16-linepos
           1   0%             eglot--bol
           4   1%        - eglot--highlight-piggyback
           2   0%         - eglot--TextDocumentPositionParams
           2   0%          - eglot--TextDocumentIdentifier
           2   0%           - eglot-path-to-uri
           2   0%            - url-generic-parse-url
           1   0%             - #<compiled-function D46>
           1   0%                kill-buffer
           2   0%         - jsonrpc-async-request
           2   0%          - jsonrpc--async-request-1
           1   0%           - jsonrpc-connection-send
           1   0%            - apply
           1   0%             - #<compiled-function 510>
           1   0%                jsonrpc--event
           2   0%        - jsonrpc-async-request
           2   0%         - jsonrpc--async-request-1
           2   0%          - jsonrpc-connection-send
           2   0%           - apply
           2   0%            - #<compiled-function 510>
           1   0%             - jsonrpc--event
           1   0%              - #<compiled-function 82A>
           1   0%               - apply
           1   0%                  jsonrpc--log-event
           1   0%             - jsonrpc--json-encode
           1   0%                json-serialize
           6   1%   - #<subr-native-elisp F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_9>
           6   1%      jit-lock-context-fontify
           3   0%   - show-paren-function
           3   0%    - show-paren--default
           2   0%     - syntax-ppss
           1   0%        parse-partial-sexp
           1   0%        syntax-propertize
           1   0%       show-paren--locate-near-paren
           1   0%   - blink-cursor-start
           1   0%      blink-cursor--start-timer
          40  12%   Automatic GC
           4   1% - eldoc-pre-command-refresh-echo-area
           4   1%  - eldoc--message
           4   1%   - eldoc-minibuffer-message
           4   1%    - apply
           4   1%       message
           2   0% - jsonrpc--process-filter
           1   0%    search-forward-regexp
           1   0%  - #<compiled-function 79A>
           1   0%     timer-set-time
           1   0% - jit-lock--antiblink-post-command
           1   0%    syntax--lbp
           1   0% - undo-auto--add-boundary
           1   0%  - undo-auto--boundaries
           1   0%     add-to-list
           0   0%   ...

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #6: 0001-Move-file-truename-to-the-C-level.patch --]
[-- Type: text/x-diff, Size: 7698 bytes --]

From 183e636eebd9f1653d0cfdacdeba77d2043954af Mon Sep 17 00:00:00 2001
From: Theodor Thornhill <theo@thornhill.no>
Date: Wed, 27 Mar 2024 19:42:56 +0100
Subject: [PATCH] Move file-truename to the C level

---
 lisp/files.el | 116 +++++---------------------------------------------
 src/fileio.c  |  27 ++++++++++++
 2 files changed, 38 insertions(+), 105 deletions(-)

diff --git a/lisp/files.el b/lisp/files.el
index 766ed573392..6b9846c2ef4 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -32,6 +32,8 @@
   (require 'pcase)
   (require 'easy-mmode)) ; For `define-minor-mode'.
 
+(declare-function file-truename "fileio.c")
+
 (defvar font-lock-keywords)
 
 (defgroup backup nil
@@ -1413,111 +1415,15 @@ files--splice-dirname-file
 	unquoted
       (let (file-name-handler-alist) (file-name-quote unquoted)))))
 
-(defun file-truename (filename &optional counter prev-dirs)
-  "Return the truename of FILENAME.
-If FILENAME is not absolute, first expands it against `default-directory'.
-The truename of a file name is found by chasing symbolic links
-both at the level of the file and at the level of the directories
-containing it, until no links are left at any level.
-
-\(fn FILENAME)"  ;; Don't document the optional arguments.
-  ;; COUNTER and PREV-DIRS are used only in recursive calls.
-  ;; COUNTER can be a cons cell whose car is the count of how many
-  ;; more links to chase before getting an error.
-  ;; PREV-DIRS can be a cons cell whose car is an alist
-  ;; of truenames we've just recently computed.
-  (cond ((or (string= filename "") (string= filename "~"))
-	 (setq filename (expand-file-name filename))
-	 (if (string= filename "")
-	     (setq filename "/")))
-	((and (string= (substring filename 0 1) "~")
-	      (string-match "~[^/]*/?" filename))
-	 (let ((first-part
-		(substring filename 0 (match-end 0)))
-	       (rest (substring filename (match-end 0))))
-	   (setq filename (concat (expand-file-name first-part) rest)))))
-
-  (or counter (setq counter (list 100)))
-  (let (done
-	;; For speed, remove the ange-ftp completion handler from the list.
-	;; We know it's not needed here.
-	;; For even more speed, do this only on the outermost call.
-	(file-name-handler-alist
-	 (if prev-dirs file-name-handler-alist
-	   (let ((tem (copy-sequence file-name-handler-alist)))
-	     (delq (rassq 'ange-ftp-completion-hook-function tem) tem)))))
-    (or prev-dirs (setq prev-dirs (list nil)))
-
-    ;; andrewi@harlequin.co.uk - on Windows, there is an issue with
-    ;; case differences being ignored by the OS, and short "8.3 DOS"
-    ;; name aliases existing for all files.  (The short names are not
-    ;; reported by directory-files, but can be used to refer to files.)
-    ;; It seems appropriate for file-truename to resolve these issues in
-    ;; the most natural way, which on Windows is to call the function
-    ;; `w32-long-file-name' - this returns the exact name of a file as
-    ;; it is stored on disk (expanding short name aliases with the full
-    ;; name in the process).
-    (if (eq system-type 'windows-nt)
-	(unless (string-match "[[*?]" filename)
-	  ;; If filename exists, use its long name.  If it doesn't
-	  ;; exist, the recursion below on the directory of filename
-	  ;; will drill down until we find a directory that exists,
-	  ;; and use the long name of that, with the extra
-	  ;; non-existent path components concatenated.
-	  (let ((longname (w32-long-file-name filename)))
-	    (if longname
-		(setq filename longname)))))
-
-    ;; If this file directly leads to a link, process that iteratively
-    ;; so that we don't use lots of stack.
-    (while (not done)
-      (setcar counter (1- (car counter)))
-      (if (< (car counter) 0)
-	  (error "Apparent cycle of symbolic links for %s" filename))
-      (let ((handler (find-file-name-handler filename 'file-truename)))
-	;; For file name that has a special handler, call handler.
-	;; This is so that ange-ftp can save time by doing a no-op.
-	(if handler
-	    (setq filename (funcall handler 'file-truename filename)
-		  done t)
-	  (let ((dir (or (file-name-directory filename) default-directory))
-		target dirfile)
-	    ;; Get the truename of the directory.
-	    (setq dirfile (directory-file-name dir))
-	    ;; If these are equal, we have the (or a) root directory.
-	    (or (string= dir dirfile)
-		(and (file-name-case-insensitive-p dir)
-		     (string-equal-ignore-case dir dirfile))
-		;; If this is the same dir we last got the truename for,
-		;; save time--don't recalculate.
-		(if (assoc dir (car prev-dirs))
-		    (setq dir (cdr (assoc dir (car prev-dirs))))
-		  (let ((old dir)
-			(new (file-name-as-directory (file-truename dirfile counter prev-dirs))))
-		    (setcar prev-dirs (cons (cons old new) (car prev-dirs)))
-		    (setq dir new))))
-	    (if (equal ".." (file-name-nondirectory filename))
-		(setq filename
-		      (directory-file-name (file-name-directory (directory-file-name dir)))
-		      done t)
-	      (if (equal "." (file-name-nondirectory filename))
-		  (setq filename (directory-file-name dir)
-			done t)
-		;; Put it back on the file name.
-		(setq filename (concat dir (file-name-nondirectory filename)))
-		;; Is the file name the name of a link?
-		(setq target (file-symlink-p filename))
-		(if target
-		    ;; Yes => chase that link, then start all over
-		    ;; since the link may point to a directory name that uses links.
-		    ;; We can't safely use expand-file-name here
-		    ;; since target might look like foo/../bar where foo
-		    ;; is itself a link.  Instead, we handle . and .. above.
-		    (setq filename (files--splice-dirname-file dir target)
-			  done nil)
-		  ;; No, we are done!
-		  (setq done t))))))))
-    filename))
+;; (defun file-truename (filename &optional _x _y)
+;;   "Return the truename of FILENAME.
+;; If FILENAME is not absolute, first expands it against `default-directory'.
+;; The truename of a file name is found by chasing symbolic links
+;; both at the level of the file and at the level of the directories
+;; containing it, until no links are left at any level.
+
+;; \(fn FILENAME)"
+;;   (file-truename-c filename))
 
 (defun file-chase-links (filename &optional limit)
   "Chase links in FILENAME until a name that is not a link.
diff --git a/src/fileio.c b/src/fileio.c
index 12da7a9ed3a..05deedead55 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -24,6 +24,8 @@ Copyright (C) 1985-1988, 1993-2024 Free Software Foundation, Inc.
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <stdlib.h>
+#include <wordexp.h>
 
 #ifdef DARWIN_OS
 #include <sys/attr.h>
@@ -367,6 +369,30 @@ restore_point_unwind (Lisp_Object location)
   unchain_marker (XMARKER (location));
 }
 
+DEFUN ("file-truename", Ffile_truename, Sfile_truename,
+       1, 3, 0,
+       doc: /* Return the truename of FILENAME.  */)
+  (Lisp_Object filename, Lisp_Object x, Lisp_Object y)
+{
+  Lisp_Object result = filename;
+
+  CHECK_STRING (filename);
+  char *c_filename = SSDATA (filename);
+
+  wordexp_t we;
+  wordexp(c_filename, &we, 0);
+
+  char *truename = realpath(we.we_wordv[0], NULL);
+  wordfree(&we);
+
+  if (!truename)
+    return result;
+
+  result = build_string(truename);
+  free(truename);
+
+  return result;
+}
 \f
 DEFUN ("find-file-name-handler", Ffind_file_name_handler,
        Sfind_file_name_handler, 2, 2, 0,
@@ -6850,6 +6876,7 @@ do (file-exists-p FILENAME) and FILENAME is handled by HANDLER, then
   DEFSYM (Qstdout, "stdout");
   DEFSYM (Qstderr, "stderr");
 
+  defsubr (&Sfile_truename);
   defsubr (&Sfind_file_name_handler);
   defsubr (&Sfile_name_directory);
   defsubr (&Sfile_name_nondirectory);
-- 
2.40.1


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-27 19:08 bug#70036: 30.0.50; Move file-truename to the C level Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-03-27 19:44 ` Eli Zaretskii
  2024-03-27 21:56   ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-27 20:12 ` Felician Nemeth
  2024-03-28  9:22 ` Ihor Radchenko
  2 siblings, 1 reply; 28+ messages in thread
From: Eli Zaretskii @ 2024-03-27 19:44 UTC (permalink / raw)
  To: Theodor Thornhill; +Cc: 70036

> Date: Wed, 27 Mar 2024 20:08:54 +0100
> From:  Theodor Thornhill via "Bug reports for GNU Emacs,
>  the Swiss army knife of text editors" <bug-gnu-emacs@gnu.org>
> 
> During the last couple of weeks I've been studying Eglots performance
> and have been noticing a couple of things that I find very
> interesting. It seems like `file-truename` is in the hot path due to the
> fact that every request to the lsp server has to create the source file
> location, and in every response we have to parse the location the
> relevant file. `file-truename` is used for this, and its performance
> isn't really up to snuff for what it provides, afaict.
> 
> Below I've supplied some benchmarks and profile reports along with the
> actual patch. Before we discuss the patch itself, I want to get some
> answers to the following:
> 
>  - Is there a reason that this function should be supplied at the lisp
>    level?

No, we could have it implemented in C.  It just never was needed,
until now, and the processing there is not trivial, to say the least.

>  - Does it have to be recursive?

No, it doesn't.

> Firstly, I'll show some benchmarks
> 
> ```
> ;; Emacs 29 branch
> 
> (benchmark-run 10000
>   (file-truename "~/Work/some/long/path/to/parse/that/is/very/deep/deep/deep/super/duper/deep/deep.el"))
> ;; (1.810892642 1 0.051070616)
> 
> 
> ;; With new C implementation
> 
> (benchmark-run 10000
>   (file-truename "~/Work/some/long/path/to/parse/that/is/very/deep/deep/deep/super/duper/deep/deep.el"))
> ;; (0.018811035 0 0.0)
> ```
> 
> As you can see, the C implementation, though naive for now is two orders
> of magnitude faster, and makes a noticeable difference when running an
> lsp server in emacs.

Yes, but comparing a partial implementation is not very useful, since
the complete one could be much more expensive.

> As for the patch - it now relies on wordexp to resolve the paths, and I
> believe there is no real feature parity with the old variant as for now,
> but I haven't seen any issues thus far. If this approach is accepted I
> will of course make sure we have feature parity, unless that isn't
> wanted.

We cannot rely on wordexp and we cannot rely on realpath: both are not
portable enough.

> +  CHECK_STRING (filename);
> +  char *c_filename = SSDATA (filename);
> +
> +  wordexp_t we;
> +  wordexp(c_filename, &we, 0);
> +
> +  char *truename = realpath(we.we_wordv[0], NULL);
> +  wordfree(&we);
> +
> +  if (!truename)
> +    return result;
> +
> +  result = build_string(truename);

You cannot pass Lisp strings to libc functions like that: you need to
do 2 things first:

  . call expand-file-name
  . encode the file name with ENCODE_FILE

This is needed because relative file names in Emacs are relative to
the current buffer's directory, not relative to the current directory
of the Emacs process, and because file names with non-ASCII characters
need to be encoded to match the encoding expected by file-related APIs
in libc.  Likewise, when you get a file name from a libc function, you
need to decode it with DECODE_FILE, before you create a Lisp string
from it

> +  free(truename);

IMO, this should be xfree, not free.  And for that to work, we need to
call realpath with 2nd argument non-NULL, but pointing to a buffer we
allocated with xmalloc, or maybe a stack-based buffer.  (But since we
cannot rely on realpath, this could be a moot point.)

Thanks.





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-27 19:08 bug#70036: 30.0.50; Move file-truename to the C level Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-27 19:44 ` Eli Zaretskii
@ 2024-03-27 20:12 ` Felician Nemeth
  2024-03-27 21:43   ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-28  9:22 ` Ihor Radchenko
  2 siblings, 1 reply; 28+ messages in thread
From: Felician Nemeth @ 2024-03-27 20:12 UTC (permalink / raw)
  To: Theodor Thornhill; +Cc: 70036

> I've been studying Eglots performance and have been noticing a couple
> of things that I find very interesting. It seems like `file-truename`
> is in the hot path

I think Eglot repeatedly calls file-truename with the same argument (or
with an argument from a small set of filenames.)

I wonder whether it would make sense for Eglot to cache the result of
file-truename.  Do you think caching would make Eglot faster than it
currently is?  (Would it still be worth moving file-truename to the C
level?)





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-27 20:12 ` Felician Nemeth
@ 2024-03-27 21:43   ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-28  6:03     ` Eli Zaretskii
  0 siblings, 1 reply; 28+ messages in thread
From: Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-27 21:43 UTC (permalink / raw)
  To: Felician Nemeth; +Cc: 70036

Felician Nemeth <felician.nemeth@gmail.com> writes:

>> I've been studying Eglots performance and have been noticing a couple
>> of things that I find very interesting. It seems like `file-truename`
>> is in the hot path
>
> I think Eglot repeatedly calls file-truename with the same argument (or
> with an argument from a small set of filenames.)

This is true, to some extent. For the requests, eglot calls it
directly. For some handlers it is called through the function
`find-buffer-visiting`, so I'd say it's advisable to keep the function
itself fast.

>
> I wonder whether it would make sense for Eglot to cache the result of
> file-truename.  Do you think caching would make Eglot faster than it
> currently is?  (Would it still be worth moving file-truename to the C
> level?)

I think it would be faster, yes. This was my first thought as well, but
I quickly threw the first attempts on that away due to the fact that the
processing almost solely lies in that function, and I'd suppose it would
make other parts of emacs faster too. Adding complications to eglot
would likely make the source more complicated rather than better on the
whole. But that's only my opinion. I'd like to try to replicate the
function 1 to 1 in C.

What do you think?

Theo





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-27 19:44 ` Eli Zaretskii
@ 2024-03-27 21:56   ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-28  1:14     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-28  6:22     ` Eli Zaretskii
  0 siblings, 2 replies; 28+ messages in thread
From: Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-27 21:56 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 70036

Eli Zaretskii <eliz@gnu.org> writes:

>> Date: Wed, 27 Mar 2024 20:08:54 +0100
>> From:  Theodor Thornhill via "Bug reports for GNU Emacs,
>>  the Swiss army knife of text editors" <bug-gnu-emacs@gnu.org>
>> 
>> During the last couple of weeks I've been studying Eglots performance
>> and have been noticing a couple of things that I find very
>> interesting. It seems like `file-truename` is in the hot path due to the
>> fact that every request to the lsp server has to create the source file
>> location, and in every response we have to parse the location the
>> relevant file. `file-truename` is used for this, and its performance
>> isn't really up to snuff for what it provides, afaict.
>> 
>> Below I've supplied some benchmarks and profile reports along with the
>> actual patch. Before we discuss the patch itself, I want to get some
>> answers to the following:
>> 
>>  - Is there a reason that this function should be supplied at the lisp
>>    level?
>
> No, we could have it implemented in C.  It just never was needed,
> until now, and the processing there is not trivial, to say the least.

Yeah, the source is complicated, but it seems most of it is to gradually
chop the path shorter and shorter.


>
>>  - Does it have to be recursive?
>
> No, it doesn't.
>

I guess making it iterable is something worth checking out anyway, so
I'll look into that a little further.

>> Firstly, I'll show some benchmarks
>> 

[...]

>> 
>> As you can see, the C implementation, though naive for now is two orders
>> of magnitude faster, and makes a noticeable difference when running an
>> lsp server in emacs.
>
> Yes, but comparing a partial implementation is not very useful, since
> the complete one could be much more expensive.
>

No doubt. The most interesting part of that benchmark is maybe to see
that the current version is very slow, not that my function is very
fast. I'd guess that even if I'd lose an order of magnitude keeping
feature parity we're better off.

>> As for the patch - it now relies on wordexp to resolve the paths, and I
>> believe there is no real feature parity with the old variant as for now,
>> but I haven't seen any issues thus far. If this approach is accepted I
>> will of course make sure we have feature parity, unless that isn't
>> wanted.
>
> We cannot rely on wordexp and we cannot rely on realpath: both are not
> portable enough.
>

OK - for my education on the portability argument. Is that because of
Emacs support targets like haiku and old versions of windows, or
something else inherent in these functions?

>> +  CHECK_STRING (filename);
>> +  char *c_filename = SSDATA (filename);
>> +
>> +  wordexp_t we;
>> +  wordexp(c_filename, &we, 0);
>> +
>> +  char *truename = realpath(we.we_wordv[0], NULL);
>> +  wordfree(&we);
>> +
>> +  if (!truename)
>> +    return result;
>> +
>> +  result = build_string(truename);
>
> You cannot pass Lisp strings to libc functions like that: you need to
> do 2 things first:
>
>   . call expand-file-name
>   . encode the file name with ENCODE_FILE
>
> This is needed because relative file names in Emacs are relative to
> the current buffer's directory, not relative to the current directory
> of the Emacs process, and because file names with non-ASCII characters
> need to be encoded to match the encoding expected by file-related APIs
> in libc.  Likewise, when you get a file name from a libc function, you
> need to decode it with DECODE_FILE, before you create a Lisp string
> from it
>
>> +  free(truename);
>
> IMO, this should be xfree, not free.  And for that to work, we need to
> call realpath with 2nd argument non-NULL, but pointing to a buffer we
> allocated with xmalloc, or maybe a stack-based buffer.  (But since we
> cannot rely on realpath, this could be a moot point.)
>
> Thanks.

Thanks for the pointers here. I'll take note of them and investigate
further.


Another much simpler way to improve Eglot performance her could be to
allow for the relevant functions to execute through handlers, to not
break other parts of Emacs. For example `find-buffer-visiting` could
allow to run through a simpler function that merely expands and looks up
the current file, considering that the LSP server likely reports on
files that are already existing, and likely most symlink shenanigans
aren't an issue here. Just thinking out loudly on this.

Theo





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-27 21:56   ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-03-28  1:14     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-28  3:05       ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-28  7:03       ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-28  6:22     ` Eli Zaretskii
  1 sibling, 2 replies; 28+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-28  1:14 UTC (permalink / raw)
  To: Theodor Thornhill; +Cc: Eli Zaretskii, 70036

Theodor Thornhill <theo@thornhill.no> writes:

> OK - for my education on the portability argument. Is that because of
> Emacs support targets like haiku and old versions of windows, or
> something else inherent in these functions?

Haiku supports realpath, but not wordexp.  We must reimplement virtually
all POSIX functions of this nature on Windows, as the versions provided
by system C libraries, if they exist at all, are inadequate for some
reason or another.  Furthermore, these functions are absent from a
number of other operating systems, such as OpenBSD and Android 14.0, and
on others (Alpine, I believe) wordexp is severely inefficient for being
implemented as a wrapper around /bin/sh.





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-28  1:14     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-03-28  3:05       ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-28  7:04         ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-28  7:03       ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  1 sibling, 1 reply; 28+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-28  3:05 UTC (permalink / raw)
  To: Theodor Thornhill; +Cc: Eli Zaretskii, 70036

Po Lu <luangruo@yahoo.com> writes:

> Haiku supports realpath, but not wordexp.  We must reimplement virtually
> all POSIX functions of this nature on Windows, as the versions provided
> by system C libraries, if they exist at all, are inadequate for some
> reason or another.  Furthermore, these functions are absent from a
> number of other operating systems, such as OpenBSD and Android 14.0, and
> on others (Alpine, I believe) wordexp is severely inefficient for being
> implemented as a wrapper around /bin/sh.

BTW, after reading the subject of this bug report, I don't believe there
is a place for wordexp at all.  wordexp is no replacement for
Fexpand_file_name, but rather a means of expanding input in the manner
of a shell, which input might include arithmetic expressions, shell
command substitutions, and other expressions valid as shell input.  If
Fexpand_file_name alone will not suffice, TRT is to replicate the
mechanics of file-truename in whole, not cutting corners with these
admittedly tempting functions.





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-27 21:43   ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-03-28  6:03     ` Eli Zaretskii
  2024-03-28  7:10       ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 28+ messages in thread
From: Eli Zaretskii @ 2024-03-28  6:03 UTC (permalink / raw)
  To: Theodor Thornhill; +Cc: 70036, felician.nemeth

> Cc: 70036@debbugs.gnu.org
> Date: Wed, 27 Mar 2024 22:43:25 +0100
> From:  Theodor Thornhill via "Bug reports for GNU Emacs,
>  the Swiss army knife of text editors" <bug-gnu-emacs@gnu.org>
> 
> Felician Nemeth <felician.nemeth@gmail.com> writes:
> 
> >> I've been studying Eglots performance and have been noticing a couple
> >> of things that I find very interesting. It seems like `file-truename`
> >> is in the hot path
> >
> > I think Eglot repeatedly calls file-truename with the same argument (or
> > with an argument from a small set of filenames.)
> 
> This is true, to some extent.

Can someone explain why Eglot needs to call file-truename in the first
place?





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-27 21:56   ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-28  1:14     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-03-28  6:22     ` Eli Zaretskii
  2024-03-28  7:03       ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  1 sibling, 1 reply; 28+ messages in thread
From: Eli Zaretskii @ 2024-03-28  6:22 UTC (permalink / raw)
  To: Theodor Thornhill; +Cc: 70036

> From: Theodor Thornhill <theo@thornhill.no>
> Cc: 70036@debbugs.gnu.org
> Date: Wed, 27 Mar 2024 22:56:41 +0100
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> >> As for the patch - it now relies on wordexp to resolve the paths, and I
> >> believe there is no real feature parity with the old variant as for now,
> >> but I haven't seen any issues thus far. If this approach is accepted I
> >> will of course make sure we have feature parity, unless that isn't
> >> wanted.
> >
> > We cannot rely on wordexp and we cannot rely on realpath: both are not
> > portable enough.
> 
> OK - for my education on the portability argument. Is that because of
> Emacs support targets like haiku and old versions of windows, or
> something else inherent in these functions?

Platforms other than GNU/Linux are the main concern, yes.  Another
aspect to consider is whether the function is available in Gnulib --
if it is, we can import the Gnulib implementation and use it on
platforms which don't provide it natively; in that case, using such a
function is okay (but the MS-Windows port will likely need to provide
its own implementation, because Emacs uses Unicode APIs to access and
process file names, something Gnulib doesn't do on Windows).

In this case, realpath is available in Gnulib, but wordexp isn't,
AFAICT.  And I'm not even sure we want to use wordexp here, because
(reading its docs) it does stuff we don't want to do in file-truename.
Why did you need to call it here?

> Another much simpler way to improve Eglot performance her could be to
> allow for the relevant functions to execute through handlers, to not
> break other parts of Emacs. For example `find-buffer-visiting` could
> allow to run through a simpler function that merely expands and looks up
> the current file, considering that the LSP server likely reports on
> files that are already existing, and likely most symlink shenanigans
> aren't an issue here. Just thinking out loudly on this.

AFAIR, find-buffer-visiting was significantly sped up recently (see
bug#66117), so if you did your benchmarks with Emacs before commit
b7a737ef49 on master, perhaps do it again with the latest master
branch.





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-28  6:22     ` Eli Zaretskii
@ 2024-03-28  7:03       ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 0 replies; 28+ messages in thread
From: Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-28  7:03 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 70036

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Theodor Thornhill <theo@thornhill.no>
>> Cc: 70036@debbugs.gnu.org
>> Date: Wed, 27 Mar 2024 22:56:41 +0100
>> 
>> Eli Zaretskii <eliz@gnu.org> writes:
>> 
>> >> As for the patch - it now relies on wordexp to resolve the paths, and I
>> >> believe there is no real feature parity with the old variant as for now,
>> >> but I haven't seen any issues thus far. If this approach is accepted I
>> >> will of course make sure we have feature parity, unless that isn't
>> >> wanted.
>> >
>> > We cannot rely on wordexp and we cannot rely on realpath: both are not
>> > portable enough.
>> 
>> OK - for my education on the portability argument. Is that because of
>> Emacs support targets like haiku and old versions of windows, or
>> something else inherent in these functions?
>
> Platforms other than GNU/Linux are the main concern, yes.  Another
> aspect to consider is whether the function is available in Gnulib --
> if it is, we can import the Gnulib implementation and use it on
> platforms which don't provide it natively; in that case, using such a
> function is okay (but the MS-Windows port will likely need to provide
> its own implementation, because Emacs uses Unicode APIs to access and
> process file names, something Gnulib doesn't do on Windows).
>
> In this case, realpath is available in Gnulib, but wordexp isn't,
> AFAICT.  And I'm not even sure we want to use wordexp here, because
> (reading its docs) it does stuff we don't want to do in file-truename.
> Why did you need to call it here?
>

Thanks! I'll admit Fexpand-file-name likely does what we want in this
case, so I'll look into this.

>> Another much simpler way to improve Eglot performance her could be to
>> allow for the relevant functions to execute through handlers, to not
>> break other parts of Emacs. For example `find-buffer-visiting` could
>> allow to run through a simpler function that merely expands and looks up
>> the current file, considering that the LSP server likely reports on
>> files that are already existing, and likely most symlink shenanigans
>> aren't an issue here. Just thinking out loudly on this.
>
> AFAIR, find-buffer-visiting was significantly sped up recently (see
> bug#66117), so if you did your benchmarks with Emacs before commit
> b7a737ef49 on master, perhaps do it again with the latest master
> branch.

These reports are made using some commit from yesterday, so these
speedups should be included. `find-buffer-visiting` is used inside of
the `publishDiagnostics` handler, which sometimes receives file paths
for the whole project, so this really becomes a bottleneck for servers
that provide project wide diags.





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-28  1:14     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-28  3:05       ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-03-28  7:03       ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  1 sibling, 0 replies; 28+ messages in thread
From: Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-28  7:03 UTC (permalink / raw)
  To: Po Lu; +Cc: Eli Zaretskii, 70036

Po Lu <luangruo@yahoo.com> writes:

> Theodor Thornhill <theo@thornhill.no> writes:
>
>> OK - for my education on the portability argument. Is that because of
>> Emacs support targets like haiku and old versions of windows, or
>> something else inherent in these functions?
>
> Haiku supports realpath, but not wordexp.  We must reimplement virtually
> all POSIX functions of this nature on Windows, as the versions provided
> by system C libraries, if they exist at all, are inadequate for some
> reason or another.  Furthermore, these functions are absent from a
> number of other operating systems, such as OpenBSD and Android 14.0, and
> on others (Alpine, I believe) wordexp is severely inefficient for being
> implemented as a wrapper around /bin/sh.

Thanks for the input!

Theo





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-28  3:05       ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-03-28  7:04         ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 0 replies; 28+ messages in thread
From: Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-28  7:04 UTC (permalink / raw)
  To: Po Lu; +Cc: Eli Zaretskii, 70036

Po Lu <luangruo@yahoo.com> writes:

> Po Lu <luangruo@yahoo.com> writes:
>
>> Haiku supports realpath, but not wordexp.  We must reimplement virtually
>> all POSIX functions of this nature on Windows, as the versions provided
>> by system C libraries, if they exist at all, are inadequate for some
>> reason or another.  Furthermore, these functions are absent from a
>> number of other operating systems, such as OpenBSD and Android 14.0, and
>> on others (Alpine, I believe) wordexp is severely inefficient for being
>> implemented as a wrapper around /bin/sh.
>
> BTW, after reading the subject of this bug report, I don't believe there
> is a place for wordexp at all.  wordexp is no replacement for
> Fexpand_file_name, but rather a means of expanding input in the manner
> of a shell, which input might include arithmetic expressions, shell
> command substitutions, and other expressions valid as shell input.  If
> Fexpand_file_name alone will not suffice, TRT is to replicate the
> mechanics of file-truename in whole, not cutting corners with these
> admittedly tempting functions.

I agree, and believe you are right. I think Fexpand_file_name should be
enough. I'll look into it!

Thanks!





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-28  6:03     ` Eli Zaretskii
@ 2024-03-28  7:10       ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-28  8:52         ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-28 11:55         ` Felician Nemeth
  0 siblings, 2 replies; 28+ messages in thread
From: Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-28  7:10 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 70036, felician.nemeth

Eli Zaretskii <eliz@gnu.org> writes:

>> Cc: 70036@debbugs.gnu.org
>> Date: Wed, 27 Mar 2024 22:43:25 +0100
>> From:  Theodor Thornhill via "Bug reports for GNU Emacs,
>>  the Swiss army knife of text editors" <bug-gnu-emacs@gnu.org>
>> 
>> Felician Nemeth <felician.nemeth@gmail.com> writes:
>> 
>> >> I've been studying Eglots performance and have been noticing a couple
>> >> of things that I find very interesting. It seems like `file-truename`
>> >> is in the hot path
>> >
>> > I think Eglot repeatedly calls file-truename with the same argument (or
>> > with an argument from a small set of filenames.)
>> 
>> This is true, to some extent.
>
> Can someone explain why Eglot needs to call file-truename in the first
> place?

I tried quickly just replacing file-truename with expand-file-name, and
from some quick testing, it seems to work, and of course remove
file-truenames slow performance in the profiles:

```
@@ -1085,7 +1089,7 @@ eglot-uri-to-path
 
 (defun eglot-path-to-uri (path)
   "Convert PATH, a file name, to LSP URI string and return it."
-  (let ((truepath (file-truename path)))
+  (let ((truepath (expand-file-name path)))
     (if (and (url-type (url-generic-parse-url path))
              ;; It might be MS Windows path which includes a drive
              ;; letter that looks like a URL scheme (bug#59338)
```

This wouldn't help for the usage in find-buffer-visiting, though. But
this one could more easily be replaced by reworking the diagnostics
handler. We could store the last received diagnostics in the server
object, and do a quick lookup from known buffers there.

Theo





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-28  7:10       ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-03-28  8:52         ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-28 11:55         ` Felician Nemeth
  1 sibling, 0 replies; 28+ messages in thread
From: Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-28  8:52 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 70036, felician.nemeth, João Távora

Theodor Thornhill <theo@thornhill.no> writes:

> Eli Zaretskii <eliz@gnu.org> writes:
>
>>> Cc: 70036@debbugs.gnu.org
>>> Date: Wed, 27 Mar 2024 22:43:25 +0100
>>> From:  Theodor Thornhill via "Bug reports for GNU Emacs,
>>>  the Swiss army knife of text editors" <bug-gnu-emacs@gnu.org>
>>> 
>>> Felician Nemeth <felician.nemeth@gmail.com> writes:
>>> 
>>> >> I've been studying Eglots performance and have been noticing a couple
>>> >> of things that I find very interesting. It seems like `file-truename`
>>> >> is in the hot path
>>> >
>>> > I think Eglot repeatedly calls file-truename with the same argument (or
>>> > with an argument from a small set of filenames.)
>>> 
>>> This is true, to some extent.
>>
>> Can someone explain why Eglot needs to call file-truename in the first
>> place?
>

[...]

> handler. We could store the last received diagnostics in the server
> object, and do a quick lookup from known buffers there.
>
> Theo


It seems to be there from the very early days, like in 2018. My guess is
that we simply never noticed the performance issues here due to other
stuff like json parsing etc.

CC'd Joao to get some perspective whether or not it is intentional or
needed at all.

Theo





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-27 19:08 bug#70036: 30.0.50; Move file-truename to the C level Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-27 19:44 ` Eli Zaretskii
  2024-03-27 20:12 ` Felician Nemeth
@ 2024-03-28  9:22 ` Ihor Radchenko
  2024-03-28 10:59   ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2 siblings, 1 reply; 28+ messages in thread
From: Ihor Radchenko @ 2024-03-28  9:22 UTC (permalink / raw)
  To: Theodor Thornhill; +Cc: 70036

[-- Attachment #1: Type: text/plain, Size: 1556 bytes --]

Theodor Thornhill via "Bug reports for GNU Emacs, the Swiss army knife
of text editors" <bug-gnu-emacs@gnu.org> writes:

> Firstly, I'll show some benchmarks
>
> ```
> ;; Emacs 29 branch
>
> (benchmark-run 10000
>   (file-truename "~/Work/some/long/path/to/parse/that/is/very/deep/deep/deep/super/duper/deep/deep.el"))
> ;; (1.810892642 1 0.051070616)
> ...
> Below are the profiles and the patch. On my system I needed to `ln -s
> lisp/loadup.el .` to make it compile. Not sure if that is due to
> differences between old and new `file-truename`, or something else.

The profiles look fishy. For example, in emacs-30-before

          73  15%           - file-truename

73 is ~0.073 seconds, which cannot be right if you profiled the above
`benchmark-run'.

I tried to record the profiles on my side, using the above
`benchmark-run' call, and what I am seeing is that most of the CPU time
is already spend in C subrs:

        1285  17% + file-name-nondirectory
        1250  16%   Automatic GC
        1182  15% + file-symlink-p
        1060  14% + file-name-case-insensitive-p
         495   6% + find-file-name-handler
         451   6% + file-name-as-directory
         425   5% + file-name-directory
         401   5% - directory-file-name
         398   5%  + setq

I am attaching my profile, as saved via M-x
profiler-report-write-profile. You can view it via M-x
profile-report-find-profile

At least, the number of calls to `file-name-nondirectory' can be
trivially reduced in the `file-truename' code - it is called up to three
times in a row.


[-- Attachment #2: profile-emacs-master.eld --]
[-- Type: application/octet-stream, Size: 260996 bytes --]

[-- Attachment #3: Type: text/plain, Size: 224 bytes --]


-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>

^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-28  9:22 ` Ihor Radchenko
@ 2024-03-28 10:59   ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-28 11:18     ` Ihor Radchenko
  0 siblings, 1 reply; 28+ messages in thread
From: Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-28 10:59 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: 70036

Ihor Radchenko <yantar92@posteo.net> writes:

> Theodor Thornhill via "Bug reports for GNU Emacs, the Swiss army knife
> of text editors" <bug-gnu-emacs@gnu.org> writes:
>
>> Firstly, I'll show some benchmarks
>>
>> ```
>> ;; Emacs 29 branch
>>
>> (benchmark-run 10000
>>   (file-truename "~/Work/some/long/path/to/parse/that/is/very/deep/deep/deep/super/duper/deep/deep.el"))
>> ;; (1.810892642 1 0.051070616)
>> ...
>> Below are the profiles and the patch. On my system I needed to `ln -s
>> lisp/loadup.el .` to make it compile. Not sure if that is due to
>> differences between old and new `file-truename`, or something else.
>
> The profiles look fishy. For example, in emacs-30-before
>

Not sure I understand what you mean. I tried it again, but this time the
call is 100000 times and in an existing file on my system which is
deeply nested. I run emacs with `emacs -Q` from a build on
`30b1b0d7cd8e4d46a601e9737350cda970f6bab0`.

the relevant part from the profile this time:
```
       15478  72% - command-execute
       15440  72%  - funcall-interactively
       15439  72%   - eval-last-sexp
       15439  72%    - #<compiled-function 0C4>
       15439  72%     - elisp--eval-last-sexp
       15436  71%      - eval
       15436  71%       - benchmark-call
       15434  71%        - #<lambda E8B>
       15434  71%         - file-truename
       13536  63%          - file-truename
       12224  57%           - file-truename
       10970  51%            - file-truename
        9673  45%             - file-truename
        8468  39%              - file-truename
        7420  34%               - file-truename
        6374  29%                - file-truename
        5286  24%                 - file-truename
        4275  19%                  - file-truename
        3313  15%                   - file-truename
        2454  11%                    - file-truename
        1652   7%                     - file-truename
         984   4%                      - file-truename
         376   1%                         file-truename
           1   0%          time-since
           1   0%     execute-extended-command
```

To me this looks like it spends a lot of time in recursive calls

Theo





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-28 10:59   ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-03-28 11:18     ` Ihor Radchenko
  2024-03-28 11:41       ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 28+ messages in thread
From: Ihor Radchenko @ 2024-03-28 11:18 UTC (permalink / raw)
  To: Theodor Thornhill; +Cc: 70036

Theodor Thornhill <theo@thornhill.no> writes:

> Not sure I understand what you mean. I tried it again, but this time the
> call is 100000 times and in an existing file on my system which is
> deeply nested. I run emacs with `emacs -Q` from a build on
> `30b1b0d7cd8e4d46a601e9737350cda970f6bab0`.
>
> the relevant part from the profile this time:
> ```
>        15478  72% - command-execute
>        15440  72%  - funcall-interactively
>        15439  72%   - eval-last-sexp
>        15439  72%    - #<compiled-function 0C4>
>        15439  72%     - elisp--eval-last-sexp
>        15436  71%      - eval
>        15436  71%       - benchmark-call
>        15434  71%        - #<lambda E8B>
>        15434  71%         - file-truename
>        13536  63%          - file-truename
>        12224  57%           - file-truename
> ...
> To me this looks like it spends a lot of time in recursive calls

No. It is just that your `file-truename' is native-compiled and the
profiler is unable to get the details from inside native subr code.

You can re-evaluate the defun to reveal the details in the profiler
output. Or use Emacs compiled without native-compilation support.

-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-28 11:18     ` Ihor Radchenko
@ 2024-03-28 11:41       ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-28 11:51         ` Ihor Radchenko
  0 siblings, 1 reply; 28+ messages in thread
From: Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-28 11:41 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: 70036

Ihor Radchenko <yantar92@posteo.net> writes:

> Theodor Thornhill <theo@thornhill.no> writes:
>
>> Not sure I understand what you mean. I tried it again, but this time the
>> call is 100000 times and in an existing file on my system which is
>> deeply nested. I run emacs with `emacs -Q` from a build on
>> `30b1b0d7cd8e4d46a601e9737350cda970f6bab0`.
>>
>> the relevant part from the profile this time:
>> ```
>>        15478  72% - command-execute
>>        15440  72%  - funcall-interactively
>>        15439  72%   - eval-last-sexp
>>        15439  72%    - #<compiled-function 0C4>
>>        15439  72%     - elisp--eval-last-sexp
>>        15436  71%      - eval
>>        15436  71%       - benchmark-call
>>        15434  71%        - #<lambda E8B>
>>        15434  71%         - file-truename
>>        13536  63%          - file-truename
>>        12224  57%           - file-truename
>> ...
>> To me this looks like it spends a lot of time in recursive calls
>
> No. It is just that your `file-truename' is native-compiled and the
> profiler is unable to get the details from inside native subr code.
>
> You can re-evaluate the defun to reveal the details in the profiler
> output. Or use Emacs compiled without native-compilation support.
>
> -- 
> Ihor Radchenko // yantar92,
> Org mode contributor,
> Learn more about Org mode at <https://orgmode.org/>.
> Support Org development at <https://liberapay.com/org-mode>,
> or support my work at <https://liberapay.com/yantar92>

I'm sorry, but I don't see the real difference here. Yes, the profile is
more detailed this way, but it doesn't change the fact that
file-truename is slow, does it?

The question to me is whether or not this is an acceptable performance
hit to take for eglot given what it's trying to do, and my opinion is
no. Whether or not it should be moved to C is open to suggestion. I'm
preparing a patch that only targets Eglot by removing reliance on this
function.

Theo

```
       17839  63% - command-execute
       17794  63%  - funcall-interactively
       17793  63%   - eval-last-sexp
       17793  63%    - #<compiled-function EF0>
       17793  63%     - elisp--eval-last-sexp
       17791  63%      - eval
       17791  63%       - benchmark-call
       17788  63%        - #<lambda 219>
       17783  63%         - file-truename
       17576  62%          - let
       17546  62%           - while
       17529  62%            - let
       17358  61%             - if
       17356  61%              - let
       16365  58%               - or
       15969  56%                - if
       15964  56%                 - let
       15951  56%                  - file-name-as-directory
       15824  56%                   - file-truename
       15788  56%                    - let
       15768  56%                     - while
       15712  55%                      - let
       15579  55%                       - if
       15577  55%                        - let
        7224  25%                         - or
        3429  12%                          - if
        3325  11%                           - let
        3059  10%                            - file-name-as-directory
        1866   6%                             - file-truename
        1501   5%                              - let
        1276   4%                               - while
        1121   3%                                - let
        1004   3%                                   find-file-name-handler
          18   0%                                   if
          49   0%                                - setcar
          26   0%                                   1-
          37   0%                                - if
          23   0%                                   <
          36   0%                                  not
          97   0%                               - if
          46   0%                                - eq
           8   0%                                   quote
           7   0%                                 or
         218   0%                              - cond
         112   0%                               - and
          95   0%                                - string=
          73   0%                                   substring
          82   0%                               - or
          57   0%                                  string=
          17   0%                                or
         123   0%                            - setcar
          83   0%                             - cons
```





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-28 11:41       ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-03-28 11:51         ` Ihor Radchenko
  2024-03-28 12:47           ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-28 13:52           ` Eli Zaretskii
  0 siblings, 2 replies; 28+ messages in thread
From: Ihor Radchenko @ 2024-03-28 11:51 UTC (permalink / raw)
  To: Theodor Thornhill; +Cc: 70036

Theodor Thornhill <theo@thornhill.no> writes:

> I'm sorry, but I don't see the real difference here. Yes, the profile is
> more detailed this way, but it doesn't change the fact that
> file-truename is slow, does it?

It does not, but it is important for your suggestion to move
`file-truename' to the C level.

If the slow parts of `file-truename' are the calls to C subroutines,
rewriting `file-truename' in C will not help much with the performance.
(Unless you try hard and move parts of the used subroutines into the C
implementation of `file-truename', but that will involve copy-pasting
parts of the existing code and cannot be a good idea without very strong
justification)

> The question to me is whether or not this is an acceptable performance
> hit to take for eglot given what it's trying to do, and my opinion is
> no. Whether or not it should be moved to C is open to suggestion. I'm
> preparing a patch that only targets Eglot by removing reliance on this
> function.

If your aim is improving eglot performance specifically, sure.
If your aim is improving `file-truename' performance in general, a more
detailed analysis can be helpful.

>        17839  63% - command-execute
>        17794  63%  - funcall-interactively
>        17793  63%   - eval-last-sexp
>        17793  63%    - #<compiled-function EF0>
>        17793  63%     - elisp--eval-last-sexp
>        17791  63%      - eval
>        17791  63%       - benchmark-call
>        17788  63%        - #<lambda 219>
>        17783  63%         - file-truename
>        17576  62%          - let
> ...

When you have recursive calls, it is generally more useful to view
reversed calltree (B) in the profiler report. It will accumulate calls
to the same function together, regardless on how deep in the call stack
these calls are made.

-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-28  7:10       ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-28  8:52         ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-03-28 11:55         ` Felician Nemeth
  2024-03-28 12:08           ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  1 sibling, 1 reply; 28+ messages in thread
From: Felician Nemeth @ 2024-03-28 11:55 UTC (permalink / raw)
  To: Theodor Thornhill; +Cc: Eli Zaretskii, 70036

Theodor Thornhill <theo@thornhill.no> writes:

> This wouldn't help for the usage in find-buffer-visiting, though. But
> this one could more easily be replaced by reworking the diagnostics
> handler. We could store the last received diagnostics in the server
> object, and do a quick lookup from known buffers there.

eglot-handle-notification:textDocument/publishDiagnostics,
eglot--xref-make-match, and eglot--apply-workspace-edit call
find-buffer-visiting.  It seems to me only the first case might be
really time sensitive.  Theo, can you email me the relevant messages
that your server sends to Emacs?  Does the server send lots of similar
diagnostics messages frequently?

Thanks,
Felicián





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-28 11:55         ` Felician Nemeth
@ 2024-03-28 12:08           ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-30  9:46             ` Felician Nemeth
  0 siblings, 1 reply; 28+ messages in thread
From: Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-28 12:08 UTC (permalink / raw)
  To: Felician Nemeth; +Cc: Eli Zaretskii, 70036

[-- Attachment #1: Type: text/plain, Size: 1188 bytes --]

Felician Nemeth <felician.nemeth@gmail.com> writes:

> Theodor Thornhill <theo@thornhill.no> writes:
>
>> This wouldn't help for the usage in find-buffer-visiting, though. But
>> this one could more easily be replaced by reworking the diagnostics
>> handler. We could store the last received diagnostics in the server
>> object, and do a quick lookup from known buffers there.
>
> eglot-handle-notification:textDocument/publishDiagnostics,
> eglot--xref-make-match, and eglot--apply-workspace-edit call
> find-buffer-visiting.  It seems to me only the first case might be
> really time sensitive.  Theo, can you email me the relevant messages
> that your server sends to Emacs?  Does the server send lots of similar
> diagnostics messages frequently?
>
> Thanks,
> Felicián

I'll try to include such a report a little later today. But yes, it
happens a lot. Consider the following patch, though. It eliminates the
issues for publishDiagnostics. It can easily be extended to the other
places where find-buffer-visiting is used.

The publishDiagnostics is sent on change from the server, so that isn't
directly initiated by Eglot.

What do you think?

Theo


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Don-t-use-file-truepath-in-Eglot-bug-70036.patch --]
[-- Type: text/x-diff, Size: 5902 bytes --]

From d43fe85eae05baa07dd4c3e873a1b94542d97ea9 Mon Sep 17 00:00:00 2001
From: Theodor Thornhill <theo@thornhill.no>
Date: Thu, 28 Mar 2024 12:56:23 +0100
Subject: [PATCH] Don't use file-truepath in Eglot (bug#70036)

`file-truepath' is slow because of recursive calls and being implemented
in lisp.  It seems to not be needed in eglot, but it is used behind the
scenes in `find-buffer-visiting', thus appearing in profiles.  Moving
the implementation to a hash map will yield similar performance
benefits, but wouldn't require us to rewrite `file-truename' in C.

* lisp/progmodes/eglot.el (eglot-lsp-server): Convert 'managed-buffers'
to a hashmap.
(eglot-uri-to-path): Don't use file-truepath, as it is too slow to be
included in the hot path.
(eglot--on-shutdown): Use buffers from buffer map.
(eglot--managed-mode): Add buffer to map, rather than list. Also remove
it from the map on deactivation.
(eglot-handle-notification): Expose server and get buffer from the
buffer map.
---
 lisp/progmodes/eglot.el | 30 +++++++++++++++++-------------
 1 file changed, 17 insertions(+), 13 deletions(-)

diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index 7d2f1a55165..c6dbca6fc6a 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -1053,8 +1053,8 @@ eglot-lsp-server
     :documentation "Map (DIR -> (WATCH ID1 ID2...)) for `didChangeWatchedFiles'."
     :initform (make-hash-table :test #'equal) :accessor eglot--file-watches)
    (managed-buffers
-    :initform nil
-    :documentation "List of buffers managed by server."
+    :documentation "Map (PATH -> BUFFER) for buffers managed by server."
+    :initform (make-hash-table :test #'equal)
     :accessor eglot--managed-buffers)
    (saved-initargs
     :documentation "Saved initargs for reconnection purposes."
@@ -1085,12 +1085,12 @@ eglot-uri-to-path
 
 (defun eglot-path-to-uri (path)
   "Convert PATH, a file name, to LSP URI string and return it."
-  (let ((truepath (file-truename path)))
+  (let ((expanded-path (expand-file-name path)))
     (if (and (url-type (url-generic-parse-url path))
              ;; It might be MS Windows path which includes a drive
              ;; letter that looks like a URL scheme (bug#59338)
              (not (and (eq system-type 'windows-nt)
-                       (file-name-absolute-p truepath))))
+                       (file-name-absolute-p expanded-path))))
         ;; Path is already a URI, so forward it to the LSP server
         ;; untouched.  The server should be able to handle it, since
         ;; it provided this URI to clients in the first place.
@@ -1098,11 +1098,11 @@ eglot-path-to-uri
       (concat "file://"
               ;; Add a leading "/" for local MS Windows-style paths.
               (if (and (eq system-type 'windows-nt)
-                       (not (file-remote-p truepath)))
+                       (not (file-remote-p expanded-path)))
                   "/")
               (url-hexify-string
                ;; Again watch out for trampy paths.
-               (directory-file-name (file-local-name truepath))
+               (directory-file-name (file-local-name expanded-path))
                eglot--uri-path-allowed-chars)))))
 
 (defun eglot-range-region (range &optional markers)
@@ -1187,7 +1187,7 @@ eglot--servers-by-xrefed-file
 (defun eglot--on-shutdown (server)
   "Called by jsonrpc.el when SERVER is already dead."
   ;; Turn off `eglot--managed-mode' where appropriate.
-  (dolist (buffer (eglot--managed-buffers server))
+  (dolist (buffer (map-values (eglot--managed-buffers server)))
     (let (;; Avoid duplicate shutdowns (github#389)
           (eglot-autoshutdown nil))
       (eglot--when-live-buffer buffer (eglot--managed-mode-off))))
@@ -1992,7 +1992,11 @@ eglot--managed-mode
       (add-hook 'eldoc-documentation-functions #'eglot-signature-eldoc-function
                 nil t)
       (eldoc-mode 1))
-    (cl-pushnew (current-buffer) (eglot--managed-buffers (eglot-current-server))))
+
+    (let ((buffer (current-buffer)))
+      (puthash (expand-file-name (buffer-file-name buffer))
+               buffer
+               (eglot--managed-buffers (eglot-current-server)))))
    (t
     (remove-hook 'after-change-functions #'eglot--after-change t)
     (remove-hook 'before-change-functions #'eglot--before-change t)
@@ -2020,10 +2024,10 @@ eglot--managed-mode
     (let ((server eglot--cached-server))
       (setq eglot--cached-server nil)
       (when server
-        (setf (eglot--managed-buffers server)
-              (delq (current-buffer) (eglot--managed-buffers server)))
+        (remhash (expand-file-name (buffer-file-name (current-buffer)))
+                 (eglot--managed-buffers server))
         (when (and eglot-autoshutdown
-                   (null (eglot--managed-buffers server)))
+                   (null (map-values (eglot--managed-buffers server))))
           (eglot-shutdown server)))))))
 
 (defun eglot--managed-mode-off ()
@@ -2346,7 +2350,7 @@ eglot-handle-notification
                           (remhash token (eglot--progress-reporters server))))))))))
 
 (cl-defmethod eglot-handle-notification
-  (_server (_method (eql textDocument/publishDiagnostics)) &key uri diagnostics
+  (server (_method (eql textDocument/publishDiagnostics)) &key uri diagnostics
            &allow-other-keys) ; FIXME: doesn't respect `eglot-strict-mode'
   "Handle notification publishDiagnostics."
   (cl-flet ((eglot--diag-type (sev)
@@ -2357,7 +2361,7 @@ eglot-handle-notification
             (mess (source code message)
               (concat source (and code (format " [%s]" code)) ": " message)))
     (if-let* ((path (expand-file-name (eglot-uri-to-path uri)))
-              (buffer (find-buffer-visiting path)))
+              (buffer (gethash path (eglot--managed-buffers server))))
         (with-current-buffer buffer
           (cl-loop
            initially
-- 
2.40.1


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-28 11:51         ` Ihor Radchenko
@ 2024-03-28 12:47           ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-28 13:52           ` Eli Zaretskii
  1 sibling, 0 replies; 28+ messages in thread
From: Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-28 12:47 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: 70036

Ihor Radchenko <yantar92@posteo.net> writes:

> Theodor Thornhill <theo@thornhill.no> writes:
>
>> I'm sorry, but I don't see the real difference here. Yes, the profile is
>> more detailed this way, but it doesn't change the fact that
>> file-truename is slow, does it?
>
> It does not, but it is important for your suggestion to move
> `file-truename' to the C level.
>
> If the slow parts of `file-truename' are the calls to C subroutines,
> rewriting `file-truename' in C will not help much with the performance.
> (Unless you try hard and move parts of the used subroutines into the C
> implementation of `file-truename', but that will involve copy-pasting
> parts of the existing code and cannot be a good idea without very strong
> justification)
>

Yeah, I agree with this.

>> The question to me is whether or not this is an acceptable performance
>> hit to take for eglot given what it's trying to do, and my opinion is
>> no. Whether or not it should be moved to C is open to suggestion. I'm
>> preparing a patch that only targets Eglot by removing reliance on this
>> function.
>
> If your aim is improving eglot performance specifically, sure.
> If your aim is improving `file-truename' performance in general, a more
> detailed analysis can be helpful.
>

And this.

Wrt eglot specifically or not - I'm not sure whether or not this affects
other parts of emacs. My guess is that it will, but I haven't
investigated that yet.

>>        17839  63% - command-execute
>>        17794  63%  - funcall-interactively
>>        17793  63%   - eval-last-sexp
>>        17793  63%    - #<compiled-function EF0>
>>        17793  63%     - elisp--eval-last-sexp
>>        17791  63%      - eval
>>        17791  63%       - benchmark-call
>>        17788  63%        - #<lambda 219>
>>        17783  63%         - file-truename
>>        17576  62%          - let
>> ...
>
> When you have recursive calls, it is generally more useful to view
> reversed calltree (B) in the profiler report. It will accumulate calls
> to the same function together, regardless on how deep in the call stack
> these calls are made.
>

TIL - thanks :)






^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-28 11:51         ` Ihor Radchenko
  2024-03-28 12:47           ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-03-28 13:52           ` Eli Zaretskii
  1 sibling, 0 replies; 28+ messages in thread
From: Eli Zaretskii @ 2024-03-28 13:52 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: 70036, theo

> Cc: 70036@debbugs.gnu.org
> From: Ihor Radchenko <yantar92@posteo.net>
> Date: Thu, 28 Mar 2024 11:51:06 +0000
> 
> If the slow parts of `file-truename' are the calls to C subroutines,
> rewriting `file-truename' in C will not help much with the performance.

That is only true if the C implementation uses the same C subroutines,
or their bodies.  If, instead, the C implementation uses something
like 'realpath', which produces the fully resolved file name ion one
go, then the profile from the Lisp implementation is not really
relevant.





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-28 12:08           ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-03-30  9:46             ` Felician Nemeth
  2024-03-30 11:18               ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-30 12:45               ` Eli Zaretskii
  0 siblings, 2 replies; 28+ messages in thread
From: Felician Nemeth @ 2024-03-30  9:46 UTC (permalink / raw)
  To: Theodor Thornhill; +Cc: Eli Zaretskii, 70036

Theodor Thornhill <theo@thornhill.no> writes:
> Felician Nemeth <felician.nemeth@gmail.com> writes:

>> Theo, can you email me the relevant messages that your server sends
>> to Emacs?  Does the server send lots of similar diagnostics messages
>> frequently?

> I'll try to include such a report a little later today. 

Thanks, that would be helpful.

> [2. text/x-diff; 0001-Don-t-use-file-truepath-in-Eglot-bug-70036.patch]...

I think using find-buffer-visiting instead of get-file-buffer and
file-truename instead of expand-file-name in Eglot is problematic.
Let's say we have these files:

/project/a.c
/project/a.h -> /other/a.h

Eglot will communicate these file names to the LSP server: /project/a.c
and /other/a.h.  Then the server cannot "associate" a.h with a.c.
Additionally, a.h will be outside of the LSP workspace.

This indeed confuses clangd a bit: it only takes into account the
changes of buffer a.h when it is saved.  (Because it assumes
/project/a.h is not opened in the editor.)

------

Regarding the patch itself, cache invalidation is missing from it.  The
user might kill a buffer or save it under a different name with
write-file.  Changing (PATH -> BUFFER) to (PATH -> (BUFFER, FILENAME))
would probably work.  Eglot should save the current buffer-file-name
when it inserts a new item into the hash of managed-buffers.  And when
it retrieves an item, it should verify whether the buffer-file-name is
the same as the saved file-name.

Can file-truepath change while buffer-file-name remains the same?  Yes,
but I think Eglot could ignore those rare cases, or handle it elsewhere.
(For example, it could update the cache entry after a buffer is saved.)





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-30  9:46             ` Felician Nemeth
@ 2024-03-30 11:18               ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-03-30 12:45               ` Eli Zaretskii
  1 sibling, 0 replies; 28+ messages in thread
From: Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-30 11:18 UTC (permalink / raw)
  To: Felician Nemeth; +Cc: Eli Zaretskii, 70036

[-- Attachment #1: Type: text/plain, Size: 3007 bytes --]

Felician Nemeth <felician.nemeth@gmail.com> writes:

> Theodor Thornhill <theo@thornhill.no> writes:
>> Felician Nemeth <felician.nemeth@gmail.com> writes:
>
>>> Theo, can you email me the relevant messages that your server sends
>>> to Emacs?  Does the server send lots of similar diagnostics messages
>>> frequently?
>
>> I'll try to include such a report a little later today. 
>
> Thanks, that would be helpful.
>

It's a little hard scraping the logs for company related data, so I
haven't done this yet, but the profiles should be pretty expanatory if
you do some edits after applying the below patch.

>> [2. text/x-diff; 0001-Don-t-use-file-truepath-in-Eglot-bug-70036.patch]...
>
> I think using find-buffer-visiting instead of get-file-buffer and
> file-truename instead of expand-file-name in Eglot is problematic.
> Let's say we have these files:
>
> /project/a.c
> /project/a.h -> /other/a.h
>
> Eglot will communicate these file names to the LSP server: /project/a.c
> and /other/a.h.  Then the server cannot "associate" a.h with a.c.
> Additionally, a.h will be outside of the LSP workspace.
>
> This indeed confuses clangd a bit: it only takes into account the
> changes of buffer a.h when it is saved.  (Because it assumes
> /project/a.h is not opened in the editor.)

So in other words this is already a bug in eglot? 

>
> ------
>
> Regarding the patch itself, cache invalidation is missing from it.  The
> user might kill a buffer or save it under a different name with
> write-file.  Changing (PATH -> BUFFER) to (PATH -> (BUFFER, FILENAME))
> would probably work.  Eglot should save the current buffer-file-name
> when it inserts a new item into the hash of managed-buffers.  And when
> it retrieves an item, it should verify whether the buffer-file-name is
> the same as the saved file-name.
>
> Can file-truepath change while buffer-file-name remains the same?  Yes,
> but I think Eglot could ignore those rare cases, or handle it elsewhere.
> (For example, it could update the cache entry after a buffer is saved.)

Actually, cache invalidation is there, at least for killing a
buffer. Major modes are disabled on killing buffer, and it is removed as
a part of the teardown. Saving to a new buffer isn't handled properly
yet, but this looks like something not really supported by Eglot yet
anyway. If I resave a buffer with C-x C-w its content will be placed in
a new buffer, but old one will not be deleted, and the new one will not
be registered with the eglot server before running M-x revert-buffer
anyways. So that seems like a different issue, really.

Please check out the new patch. I find no issues diverging from the
current behavior using this one. It seems to solve all the performance
issues I stated, and now completely new stuff shows up in the profile,
which to me sounds like the bottlenecks have moved, suggesting we get a
nice performance upgrade.

What do you think? I'll test this for a while and install to master in a
few days if nothing comes up :-)

Theo


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Don-t-use-file-truepath-in-Eglot-bug-70036.patch --]
[-- Type: text/x-diff, Size: 8181 bytes --]

From 26e5f3cc8e767215f7c50800a6d713702b8fa144 Mon Sep 17 00:00:00 2001
From: Theodor Thornhill <theo@thornhill.no>
Date: Thu, 28 Mar 2024 12:56:23 +0100
Subject: [PATCH] Don't use file-truepath in Eglot (bug#70036)

`file-truepath' is slow because of recursive calls and being implemented
in lisp.  It seems to not be needed in eglot, but it is used behind the
scenes in `find-buffer-visiting', thus appearing in profiles.  Moving
the implementation to a hash map will yield similar performance
benefits, but wouldn't require us to rewrite `file-truename' in C.

* lisp/progmodes/eglot.el (eglot-lsp-server): Convert 'managed-buffers'
to a hashmap.
(eglot-uri-to-path): Don't use file-truepath, as it is too slow to be
included in the hot path.
(eglot--on-shutdown): Use buffers from buffer map.
(eglot--managed-mode): Add buffer to map, rather than list. Also remove
it from the map on deactivation.
(eglot-handle-notification): Expose server and get buffer from the
buffer map.
---
 lisp/progmodes/eglot.el | 42 +++++++++++++++++++++++------------------
 1 file changed, 24 insertions(+), 18 deletions(-)

diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index f247c43203c..7f4284bf09d 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -1053,8 +1053,8 @@ eglot-lsp-server
     :documentation "Map (DIR -> (WATCH ID1 ID2...)) for `didChangeWatchedFiles'."
     :initform (make-hash-table :test #'equal) :accessor eglot--file-watches)
    (managed-buffers
-    :initform nil
-    :documentation "List of buffers managed by server."
+    :documentation "Map (PATH -> BUFFER) for buffers managed by server."
+    :initform (make-hash-table :test #'equal)
     :accessor eglot--managed-buffers)
    (saved-initargs
     :documentation "Saved initargs for reconnection purposes."
@@ -1085,12 +1085,12 @@ eglot-uri-to-path
 
 (defun eglot-path-to-uri (path)
   "Convert PATH, a file name, to LSP URI string and return it."
-  (let ((truepath (file-truename path)))
+  (let ((expanded-path (expand-file-name path)))
     (if (and (url-type (url-generic-parse-url path))
              ;; It might be MS Windows path which includes a drive
              ;; letter that looks like a URL scheme (bug#59338)
              (not (and (eq system-type 'windows-nt)
-                       (file-name-absolute-p truepath))))
+                       (file-name-absolute-p expanded-path))))
         ;; Path is already a URI, so forward it to the LSP server
         ;; untouched.  The server should be able to handle it, since
         ;; it provided this URI to clients in the first place.
@@ -1098,11 +1098,11 @@ eglot-path-to-uri
       (concat "file://"
               ;; Add a leading "/" for local MS Windows-style paths.
               (if (and (eq system-type 'windows-nt)
-                       (not (file-remote-p truepath)))
+                       (not (file-remote-p expanded-path)))
                   "/")
               (url-hexify-string
                ;; Again watch out for trampy paths.
-               (directory-file-name (file-local-name truepath))
+               (directory-file-name (file-local-name expanded-path))
                eglot--uri-path-allowed-chars)))))
 
 (defun eglot-range-region (range &optional markers)
@@ -1187,7 +1187,7 @@ eglot--servers-by-xrefed-file
 (defun eglot--on-shutdown (server)
   "Called by jsonrpc.el when SERVER is already dead."
   ;; Turn off `eglot--managed-mode' where appropriate.
-  (dolist (buffer (eglot--managed-buffers server))
+  (dolist (buffer (map-values (eglot--managed-buffers server)))
     (let (;; Avoid duplicate shutdowns (github#389)
           (eglot-autoshutdown nil))
       (eglot--when-live-buffer buffer (eglot--managed-mode-off))))
@@ -1992,7 +1992,11 @@ eglot--managed-mode
       (add-hook 'eldoc-documentation-functions #'eglot-signature-eldoc-function
                 nil t)
       (eldoc-mode 1))
-    (cl-pushnew (current-buffer) (eglot--managed-buffers (eglot-current-server))))
+
+    (let ((buffer (current-buffer)))
+      (puthash (expand-file-name (buffer-file-name buffer))
+               buffer
+               (eglot--managed-buffers (eglot-current-server)))))
    (t
     (remove-hook 'after-change-functions #'eglot--after-change t)
     (remove-hook 'before-change-functions #'eglot--before-change t)
@@ -2020,10 +2024,10 @@ eglot--managed-mode
     (let ((server eglot--cached-server))
       (setq eglot--cached-server nil)
       (when server
-        (setf (eglot--managed-buffers server)
-              (delq (current-buffer) (eglot--managed-buffers server)))
+        (remhash (expand-file-name (buffer-file-name (current-buffer)))
+                 (eglot--managed-buffers server))
         (when (and eglot-autoshutdown
-                   (null (eglot--managed-buffers server)))
+                   (null (map-values (eglot--managed-buffers server))))
           (eglot-shutdown server)))))))
 
 (defun eglot--managed-mode-off ()
@@ -2346,7 +2350,7 @@ eglot-handle-notification
                           (remhash token (eglot--progress-reporters server))))))))))
 
 (cl-defmethod eglot-handle-notification
-  (_server (_method (eql textDocument/publishDiagnostics)) &key uri diagnostics
+  (server (_method (eql textDocument/publishDiagnostics)) &key uri diagnostics
            &allow-other-keys) ; FIXME: doesn't respect `eglot-strict-mode'
   "Handle notification publishDiagnostics."
   (cl-flet ((eglot--diag-type (sev)
@@ -2357,7 +2361,7 @@ eglot-handle-notification
             (mess (source code message)
               (concat source (and code (format " [%s]" code)) ": " message)))
     (if-let* ((path (expand-file-name (eglot-uri-to-path uri)))
-              (buffer (find-buffer-visiting path)))
+              (buffer (gethash path (eglot--managed-buffers server))))
         (with-current-buffer buffer
           (cl-loop
            initially
@@ -2842,7 +2846,7 @@ eglot--xref-make-match
 Try to visit the target file for a richer summary line."
   (pcase-let*
       ((file (eglot-uri-to-path uri))
-       (visiting (or (find-buffer-visiting file)
+       (visiting (or (gethash file (eglot--managed-buffers (eglot-current-server)))
                      (gethash uri eglot--temp-location-buffers)))
        (collect (lambda ()
                   (eglot--widening
@@ -3542,13 +3546,14 @@ eglot--propose-changes-as-diff
   (with-current-buffer (get-buffer-create "*EGLOT proposed server changes*")
     (buffer-disable-undo (current-buffer))
     (let ((inhibit-read-only t)
-          (target (current-buffer)))
+          (target (current-buffer))
+          (managed-buffers (eglot--managed-buffers (eglot-current-server))))
       (diff-mode)
       (erase-buffer)
       (pcase-dolist (`(,path ,edits ,_) prepared)
         (with-temp-buffer
           (let* ((diff (current-buffer))
-                 (existing-buf (find-buffer-visiting path))
+                 (existing-buf (gethash path (gethash path managed-buffers)))
                  (existing-buf-label (prin1-to-string existing-buf)))
             (with-temp-buffer
               (if existing-buf
@@ -3583,7 +3588,8 @@ eglot--apply-workspace-edit
                      (eglot--dbind ((VersionedTextDocumentIdentifier) uri version)
                          textDocument
                        (list (eglot-uri-to-path uri) edits version)))
-                   documentChanges)))
+                   documentChanges))
+          (managed-buffers (eglot--managed-buffers (eglot-current-server))))
       (unless (and changes documentChanges)
         ;; We don't want double edits, and some servers send both
         ;; changes and documentChanges.  This unless ensures that we
@@ -3591,7 +3597,7 @@ eglot--apply-workspace-edit
         (cl-loop for (uri edits) on changes by #'cddr
                  do (push (list (eglot-uri-to-path uri) edits) prepared)))
       (cl-flet ((notevery-visited-p ()
-                  (cl-notevery #'find-buffer-visiting
+                  (cl-notevery (lambda (p) (gethash p managed-buffers))
                                (mapcar #'car prepared)))
                 (accept-p ()
                   (y-or-n-p
-- 
2.40.1


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-30  9:46             ` Felician Nemeth
  2024-03-30 11:18               ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-03-30 12:45               ` Eli Zaretskii
  2024-03-31 12:57                 ` Felician Nemeth
  1 sibling, 1 reply; 28+ messages in thread
From: Eli Zaretskii @ 2024-03-30 12:45 UTC (permalink / raw)
  To: Felician Nemeth; +Cc: 70036, theo

> From: Felician Nemeth <felician.nemeth@gmail.com>
> Cc: Eli Zaretskii <eliz@gnu.org>,  70036@debbugs.gnu.org
> Date: Sat, 30 Mar 2024 10:46:00 +0100
> 
> I think using find-buffer-visiting instead of get-file-buffer and
> file-truename instead of expand-file-name in Eglot is problematic.
> Let's say we have these files:
> 
> /project/a.c
> /project/a.h -> /other/a.h
> 
> Eglot will communicate these file names to the LSP server: /project/a.c
> and /other/a.h.  Then the server cannot "associate" a.h with a.c.
> Additionally, a.h will be outside of the LSP workspace.
> 
> This indeed confuses clangd a bit: it only takes into account the
> changes of buffer a.h when it is saved.  (Because it assumes
> /project/a.h is not opened in the editor.)

Can LSP servers resolve symlinks?  If they can, then expand-file-name
is TRT, AFAIU.





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-30 12:45               ` Eli Zaretskii
@ 2024-03-31 12:57                 ` Felician Nemeth
  2024-03-31 13:32                   ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 28+ messages in thread
From: Felician Nemeth @ 2024-03-31 12:57 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 70036, theo

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Felician Nemeth <felician.nemeth@gmail.com>
>> Cc: Eli Zaretskii <eliz@gnu.org>,  70036@debbugs.gnu.org
>> Date: Sat, 30 Mar 2024 10:46:00 +0100
>> 
>> I think using find-buffer-visiting instead of get-file-buffer and
>> file-truename instead of expand-file-name in Eglot is problematic.
>> Let's say we have these files:
>> 
>> /project/a.c
>> /project/a.h -> /other/a.h
>> 
>> Eglot will communicate these file names to the LSP server: /project/a.c
>> and /other/a.h.  Then the server cannot "associate" a.h with a.c.
>> Additionally, a.h will be outside of the LSP workspace.
>> 
>> This indeed confuses clangd a bit: it only takes into account the
>> changes of buffer a.h when it is saved.  (Because it assumes
>> /project/a.h is not opened in the editor.)
>
> Can LSP servers resolve symlinks?  If they can, then expand-file-name
> is TRT, AFAIU.

The LSP specification does not talk about symlinks.  The servers I used
let the operating system resolve symlinks for them.

All in all, I think Eglot should switch to use expand-file-name as well.
It should also use get-file-buffer instead of the more complicated
caching mechanism proposed previously.  Theo?

Nevertheless, Eglot will continue to handle poorly the case when there
is a symlink in a project that points outside of the project.
Hopefully, this can be potentially solved later with the idea behind
eglot--servers-by-xrefed-file.





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#70036: 30.0.50; Move file-truename to the C level
  2024-03-31 12:57                 ` Felician Nemeth
@ 2024-03-31 13:32                   ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 0 replies; 28+ messages in thread
From: Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-31 13:32 UTC (permalink / raw)
  To: Felician Nemeth; +Cc: Eli Zaretskii, 70036

[-- Attachment #1: Type: text/html, Size: 2571 bytes --]

^ permalink raw reply	[flat|nested] 28+ messages in thread

end of thread, other threads:[~2024-03-31 13:32 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-03-27 19:08 bug#70036: 30.0.50; Move file-truename to the C level Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-27 19:44 ` Eli Zaretskii
2024-03-27 21:56   ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-28  1:14     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-28  3:05       ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-28  7:04         ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-28  7:03       ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-28  6:22     ` Eli Zaretskii
2024-03-28  7:03       ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-27 20:12 ` Felician Nemeth
2024-03-27 21:43   ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-28  6:03     ` Eli Zaretskii
2024-03-28  7:10       ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-28  8:52         ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-28 11:55         ` Felician Nemeth
2024-03-28 12:08           ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-30  9:46             ` Felician Nemeth
2024-03-30 11:18               ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-30 12:45               ` Eli Zaretskii
2024-03-31 12:57                 ` Felician Nemeth
2024-03-31 13:32                   ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-28  9:22 ` Ihor Radchenko
2024-03-28 10:59   ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-28 11:18     ` Ihor Radchenko
2024-03-28 11:41       ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-28 11:51         ` Ihor Radchenko
2024-03-28 12:47           ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-28 13:52           ` Eli Zaretskii

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).