From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Dmitry Gutov Newsgroups: gmane.emacs.devel Subject: Re: Adding support for xref jumping to headers/interfaces Date: Sat, 11 Nov 2023 02:42:55 +0200 Message-ID: <4b0b05a2-8c83-7026-4310-327d595dfd8a@gutov.dev> References: <71ea5e83-183f-2ae3-8146-6a31045a0309@yandex.ru> <834jqzafse.fsf@gnu.org> <83h6uv47e8.fsf@gnu.org> <4639d7ca-2109-864c-33c0-38e65f26f262@yandex.ru> <835ybb3txt.fsf@gnu.org> <83wn3q311i.fsf@gnu.org> <412afa2d-5dbc-52da-39c4-99be3873929c@yandex.ru> <83o7p20wdi.fsf@gnu.org> <72b09256-5a1b-8962-9e3c-7d2ffd0dc0d7@yandex.ru> <83ilf925n8.fsf@gnu.org> <95afa441-18ae-e62a-be16-be73a545bbba@yandex.ru> <54cb435f-4d51-4c0d-43d8-2991dd7ee6bd@gutov.dev> Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="21473"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.13.0 To: Spencer Baugh , emacs-devel@gnu.org Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Sat Nov 11 01:43:51 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 1r1c6F-0005Dz-Qc for ged-emacs-devel@m.gmane-mx.org; Sat, 11 Nov 2023 01:43:49 +0100 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r1c5Z-0005f6-PT; Fri, 10 Nov 2023 19:43:05 -0500 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 1r1c5W-0005ev-GL for emacs-devel@gnu.org; Fri, 10 Nov 2023 19:43:02 -0500 Original-Received: from out2-smtp.messagingengine.com ([66.111.4.26]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r1c5T-0003NJ-Rt for emacs-devel@gnu.org; Fri, 10 Nov 2023 19:43:02 -0500 Original-Received: from compute2.internal (compute2.nyi.internal [10.202.2.46]) by mailout.nyi.internal (Postfix) with ESMTP id 2E3595C01F8; Fri, 10 Nov 2023 19:42:59 -0500 (EST) Original-Received: from mailfrontend2 ([10.202.2.163]) by compute2.internal (MEProxy); Fri, 10 Nov 2023 19:42:59 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gutov.dev; h=cc :content-transfer-encoding:content-type:content-type:date:date :from:from:in-reply-to:in-reply-to:message-id:mime-version :references:reply-to:sender:subject:subject:to:to; s=fm2; t= 1699663379; x=1699749779; bh=VfbCeeT6297yL4jS7yOjc0MqlR4Ohq8Y+/m /fnMPBME=; b=Uup3wGFSQpG3p7PSZE4gOvBDgKFJUN/Sa5AcZrF9QARQOrUBxJq vpFvhCXVnybfhmzRMmjcmS6ki1H75kANroJ6yIr0Ad5t14hmu46+3cQ87+V9ydIH R1JOVMVUE/t5/XWynUoUprJSqTtffI/zJzg363v/4XyOAss6392Q+HICHKyjRVmV vjLTf2e9Rd/U6RC6XgPw62Zn/0Mz8XKT0s1Bba9xcR0OPUwF10U+jNK+yBZBlQqy mvCa3DZGY9tKe0Ur92rJb/WEQ7jASh5G+7ILjbkoNoTPkXPIKt+0OLfq2FrAyvVN 4MXPyqrdO5RpSjjjzK8/MErAgRsplQAhXAQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-transfer-encoding:content-type :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:sender:subject:subject:to:to:x-me-proxy:x-me-proxy :x-me-sender:x-me-sender:x-sasl-enc; s=fm3; t=1699663379; x= 1699749779; bh=VfbCeeT6297yL4jS7yOjc0MqlR4Ohq8Y+/m/fnMPBME=; b=c sWqqR8ZAmw9kZaLWUd+z7iTpcYUYPk8VU1VujQi6OM56oHBkqZvuHGk5Z0eb+Dbr 5QlrcKpKzmotihiox1LrGuZbZncqaF9RnS6ndZmnAa9oxg7Xpa+hgRiLduF1on+T qjl+GhUmldkJnekSkkBQUH5BBnUY5tJNKnQY9BAV7GTTAt8AIQLHX0UXEZVOPaTj vZ/p8jbYqSrpEkqtRoAUdr1Wxmj8X8GC92HcnELJ+UR34AC2NWO/0XI3dyA+CI1x P5NRnAQJRlNR9mD5xA76/E41+gkAYcnr4TlJim3ctshWo7FXLaraRS9mEAxGi6n4 /7NOp6LEK4Q8/wqLKEl2g== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvkedruddvgedgvdegucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhepkfffgggfuffvfhfhjggtgfesthejredttdefjeenucfhrhhomhepffhmihht rhihucfiuhhtohhvuceoughmihhtrhihsehguhhtohhvrdguvghvqeenucggtffrrghtth gvrhhnpeeghedthedujeeiteeutddtjeekheejteeukeehffdutdejuedvfeevueeviedu udenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpegumh hithhrhiesghhuthhovhdruggvvh X-ME-Proxy: Feedback-ID: i0e71465a:Fastmail Original-Received: by mail.messagingengine.com (Postfix) with ESMTPA; Fri, 10 Nov 2023 19:42:57 -0500 (EST) Content-Language: en-US In-Reply-To: Received-SPF: pass client-ip=66.111.4.26; envelope-from=dmitry@gutov.dev; helo=out2-smtp.messagingengine.com X-Spam_score_int: -60 X-Spam_score: -6.1 X-Spam_bar: ------ X-Spam_report: (-6.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, NICE_REPLY_A=-3.265, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham 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:312510 Archived-At: On 09/11/2023 19:57, Spencer Baugh wrote: >> Fair enough. So "definitions" are the places we want to see 90% of the >> time when learning about a funciton or a variable. And the other >> reference kinds (BTW, what to call that? "definition kinds"? >> "reference kinds"?) must be apparently less useful. > > Right. > >> Do you know which category does "eglot-find-typeDefinition" falls >> into, and why? Aside from the fact that it, historically, uses a >> separate endpoint. > > It's on the less-useful, more-rarely-used side. It seems like typeDefinition would still be in the category of "definitions", i.e. preferably dispatched through xref-show-definitions-function, so e.g. if only one type definition is found, navigation doesn't show the list and jumps directly. But not in the output of xref-find-definitions. >>> If you mean something a non-default version of xref-find-definitions, or >>> another binding for another basically-identical command, sure, I think >>> that would be good. We can have both that command *and* have a command >>> which prompts for KIND and defaults to "all". >> >> We could indeed, if we decide what to call it. "extras" seems out >> (since it would include both definitions and additional reference >> kinds). Just "xref-find-by-kind"? Then it's less obvious to have the >> default behavior showing all. > > Actually, maybe this should be a backend-specific thing. The backend > could specify a default along with its list of kinds. And if we request > that default kind (which might be 'all), then the backend will decide > what kinds to send us. If the backend specifies the default behavior of xref-find-by-kind, then it's what, another generic method to override? > That neatly avoids us having to make any kind of design decisions about > the semantic meaning of different kinds, and what sorts of kinds exist, > and all that. Well, neatly is in the eye of the beholder - we would end up with more choices for backend authors and less consistent experience for the users. > (Other than having a few standard kinds, which I still think we should > do, if only for the sake of getting the eglot-find-* commands out of > eglot and into xref. But we don't have to really think about what those > standard kinds mean semantically) Ultimately, the backend decides what "implementations" or "type definitions" are anyway. But we get to make suggestions. >>> Although I suppose there's >>> also no limit on how many implementations of a generic function there >>> can be, and that's definitely something that fits in "definitions". >> >> The fact that there are often many definitions for a generic function >> indeed is an argument toward including more "kinds" into >> xref-find-definitions' output by default. But only as many as it would >> be actually useful, especially if the kinds are mutually exclusive (a >> symbol is either one or the other) or intersect very rarely, or can be >> distinguished by context. > > I suggest this should be up to the backend. IMO xref-find-definitions > should stay fast in the common case, but sometimes it will unavoidably > have to include multiple things, like for cl-defgenerics in Elisp. We > can't make the decision about how and when to do that for every backend. It will be up to the backend: the xref-backend-definitions won't be going away. We'll need stronger reasons to change that. > Actually, it occurs to me that if we had an xref-find-implementations > command from the start, with a convenient binding, maybe > xref-find-definitions would just only show the cl-defgeneric, and jump > to it right away. And only if you hit xref-find-implementations would > you jump to the cl-defmethods. We can't make that change now, but I > don't think it would be worse! And if a backend wants a design like > that, I think the backend should be able to have it. Not sure I would like the result that you are describing, but indeed, if a backend wants to do it like this, nothing will be stopping it. Are LSP servers behaving like this? E.g. jdt-ls jumping to the method definition in an interface, even when the owner type is a statically known class? >>> Maybe the backend could decide what kinds get included in "all". Then >>> it could deliberately avoid including anything "reference-like". >> >> Could we want several such commands? E.g. one for "all definition-like >> hits" and another for "all reference-like hits"? With separate sets of >> kinds for definitions and references? > > Possibly, but my suggestion is that "all definition-like hits" and "all > reference-like hits" should just be kinds exposed by the backend. Two different generics returning lists of kinds, or just one? If two, then could one give an example of a "reference-like" kind except for "references"? I've taken a couple of guesses, but I'm exactly sure of them. > *Maybe* we'll have them be "standard kinds" with a command and binding > by default, but maybe not. (We could always add more standard kinds and > commands and bindings later.) Yes. >> It is less flexible because any time a backend wants to use a new >> "kind", it will need to expend some effort and add it to the core >> somehow. Define a new command or two and assign them to the said >> prefix map. This could also lead to conflicts if backend authors don't >> do this carefully enough. > > No, I suggest that xref-backend-extra-kinds should be able to return > whatever arbitrary symbols it wants. Just, if its "kinds" match the > kinds used in the core, then it will benefit from the commands and > bindings defined by the core. > > Perfectly extensible, but still benefiting from standardization. Would the user be able to input/use the kinds that are in the list returned by the backend but are not in the set of "kinds used in the core"? If yes, how? Using a backend-provided command? >>> I do think that if we go with an API which has any notion of "kinds", we >>> should have some standard "kinds" in the core like implementation, >>> declaration, type-definition. I don't see any reason not to do that. >> >> We could have a "registry" of kinds, associating each of them with a >> key. Then the result could be more dynamic, e.g. M-' could be bound to >> a command that reads the key and performs the dispatch to the >> corresponding search (similar to project-switch-project). >> >> And the users (though probably not packages) would later be able to >> customize that mapping, adding new kinds or modifying the keys. >> >> With this approach we don't end up with many xref-find-xyz commands, >> most of which just clutter the namespace, staying unused for a large >> proportion of the users. OTOH, one wouldn't be able to examine the >> prefix map and its contents and definitions with (M-' C-h) -- that's a >> minor reduction in usability. > > The only difference between this and a regular keymap which maps keys to > commands is: > >> we don't end up with many xref-find-xyz commands > > But we can also avoid that by just... not defining many such commands > and kinds. A mode will always have the ability to define its own > mode-specific kinds in the mode's own namespace, which don't clutter > xref-*. Indeed. But if we don't define a dynamic dispatcher at all, it seems we won't need the backend to tell us about the supported kinds either. It would just support some and perhaps return error for unknown kinds. >>>>> xref-extra-kind, prompting for kind: M-' M-' >>>> >>>> Would we need this command, if we had separate commands for each kind >>>> already? >>> This would support kinds which: >>> - are language-specific, >>> - or are more rarely used and don't need a dedicated command, >>> - or are both. >> >> What would be the more frequently used, less language-specific commands? >> >> If we could agree on such list, we could indeed have those >> xref-find-a/b/c definitions and a command with completing-read for the >> rest. > > I suggest that we should attempt to provide the following standard > kinds: > > 'declaration > 'implementation > 'type-definition > > I personally think these would work well for a wide range of languages. > Yes, these are the ones which LSP has; but just because they're from > LSP, doesn't mean they're completely wrong. They seem to me to strike > the right balance of commonality between languages. Fine with me. I don't see any problem with borrowing the list from LSP. >>> Actually, this example has just convinced me that I definitely want >>> "kind-specific commands", even for Elisp. That would be great. >>> For functions without separate declarations, find-implementation and >>> find-declaration could jump to the same place. In some languages, maybe >>> that's just always what happens. >> >> Aren't find-implementation included in find-definition anyway? Or >> *are* the same as find-definition, for example, in LSP's approach? >> Aside from the fact that the latter also works for variables and other >> symbols. > > No, find-implementation shows all the implementations of a generic > function/interface method. (e.g. cl-defmethod) > > find-definition (as I understand it) jumps to the interface definition > itself (e.g. cl-defgeneric). Which in my experience in Elisp, is > usually what I want; I want to see the documentation and default > implementation and surrounding code, before I look at a specific > implementation. I usually want to jump to where the function is implemented. Preferably, for the given type (though we don't support that). Perhaps I would use the proposed xref-find-implementations for this purpose, but it's bound to have a more complex key sequence than 'M-.'. > I think that's a pretty reasonable way for it to work. Actually, maybe > we could add a customization point for the Elisp backend so that a user > can choose to make find-definition work that way, if we do add a > find-implementation command. We could. > There's also find-declaration - for Elisp, I think that one *is* the > mostly same as find-definition, since Elisp doesn't have separate > declarations. (declare-function is kind of a different thing. Maybe > find-declaration should jump to the declare-function instance in the > current file, if it can, but for now it's fine.) Yep. Though for cases where there is no corresponding meaningful implementation, I would rather abort with an error. Doing otherwise seems misleading (like we have special handing of this case, but we don't). >> I think the aforementioned type definitions in Elisp would be better >> included in the "find definitions" set because they generally don't >> clash with other kinds. > > I think they actually do clash in the important case. > > For global variables, e.g. defcustoms, I want M-. to jump to the > defcustom, and find-type-definition would do the same thing. So there's > no clash. And in fact this works today, nothing new. > > For local variables with no global definition though, I would like > M-. to jump straight to the local binding inside the function, but > find-type-definition should jump to the cl-defstruct defining the value > of the variable. That's how it works in Eglot today, at least in Rust > and OCaml. > > Actually supporting "jump to local binding" and "jump to type > definition" in Elisp is of course quite hard, but if we did someday find > a way to support it, I see no reason the behavior shouldn't match Eglot. Yes, agreed. >>>>> - users could still use completing-read to type the kind >>>>> Plus, if we do use M-' or any other short binding for this, we >>>>> should >>>>> almost certainly make it the start of a new prefix-map rather than bind >>>>> M-' directly to the new command; doing otherwise would be wasteful of >>>>> valuable keybinding space. >>>> >>>> If we're going to have separate commands for kinds, that is indeed a >>>> good idea. >>> I almost want to say that we should have it be a prefix regardless >>> of >>> whether we have separate commands for kinds. I guess it depends: >> >> You probably meant "have it be the binding". >> >>> - if we use C-M-?, that's already such a hard key to hit that maybe it's >>> okay if we bind it directly to a command >>> - if we use M-' or something similarly convenient, it would be >>> really >>> tragic to not reclaim all that premium keybinding space. >> >> It's possible that it would work better the other way around: if we >> discover that people really want a prefix map, then it would be easier >> to argue for an easy-to-hit binding like the above. > > True, true. And if we end up with a single command design, then C-M-? > will suffice. I'm guessing people would object to using C-M-? because in the terminal it just maps to M-? which is taken.