Skip to content

Extract gomodifytags to a library#117

Merged
fatih merged 1 commit intofatih:mainfrom
madelinekalil:mkalil/create-package
Mar 25, 2025
Merged

Extract gomodifytags to a library#117
fatih merged 1 commit intofatih:mainfrom
madelinekalil:mkalil/create-package

Conversation

@madelinekalil
Copy link
Contributor

This PR moves the gomodifytags tool into its package to be imported by other applications (i.e.: gopls).

A new entrypoint into gomodifytags is provided via config.Apply(), which uses the specified start and end positions to apply struct tag modifications to the node without returning the new content.

related: golang/vscode-go#2002

@madelinekalil madelinekalil force-pushed the mkalil/create-package branch 2 times, most recently from 677b892 to 95ee7b3 Compare February 19, 2025 16:02

ast.Inspect(node, rewriteFunc)

c.Start = start
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks wrong. We should not be mutating the config. Perhaps this is meant to set the output start and end?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the Start and End values aren't configured by the user, we just store them in the config. They're set by gomodifytags during the findSelection step which uses the line, offset, or struct selection to determine the start and end position of fields that will be modified. (We don't use findSelection in the new Apply routine since we are handling finding the selection on the gopls side).


Fset *token.FileSet

Remove []string
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just did an experiment, that I think works out nicely:

There's this tension that many of the fields in the Config are actually related to the set up and tear down of the Run, and are not relevant to Apply. Specifically, if the Run is structured as Parse->Find->Apply->Format, many of these options are related to Parsing, Finding, and Formatting.

I think we should break up these fields into two structs, called something like Pass and Mutation. Pass holds everything above Remove, and Mutation holds everything in Remove and below.

The signature of Apply can then be:
func (m *Mutation) Apply(fset *token.FileSet, node ast.Node, mutation Mutation)

or maybe
func (m *Mutation) Apply(fset *token.FileSet, node ast.Node, start, end token.Pos)

(I'm not sure whether we need to pass start, end in gopls).

In fact, the more I read this, the more I think we should keep the Pass objects in the main package. We can of course move it here later if it is needed, but it is rather specific to the command line, and it seems prudent to keep the new API we are adding as minimal as possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I moved the Pass fields and methods back into the main package, so now the modifytags package only exposes Apply, Rewrite, Validate, and the Mutation and RewriteErrors types used by the main run() method

@madelinekalil madelinekalil force-pushed the mkalil/create-package branch 2 times, most recently from 3085e5f to 8aff27c Compare February 25, 2025 21:03
Copy link
Collaborator

@findleyr findleyr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your patience! I have some high level comments about the API which, while superficial, I think we should sort out before finishing the review.

@madelinekalil madelinekalil force-pushed the mkalil/create-package branch 2 times, most recently from 0f50ad5 to 1cfbd05 Compare March 5, 2025 16:25
@fatih
Copy link
Owner

fatih commented Mar 5, 2025

Thanks everyone for the thorough work. I welcome the new changes. On my end, the changes look great and I can merge it whenever you want. Please let me know if this finished and ready to be merged. I can cut a new release afterwards (v1.18.0)

Copy link
Collaborator

@findleyr findleyr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great! I mostly commented on comments, with two meaningful exceptions:

  1. I think that Apply should accept a range of byte offsets, not a range of lines, since that is more flexible.
  2. Should we rename Mutation to Modification? I hadn't thought of this name before, and now that I have, it seems obvious.

I've reviewed everything: once we sort out these comments, this is good to go!


// Apply applies the struct tag modifications of the receiver to all
// structs contained within the given node between start and end lines, mutating its input.
func (mut *Mutation) Apply(fset *token.FileSet, node ast.Node, start, end int) error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eek, I wonder if we should make this an offset, rather than a line. Sorry for missing this in my review of the API before.

We can keep the command line interface to pass line, but passing an offset is more useful for a tool integration.

For example, if I have struct{ a, b, c int }, and want to add struct tags to a and b, I shouldn't need to split my type expression on to multiple lines.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We decided to pass token.Pos for start and end.

@madelinekalil madelinekalil force-pushed the mkalil/create-package branch 2 times, most recently from a7689e7 to 3a5d8cf Compare March 13, 2025 20:34
@madelinekalil madelinekalil force-pushed the mkalil/create-package branch from 3a5d8cf to fba5866 Compare March 14, 2025 16:04
@madelinekalil madelinekalil force-pushed the mkalil/create-package branch from fba5866 to 1cc6959 Compare March 19, 2025 20:29
Copy link
Collaborator

@findleyr findleyr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM modulo the remaining comments, which I think are straightforward! Once these are addressed, let's ask fatih to review.

main.go Outdated
structs[x.Pos()] = &structType{
name: structName,
node: x,
structs[x.Pos()] = &ast.TypeSpec{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm so sorry, but my understanding of this code was inaccurate, and the suggestion to use a TypeSpec was wong.

I thought we only cared about type declarations, but I see now that the pre-existing logic also handles

var V struct {
   F int
}

That's why we can't simply keep track of type specs. Therefore, I think you should just revert back to the structType type, perhaps with longer documentation explaining that structs can also be selected by their variable name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense thanks, I also forgot about handling the variable case.

This PR moves the gomodifytags tool into its package to be imported by other applications (i.e.: gopls).

A new entrypoint into gomodifytags is provided via Modification.Apply(), which uses the specified start and end positions
to apply struct tag modifications to the file without returning the new content.

Also updates staticcheck version

related: golang/vscode-go#2002
@madelinekalil madelinekalil force-pushed the mkalil/create-package branch from 1cc6959 to 91c121f Compare March 21, 2025 20:30
@findleyr
Copy link
Collaborator

@fatih I think these are ready for your review.

I suggest we proceed as follows:

  1. Finish this review and merge this PR. Do not tag yet.
  2. @madelinekalil will integrate these into gopls by requiring gomodifytags@master (a pseudoversion).
  3. We review this integration.
  4. Tag the next version gomodifytags.

This may be overly cautious, but it's possible that reviewing the integration into gopls may turn up API considerations that we didn't anticipate in this CL, so if we hold off on tagging a bit, it may be better.

@fatih
Copy link
Owner

fatih commented Mar 25, 2025

Thanks for the great work. I've followed along all the contributions made so far and I think I don't have much to add here. I agree that it makes sense to test the HEAD before cutting a release. I'm merging the PR now, and won't cut a tag until I get a sign off from you folks.

@fatih fatih merged commit 8c663b1 into fatih:main Mar 25, 2025
1 check passed
gopherbot pushed a commit to golang/tools that referenced this pull request Apr 23, 2025
Previously, the VSCode extension installed and
executed gomodifytags directly.
To reduce third-party dependencies in gopls, we are submitting a
change to gomodifytags to extract the functionality into a library
called by gopls (fatih/gomodifytags#117). Now, the extension will call the gopls.modify_tags
command and the command handler will invoke the modifytags package's
Apply() method.

Also adds basic code actions for adding and removing struct tags. This will be extended once we
implement a dialogue feature in gopls.

Change-Id: Idf130c95eec5d469a454cb6f21897629b3364b06
Reviewed-on: https://go-review.googlesource.com/c/tools/+/652495
Reviewed-by: Hongxiang Jiang <hxjiang@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Findley <rfindley@google.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants