Skip to content

Fix issue where structure tag could get stuck at the wrong location#76194

Merged
CyrusNajmabadi merged 4 commits intodotnet:mainfrom
CyrusNajmabadi:outliningStale
Dec 12, 2024
Merged

Fix issue where structure tag could get stuck at the wrong location#76194
CyrusNajmabadi merged 4 commits intodotnet:mainfrom
CyrusNajmabadi:outliningStale

Conversation

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor

@CyrusNajmabadi CyrusNajmabadi commented Dec 1, 2024

Partial fix of #73799

@ghost ghost added Area-IDE untriaged Issues and PRs which have not yet been triaged by a lead labels Dec 1, 2024
@CyrusNajmabadi CyrusNajmabadi changed the title In progress Fix issue where structure tag could get stuck at the wrong location Dec 1, 2024
@CyrusNajmabadi
Copy link
Copy Markdown
Contributor Author

@olegtk @AmadeusW @veler can you point me to where your code for IStructureTag structure tags is? I have a feelling you are not using .EdgeInclusive as your span tracking mode for them, causing them to not move to the desired location as the user edits at the start of a structure tag.

If that's the case, we'll likely want you to change that to fix #73799 completely.

@CyrusNajmabadi CyrusNajmabadi marked this pull request as ready for review December 1, 2024 01:02
@CyrusNajmabadi CyrusNajmabadi requested a review from a team as a code owner December 1, 2024 01:02
@ToddGrun
Copy link
Copy Markdown
Contributor

ToddGrun commented Dec 3, 2024

can you point me to where your code for IStructureTag structure tags is

Is this it?

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor Author

Thanks. Given:

            guideLineSpanStart = guideLineSpanStart != null ?
                MappingPointSnapshot.Create(
                    targetSnapshot,
                    new SnapshotPoint(tag.Snapshot, guideLineSpanStart.Value),
                    PointTrackingMode.Positive, null).GetPoint(targetSnapshot, PositionAffinity.Predecessor) :
                    null;

This should likely move to PointTrackingMode.Negative @olegtk

var latestSnapshot = latestStructureTag.Snapshot;
var previousSnapshot = previousStructureTag.Snapshot;

var previousStructureStart = new SnapshotPoint(previousSnapshot, previousStructureTag.HeaderSpan.Start);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

HeaderSpan

Is this just about the guideline behavior? If so, would GuideLineSpan.Start be appropriate to use? FYI, I believe you could also use a SnapshotSpan and SpanTrackingMode.Inclusive

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We can't use guidelinespan as we don't set it. it's just:

 public Span? GuideLineSpan => null;

we defer to the editor to determine that span.

Copy link
Copy Markdown
Contributor

@ToddGrun ToddGrun Dec 12, 2024

Choose a reason for hiding this comment

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

I still feel like mapping a span is better than two calls to mapping points.

var previousStructureStart = new SnapshotSpan(previousSnapshot, previousStructureTag.HeaderSpan.Start, 0);

if (!previousStructureStart.TranslateTo(latestSnapshot, SpanTrackingMode.EdgeInclusive).IsEmpty)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

To clarify, I was thinking of something like the following (with the comment needing to be altered)

    protected sealed override bool TagEquals(IContainerStructureTag latestTag, IContainerStructureTag previousTag)
    {
        if (latestTag is not StructureTag latestStructureTag || previousTag is not StructureTag previousStructureTag)
        {
            Contract.Fail("Tags were the wrong type");
            return latestTag == previousTag;
        }

        // If these tags are the same object in memory, then they are obviously equal
        if (latestTag == previousTag)
            return true;

        var latestSnapshot = latestStructureTag.Snapshot;
        var previousSnapshot = previousStructureTag.Snapshot;

        var previousStructureStart = new SnapshotSpan(previousSnapshot, previousStructureTag.HeaderSpan.Start, 0);

        if (previousStructureStart.TranslateTo(latestSnapshot, SpanTrackingMode.EdgeInclusive).IsEmpty)
        {
            // We can't know that how we think this block moved is actually how the editor actually moved it.
            // Specifically, the tracking mode is an implementation detail.  As such, we don't want to reuse this tag as
            // its stale data (as mapped by the editor) may not be where we'd expect the new block's data to be.  This
            // can happen when the user types right at the start of a structure tag, causing it to move inwards
            // undesirably.

            return latestTag.Equals(previousTag);
        }

        return false;
    }

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor Author

@ToddGrun ptal.

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor Author

@JoeRobich @akhera99 ptal.

Copy link
Copy Markdown
Contributor

@ToddGrun ToddGrun left a comment

Choose a reason for hiding this comment

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

:shipit:

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor Author

Tlaked offline. Going to take the code as is as i personally think it is clearer, and this is not a hot path.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area-IDE untriaged Issues and PRs which have not yet been triaged by a lead

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants