Skip to content

Commit

Permalink
Add package name to output (microsoft#12683)
Browse files Browse the repository at this point in the history
  • Loading branch information
rchiodo committed Jun 30, 2020
1 parent a6db5c0 commit 8bb3ddf
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 9 deletions.
4 changes: 3 additions & 1 deletion package.nls.json
Expand Up @@ -550,5 +550,7 @@
"DataScienceRendererExtension.installationCompleteMessage": "complete.",
"DataScienceRendererExtension.startingDownloadOutputMessage": "Starting download of Notebook Renderers extension.",
"DataScienceRendererExtension.downloadingMessage": "Downloading Notebook Renderers Extension...",
"DataScienceRendererExtension.downloadCompletedOutputMessage": "Notebook Renderers extension download complete."
"DataScienceRendererExtension.downloadCompletedOutputMessage": "Notebook Renderers extension download complete.",
"DataScience.uriProviderDescriptionFormat": "{0} (From {1} extension)",
"DataScience.unknownPackage": "unknown"
}
5 changes: 5 additions & 0 deletions src/client/common/utils/localize.ts
Expand Up @@ -316,6 +316,11 @@ export namespace DataScience {
'DataScience.unknownServerUri',
'Server URI cannot be used. Did you uninstall an extension that provided a Jupyter server connection?'
);
export const uriProviderDescriptionFormat = localize(
'DataScience.uriProviderDescriptionFormat',
'{0} (From {1} extension)'
);
export const unknownPackage = localize('DataScience.unknownPackage', 'unknown');
export const historyTitle = localize('DataScience.historyTitle', 'Python Interactive');
export const dataExplorerTitle = localize('DataScience.dataExplorerTitle', 'Data Viewer');
export const badWebPanelFormatString = localize(
Expand Down
60 changes: 54 additions & 6 deletions src/client/datascience/jupyterUriProviderRegistration.ts
@@ -1,8 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import { inject, injectable } from 'inversify';
import * as path from 'path';
import { IFileSystem } from '../common/platform/types';
import { IExtensions } from '../common/types';
import * as localize from '../common/utils/localize';
import { EXTENSION_ROOT_DIR } from '../constants';
import { JupyterUriProviderWrapper } from './jupyterUriProviderWrapper';
import {
IJupyterServerUri,
IJupyterUriProvider,
Expand All @@ -13,20 +17,23 @@ import {
@injectable()
export class JupyterUriProviderRegistration implements IJupyterUriProviderRegistration {
private loadedOtherExtensionsPromise: Promise<void> | undefined;
private providers = new Map<string, IJupyterUriProvider>();
private providers = new Map<string, Promise<IJupyterUriProvider>>();

constructor(@inject(IExtensions) private readonly extensions: IExtensions) {}
constructor(
@inject(IExtensions) private readonly extensions: IExtensions,
@inject(IFileSystem) private readonly fileSystem: IFileSystem
) {}

public async getProviders(): Promise<ReadonlyArray<IJupyterUriProvider>> {
await this.checkOtherExtensions();

// Other extensions should have registered in their activate callback
return [...this.providers.values()];
return Promise.all([...this.providers.values()]);
}

public registerProvider(provider: IJupyterUriProvider) {
if (!this.providers.has(provider.id)) {
this.providers.set(provider.id, provider);
this.providers.set(provider.id, this.createProvider(provider));
} else {
throw new Error(`IJupyterUriProvider already exists with id ${provider.id}`);
}
Expand All @@ -35,8 +42,9 @@ export class JupyterUriProviderRegistration implements IJupyterUriProviderRegist
public async getJupyterServerUri(id: string, handle: JupyterServerUriHandle): Promise<IJupyterServerUri> {
await this.checkOtherExtensions();

const provider = this.providers.get(id);
if (provider) {
const providerPromise = this.providers.get(id);
if (providerPromise) {
const provider = await providerPromise;
return provider.getServerUri(handle);
}
throw new Error(localize.DataScience.unknownServerUri());
Expand All @@ -55,4 +63,44 @@ export class JupyterUriProviderRegistration implements IJupyterUriProviderRegist
.map((e) => (e.isActive ? Promise.resolve() : e.activate()));
await Promise.all(list);
}

private async createProvider(provider: IJupyterUriProvider): Promise<IJupyterUriProvider> {
const packageName = await this.determineExtensionFromCallstack();
return new JupyterUriProviderWrapper(provider, packageName);
}

private async determineExtensionFromCallstack(): Promise<string> {
const stack = new Error().stack;
if (stack) {
const root = EXTENSION_ROOT_DIR.toLowerCase();
const frames = stack.split('\n').map((f) => {
const result = /\((.*)\)/.exec(f);
if (result) {
return result[1];
}
});
for (const frame of frames) {
if (frame && !frame.startsWith(root)) {
// This file is from a different extension. Try to find its package.json
let dirName = path.dirname(frame);
let last = frame;
while (dirName && dirName.length < last.length) {
const possiblePackageJson = path.join(dirName, 'package.json');
if (await this.fileSystem.fileExists(possiblePackageJson)) {
const text = await this.fileSystem.readFile(possiblePackageJson);
try {
const json = JSON.parse(text);
return `${json.publisher}.${json.name}`;
} catch {
// If parse fails, then not the extension
}
}
last = dirName;
dirName = path.dirname(dirName);
}
}
}
}
return localize.DataScience.unknownPackage();
}
}
44 changes: 44 additions & 0 deletions src/client/datascience/jupyterUriProviderWrapper.ts
@@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import * as vscode from 'vscode';
import * as localize from '../common/utils/localize';
import { IJupyterServerUri, IJupyterUriProvider, JupyterServerUriHandle } from './types';

/**
* This class wraps an IJupyterUriProvider provided by another extension. It allows us to show
* extra data on the other extension's UI.
*/
export class JupyterUriProviderWrapper implements IJupyterUriProvider {
constructor(private readonly provider: IJupyterUriProvider, private packageName: string) {}
public get id() {
return this.provider.id;
}
public getQuickPickEntryItems(): vscode.QuickPickItem[] {
return this.provider.getQuickPickEntryItems().map((q) => {
return {
...q,
// Add the package name onto the description
description: localize.DataScience.uriProviderDescriptionFormat().format(
q.description || '',
this.packageName
),
original: q
};
});
}
public handleQuickPick(
item: vscode.QuickPickItem,
back: boolean
): Promise<JupyterServerUriHandle | 'back' | undefined> {
// tslint:disable-next-line: no-any
if ((item as any).original) {
// tslint:disable-next-line: no-any
return this.provider.handleQuickPick((item as any).original, back);
}
return this.provider.handleQuickPick(item, back);
}

public getServerUri(handle: JupyterServerUriHandle): Promise<IJupyterServerUri> {
return this.provider.getServerUri(handle);
}
}
Expand Up @@ -3,9 +3,10 @@
'use strict';

import { assert } from 'chai';
import { instance, mock, when } from 'ts-mockito';
import { anything, instance, mock, when } from 'ts-mockito';
import * as TypeMoq from 'typemoq';
import * as vscode from 'vscode';
import { FileSystem } from '../../client/common/platform/fileSystem';
import { JupyterUriProviderRegistration } from '../../client/datascience/jupyterUriProviderRegistration';
import { IJupyterServerUri, IJupyterUriProvider, JupyterServerUriHandle } from '../../client/datascience/types';
import { MockExtensions } from './mockExtensions';
Expand Down Expand Up @@ -47,6 +48,8 @@ suite('DataScience URI Picker', () => {
let registration: JupyterUriProviderRegistration | undefined;
const extensions = mock(MockExtensions);
const extensionList: vscode.Extension<any>[] = [];
const fileSystem = mock(FileSystem);
when(fileSystem.fileExists(anything())).thenResolve(false);
providerIds.forEach((id) => {
const extension = TypeMoq.Mock.ofType<vscode.Extension<any>>();
const packageJson = TypeMoq.Mock.ofType<any>();
Expand All @@ -64,7 +67,7 @@ suite('DataScience URI Picker', () => {
extensionList.push(extension.object);
});
when(extensions.all).thenReturn(extensionList);
registration = new JupyterUriProviderRegistration(instance(extensions));
registration = new JupyterUriProviderRegistration(instance(extensions), instance(fileSystem));
return registration;
}

Expand Down

0 comments on commit 8bb3ddf

Please sign in to comment.