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

[outline] Use the DocumentSymbol.detail not just only the `Document… #3314

Merged
merged 1 commit into from
Dec 4, 2018
Merged

Conversation

vrubezhny
Copy link
Contributor

@vrubezhny vrubezhny commented Oct 29, 2018

…Symbol.name` #2894

This fix:

  • adds the decorator service to the outline
  • adds the tree node decorator to the monaco editor in order to decorate nodes using DocumentSymbo's detail property

Signed-off-by: Victor Rubezhny vrubezhny@redhat.com

t2894

[outline] Outline is added with a possibility to decorate the text of the tree elements.

Copy link
Contributor

@kittaakos kittaakos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice feature, perhaps we could do this a bit differently. I did not want to add any inline comments but comment on this in general and discuss it before getting into the code.

The colored ranges is a useful addition, but I do not like the way we're extracting the detail meta-information from the name of the tree node by checking the position of the colon (:). Instead;

  • I would update the OutlineSymbolInformationNode with an optional detail property.
  • Proposal: instead of having a decorator for the Outline (that we might need later), we can attach the decoration information to the tree node.

https://github.com/theia-ide/theia/blob/8bb854571382333ffe50d2e209e3d6ec7c1bf203/packages/core/src/browser/tree/tree-widget.tsx#L594-L603

https://github.com/theia-ide/theia/blob/8bb854571382333ffe50d2e209e3d6ec7c1bf203/packages/core/src/browser/tree/tree-decorator.ts#L507-L525

  • If rather stick to your approach, please update the JavaDecorator to use OutlineSymbolInformationNode instead of the TreeNode.
  • Finally, you could use the caption suffixes to achieve the same UX. See here. No colored ranges are available; still one can use different colors for the label.

Copy link
Contributor

@kittaakos kittaakos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a few remarks if you are willing to continue with your initial idea.

