VsixCommunity / Community.VisualStudio.Toolkit

Making it easier to write Visual Studio extensions
Other
256 stars 44 forks source link

How best to store per project/solution configuration used by an extension? #274

Open mrlacey opened 2 years ago

mrlacey commented 2 years ago

I have (and want to make more) extensions that rely on configuration appropriate to each project or solution that is loaded.

What's the best approach for storing this data? Is it to add extension-specific files to the project (or solution) that can hold this configuration? Is it possible to combine this configuration with existing files? (maybe *.*proj or .editorconfig files) Any insights or best practices that anyone can recommend?

Does (or can) the toolkit do anything to make this easier?

RobertvanderHulst commented 2 years ago

Have you considered the "user project" file. We use that for example to store the "view all files" setting.

mrlacey commented 2 years ago

Have you considered the "user project" file. We use that for example to store the "view all files" setting.

Do you have more details, or can you point me to an example?

RobertvanderHulst commented 2 years ago

Matt,

Our project system is based on MPF. We have added a property of type MSBuild.Project to our project type that contains a BuildProject. When we need the data from the user file we either create a new buildproject (this.userBuildProject = new MSBuild.Project()) or we open the existing project

System.Xml.XmlReader xmlReader = System.Xml.XmlReader.Create(this.UserFileName);
this.userBuildProject = new MSBuild.Project(xmlReader);

Because this is now a MsBuild.Project we can use the normal MsBuild project api to set /get properties:

string propertyValue = this.UserBuildProject.GetPropertyValue(XProjectFileConstants.ProjectView);
.
.

this.UserBuildProject.SetProperty(
                XProjectFileConstants.ProjectView,
                (this.showAllFilesEnabled ? XProjectFileConstants.ShowAllFiles : XProjectFileConstants.ProjectFiles));

Our VS integration code can be found here: https://github.com/X-Sharp/XSharpPublic/tree/main/VisualStudio

The result is a file with the name MyProject.xsproj.user (our project file extension is "xsproj") with the contents

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <ProjectView>ShowAllFiles</ProjectView>
  </PropertyGroup>
</Project>

and we have added the .user extension to our .gitignore file

mrlacey commented 2 years ago

thanks @RobertvanderHulst I'm already doing something similar but was wondering about alternatives, especially for extensions that are working with pre-existing project types.

reduckted commented 2 years ago

You can save to the .csproj.user file without needing to use MSBuild. The IVsBuildPropertyStorage.SetPropertyValue method has a parameter that lets you specify whether to use the project file or the user file:

if (hierarchy is IVsBuildPropertyStorage storage) {
    // Save to the project file.
    storage.SetPropertyValue("Foo", "", (uint)_PersistStorageType.PST_PROJECT_FILE, "Bar");
    // Save to the user file.
    storage.SetPropertyValue("Foo", "", (uint)_PersistStorageType.PST_USER_FILE, "Bar");
}

The toolkit does not currently allow you to choose, but I've just added #283 which adds overloads to the TrySetAttributeAsync methods:

await project.TrySetAttributeAsync("Foo", "Bar", ProjectStorageType.ProjectFile);  // Save to the `.csproj` file.
await project.TrySetAttributeAsync("Foo", "Bar", ProjectStorageType.UserFile); // Save to the `.csproj.user` file.

I've never tried saving to the solution's user options file, but I did find this article, which might be of some help. Seems a bit more complicated than saving to a project file. https://docs.microsoft.com/visualstudio/extensibility/internals/solution-user-options-dot-suo-file

What's the best approach for storing this data? ... Any insights or best practices that anyone can recommend?

I guess it depends on what you intend the configuration to be for.

If the configuration is purely for that user's environment, then the project's user file would be OK. That file is not recommended to be added to source control, so anything you add to it won't affect other users.

If the configuration needs to be shared with other users, then .editorconfig could be used, but I think that's more useful for storing configuration for individual files (like analyzer settings and code formatting) rather than for an entire project or solution.

Another option would be to write a configuration file to the .vs directory at the root of the solution. You can then check that configuration file into source control and share it with other users if needed. I've done that in the past by saving the configuration to a JSON file in that directory.

Regarding .editorconfig files, Visual Studio doesn't (to my knowledge) have any built-in support for reading those files, but the editorconfig NuGet package is really easy to use to read the files. Not sure how you would go about writing to them though.

mrlacey commented 2 years ago

Thanks @reduckted the information on saving in the project/user file is helpful. I looked at using editorconfig files previously but couldn't find an effective way to read and write to them though.

lsoft commented 1 year ago

let me add few cents here.

may be I miss the point, but adding some extension settings into the existing user file is not good in all cases. I can imagine that I want NOT to share user file between the team members, but I want+need to share Vsix setting in my team. I have an exactly situation at my job: user file contains a specific settings for every programmer (like show all files, StartArguments) which I do not want to share, but project-related Vsix must have the same settings across whole team and I need share it.

so, for my Vsixes, I had chosen to have an additional file which lives in the folder of sln/project. Such file can be added to the repo to share Vsix settings, or to .gitignore to do not.

One more point. If your Vsix need to control its settings on sln basis, then probably you should consider: do you need to support slnf or only sln? My team uses a single sln, with a lot of slnf based on the sln. And, unfortunately, our Vsix need to know which slnf is opened (to choose correct settings). And here we (as Vsixers) are in troubles, because there is no good way to determine slnf full file path. I posted a bug in VS community 2 years ago, but no feedback had came. So, if you choose to support per-slnf-settings scenario, you will have additional worries))

Sorry if I misunderstood the topic and for poor English!