/********************************************************************************
 * Copyright (C) 2017-2018 TypeFox and others.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the Eclipse
 * Public License v. 2.0 are satisfied: GNU General Public License, version 2
 * with the GNU Classpath Exception which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 ********************************************************************************/ import { injectable , inject } from '@theia/core/shared/inversify' ; import { TreeWidget , TreeNode , NodeProps , SelectableTreeNode , CompositeTreeNode , TreeProps , ContextMenuRenderer , TreeModel , ExpandableTreeNode , codicon } from '@theia/core/lib/browser' ; import { OutlineViewTreeModel } from './outline-view-tree-model' ; import { Message } from '@theia/core/shared/@phosphor/messaging' ; import { Emitter , Mutable , UriSelection } from '@theia/core' ; import * as React from '@theia/core/shared/react' ; import { Range } from '@theia/core/shared/vscode-languageserver-protocol' ; import URI from '@theia/core/lib/common/uri' ; import { nls } from '@theia/core/lib/common/nls' ; /**
 * Representation of an outline symbol information node.
 */ export interface OutlineSymbolInformationNode extends CompositeTreeNode , SelectableTreeNode , ExpandableTreeNode { /**
     * The `iconClass` for the given tree node.
     */ iconClass : string ; } /**
 * Collection of outline symbol information node functions.
 */ export namespace OutlineSymbolInformationNode { param node returns export function is ( node ) { return ! ! node && SelectableTreeNode . is ( node ) && 'iconClass' in node ; } export function hasRange ( node ) { return typeof node === 'object' && ! ! node && 'range' in node && Range . is ( ( node as { range : Range ; } ) . range ) ; } } export type OutlineViewWidgetFactory = ( ) => OutlineViewWidget ; export const OutlineViewWidgetFactory = Symbol ( 'OutlineViewWidgetFactory' ) ; @ injectable ( ) export class OutlineViewWidget extends TreeWidget { static LABEL = nls . localizeByDefault ( 'Outline' ) ; readonly onDidChangeOpenStateEmitter = new Emitter < boolean > ( ) ; constructor ( @ inject ( TreeProps ) protected readonly treeProps , @ inject ( OutlineViewTreeModel ) model , @ inject ( ContextMenuRenderer ) protected readonly contextMenuRenderer ) { super ( treeProps , model , contextMenuRenderer ) ; this . id = 'outline-view' ; this . title . label = OutlineViewWidget . LABEL ; this . title . caption = OutlineViewWidget . LABEL ; this . title . closable = true ; this . title . iconClass = codicon ( 'symbol-class' ) ; this . addClass ( 'theia-outline-view' ) ; } param roots public setOutlineTree ( roots ) { const nodes = this . reconcileTreeState ( roots ) ; this . model . root = this . getRoot ( nodes ) ; } protected getRoot ( children ) { return { id : 'outline-view-root' , name : OutlineViewWidget . LABEL , visible : false , children , parent : undefined } ; } param nodes returns protected reconcileTreeState ( nodes ) { nodes . forEach ( node => { if ( OutlineSymbolInformationNode . is ( node ) ) { const treeNode = this . model . getNode ( node . id ) ; if ( treeNode && OutlineSymbolInformationNode . is ( treeNode ) ) { treeNode . expanded = node . expanded ; treeNode . selected = node . selected ; } this . reconcileTreeState ( Array . from ( node . children ) ) ; } } ) ; return nodes ; } protected onAfterHide ( msg ) { super . onAfterHide ( msg ) ; this . onDidChangeOpenStateEmitter . fire ( false ) ; } protected onAfterShow ( msg ) { super . onAfterShow ( msg ) ; this . onDidChangeOpenStateEmitter . fire ( true ) ; } renderIcon ( node , props ) { if ( OutlineSymbolInformationNode . is ( node ) ) { return < div className = { 'symbol-icon symbol-icon-center ' + node . iconClass } > < /div>; } return undefined ; } protected createNodeAttributes ( node , props ) { const elementAttrs = super . createNodeAttributes ( node , props ) ; return { ... elementAttrs , title : this . getNodeTooltip ( node ) } ; } param node returns protected getNodeTooltip ( node ) { if ( OutlineSymbolInformationNode . is ( node ) ) { return node . name + ` (${ node . iconClass })` ; } return undefined ; } protected isExpandable ( node ) { return OutlineSymbolInformationNode . is ( node ) && node . children . length > 0 ; } protected renderTree ( model ) { if ( CompositeTreeNode . is ( this . model . root ) && ! this . model . root . children . length ) { return < div className = 'theia-widget-noInfo no-outline' > { nls . localizeByDefault ( 'No outline information available.' ) } < /div>; } return super . renderTree ( model ) ; } protected deflateForStorage ( node ) { const deflated = super . deflateForStorage ( node ) as { uri : string ; } ; if ( UriSelection . is ( node ) ) { deflated . uri = node . uri . toString ( ) ; } return deflated ; } protected inflateFromStorage ( node , parent ? ) { const inflated = super . inflateFromStorage ( node , parent ) as Mutable < TreeNode & UriSelection > ; if ( node && 'uri' in node && typeof node . uri === 'string' ) { inflated . uri = new URI ( node . uri ) ; } return inflated ; } }