Skip to content

Commit 569ca65

Browse files
Show PyPi link in requirements (#23288)
Show PyPi link in requirements, see #14495. Apart from the test to verify the register of `RequirementsTxtLinkActivator`, there are no other tests. This is my first contribution in TypeScript, if you have an example of a test that could be done, I'm interested :) --------- Co-authored-by: Karthik Nadig <[email protected]>
1 parent 8ead7e2 commit 569ca65

File tree

4 files changed

+64
-0
lines changed

4 files changed

+64
-0
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { injectable } from 'inversify';
2+
import { Hover, languages, TextDocument, Position } from 'vscode';
3+
import { IExtensionSingleActivationService } from './types';
4+
5+
const PYPI_PROJECT_URL = 'https://pypi.org/project';
6+
7+
export function generatePyPiLink(name: string): string | null {
8+
// Regex to allow to find every possible pypi package (base regex from https://peps.python.org/pep-0508/#names)
9+
const projectName = name.match(/^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*)($|=| |;|\[)/i);
10+
return projectName ? `${PYPI_PROJECT_URL}/${projectName[1]}/` : null;
11+
}
12+
13+
@injectable()
14+
export class RequirementsTxtLinkActivator implements IExtensionSingleActivationService {
15+
public readonly supportedWorkspaceTypes = { untrustedWorkspace: true, virtualWorkspace: true };
16+
17+
// eslint-disable-next-line class-methods-use-this
18+
public async activate(): Promise<void> {
19+
languages.registerHoverProvider([{ pattern: '**/*requirement*.txt' }, { pattern: '**/requirements/*.txt' }], {
20+
provideHover(document: TextDocument, position: Position) {
21+
const link = generatePyPiLink(document.lineAt(position.line).text);
22+
return link ? new Hover(link) : null;
23+
},
24+
});
25+
}
26+
}

src/client/activation/serviceRegistry.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { LoadLanguageServerExtension } from './common/loadLanguageServerExtensio
1515
import { PartialModeStatusItem } from './partialModeStatus';
1616
import { ILanguageServerWatcher } from '../languageServer/types';
1717
import { LanguageServerWatcher } from '../languageServer/watcher';
18+
import { RequirementsTxtLinkActivator } from './requirementsTxtLinkActivator';
1819

1920
export function registerTypes(serviceManager: IServiceManager): void {
2021
serviceManager.addSingleton<IExtensionActivationService>(IExtensionActivationService, PartialModeStatusItem);
@@ -34,4 +35,9 @@ export function registerTypes(serviceManager: IServiceManager): void {
3435

3536
serviceManager.addSingleton<ILanguageServerWatcher>(ILanguageServerWatcher, LanguageServerWatcher);
3637
serviceManager.addBinding(ILanguageServerWatcher, IExtensionActivationService);
38+
39+
serviceManager.addSingleton<IExtensionSingleActivationService>(
40+
IExtensionSingleActivationService,
41+
RequirementsTxtLinkActivator,
42+
);
3743
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
'use strict';
5+
6+
import { expect } from 'chai';
7+
import { generatePyPiLink } from '../../client/activation/requirementsTxtLinkActivator';
8+
9+
suite('Link to PyPi in requiements test', () => {
10+
[
11+
['pytest', 'pytest'],
12+
['pytest-cov', 'pytest-cov'],
13+
['pytest_cov', 'pytest_cov'],
14+
['pytest_cov[an_extra]', 'pytest_cov'],
15+
['pytest == 0.6.1', 'pytest'],
16+
['pytest== 0.6.1', 'pytest'],
17+
['requests [security] >= 2.8.1, == 2.8.* ; python_version < "2.7"', 'requests'],
18+
['# a comment', null],
19+
['', null],
20+
].forEach(([input, expected]) => {
21+
test(`PyPI link case: "${input}"`, () => {
22+
expect(generatePyPiLink(input!)).equal(expected ? `https://pypi.org/project/${expected}/` : null);
23+
});
24+
});
25+
});

src/test/activation/serviceRegistry.unit.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
import { ServiceManager } from '../../client/ioc/serviceManager';
1515
import { IServiceManager } from '../../client/ioc/types';
1616
import { LoadLanguageServerExtension } from '../../client/activation/common/loadLanguageServerExtension';
17+
import { RequirementsTxtLinkActivator } from '../../client/activation/requirementsTxtLinkActivator';
1718

1819
suite('Unit Tests - Language Server Activation Service Registry', () => {
1920
let serviceManager: IServiceManager;
@@ -46,5 +47,11 @@ suite('Unit Tests - Language Server Activation Service Registry', () => {
4647
LoadLanguageServerExtension,
4748
),
4849
).once();
50+
verify(
51+
serviceManager.addSingleton<IExtensionSingleActivationService>(
52+
IExtensionSingleActivationService,
53+
RequirementsTxtLinkActivator,
54+
),
55+
).once();
4956
});
5057
});

0 commit comments

Comments
 (0)