Add a Share menu and a share vscode.dev command (#152765)

* Add a share menu
Fixes #146309

* Add vscod.dev command in github extension

* Make share menu proposed

* Add share submenu into editor context

* Add proposed to editor share menu
This commit is contained in:
Alex Ross
2022-06-27 09:56:36 +02:00
committed by GitHub
parent e71b6105eb
commit ffe53e8d71
13 changed files with 190 additions and 17 deletions

View File

@@ -7,6 +7,7 @@ import * as vscode from 'vscode';
import { API as GitAPI } from './typings/git';
import { publishRepository } from './publish';
import { DisposableStore } from './util';
import { getPermalink } from './links';
export function registerCommands(gitAPI: GitAPI): vscode.Disposable {
const disposables = new DisposableStore();
@@ -19,5 +20,16 @@ export function registerCommands(gitAPI: GitAPI): vscode.Disposable {
}
}));
disposables.add(vscode.commands.registerCommand('github.copyVscodeDevLink', async () => {
try {
const permalink = getPermalink(gitAPI, 'https://vscode.dev/github');
if (permalink) {
vscode.env.clipboard.writeText(permalink);
}
} catch (err) {
vscode.window.showErrorMessage(err.message);
}
}));
return disposables;
}

View File

@@ -5,10 +5,10 @@
import { commands, Disposable, ExtensionContext, extensions } from 'vscode';
import { GithubRemoteSourceProvider } from './remoteSourceProvider';
import { GitExtension } from './typings/git';
import { API, GitExtension } from './typings/git';
import { registerCommands } from './commands';
import { GithubCredentialProviderManager } from './credentialProvider';
import { DisposableStore } from './util';
import { DisposableStore, repositoryHasGitHubRemote } from './util';
import { GithubPushErrorHandler } from './pushErrorHandler';
import { GitBaseExtension } from './typings/git-base';
import { GithubRemoteSourcePublisher } from './remoteSourcePublisher';
@@ -48,6 +48,21 @@ function initializeGitBaseExtension(): Disposable {
return disposables;
}
function setGitHubContext(gitAPI: API, disposables: DisposableStore) {
if (gitAPI.repositories.find(repo => repositoryHasGitHubRemote(repo))) {
commands.executeCommand('setContext', 'github.hasGitHubRepo', true);
} else {
const openRepoDisposable = gitAPI.onDidOpenRepository(async e => {
await e.status();
if (repositoryHasGitHubRemote(e)) {
commands.executeCommand('setContext', 'github.hasGitHubRepo', true);
openRepoDisposable.dispose();
}
});
disposables.add(openRepoDisposable);
}
}
function initializeGitExtension(): Disposable {
const disposables = new DisposableStore();
@@ -64,6 +79,7 @@ function initializeGitExtension(): Disposable {
disposables.add(new GithubCredentialProviderManager(gitAPI));
disposables.add(gitAPI.registerPushErrorHandler(new GithubPushErrorHandler()));
disposables.add(gitAPI.registerRemoteSourcePublisher(new GithubRemoteSourcePublisher(gitAPI)));
setGitHubContext(gitAPI, disposables);
commands.executeCommand('setContext', 'git-base.gitEnabled', true);
} else {

View File

@@ -0,0 +1,78 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { API as GitAPI, Repository } from './typings/git';
import { getRepositoryFromUrl } from './util';
export function isFileInRepo(repository: Repository, file: vscode.Uri): boolean {
return file.path.toLowerCase() === repository.rootUri.path.toLowerCase() ||
(file.path.toLowerCase().startsWith(repository.rootUri.path.toLowerCase()) &&
file.path.substring(repository.rootUri.path.length).startsWith('/'));
}
export function getRepositoryForFile(gitAPI: GitAPI, file: vscode.Uri): Repository | undefined {
for (const repository of gitAPI.repositories) {
if (isFileInRepo(repository, file)) {
return repository;
}
}
return undefined;
}
function getFileAndPosition(): { uri: vscode.Uri | undefined; range: vscode.Range | undefined } {
let uri: vscode.Uri | undefined;
let range: vscode.Range | undefined;
if (vscode.window.activeTextEditor) {
uri = vscode.window.activeTextEditor.document.uri;
range = vscode.window.activeTextEditor.selection;
}
return { uri, range };
}
function rangeString(range: vscode.Range | undefined) {
if (!range) {
return '';
}
let hash = `#L${range.start.line + 1}`;
if (range.start.line !== range.end.line) {
hash += `-L${range.end.line + 1}`;
}
return hash;
}
export function getPermalink(gitAPI: GitAPI, hostPrefix?: string): string | undefined {
hostPrefix = hostPrefix ?? 'https://github.com';
const { uri, range } = getFileAndPosition();
if (!uri) {
return;
}
const gitRepo = getRepositoryForFile(gitAPI, uri);
if (!gitRepo) {
return;
}
let repo: { owner: string; repo: string } | undefined;
gitRepo.state.remotes.find(remote => {
if (remote.fetchUrl) {
const foundRepo = getRepositoryFromUrl(remote.fetchUrl);
if (foundRepo && (remote.name === gitRepo.state.HEAD?.upstream?.remote)) {
repo = foundRepo;
return;
} else if (foundRepo && !repo) {
repo = foundRepo;
}
}
return;
});
if (!repo) {
return;
}
const commitHash = gitRepo.state.HEAD?.commit;
const pathSegment = uri.path.substring(gitRepo.rootUri.path.length);
return `${hostPrefix}/${repo.owner}/${repo.repo}/blob/${commitHash
}${pathSegment}${rangeString(range)}`;
}

View File

@@ -7,17 +7,7 @@ import { workspace } from 'vscode';
import { RemoteSourceProvider, RemoteSource } from './typings/git-base';
import { getOctokit } from './auth';
import { Octokit } from '@octokit/rest';
function getRepositoryFromUrl(url: string): { owner: string; repo: string } | undefined {
const match = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\.git/i.exec(url)
|| /^git@github\.com:([^/]+)\/([^/]+)\.git/i.exec(url);
return match ? { owner: match[1], repo: match[2] } : undefined;
}
function getRepositoryFromQuery(query: string): { owner: string; repo: string } | undefined {
const match = /^([^/]+)\/([^/]+)$/i.exec(query);
return match ? { owner: match[1], repo: match[2] } : undefined;
}
import { getRepositoryFromQuery, getRepositoryFromUrl } from './util';
function asRemoteSource(raw: any): RemoteSource {
const protocol = workspace.getConfiguration('github').get<'https' | 'ssh'>('gitProtocol');

View File

@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { Repository } from './typings/git';
export class DisposableStore {
@@ -21,3 +22,18 @@ export class DisposableStore {
this.disposables.clear();
}
}
export function getRepositoryFromUrl(url: string): { owner: string; repo: string } | undefined {
const match = /^https:\/\/github\.com\/([^/]+)\/([^/]+?)(\.git)?$/i.exec(url)
|| /^git@github\.com:([^/]+)\/([^/]+?)(\.git)?$/i.exec(url);
return match ? { owner: match[1], repo: match[2] } : undefined;
}
export function getRepositoryFromQuery(query: string): { owner: string; repo: string } | undefined {
const match = /^([^/]+)\/([^/]+)$/i.exec(query);
return match ? { owner: match[1], repo: match[2] } : undefined;
}
export function repositoryHasGitHubRemote(repository: Repository) {
return !!repository.state.remotes.find(remote => remote.fetchUrl ? getRepositoryFromUrl(remote.fetchUrl) : undefined);
}