Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setting formatter via initializationOptions doesn't work #101

Open
chasecaleb opened this issue Aug 23, 2023 · 7 comments
Open

Setting formatter via initializationOptions doesn't work #101

chasecaleb opened this issue Aug 23, 2023 · 7 comments
Labels
A-emacs Area: Emacs A-lsp Area: LSP conformance issues and missing features

Comments

@chasecaleb
Copy link

Overview

I'm not sure if I'm doing it wrong or if there's a bug in either nil or Emacs eglot, so I'm hoping you can help me. I'm trying to switch from lsp-mode to eglot with Emacs 29 and I can't seem to get initialization-time formatter configuration to work. I expect that calling eglot-format-buffer should work regardless of whether the formatter is configured during initialization or at run-time, but only the latter does anything.

For reference, here's an online version of eglot's info page: https://joaotavora.github.io/eglot/#User_002dspecific-configuration

Version info:

caleb@desktop> nil --version
nil 2023-08-09
caleb@desktop> emacs --version
GNU Emacs 29.1
Copyright (C) 2023 Free Software Foundation, Inc.
GNU Emacs comes with ABSOLUTELY NO WARRANTY.
You may redistribute copies of GNU Emacs
under the terms of the GNU General Public License.
For more information about these matters, see the file named COPYING.
caleb@desktop> uname -a
Linux desktop 6.4.11 #1-NixOS SMP PREEMPT_DYNAMIC Wed Aug 16 16:32:31 UTC 2023 x86_64 GNU/Linux

Broken: initializationOptions

Config:

