Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## [v1.1.0]

### Added
- `copy-github-permalink.show`: Show Github's permalink in VS Code.

## [v1.0.2]

### Fixed
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ This extension by default will copy permalink to your `origin/master` because it
To activate, enter these commands in the Command Palette:

- `copy-github-permalink.copy`: Copy permalink of the selected line.
- `copy-github-permalink.show`: Show Github's permalink in VS Code.

## Extension Settings

Expand Down
12 changes: 8 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"displayName": "Copy Github Permalink",
"description": "Copy Github Permalink",
"icon": "img/ico.png",
"version": "1.0.2",
"version": "1.1.0",
"homepage": "https://github.com/tejanium/vscode-copy-github-permalink/blob/master/README.md",
"bugs": {
"url": "https://github.com/tejanium/vscode-copy-github-permalink/issues"
Expand All @@ -15,20 +15,24 @@
"url": "https://github.com/tejanium/vscode-copy-github-permalink"
},
"engines": {
"vscode": "^1.47.0"
"vscode": "^1.49.0"
},
"categories": [
"Other"
],
"activationEvents": [
"onCommand:copy-github-permalink.copy"
"onCommand:copy-github-permalink.copy",
"onCommand:copy-github-permalink.show"
],
"main": "./dist/extension",
"contributes": {
"commands": [
{
"command": "copy-github-permalink.copy",
"title": "Github Permalink: Copy"
}, {
"command": "copy-github-permalink.show",
"title": "Github Permalink: Show"
}
],
"configuration": {
Expand All @@ -46,7 +50,7 @@
"vscode:prepublish": "webpack --mode production",
"compile": "webpack --mode development",
"test-compile": "tsc -p ./",
"lint": "eslint src --ext ts",
"lint": "eslint src --ext ts --fix",
"watch": "webpack --mode development --watch --info-verbosity verbose",
"pretest": "yarn --frozen-lockfile && yarn compile && yarn test-compile && yarn lint",
"test": "node ./out/test/runTest.js",
Expand Down
22 changes: 20 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import * as vscode from 'vscode';
import { Document } from './model/document';
import { Permalink } from './model/permalink';

export function activate(context: vscode.ExtensionContext) {
let disposable = vscode.commands.registerCommand('copy-github-permalink.copy', async () => {
const copy = vscode.commands.registerCommand('copy-github-permalink.copy', async () => {
const editor = vscode.window.activeTextEditor;

if (editor && editor.document.uri.scheme === 'file') {
Expand All @@ -20,7 +21,24 @@ export function activate(context: vscode.ExtensionContext) {
}
});

context.subscriptions.push(disposable);
context.subscriptions.push(copy);

const show = vscode.commands.registerCommand('copy-github-permalink.show', async () => {
const input = await vscode.window.showInputBox();

if (input) {
const document = new Document(input);
const showOpts = { preview: false };

if (document.range) {
Object.assign(showOpts, { selection: document.range });
}

vscode.window.showTextDocument(await document.document(), showOpts);
}
});

context.subscriptions.push(show);
}

function getBranch(): string {
Expand Down
38 changes: 38 additions & 0 deletions src/model/document.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import * as vscode from 'vscode';

export class Document {
private path: string | undefined;
private position: string | undefined;

constructor(private permalink: string) {
[this.path, this.position] = this.relativePathWithLines.split('#') || [];
}

async document(): Promise<vscode.TextDocument> {
const root = vscode.workspace.workspaceFolders![0].uri.fsPath;

return await vscode.workspace.openTextDocument(`${root}/${this.path}`);
}

get range(): vscode.Range | undefined {
if (!this.position) {
return;
}

const [start, end] = this.position.split("-") || [];
const startPosition = this.getPosition(start, -1);
const endPosition = this.getPosition(end);

return new vscode.Range(startPosition, endPosition);
}

private get relativePathWithLines(): string {
return this.permalink.split('blob').pop()?.split('/').slice(2).join('/') || '';
}

private getPosition(raw: string | undefined, offset: number = 0) {
const safeRaw = raw?.replace(/\D/g, '') || '0';

return new vscode.Position(parseInt(safeRaw) + offset, 0);
}
}
69 changes: 61 additions & 8 deletions src/test/suite/extension.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,18 @@ function configureBranch(sandbox: sinon.SinonSandbox, branch: string): void {
} as vscode.WorkspaceConfiguration);
};

async function getFixtureFile(path: string): Promise<vscode.TextDocument> {
const root = vscode.workspace.workspaceFolders![0].uri.fsPath;

return await vscode.workspace.openTextDocument(`${root}/${path}`);
}

suite('Test commands', () => {
let sandbox: sinon.SinonSandbox;

before(async () => {
const root = vscode.workspace.workspaceFolders![0].uri.fsPath;
const document = await vscode.workspace.openTextDocument(`${root}/test/fixtures/file.txt`);
const document = await getFixtureFile('test/fixtures/file.txt');

await vscode.window.showTextDocument(document);
});

Expand All @@ -47,7 +53,7 @@ suite('Test commands', () => {

suite('copy-github-permalink.copy', () => {
test('Display copied information and put the link to clipboard', async () => {
const infoStub = sandbox.stub(vscode.window, 'showInformationMessage') as sinon.SinonStub<[string, any?], Thenable<string | undefined>>;
const infoStub = sandbox.stub(vscode.window, 'showInformationMessage') as unknown as sinon.SinonStub<[string], Thenable<string | undefined>>;

configureBranch(sandbox, 'HEAD');
mockGit();
Expand All @@ -59,7 +65,7 @@ suite('Test commands', () => {
});

test('Git remote is HTTP', async () => {
const infoStub = sandbox.stub(vscode.window, 'showInformationMessage') as sinon.SinonStub<[string, any?], Thenable<string | undefined>>;
const infoStub = sandbox.stub(vscode.window, 'showInformationMessage') as unknown as sinon.SinonStub<[string], Thenable<string | undefined>>;

configureBranch(sandbox, 'HEAD');
mockGit('http://github.com/owner/name.git');
Expand All @@ -71,7 +77,7 @@ suite('Test commands', () => {
});

test('Git remote is HTTPS', async () => {
const infoStub = sandbox.stub(vscode.window, 'showInformationMessage') as sinon.SinonStub<[string, any?], Thenable<string | undefined>>;
const infoStub = sandbox.stub(vscode.window, 'showInformationMessage') as unknown as sinon.SinonStub<[string], Thenable<string | undefined>>;

configureBranch(sandbox, 'HEAD');
mockGit('https://github.com/owner/name.git');
Expand All @@ -83,7 +89,7 @@ suite('Test commands', () => {
});

test('Display copied information and put the link of all lines to clipboard', async () => {
const infoStub = sandbox.stub(vscode.window, 'showInformationMessage') as sinon.SinonStub<[string, any?], Thenable<string | undefined>>;
const infoStub = sandbox.stub(vscode.window, 'showInformationMessage') as unknown as sinon.SinonStub<[string], Thenable<string | undefined>>;

configureBranch(sandbox, 'HEAD');
mockGit();
Expand All @@ -96,7 +102,7 @@ suite('Test commands', () => {
});

test('Select another branch', async () => {
const infoStub = sandbox.stub(vscode.window, 'showInformationMessage') as sinon.SinonStub<[string, any?], Thenable<string | undefined>>;
const infoStub = sandbox.stub(vscode.window, 'showInformationMessage') as unknown as sinon.SinonStub<[string], Thenable<string | undefined>>;

configureBranch(sandbox, 'origin/master');
mockGit();
Expand All @@ -108,7 +114,7 @@ suite('Test commands', () => {
});

test('Cannot get git information', async () => {
const warningStub = sandbox.stub(vscode.window, 'showWarningMessage') as sinon.SinonStub<[string, any?], Thenable<string | undefined>>;
const warningStub = sandbox.stub(vscode.window, 'showWarningMessage') as unknown as sinon.SinonStub<[string], Thenable<string | undefined>>;

configureBranch(sandbox, 'origin/master');
mockGit('');
Expand All @@ -118,4 +124,51 @@ suite('Test commands', () => {
sandbox.assert.calledWith(warningStub, 'Could not get Git info, please try a little later');
});
});

suite('copy-github-permalink.show', () => {
test('Show the file', async () => {
const showDocumentStub = sandbox.stub(vscode.window, 'showTextDocument') as unknown as sinon.SinonStub<[vscode.TextDocument, vscode.TextDocumentShowOptions]>;

sandbox.stub(vscode.window, 'showInputBox').resolves('https://github.com/owner/name/blob/sha1234567890/test/fixtures/file.txt#L1-L3');

await vscode.commands.executeCommand('copy-github-permalink.show');

const document = await getFixtureFile('test/fixtures/file.txt');
const range = new vscode.Range(new vscode.Position(0, 0), new vscode.Position(3, 0));
sandbox.assert.calledWith(showDocumentStub, document, { preview: false, selection: range });
});

test('Show the file even without sha', async () => {
const showDocumentStub = sandbox.stub(vscode.window, 'showTextDocument') as unknown as sinon.SinonStub<[vscode.TextDocument, vscode.TextDocumentShowOptions]>;

sandbox.stub(vscode.window, 'showInputBox').resolves('https://github.com/owner/name/blob/master/test/fixtures/file.txt');

await vscode.commands.executeCommand('copy-github-permalink.show');

const document = await getFixtureFile('test/fixtures/file.txt');
sandbox.assert.calledWith(showDocumentStub, document, { preview: false });
});

test('Show the file even without line', async () => {
const showDocumentStub = sandbox.stub(vscode.window, 'showTextDocument') as unknown as sinon.SinonStub<[vscode.TextDocument, vscode.TextDocumentShowOptions]>;

sandbox.stub(vscode.window, 'showInputBox').resolves('https://github.com/owner/name/blob/sha1234567890/test/fixtures/file.txt');

await vscode.commands.executeCommand('copy-github-permalink.show');

const document = await getFixtureFile('test/fixtures/file.txt');
sandbox.assert.calledWith(showDocumentStub, document, { preview: false });
});

test('Show the file even with missing line', async () => {
const showDocumentStub = sandbox.stub(vscode.window, 'showTextDocument') as unknown as sinon.SinonStub<[vscode.TextDocument, vscode.TextDocumentShowOptions]>;

sandbox.stub(vscode.window, 'showInputBox').resolves('https://github.com/owner/name/blob/sha1234567890/test/fixtures/file.txt#');

await vscode.commands.executeCommand('copy-github-permalink.show');

const document = await getFixtureFile('test/fixtures/file.txt');
sandbox.assert.calledWith(showDocumentStub, document, { preview: false });
});
});
});