vscode-csharp icon indicating copy to clipboard operation
vscode-csharp copied to clipboard

Outline model (aka `DocumentSymbolProvider`) doesn't reflect actual document hierarchy, which breaks "Sticky Scroll"

Open dannymcgee opened this issue 11 months ago • 1 comments

Expected behavior

C# documents support VS Code's "Sticky Scroll" feature when configured with the default settings ("editor.stickyScroll.defaultModel": "outlineModel").

Actual behavior

The C# extension's outline model (provided by DocumentSymbolProvider) flattens the document hierarchy and excludes critical symbols like namespaces, which breaks the "Sticky Scroll" behavior.

Additional context

This issue can be partially worked around by changing editor.stickyScroll.defaultModel to foldingProviderModel for C# projects, but this is an imperfect solution that produces its own set of problems.

Steps to reproduce

Setup a C# project with this example document:

namespace Example
{
    public class Foo
    {
        int m_Lorem = 0;
        int m_Ipsum = 1;

        public enum Bar
        {
            None = 0,
            One = 1,
            Two = 2,
            Three = 3,
        }

        Bar Dolor { get; set; } = Bar.None;

        public Foo(int lorem, int ipsum, Bar dolor)
        {
            m_Lorem = lorem;
            m_Ipsum = ipsum;
            Dolor = dolor;
        }

        public void SwapLipsum()
        {
            (m_Ipsum, m_Lorem) = (m_Lorem, m_Ipsum);
        }

        public int Sum() => m_Lorem + m_Ipsum + (int)Dolor;

        public static Foo operator++(Foo foo)
        {
            ++foo.m_Lorem;
            ++foo.m_Ipsum;
            foo.Dolor = (Bar)(((int)foo.Dolor + 1) % 4);

            return foo;
        }
    }
}

Compare/contrast the Outline view and sticky-scroll behavior of the C# document above with this (roughly) equivalent TypeScript document:

namespace Example {
  export class Foo {
    private lorem = 0;
    private ipsum = 1;

    // Super contrived example because TypeScript doesn't do enums nested in classes
    static readonly Bar = class {
      static readonly None = 0;
      static readonly One = 1;
      static readonly Two = 2;
      static readonly Three = 3;
    }

    private _dolor: 0|1|2|3 = Foo.Bar.None;
    get dolor() { return this._dolor; }
    set dolor(value) { this._dolor = value; }

    constructor (lorem: number, ipsum: number, dolor: 0|1|2|3) {
      this.lorem = lorem;
      this.ipsum = ipsum;
      this.dolor = dolor;
    }

    swapLipsum(): void {
      [this.ipsum, this.lorem] = [this.lorem, this.ipsum];
    }

    sum = () => this.lorem + this.ipsum + this.dolor;

    inc(): Foo {
      let result = new Foo(this.lorem, this.ipsum, this.dolor);
      ++result.lorem;
      ++result.ipsum;
      result.dolor = ((result.dolor + 1) % 4) as 0|1|2|3;

      return result;
    }
  }
}

Screenshots

Image

Image

Environment data

dotnet --info output:

.NET SDK:
  Version:           9.0.100
  Commit:            59db016f11
  Workload version:  9.0.100-manifests.4a280210
  MSBuild version:   17.12.7+5b8665660

Runtime Environment:
  OS Name:     Windows
  OS Version:  10.0.22631
  OS Platform: Windows
  RID:         win-x64
  Base Path:   C:\Program Files\dotnet\sdk\9.0.100\

.NET workloads installed:
  There are no installed workloads to display.
  Configured to use loose manifests when installing new manifests.

Host:
  Version:      9.0.0
  Architecture: x64
  Commit:       9d5a6a9aa4

.NET SDKs installed:
  3.1.426 [C:\Program Files\dotnet\sdk]
  9.0.100 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.36 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 7.0.20 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 8.0.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 9.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.36 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 7.0.20 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 8.0.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 9.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.11 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.36 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 7.0.20 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 8.0.11 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 9.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Other architectures found:
  x86   [C:\Program Files (x86)\dotnet]
    registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation]

Environment variables:
  Not set

global.json file:
  Not found

VS Code version: 1.97.1 C# Extension version: 2.63.32

OmniSharp log

N/A

dannymcgee avatar Feb 11 '25 20:02 dannymcgee

thanks for the detailed report. Definitely should update our document symbols handler to play nicer here.

dibarbet avatar Feb 20 '25 00:02 dibarbet