if (color) {
result.push({ data: caption.substr(i, length), color: color });
} else {
result.push({ data: caption.substr(i, length), highligh: true, color: color });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to fix this typo: highligh.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, probably there was a typo. I just left it as it was before.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what the policy is in Theia about drive-by fixes.

const name = node.name;
const colon = name.indexOf(':');
return {
priority: 200, highlight: {ranges: [{offset: colon, length: name.length - colon, color: '0x847a4c'}]}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0x847a4c => We need to use pre-defined constants instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This color value is actually not used. I just couldn't find a proper way to use here something like var(--theia-word-highlight-color0), so the actual color to be used is specified in browser/style/tree.css like:

.theia-TreeNodeSegment label {
    color: var(--theia-word-highlight-color0);
}

The point is that the highlight property stays undefined and, as such, label element is used to decorate the text instead of mark

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's unused, we should use a constant that marks it as such, e.g. '0xdeadbeef" or something similar.

export const OutlineTreeDecorator = Symbol('OutlineTreeDecorator');

/**
* Decorator service for the navigator.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

navigator => copy-paste. (Same above.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure. to be fixed

protected readonly emitter = new Emitter<(tree: Tree) => Map<string, TreeDecoration.Data>>();

@postConstruct()
protected init(): void {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exactly

const { name } = treeNode;
const colon = name.indexOf(':');

if (colon !== -1 && name.length > colon + 1) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a bit puzzled; is name.length > colon + 1 equivalent to !name.endsWith(':')? If so, please simplify. Otherwise, can you please explain to me what use-case you want to handle here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, exactly.

@@ -383,6 +395,9 @@ export namespace TreeDecoration {

}

export namespace ColoredRange {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Obsolete?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's used instead of Range in CaptionHighltght for ranges property:

   export interface CaptionHighlight {

        /**
         * The ranges to highlight in the caption.
         */
        readonly ranges: CaptionHighlight.ColoredRange[]
...

But again, I couldn't properly set it when creating ranges in JavaDecorator (so, the color defined in css is used instead)

Sorry, I know, there is a lot of such inconsistencies in this PR, but the intention was to present the solution to the discussion and find a way to improve it and fix it before it can be accepted.

@vrubezhny
Copy link
Contributor Author

I like your proposal with adding detail meta information, but...

  • I would update the OutlineSymbolInformationNode with an optional detail property.
  • Proposal: instead of having a decorator for the Outline (that we might need later), we can attach the decoration information to the tree node.

Who'll bring this additional information to the node? Do we still need some kind of decorator to provide such info for the nodes?
Currently, all the TreeNodes are created in https://github.com/theia-ide/theia/blob/master/packages/monaco/src/browser/monaco-outline-contribution.ts#L275, so we can't distinguish here between a css style node and a java node, for example. (In order to decorate the only JavaElement nodes I've added a filter that checks if the current editor is a Java editor, so the other kinds of outline tree nodes (like, for example, in CSS editor: .theia-ExpansionToggle:hover) stay not decorated)

  • If rather stick to your approach, please update the JavaDecorator to use OutlineSymbolInformationNode instead of the TreeNode.

Just a question: why? I don't use any special properties/methods of OutlineSymbolInformationNode, the generic TreeNode has all I need...

  • Finally, you could use the caption suffixes to achieve the same UX. See here. No colored ranges are available; still one can use different colors for the label.

Thanks for pointing me to this type hierarchy example, I was looking mostly at git decorations that use caption suffixes and there I didn't spot that I can use them the same way I did in my solution. Shame on me, but well, this is my first try :)

@kittaakos
Copy link
Contributor

Who'll bring this additional information to the node?

It is on the DocumentSymbol. We can generalize it. If the detail is there, we decorate it. Otherwise, we do nothing. There's no need to have specific code for the Java Outline.

Just a question: why? I don't use any special properties/methods of OutlineSymbolInformationNode, the generic TreeNode has all I need...

Just a question: why? I don't use any special properties/methods of OutlineSymbolInformationNode, the generic TreeNode has all I need...

I think it would be better to work with OutlineSymbolInformationNode if you use it for the Outline. And as I said, I think we could do better than finding a non-trailing colon in text and do something based on that.

const name = node.name;
const colon = name.indexOf(':');
return {
priority: 200, highlight: {ranges: [{offset: colon, length: name.length - colon, color: '0x847a4c'}]}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's unused, we should use a constant that marks it as such, e.g. '0xdeadbeef" or something similar.

if (color) {
result.push({ data: caption.substr(i, length), color: color });
} else {
result.push({ data: caption.substr(i, length), highligh: true, color: color });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what the policy is in Theia about drive-by fixes.

import { JAVA_LANGUAGE_ID } from '../common';

@injectable()
export class JavaDecorator implements TreeDecorator {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @kittaakos that use of "details" should not be Java-specific. See my comment in the PR main page for more.

@tsmaeder
Copy link
Contributor

tsmaeder commented Nov 5, 2018

I'm liking the conversation in this PR, here's my 2 cents:

  1. DocumentSymbol.detail is not specific to Java, so our handling of it should not be Java-specific
  2. However, the fact that we show DocumentSymbol instances is specific to the MonacoOutlineContribution. If we extend an interface, we should extend that one.
  3. We do not know what kinds of information decorators need to have: we should expose the "DocumentSymbol" object the tree node represents in MonacoOutlineSymbolInformationNode
  4. We can use property accessors to compute the things we need to implement the TreeNode interface based on the SymbolData, so when we create the tree nodes at https://github.com/theia-ide/theia/blob/master/packages/monaco/src/browser/monaco-outline-contribution.ts#L275 , we should not just return an anonymous JS object, but a class
  5. Tree decorators can add prefixes and suffixes to the tree node caption. Shouldn't we use this instead of futzing with the highlights? Shouldn't 'detail' be a suffix?

@vrubezhny
Copy link
Contributor Author

The PR is updated

return this.emitter.event;
}

protected collectDecorators(tree: Tree): Map<string, TreeDecoration.Data> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems copied from the GitTreeDecorator, but the naming here is off: this is not collecting decorators, this is collecting decorations. The method should be named "collectDecorations"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree

result.set(treeNode.id, treeNode);
}
}
return new Map(Array.from(result.entries()).map(m => [m[0], this.toDecorator(m[1])] as [string, TreeDecoration.Data]));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no idea why we're not producing a map of id->decoration directly. Why go through this dance with creating an array, iterating over it, etc?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree again.

@vrubezhny
Copy link
Contributor Author

I've rebased and squashed the commits of the PR

@kittaakos kittaakos self-requested a review November 22, 2018 17:08
@@ -342,6 +347,7 @@ export interface MonacoOutlineSymbolInformationNode extends OutlineSymbolInforma
uri: URI;
range: Range;
fullRange: Range;
detail: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have not yet tried your changes in action but I think this should be detail?: string instead. See the DocumentSymbol.detail in the specification.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought it should be detail?: string, but then I found its definition in monaco.d.ts in node_modules/@typefox/monaco-editor-core/ which tells that it's not optional:

documentsymbol detail

That's why I've made it to be detail: string. Still not sure if it's correct, but changing it to be optional will change nothing as it will be always initialized from DocumentSymbol.detail which is not an optional.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DocumentSymbol.detail which is not an optional.

@akosyakov, what's the truth? The TS-LS fills the detail with an empty string, but according to the LSP, DocumentSymbol.detail is optional. Thanks!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we don't control monace.d.ts we will have to handle the discrepancy: note that null is a valid value for a non-optional field, so we just have to handle the case of "undefined field" in the LSP version of DcocumentSymbol correctly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we don't control monace.d.ts

FYI: @typefox/monaco-editor-core

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I mean is that it doesn't really matter: we either have a value or we don't. If monaco wants a value, we can pass null if we don't have on.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akosyakov, ping.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the truth?

In VS Code and Monaco DocumentSymobl.detail is mandatory, in LSP is optional. They are all different APIs.

The TS-LS fills the detail with an empty string, but according to the LSP, DocumentSymbol.detail is optional.

TS-LS fills detail with empty string to work around a bug in version of vscode-languageserver-node which we use for Monaco language client. It can not distinguish between LSP DocumentSymbol and SymbolInformation otherwise: https://github.com/TypeFox/vscode-languageserver-node/blob/fb80c5e2db4c714032c28d6f482e770642ae9e23/types/src/main.ts#L1701

Copy link
Contributor

@tsmaeder tsmaeder Nov 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't see why this makes a difference: if there is no value, let's use an emtpy string and be done with. What is the actual problem here? @akosyakov

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

work around a bug in version of vscode-languageserver-node which we use for Monaco language client. It can not distinguish between LSP DocumentSymbol and SymbolInformation

Note, the issue seems to be fixed in the original source: https://github.com/Microsoft/vscode-languageserver-node/blob/c80ead2e4162cf3f7b547ea2dfc57e64dbd6d423/types/src/main.ts#L1903-L1911

Copy link
Contributor

@kittaakos kittaakos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works nicely. Great enhancement! I already added a comment; the detail should be optional.

Copy link
Contributor

@kittaakos kittaakos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the patch!

@kittaakos
Copy link
Contributor

I have restarted the Windows build. Presumably, it was failing with an unrelated error.
@vrubezhny, ping me if I should push the "merge" button.

@vrubezhny
Copy link
Contributor Author

vrubezhny commented Nov 28, 2018

@kittaakos I've added a fixup that makes MonacoOutlineSymbolInformationNode.detail to be optional. So, if DocumentSymbol.detail value of a node is undefined - it should be treated correctly and such a node is not to be decorated.
Please, review.

@kittaakos
Copy link
Contributor

I am checking this PR again...

Copy link
Contributor

@kittaakos kittaakos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works great! Sorry for not noticing your latest comment on the PR, @vrubezhny.

@vrubezhny
Copy link
Contributor Author

@kittaakos Good! Then I'm going to squash it, rebase and then merge it

@kittaakos
Copy link
Contributor

Good! Then I'm going to squash it, rebase and then merge it

Super, thanks!

…Symbol.name` #2894

This fix:
- adds the decorator service to the outline
- adds the tree node decorator to the monaco editor in order to decorate nodes using DocumentSymbo's detail property

Signed-off-by: Victor Rubezhny <vrubezhny@redhat.com>
@vrubezhny vrubezhny merged commit 5b3d0aa into eclipse-theia:master Dec 4, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants