This is an archive of the discontinued LLVM Phabricator instance.

[clangd] Add semantic selection to ClangdLSPServer.
ClosedPublic

Authored by usaxena95 on Sep 18 2019, 10:18 AM.

Details

Summary

This adds semantic selection to the LSP Server.
Adds support for serialization of input request and the output reply.
Also adds regression tests for the feature.

Currently we do not support multi cursor.The LSP Server only accepts single position in the request as opposed to many position in the spec.

Spec:
https://github.com/microsoft/language-server-protocol/blob/dbaeumer/3.15/specification.md#textDocument_selectionRange

Diff Detail

Repository
rL LLVM

Event Timeline

usaxena95 created this revision.Sep 18 2019, 10:18 AM
Herald added a project: Restricted Project. · View Herald TranscriptSep 18 2019, 10:18 AM

A drive-by comment...

clang-tools-extra/clangd/ClangdLSPServer.cpp
1135 ↗(On Diff #220699)

This should be return Reply(...), right?
Especially bad if positions is empty as we attempt to read the first element right away.

Fixed error message.

usaxena95 marked 2 inline comments as done.

Addressed comments.

usaxena95 added inline comments.Sep 18 2019, 11:16 AM
clang-tools-extra/clangd/ClangdLSPServer.cpp
1135 ↗(On Diff #220699)

Aah. Yeah. Thanks.

I think we're missing the client/server capability implementation in this patch, we should include them.

clang-tools-extra/clangd/ClangdLSPServer.cpp
1131 ↗(On Diff #220710)

maybe add an assert(!Params.positions.empty()). I think we should not run into this case.

clang-tools-extra/clangd/Protocol.h
1234 ↗(On Diff #220710)

The data structures defined in Protocol are mirrored to LSP's. I think we should follow it for semantic selection as well (even though LSP use a wired structure, linked list).

so here, the structure should be like

struct SelectionRange {
    /**
     * The [range](#Range) of this selection range.
     */
    Range range;
    /**
     * The parent selection range containing this range. Therefore `parent.range` must contain `this.range`.
     */
    llvm::Optional<SelectionRange> parent;
}

And we'd need to define a render function in ClangLSPServer to covert the vector<Range> to the LSP's SelectionRange.

1241 ↗(On Diff #220710)

does this operator get used in this patch?

clang-tools-extra/clangd/test/selection-range.test
4 ↗(On Diff #220710)

could we create a smaller test? just void func() {} is enough, I'd keep the lit test as small as possible (since this feature has been test in the unittest)

ilya-biryukov added inline comments.Sep 19 2019, 1:13 AM
clang-tools-extra/clangd/ClangdLSPServer.cpp
1131 ↗(On Diff #220710)

But Params comes to clangd over LSP, right?
That means assert can fire in case of bad inputs over LSP to clangd.
Bad inputs over LSP should never crash clangd.

usaxena95 updated this revision to Diff 221499.Sep 24 2019, 3:34 AM
usaxena95 marked 7 inline comments as done.

Addressed comments:
Added client/server capabilities.
Made lit test smaller.

I am not sure adding client capability is useful here. I have not used the client capability for selectionRange anywhere and I think we can remove it.
WDYT ?

clang-tools-extra/clangd/ClangdLSPServer.cpp
1131 ↗(On Diff #220710)

Yes this comes from the client and can be a bad input. We should just return error and not crash in such case.

clang-tools-extra/clangd/Protocol.h
1241 ↗(On Diff #220710)

No. Removed it.

I am not sure adding client capability is useful here. I have not used the client capability for selectionRange anywhere and I think we can remove it.
WDYT ?

The SelectionRangeClientCapabilities determines what should the LSP server send the client, if it is true, clangd should send SelectionRangeRegistrationOptions. But looking at the current specification, it doesn't seem to add too much value. I think we can just simplify return a bool for now (as you did in this patch).

clang-tools-extra/clangd/ClangdLSPServer.cpp
141 ↗(On Diff #221499)

nit: use for range.

1131 ↗(On Diff #220710)

but the code still doesn't handle the empty case?

clang-tools-extra/clangd/Protocol.h
1248 ↗(On Diff #221499)

I think we can simplify the code further, using llvm::Optional<SelectionRange> should be enough, the parent is null for the outer-most range.

usaxena95 updated this revision to Diff 221510.Sep 24 2019, 4:37 AM
usaxena95 marked 5 inline comments as done.

Addressed comments.

The SelectionRangeClientCapabilities determines what should the LSP server send the client, if it is true, clangd should send SelectionRangeRegistrationOptions.
But looking at the current specification, it doesn't seem to add too much value. I think we can just simplify return a bool for now (as you did in this patch).

Yeah. So should I remove the client capability since we do not use it and just return bool as now?

clang-tools-extra/clangd/ClangdLSPServer.cpp
1131 ↗(On Diff #220710)

We do right ?
If the size != 1 then we just return an error.

clang-tools-extra/clangd/Protocol.h
1248 ↗(On Diff #221499)

I don't think it is possible to do that since the type (SelectionRange) would be incomplete at that point. For example size of this class cannot be computed.

mostly good, a few nits.

clang-tools-extra/clangd/ClangdLSPServer.cpp
136 ↗(On Diff #221510)

nit: remove the {}, in LLVM we prefer no {} for a single-statement body.

1169 ↗(On Diff #221510)

does Reply({render(std::move(*Ranges))}); work?

1131 ↗(On Diff #220710)

yes, it is correct now. maybe I read the wrong snapshot before.

clang-tools-extra/clangd/Protocol.h
422 ↗(On Diff #221510)

I think we could drop this field now, it is not used, as we always return selectionRangeProvider:true.

1241 ↗(On Diff #221510)

nit: please remove the markdown elements around the range in the comment.

1251 ↗(On Diff #221510)

Are these constructors needed?

1248 ↗(On Diff #221499)

ah, good point, I see. We could just use std::unique_ptr<SelectionRange> parent;.

usaxena95 updated this revision to Diff 221521.Sep 24 2019, 5:46 AM
usaxena95 marked 9 inline comments as done.

Addressed comments.

clang-tools-extra/clangd/ClangdLSPServer.cpp
1169 ↗(On Diff #221510)

List initialization of vector of move only types is painful 😃
Even this does not work: Reply(std::vector<SelectionRange>{render(std::move(*Ranges))}); because I think it vector tries to copy construct it because of initializer_lists

clang-tools-extra/clangd/Protocol.h
1251 ↗(On Diff #221510)

We don't need them if we use just unique_ptr. They were needed before in Optional<unique_ptr>. Somehow it was not able to deduce this is a trivially-constructible + move only class.

hokein accepted this revision.Sep 24 2019, 5:58 AM

thanks, looks good.

clang-tools-extra/clangd/Protocol.cpp
18 ↗(On Diff #221521)

the header seems not used?

This revision is now accepted and ready to land.Sep 24 2019, 5:58 AM
usaxena95 updated this revision to Diff 221534.Sep 24 2019, 6:35 AM

Removed ununsed header.

This revision was automatically updated to reflect the committed changes.
Herald added a project: Restricted Project. · View Herald TranscriptSep 24 2019, 6:38 AM