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

Support preserving formatting when opening a project #1036

Merged
merged 12 commits into from
Sep 16, 2016

Conversation

dsplaisted
Copy link
Member

Fixes #707

Adds the following overloads to ProjectRootElement:

public static ProjectRootElement Create(XmlReader xmlReader, ProjectCollection projectCollection, bool preserveFormatting)
public static ProjectRootElement Open(string path, ProjectCollection projectCollection, bool preserveFormatting)

@rainersigwald
Copy link
Member

We need to make sure this doesn't conflict with #1004, where @maddin2016 is playing in similar areas.

@@ -227,6 +227,8 @@ internal ProjectRootElement Get(string projectFile, OpenProjectRootElement openP
// use: it checks the file content as well as the timestamp. That's better than completely disabling
// the cache as we get test coverage of the rest of the cache code.
XmlDocument document = new XmlDocument();
// TODO: does PreserveWhitespace need to be an option here?
//document.PreserveWhitespace = true;
Copy link
Member

Choose a reason for hiding this comment

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

Seems like it'd need to match what gets inserted into the cache, which would depend on how it was loaded, right?

Copy link
Member Author

Choose a reason for hiding this comment

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

I've updated this so that it won't use the cached version if it doesn't match the preserveFormatting setting requested.

{
var newWhitespaceNode = XmlDocument.CreateWhitespace(reference.XmlElement.PreviousSibling.Value);
XmlElement.InsertAfter(newWhitespaceNode, reference.XmlElement);
}
Copy link
Member

Choose a reason for hiding this comment

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

It's unfortunate that we have to do this manually. But this seems like a reasonable strategy (and good comment).

Copy link
Member

Choose a reason for hiding this comment

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

But can it be abstracted and reused?

Copy link
Member Author

Choose a reason for hiding this comment

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

How do you mean abstracted and reused? ProjectElementContainer seems like it is already the generic place to put this logic.

Copy link
Member

Choose a reason for hiding this comment

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

This and below have very similar logic to find, clone, and insert whitespace. Though I guess it's only twice, so the generalization may not be that general.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, I didn't see an elegant way to unify the different cases that exist within ProjectElementContainer.

@rainersigwald
Copy link
Member

    /// DO NOT BREAK THIS VERY USEFUL SAFETY CONTRACT.

Does this still hold?


Refers to: src/XMakeBuildEngine/Construction/ProjectElementContainer.cs:305 in 1a2e1da. [](commit_id = 1a2e1da, deletion_comment = False)

else if (XmlElement.PreviousSibling != null &&
XmlElement.PreviousSibling.NodeType == XmlNodeType.Whitespace)
{
// This container didn't have any whitespace in in. This probably means it didn't have separate open
Copy link
Member

Choose a reason for hiding this comment

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

in it

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed

// So try to match the surrounding formatting and add one indentation level
if (XmlElement.FirstChild.NodeType == XmlNodeType.Whitespace)
{
// This container had a whitespace node, which should generally be a newline and the indent
Copy link
Member

Choose a reason for hiding this comment

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

how true is "generally"? Should we bother to validate this assumption?

Copy link
Member Author

Choose a reason for hiding this comment

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

I thought about this and it seemed OK to leave it as is. If there's spacing between the elements but not a newline, then we'll still be matching the surrounding formatting, even though the formatting seems weird.

Copy link
Member

Choose a reason for hiding this comment

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

Ok, I buy it. Can you put that in the comment?

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

// and close tags. So add a whitespace node before the new child with additional indentation over the
// container's indentation, and add a whitespace node with the same level of indentation as the container
// after the new child so the closing tag will be indented properly.
string whitespace = XmlElement.PreviousSibling.Value;
Copy link
Member

Choose a reason for hiding this comment

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

rename to parentWhitespace?

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

<ItemGroup>
<ProjectReference Include=`..\CLREXE\CLREXE.vcxproj`>
<!-- Comment -->
<Project>{3699f81b-2d03-46c5-abd7-e88a4c946f28}</Project>
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure I prefer this behavior. It can be useful to have both

// Long comments explaining
code;

and

code; // clarified

Copy link
Member Author

Choose a reason for hiding this comment

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

The behavior today is that comments are put on separate lines. If you specify the option to preserve formatting, then the location of the comment will be preserved. This test covers both behaviors, which is why you see the reformatted version.

Copy link
Member

Choose a reason for hiding this comment

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

Ohhh. Gotcha.

project.SaveLogicalProject(writer);

string actual = writer.ToString();
string expected = @"<?xml version=""1.0"" encoding=""utf-16""?>" +
Copy link
Member

Choose a reason for hiding this comment

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

VerifyAssertLineByLine(expected, actual);
}

[Fact(Skip = "https://github.com/Microsoft/msbuild/issues/362")]
Copy link
Member

Choose a reason for hiding this comment

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

So the preserve-whitespace flag doesn't fix this? Boo :(

Copy link
Member Author

Choose a reason for hiding this comment

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

Apparently not... 😞

@dsplaisted
Copy link
Member Author

@rainersigwald

    /// DO NOT BREAK THIS VERY USEFUL SAFETY CONTRACT.

Does this still hold?


Refers to: src/XMakeBuildEngine/Construction/ProjectElementContainer.cs:305 in 1a2e1da. [](commit_id = 1a2e1da, deletion_comment = False)

Yes. The comment is referring to the NextSibling and PreviousSibling properties of ProjectElement, which don't include the whitespace nodes.

@rainersigwald
Copy link
Member

Ah, now I see that I evidently didn't hit "comment" on my most important comment. Sorry.

My instinct is to say that this shouldn't be an option--that is, we should always preserve whitespace. But you've plumbed through an option. Do you think that should be exposed to the user?

@dsplaisted
Copy link
Member Author

I went with an opt-in option to avoid a compat risk (as suggested by Dave). How much of a compat risk would there be to always preserving formatting?

Also note that there's some risk that there are still cases where preserving the formatting will result in ugly formatting when the project is modified, as would be the case without the changes on ProjectElementContainer to add or remove whitespace when adding or removing child elements.

Copy link
Contributor

@AndyGerlicher AndyGerlicher left a comment

Choose a reason for hiding this comment

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

Looks good!

@dsplaisted dsplaisted merged commit 2db4d4b into dotnet:master Sep 16, 2016
@davkean
Copy link
Member

davkean commented Sep 27, 2016

@dsplaisted Where did we land with this? Did we sync with CPS to call this overload?

@dsplaisted
Copy link
Member Author

@davkean No, AFAIK CPS isn't consuming this yet. I'm not sure if this has made it into a drop of MSBuild that they are using. @AndyGerlicher, can you comment?

@srivatsn
Copy link
Contributor

@lifengl have we reacted to this in CPS?

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.

7 participants