Skip to content
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

Implement method textDocument/declaration #2684

Closed
lbrayner opened this issue Jun 1, 2023 · 8 comments · Fixed by #2702
Closed

Implement method textDocument/declaration #2684

lbrayner opened this issue Jun 1, 2023 · 8 comments · Fixed by #2702
Assignees
Milestone

Comments

@lbrayner
Copy link
Contributor

lbrayner commented Jun 1, 2023

For a method, go to the interface declaration; otherwise, the same as textDocument/definition (can't think of any other case).

@lbrayner lbrayner changed the title [IMPROVEMENT] Implement method textDocument/declaration [ENHANCEMENT] Implement method textDocument/declaration Jun 1, 2023
@rgrunber rgrunber changed the title [ENHANCEMENT] Implement method textDocument/declaration Implement method textDocument/declaration Jun 2, 2023
@rgrunber
Copy link
Contributor

rgrunber commented Jun 2, 2023

As a workaround, there is method hierarchy (#2502), which could get you to that base interface that declares the method.

To implement, I think we could just use https://github.com/eclipse-jdt/eclipse.jdt.ui/blob/72616d44d5f3c8f8eb64991fe5d54d3fac8483c6/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/actions/OpenSuperImplementationAction.java#L168-L171 . The classes used there are all from jdt.core.manipulation (even though it's defined in jdt.ui)

@lbrayner
Copy link
Contributor Author

lbrayner commented Jun 2, 2023

Nice! That makes total sense, I'll look into it, thanks, @rgrunber !!

@lbrayner
Copy link
Contributor Author

lbrayner commented Jun 2, 2023

Hey @rgrunber , I was able to implement a call to java.navigate.openTypeHierarchy in Neovim: mfussenegger/nvim-jdtls@master...lbrayner:nvim-jdtls:hierarchy

However, upon invoking it on a method I was only able to get a TypeHierarchyItem with the correct Interface class, but the range field points to line containing public interface BlaBlaBla, not the line with the method declaration, because I'm using version 1.9.0, I think. f4dad2f hasn't been released yet, from what I can see, so I'll have to build from master and test it.

Will keep you posted!

@rgrunber
Copy link
Contributor

rgrunber commented Jun 5, 2023

Here's the commit 782ca15 . You'll see it's in 1.21.0 of JDT-LS. I think the commit you pointed to was from the PR but the SHA-1 changed due to it getting rebased so it shows up as not belonging to anything.

Hopefully that works. If not, I do think fixing it the proper way (by supporting the textDocument/declaration request) is something we should prioritize.

@lbrayner
Copy link
Contributor Author

lbrayner commented Jun 5, 2023

@rgrunber yes, I built master and it works just fine for my use case -- the only nitpick is that it does not return the exact column (character) on the line, only the first non blank character.

I'm keeping this open for now because I'm experiencing issues with the Neovim plugin that I use (https://github.com/mfussenegger/nvim-jdtls), and I want to understand how java.navigate.openTypeHierarchy behaves when used with types, and how the resolveDepth argument works also, so I can report back and possibly suggest enhancements.

Thanks!

@rgrunber
Copy link
Contributor

rgrunber commented Jun 5, 2023

@rgrunber yes, I built master and it works just fine for my use case -- the only nitpick is that it does not return the exact column (character) on the line, only the first non blank character.

I'm keeping this open for now because I'm experiencing issues with the Neovim plugin that I use (https://github.com/mfussenegger/nvim-jdtls), and I want to understand how java.navigate.openTypeHierarchy behaves when used with types, and how the resolveDepth argument works also, so I can report back and possibly suggest enhancements.

We can keep this issue open until we properly implement textDocument/declaration. As for how it works :

Note that this API might change once we merge #2033, since type hierarchy is finally part of the LSP Spec (and when we implemented it under the delegate commands, it wasn't)

java.navigate.openTypeHierarchy is called by the client with a text document location, a direction (often Both) and a resolve depth of 0. A depth of 0 is so it returns almost immediately with the element data, and gives the client something to render while it waits for the results to return.

java.navigate.resolveTypeHierarchy is called afterwards with the returned type hierarchy item, direction, and resolve depth (generally 1 ). I guess you could declare a larger resolve depth and have everything resolve at once but I think we decided it would be better for an incremental approach to get some results to render. Other clients can make their own choices here. Since we only resolve 1 element deep, we're performing a breadth first search where for any given element we resolve 1 level of parents/children, wait for the results and resolve 1 element deep from those returned results until a given element has no children, or its parent is java.lang.Object.

The "direction" is either 0 (children), 1 (parents), or 2 (both) and indicates whether for the given type hierarchy element, we attempt to determine its children (subtypes) or parents (super types), or both. The "resolve" field is just how many levels deep we intend to resolve elements.

Here's a sample of the request/response interaction :

interaction
[Trace - 18:48:24] Sending request 'workspace/executeCommand - (35)'.
Params: {
    "command": "java.navigate.openTypeHierarchy",
    "arguments": [
        "{\"textDocument\":{\"uri\":\"file:///home/rgrunber/sample-projects/type-hierarchy/org/example/Zero.java\"},\"position\":{\"line\":2,\"character\":15}}",
        "2",
        "0"
    ]
}

[Trace - 18:48:24] Received response 'workspace/executeCommand - (35)' in 13ms.
Result: {
    "name": "Zero",
    "detail": "org.example",
    "kind": 5,
    "deprecated": false,
    "uri": "file:///home/rgrunber/sample-projects/type-hierarchy/org/example/Zero.java",
    "range": {
        "start": {
            "line": 2,
            "character": 0
        },
        "end": {
            "line": 10,
            "character": 1
        }
    },
    "selectionRange": {
        "start": {
            "line": 2,
            "character": 13
        },
        "end": {
            "line": 2,
            "character": 17
        }
    },
    "data": {
        "element": "=type-hierarchy_9250e329/_<org.example{Zero.java[Zero"
    }
}


[Trace - 18:48:25] Sending request 'workspace/executeCommand - (36)'.
Params: {
    "command": "java.navigate.resolveTypeHierarchy",
    "arguments": [
        "{\"name\":\"Zero\",\"detail\":\"org.example\",\"kind\":5,\"deprecated\":false,\"uri\":\"file:///home/rgrunber/sample-projects/type-hierarchy/org/example/Zero.java\",\"range\":{\"start\":{\"line\":2,\"character\":0},\"end\":{\"line\":10,\"character\":1}},\"selectionRange\":{\"start\":{\"line\":2,\"character\":13},\"end\":{\"line\":2,\"character\":17}},\"data\":{\"element\":\"=type-hierarchy_9250e329/_<org.example{Zero.java[Zero\"}}",
        "1",
        "1"
    ]
}

[Trace - 18:48:25] Received response 'workspace/executeCommand - (36)' in 69ms.
Result: {
    "name": "Zero",
    "detail": "org.example",
    "kind": 5,
    "deprecated": false,
    "uri": "file:///home/rgrunber/sample-projects/type-hierarchy/org/example/Zero.java",
    "range": {
        "start": {
            "line": 2,
            "character": 0
        },
        "end": {
            "line": 10,
            "character": 1
        }
    },
    "selectionRange": {
        "start": {
            "line": 2,
            "character": 13
        },
        "end": {
            "line": 2,
            "character": 17
        }
    },
    "parents": [
        {
            "name": "Object",
            "detail": "java.lang",
            "kind": 5,
            "deprecated": false,
            "uri": "jdt://contents/java.base/java.lang/Object.class?=type-hierarchy_9250e329/%5C/usr%5C/lib%5C/jvm%5C/java-20-openjdk%5C/lib%5C/jrt-fs.jar%60java.base=/javadoc_location=/https:%5C/%5C/docs.oracle.com%5C/en%5C/java%5C/javase%5C/20%5C/docs%5C/api%5C/=/%3Cjava.lang(Object.class",
            "range": {
                "start": {
                    "line": 30,
                    "character": 0
                },
                "end": {
                    "line": 592,
                    "character": 1
                }
            },
            "selectionRange": {
                "start": {
                    "line": 38,
                    "character": 13
                },
                "end": {
                    "line": 38,
                    "character": 19
                }
            },
            "data": {
                "element": "=type-hierarchy_9250e329/\\/usr\\/lib\\/jvm\\/java-20-openjdk\\/lib\\/jrt-fs.jar`java.base=/javadoc_location=/https:\\/\\/docs.oracle.com\\/en\\/java\\/javase\\/20\\/docs\\/api\\/=/<java.lang(Object.class[Object"
            }
        }
    ],
    "data": {
        "element": "=type-hierarchy_9250e329/_<org.example{Zero.java[Zero"
    }
}


[Trace - 18:48:25] Sending request 'workspace/executeCommand - (37)'.
Params: {
    "command": "java.navigate.resolveTypeHierarchy",
    "arguments": [
        "{\"name\":\"Object\",\"detail\":\"java.lang\",\"kind\":5,\"deprecated\":false,\"uri\":\"jdt://contents/java.base/java.lang/Object.class?=type-hierarchy_9250e329/%5C/usr%5C/lib%5C/jvm%5C/java-20-openjdk%5C/lib%5C/jrt-fs.jar%60java.base=/javadoc_location=/https:%5C/%5C/docs.oracle.com%5C/en%5C/java%5C/javase%5C/20%5C/docs%5C/api%5C/=/%3Cjava.lang(Object.class\",\"range\":{\"start\":{\"line\":30,\"character\":0},\"end\":{\"line\":592,\"character\":1}},\"selectionRange\":{\"start\":{\"line\":38,\"character\":13},\"end\":{\"line\":38,\"character\":19}},\"data\":{\"element\":\"=type-hierarchy_9250e329/\\\\/usr\\\\/lib\\\\/jvm\\\\/java-20-openjdk\\\\/lib\\\\/jrt-fs.jar`java.base=/javadoc_location=/https:\\\\/\\\\/docs.oracle.com\\\\/en\\\\/java\\\\/javase\\\\/20\\\\/docs\\\\/api\\\\/=/<java.lang(Object.class[Object\"}}",
        "1",
        "1"
    ]
}


[Trace - 18:48:25] Received response 'workspace/executeCommand - (37)' in 14ms.
Result: {
    "name": "Object",
    "detail": "java.lang",
    "kind": 5,
    "deprecated": false,
    "uri": "jdt://contents/java.base/java.lang/Object.class?=type-hierarchy_9250e329/%5C/usr%5C/lib%5C/jvm%5C/java-20-openjdk%5C/lib%5C/jrt-fs.jar%60java.base=/javadoc_location=/https:%5C/%5C/docs.oracle.com%5C/en%5C/java%5C/javase%5C/20%5C/docs%5C/api%5C/=/%3Cjava.lang(Object.class",
    "range": {
        "start": {
            "line": 30,
            "character": 0
        },
        "end": {
            "line": 592,
            "character": 1
        }
    },
    "selectionRange": {
        "start": {
            "line": 38,
            "character": 13
        },
        "end": {
            "line": 38,
            "character": 19
        }
    },
    "parents": [],
    "data": {
        "element": "=type-hierarchy_9250e329/\\/usr\\/lib\\/jvm\\/java-20-openjdk\\/lib\\/jrt-fs.jar`java.base=/javadoc_location=/https:\\/\\/docs.oracle.com\\/en\\/java\\/javase\\/20\\/docs\\/api\\/=/<java.lang(Object.class[Object"
    }
}


[Trace - 18:48:25] Sending request 'workspace/executeCommand - (38)'.
Params: {
    "command": "java.navigate.resolveTypeHierarchy",
    "arguments": [
        "{\"name\":\"Zero\",\"detail\":\"org.example\",\"kind\":5,\"deprecated\":false,\"uri\":\"file:///home/rgrunber/sample-projects/type-hierarchy/org/example/Zero.java\",\"range\":{\"start\":{\"line\":2,\"character\":0},\"end\":{\"line\":10,\"character\":1}},\"selectionRange\":{\"start\":{\"line\":2,\"character\":13},\"end\":{\"line\":2,\"character\":17}},\"data\":{\"element\":\"=type-hierarchy_9250e329/_<org.example{Zero.java[Zero\"}}",
        "2",
        "1"
    ]
}


[Info  - 18:48:25] Jun. 5, 2023, 6:48:25 p.m. >> workspace/executeCommand java.navigate.resolveTypeHierarchy
[Trace - 18:48:26] Received response 'workspace/executeCommand - (38)' in 70ms.
Result: {
    "name": "Zero",
    "detail": "org.example",
    "kind": 5,
    "deprecated": false,
    "uri": "file:///home/rgrunber/sample-projects/type-hierarchy/org/example/Zero.java",
    "range": {
        "start": {
            "line": 2,
            "character": 0
        },
        "end": {
            "line": 10,
            "character": 1
        }
    },
    "selectionRange": {
        "start": {
            "line": 2,
            "character": 13
        },
        "end": {
            "line": 2,
            "character": 17
        }
    },
    "parents": [
        {
            "name": "Object",
            "detail": "java.lang",
            "kind": 5,
            "deprecated": false,
            "uri": "jdt://contents/java.base/java.lang/Object.class?=type-hierarchy_9250e329/%5C/usr%5C/lib%5C/jvm%5C/java-20-openjdk%5C/lib%5C/jrt-fs.jar%60java.base=/javadoc_location=/https:%5C/%5C/docs.oracle.com%5C/en%5C/java%5C/javase%5C/20%5C/docs%5C/api%5C/=/%3Cjava.lang(Object.class",
            "range": {
                "start": {
                    "line": 30,
                    "character": 0
                },
                "end": {
                    "line": 592,
                    "character": 1
                }
            },
            "selectionRange": {
                "start": {
                    "line": 38,
                    "character": 13
                },
                "end": {
                    "line": 38,
                    "character": 19
                }
            },
            "data": {
                "element": "=type-hierarchy_9250e329/\\/usr\\/lib\\/jvm\\/java-20-openjdk\\/lib\\/jrt-fs.jar`java.base=/javadoc_location=/https:\\/\\/docs.oracle.com\\/en\\/java\\/javase\\/20\\/docs\\/api\\/=/<java.lang(Object.class[Object"
            }
        }
    ],
    "children": [
        {
            "name": "One",
            "detail": "org.example",
            "kind": 5,
            "deprecated": false,
            "uri": "file:///home/rgrunber/sample-projects/type-hierarchy/org/example/One.java",
            "range": {
                "start": {
                    "line": 2,
                    "character": 0
                },
                "end": {
                    "line": 6,
                    "character": 1
                }
            },
            "selectionRange": {
                "start": {
                    "line": 2,
                    "character": 13
                },
                "end": {
                    "line": 2,
                    "character": 16
                }
            },
            "data": {
                "element": "=type-hierarchy_9250e329/_<org.example{One.java[One"
            }
        },
        {
            "name": "Two",
            "detail": "org.example",
            "kind": 5,
            "deprecated": false,
            "uri": "file:///home/rgrunber/sample-projects/type-hierarchy/org/example/Two.java",
            "range": {
                "start": {
                    "line": 2,
                    "character": 0
                },
                "end": {
                    "line": 3,
                    "character": 1
                }
            },
            "selectionRange": {
                "start": {
                    "line": 2,
                    "character": 13
                },
                "end": {
                    "line": 2,
                    "character": 16
                }
            },
            "data": {
                "element": "=type-hierarchy_9250e329/_<org.example{Two.java[Two"
            }
        }
    ],
    "data": {
        "element": "=type-hierarchy_9250e329/_<org.example{Zero.java[Zero"
    }
}


[Trace - 18:48:26] Sending request 'workspace/executeCommand - (39)'.
Params: {
    "command": "java.navigate.resolveTypeHierarchy",
    "arguments": [
        "{\"name\":\"One\",\"detail\":\"org.example\",\"kind\":5,\"deprecated\":false,\"uri\":\"file:///home/rgrunber/sample-projects/type-hierarchy/org/example/One.java\",\"range\":{\"start\":{\"line\":2,\"character\":0},\"end\":{\"line\":6,\"character\":1}},\"selectionRange\":{\"start\":{\"line\":2,\"character\":13},\"end\":{\"line\":2,\"character\":16}},\"data\":{\"element\":\"=type-hierarchy_9250e329/_<org.example{One.java[One\"}}",
        "2",
        "1"
    ]
}


[Trace - 18:48:26] Sending request 'workspace/executeCommand - (40)'.
Params: {
    "command": "java.navigate.resolveTypeHierarchy",
    "arguments": [
        "{\"name\":\"Two\",\"detail\":\"org.example\",\"kind\":5,\"deprecated\":false,\"uri\":\"file:///home/rgrunber/sample-projects/type-hierarchy/org/example/Two.java\",\"range\":{\"start\":{\"line\":2,\"character\":0},\"end\":{\"line\":3,\"character\":1}},\"selectionRange\":{\"start\":{\"line\":2,\"character\":13},\"end\":{\"line\":2,\"character\":16}},\"data\":{\"element\":\"=type-hierarchy_9250e329/_<org.example{Two.java[Two\"}}",
        "2",
        "1"
    ]
}


[Info  - 18:48:26] Jun. 5, 2023, 6:48:26 p.m. >> workspace/executeCommand java.navigate.resolveTypeHierarchy
[Trace - 18:48:26] Received response 'workspace/executeCommand - (40)' in 58ms.
Result: {
    "name": "Two",
    "detail": "org.example",
    "kind": 5,
    "deprecated": false,
    "uri": "file:///home/rgrunber/sample-projects/type-hierarchy/org/example/Two.java",
    "range": {
        "start": {
            "line": 2,
            "character": 0
        },
        "end": {
            "line": 3,
            "character": 1
        }
    },
    "selectionRange": {
        "start": {
            "line": 2,
            "character": 13
        },
        "end": {
            "line": 2,
            "character": 16
        }
    },
    "parents": [
        {
            "name": "Zero",
            "detail": "org.example",
            "kind": 5,
            "deprecated": false,
            "uri": "file:///home/rgrunber/sample-projects/type-hierarchy/org/example/Zero.java",
            "range": {
                "start": {
                    "line": 2,
                    "character": 0
                },
                "end": {
                    "line": 10,
                    "character": 1
                }
            },
            "selectionRange": {
                "start": {
                    "line": 2,
                    "character": 13
                },
                "end": {
                    "line": 2,
                    "character": 17
                }
            },
            "data": {
                "element": "=type-hierarchy_9250e329/_<org.example{Zero.java[Zero"
            }
        }
    ],
    "children": [
        {
            "name": "Five",
            "detail": "org.example",
            "kind": 5,
            "deprecated": false,
            "uri": "file:///home/rgrunber/sample-projects/type-hierarchy/org/example/Five.java",
            "range": {
                "start": {
                    "line": 2,
                    "character": 0
                },
                "end": {
                    "line": 3,
                    "character": 1
                }
            },
            "selectionRange": {
                "start": {
                    "line": 2,
                    "character": 13
                },
                "end": {
                    "line": 2,
                    "character": 17
                }
            },
            "data": {
                "element": "=type-hierarchy_9250e329/_<org.example{Five.java[Five"
            }
        },
        {
            "name": "Six",
            "detail": "org.example",
            "kind": 5,
            "deprecated": false,
            "uri": "file:///home/rgrunber/sample-projects/type-hierarchy/org/example/Six.java",
            "range": {
                "start": {
                    "line": 2,
                    "character": 0
                },
                "end": {
                    "line": 7,
                    "character": 1
                }
            },
            "selectionRange": {
                "start": {
                    "line": 2,
                    "character": 13
                },
                "end": {
                    "line": 2,
                    "character": 16
                }
            },
            "data": {
                "element": "=type-hierarchy_9250e329/_<org.example{Six.java[Six"
            }
        }
    ],
    "data": {
        "element": "=type-hierarchy_9250e329/_<org.example{Two.java[Two"
    }
}


[Trace - 18:48:26] Received response 'workspace/executeCommand - (39)' in 66ms.
Result: {
    "name": "One",
    "detail": "org.example",
    "kind": 5,
    "deprecated": false,
    "uri": "file:///home/rgrunber/sample-projects/type-hierarchy/org/example/One.java",
    "range": {
        "start": {
            "line": 2,
            "character": 0
        },
        "end": {
            "line": 6,
            "character": 1
        }
    },
    "selectionRange": {
        "start": {
            "line": 2,
            "character": 13
        },
        "end": {
            "line": 2,
            "character": 16
        }
    },
    "parents": [
        {
            "name": "Zero",
            "detail": "org.example",
            "kind": 5,
            "deprecated": false,
            "uri": "file:///home/rgrunber/sample-projects/type-hierarchy/org/example/Zero.java",
            "range": {
                "start": {
                    "line": 2,
                    "character": 0
                },
                "end": {
                    "line": 10,
                    "character": 1
                }
            },
            "selectionRange": {
                "start": {
                    "line": 2,
                    "character": 13
                },
                "end": {
                    "line": 2,
                    "character": 17
                }
            },
            "data": {
                "element": "=type-hierarchy_9250e329/_<org.example{Zero.java[Zero"
            }
        }
    ],
    "children": [
        {
            "name": "Four",
            "detail": "org.example",
            "kind": 5,
            "deprecated": false,
            "uri": "file:///home/rgrunber/sample-projects/type-hierarchy/org/example/Four.java",
            "range": {
                "start": {
                    "line": 2,
                    "character": 0
                },
                "end": {
                    "line": 7,
                    "character": 1
                }
            },
            "selectionRange": {
                "start": {
                    "line": 2,
                    "character": 13
                },
                "end": {
                    "line": 2,
                    "character": 17
                }
            },
            "data": {
                "element": "=type-hierarchy_9250e329/_<org.example{Four.java[Four"
            }
        },
        {
            "name": "Three",
            "detail": "org.example",
            "kind": 5,
            "deprecated": false,
            "uri": "file:///home/rgrunber/sample-projects/type-hierarchy/org/example/Three.java",
            "range": {
                "start": {
                    "line": 2,
                    "character": 0
                },
                "end": {
                    "line": 3,
                    "character": 1
                }
            },
            "selectionRange": {
                "start": {
                    "line": 2,
                    "character": 13
                },
                "end": {
                    "line": 2,
                    "character": 18
                }
            },
            "data": {
                "element": "=type-hierarchy_9250e329/_<org.example{Three.java[Three"
            }
        }
    ],
    "data": {
        "element": "=type-hierarchy_9250e329/_<org.example{One.java[One"
    }
}

You can find the code that's used on the vscode-java client-side to do this at https://github.com/redhat-developer/vscode-java/blob/master/src/typeHierarchy/model.ts . Hope this helps.

@lbrayner
Copy link
Contributor Author

lbrayner commented Jun 6, 2023

Thanks, @rgrunber , this helps a lot! I'll be sure to keep up with these developments and maybe implement code similar to vscode-java and make a PR on nvim-jdtls. 🤝

@hopehadfield
Copy link
Contributor

Working on this

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

Successfully merging a pull request may close this issue.

3 participants