From d18b8c4da5fb2118bde6954cf463ecaf9bb026dc Mon Sep 17 00:00:00 2001 From: LouisGobert Date: Tue, 23 Apr 2024 21:05:52 +0200 Subject: [PATCH 1/9] Show PyPi link in requirements --- .../requirementsTxtLinkActivator.ts | 24 +++++++++++++++++++ src/client/activation/serviceRegistry.ts | 6 +++++ .../activation/serviceRegistry.unit.test.ts | 7 ++++++ 3 files changed, 37 insertions(+) create mode 100644 src/client/activation/requirementsTxtLinkActivator.ts diff --git a/src/client/activation/requirementsTxtLinkActivator.ts b/src/client/activation/requirementsTxtLinkActivator.ts new file mode 100644 index 000000000000..d4bad3c06668 --- /dev/null +++ b/src/client/activation/requirementsTxtLinkActivator.ts @@ -0,0 +1,24 @@ +import { injectable } from 'inversify'; +import { Hover, languages, TextDocument, Position } from 'vscode'; +import { IExtensionSingleActivationService } from './types'; + +@injectable() +export class RequirementsTxtLinkActivator implements IExtensionSingleActivationService { + // eslint-disable-next-line class-methods-use-this + public readonly supportedWorkspaceTypes = { untrustedWorkspace: true, virtualWorkspace: true }; + public async activate(): Promise { + languages.registerHoverProvider( + [{ pattern: '**/*requirement*.txt' },{ pattern: '**/requirements/*.txt' }], + { + provideHover(document: TextDocument, position: Position) { + // Regex to allow to find every possible pypi package (base regex from https://peps.python.org/pep-0508/#names) + const regex = /^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*)($|=| |;|\[)/i + const row = document.lineAt(position.line).text; + const projectName = row.match(regex); + return(projectName) ? new Hover(`https://pypi.org/project/${projectName[1]}/`) : null + }, + }, + ); + } +} + diff --git a/src/client/activation/serviceRegistry.ts b/src/client/activation/serviceRegistry.ts index a52a6555236d..875afa12f0b4 100644 --- a/src/client/activation/serviceRegistry.ts +++ b/src/client/activation/serviceRegistry.ts @@ -15,6 +15,7 @@ import { LoadLanguageServerExtension } from './common/loadLanguageServerExtensio import { PartialModeStatusItem } from './partialModeStatus'; import { ILanguageServerWatcher } from '../languageServer/types'; import { LanguageServerWatcher } from '../languageServer/watcher'; +import { RequirementsTxtLinkActivator } from './requirementsTxtLinkActivator'; export function registerTypes(serviceManager: IServiceManager): void { serviceManager.addSingleton(IExtensionActivationService, PartialModeStatusItem); @@ -34,4 +35,9 @@ export function registerTypes(serviceManager: IServiceManager): void { serviceManager.addSingleton(ILanguageServerWatcher, LanguageServerWatcher); serviceManager.addBinding(ILanguageServerWatcher, IExtensionActivationService); + + serviceManager.addSingleton( + IExtensionSingleActivationService, + RequirementsTxtLinkActivator, + ); } diff --git a/src/test/activation/serviceRegistry.unit.test.ts b/src/test/activation/serviceRegistry.unit.test.ts index cf715b90ecfe..177eae810810 100644 --- a/src/test/activation/serviceRegistry.unit.test.ts +++ b/src/test/activation/serviceRegistry.unit.test.ts @@ -14,6 +14,7 @@ import { import { ServiceManager } from '../../client/ioc/serviceManager'; import { IServiceManager } from '../../client/ioc/types'; import { LoadLanguageServerExtension } from '../../client/activation/common/loadLanguageServerExtension'; +import { RequirementsTxtLinkActivator } from '../../client/activation/requirementsTxtLinkActivator'; suite('Unit Tests - Language Server Activation Service Registry', () => { let serviceManager: IServiceManager; @@ -46,5 +47,11 @@ suite('Unit Tests - Language Server Activation Service Registry', () => { LoadLanguageServerExtension, ), ).once(); + verify( + serviceManager.addSingleton( + IExtensionSingleActivationService, + RequirementsTxtLinkActivator, + ), + ).once(); }); }); From 6fbde074d6ced47fbc435e716febaad9af327420 Mon Sep 17 00:00:00 2001 From: LouisGobert Date: Wed, 24 Apr 2024 17:01:16 +0200 Subject: [PATCH 2/9] Add repo regex test --- .../requirementsTxtLinkActivator.ts | 25 +++++------ .../requirementsTxtLinkActivator.unit.test.ts | 43 +++++++++++++++++++ 2 files changed, 56 insertions(+), 12 deletions(-) create mode 100644 src/test/activation/requirementsTxtLinkActivator.unit.test.ts diff --git a/src/client/activation/requirementsTxtLinkActivator.ts b/src/client/activation/requirementsTxtLinkActivator.ts index d4bad3c06668..3f4e55588119 100644 --- a/src/client/activation/requirementsTxtLinkActivator.ts +++ b/src/client/activation/requirementsTxtLinkActivator.ts @@ -2,23 +2,24 @@ import { injectable } from 'inversify'; import { Hover, languages, TextDocument, Position } from 'vscode'; import { IExtensionSingleActivationService } from './types'; +const PYPI_PROJECT_URL = 'https://pypi.org/project'; @injectable() export class RequirementsTxtLinkActivator implements IExtensionSingleActivationService { // eslint-disable-next-line class-methods-use-this public readonly supportedWorkspaceTypes = { untrustedWorkspace: true, virtualWorkspace: true }; + public async activate(): Promise { - languages.registerHoverProvider( - [{ pattern: '**/*requirement*.txt' },{ pattern: '**/requirements/*.txt' }], - { - provideHover(document: TextDocument, position: Position) { - // Regex to allow to find every possible pypi package (base regex from https://peps.python.org/pep-0508/#names) - const regex = /^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*)($|=| |;|\[)/i - const row = document.lineAt(position.line).text; - const projectName = row.match(regex); - return(projectName) ? new Hover(`https://pypi.org/project/${projectName[1]}/`) : null - }, + languages.registerHoverProvider([{ pattern: '**/*requirement*.txt' }, { pattern: '**/requirements/*.txt' }], { + provideHover(document: TextDocument, position: Position) { + const link = RequirementsTxtLinkActivator.generatePyPiLink(document.lineAt(position.line).text); + return link ? new Hover(link) : null; }, - ); + }); } -} + public static generatePyPiLink(name: string): string | null { + // Regex to allow to find every possible pypi package (base regex from https://peps.python.org/pep-0508/#names) + const projectName = name.match(/^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*)($|=| |;|\[)/i); + return projectName ? `${PYPI_PROJECT_URL}/${projectName[1]}/` : null; + } +} diff --git a/src/test/activation/requirementsTxtLinkActivator.unit.test.ts b/src/test/activation/requirementsTxtLinkActivator.unit.test.ts new file mode 100644 index 000000000000..c1031cc34a63 --- /dev/null +++ b/src/test/activation/requirementsTxtLinkActivator.unit.test.ts @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { expect } from 'chai'; +import { RequirementsTxtLinkActivator } from '../../client/activation/requirementsTxtLinkActivator'; + +/** * +requests [security] >= 2.8.1, == 2.8.* ; python_version < "2.7" +urllib3 @ https://github.com/urllib3/urllib3/archive/refs/tags/1.26.8.zip + +# It is possible to refer to other requirement files or constraints files. +-r other-requirements.txt +-c constraints.txt + +# It is possible to refer to specific local distribution paths. +./downloads/numpy-1.9.2-cp34-none-win32.whl + +# It is possible to refer to URLs. +http://wxpython.org/Phoenix/snapshot-builds/wxPython_Phoenix-3.0.3.dev1820+49a8884-cp34-none-win_amd64.whl + + */ + +suite('Link to PyPi in requiements test', () => { + test('Check if all possible project name are matched', () => { + const rows = [ + ['pytest', 'pytest'], + ['pytest-cov', 'pytest-cov'], + ['pytest_cov', 'pytest_cov'], + ['pytest_cov[an_extra]', 'pytest_cov'], + ['pytest == 0.6.1', 'pytest'], + ['pytest== 0.6.1', 'pytest'], + ['requests [security] >= 2.8.1, == 2.8.* ; python_version < "2.7"', 'requests'], + ['# a comment', null], + ['', null], + ]; + + rows.forEach(([input, expected]) => { + expect(RequirementsTxtLinkActivator.generatePyPiLink(input)).equal(expected ? `https://pypi.org/project/${expected}/`: null); + }); + }); +}); From 1767fd5b6862be819be82cbe513408e358fd731a Mon Sep 17 00:00:00 2001 From: LouisGobert <45512639+LouisGobert@users.noreply.github.com> Date: Wed, 24 Apr 2024 20:05:20 +0200 Subject: [PATCH 3/9] Update src/test/activation/requirementsTxtLinkActivator.unit.test.ts Co-authored-by: Karthik Nadig --- src/test/activation/requirementsTxtLinkActivator.unit.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/activation/requirementsTxtLinkActivator.unit.test.ts b/src/test/activation/requirementsTxtLinkActivator.unit.test.ts index c1031cc34a63..8f1bcc394fc3 100644 --- a/src/test/activation/requirementsTxtLinkActivator.unit.test.ts +++ b/src/test/activation/requirementsTxtLinkActivator.unit.test.ts @@ -4,7 +4,7 @@ 'use strict'; import { expect } from 'chai'; -import { RequirementsTxtLinkActivator } from '../../client/activation/requirementsTxtLinkActivator'; +import { generatePyPiLink } from '../../client/activation/requirementsTxtLinkActivator'; /** * requests [security] >= 2.8.1, == 2.8.* ; python_version < "2.7" From da2a9ce14abc2ff5e465f6a8b49f0c7cc09a9eb9 Mon Sep 17 00:00:00 2001 From: LouisGobert <45512639+LouisGobert@users.noreply.github.com> Date: Wed, 24 Apr 2024 20:05:33 +0200 Subject: [PATCH 4/9] Update src/test/activation/requirementsTxtLinkActivator.unit.test.ts Co-authored-by: Karthik Nadig --- .../requirementsTxtLinkActivator.unit.test.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/test/activation/requirementsTxtLinkActivator.unit.test.ts b/src/test/activation/requirementsTxtLinkActivator.unit.test.ts index 8f1bcc394fc3..898e391f41c6 100644 --- a/src/test/activation/requirementsTxtLinkActivator.unit.test.ts +++ b/src/test/activation/requirementsTxtLinkActivator.unit.test.ts @@ -23,8 +23,7 @@ http://wxpython.org/Phoenix/snapshot-builds/wxPython_Phoenix-3.0.3.dev1820+49a88 */ suite('Link to PyPi in requiements test', () => { - test('Check if all possible project name are matched', () => { - const rows = [ + [ ['pytest', 'pytest'], ['pytest-cov', 'pytest-cov'], ['pytest_cov', 'pytest_cov'], @@ -34,10 +33,9 @@ suite('Link to PyPi in requiements test', () => { ['requests [security] >= 2.8.1, == 2.8.* ; python_version < "2.7"', 'requests'], ['# a comment', null], ['', null], - ]; - - rows.forEach(([input, expected]) => { - expect(RequirementsTxtLinkActivator.generatePyPiLink(input)).equal(expected ? `https://pypi.org/project/${expected}/`: null); + ].forEach(([input, expected])=> { + test(`PyPI link case: "${input}"`, () => { + expect(generatePyPiLink(input)).equal(expected ? `https://pypi.org/project/${expected}/`: null); }); }); }); From 7e1cddd24a68906f616acde60d626dcda2f38f3e Mon Sep 17 00:00:00 2001 From: LouisGobert <45512639+LouisGobert@users.noreply.github.com> Date: Wed, 24 Apr 2024 20:05:40 +0200 Subject: [PATCH 5/9] Update src/client/activation/requirementsTxtLinkActivator.ts Co-authored-by: Karthik Nadig --- .../activation/requirementsTxtLinkActivator.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/client/activation/requirementsTxtLinkActivator.ts b/src/client/activation/requirementsTxtLinkActivator.ts index 3f4e55588119..90199194b28d 100644 --- a/src/client/activation/requirementsTxtLinkActivator.ts +++ b/src/client/activation/requirementsTxtLinkActivator.ts @@ -3,6 +3,13 @@ import { Hover, languages, TextDocument, Position } from 'vscode'; import { IExtensionSingleActivationService } from './types'; const PYPI_PROJECT_URL = 'https://pypi.org/project'; + +export function generatePyPiLink(name: string): string | null { + // Regex to allow to find every possible pypi package (base regex from https://peps.python.org/pep-0508/#names) + const projectName = name.match(/^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*)($|=| |;|\[)/i); + return projectName ? `${PYPI_PROJECT_URL}/${projectName[1]}/` : null; +} + @injectable() export class RequirementsTxtLinkActivator implements IExtensionSingleActivationService { // eslint-disable-next-line class-methods-use-this @@ -11,15 +18,9 @@ export class RequirementsTxtLinkActivator implements IExtensionSingleActivationS public async activate(): Promise { languages.registerHoverProvider([{ pattern: '**/*requirement*.txt' }, { pattern: '**/requirements/*.txt' }], { provideHover(document: TextDocument, position: Position) { - const link = RequirementsTxtLinkActivator.generatePyPiLink(document.lineAt(position.line).text); + const link = generatePyPiLink(document.lineAt(position.line).text); return link ? new Hover(link) : null; }, }); } - - public static generatePyPiLink(name: string): string | null { - // Regex to allow to find every possible pypi package (base regex from https://peps.python.org/pep-0508/#names) - const projectName = name.match(/^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*)($|=| |;|\[)/i); - return projectName ? `${PYPI_PROJECT_URL}/${projectName[1]}/` : null; - } } From b57c17cd2fe01c992d09fb2ba0125d614e560d7e Mon Sep 17 00:00:00 2001 From: LouisGobert <45512639+LouisGobert@users.noreply.github.com> Date: Wed, 24 Apr 2024 22:27:54 +0200 Subject: [PATCH 6/9] Update src/client/activation/requirementsTxtLinkActivator.ts Co-authored-by: Karthik Nadig --- src/client/activation/requirementsTxtLinkActivator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/activation/requirementsTxtLinkActivator.ts b/src/client/activation/requirementsTxtLinkActivator.ts index 90199194b28d..fbb4383a6125 100644 --- a/src/client/activation/requirementsTxtLinkActivator.ts +++ b/src/client/activation/requirementsTxtLinkActivator.ts @@ -4,7 +4,7 @@ import { IExtensionSingleActivationService } from './types'; const PYPI_PROJECT_URL = 'https://pypi.org/project'; -export function generatePyPiLink(name: string): string | null { +export function generatePyPiLink(name: string): string | undefined { // Regex to allow to find every possible pypi package (base regex from https://peps.python.org/pep-0508/#names) const projectName = name.match(/^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*)($|=| |;|\[)/i); return projectName ? `${PYPI_PROJECT_URL}/${projectName[1]}/` : null; From 08ae13c271d9693f570d9ed393a6e4c2bd0363c6 Mon Sep 17 00:00:00 2001 From: LouisGobert Date: Wed, 24 Apr 2024 22:46:19 +0200 Subject: [PATCH 7/9] Fix type issue --- .../requirementsTxtLinkActivator.ts | 4 +- .../requirementsTxtLinkActivator.unit.test.ts | 38 ++++++------------- 2 files changed, 13 insertions(+), 29 deletions(-) diff --git a/src/client/activation/requirementsTxtLinkActivator.ts b/src/client/activation/requirementsTxtLinkActivator.ts index fbb4383a6125..0bf6130811cd 100644 --- a/src/client/activation/requirementsTxtLinkActivator.ts +++ b/src/client/activation/requirementsTxtLinkActivator.ts @@ -4,12 +4,12 @@ import { IExtensionSingleActivationService } from './types'; const PYPI_PROJECT_URL = 'https://pypi.org/project'; -export function generatePyPiLink(name: string): string | undefined { +export function generatePyPiLink(name: string): string | null { // Regex to allow to find every possible pypi package (base regex from https://peps.python.org/pep-0508/#names) const projectName = name.match(/^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*)($|=| |;|\[)/i); return projectName ? `${PYPI_PROJECT_URL}/${projectName[1]}/` : null; } - + @injectable() export class RequirementsTxtLinkActivator implements IExtensionSingleActivationService { // eslint-disable-next-line class-methods-use-this diff --git a/src/test/activation/requirementsTxtLinkActivator.unit.test.ts b/src/test/activation/requirementsTxtLinkActivator.unit.test.ts index 898e391f41c6..ebea4af29182 100644 --- a/src/test/activation/requirementsTxtLinkActivator.unit.test.ts +++ b/src/test/activation/requirementsTxtLinkActivator.unit.test.ts @@ -6,36 +6,20 @@ import { expect } from 'chai'; import { generatePyPiLink } from '../../client/activation/requirementsTxtLinkActivator'; -/** * -requests [security] >= 2.8.1, == 2.8.* ; python_version < "2.7" -urllib3 @ https://github.com/urllib3/urllib3/archive/refs/tags/1.26.8.zip - -# It is possible to refer to other requirement files or constraints files. --r other-requirements.txt --c constraints.txt - -# It is possible to refer to specific local distribution paths. -./downloads/numpy-1.9.2-cp34-none-win32.whl - -# It is possible to refer to URLs. -http://wxpython.org/Phoenix/snapshot-builds/wxPython_Phoenix-3.0.3.dev1820+49a8884-cp34-none-win_amd64.whl - - */ - suite('Link to PyPi in requiements test', () => { [ - ['pytest', 'pytest'], - ['pytest-cov', 'pytest-cov'], - ['pytest_cov', 'pytest_cov'], - ['pytest_cov[an_extra]', 'pytest_cov'], - ['pytest == 0.6.1', 'pytest'], - ['pytest== 0.6.1', 'pytest'], - ['requests [security] >= 2.8.1, == 2.8.* ; python_version < "2.7"', 'requests'], - ['# a comment', null], - ['', null], - ].forEach(([input, expected])=> { + ['pytest', 'pytest'], + ['pytest-cov', 'pytest-cov'], + ['pytest_cov', 'pytest_cov'], + ['pytest_cov[an_extra]', 'pytest_cov'], + ['pytest == 0.6.1', 'pytest'], + ['pytest== 0.6.1', 'pytest'], + ['requests [security] >= 2.8.1, == 2.8.* ; python_version < "2.7"', 'requests'], + ['# a comment', null], + ['', null], + ].forEach(([input, expected]) => { test(`PyPI link case: "${input}"`, () => { - expect(generatePyPiLink(input)).equal(expected ? `https://pypi.org/project/${expected}/`: null); + expect(generatePyPiLink(input!)).equal(expected ? `https://pypi.org/project/${expected}/` : null); }); }); }); From eda93746cf8fa53d7672fc65bf361c081bf6b88e Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Mon, 6 May 2024 13:04:25 -0700 Subject: [PATCH 8/9] Fix linting --- src/client/activation/requirementsTxtLinkActivator.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/client/activation/requirementsTxtLinkActivator.ts b/src/client/activation/requirementsTxtLinkActivator.ts index 0bf6130811cd..c6a483fee6b9 100644 --- a/src/client/activation/requirementsTxtLinkActivator.ts +++ b/src/client/activation/requirementsTxtLinkActivator.ts @@ -12,9 +12,10 @@ export function generatePyPiLink(name: string): string | null { @injectable() export class RequirementsTxtLinkActivator implements IExtensionSingleActivationService { - // eslint-disable-next-line class-methods-use-this + public readonly supportedWorkspaceTypes = { untrustedWorkspace: true, virtualWorkspace: true }; + // eslint-disable-next-line class-methods-use-this public async activate(): Promise { languages.registerHoverProvider([{ pattern: '**/*requirement*.txt' }, { pattern: '**/requirements/*.txt' }], { provideHover(document: TextDocument, position: Position) { From 900a0a5464a8819c77479c932db237bcd6ed786a Mon Sep 17 00:00:00 2001 From: LouisGobert Date: Mon, 6 May 2024 22:13:29 +0200 Subject: [PATCH 9/9] Run prettier on requirementsTxtLinkActivator.ts --- src/client/activation/requirementsTxtLinkActivator.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/client/activation/requirementsTxtLinkActivator.ts b/src/client/activation/requirementsTxtLinkActivator.ts index c6a483fee6b9..fcb6b72e545e 100644 --- a/src/client/activation/requirementsTxtLinkActivator.ts +++ b/src/client/activation/requirementsTxtLinkActivator.ts @@ -12,7 +12,6 @@ export function generatePyPiLink(name: string): string | null { @injectable() export class RequirementsTxtLinkActivator implements IExtensionSingleActivationService { - public readonly supportedWorkspaceTypes = { untrustedWorkspace: true, virtualWorkspace: true }; // eslint-disable-next-line class-methods-use-this