From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Theodor Thornhill via "Emacs development discussions." Newsgroups: gmane.emacs.devel Subject: Re: LSP vs Emacs indentation [Was: bug#64784: 30.0.50; Eglot: Lisp error: (wrong-type-argument number-or-marker-p return) in eglot--post-self-insert-hook] Date: Mon, 24 Jul 2023 20:13:04 +0200 Message-ID: <871qgxxjnz.fsf@thornhill.no> References: <87bkg4bkfu.fsf@fastmail.fm> <83a5voa328.fsf@gnu.org> <87h6pw9tpa.fsf@gmail.com> <875y6bm5ut.fsf@fastmail.fm> <87r0oz6i9p.fsf_-_@gmail.com> <87wmypb5dq.fsf@gnu.org> Reply-To: Theodor Thornhill Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="38021"; mail-complaints-to="usenet@ciao.gmane.io" Cc: Eli Zaretskii , Stefan Monnier , emacs-devel@gnu.org To: Tassilo Horn , =?utf-8?B?Sm/Do28gVMOhdm9yYQ==?= Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Mon Jul 24 20:13:50 2023 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1qO046-0009iV-1F for ged-emacs-devel@m.gmane-mx.org; Mon, 24 Jul 2023 20:13:50 +0200 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1qO03T-0008F1-QP; Mon, 24 Jul 2023 14:13:11 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1qO03S-0008Eo-7q for emacs-devel@gnu.org; Mon, 24 Jul 2023 14:13:10 -0400 Original-Received: from out-35.mta1.migadu.com ([95.215.58.35]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1qO03P-0003st-Qw for emacs-devel@gnu.org; Mon, 24 Jul 2023 14:13:09 -0400 X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=thornhill.no; s=key1; t=1690222385; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=qBkNlHaVpgsFITIMSZJcbpqdPnu/V13I7nI872Qa+n0=; b=ZhuW88rZkc0gQEfb9M8DXdJjx+UQQLMMbrrh9EkwiVKxqnSJBLf5X5qXkQRiM4Yj27UIPT Wslw8qu9DySfNky3Q2MioY82SvoLsCt8RXjQcbYfTIIYiSHhHnCGbJxPlhAb5xkAl2GI/m I5SwzymxuFnw3r3gXQ9nRvoQeckqdByOdGQ7W+Wq5I9Ze08W6bcCso7/4XMu2vEyXd18lb zOFoJfrEt0clxOW8vMbe1O2MwLtwmVmgkr6y7hr9aJ30sCOEV7M3NbR+oK0KTZQ0mo470v KkTtTQumuAG0bzndPgHQCmLrAysVdBbjhc5DQkM/XEKKzx6pnbOlaraDDbGUaA== In-Reply-To: <87wmypb5dq.fsf@gnu.org> X-Migadu-Flow: FLOW_OUT Received-SPF: pass client-ip=95.215.58.35; envelope-from=theo@thornhill.no; helo=out-35.mta1.migadu.com X-Spam_score_int: -16 X-Spam_score: -1.7 X-Spam_bar: - X-Spam_report: (-1.7 / 5.0 requ) BAYES_00=-1.9, DKIM_INVALID=0.1, DKIM_SIGNED=0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.devel:308066 Archived-At: Tassilo Horn writes: > Jo=C3=A3o T=C3=A1vora writes: > > Hi Jo=C3=A3o, > >> If your aim is to make the LSP side "win", > > Yes, please! > >> I don't think you should use the "trigger character" technique >> specifically. But in Emacs you can of course bind keys to commands >> that invoke 'eglot-format' synchronously. >> >> Even better, I think the most correct way is to buffer-locally set >> 'indent-line-function' and 'indent-region-function', so you can keep >> the familiar feeling of TAB. > > I've now tried this: > > (defun th/eglot-indent-line () > (eglot-format (line-beginning-position) (line-end-position))) >=20=20=20 > (defun th/eglot-format-setup () > (setq-local indent-region-function #'eglot-format) > (setq-local indent-line-function #'th/eglot-indent-line)) >=20=20=20 > (add-hook 'eglot-managed-mode-hook #'th/eglot-format-setup) > > Basically, it works, but it seems rust-analyzer doesn't support > formatting of only a range. > > eglot--error: [eglot] Unsupported or ignored LSP capability `:documentR= angeFormattingProvider' > > No big deal, so now I tried just using eglot-format also an > indent-line-function. But indeed, then I cannot insert newlines > anymore. :-) > > I'll try experimenting a bit more at some time. > One issue with this is that most/many formatters remove whitespace, and for indentation _inserting_ whitespace is paramount. Consider (| is the cursor) ``` func foo() {|} ``` Now if you type RET we'd expect some incantation of ``` func foo() { |=20 } ``` to be the expected output, not the cursor at col 0, which is what happens now. That means we'd have to do something like ``` (defun eglot-indent-line () (eglot-format (line-beginning-position) (line-end-position)) (eglot-newline-and-indent-according-to-mode)) ``` Where the 'eglot-newline-and-indent-according-to-mode' has to calculate the expected indentation. I don't understand how we'd expect the formatters to do that indentation for us. They only care about code already written, not code yet to be written. So if we'd have to calculate that offset anyway, do we win much? How about a hybrid approach, where eglot can take care of the formatting part, but the "move cursor to indentation of parent + N spaces" is handled by the respective major modes? This would make this contrived example work: ``` func foo() { foo()| foo() foo()=20=20=20=20=20=20 } ``` Now type RET, and output of the file would be: ``` func foo() { foo() | foo() foo()=20=20=20=20=20=20 } ``` or ``` func foo() { if err !=3D nil {|} } ``` Now type RET, and output of the file would be: ``` func foo() { if err !=3D nil { | } } ``` The placement of the wrongly indented function calls are formatted by eglot, and the indentation of the blank line is handled by emacs. This diff will show a very naive example implementation of this. Thanks, Theo @@ -1816,9 +1816,16 @@ 'eglot--ensure-list ;;; Minor modes ;;; + +(defun eglot-electric-newline () + (interactive) + (eglot-format) + (newline-and-indent)) + (defvar eglot-mode-map (let ((map (make-sparse-keymap))) (define-key map [remap display-local-help] #'eldoc-doc-buffer) + (define-key map [remap newline] #'eglot-electric-newline) map)) =20 (defvar-local eglot--current-flymake-report-fn nil