Skip to content
This repository has been archived by the owner on Aug 23, 2022. It is now read-only.

Commit

Permalink
feat(codelens): add Codelens to run TestFramework (#2)
Browse files Browse the repository at this point in the history
* feat(codelens): add Codelens to run TestFramework

* chore(doc): update
  • Loading branch information
yaegassy committed Aug 16, 2022
1 parent ad766c8 commit e36eb1f
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 2 deletions.
12 changes: 10 additions & 2 deletions README.md
@@ -1,6 +1,6 @@
# coc-pyright-tools

`coc-pyright-tools` is a coc-extension that adds its own functionality to [coc-pyright](https://github.com/fannheyward/coc-pyright) for [coc.nvim](https://github.com/neoclide/coc.nvim). Currently the **"Inlay Hints"**, Test Framework commands feature is available.
`coc-pyright-tools` is a coc-extension that adds its own functionality to [coc-pyright](https://github.com/fannheyward/coc-pyright) for [coc.nvim](https://github.com/neoclide/coc.nvim). Currently the "Inlay Hints", "CodeLens" and "Test Framework commands" feature is available.

## Install

Expand All @@ -26,10 +26,14 @@ Check about Pylance inlay type hints. See: <https://devblogs.microsoft.com/pytho

https://user-images.githubusercontent.com/188642/184496855-793312d6-1bfc-4332-a64d-a625c6bba738.mp4

### (TODO) CodeLens
### CodeLens

Running test such as `unittest`, `pytest`, etc.

**DEMO (mp4)**:

https://user-images.githubusercontent.com/188642/184796894-66b622bb-15e5-41f1-9562-42cd46ed7bc6.mp4

## Commands

- `pyright-tools.toggleInlayHints`: Toggle enable/disable inlay hints
Expand All @@ -45,6 +49,10 @@ Running test such as `unittest`, `pytest`, etc.
- `pyright-tools.inlayHints.enable`: Enable/disable inlay hints feature, `true`
- `pyright-tools.inlayHints.functionReturnTypes`: Enable/disable inlay hints for function return types, default: `true`
- `pyright-tools.inlayHints.variableTypes`: Enable/disable inlay hints for variable types, default: `true`
- `pyright-tools.codeLens.enable`: Enable/disable codelens feature, default: `true`
- `pyright-tools.codeLens.testFramework`: Testing framework used by codelens, valid options `["unittest", "pytest"]`, default: `unittest`
- `pyright-tools.codeLens.unittestTitle`: CodeLens title. Can be changed to any display, default: `>> [Run unittest]`
- `pyright-tools.codeLens.pytestTitle`: CodeLens title. Can be changed to any display, default: `>> [Run pytest]`
- `pyright-tools.test.unittestArgs`: Arguments passed in. Each argument is a separate item in the array, default: `[]`
- `pyright-tools.test.pytestArgs`: Arguments passed in. Each argument is a separate item in the array, default: `[]`
- `pyright-tools.terminal.enableSplitRight`: Use vertical belowright for unittest/pytest terminal window, default: `false`
Expand Down
24 changes: 24 additions & 0 deletions package.json
Expand Up @@ -79,6 +79,30 @@
"default": true,
"description": "Enable/disable inlay hints for variable types"
},
"pyright-tools.codeLens.enable": {
"type": "boolean",
"default": true,
"description": "Enable/disable codelens feature"
},
"pyright-tools.codeLens.testFramework": {
"type": "string",
"default": "unittest",
"description": "Testing framework used by codelens",
"enum": [
"unittest",
"pytest"
]
},
"pyright-tools.codeLens.unittestTitle": {
"type": "string",
"default": ">> [Run unittest]",
"description": "CodeLens title. Can be changed to any display."
},
"pyright-tools.codeLens.pytestTitle": {
"type": "string",
"default": ">> [Run pytest]",
"description": "CodeLens title. Can be changed to any display."
},
"pyright-tools.test.unittestArgs": {
"type": "array",
"description": "Arguments passed in. Each argument is a separate item in the array.",
Expand Down
113 changes: 113 additions & 0 deletions src/features/codeLens.ts
@@ -0,0 +1,113 @@
import {
CancellationToken,
CodeLens,
CodeLensProvider,
events,
ExtensionContext,
languages,
LinesTextDocument,
Position,
Range,
Uri,
workspace,
} from 'coc.nvim';

import path from 'path';

import * as testFrameworkParser from '../parsers/testFramework';

export function register(context: ExtensionContext) {
if (workspace.getConfiguration('pyright-tools').get<boolean>('codeLens.enable', true)) {
context.subscriptions.push(
languages.registerCodeLensProvider([{ language: 'python', scheme: 'file' }], new TestFrameworkCodeLensProvider())
);
}
}

export class TestFrameworkCodeLensProvider implements CodeLensProvider {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async provideCodeLenses(document: LinesTextDocument, token: CancellationToken) {
const fileName = path.basename(Uri.parse(document.uri).fsPath);
if (document.languageId !== 'python' || (!fileName.startsWith('test_') && !fileName.endsWith('_test.py'))) {
return [];
}

const codeLenses: CodeLens[] = [];
const codeLensTitle = this.getCodeLensTitle();

// do not process codelens when in insert mode
if (events.insertMode) return codeLenses;

try {
const parsed = testFrameworkParser.parse(document.getText());
if (!parsed) return;
const walker = this.initWalker();
walker.walk(parsed.parseTree);

for (const item of walker.featureItems) {
if (item.startOffset && item.endOffset) {
const itemStartPostion = document.positionAt(item.startOffset);
const itemEndPostion = document.positionAt(item.endOffset);
const codeLensCommandId = this.getCodeLensCommand();

const lens: CodeLens = {
range: Range.create(
Position.create(itemStartPostion.line, itemStartPostion.character),
Position.create(itemEndPostion.line, itemEndPostion.character)
),
command: {
title: codeLensTitle,
command: codeLensCommandId,
},
};

codeLenses.push(lens);
}
}
} catch (e) {
// noop
}

// For some reason, the virtual text does not disappear even when the
// number of code lens goes from 1 to 0.
//
// It may be a bug in coc.nvim itself, but it sends code lens with Range
// of 0 and forces a refresh.
if (codeLenses.length === 0) {
codeLenses.push({
range: Range.create(Position.create(0, 0), Position.create(0, 0)),
});
}

return codeLenses;
}

private initWalker() {
const codeLensTestFramework = workspace
.getConfiguration('pyright-tools')
.get<string>('codeLens.testFramework', 'unittest');
const testFramework = codeLensTestFramework as testFrameworkParser.TestFramework;

return new testFrameworkParser.TestFrameworkWalker(testFramework);
}

private getCodeLensTitle() {
const testFramework = workspace.getConfiguration('pyright-tools').get<string>('codeLens.testFramework', 'unittest');

if (testFramework === 'pytest') {
return workspace.getConfiguration('pyright-tools').get('codeLens.pytestTitle', '>> [RUN pytest]');
} else {
return workspace.getConfiguration('pyright-tools').get('codeLens.unittestTitle', '>> [RUN unittest]');
}
}

private getCodeLensCommand() {
const testFramework = workspace.getConfiguration('pyright-tools').get<string>('codeLens.testFramework', 'unittest');

if (testFramework === 'pytest') {
return 'pyright-tools.pytest.singleTest';
} else {
return 'pyright-tools.unittest.singleTest';
}
}
}
2 changes: 2 additions & 0 deletions src/index.ts
Expand Up @@ -3,12 +3,14 @@ import { ExtensionContext, LanguageClient, services, workspace } from 'coc.nvim'
import * as typeInlayHintsFeature from './features/inlayHints';
import * as pytestCommandFeature from './commands/pytest';
import * as unittestCommandFeature from './commands/unittest';
import * as testFrameworkCodeLensFeature from './features/codeLens';

export async function activate(context: ExtensionContext): Promise<void> {
if (!workspace.getConfiguration('pyright-tools').get<boolean>('enable', true)) return;

unittestCommandFeature.register(context);
pytestCommandFeature.register(context);
testFrameworkCodeLensFeature.register(context);

const service = services.getService('pyright');
if (service) {
Expand Down

0 comments on commit e36eb1f

Please sign in to comment.