From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Eshel Yaron Newsgroups: gmane.emacs.devel Subject: Extract to new definition (was: Adding refactoring capabilities to Emacs) Date: Sun, 01 Oct 2023 17:07:14 +0200 Message-ID: References: <83fs4f36wi.fsf@gnu.org> Mime-Version: 1.0 Content-Type: text/plain Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="37829"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Cc: Eli Zaretskii , "Philip K." , Dmitry Gutov , Stefan Monnier , emacs-devel@gnu.org To: =?utf-8?B?Sm/Do28gVMOhdm9yYQ==?= Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Sun Oct 01 17:08:37 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 1qmy3g-0009aj-OO for ged-emacs-devel@m.gmane-mx.org; Sun, 01 Oct 2023 17:08:36 +0200 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1qmy2T-0003hp-Sr; Sun, 01 Oct 2023 11:07:21 -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 1qmy2S-0003hg-RR for emacs-devel@gnu.org; Sun, 01 Oct 2023 11:07:20 -0400 Original-Received: from mail.eshelyaron.com ([107.175.124.16] helo=eshelyaron.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1qmy2R-0007IB-2w; Sun, 01 Oct 2023 11:07:20 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=eshelyaron.com; s=mail; t=1696172837; bh=xXmwjK8o+qayGjqBGoylPypRZiRD0xWcwqmO5Zd4NYw=; h=From:To:Cc:Subject:In-Reply-To:References:Date:From; b=qqnOAxqS9dVPIatsNdsxhGXue3MR9NceiqXVkP1Mi+e1GbAPPaDtvEhfUmEh6+B0B JPrq1LmaL3so9+d+ufcIqYziIcSu6XCybl345UgVbxVlPHBMyVoG0YXFI8U+hJnLCm 4fI6G3okkErs55GcldaphZvLBTHnpWBLwE4bmOWWUE+m2SY0OYK2pdOzR984K/zGyt 4GVi5NBS6fziKGOb/pUYx0JCtcysD1mU5dO68Yv4hmCCILoLSdL8VnLLVjxU8JF8UM rGg8822iLhoShQzqE/vx438igpz0h9rLAc0gn6N0yjaemFhZTBNfMq6vzU6llz7zSZ VpOk+mWd9jWog== In-Reply-To: (Eshel Yaron's message of "Fri, 08 Sep 2023 15:18:29 +0200") Received-SPF: pass client-ip=107.175.124.16; envelope-from=me@eshelyaron.com; helo=eshelyaron.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.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, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 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:311221 Archived-At: Hi, >> ...can you, say, extract subexpressions to variables, organize >> imports, etc? > > Yes, there are some other things already in place, like updating imports > and adding exports. I have a work-in-progress implementation for > extracting subexpressions (subterms, in Prolog), but nothing in terms of > existing commands. Since writing this message, I've added that "extract" command to `sweeprolog`. I'm still working on some final tweaks, but I'm pretty happy with the result so I thought I'd share some details and insights. The new command is called `sweeprolog-extract-region-to-predicate`. You call it with point and mark surrounding some code inside one definition that you want to extract to a new, separate definition. You do this either because you want to reuse this piece of computation somewhere else or simply to break a large complicated definition into smaller parts. For example, with the following code in a `sweeprolog-mode` buffer, where ^ and $ denote the region: --8<---------------cut here---------------start------------->8--- foo(X, Y, Z) :- ^bar1(X,A), bar2(A,B), bar3(C,Y)$, baz(Y,Z). --8<---------------cut here---------------end--------------->8--- Typing `M-x sweeprolog-extract-region-to-predicate RET bar RET` yields: --8<---------------cut here---------------start------------->8--- foo(X, Y, Z) :- bar(X,Y), baz(Y,Z). bar(X, Y) :- bar1(X,A), bar2(A,B), bar3(C,Y). --8<---------------cut here---------------end--------------->8--- The complex goal (expression) with `bar1`, `bar2` and `bar3` makes way for a single call to `bar`, while a definition of `bar` is created in the current buffer. Crucially, this command analyzes the selected goal and its context to determine how many and which arguments the new definition should have. In the simplest case, you mark a piece of code, call the command, it prompts you for the name of the new definition, and performs the extraction at once. There are, however, some subtleties to consider: - The selection may be invalid. Invalid means that the contents of the region do not constitute a valid Prolog goal, that's a clear user error, so this command complains and bails. - Some extraction operations are unsafe. Unsafe means that extracting the selected piece of code may change the semantics of the program. (Un)safety is very language-specific, and it's more difficult to determine than validity, since safety is a semantic property while validity is syntactic. For Prolog, extracting a goal that contains a "cut" can have a semantic effect, because the "cut" operates on the defintion in which it occurs. In fact, it's not generally decidable whether extraction will have an affect on the semantics of the program or not. That's why `sweeprolog-extract-region-to-predicate` asks for confirmation if it can't be certain that extracting the specified goal preserves the semantics of the surrounding code. - The name you give to the new definition might already be in use. That's something `sweeprolog-extract-region-to-predicate` does not currently protect you from, but I intend to address that soon. Ideally, the command should detect this collision early, and bail or ask for confirmation before changing the buffer. My hope is that when we get that generic refactoring interface in Emacs, it'll support these distinctions and handle them appropriately. This command also has an extension on top of the basic local extraction operation: If you call it with a prefix argument, then after performing the extraction as usual, it searches the current buffer for other goals that the extracted goal subsumes, and suggests replacing them one after the other with calls to the newly defined predicate with appropriate arguments. This is useful when you have several occurrences of some complex idiom scattered around that you want to extract to a stand alone definition. In terms of user interface, there's also a user option that says where to insert the new definition, and that's about it. (A couple more details are mentioned in the manual: https://eshelyaron.com/man/sweep/Extract-Goal.html) Best, Eshel