-
Notifications
You must be signed in to change notification settings - Fork 675
Support hierarchical document symbols in LSP #4661
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
Conversation
It might be nice to add a recursion limit parameter here, firstly because it is probably possible to get an infinitely recursive structure (from analysis), but also just to save time/space when returning the symbols. VS is unlikely to request more than two levels deep, for example. |
case PythonMemberType.Unknown: return SymbolKind.None; | ||
case PythonMemberType.Class: return SymbolKind.Class; | ||
case PythonMemberType.Instance: return SymbolKind.Object; | ||
case PythonMemberType.Class: return m.Scope is FunctionScope ? SymbolKind.Variable : SymbolKind.Class; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be a constant mapping. If nestedinner classes have an incorrect member type, that is probably its own bug.
Alternatively, handle this extra check earlier, so that Scope doesn't have to be passed around here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It a minor nit. In class method first argument comes as class although technically it is an argument in the signature. It looks a bit odd in the outline as a class inside a function. Or maybe its OK?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, you'd certainly want it to look like a class in this case, so perhaps the answer is to explicitly handle arguments differently?
def make_type():
class MyClass:
pass
return MyClass
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
@@ -50,6 +51,14 @@ await GetModuleVariablesAsync(entry as ProjectEntry, opts, @params.query, 50) | |||
.ToArray(); | |||
} | |||
|
|||
public async Task<DocumentSymbol[]> NewDocumentSymbol(DocumentSymbolParams @params, CancellationToken cancellationToken) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Public?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see, this is the newer one. Personally I'd prefer a suffix 2
or 3_10
(for the LSP version), or a descriptive prefix (HierarchyDocumentSymbol
?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure
Handle trace logging consistently in VSC
There are some problems rewriting |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the logic for finding the hierarchy could be simpler if you start with an AST walk and then just go to the analysis for names you can't tell the type of. This is fundamentally what we do for analysis classifications - you may even be able to reuse that walker.
|
||
public interface IServer2 : IServer { | ||
Task<DocumentSymbol[]> HierarchicalDocumentSymbol(DocumentSymbolParams @params, CancellationToken cancellationToken); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're likely to end up with a lot of IServer#
interfaces if they only gain one method at a time... but maybe that's how we have to do it given how frequently this releases?
Arguably, if there's no user for it, maybe we could batch up interface additions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extension (IC) will be using it most probably since they need symbols with types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do they need to use it via the interface? Or are they downcasting to the Server class anyway?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They use IServer
except there is some odd code that casts it to Server
to see it is actual Python.
|
||
private static string GetFunctionKind(MemberResult m) { | ||
if (m.MemberType == PythonMemberType.Function) { | ||
var funcDef = m.Values.FirstOrDefault(x => x is IPythonFunction)?.PythonType as FunctionDefinition; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This always results in null
- you will want m.Values.OfType<FunctionInfo>()
and then check their .IsStatic
/.IsClassMethod
/.IsProperty
member.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
var topLevel = new List<MemberResult>(); | ||
|
||
foreach (var m in members) { | ||
var parent = members.FirstOrDefault(x => x.Scope?.Node == m.Scope?.OuterScope?.Node && x.Name == m.Scope?.Name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should be able to avoid this check by more carefully collecting values from scopes, yes? And save adding scope references to the MemberResult struct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or arguably we can extract nearly enough information from an AST walk (the names at least), and then use a direct analysis lookup to resolve those that we don't know for sure (like we do in the analysis classifier).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I am actually going to write document symbols fetch using AST, but that is going to be September. microsoft/vscode-python#2430
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MemberResult is a public API, so adding/removing a field will be a breaking change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll need to revisit public surface going forward, add more interfaces since it gets harder to make changes as we go.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can make MemberResult2
deriving from the base technically
var opts = GetMemberOptions.ExcludeBuiltins | GetMemberOptions.DeclaredOnly; | ||
var entry = ProjectFiles.GetEntry(@params.textDocument); | ||
|
||
var members = await GetModuleVariablesAsync(entry as ProjectEntry, opts, string.Empty, 50); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cancellationToken
Per LSP 4.4+, VS Code 1.26+
microsoft/vscode-python#2384
Questions: