razor: Light bulb refactorings can break C# semantic colorization

In a Blazor app in Codespaces open up Counter.razor and add:

@code {
    class Test : IDisposable
    {
    }
}

Invoke the light bulb to implement the dispose pattern for IDisposable and see colorization break (ignore the double light bulbs, that’s another issue but this happens even without htat):

ZH9VLkgiuO

I wasn’t able to reproduce this in a pure C# file so I’m imagining it has something to do with Razor. Also it doesn’t seem to reproduce locally. @gundermanc I imagine this is a LiveShare sync issue? I would have imagined that it would have been “eventually” correct but it never corrects

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 15 (15 by maintainers)

Most upvoted comments

FYI: I have filed a proposed user story to harden the LSP client’s portion of these scenarios: https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1243527.

The intent is that if possible, we support server-side edits and have a ‘gating’ mechanism where we don’t actually inform the LSP server about the edit until it has been acknowledged by the client.

I don’t have any more details yet, this will probably require some prototyping and possibly a change to the in-proc Roslyn bits.

would this fall under the larger semantic token issue we saw yesterday in triage that you were planning to run by Christian?

That sounds like the issue fixed by this PR: https://devdiv.visualstudio.com/DevDiv/_git/VSLanguageServerClient/pullrequest/313631

@NTaylorMullen do you have a set of lightbulb actions that are known to consistently cause this issue? Doing so will help me prioritize which end needs fixed first.

I get this with Add ‘Debugger Display’ Attribute as well.

image

@page "/counter"


<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

<NonExistantComponent></NonExistantComponent>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }

    public override bool Equals(object obj)
    {
        return obj is Counter counter &&
            currentCount == counter.currentCount;
    }

    public override int GetHashCode()
    {
        return HashCode.Combine(currentCount);
    }

    class Skj : IDisposable
    {
        public Skj()
        {
        }

        public Skj(string lksdajf)
        {
            Lksdajf = lksdajf ?? throw new ArgumentNullException(nameof(lksdajf));
        }

        public string Lksdajf { get; }

        public void Dispose()
        {
            throw new NotImplementedException();
        }
    }
}