(add-to-list 'eglot-server-programs
             `(nix-mode . ("nil" :initializationOptions (:nil (:formatting (:command ["nixpkgs-fmt"]))))))

LSP messages shown after loading a nix file and running eglot-format-buffer:

[internal] Wed Aug 23 17:01:54 2023:
(:message "Running language server: nil")
[client-request] (id:1) Wed Aug 23 17:01:54 2023:
(:jsonrpc "2.0" :id 1 :method "initialize" :params
 (:processId 1358112 :rootPath "/home/caleb/code/emacs.nix/" :rootUri "file:///home/caleb/code/emacs.nix" :initializationOptions
  (:nil
   (:formatting
    (:command
     ["nixpkgs-fmt"])))
  :capabilities
  (:workspace
   (:applyEdit t :executeCommand
    (:dynamicRegistration :json-false)
    :workspaceEdit
    (:documentChanges t)
    :didChangeWatchedFiles
    (:dynamicRegistration t)
    :symbol
    (:dynamicRegistration :json-false)
    :configuration t :workspaceFolders t)
   :textDocument
   (:synchronization
    (:dynamicRegistration :json-false :willSave t :willSaveWaitUntil t :didSave t)
    :completion
    (:dynamicRegistration :json-false :completionItem
     (:snippetSupport t :deprecatedSupport t :resolveSupport
      (:properties
       ["documentation" "details" "additionalTextEdits"])
      :tagSupport
      (:valueSet
       [1]))
     :contextSupport t)
    :hover
    (:dynamicRegistration :json-false :contentFormat
     ["markdown" "plaintext"])
    :signatureHelp
    (:dynamicRegistration :json-false :signatureInformation
     (:parameterInformation
      (:labelOffsetSupport t)
      :activeParameterSupport t))
    :references
    (:dynamicRegistration :json-false)
    :definition
    (:dynamicRegistration :json-false :linkSupport t)
    :declaration
    (:dynamicRegistration :json-false :linkSupport t)
    :implementation
    (:dynamicRegistration :json-false :linkSupport t)
    :typeDefinition
    (:dynamicRegistration :json-false :linkSupport t)
    :documentSymbol
    (:dynamicRegistration :json-false :hierarchicalDocumentSymbolSupport t :symbolKind
     (:valueSet
      [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26]))
    :documentHighlight
    (:dynamicRegistration :json-false)
    :codeAction
    (:dynamicRegistration :json-false :codeActionLiteralSupport
     (:codeActionKind
      (:valueSet
       ["quickfix" "refactor" "refactor.extract" "refactor.inline" "refactor.rewrite" "source" "source.organizeImports"]))
     :isPreferredSupport t)
    :formatting
    (:dynamicRegistration :json-false)
    :rangeFormatting
    (:dynamicRegistration :json-false)
    :rename
    (:dynamicRegistration :json-false)
    :inlayHint
    (:dynamicRegistration :json-false)
    :publishDiagnostics
    (:relatedInformation :json-false :codeDescriptionSupport :json-false :tagSupport
     (:valueSet
      [1 2])))
   :window
   (:workDoneProgress t)
   :general
   (:positionEncodings
    ["utf-32" "utf-8" "utf-16"])
   :experimental #s(hash-table size 1 test eql rehash-size 1.5 rehash-threshold 0.8125 data
                               ()))
  :workspaceFolders
  [(:uri "file:///home/caleb/code/emacs.nix" :name "~/code/emacs.nix/")]))
[server-reply] (id:1) Wed Aug 23 17:01:54 2023:
(:jsonrpc "2.0" :id 1 :result
 (:capabilities
  (:codeActionProvider t :completionProvider
   (:triggerCharacters
    ["." "?"])
   :definitionProvider t :documentFormattingProvider t :documentHighlightProvider t :documentLinkProvider
   (:resolveProvider t)
   :documentSymbolProvider t :hoverProvider t :referencesProvider t :renameProvider
   (:prepareProvider t)
   :selectionRangeProvider t :semanticTokensProvider
   (:full
    (:delta :json-false)
    :legend
    (:tokenModifiers
     ["builtin" "conditional" "definition" "delimiter" "escape" "parenthesis" "readonly" "unresolved" "withAttribute"]
     :tokenTypes
     ["boolean" "comment" "constant" "function" "keyword" "number" "operator" "parameter" "path" "property" "punctuation" "string" "struct" "variable"])
    :range t)
   :textDocumentSync
   (:change 2 :openClose t))
  :serverInfo
  (:name "nil" :version "2023-08-09")))
[client-notification] Wed Aug 23 17:01:54 2023:
(:jsonrpc "2.0" :method "initialized" :params #s(hash-table size 1 test eql rehash-size 1.5 rehash-threshold 0.8125 data
                                                            ()))
[client-notification] Wed Aug 23 17:01:54 2023:
(:jsonrpc "2.0" :method "textDocument/didOpen" :params
 (:textDocument
  (:uri "file:///home/caleb/code/emacs.nix/foo.nix" :version 0 :languageId "nix" :text "{}:\n{\n         foo = \"123\";\n}\n")))
[client-notification] Wed Aug 23 17:01:54 2023:
(:jsonrpc "2.0" :method "workspace/didChangeConfiguration" :params
 (:settings #s(hash-table size 1 test eql rehash-size 1.5 rehash-threshold 0.8125 data
                          ())))
[server-request] (id:0) Wed Aug 23 17:01:54 2023:
(:jsonrpc "2.0" :id 0 :method "workspace/configuration" :params
 (:items
  [(:section "nil")]))
[client-reply] (id:0) Wed Aug 23 17:01:54 2023:
(:jsonrpc "2.0" :id 0 :result
 [nil])
[server-request] (id:1) Wed Aug 23 17:01:54 2023:
(:jsonrpc "2.0" :id 1 :method "client/registerCapability" :params
 (:registrations
  [(:id "workspace/didChangeWatchedFiles" :method "workspace/didChangeWatchedFiles" :registerOptions
    (:watchers
     [(:globPattern "/home/caleb/code/emacs.nix/flake.lock")
      (:globPattern "/home/caleb/code/emacs.nix/flake.nix")]))]))
[client-reply] (id:1) Wed Aug 23 17:01:54 2023:
(:jsonrpc "2.0" :id 1 :result nil)
[server-request] (id:2) Wed Aug 23 17:01:54 2023:
(:jsonrpc "2.0" :id 2 :method "workspace/configuration" :params
 (:items
  [(:section "nil")]))
[client-reply] (id:2) Wed Aug 23 17:01:54 2023:
(:jsonrpc "2.0" :id 2 :result
 [nil])
[server-request] (id:3) Wed Aug 23 17:01:54 2023:
(:jsonrpc "2.0" :id 3 :method "window/workDoneProgress/create" :params
 (:token "nil/loadNixosOptionsProgress"))
[client-reply] (id:3) Wed Aug 23 17:01:54 2023:
(:jsonrpc "2.0" :id 3 :result nil)
[server-notification] Wed Aug 23 17:01:54 2023:
(:jsonrpc "2.0" :method "$/progress" :params
 (:token "nil/loadNixosOptionsProgress" :value
  (:kind "begin" :title "Loading NixOS options from 'nixpkgs'")))
[server-notification] Wed Aug 23 17:01:56 2023:
(:jsonrpc "2.0" :method "$/progress" :params
 (:token "nil/loadNixosOptionsProgress" :value
  (:kind "end")))
[client-request] (id:2) Wed Aug 23 17:01:56 2023:
(:jsonrpc "2.0" :id 2 :method "textDocument/formatting" :params
 (:textDocument
  (:uri "file:///home/caleb/code/emacs.nix/foo.nix")
  :options
  (:tabSize 2 :insertSpaces t :insertFinalNewline t :trimFinalNewlines t)))
[server-reply] (id:2) Wed Aug 23 17:01:56 2023:
(:jsonrpc "2.0" :id 2 :result nil)
[client-request] (id:3) Wed Aug 23 17:01:57 2023:
(:jsonrpc "2.0" :id 3 :method "textDocument/hover" :params
 (:textDocument
  (:uri "file:///home/caleb/code/emacs.nix/foo.nix")
  :position
  (:line 0 :character 0)))
[client-request] (id:4) Wed Aug 23 17:01:57 2023:
(:jsonrpc "2.0" :id 4 :method "textDocument/documentHighlight" :params
 (:textDocument
  (:uri "file:///home/caleb/code/emacs.nix/foo.nix")
  :position
  (:line 0 :character 0)))
[server-reply] (id:3) Wed Aug 23 17:01:57 2023:
(:jsonrpc "2.0" :id 3 :result nil)
[server-reply] (id:4) Wed Aug 23 17:01:57 2023:
(:jsonrpc "2.0" :id 4 :result
 [])

I notice there's a workspace/didChangeConfiguration message here, even though I'm not telling eglot to do that since eglot-workspace-configuration is nil, but I think (:settings #s(hash-table size 1 test eql rehash-size 1.5 rehash-threshold 0.8125 data ()))) represents an empty table?

Working: post-initialization settings

If I use this config instead, formatting works:

(setq-default eglot-workspace-configuration
              '(:nil (:formatting (:command ["nixpkgs-fmt"]))))

Messages:

[internal] Wed Aug 23 17:00:19 2023:
(:message "Running language server: /run/current-system/sw/bin/nil")
[client-request] (id:1) Wed Aug 23 17:00:19 2023:
(:jsonrpc "2.0" :id 1 :method "initialize" :params
 (:processId 1357372 :rootPath "/home/caleb/code/emacs.nix/" :rootUri "file:///home/caleb/code/emacs.nix" :initializationOptions #s(hash-table size 1 test eql rehash-size 1.5 rehash-threshold 0.8125 data
                                                                                                                                               ())
  :capabilities
  (:workspace
   (:applyEdit t :executeCommand
    (:dynamicRegistration :json-false)
    :workspaceEdit
    (:documentChanges t)
    :didChangeWatchedFiles
    (:dynamicRegistration t)
    :symbol
    (:dynamicRegistration :json-false)
    :configuration t :workspaceFolders t)
   :textDocument
   (:synchronization
    (:dynamicRegistration :json-false :willSave t :willSaveWaitUntil t :didSave t)
    :completion
    (:dynamicRegistration :json-false :completionItem
     (:snippetSupport t :deprecatedSupport t :resolveSupport
      (:properties
       ["documentation" "details" "additionalTextEdits"])
      :tagSupport
      (:valueSet
       [1]))
     :contextSupport t)
    :hover
    (:dynamicRegistration :json-false :contentFormat
     ["markdown" "plaintext"])
    :signatureHelp
    (:dynamicRegistration :json-false :signatureInformation
     (:parameterInformation
      (:labelOffsetSupport t)
      :activeParameterSupport t))
    :references
    (:dynamicRegistration :json-false)
    :definition
    (:dynamicRegistration :json-false :linkSupport t)
    :declaration
    (:dynamicRegistration :json-false :linkSupport t)
    :implementation
    (:dynamicRegistration :json-false :linkSupport t)
    :typeDefinition
    (:dynamicRegistration :json-false :linkSupport t)
    :documentSymbol
    (:dynamicRegistration :json-false :hierarchicalDocumentSymbolSupport t :symbolKind
     (:valueSet
      [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26]))
    :documentHighlight
    (:dynamicRegistration :json-false)
    :codeAction
    (:dynamicRegistration :json-false :codeActionLiteralSupport
     (:codeActionKind
      (:valueSet
       ["quickfix" "refactor" "refactor.extract" "refactor.inline" "refactor.rewrite" "source" "source.organizeImports"]))
     :isPreferredSupport t)
    :formatting
    (:dynamicRegistration :json-false)
    :rangeFormatting
    (:dynamicRegistration :json-false)
    :rename
    (:dynamicRegistration :json-false)
    :inlayHint
    (:dynamicRegistration :json-false)
    :publishDiagnostics
    (:relatedInformation :json-false :codeDescriptionSupport :json-false :tagSupport
     (:valueSet
      [1 2])))
   :window
   (:workDoneProgress t)
   :general
   (:positionEncodings
    ["utf-32" "utf-8" "utf-16"])
   :experimental #s(hash-table size 1 test eql rehash-size 1.5 rehash-threshold 0.8125 data
                               ()))
  :workspaceFolders
  [(:uri "file:///home/caleb/code/emacs.nix" :name "~/code/emacs.nix/")]))
[server-reply] (id:1) Wed Aug 23 17:00:19 2023:
(:jsonrpc "2.0" :id 1 :result
 (:capabilities
  (:codeActionProvider t :completionProvider
   (:triggerCharacters
    ["." "?"])
   :definitionProvider t :documentFormattingProvider t :documentHighlightProvider t :documentLinkProvider
   (:resolveProvider t)
   :documentSymbolProvider t :hoverProvider t :referencesProvider t :renameProvider
   (:prepareProvider t)
   :selectionRangeProvider t :semanticTokensProvider
   (:full
    (:delta :json-false)
    :legend
    (:tokenModifiers
     ["builtin" "conditional" "definition" "delimiter" "escape" "parenthesis" "readonly" "unresolved" "withAttribute"]
     :tokenTypes
     ["boolean" "comment" "constant" "function" "keyword" "number" "operator" "parameter" "path" "property" "punctuation" "string" "struct" "variable"])
    :range t)
   :textDocumentSync
   (:change 2 :openClose t))
  :serverInfo
  (:name "nil" :version "2023-08-09")))
[client-notification] Wed Aug 23 17:00:19 2023:
(:jsonrpc "2.0" :method "initialized" :params #s(hash-table size 1 test eql rehash-size 1.5 rehash-threshold 0.8125 data
                                                            ()))
[client-notification] Wed Aug 23 17:00:19 2023:
(:jsonrpc "2.0" :method "textDocument/didOpen" :params
 (:textDocument
  (:uri "file:///home/caleb/code/emacs.nix/foo.nix" :version 0 :languageId "nix" :text "{}:\n{\n         foo = \"123\";\n}\n")))
[client-notification] Wed Aug 23 17:00:19 2023:
(:jsonrpc "2.0" :method "workspace/didChangeConfiguration" :params
 (:settings
  (:nil
   (:formatting
    (:command
     ["nixpkgs-fmt"])))))
[server-request] (id:0) Wed Aug 23 17:00:19 2023:
(:jsonrpc "2.0" :id 0 :method "workspace/configuration" :params
 (:items
  [(:section "nil")]))
[client-reply] (id:0) Wed Aug 23 17:00:19 2023:
(:jsonrpc "2.0" :id 0 :result
 [(:formatting
   (:command
    ["nixpkgs-fmt"]))])
[server-request] (id:1) Wed Aug 23 17:00:19 2023:
(:jsonrpc "2.0" :id 1 :method "client/registerCapability" :params
 (:registrations
  [(:id "workspace/didChangeWatchedFiles" :method "workspace/didChangeWatchedFiles" :registerOptions
    (:watchers
     [(:globPattern "/home/caleb/code/emacs.nix/flake.lock")
      (:globPattern "/home/caleb/code/emacs.nix/flake.nix")]))]))
[client-reply] (id:1) Wed Aug 23 17:00:19 2023:
(:jsonrpc "2.0" :id 1 :result nil)
[server-request] (id:2) Wed Aug 23 17:00:19 2023:
(:jsonrpc "2.0" :id 2 :method "workspace/configuration" :params
 (:items
  [(:section "nil")]))
[client-reply] (id:2) Wed Aug 23 17:00:19 2023:
(:jsonrpc "2.0" :id 2 :result
 [(:formatting
   (:command
    ["nixpkgs-fmt"]))])
[server-request] (id:3) Wed Aug 23 17:00:19 2023:
(:jsonrpc "2.0" :id 3 :method "window/workDoneProgress/create" :params
 (:token "nil/loadNixosOptionsProgress"))
[client-reply] (id:3) Wed Aug 23 17:00:19 2023:
(:jsonrpc "2.0" :id 3 :result nil)
[server-notification] Wed Aug 23 17:00:19 2023:
(:jsonrpc "2.0" :method "$/progress" :params
 (:token "nil/loadNixosOptionsProgress" :value
  (:kind "begin" :title "Loading NixOS options from 'nixpkgs'")))
[server-notification] Wed Aug 23 17:00:20 2023:
(:jsonrpc "2.0" :method "$/progress" :params
 (:token "nil/loadNixosOptionsProgress" :value
  (:kind "end")))
[client-request] (id:2) Wed Aug 23 17:00:26 2023:
(:jsonrpc "2.0" :id 2 :method "textDocument/hover" :params
 (:textDocument
  (:uri "file:///home/caleb/code/emacs.nix/foo.nix")
  :position
  (:line 3 :character 1)))
[client-request] (id:3) Wed Aug 23 17:00:26 2023:
(:jsonrpc "2.0" :id 3 :method "textDocument/documentHighlight" :params
 (:textDocument
  (:uri "file:///home/caleb/code/emacs.nix/foo.nix")
  :position
  (:line 3 :character 1)))
[server-reply] (id:2) Wed Aug 23 17:00:26 2023:
(:jsonrpc "2.0" :id 2 :result nil)
[server-reply] (id:3) Wed Aug 23 17:00:26 2023:
(:jsonrpc "2.0" :id 3 :result
 [])
[client-request] (id:4) Wed Aug 23 17:00:28 2023:
(:jsonrpc "2.0" :id 4 :method "textDocument/formatting" :params
 (:textDocument
  (:uri "file:///home/caleb/code/emacs.nix/foo.nix")
  :options
  (:tabSize 2 :insertSpaces t :insertFinalNewline t :trimFinalNewlines t)))
[server-reply] (id:4) Wed Aug 23 17:00:28 2023:
(:jsonrpc "2.0" :id 4 :result
 [(:newText "{}:\n{\n  foo = \"123\";\n}\n" :range
   (:end
    (:character 0 :line 4)
    :start
    (:character 0 :line 0)))])
[internal] (id:5) Wed Aug 23 17:00:29 2023:
(:deferring :textDocument/hover :id 5 :params
 (:textDocument
  (:uri "file:///home/caleb/code/emacs.nix/foo.nix")
  :position
  (:line 3 :character 1)))
[internal] (id:6) Wed Aug 23 17:00:29 2023:
(:deferring :textDocument/documentHighlight :id 6 :params
 (:textDocument
  (:uri "file:///home/caleb/code/emacs.nix/foo.nix")
  :position
  (:line 3 :character 1)))
[client-notification] Wed Aug 23 17:00:29 2023:
(:jsonrpc "2.0" :method "textDocument/didChange" :params
 (:textDocument
  (:uri "file:///home/caleb/code/emacs.nix/foo.nix" :version 1)
  :contentChanges
  [(:range
    (:start
     (:line 0 :character 0)
     :end
     (:line 4 :character 0))
    :rangeLength 30 :text "{}:\n{\n  foo = \"123\";\n}\n")]))
[internal] Wed Aug 23 17:00:29 2023:
(:maybe-run-deferred
 (6 5))
[client-request] (id:6) Wed Aug 23 17:00:29 2023:
(:jsonrpc "2.0" :id 6 :method "textDocument/documentHighlight" :params
 (:textDocument
  (:uri "file:///home/caleb/code/emacs.nix/foo.nix")
  :position
  (:line 3 :character 1)))
[client-request] (id:5) Wed Aug 23 17:00:29 2023:
(:jsonrpc "2.0" :id 5 :method "textDocument/hover" :params
 (:textDocument
  (:uri "file:///home/caleb/code/emacs.nix/foo.nix")
  :position
  (:line 3 :character 1)))
[server-reply] (id:6) Wed Aug 23 17:00:29 2023:
(:jsonrpc "2.0" :id 6 :result
 [])
[server-reply] (id:5) Wed Aug 23 17:00:29 2023:
(:jsonrpc "2.0" :id 5 :result nil)
@DamienCassou
Copy link

I have the same issue with the same setup.

@dschrempf
Copy link

I also observe that automatic formatting is broken with nil.

@oxalica
Copy link
Owner

oxalica commented Oct 9, 2023

I'm not familiar with emacs but the log seems weird since it sends initializationOptions but responding [nil] for workspace/configuration requests. I tried to use initializationOptions with workspace/configuration disabled under nvim and it seems to work fine.

Could you set env var NIL_LOG to be nil=debug and provide the stderr of the server? If it's hard to retrieve the server's stderr, NIL_LOG_PATH can also be set to a file path to write the log into.

@oxalica oxalica added A-lsp Area: LSP conformance issues and missing features A-emacs Area: Emacs labels Oct 9, 2023
@cmacrae
Copy link

cmacrae commented Mar 24, 2024

I have this working with the following config. looks like, when you set initializationOptions for a language server, the options are nested under the name of the language server already. so, having your options under (:nil) is actually incorrect.

(use-package eglot
  :ensure nil
  :after inheritenv
  :hook
  (prog-mode . eglot-ensure)
  (prog-mode . (lambda () (add-hook 'before-save-hook 'eglot-format nil t)))
  :config
  (with-eval-after-load 'eglot
    (dolist (mode '((nix-mode . ("nil" :initializationOptions
                                       (:formatting (:command [ "nixpkgs-fmt" ]))))))
      (add-to-list 'eglot-server-programs mode))))

@yzhou216
Copy link

yzhou216 commented Oct 3, 2024

@cmacrae This might be irrelevant to the original issue, but I had some problems setting this up. I did it without using the with-eval-after-load macro and that didn't work although I see the eglot-server-programs getting updated correctly. Your config worked for me, could you please explain why you're doing it this way?

@cmacrae
Copy link

cmacrae commented Oct 3, 2024

@yzhou216 glad it works for you :) without with-eval-after-load eglot may not have finished loading. even though eglot-server-programs may be getting updated, if eglot hasn't finished loading, evaluation of the list may somehow not be effective - i can only surmise without digging into eglot's machinery

@yzhou216
Copy link

yzhou216 commented Oct 6, 2024

@cmacrae Thank you for the explanation! That was very helpful :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-emacs Area: Emacs A-lsp Area: LSP conformance issues and missing features
Projects
None yet
Development

No branches or pull requests

6 participants