Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example on how to modify Sln files #3

Closed
fhaag opened this issue Dec 10, 2017 · 5 comments
Closed

Example on how to modify Sln files #3

fhaag opened this issue Dec 10, 2017 · 5 comments
Labels

Comments

@fhaag
Copy link

fhaag commented Dec 10, 2017

Could you add a simple example of how to actually modify an .sln file? I am looking for something like, e.g. load an .sln file, search through the names of projects included in it, and remove one of these project inclusions, then save the .sln file again. How is this done?

Unfortunately, I failed to see any hint to that in the readme file. There is some talk about custom "handlers" that can apparently be used to write something, but all I am getting from the Sln object is a Results object with (read-only) enumerables.

@3F
Copy link
Owner

3F commented Dec 10, 2017

There is some talk about custom "handlers"

The custom handlers may be applied (this is not required for your case) for any non-standard logic, or for any new not yet supported data from .sln format, and so on.
That is, you don't need to implement this yourself for any common operations with known data because it's already implemented for ISlnResult via Core.SlnHandlers.

Map is the main idea of any modifications.

And the Core.ObjHandlers just provides "committing" new data vice versa from any related collection.

I am looking for something like, e.g. load an .sln file, search through the names of projects included in it, and remove one of these project inclusions, then save the .sln file again. How is this done?

For example:

using(var sln = new Sln(@"original.sln", SlnItems.All))
{
    // new collection from available projects but without project 'UnLib'
    var projects = sln.Result.ProjectItems.Where(p => p.name != "UnLib"); 

    // prepare write-handlers
    var whandlers = new Dictionary<Type, HandlerValue>() {
        [typeof(LProject)] = new HandlerValue(new WProject(projects, sln.Result.ProjectDependencies)),
    };

    // save result
    using(var w = new SlnWriter(@"modified.sln", whandlers)) {
        w.Write(sln.Result.Map);
    }
}
// That's all. You should get .sln without `UnLib` project.

@3F 3F added the question label Dec 10, 2017
@3F 3F mentioned this issue Mar 1, 2019
@wouterroos
Copy link
Contributor

Hi,

When using the example above on an 'empty' solution, sln.Result.ProjectDependencies is null and thus the code will throw an exception when adding a new project. Is this the proper way to add new projects to a solution that doesn't contain any project (or project dependencies) yet?

@3F
Copy link
Owner

3F commented Jun 13, 2019

sln.Result.ProjectDependencies is null and thus the code will throw an exception

You should notice much more null-values when loading an 'empty' solution :)

An sln.Result just represents various collections, so you can just initialize something from scratch if you need it. For example, for mentioned ProjectDependencies and WProject from my example above:

  1. WProject requires any ISlnProjectDependencies instance.
  2. The default public implemention exactly is LProjectDependencies, ie. nothing easer than just:
new WProject(projects, new LProjectDependencies())

etc.

Is this the proper way to add new projects to a solution that doesn't contain any project (or project dependencies) yet?

However, you need a little bit more if we're talking about adding new projects into absolutely 'empty' solution (when no initial records about projects at all), like:

MinimumVisualStudioVersion = 10.0.40219.1
Global
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
EndGlobal

Today we need add information about planned projects into prepared map data. For example:

// some project collection from scratch ~
var projects = new[] { new ProjectItem("MyProject", ProjectType.Cs, "path") };

// place LProject handler inside map:
sln.Result.Map.Insert
(
    (int)sln.Result.Map.First(i => i.Raw == "Global").Line - 1,
    new Section(new LProject(), null)
);

// now you can use SlnWriter as before

2.2 does not provide any wrapper for some automation of this. But I think we need an additional layer anyway.

Moreover, in #9 I already voiced about preparing some "intermediate wrapper for creating new write-handlers from result"

@wouterroos
Copy link
Contributor

Thanks, I got it to work.

@3F
Copy link
Owner

3F commented Apr 29, 2024

Since this issue is mentioned too much, please note that there are new related features in 2.7+

SMap

New map wrapper in order to control ISections easily. For example,

smap.Add
(
    SMap.AddType.Before,
    typeof(LExtensibilityGlobals),
    new Section(new LMyHandler()) // place it before LExtensibilityGlobals
);

see tests for details

ISlnResult -> ISlnWhData

ISlnWhData is new minimal subset of the data for the default handlers. That is, in addition to standard handler definitions, I mean:

Dictionary<Type, HandlerValue> whandlers = new()
{
    [typeof(LVisualStudioVersion)] = new(new WVisualStudioVersion(SlnHeader.MakeDefault())),
    [typeof(LProject)] = new(new WProject(projects)),
    ...
};

using SlnWriter w = new(solutionFile, whandlers));

the modern SlnWriter now understands also ISlnWhData. And while ISlnWhData can be implemented manually, 2.7 also provides LhDataHelper helper in order to use it in a chain like style:

LhDataHelper hdata = new();
hdata.SetHeader(SlnHeader.MakeDefault())
        .SetProjects(projects)
        .SetProjectConfigs(prjConfs)
        .SetSolutionConfigs(slnConf);

using SlnWriter w = new(solutionFile, hdata);
await w.WriteAsync(sln.Result.Map);

And new DefaultHandlers can also help to generate the default w\handlers using this data

Dictionary<Type, HandlerValue> whandlers = DefaultHandlers.MakeFrom(data)

Modern SlnWriter

SlnWriter now also provides WriteAsString() & WriteAsStringAsync() to save the result as string instead of file.

using SlnWriter w = new(handlers);
string data = await w.WriteAsStringAsync(sln.Result.Map);

I hope that now modifications can actually be done in a few steps.

More about:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants