0

I have added my own explorer view in my extension. Here I added nodes/tree view items however I am not finding any way to customize and choose color my tree view items in explorer view. Any idea how to achieve this? There should be some way because when some file has error then its color is set to different compared to other open file.

ajayg
  • 27
  • 4

1 Answers1

0

[I assume this is your github issue: Not able to use FileDecorationProvider for tree view item.]

Here is my attempt at using a FileDecorationProvider for a custom TreeView. With the caveat that I am new to typescript and FileDecorations.

If you have seen Support proposed DecorationProvider api on custom views you know there are limitations on using a FileDecorationProvider for coloring TreeItem's - primarily that the decoration/coloration cannot be limited to your treeView - wherever that resourceUri apeears, like in the Explorer, your fileDecoration will be applied. That is very unfortunate but I don't believe there is any way to avoid that for now.

First, in your TreeItem class you will have to give whichever items you want decorated a resourceUri. Like this:

export class TreeTab extends vscode.TreeItem {

    constructor( public readonly tab: vscode.Tab, public index: number = 0 ) {
        super(tab.label, vscode.TreeItemCollapsibleState.None);

        this.tab = tab;
        if (tab.input instanceof vscode.TabInputText) {
            this.resourceUri = tab.input.uri;
        }
}

Ignore the specifics of the code for my extension, the point is:

this.resourceUri = <some vscode.Uri>;

Secondly, this is how I set up my FileDecoration class:

import {window, Tab, TabInputText, Uri, Disposable, Event, EventEmitter, FileDecoration, FileDecorationProvider, ThemeColor} from 'vscode';


export class TreeFileDecorationProvider implements FileDecorationProvider {

  private disposables: Array<Disposable> = [];

  private readonly _onDidChangeFileDecorations: EventEmitter<Uri | Uri[]> = new EventEmitter< Uri | Uri[]>();
  readonly onDidChangeFileDecorations: Event<Uri | Uri[]> = this._onDidChangeFileDecorations.event;

  constructor() {
    this.disposables = [];
    this.disposables.push(window.registerFileDecorationProvider(this));
  } 

  async updateActiveEditor(activeTab: Tab): Promise<void>  {

    if (activeTab.input instanceof TabInputText)
      this._onDidChangeFileDecorations.fire(activeTab.input.uri);

    // filter to get only non-activeTabs
    activeTab.group.tabs.map( tab => {
      if (!tab.isActive && tab.input instanceof TabInputText) 
        this._onDidChangeFileDecorations.fire(tab.input.uri);
    });
  }

  async provideFileDecoration(uri: Uri): Promise<FileDecoration | undefined> {
    const activeEditor = window.activeTextEditor.document.uri;
    if (uri.fsPath === activeEditor.fsPath) {
      return {
        badge: "⇐",
        color: new ThemeColor("charts.red"), 
        // color: new vscode.ThemeColor("tab.activeBackground"), 
        // tooltip: ""
      };
    }
    else return null;  // to get rid of the custom fileDecoration
  }

  dispose() {
    this.disposables.forEach((d) => d.dispose());
  }
}

provideFileDecoration(uri: Uri) does the actual decorating. It finds only certain files and decorates them, and by returning null resets that previously decorated uri (as supplied by the uri argument).

updateActiveEditor() is an exported method that I call in other parts of the extension when I want to change a file decoration. So elsewhere I have this in another file:

import { TreeFileDecorationProvider } from './fileDecorator';

export class EditorManager {

    public TreeItemDecorator: TreeFileDecorationProvider;

// and then on a listener that gets triggered when I need to make a change to some things including the FileDecoration for a uri
     this.TreeItemDecorator.updateActiveEditor(activeTab);

this.TreeItemDecorator.updateActiveEditor(activeTab); that calls the updateActiveEditor method in the TreeFileDecorationProvider class which calls the this._onDidChangeFileDecorations.fire(<some uri>); method for uri's that need to have the decoration applied and also for uri's that need to have the decoration removed.

this._onDidChangeFileDecorations.fire(<some uri>); will call provideFileDecoration(uri: Uri) where the actual decoration will be applied or removed depending on some state of that uri.

  • I am sure there is a way to call onDidChangeFileDecorations() directly from another file in your project (if you don't need to do any pre-processing of the uri like I have to do. I just haven't figured out how to construct the argument for that function yet. Perhaps someone will help on that point.

You can see here:

color: new ThemeColor("charts.red"), 
// color: new vscode.ThemeColor("tab.activeBackground"),

how a color is chosen - it must be some ThemeColor. The charts theme colors has a few basic colors that are handy to refer to. See theme color references, Charts therein.

The badge option can take up to 2 characters, but as you see I copied/pasted a unicode character for mine and that works.

As I mentioned my FileDecorationProvider is called from an eventListener, but you may not need that for your use case - if decorations do not have to added and removed based on user actions like in my case. So you may be able to call your FileDecorationProvider right from your extension.ts activate() like so:

import * as vscode from 'vscode';
import { TreeFileDecorationProvider } from './fileDecorator';


export async function activate(context: vscode.ExtensionContext) {

    new TreeFileDecorationProvider();
}

Other references:

  1. a treeDecorationProvider.ts example
  2. part of the git extension that does file decorations
  3. Custom view decorations in VSCode extension
Mark
  • 143,421
  • 24
  • 428
  • 436
  • This is not working for tree view item instead it seems to be for the file open in the editor. In my case i do not have any file open i.e. there is no window.activeTextEditor. How this sample is associated with treeview item color docoration? – ajayg Nov 24 '22 at 16:42
  • Did you read the github issue I linked? What you are seeing is the expected result. I only used the activeEditor as an example to change the TreeItem label color based on whether the file was active. It is just an example of manipulating the color. – Mark Dec 06 '22 at 22:56
  • ok..let me try again ..thanks – ajayg Dec 08 '22 at 06:00