microsoft / microsoft/language-server-protocol

Clarifications for the intended type hierarchy behaviour

Open
#1,984 5 comments 4 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

discussion
Dominant language
TypeScript
Stars
13k
Forks
1k
Avg merge
6d 1h
Merged PRs (30d)
10

Description

We are starting to ship type hierarchy for Ruby in the Ruby LSP and we hit a few doubts about the intended experience is on certain cases. I'm going to divide the doubts below so that I can provide a detailed explanation for each.

Re-opening of an existing declaration

In Ruby, it's common to re-open the same namespace in different files, adding more declarations inside of it. For example:

# file1.rb
module Foo
  class Bar
  end
end

# file2.rb
module Foo
  class Bar
  end
end

# In this scenario, the declarations of both `Foo` and `Bar` are re-opened in the two files

A TypeHierarchyItem only supports a single document URI attached to it.

What is the expectation in this case? Should the server return the complete type hierarchy information, but pick one of the document URIs? Or should all re-openings be returned in the list?

Is this a limitation and multiple document URIs should be allowed for type hierarchy items?

Returning more than one item in the prepare request

The prepare request mentions the following: ...return a type hierarchy for the language element of given text document positions. However, it seems that only a single position is sent as part of the request parameters, but the response expected is a list of entries.

While trying to understand what to do for the doubt above this one, we attempted to return all re-openings of the same namespace in the prepare request, but noticed that it doesn't work. The only way to make it work is to return only one item inside of the response array.

Is this intentional? And if so, should we document that in the spec?

Show linearized type hierarchy or only direct ancestors

Another doubt that came up regarding the user experience is whether we should display the linearized ancestor chain or show only the direct chain. For example:

module Foo; end
module Bar
  # Add Foo as an ancestor of Bar
  include Foo
end

class Baz
  # Add Bar as an ancestor of Baz
  include Bar
end

In this example, the direct ancestor chain of Baz is just [Bar, Bar, Object] (Object is implicitly the parent class of all Ruby classes).

The linearized ancestor chain is [Baz, Bar, Foo, Object, Kernel, BasicObject], which includes the linearized ancestors of each ancestor present in the chain for Baz on top of the class itself.

Is the intention behind the feature to display the linearization or only the direct ancestors of a namespace? Or is it implementors choice?

Singleton classes

Finally, the last doubt is concerning Ruby's singleton class ancestors. In Ruby, every class or module has the regular ancestors and the singleton ancestors.

The regular ancestor chain is where instance methods are defined and searched for. The singleton ancestor chain is where static (class) methods are defined and searched for. For example:

class Foo
end

# Ancestors of Foo
[Foo, Object, Kernel, BasicObject]

# Ancestors of the singleton class of Foo
[<Class:Foo>, <Class:Object>, <Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]

The distinction is important because Ruby developers can add ancestors that will only exist in the regular chain or in the singleton chain (through include or extend).

class Foo
  include IncludedModule
  extend ExtendedModule
end

# Ancestors of Foo
[Foo, IncludedModule, Object, Kernel, BasicObject]

# Ancestors of the singleton class of Foo
[<Class:Foo>, ExtendedModule, <Class:Object>, <Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]

I'm not sure this concept exists for other languages, but for Ruby it would make perfect sense to have two actions: show attached type hierarchy (the regular one) and show singleton type hierarchy.

What is the recommendation to handle this case? Do we implement this as a custom menu item for the Ruby extension only? Is there interest in having something generic to handle these multiple hierarchy chains associated with the same namespace? Should we always display both at the same time in the UI?

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read the linked LSP 3.17 definitions for TypeHierarchyItem, textDocument/prepareTypeHierarchy, and TypeHierarchyPrepareParams. Resolve the questions about reopened declarations, prepare responses, ancestor linearization, and singleton classes, then reflect the agreed behavior in the protocol specification or its documentation.

Written by the indexing model from the issue text.

Assessment

Tech stack
ruby
Domain
api, documentation
Issue type
Documentation
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.