From b36aff808d555be9059ab2dac6cb26f2ab32d8e4 Mon Sep 17 00:00:00 2001 From: netcon Date: Tue, 11 Aug 2026 17:54:33 +0800 Subject: [PATCH 1/2] feat: use resourceLabelFormatters (#714) * feat: use resourceLabelFormatters * feat: use absolute paths uniformly * feat: fit uri state * feat: standardize uri authorization * feat: avoid unnecessary readfile requests --- extensions/github1s/package.json | 29 +++++- .../src/adapters/bitbucket1s/parse-path.ts | 11 ++- .../src/adapters/bitbucket1s/router-parser.ts | 4 +- .../src/adapters/github1s/data-source.ts | 34 ++++--- .../src/adapters/github1s/parse-path.ts | 2 +- .../src/adapters/github1s/router-parser.ts | 4 +- .../src/adapters/gitlab1s/data-source.ts | 33 ++++--- .../src/adapters/gitlab1s/parse-path.ts | 2 +- .../src/adapters/gitlab1s/router-parser.ts | 4 +- .../src/adapters/npmjs1s/data-source.ts | 12 +-- .../src/adapters/npmjs1s/router-parser.ts | 4 +- .../src/adapters/ossinsight/data-source.ts | 4 +- .../src/adapters/ossinsight/router-parser.ts | 3 +- .../src/adapters/sourcegraph/data-source.ts | 57 ++++++----- .../github1s/src/adapters/sourcegraph/file.ts | 6 +- extensions/github1s/src/adapters/types.ts | 5 +- extensions/github1s/src/changes/files.ts | 51 ++++------ extensions/github1s/src/changes/index.ts | 6 +- extensions/github1s/src/changes/quick-diff.ts | 53 +++++----- extensions/github1s/src/commands/blame.ts | 10 +- .../github1s/src/commands/code-review.ts | 10 +- extensions/github1s/src/commands/commit.ts | 20 ++-- extensions/github1s/src/commands/editor.ts | 51 ++++------ extensions/github1s/src/commands/global.ts | 6 +- extensions/github1s/src/commands/ref.ts | 4 +- extensions/github1s/src/extension.ts | 17 +--- extensions/github1s/src/helpers/submodule.ts | 18 ++-- extensions/github1s/src/helpers/util.ts | 16 +-- .../github1s/src/listeners/router/explorer.ts | 2 +- extensions/github1s/src/listeners/vscode.ts | 14 +-- extensions/github1s/src/messages.ts | 2 +- .../src/providers/decorations/changed-file.ts | 27 +++-- .../providers/decorations/source-control.ts | 14 ++- .../src/providers/decorations/submodule.ts | 4 + .../github1s/src/providers/definition.ts | 29 +++--- .../github1s/src/providers/file-search.ts | 35 ++++--- .../src/providers/file-system/index.ts | 98 +++++++------------ extensions/github1s/src/providers/hover.ts | 25 ++--- extensions/github1s/src/providers/index.ts | 5 +- .../github1s/src/providers/reference.ts | 18 ++-- .../github1s/src/providers/text-search.ts | 9 +- .../github1s/src/repository/commit-manager.ts | 4 +- extensions/github1s/src/repository/index.ts | 26 +++-- extensions/github1s/src/router/index.ts | 74 +++++++++----- extensions/github1s/src/statusbar/checkout.ts | 2 +- extensions/github1s/src/statusbar/sponsors.ts | 2 +- .../github1s/src/views/code-review-list.ts | 15 ++- extensions/github1s/src/views/commit-list.ts | 19 ++-- src/index.ts | 1 - src/oauth-web.ts | 2 - 50 files changed, 448 insertions(+), 455 deletions(-) diff --git a/extensions/github1s/package.json b/extensions/github1s/package.json index cafb1e48c..927bdf563 100644 --- a/extensions/github1s/package.json +++ b/extensions/github1s/package.json @@ -26,6 +26,32 @@ "vscode": "^1.48.0" }, "contributes": { + "resourceLabelFormatters": [ + { + "scheme": "github1s", + "authority": "**/*+?*", + "formatting": { + "label": "${path} (${authoritySuffix:7})", + "separator": "/" + } + }, + { + "scheme": "gitlab1s", + "authority": "**/*+?*", + "formatting": { + "label": "${path} (${authoritySuffix:7})", + "separator": "/" + } + }, + { + "scheme": "bitbucket1s", + "authority": "**/*+?*", + "formatting": { + "label": "${path} (${authoritySuffix:7})", + "separator": "/" + } + } + ], "viewsContainers": { "activitybar": [ { @@ -596,7 +622,8 @@ "scripts": { "clean": "rm -rf dist out", "watch": "webpack --config webpack.config.js --watch", - "compile": "webpack --config webpack.config.js --mode production" + "compile": "webpack --config webpack.config.js --mode production", + "test": "node --experimental-strip-types --test test/*.test.ts" }, "keywords": [], "author": "", diff --git a/extensions/github1s/src/adapters/bitbucket1s/parse-path.ts b/extensions/github1s/src/adapters/bitbucket1s/parse-path.ts index 2ac025f6d..4ba8f48f1 100644 --- a/extensions/github1s/src/adapters/bitbucket1s/parse-path.ts +++ b/extensions/github1s/src/adapters/bitbucket1s/parse-path.ts @@ -13,9 +13,14 @@ const parseTreeOrBlobUrl = async (path: string): Promise => { const repoFullName = `${owner}/${repo}`; const dataSource = SourcegraphDataSource.getInstance('bitbucket'); const { ref, path: filePath } = await dataSource.extractRefPath(repoFullName, restParts.join('/')); - const fileType = await dataSource.detectPathFileType(repo, ref, filePath); + const fileType = await dataSource.detectPathFileType(repoFullName, ref, filePath); - return { pageType: fileType === FileType.Directory ? PageType.Tree : PageType.Blob, repo, ref, filePath }; + return { + pageType: fileType === FileType.Directory ? PageType.Tree : PageType.Blob, + repo: repoFullName, + ref, + filePath, + }; }; const parseCommitsUrl = async (path: string): Promise => { @@ -64,6 +69,6 @@ export const parseBitbucketPath = async (path: string): Promise => repo: 'atlassian/clover', ref: 'HEAD', pageType: PageType.Tree, - filePath: '', + filePath: '/', }; }; diff --git a/extensions/github1s/src/adapters/bitbucket1s/router-parser.ts b/extensions/github1s/src/adapters/bitbucket1s/router-parser.ts index 658a7cd9a..d5d4de6a0 100644 --- a/extensions/github1s/src/adapters/bitbucket1s/router-parser.ts +++ b/extensions/github1s/src/adapters/bitbucket1s/router-parser.ts @@ -21,12 +21,12 @@ export class BitbucketRouterParser extends adapterTypes.RouterParser { } buildTreePath(repo: string, ref?: string, filePath?: string): string { - return ref ? (filePath ? `/${repo}/src/${ref}/${filePath}` : `/${repo}/src/${ref}`) : `/${repo}`; + return ref ? `/${repo}/src/${ref}${filePath && filePath !== '/' ? filePath : ''}` : `/${repo}`; } buildBlobPath(repo: string, ref: string, filePath: string, startLine?: number, endLine?: number): string { const hash = startLine ? (endLine ? `#L${startLine}-L${endLine}` : `#L${startLine}`) : ''; - return `/${repo}/src/${ref}/${filePath}${hash}`; + return `/${repo}/src/${ref}${filePath}${hash}`; } buildCommitListPath(repo: string): string { diff --git a/extensions/github1s/src/adapters/github1s/data-source.ts b/extensions/github1s/src/adapters/github1s/data-source.ts index 9734bd3d5..ba7497ce7 100644 --- a/extensions/github1s/src/adapters/github1s/data-source.ts +++ b/extensions/github1s/src/adapters/github1s/data-source.ts @@ -33,6 +33,7 @@ import { FILE_BLAME_QUERY } from './graphql'; import { GitHubFetcher } from './fetcher'; import { SourcegraphDataSource } from '../sourcegraph/data-source'; import { decorate, memorize } from '@/helpers/func'; +import { normalizePath, trimStart, concatPath, isString } from '@/helpers/util'; const parseRepoFullName = (repoFullName: string) => { const [owner, repo] = repoFullName.split('/'); @@ -41,7 +42,7 @@ const parseRepoFullName = (repoFullName: string) => { const encodeFilePath = (filePath: string): string => { const pathParts = filePath.split('/').filter(Boolean); - return pathParts.map((segment) => encodeURIComponent(segment)).join('/'); + return `/${pathParts.map((segment) => encodeURIComponent(segment)).join('/')}`; }; const FileTypeMap = { @@ -104,13 +105,13 @@ export class GitHub1sDataSource extends DataSource { @trySourcegraphApiFirst async provideDirectory(repoFullName: string, ref: string, path: string, recursive = false): Promise { const fetcher = GitHubFetcher.getInstance(); - const encodedPath = encodeFilePath(path); + const encodedPath = trimStart(encodeFilePath(path), '/'); // github api will return all files if `recursive` exists, even the value if false const recursiveParams = recursive ? { recursive } : {}; const requestParams = { ref, path: encodedPath, ...parseRepoFullName(repoFullName), ...recursiveParams }; const { data } = await fetcher.request('GET /repos/{owner}/{repo}/git/trees/{ref}:{path}', requestParams); const parseTreeItem = (treeItem): DirectoryEntry => ({ - path: treeItem.path, + path: concatPath(path, treeItem.path), type: FileTypeMap[treeItem.type] || FileType.File, commitSha: FileTypeMap[treeItem.type] === FileType.Submodule ? treeItem.sha || 'HEAD' : undefined, size: treeItem.size, @@ -126,7 +127,7 @@ export class GitHub1sDataSource extends DataSource { async provideFile(repoFullName: string, ref: string, path: string): Promise { const fetcher = GitHubFetcher.getInstance(); const { owner, repo } = parseRepoFullName(repoFullName); - const requestParams = { owner, repo, ref, path }; + const requestParams = { owner, repo, ref, path: trimStart(path, '/') }; const { data } = await fetcher.request('GET /repos/{owner}/{repo}/contents/{path}', requestParams); return { content: toUint8Array((data as any).content) }; } @@ -156,16 +157,16 @@ export class GitHub1sDataSource extends DataSource { const matchPathRef = (ref) => refAndPath.startsWith(`${ref}/`) || refAndPath === ref; const matchedRef = this.matchedRefsMap.get(repoFullName)?.find(matchPathRef); if (matchedRef) { - return { ref: matchedRef, path: refAndPath.slice(matchedRef.length + 1) }; + return { ref: matchedRef, path: normalizePath(refAndPath.slice(matchedRef.length + 1)) }; } const mapKey = `${repoFullName} ${refAndPath}`; if (!this.refPathPromiseMap.has(mapKey)) { const refPathPromise = new Promise<{ ref: string; path: string }>(async (resolve, reject) => { if (!refAndPath) { - return resolve({ ref: await this.getDefaultBranch(repoFullName), path: '' }); + return resolve({ ref: await this.getDefaultBranch(repoFullName), path: '/' }); } if (refAndPath.match(/^HEAD(\/.*)?$/i)) { - return resolve({ ref: 'HEAD', path: refAndPath.slice(5) }); + return resolve({ ref: 'HEAD', path: normalizePath(refAndPath.slice(5)) }); } const fetcher = GitHubFetcher.getInstance(); @@ -174,7 +175,8 @@ export class GitHub1sDataSource extends DataSource { const requestUrl = `GET /repos/{owner}/{repo}/git/extract-ref/{refAndPath}`; const response = await fetcher.request(requestUrl, requestParams).catch(reject); response?.data?.ref && this.matchedRefsMap.get(repoFullName)?.push(response.data.ref); - return resolve(response?.data || { ref: 'HEAD', path: '' }); + const result = response?.data || { ref: 'HEAD', path: '/' }; + return resolve({ ...result, path: normalizePath(result.path) }); }); this.refPathPromiseMap.set(mapKey, refPathPromise); } @@ -247,7 +249,7 @@ export class GitHub1sDataSource extends DataSource { page: options?.page, per_page: options?.pageSize, sha: options?.from, - path: options?.path, + path: isString(options?.path) ? trimStart(options.path, '/') : undefined, author: options?.author, }; const requestParams = { owner, repo, ...queryParams }; @@ -279,8 +281,8 @@ export class GitHub1sDataSource extends DataSource { createTime: data.commit.author?.date ? new Date(data.commit.author.date) : undefined, parents: data.parents.map((parent) => parent.sha) || [], files: data.files?.map((item) => ({ - path: item.filename || item.previous_filename!, - previousPath: item.previous_filename, + path: normalizePath(item.filename || item.previous_filename!), + previousPath: item.previous_filename ? normalizePath(item.previous_filename) : undefined, status: item.status as FileChangeStatus, })), avatarUrl: data.author?.avatar_url, @@ -299,8 +301,8 @@ export class GitHub1sDataSource extends DataSource { const { data } = await fetcher.request('GET /repos/{owner}/{repo}/commits/{ref}', requestParams); return ( data.files?.map((item) => ({ - path: item.filename || item.previous_filename!, - previousPath: item.previous_filename, + path: normalizePath(item.filename || item.previous_filename!), + previousPath: item.previous_filename ? normalizePath(item.previous_filename) : undefined, status: item.status as FileChangeStatus, })) || [] ); @@ -367,8 +369,8 @@ export class GitHub1sDataSource extends DataSource { const { data } = await fetcher.request('GET /repos/{owner}/{repo}/pulls/{pull_number}/files', filesRequestParams); return data.map((item) => ({ - path: item.filename, - previousPath: item.previous_filename, + path: normalizePath(item.filename), + previousPath: item.previous_filename ? normalizePath(item.previous_filename) : undefined, status: item.status as FileChangeStatus, })); } @@ -377,7 +379,7 @@ export class GitHub1sDataSource extends DataSource { async provideFileBlameRanges(repoFullName: string, ref: string, path: string): Promise { const fetcher = GitHubFetcher.getInstance(); const { owner, repo } = parseRepoFullName(repoFullName); - const requestParams = { owner, repo, ref, path }; + const requestParams = { owner, repo, ref, path: trimStart(path, '/') }; const data = await fetcher.graphql(FILE_BLAME_QUERY, requestParams); const blameRanges = (data as any)?.repository?.object?.blame?.ranges; diff --git a/extensions/github1s/src/adapters/github1s/parse-path.ts b/extensions/github1s/src/adapters/github1s/parse-path.ts index b16ab61a6..cf20392ef 100644 --- a/extensions/github1s/src/adapters/github1s/parse-path.ts +++ b/extensions/github1s/src/adapters/github1s/parse-path.ts @@ -164,6 +164,6 @@ export const parseGitHubPath = async (path: string): Promise => { repo: DEFAULT_REPO, ref: await getDefaultBranch(DEFAULT_REPO), pageType: PageType.Tree, - filePath: '', + filePath: '/', }; }; diff --git a/extensions/github1s/src/adapters/github1s/router-parser.ts b/extensions/github1s/src/adapters/github1s/router-parser.ts index 99345bb33..a38dab5bc 100644 --- a/extensions/github1s/src/adapters/github1s/router-parser.ts +++ b/extensions/github1s/src/adapters/github1s/router-parser.ts @@ -22,12 +22,12 @@ export class GitHub1sRouterParser extends adapterTypes.RouterParser { } buildTreePath(repo: string, ref?: string, filePath?: string): string { - return ref ? (filePath ? `/${repo}/tree/${ref}/${filePath}` : `/${repo}/tree/${ref}`) : `/${repo}`; + return ref ? `/${repo}/tree/${ref}${filePath && filePath !== '/' ? filePath : ''}` : `/${repo}`; } buildBlobPath(repo: string, ref: string, filePath: string, startLine?: number, endLine?: number): string { const hash = startLine ? (endLine ? `#L${startLine}-L${endLine}` : `#L${startLine}`) : ''; - return `/${repo}/blob/${ref}/${filePath}${hash}`; + return `/${repo}/blob/${ref}${filePath}${hash}`; } buildCommitListPath(repo: string): string { diff --git a/extensions/github1s/src/adapters/gitlab1s/data-source.ts b/extensions/github1s/src/adapters/gitlab1s/data-source.ts index d84c9940a..9b79a2a95 100644 --- a/extensions/github1s/src/adapters/gitlab1s/data-source.ts +++ b/extensions/github1s/src/adapters/gitlab1s/data-source.ts @@ -32,6 +32,7 @@ import { matchSorter } from 'match-sorter'; import { GitLabFetcher } from './fetcher'; import { SourcegraphDataSource } from '../sourcegraph/data-source'; import { decorate, memorize } from '@/helpers/func'; +import { trimStart, normalizePath, isString } from '@/helpers/util'; const FileTypeMap = { blob: FileType.File, @@ -102,13 +103,13 @@ export class GitLab1sDataSource extends DataSource { let page = 1; let files = []; const parseTreeItem = (treeItem): DirectoryEntry => ({ - path: treeItem.path.slice(path.length), + path: normalizePath(treeItem.path), type: FileTypeMap[treeItem.type] || FileType.File, - commitSha: FileTypeMap[treeItem.id] === FileType.Submodule ? treeItem.sha || 'HEAD' : undefined, + commitSha: FileTypeMap[treeItem.type] === FileType.Submodule ? treeItem.sha || 'HEAD' : undefined, size: treeItem.size, }); while (page > 0) { - const requestParams = { ref, page, path, repo, recursive }; + const requestParams = { ref, page, path: trimStart(path, '/'), repo, recursive }; const { data, headers } = await fetcher.request( 'GET /projects/{repo}/repository/tree?recursive={recursive}&per_page=100&page={page}&ref={ref}&path={path}', requestParams, @@ -127,7 +128,7 @@ export class GitLab1sDataSource extends DataSource { @trySourcegraphApiFirst async provideFile(repo: string, ref: string, path: string): Promise { const fetcher = GitLabFetcher.getInstance(); - const requestParams = { ref, path, repo }; + const requestParams = { ref, path: trimStart(path, '/'), repo }; const { data } = await fetcher.request('GET /projects/{repo}/repository/files/{path}?ref={ref}', requestParams); return { content: toUint8Array((data as any).content) }; } @@ -164,10 +165,10 @@ export class GitLab1sDataSource extends DataSource { @trySourcegraphApiFirst async extractRefPath(repo: string, refAndPath: string): Promise<{ ref: string; path: string }> { if (!refAndPath) { - return { ref: await this.getDefaultBranch(repo), path: '' }; + return { ref: await this.getDefaultBranch(repo), path: '/' }; } if (refAndPath.match(/^HEAD(\/.*)?$/i)) { - return { ref: 'HEAD', path: refAndPath.slice(5) }; + return { ref: 'HEAD', path: normalizePath(refAndPath.slice(5)) }; } if (!this.matchedRefsMap.has(repo)) { this.matchedRefsMap.set(repo, []); @@ -175,13 +176,13 @@ export class GitLab1sDataSource extends DataSource { const matchPathRef = (ref) => refAndPath.startsWith(`${ref}/`) || refAndPath === ref; const pathRef = this.matchedRefsMap.get(repo)?.find(matchPathRef); if (pathRef) { - return { ref: pathRef, path: refAndPath.slice(pathRef.length + 1) }; + return { ref: pathRef, path: normalizePath(refAndPath.slice(pathRef.length + 1)) }; } const [branches, tags] = await this.prepareAllRefs(repo); const exactRef = [...branches, ...tags].map((item) => item.name).find(matchPathRef); const ref = exactRef || refAndPath.split('/')[0] || 'HEAD'; exactRef && this.matchedRefsMap.get(repo)?.push(ref); - return { ref, path: refAndPath.slice(ref.length + 1) }; + return { ref, path: normalizePath(refAndPath.slice(ref.length + 1)) }; } async prepareAllRefs(repo: string) { @@ -250,7 +251,7 @@ export class GitLab1sDataSource extends DataSource { page: options?.page, per_page: options?.pageSize, sha: options?.from, - path: options?.path, + path: isString(options?.path) ? trimStart(options.path, '/') : undefined, author: options?.author, }; const requestParams = { repo, ...queryParams }; @@ -286,8 +287,8 @@ export class GitLab1sDataSource extends DataSource { createTime: data.created_at ? new Date(data.created_at) : undefined, parents: data.parent_ids || [], files: data.files?.map((item) => ({ - path: item.filename || item.previous_filename!, - previousPath: item.previous_filename, + path: normalizePath(item.filename || item.previous_filename!), + previousPath: item.previous_filename ? normalizePath(item.previous_filename) : undefined, status: item.status as FileChangeStatus, })), avatarUrl: data?.avatar_url, @@ -301,8 +302,8 @@ export class GitLab1sDataSource extends DataSource { const { data } = await fetcher.request('GET /projects/{repo}/repository/commits/{ref}/diff', requestParams); return ( data?.map((item) => ({ - path: item.new_path || item.old_path!, - previousPath: item.old_path, + path: normalizePath(item.new_path || item.old_path!), + previousPath: item.old_path ? normalizePath(item.old_path) : undefined, status: item.new_file ? FileChangeStatus.Added : item.deleted_file @@ -373,8 +374,8 @@ export class GitLab1sDataSource extends DataSource { ); return data.changes.map((item) => ({ - path: item.new_path, - previousPath: item.old_path, + path: normalizePath(item.new_path), + previousPath: item.old_path ? normalizePath(item.old_path) : undefined, status: item.new_file ? FileChangeStatus.Added : item.deleted_file @@ -388,7 +389,7 @@ export class GitLab1sDataSource extends DataSource { @trySourcegraphApiFirst async provideFileBlameRanges(repo: string, ref: string, path: string): Promise { const fetcher = GitLabFetcher.getInstance(); - const requestParams = { repo, ref, path }; + const requestParams = { repo, ref, path: trimStart(path, '/') }; const { data } = await fetcher.request( 'GET /projects/{repo}/repository/files/{path}/blame?ref={ref}', requestParams, diff --git a/extensions/github1s/src/adapters/gitlab1s/parse-path.ts b/extensions/github1s/src/adapters/gitlab1s/parse-path.ts index 7dc02a531..9e108ba6d 100644 --- a/extensions/github1s/src/adapters/gitlab1s/parse-path.ts +++ b/extensions/github1s/src/adapters/gitlab1s/parse-path.ts @@ -164,6 +164,6 @@ export const parseGitLabPath = async (path: string): Promise => { repo: DEFAULT_REPO, ref: await getDefaultBranch(DEFAULT_REPO), pageType: PageType.Tree, - filePath: '', + filePath: '/', }; }; diff --git a/extensions/github1s/src/adapters/gitlab1s/router-parser.ts b/extensions/github1s/src/adapters/gitlab1s/router-parser.ts index 2e1809fbd..40aca6882 100644 --- a/extensions/github1s/src/adapters/gitlab1s/router-parser.ts +++ b/extensions/github1s/src/adapters/gitlab1s/router-parser.ts @@ -22,12 +22,12 @@ export class GitLab1sRouterParser extends adapterTypes.RouterParser { } buildTreePath(repo: string, ref?: string, filePath?: string): string { - return ref ? (filePath ? `/${repo}/-/tree/${ref}/${filePath}` : `/${repo}/-/tree/${ref}`) : `/${repo}`; + return ref ? `/${repo}/-/tree/${ref}${filePath && filePath !== '/' ? filePath : ''}` : `/${repo}`; } buildBlobPath(repo: string, ref: string, filePath: string, startLine?: number, endLine?: number): string { const hash = startLine ? (endLine ? `#L${startLine}-L${endLine}` : `#L${startLine}`) : ''; - return `/${repo}/-/blob/${ref}/${filePath}${hash}`; + return `/${repo}/-/blob/${ref}${filePath}${hash}`; } buildCommitListPath(repo: string): string { diff --git a/extensions/github1s/src/adapters/npmjs1s/data-source.ts b/extensions/github1s/src/adapters/npmjs1s/data-source.ts index 8e9b3606c..4fac6312e 100644 --- a/extensions/github1s/src/adapters/npmjs1s/data-source.ts +++ b/extensions/github1s/src/adapters/npmjs1s/data-source.ts @@ -6,6 +6,7 @@ import { CommonQueryOptions, DataSource, Directory, DirectoryEntry, File, FileType, Tag } from '../types'; import { matchSorter } from 'match-sorter'; import * as dayjs from 'dayjs'; +import { normalizePath } from '@/helpers/util'; type PackageFile = { path: string; @@ -23,14 +24,13 @@ type PackageEntry = PackageFile | PackageDirectory; type PackageVersion = { name: string; tag?: string; time?: Date }; -const retrieveFiles = (files: PackageEntry[], pathDeep: number, recursive: boolean) => { +const retrieveFiles = (files: PackageEntry[], recursive: boolean) => { const entries: DirectoryEntry[] = []; for (const item of files) { const fileType = item.type === 'directory' ? FileType.Directory : FileType.File; - const filePath = item.path.split(/\/+/).filter(Boolean).slice(pathDeep).join('/'); - entries.push({ type: fileType, path: filePath }); + entries.push({ type: fileType, path: normalizePath(item.path) }); if (recursive && item.type === 'directory' && item.files?.length) { - entries.push(...retrieveFiles(item.files, pathDeep, recursive)); + entries.push(...retrieveFiles(item.files, recursive)); } } return entries; @@ -70,12 +70,12 @@ export class Npmjs1sDataSource extends DataSource { }, await this.getPackageFiles(packageName, version), ); - const entries = parentFiles ? retrieveFiles(parentFiles, pathParts.length, recursive) : []; + const entries = parentFiles ? retrieveFiles(parentFiles, recursive) : []; return { entries, truncated: false }; } async provideFile(packageName: string, version: string, path: string): Promise { - const response = await fetch(`https://unpkg.com/${packageName}@${version}/${path}`); + const response = await fetch(`https://unpkg.com/${packageName}@${version}${path}`); return { content: new Uint8Array(await response.arrayBuffer()) }; } diff --git a/extensions/github1s/src/adapters/npmjs1s/router-parser.ts b/extensions/github1s/src/adapters/npmjs1s/router-parser.ts index 76f635782..5b67a229a 100644 --- a/extensions/github1s/src/adapters/npmjs1s/router-parser.ts +++ b/extensions/github1s/src/adapters/npmjs1s/router-parser.ts @@ -10,7 +10,7 @@ export const parseNpmPath = async (path: string): Promise => { const pathParts = parsePath(path).pathname?.split('/').filter(Boolean) || []; if (!pathParts.length) { - return { pageType: PageType.Tree, repo: 'lodash', ref: 'latest', filePath: '' }; + return { pageType: PageType.Tree, repo: 'lodash', ref: 'latest', filePath: '/' }; } const trimedParts = pathParts[0] === 'package' ? pathParts.slice(1) : pathParts; @@ -20,7 +20,7 @@ export const parseNpmPath = async (path: string): Promise => { const packageVersion = trimedParts[packagePartsLength] === 'v' ? trimedParts[packagePartsLength + 1] || 'latest' : 'latest'; - return { pageType: PageType.Tree as const, repo: packageName, ref: packageVersion, filePath: '' }; + return { pageType: PageType.Tree as const, repo: packageName, ref: packageVersion, filePath: '/' }; }; export class Npmjs1sRouterParser extends RouterParser { diff --git a/extensions/github1s/src/adapters/ossinsight/data-source.ts b/extensions/github1s/src/adapters/ossinsight/data-source.ts index 3f03ebcd4..a9a19278e 100644 --- a/extensions/github1s/src/adapters/ossinsight/data-source.ts +++ b/extensions/github1s/src/adapters/ossinsight/data-source.ts @@ -129,7 +129,7 @@ export class OSSInsightDataSource extends DataSource { } async provideDirectory(repo: string, ref: string, path: string, recursive?: boolean): Promise { - const walk = async (item: StructureItem | undefined, recursive = false, basePath = '') => { + const walk = async (item: StructureItem | undefined, recursive = false, basePath = '/') => { const directoryEntires: Directory['entries'] = []; for (const child of await this.getStructureItemChildren(item)) { const currentPath = joinPath(basePath, child.name); @@ -143,7 +143,7 @@ export class OSSInsightDataSource extends DataSource { return { truncated: false, - entries: await walk(await this.resolveStructureItem(path), recursive), + entries: await walk(await this.resolveStructureItem(path), recursive, path), }; } diff --git a/extensions/github1s/src/adapters/ossinsight/router-parser.ts b/extensions/github1s/src/adapters/ossinsight/router-parser.ts index 8da4a14ef..043e41de3 100644 --- a/extensions/github1s/src/adapters/ossinsight/router-parser.ts +++ b/extensions/github1s/src/adapters/ossinsight/router-parser.ts @@ -7,6 +7,7 @@ import { parsePath } from 'history'; import * as queryString from 'query-string'; import * as adapterTypes from '../types'; import { GitHub1sRouterParser } from '../github1s/router-parser'; +import { normalizePath } from '@/helpers/util'; export class OSSInsightRouterParser extends GitHub1sRouterParser { protected static instance: OSSInsightRouterParser | null = null; @@ -20,7 +21,7 @@ export class OSSInsightRouterParser extends GitHub1sRouterParser { async parsePath(path: string): Promise { const { path: pathsOrNull } = queryString.parse((parsePath(path).search || '').slice(1)); - const filePath = (Array.isArray(pathsOrNull) ? pathsOrNull[0] : pathsOrNull) || ''; + const filePath = normalizePath((Array.isArray(pathsOrNull) ? pathsOrNull[0] : pathsOrNull) || ''); const pageType = filePath.endsWith('.md') ? adapterTypes.PageType.Blob : adapterTypes.PageType.Tree; return { pageType, repo: '', ref: '', filePath }; } diff --git a/extensions/github1s/src/adapters/sourcegraph/data-source.ts b/extensions/github1s/src/adapters/sourcegraph/data-source.ts index 8fd002c68..d46302920 100644 --- a/extensions/github1s/src/adapters/sourcegraph/data-source.ts +++ b/extensions/github1s/src/adapters/sourcegraph/data-source.ts @@ -3,7 +3,6 @@ * @author netcon */ -import { joinPath } from '@/helpers/util'; import { matchSorter } from 'match-sorter'; import { Branch, @@ -35,6 +34,7 @@ import { getSymbolReferences } from './reference'; import { getRepository } from './repository'; import { getTextSearchResults } from './search'; import { decorate, memorize } from '@/helpers/func'; +import { normalizePath, trimStart } from '@/helpers/util'; type SupportedPlatform = 'github' | 'gitlab' | 'bitbucket'; @@ -71,26 +71,24 @@ export class SourcegraphDataSource extends DataSource { } async provideDirectory(repo: string, ref: string, path: string, recursive = false): Promise { - const directories = await readDirectory(this.buildRepository(repo), ref, path, recursive); + const directories = await readDirectory(this.buildRepository(repo), ref, trimStart(path, '/'), recursive); directories.entries.forEach((entry) => { - const mapKey = `${repo} ${ref} ${joinPath(path, entry.path)}`; + const mapKey = `${repo} ${ref} ${entry.path}`; this.fileTypeMap.set(mapKey, entry.type); }); return directories; } async detectPathFileType(repo: string, ref: string, path: string) { - const pathParts = path.split('/').filter(Boolean); - const trimmedPath = pathParts.join('/'); - if (!trimmedPath) { + if (path === '/') { return FileType.Directory; } - const mapKey = `${repo} ${ref} ${trimmedPath}`; + const mapKey = `${repo} ${ref} ${path}`; if (this.fileTypeMap.has(mapKey)) { return this.fileTypeMap.get(mapKey)!; } - await this.provideDirectory(repo, ref, pathParts.slice(0, -1).join('/'), false); - return this.fileTypeMap.get(trimmedPath) || FileType.File; + await this.provideDirectory(repo, ref, normalizePath(path.split('/').slice(0, -1).join('/')), false); + return this.fileTypeMap.get(mapKey) || FileType.File; } async provideRepository(repo: string) { @@ -101,6 +99,7 @@ export class SourcegraphDataSource extends DataSource { } async provideFile(repo: string, ref: string, path: string): Promise { + const apiPath = trimStart(path, '/'); // sourcegraph api break binary files and text coding, so we use github api first here if (this.platform === 'github') { // For GitHub repositories, request GitHub User Content API first (it seems no Rate Limit), @@ -108,13 +107,13 @@ export class SourcegraphDataSource extends DataSource { // Content API goes wrong, then try Sourcegraph API. Use `try catch` because if fallback to // GitHub REST API may trigger a pop-up window to request authentication for anonymous users. try { - return fetch(encodeURI(`https://raw.githubusercontent.com/${repo}/${ref}/${path}`)) + return fetch(encodeURI(`https://raw.githubusercontent.com/${repo}/${ref}/${apiPath}`)) .then((response) => (response.ok ? response.arrayBuffer() : Promise.reject({ response }))) .then((buffer) => ({ content: new Uint8Array(buffer) })); } catch {} } // TODO: support binary files for other platforms - const { content } = await readFile(this.buildRepository(repo), ref, path); + const { content } = await readFile(this.buildRepository(repo), ref, apiPath); return { content: this.textEncoder.encode(content) }; } @@ -132,10 +131,10 @@ export class SourcegraphDataSource extends DataSource { async extractRefPath(repo: string, refAndPath: string): Promise<{ ref: string; path: string }> { if (!refAndPath) { - return { ref: await this.getDefaultBranch(repo), path: '' }; + return { ref: await this.getDefaultBranch(repo), path: '/' }; } if (refAndPath.match(/^HEAD(\/.*)?$/i)) { - return { ref: 'HEAD', path: refAndPath.slice(5) }; + return { ref: 'HEAD', path: normalizePath(refAndPath.slice(5)) }; } if (!this.matchedRefsMap.has(repo)) { this.matchedRefsMap.set(repo, []); @@ -143,13 +142,13 @@ export class SourcegraphDataSource extends DataSource { const matchPathRef = (ref) => refAndPath.startsWith(`${ref}/`) || refAndPath === ref; const pathRef = this.matchedRefsMap.get(repo)?.find(matchPathRef); if (pathRef) { - return { ref: pathRef, path: refAndPath.slice(pathRef.length + 1) }; + return { ref: pathRef, path: normalizePath(refAndPath.slice(pathRef.length + 1)) }; } const { branches, tags } = await this.prepareAllRefs(repo); const exactRef = [...branches, ...tags].map((item) => item.name).find(matchPathRef); const ref = exactRef || refAndPath.split('/')[0] || 'HEAD'; exactRef && this.matchedRefsMap.get(repo)?.push(ref); - return { ref, path: refAndPath.slice(ref.length + 1) }; + return { ref, path: normalizePath(refAndPath.slice(ref.length + 1)) }; } async provideBranches(repo: string, options?: CommonQueryOptions): Promise { @@ -184,17 +183,21 @@ export class SourcegraphDataSource extends DataSource { query: TextSearchQuery, options: TextSearchOptions, ): Promise { - return getTextSearchResults(this.buildRepository(repo), ref, query, options); + const results = await getTextSearchResults(this.buildRepository(repo), ref, query, options); + return { + ...results, + results: results.results.map((result) => ({ ...result, path: normalizePath(result.path) })), + }; } async provideCommits(repo: string, options?: CommitsQueryOptions): Promise<(Commit & { files?: ChangedFile[] })[]> { let commits = await getCommits( this.buildRepository(repo), options?.from || 'HEAD', - options?.path, + options?.path === undefined ? undefined : trimStart(options.path, '/'), options?.pageSize ? options.pageSize * (options.page || 1) : undefined, ); - if (options?.path && commits.length) { + if (options?.path && options.path !== '/' && commits.length) { // find the latested that related the `options.path` file const changedFiles = await this.provideCommitChangedFiles(repo, commits[0].sha); commits = changedFiles.find((file) => file.path === options.path) ? commits : commits.slice(1); @@ -207,11 +210,15 @@ export class SourcegraphDataSource extends DataSource { } async provideCommitChangedFiles(repo: string, ref: string, _options?: CommonQueryOptions): Promise { - return compareCommits(this.buildRepository(repo), `${ref}~`, ref); + return (await compareCommits(this.buildRepository(repo), `${ref}~`, ref)).map((file) => ({ + ...file, + path: normalizePath(file.path), + previousPath: file.previousPath ? normalizePath(file.previousPath) : undefined, + })); } async provideFileBlameRanges(repo: string, ref: string, path: string): Promise { - return getFileBlameRanges(this.buildRepository(repo), ref, path); + return getFileBlameRanges(this.buildRepository(repo), ref, trimStart(path, '/')); } async provideSymbolDefinitions( @@ -222,7 +229,10 @@ export class SourcegraphDataSource extends DataSource { character: number, symbol: string, ): Promise { - return getSymbolDefinitions(this.buildRepository(repo), ref, path, line, character, symbol); + const apiPath = trimStart(path, '/'); + return getSymbolDefinitions(this.buildRepository(repo), ref, apiPath, line, character, symbol).then((locations) => + locations.map((location) => ({ ...location, path: normalizePath(location.path) })), + ); } async provideSymbolReferences( @@ -233,7 +243,10 @@ export class SourcegraphDataSource extends DataSource { character: number, symbol: string, ): Promise { - return getSymbolReferences(this.buildRepository(repo), ref, path, line, character, symbol); + const apiPath = trimStart(path, '/'); + return getSymbolReferences(this.buildRepository(repo), ref, apiPath, line, character, symbol).then((locations) => + locations.map((location) => ({ ...location, path: normalizePath(location.path) })), + ); } async provideSymbolHover( diff --git a/extensions/github1s/src/adapters/sourcegraph/file.ts b/extensions/github1s/src/adapters/sourcegraph/file.ts index 6366b43b9..280c1bda5 100644 --- a/extensions/github1s/src/adapters/sourcegraph/file.ts +++ b/extensions/github1s/src/adapters/sourcegraph/file.ts @@ -6,6 +6,7 @@ import { gql } from '@apollo/client/core'; import { querySourcegraphRepository } from './common'; import { Directory, FileType } from '../types'; +import { normalizePath } from '@/helpers/util'; const FILE_COUNT_LIMIT = 50000; @@ -39,12 +40,11 @@ export const readDirectory = async ( variables: { repository, ref, path, recursive }, }); const files = repositoryData.commit?.tree?.entries || []; - const pathParts = path.split('/').filter(Boolean); return { entries: files.map((file) => ({ - path: file.path.split('/').filter(Boolean).slice(pathParts.length).join('/'), + path: normalizePath(file.path), type: file.isDirectory ? FileType.Directory : file.submodule ? FileType.Submodule : FileType.File, - commitSha: file.submodule?.sha, + commitSha: file.submodule?.commit, })), truncated: files.length >= FILE_COUNT_LIMIT, }; diff --git a/extensions/github1s/src/adapters/types.ts b/extensions/github1s/src/adapters/types.ts index f53e0c380..ecc563d99 100644 --- a/extensions/github1s/src/adapters/types.ts +++ b/extensions/github1s/src/adapters/types.ts @@ -169,10 +169,9 @@ export type SymbolReferences = CodeLocation[]; export type SymbolHover = { markdown: string }; +// All repository path parameters and return values start with '/'. export class DataSource { // if `recursive` is true, it should try to return all subtrees - // the returned Directory.entries.path is relative the `path` in arguments, - // so if `recursive` is false, the returned path should be the file name provideDirectory(repo: string, ref: string, path: string, recursive = false): Promisable { return null; } @@ -344,7 +343,7 @@ export type RouterState = { repo: string; ref: string } & ( export class RouterParser { // parse giving path (starts with '/', may includes search and hash) to Router state, parsePath(path: string): Promisable { - return { repo: '', ref: 'HEAD', pageType: PageType.Tree, filePath: '' }; + return { repo: '', ref: 'HEAD', pageType: PageType.Tree, filePath: '/' }; } // build the tree page path diff --git a/extensions/github1s/src/changes/files.ts b/extensions/github1s/src/changes/files.ts index 56c9f42cd..8d4b0ddd7 100644 --- a/extensions/github1s/src/changes/files.ts +++ b/extensions/github1s/src/changes/files.ts @@ -22,18 +22,10 @@ interface VSCodeChangedFile { export const getCodeReviewChangedFiles = async ( codeReview: adapterTypes.CodeReview & { sourceSha: string; targetSha: string }, ) => { - const scheme = adapterManager.getCurrentScheme(); - const { repo } = await router.getState(); - const baseRootUri = vscode.Uri.parse('').with({ - scheme: scheme, - authority: `${repo}+${codeReview.targetSha}`, - path: '/', - }); - const headRootUri = baseRootUri.with({ - authority: `${repo}+${codeReview.sourceSha}`, - }); + const baseRootUri = router.buildUri({ ref: codeReview.targetSha }); + const headRootUri = router.buildUri({ ref: codeReview.sourceSha }, baseRootUri); - const repository = Repository.getInstance(scheme, repo); + const repository = Repository.getCurrentInstance(); const changedFiles = await repository.getCodeReviewChangedFiles(codeReview.id); return changedFiles.map((changedFile) => { @@ -42,30 +34,22 @@ export const getCodeReviewChangedFiles = async ( const baseFilePath = changedFile.previousPath || changedFile.path; const headFilePath = changedFile.path; return { - baseFileUri: vscode.Uri.joinPath(baseRootUri, baseFilePath), - headFileUri: vscode.Uri.joinPath(headRootUri, headFilePath), + baseFileUri: baseRootUri.with({ path: baseFilePath }), + headFileUri: headRootUri.with({ path: headFilePath }), status: changedFile.status, }; }); }; export const getCommitChangedFiles = async (commit: adapterTypes.Commit) => { - const currentAdapter = adapterManager.getCurrentAdapter(); - const scheme = currentAdapter.scheme; - const { repo } = await router.getState(); // if the commit.parents is more than one element // the parents[1].sha should be the merge source commitSha // so we use the parents[0].sha as the parent commitSha - const baseRef = commit?.parents?.[0]; - const baseRootUri = vscode.Uri.parse('').with({ - scheme: currentAdapter.scheme, - authority: `${repo}+${baseRef || 'HEAD'}`, - path: '/', - }); - const headRootUri = baseRootUri.with({ - authority: `${repo}+${commit.sha || 'HEAD'}`, - }); - const repository = Repository.getInstance(scheme, repo); + const parentCommitSha = commit?.parents?.[0] || ''; + const baseRootUri = router.buildUri({ ref: parentCommitSha }); + const headRootUri = router.buildUri({ ref: commit.sha }, baseRootUri); + + const repository = Repository.getCurrentInstance(); const changedFiles = await repository.getCommitChangedFiles(commit.sha); return changedFiles.map((commitFile) => { @@ -74,26 +58,25 @@ export const getCommitChangedFiles = async (commit: adapterTypes.Commit) => { const baseFilePath = commitFile.previousPath || commitFile.path; const headFilePath = commitFile.path; return { - baseFileUri: vscode.Uri.joinPath(baseRootUri, baseFilePath), - headFileUri: vscode.Uri.joinPath(headRootUri, headFilePath), + baseFileUri: baseRootUri.with({ path: baseFilePath }), + headFileUri: headRootUri.with({ path: headFilePath }), status: commitFile.status, }; }); }; export const getChangedFiles = async (): Promise => { - const routerState = await router.getState(); - const scheme = adapterManager.getCurrentScheme(); + const routerState = router.getState(); // code review page if (routerState.pageType === adapterTypes.PageType.CodeReview) { - const repository = Repository.getInstance(scheme, routerState.repo); + const repository = Repository.getInstance(routerState.scheme, routerState.repo); const codeReview = await repository.getCodeReviewItem(routerState.codeReviewId); return codeReview ? getCodeReviewChangedFiles(codeReview) : []; } // commit page else if (routerState.pageType === adapterTypes.PageType.Commit) { - const repository = Repository.getInstance(scheme, routerState.repo); + const repository = Repository.getInstance(routerState.scheme, routerState.repo); const commit = await repository.getCommitItem(routerState.commitSha); return commit ? getCommitChangedFiles(commit) : []; } @@ -108,8 +91,8 @@ export const getChangedFileDiffTitle = ( ) => { const baseFileName = basename(baseFileUri.path); const headFileName = basename(headFileUri.path); - const [_repo, baseCommitSha] = baseFileUri.authority.split('+'); - const [__repo, headCommitSha] = headFileUri.authority.split('+'); + const { ref: baseCommitSha } = router.parseUri(baseFileUri); + const { ref: headCommitSha } = router.parseUri(headFileUri); const baseFileLabel = `${baseFileName} (${baseCommitSha?.slice(0, 7)})`; const headFileLabel = `${headFileName} (${headCommitSha?.slice(0, 7)})`; diff --git a/extensions/github1s/src/changes/index.ts b/extensions/github1s/src/changes/index.ts index 8847d5b5e..4a3456f8b 100644 --- a/extensions/github1s/src/changes/index.ts +++ b/extensions/github1s/src/changes/index.ts @@ -7,11 +7,9 @@ import * as vscode from 'vscode'; import * as adapterTypes from '@/adapters/types'; import { GitHub1sQuickDiffProvider } from './quick-diff'; import { getChangedFileDiffCommand, getChangedFiles } from './files'; -import adapterManager from '@/adapters/manager'; export const updateSourceControlChanges = (() => { - const rootUri = vscode.Uri.parse('').with({ scheme: adapterManager.getCurrentScheme() }); - const sourceControl = vscode.scm.createSourceControl('github1s', 'GitHub1s', rootUri); + const sourceControl = vscode.scm.createSourceControl('github1s', 'GitHub1s'); const changesGroup = sourceControl.createResourceGroup('changes', 'Changes'); sourceControl.quickDiffProvider = new GitHub1sQuickDiffProvider(); @@ -20,7 +18,7 @@ export const updateSourceControlChanges = (() => { changesGroup.resourceStates = changedFiles.map((changedFile) => { return { - resourceUri: changedFile.headFileUri, + resourceUri: changedFile.headFileUri.with({ authority: '' }), decorations: { strikeThrough: changedFile.status === adapterTypes.FileChangeStatus.Removed, tooltip: changedFile.status, diff --git a/extensions/github1s/src/changes/quick-diff.ts b/extensions/github1s/src/changes/quick-diff.ts index 507b89b38..d620b218c 100644 --- a/extensions/github1s/src/changes/quick-diff.ts +++ b/extensions/github1s/src/changes/quick-diff.ts @@ -12,11 +12,9 @@ import * as adapterTypes from '@/adapters/types'; // get the original source uri when the `routerState.pageType` is `PageType.PULL` const getOriginalResourceForPull = async (uri: vscode.Uri, codeReviewId: string): Promise => { - const routeState = await router.getState(); - const currentScheme = adapterManager.getCurrentScheme(); - const repository = Repository.getInstance(currentScheme, routeState.repo); + const repository = await Repository.getCurrentInstance(); const codeReviewFiles = await repository.getCodeReviewChangedFiles(codeReviewId); - const changedFile = codeReviewFiles?.find((changedFile) => changedFile.path === uri.path.slice(1)); + const changedFile = codeReviewFiles?.find((changedFile) => changedFile.path === uri.path); if ( !changedFile || @@ -31,19 +29,17 @@ const getOriginalResourceForPull = async (uri: vscode.Uri, codeReviewId: string) return null; } - const originalAuthority = `${routeState.repo}+${codeReview!.targetSha}`; - const originalPath = changedFile.previousPath ? `/${changedFile.previousPath}` : uri.path; - - return uri.with({ authority: originalAuthority, path: originalPath }); + return router.buildUri({ + ref: codeReview.targetSha, + path: changedFile.previousPath || uri.path, + }); }; // get the original source uri when the `routerState.pageType` is `PageType.COMMIT` const getOriginalResourceForCommit = async (uri: vscode.Uri, commitSha: string) => { - const routeState = await router.getState(); - const currentScheme = adapterManager.getCurrentScheme(); - const repository = Repository.getInstance(currentScheme, routeState.repo); + const repository = Repository.getCurrentInstance(); const commitFiles = await repository.getCommitChangedFiles(commitSha); - const changedFile = commitFiles?.find((changedFile) => changedFile.path === uri.path.slice(1)); + const changedFile = commitFiles?.find((changedFile) => changedFile.path === uri.path); if ( !changedFile || @@ -59,33 +55,28 @@ const getOriginalResourceForCommit = async (uri: vscode.Uri, commitSha: string) return emptyFileUri; } - const originalAuthority = `${routeState.repo}+${parentCommitSha}`; - const originalPath = changedFile.previousPath ? `/${changedFile.previousPath}` : uri.path; - - return uri.with({ authority: originalAuthority, path: originalPath }); + return router.buildUri({ + ref: parentCommitSha, + path: changedFile.previousPath || uri.path, + }); }; export class GitHub1sQuickDiffProvider implements vscode.QuickDiffProvider { provideOriginalResource(uri: vscode.Uri, _token: vscode.CancellationToken): vscode.ProviderResult { - if (uri.scheme !== adapterManager.getCurrentScheme()) { + const routerState = router.getState(); + // only the file belong to current workspace could be provided a quick diff + if (uri.scheme !== routerState.scheme || uri.authority) { return null; } - return router.getState().then(async (routerState) => { - // only the file belong to current authority could be provided a quick diff - if (uri.authority && uri.authority !== (await router.getAuthority())) { - return null; - } - - if (routerState.pageType === adapterTypes.PageType.CodeReview) { - return getOriginalResourceForPull(uri, routerState.codeReviewId); - } + if (routerState.pageType === adapterTypes.PageType.CodeReview) { + return getOriginalResourceForPull(uri, routerState.codeReviewId); + } - if (routerState.pageType === adapterTypes.PageType.Commit) { - return getOriginalResourceForCommit(uri, routerState.commitSha); - } + if (routerState.pageType === adapterTypes.PageType.Commit) { + return getOriginalResourceForCommit(uri, routerState.commitSha); + } - return null; - }); + return null; } } diff --git a/extensions/github1s/src/commands/blame.ts b/extensions/github1s/src/commands/blame.ts index 2c67eb155..837a94997 100644 --- a/extensions/github1s/src/commands/blame.ts +++ b/extensions/github1s/src/commands/blame.ts @@ -195,18 +195,16 @@ class EditorGitBlame { } async getBlameRanges(): Promise { - const filePath = this.editor.document?.uri.path; - const fileAuthority = this.editor.document?.uri.authority || (await router.getAuthority()); - const [repo, ref] = fileAuthority.split('+').filter(Boolean); - const scheme = adapterManager.getCurrentScheme(); + const { scheme, repo, ref, path } = router.parseUri(this.editor.document?.uri); const repository = Repository.getInstance(scheme, repo); - return filePath ? repository.getFileBlameRanges(ref, filePath.slice(1)) : []; + return path.length > 1 ? repository.getFileBlameRanges(ref, path) : []; } async open() { this.refreshDisposables.forEach((disposable) => disposable.dispose()); setVSCodeContext('github1s:features:gutterBlame:open', true); - const { platformName } = adapterManager.getCurrentAdapter(); + const { scheme } = router.parseUri(this.editor.document.uri); + const { platformName } = adapterManager.getAdapter(scheme); (await this.getBlameRanges()).forEach((blameRange) => { const hoverMessage = createCommitMessagePreviewMarkdown(blameRange, platformName); diff --git a/extensions/github1s/src/commands/code-review.ts b/extensions/github1s/src/commands/code-review.ts index 77e746ea8..8c943d312 100644 --- a/extensions/github1s/src/commands/code-review.ts +++ b/extensions/github1s/src/commands/code-review.ts @@ -44,10 +44,10 @@ const commandSwitchToCodeReview = async (codeReviewItemOrId?: string | CodeRevie ? codeReviewItemOrId : codeReviewItemOrId.codeReview.id : ''; + const { repo } = router.getState(); const adapter = adapterManager.getCurrentAdapter(); - const { repo } = await router.getState(); const typeName = CodeReviewTypeName[adapter.codeReviewType || CodeReviewType.CodeReview]; - const repository = Repository.getInstance(adapter.scheme, repo); + const repository = Repository.getCurrentInstance(); // if the a codeReviewId isn't provided, use quickInput if (!codeReviewId) { @@ -90,7 +90,7 @@ const commandSwitchToCodeReview = async (codeReviewItemOrId?: string | CodeRevie } } - const routerParser = await router.resolveParser(); + const routerParser = router.getParser(); (await checkCodeReviewExists(repo, codeReviewId!)) && router.replace(await routerParser.buildCodeReviewPath(repo, codeReviewId!)); }; @@ -103,8 +103,8 @@ const commandOpenCodeReviewOnOfficialPage = async (codeReviewItemOrId?: string | : codeReviewItemOrId.codeReview.id : ''; if (codeReviewId) { - const { repo } = await router.getState(); - const routerParser = await router.resolveParser(); + const { repo } = router.getState(); + const routerParser = router.getParser(); const codeReviewPath = await routerParser.buildCodeReviewPath(repo, codeReviewId); const codeReviewLink = await routerParser.buildExternalLink(codeReviewPath); return vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(codeReviewLink)); diff --git a/extensions/github1s/src/commands/commit.ts b/extensions/github1s/src/commands/commit.ts index 1ae1ccdb2..d7fa85d42 100644 --- a/extensions/github1s/src/commands/commit.ts +++ b/extensions/github1s/src/commands/commit.ts @@ -31,9 +31,8 @@ const commandSwitchToCommit = async (commitItemOrSha?: string | CommitTreeItem) ? commitItemOrSha : commitItemOrSha.commit.sha : ''; - const adapter = adapterManager.getCurrentAdapter(); - const { repo } = await router.getState(); - const repository = Repository.getInstance(adapter.scheme, repo); + const { repo } = router.getState(); + const repository = Repository.getCurrentInstance(); // if the a commitSha isn't provided, use quickInput if (!commitSha) { @@ -76,7 +75,7 @@ const commandSwitchToCommit = async (commitItemOrSha?: string | CommitTreeItem) } } - const routerParser = await router.resolveParser(); + const routerParser = router.getParser(); if (await checkCommitExists(repo, commitSha!)) { router.replace(await routerParser.buildCommitPath(repo, commitSha!)); } @@ -87,12 +86,11 @@ const commandDiffCommitFile = async (commitItem: CommitTreeItem) => { if (!commitSha) { return; } - const { repo } = await router.getState(); const activeDocumentUri = vscode.window.activeTextEditor?.document?.uri; - const fileUri = activeDocumentUri?.with({ - authority: `${repo}+${commitSha}`, - query: '', - }); + if (!activeDocumentUri) { + return; + } + const fileUri = router.buildUri({ ref: commitSha }, activeDocumentUri).with({ query: '' }); return vscode.commands.executeCommand('github1s.commands.openFilePreviousRevision', fileUri); }; @@ -104,8 +102,8 @@ const commandOpenCommitOnOfficialPage = async (commitItemOrSha?: string | Commit : commitItemOrSha.commit.sha : ''; if (commitSha) { - const { repo } = await router.getState(); - const routerParser = await router.resolveParser(); + const { repo } = router.getState(); + const routerParser = router.getParser(); const commitPath = await routerParser.buildCommitPath(repo, commitSha); const commitLink = await routerParser.buildExternalLink(commitPath); return vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(commitLink)); diff --git a/extensions/github1s/src/commands/editor.ts b/extensions/github1s/src/commands/editor.ts index 80c793ae7..24db76644 100644 --- a/extensions/github1s/src/commands/editor.ts +++ b/extensions/github1s/src/commands/editor.ts @@ -7,7 +7,6 @@ import * as vscode from 'vscode'; import * as queryString from 'query-string'; import router from '@/router'; import { emptyFileUri } from '@/providers'; -import { basename } from '@/helpers/util'; import { FileChangeStatus } from '@/adapters/types'; import { Repository } from '@/repository'; import { getChangedFiles, getChangedFileDiffCommand, getChangedFileDiffTitle } from '@/changes/files'; @@ -37,21 +36,7 @@ const commandDiffChangedFile = async (fileUri: vscode.Uri) => { }; const openFileToEditor = async (fileUri) => { - const isCurrentAuthority = fileUri.authority === (await router.getAuthority()); - - // In order to make the file explorer focus corresponding file when - // the `fileUri.authority` equals `current authority`, set the - // `fileUri.authority` to '' in this case - const targetFileUri = isCurrentAuthority ? fileUri.with({ authority: '' }) : fileUri; - - let editorLabel: string | undefined = undefined; - if (!isCurrentAuthority) { - // the authority here should be `{repo}+{commitSha}` - const [_repo, commitSha] = targetFileUri.authority.split('+'); - editorLabel = `${basename(targetFileUri.path)} (${commitSha.slice(0, 7)})`; - } - - return vscode.commands.executeCommand('vscode.open', targetFileUri, { preview: false }, editorLabel); + return vscode.commands.executeCommand('vscode.open', fileUri, { preview: false }); }; // open the left file in the diff editor title @@ -69,14 +54,12 @@ const commandDiffViewOpenRightFile = async (fileUri: vscode.Uri) => { // get the file uri with the concrete commit sha, the `ref` in // `fileUri.authority` maybe newer but not related this file const getConcreteFileUri = async (fileUri: vscode.Uri) => { - // the `fileUri.authority` maybe empty, fallback to router.getAuthority() in this case - const fileAuthority = fileUri.authority || (await router.getAuthority()); - const [repo, ref] = fileAuthority.split('+').filter(Boolean); - const repository = Repository.getInstance(fileUri.scheme, repo); - const commit = await repository.getFileLatestCommit(ref, fileUri.path.slice(1)); - const latestCommitSha = commit?.sha || (await repository.getCommitItem(ref))?.sha || 'HEAD'; - - return fileUri.with({ authority: `${repo}+${latestCommitSha}` }); + const { ref, path } = router.parseUri(fileUri); + const repository = Repository.getInstanceByUri(fileUri); + const commit = await repository.getFileLatestCommit(ref, path); + const latestCommitSha = commit?.sha || (await repository.getCommitItem(ref))?.sha; + + return router.buildUri({ ref: latestCommitSha }, fileUri); }; // show the file's diff between current commit and previous commit @@ -87,15 +70,15 @@ const commandOpenFilePreviousRevision = async (fileUri: vscode.Uri) => { // a normal file editor (not a diff editor), just use `fileUri` in this case queryBaseUriStr ? vscode.Uri.parse(queryBaseUriStr as string) : fileUri, ); - const [repo, rightCommitSha] = rightFileUri.authority.split('+').filter(Boolean); + const { repo, ref: rightCommitSha } = router.parseUri(rightFileUri); - const repository = Repository.getInstance(fileUri.scheme, repo); - const leftCommit = await repository.getPreviousCommit(rightCommitSha, fileUri.path.slice(1)); + const repository = Repository.getInstanceByUri(rightFileUri); + const leftCommit = await repository.getPreviousCommit(rightCommitSha, rightFileUri.path); // if we can't find previous commit, use the `emptyFileUri` as the leftFileUri - const leftFileUri = leftCommit ? rightFileUri.with({ authority: `${repo}+${leftCommit.sha}` }) : emptyFileUri; + const leftFileUri = leftCommit ? router.buildUri({ ref: leftCommit.sha }, rightFileUri) : emptyFileUri; const changedStatus = leftCommit ? FileChangeStatus.Modified : FileChangeStatus.Added; - const hasNextRevision = !!(await repository.getNextCommit(rightCommitSha, rightFileUri.path.slice(1))); + const hasNextRevision = !!(await repository.getNextCommit(rightCommitSha, rightFileUri.path)); const query = queryString.stringify({ base: leftFileUri.with({ query: '' }).toString(), @@ -118,16 +101,16 @@ const commandOpenFilePreviousRevision = async (fileUri: vscode.Uri) => { const commandOpenFileNextRevision = async (fileUri: vscode.Uri) => { const leftFileUri = await getConcreteFileUri(fileUri); - const [repo, leftCommitSha] = leftFileUri.authority.split('+').filter(Boolean); - const repository = Repository.getInstance(fileUri.scheme, repo); - const rightCommit = await repository.getNextCommit(leftCommitSha, fileUri.path.slice(1)); + const { ref: leftCommitSha } = router.parseUri(leftFileUri); + const repository = Repository.getInstanceByUri(leftFileUri); + const rightCommit = await repository.getNextCommit(leftCommitSha, leftFileUri.path); if (!rightCommit) { return vscode.window.showInformationMessage('There is no next commit found.'); } - const rightFileUri = leftFileUri.with({ authority: `${repo}+${rightCommit.sha}` }); - const hasNextRevision = !!(await repository.getNextCommit(rightCommit.sha, rightFileUri.path.slice(1))); + const rightFileUri = router.buildUri({ ref: rightCommit.sha }, leftFileUri); + const hasNextRevision = !!(await repository.getNextCommit(rightCommit.sha, rightFileUri.path)); const query = queryString.stringify({ base: leftFileUri.with({ query: '' }).toString(), diff --git a/extensions/github1s/src/commands/global.ts b/extensions/github1s/src/commands/global.ts index 25c106553..72448990a 100644 --- a/extensions/github1s/src/commands/global.ts +++ b/extensions/github1s/src/commands/global.ts @@ -10,8 +10,8 @@ import { getRecentRepositories, removeRecentRepository } from '@/helpers/context import { adapterManager } from '@/adapters'; export const commandOpenOnOfficialPage = async () => { - const location = (await router.getHistory()).location; - const routerParser = await router.resolveParser(); + const location = router.getHistory().location; + const routerParser = router.getParser(); const fullPath = `${location.pathname}${location.search}${location.hash}`; const externalLink = await routerParser.buildExternalLink(fullPath); @@ -60,7 +60,7 @@ export const commandOpenRepository = async () => { const choice = quickPick.activeItems[0]; const repository = choice === manualInputItem ? quickPick.value : choice.label; const targetLink = vscode.Uri.parse((await router.href()) || '').with({ - path: await (await router.resolveParser()).buildTreePath(repository), + path: await router.getParser().buildTreePath(repository), }); vscode.commands.executeCommand('vscode.open', targetLink); quickPick.hide(); diff --git a/extensions/github1s/src/commands/ref.ts b/extensions/github1s/src/commands/ref.ts index 08b68cee0..4d160cab4 100644 --- a/extensions/github1s/src/commands/ref.ts +++ b/extensions/github1s/src/commands/ref.ts @@ -20,8 +20,8 @@ const checkoutToItem: vscode.QuickPickItem = { // check out to branch/tag/commit const commandCheckoutTo = async () => { - const routerParser = await router.resolveParser(); - const routeState = await router.getState(); + const routerParser = router.getParser(); + const routeState = router.getState(); const quickPick = vscode.window.createQuickPick(); const loadMoreRefPickerItems = async () => { diff --git a/extensions/github1s/src/extension.ts b/extensions/github1s/src/extension.ts index adc332673..15e742514 100644 --- a/extensions/github1s/src/extension.ts +++ b/extensions/github1s/src/extension.ts @@ -49,15 +49,11 @@ export async function activate(context: vscode.ExtensionContext) { // initialize the VSCode's state according to the router url const initialVSCodeState = async () => { - const routerState = await router.getState(); - const scheme = adapterManager.getCurrentScheme(); + const routerState = router.getState(); - if (routerState.pageType === PageType.Tree && routerState.filePath) { - vscode.commands.executeCommand( - 'revealInExplorer', - vscode.Uri.parse('').with({ scheme, path: `/${routerState.filePath}` }), - ); - } else if (routerState.pageType === PageType.Blob && routerState.filePath) { + if (routerState.pageType === PageType.Tree && routerState.filePath !== '/') { + vscode.commands.executeCommand('revealInExplorer', router.buildUri({ path: routerState.filePath })); + } else if (routerState.pageType === PageType.Blob && routerState.filePath !== '/') { const { startLine, endLine } = routerState; let documentShowOptions: vscode.TextDocumentShowOptions = {}; if (startLine || endLine) { @@ -65,10 +61,7 @@ const initialVSCodeState = async () => { const endPosition = new vscode.Position((endLine || startLine)! - 1, 1 << 20); documentShowOptions = { selection: new vscode.Range(startPosition, endPosition) }; } - vscode.window.showTextDocument( - vscode.Uri.parse('').with({ scheme, path: `/${routerState.filePath}` }), - documentShowOptions, - ); + vscode.window.showTextDocument(router.buildUri({ path: routerState.filePath }), documentShowOptions); } else if (routerState.pageType === PageType.CodeReviewList) { vscode.commands.executeCommand('github1s.views.codeReviewList.focus'); } else if (routerState.pageType === PageType.CommitList) { diff --git a/extensions/github1s/src/helpers/submodule.ts b/extensions/github1s/src/helpers/submodule.ts index 91e5c5f00..0ac890b40 100644 --- a/extensions/github1s/src/helpers/submodule.ts +++ b/extensions/github1s/src/helpers/submodule.ts @@ -3,6 +3,7 @@ * @author netcon */ +import { AdapterManager } from '@/adapters/manager'; import { FileSystemError, Uri } from 'vscode'; // the code below is come from https://github.com/microsoft/vscode/blob/1.52.1/extensions/git/src/git.ts#L661 @@ -73,7 +74,7 @@ export const parseGitmodules = (raw: string): Submodule[] => { return result; }; -export const parseSubmoduleUrl = (url: string) => { +export const parseSubmoduleUrl = async (url: string) => { try { let host = ''; let path = ''; @@ -87,20 +88,19 @@ export const parseSubmoduleUrl = (url: string) => { host = submoduleUri.authority; path = submoduleUri.path; } - let submoduleScheme = 'github1s'; + let subScheme = 'github1s'; if (/\bgithub\.com/i.test(host)) { - submoduleScheme = 'github1s'; + subScheme = 'github1s'; } else if (/\bgitlab\.com/i.test(host)) { - submoduleScheme = 'gitlab1s'; + subScheme = 'gitlab1s'; } else if (/\bbitbucket\.org/i.test(host)) { - submoduleScheme = 'bitbucket1s'; + subScheme = 'bitbucket1s'; } else { throw FileSystemError.Unavailable('only github submodules are supported now'); } - const [submoduleOwner, submoduleRepoPart] = path.split('/').filter(Boolean); - // if there are a repo which the name endsWith '.git' (likes conwnet/demo.git), this ambiguity may cause a problem - const submoduleRepo = submoduleRepoPart.endsWith('.git') ? submoduleRepoPart.slice(0, -4) : submoduleRepoPart; - return [submoduleScheme, `${submoduleOwner}/${submoduleRepo}`]; + + const subRepoPath = path.endsWith('.git') ? path.slice(0, -4) : path; + return [subScheme, subRepoPath.split('/').filter(Boolean).join('/')]; } catch (e) { throw FileSystemError.Unavailable('Can not found valid submodule declare'); } diff --git a/extensions/github1s/src/helpers/util.ts b/extensions/github1s/src/helpers/util.ts index a1a15c29e..bf491fca9 100644 --- a/extensions/github1s/src/helpers/util.ts +++ b/extensions/github1s/src/helpers/util.ts @@ -5,6 +5,7 @@ export const noop = () => {}; export const isNil = (value: any) => value === undefined || value === null; +export const isString = (value: any) => typeof value === 'string'; export const trimStart = (str: string, chars: string = ' '): string => { let index = 0; @@ -32,11 +33,19 @@ export const joinPath = (...segments: string[]): string => { }); }; +export const normalizePath = (path: string): string => (path.startsWith('/') ? path : `/${path}`); + +export const concatPath = (basePath: string, path: string): string => { + return joinPath(normalizePath(basePath), path); +}; + export const dirname = (path: string): string => { const trimmedPath = trimEnd(path, '/'); return trimmedPath.substr(0, trimmedPath.lastIndexOf('/')) || ''; }; +export const getFileTreeItemDescription = (path: string): string | boolean => dirname(path) || false; + export const basename = (path: string): string => { const trimmedPath = trimEnd(path, '/'); return trimmedPath.substr(trimmedPath.lastIndexOf('/') + 1) || ''; @@ -56,10 +65,3 @@ export const prop = (obj: object, path: (string | number)[] = []): any => { export const last = (array: readonly T[]): T => { return array[array.length - 1]; }; - -export const encodeFilePath = (filePath: string): string => { - return filePath - .split('/') - .map((segment) => encodeURIComponent(segment)) - .join('/'); -}; diff --git a/extensions/github1s/src/listeners/router/explorer.ts b/extensions/github1s/src/listeners/router/explorer.ts index b129f5c29..082e53433 100644 --- a/extensions/github1s/src/listeners/router/explorer.ts +++ b/extensions/github1s/src/listeners/router/explorer.ts @@ -42,6 +42,6 @@ export const explorerRouterListener = (currentState: RouterState, previousState: GitHub1sChangedFileDecorationProvider.getInstance().updateDecorations(); GitHub1sSubmoduleDecorationProvider.getInstance().updateDecorations(); GitHub1sSourceControlDecorationProvider.getInstance().updateDecorations(); - GitHub1sFileSearchProvider.getInstance().loadFilesForCurrentAuthority(); + GitHub1sFileSearchProvider.getInstance().loadFilesForCurrentWorkspace(); } }; diff --git a/extensions/github1s/src/listeners/vscode.ts b/extensions/github1s/src/listeners/vscode.ts index 6f96d4a5f..d0c867e85 100644 --- a/extensions/github1s/src/listeners/vscode.ts +++ b/extensions/github1s/src/listeners/vscode.ts @@ -13,9 +13,9 @@ import { adapterManager } from '@/adapters'; const handleRouterOnActiveEditorChange = async (editor: vscode.TextEditor | undefined) => { // replace current url when user change active editor - const { repo, ref, pageType } = await router.getState(); + const { repo, ref, pageType } = router.getState(); const activeFileUri = editor?.document.uri; - const routerParser = await router.resolveParser(); + const routerParser = router.getParser(); // only `tree/blob` page will replace url with the active editor change if (![PageType.Tree, PageType.Blob].includes(pageType)) { @@ -32,7 +32,7 @@ const handleRouterOnActiveEditorChange = async (editor: vscode.TextEditor | unde return; } - const browserPath = await routerParser.buildBlobPath(repo, ref, activeFileUri.path.slice(1)); + const browserPath = await routerParser.buildBlobPath(repo, ref, activeFileUri.path); router.replace(browserPath); }; @@ -50,8 +50,8 @@ const handlegutterBlameOpenContextOnActiveEditorChange = async () => { // add the line number anchor when user selection lines in a editor const handleRouterOnTextEditorSelectionChange = async (editor: vscode.TextEditor) => { - const { repo, ref, pageType } = await router.getState(); - const routerParser = await router.resolveParser(); + const { repo, ref, pageType } = router.getState(); + const routerParser = router.getParser(); // only add the line number anchor when pageType is PageType.Blob if (pageType !== PageType.Blob || !editor?.selection) { @@ -62,12 +62,12 @@ const handleRouterOnTextEditorSelectionChange = async (editor: vscode.TextEditor const browserPath = await routerParser.buildBlobPath( repo, ref, - activeFileUri.path.slice(1), + activeFileUri.path, !editor.selection.isEmpty ? editor.selection.start.line + 1 : undefined, editor.selection.end.line !== editor.selection.start.line ? editor.selection.end.line + 1 : undefined, ); - browserPath !== (await router.getPath()) && router.replace(browserPath); + browserPath !== router.getPath() && router.replace(browserPath); }; // refresh file history view if active editor changed diff --git a/extensions/github1s/src/messages.ts b/extensions/github1s/src/messages.ts index 00fc06b3d..8851abfa3 100644 --- a/extensions/github1s/src/messages.ts +++ b/extensions/github1s/src/messages.ts @@ -14,7 +14,7 @@ export const showSourcegraphSearchMessage = (() => { return; } alreadyShown = true; - const { repo, ref } = await router.getState(); + const { repo, ref } = router.getState(); const url = `https://sourcegraph.com/github.com/${repo}@${ref}`; vscode.window.showInformationMessage(`The code search ability is powered by [Sourcegraph](${url})`); }; diff --git a/extensions/github1s/src/providers/decorations/changed-file.ts b/extensions/github1s/src/providers/decorations/changed-file.ts index f9ee6da14..b7a8cedbe 100644 --- a/extensions/github1s/src/providers/decorations/changed-file.ts +++ b/extensions/github1s/src/providers/decorations/changed-file.ts @@ -43,7 +43,7 @@ export const changedFileDecorationDataMap: { [key: string]: FileDecoration } = { }; const getFileDecorationFromChangeFiles = (uri: Uri, changedFiles: ChangedFile[]): FileDecoration | null => { - const changedFile = changedFiles.find((changedFile) => changedFile.path === uri.path.slice(1)); + const changedFile = changedFiles.find((changedFile) => changedFile.path === uri.path); if (changedFile) { return changedFileDecorationDataMap[changedFile.status]; @@ -51,7 +51,7 @@ const getFileDecorationFromChangeFiles = (uri: Uri, changedFiles: ChangedFile[]) // we have to determine the changed folder manually rather then use // the `propagate` property of FileDecoration, because the file tree // in the file explorer is lazy load - const folderPath = `${uri.path.slice(1)}/`; + const folderPath = uri.path.endsWith('/') ? uri.path : `${uri.path}/`; const includeChangedFile = changedFiles.find((changedFile) => changedFile.path.startsWith(folderPath)); if (includeChangedFile) { return { @@ -63,15 +63,13 @@ const getFileDecorationFromChangeFiles = (uri: Uri, changedFiles: ChangedFile[]) }; const getFileDecorationForCodeReview = async (uri: Uri, codeReviewId: string): Promise => { - const [repo] = (uri.authority || (await router.getAuthority()))?.split('+') || []; - const repository = Repository.getInstance(uri.scheme, repo); + const repository = Repository.getInstanceByUri(uri); const changedFiles = await repository.getCodeReviewChangedFiles(codeReviewId); return getFileDecorationFromChangeFiles(uri, changedFiles); }; const getFileDecorationForCommit = async (uri: Uri, commitSha: string): Promise => { - const [repo] = (uri.authority || (await router.getAuthority()))?.split('+') || []; - const repository = Repository.getInstance(uri.scheme, repo); + const repository = Repository.getInstanceByUri(uri); const changedFiles = await repository.getCommitChangedFiles(commitSha); return getFileDecorationFromChangeFiles(uri, changedFiles); }; @@ -105,14 +103,13 @@ export class GitHub1sChangedFileDecorationProvider implements FileDecorationProv return null; } - return router.getState().then((routerState) => { - if (routerState.pageType === PageType.CodeReview) { - return getFileDecorationForCodeReview(uri, routerState.codeReviewId); - } - if (routerState.pageType === PageType.Commit) { - return getFileDecorationForCommit(uri, routerState.commitSha); - } - return null; - }); + const routerState = router.getState(); + if (routerState.pageType === PageType.CodeReview) { + return getFileDecorationForCodeReview(uri, routerState.codeReviewId); + } + if (routerState.pageType === PageType.Commit) { + return getFileDecorationForCommit(uri, routerState.commitSha); + } + return null; } } diff --git a/extensions/github1s/src/providers/decorations/source-control.ts b/extensions/github1s/src/providers/decorations/source-control.ts index 7f30b8371..0a7f352ad 100644 --- a/extensions/github1s/src/providers/decorations/source-control.ts +++ b/extensions/github1s/src/providers/decorations/source-control.ts @@ -56,17 +56,15 @@ export class GitHub1sSourceControlDecorationProvider implements FileDecorationPr } if (uri.scheme === GitHub1sSourceControlDecorationProvider.codeReviewSchema) { - return router.getState().then((routerState) => { - const query = queryString.parse(uri.query); - return +(routerState as any).codeReviewId === +query.id! ? selectedViewItemDecoration : null; - }); + const routerState = router.getState(); + const query = queryString.parse(uri.query); + return +(routerState as any).codeReviewId === +query.id! ? selectedViewItemDecoration : null; } if (uri.scheme === GitHub1sSourceControlDecorationProvider.commitSchema) { - return router.getState().then((routerState) => { - const query = queryString.parse(uri.query); - return (routerState as any).commitSha === query.sha ? selectedViewItemDecoration : null; - }); + const routerState = router.getState(); + const query = queryString.parse(uri.query); + return (routerState as any).commitSha === query.sha ? selectedViewItemDecoration : null; } } } diff --git a/extensions/github1s/src/providers/decorations/submodule.ts b/extensions/github1s/src/providers/decorations/submodule.ts index b24dbfd11..080852ad0 100644 --- a/extensions/github1s/src/providers/decorations/submodule.ts +++ b/extensions/github1s/src/providers/decorations/submodule.ts @@ -16,6 +16,7 @@ import { } from 'vscode'; import { GitHub1sFileSystemProvider } from '../file-system'; import { Directory } from '../file-system/types'; +import adapterManager from '@/adapters/manager'; export class GitHub1sSubmoduleDecorationProvider implements FileDecorationProvider, Disposable { private static instance: GitHub1sSubmoduleDecorationProvider | null = null; @@ -49,6 +50,9 @@ export class GitHub1sSubmoduleDecorationProvider implements FileDecorationProvid } provideFileDecoration(uri: Uri, _token: CancellationToken): ProviderResult { + if (!adapterManager.getAllAdapters().some((adapter) => adapter.scheme === uri.scheme)) { + return null; + } return GitHub1sFileSystemProvider.getInstance() .lookup(uri, false) .then((entry) => { diff --git a/extensions/github1s/src/providers/definition.ts b/extensions/github1s/src/providers/definition.ts index b11c92a9b..f78b027ce 100644 --- a/extensions/github1s/src/providers/definition.ts +++ b/extensions/github1s/src/providers/definition.ts @@ -8,6 +8,18 @@ import router from '@/router'; import { showSourcegraphSymbolMessage } from '@/messages'; import adapterManager from '@/adapters/manager'; +export const mapScopeScheme = (scopeScheme: string) => { + if (scopeScheme === 'github') { + return 'github1s'; + } else if (scopeScheme === 'gitlab') { + return 'gitlab1s'; + } else if (scopeScheme === 'bitbucket') { + return 'bitbucket1s'; + } else { + return scopeScheme; + } +}; + export class GitHub1sDefinitionProvider implements vscode.DefinitionProvider, vscode.Disposable { private static instance: GitHub1sDefinitionProvider | null = null; private readonly disposable: vscode.Disposable; @@ -37,12 +49,10 @@ export class GitHub1sDefinitionProvider implements vscode.DefinitionProvider, vs return []; } - const authority = document.uri.authority || (await router.getAuthority()); - const [repo, ref] = authority.split('+').filter(Boolean); - const { scheme, path } = document.uri; + const { scheme, repo, ref, path } = router.parseUri(document.uri); const { line, character } = position; - const dataSource = await adapterManager.getCurrentAdapter().resolveDataSource(); + const dataSource = await adapterManager.getAdapter(scheme).resolveDataSource(); const symbolDefinitions = await dataSource.provideSymbolDefinitions(repo, ref, path, line, character, symbol); if (symbolDefinitions.length) { @@ -50,17 +60,14 @@ export class GitHub1sDefinitionProvider implements vscode.DefinitionProvider, vs } return symbolDefinitions.map(({ scope, path, range }) => { - const isSameRepo = !scope || (scope.scheme === scheme && scope.repo === repo); + const toScheme = mapScopeScheme(scope?.scheme || ''); + const isSameRepo = !scope || (toScheme === scheme && scope.repo === repo); // if the definition target and the searched symbol is in the same // repository, just replace the `document.uri.path` with targetPath // (so that the target file will open with expanding the file explorer) const uri = isSameRepo - ? document.uri.with({ path: `/${path}` }) - : vscode.Uri.parse('').with({ - scheme: scope!.scheme, - authority: `${scope!.repo}+${scope!.ref}`, - path: `/${path}`, - }); + ? document.uri.with({ path }) + : router.buildUri({ scheme: toScheme, repo: scope?.repo, ref: scope?.ref, path }); const { start, end } = range; return { uri, diff --git a/extensions/github1s/src/providers/file-search.ts b/extensions/github1s/src/providers/file-search.ts index d1b9f839f..15a5ca48c 100644 --- a/extensions/github1s/src/providers/file-search.ts +++ b/extensions/github1s/src/providers/file-search.ts @@ -30,7 +30,7 @@ export class GitHub1sFileSearchProvider implements FileSearchProvider, Disposabl // Once we have loaded the files, it will also populate the files into // fileSystemProvider's cache. So after that, we don't have to send // a request when you open the new directory in explorer late - this.loadFilesForCurrentAuthority(); + this.loadFilesForCurrentWorkspace(); } public static getInstance(): GitHub1sFileSearchProvider { @@ -44,27 +44,30 @@ export class GitHub1sFileSearchProvider implements FileSearchProvider, Disposabl this.disposable?.dispose(); } - // load the files for current authority - async loadFilesForCurrentAuthority() { - return this.getFileUris(await router.getAuthority()); + // load the files for current workspace + async loadFilesForCurrentWorkspace() { + return this.getFileUris(); } /** - * Get all files for the repo with specified by `authority`. + * Get all files for the repo with specified for current workspace. * The response of corresponding API maybe truncated, if so, * we should not insert the response to the fileSystemProvider's * cache, and the fuzzy search maybe not work fine */ - getFileUris = reuseable(async (authority: string): Promise => { - if (this.fileUrisMap.has(authority)) { - return this.fileUrisMap.get(authority)!; + getFileUris = reuseable(async (): Promise => { + const currentAdapter = adapterManager.getCurrentAdapter(); + const scheme = currentAdapter.scheme; + const { repo, ref } = router.getState(); + const cacheKey = `${scheme}:${repo}+${ref}`; + + if (this.fileUrisMap.has(cacheKey)) { + return this.fileUrisMap.get(cacheKey)!; } - const [repo, ref] = authority.split('+'); - const currentAdapter = adapterManager.getCurrentAdapter(); const dataSource = await currentAdapter.resolveDataSource(); - const rootDirectoryData = await dataSource.provideDirectory(repo, ref, '', true); - const rootDirectoryUri = Uri.parse('').with({ scheme: currentAdapter.scheme, authority, path: '/' }); + const rootDirectoryData = await dataSource.provideDirectory(repo, ref, '/', true); + const rootDirectoryUri = router.buildUri({ scheme, repo, ref, path: '/' }); // the number of items in the tree array maybe exceeded maximum limit, only // insert the data to fileSystemProvider's cache if `treeData.truncated` is false @@ -77,8 +80,8 @@ export class GitHub1sFileSearchProvider implements FileSearchProvider, Disposabl const fileUris = (rootDirectoryData?.entries || []) .filter((item) => item.type === adapterTypes.FileType.File) - .map((item) => Uri.joinPath(rootDirectoryUri, item.path)); - this.fileUrisMap.set(authority, fileUris); + .map((item) => rootDirectoryUri.with({ path: item.path })); + this.fileUrisMap.set(cacheKey, fileUris); return fileUris; }); @@ -87,8 +90,8 @@ export class GitHub1sFileSearchProvider implements FileSearchProvider, Disposabl _options: FileSearchOptions, _token: CancellationToken, ): ProviderResult { - return router.getAuthority().then(async (authority) => { - return matchSorter(await this.getFileUris(authority), query.pattern); + return new Promise(async (resolve) => { + resolve(matchSorter(await this.getFileUris(), query.pattern)); }); } } diff --git a/extensions/github1s/src/providers/file-system/index.ts b/extensions/github1s/src/providers/file-system/index.ts index 1bbcb1b3e..1ef1c2a97 100644 --- a/extensions/github1s/src/providers/file-system/index.ts +++ b/extensions/github1s/src/providers/file-system/index.ts @@ -64,7 +64,7 @@ export class GitHub1sFileSystemProvider implements FileSystemProvider, Disposabl // insert DirectoryEntry into the cache `this.root` public async populateWithDirectoryEntities(base: Uri, entries: adapterTypes.DirectoryEntry[]) { - const baseDirectory = await this.lookupAsDirectory(base, true); + const baseDirectory = await this.lookupAsDirectory(base.with({ path: '/' }), true); if (!baseDirectory) { return; } @@ -86,17 +86,16 @@ export class GitHub1sFileSystemProvider implements FileSystemProvider, Disposabl } // --- lookup - // ensure the authority field in `the uri of returned entry` is exists public async lookup(uri: Uri, silent: false): Promise; public async lookup(uri: Uri, silent: boolean): Promise; public async lookup(uri: Uri, silent: boolean): Promise { const parts = uri.path.split('/').filter(Boolean); - // if the authority of uri is empty, we should use `current authority` - const authority = uri.authority || (await router.getAuthority()); - if (!this.root.has(authority)) { - this.root.set(authority, createEntry(adapterTypes.FileType.Directory, uri.with({ authority, path: '/' }), '')); + const { scheme, repo, ref } = router.parseUri(uri); + const lookupKey = `${scheme}:${repo}+${ref}`; + if (!this.root.has(lookupKey)) { + this.root.set(lookupKey, createEntry(adapterTypes.FileType.Directory, uri.with({ path: '/' }), '')); } - let entry = this.root.get(authority); + let entry = this.root.get(lookupKey); for (const part of parts) { let child: Entry | undefined; if (entry instanceof Directory) { @@ -147,38 +146,17 @@ export class GitHub1sFileSystemProvider implements FileSystemProvider, Disposabl return this.lookup(uri, false); } - // it used by `@/src/providers/fileDecorationProvider.ts` - // update the uri of a git submodule as directory, which the type of corresponding githubEntry should be `commit`. - // the `directory.uri.authority` and the `directory.uri.path` must belong to the `parent repository` before called. - // and the `directory.name` is the corresponding `directory name` in `parent repository` before called. - // once the function is called successful, the `directory.uri.authority` field, the `directory.uri.path`, - // and the `directory.uri.name` field would be changed to the `submodule repository's`. - // - // so this function could be called only once for a submodule directory, for example: - // - the directory argument before called may looks like: - // { - // uri: { - // scheme: 'github1s', - // authority: 'conwnet+github1s+master', // this is the authority of `parent repository` - // path: '/some/submodule/path' // the corresponding path in `parent repository` - // }, - // name: 'vscode', // the name is the `directory name` of `parent repository` before called - // entries: null, // the entries should be null to indicated we haven't call this for `parent` - // isSubmodule: true, // this Directory must be a submodule - // ...otherFields - // } - // - and the directory argument after called may looks like: - // { - // uri: { - // scheme: 'github1s', - // authority: 'microsoft+vscode+master', // this is the authority of `submodule repository` - // path: '/' // the `path` filed should be '/' to indicated to the root directory of `submodule repository` - // }, - // name: '', // the name is the '' to indicated it is a root directory of `submodule repository` - // entries: Map {...}, // the entries contains the files of `submodule repository` - // isSubmodule: true, // this Directory must be a submodule - // ...otherFields - // } + /** + * Prepares a submodule directory for loading from its own repository. + * + * Before the first read, `directory.uri` and `directory.name` locate the submodule in its parent repository. + * The parent URI may have an empty authority when it belongs to the current workspace. This method resolves + * the matching `.gitmodules` entry, then changes the directory to represent the submodule repository root: + * `directory.uri` receives an explicit repository and ref, and `directory.name` becomes empty. The same + * directory is also registered in `root` under the submodule repository key. + * + * This method does not populate `directory.entries`; `readDirectory` does that after the repository switch. + */ private _updateSubmoduleDirectory = reuseable(async (directory: Directory): Promise<[string, FileType][]> => { // if the directory is not submodule, or it has be called already if (!directory.isSubmodule || directory.entries) { @@ -197,17 +175,14 @@ export class GitHub1sFileSystemProvider implements FileSystemProvider, Disposabl if (!gitmoduleData) { throw FileSystemError.FileNotFound(`can't found corresponding declare in .gitmodules`); } - const [submoduleScheme, submoduleRepo] = parseSubmoduleUrl(gitmoduleData.url); - const submoduleAuthority = `${submoduleRepo}+${directory.sha || 'HEAD'}`; + const subRef = directory.sha || 'HEAD'; + const [subScheme, subRepo] = await parseSubmoduleUrl(gitmoduleData.url); + const lookupKey = `${subScheme}:${subRepo}+${subRef}`; directory.name = ''; // update the name field to '' to indicated it is an root directory // update the uri field to indicated it is belong the `submodule repository` - directory.uri = Uri.parse('').with({ - scheme: submoduleScheme, - authority: submoduleAuthority, - path: '/', - }); + directory.uri = router.buildUri({ scheme: subScheme, repo: subRepo, ref: subRef, path: '/' }); // insert the directory in to this.root map because it indicated another repository - this.root.set(submoduleAuthority, directory); + this.root.set(lookupKey, directory); return []; }); @@ -224,11 +199,11 @@ export class GitHub1sFileSystemProvider implements FileSystemProvider, Disposabl if (parent.isSubmodule) { await this._updateSubmoduleDirectory(parent); } - const [repo, ref] = parent.uri.authority.split('+'); - const path = Uri.joinPath(parent.uri, parent.name).path.slice(1); // delete leading '/' - const dataSource = await this._resolveDataSource(uri.scheme); + const { scheme, repo, ref } = router.parseUri(parent.uri); + const path = Uri.joinPath(parent.uri, parent.name).path; + const dataSource = await adapterManager.getAdapter(scheme).resolveDataSource(); const data = await dataSource.provideDirectory(repo, ref, path, false); - data?.entries && (await this.populateWithDirectoryEntities(uri, data.entries)); + data?.entries && (await this.populateWithDirectoryEntities(parent.uri, data.entries)); return parent.getNameTypePairs(); }, (uri) => uri.toString(), @@ -236,20 +211,15 @@ export class GitHub1sFileSystemProvider implements FileSystemProvider, Disposabl readFile = reuseable( async (uri: Uri): Promise => { - let { scheme, authority, path } = uri; - // if `authority` is same with current, try to find it with `this.lookupAsFile`, - // we can't use `router.getAuthority()` directly because this file may be in submodule - if (authority === workspace.workspaceFolders?.[0].uri.authority) { - const file = (await this.lookupAsFile(uri, false))!; - scheme = file.uri.scheme; - authority = file.uri.authority; - path = joinPath(file.uri.path, file.name); - } - const cacheKey = `${scheme} ${authority} ${path}`; + // If a file belongs to the current workspace, + // check its existence to avoid unnecessary content requests. + // It is efficient for some built-in files like `.vscode/...` + !uri.authority && (await this.lookupAsFile(uri, false)); + const { scheme, repo, ref, path } = router.parseUri(uri); + const cacheKey = `${scheme}:${repo}+${ref}${path}`; if (!this.contentCache.has(cacheKey)) { - const [repo, ref] = authority.split('+'); - const dataSource = await this._resolveDataSource(scheme); - const data = await dataSource.provideFile(repo, ref, path.slice(1)); + const dataSource = await adapterManager.getAdapter(scheme).resolveDataSource(); + const data = await dataSource.provideFile(repo, ref, path); data && this.contentCache.set(cacheKey, data.content); } return this.contentCache.get(cacheKey) || new Uint8Array(); diff --git a/extensions/github1s/src/providers/hover.ts b/extensions/github1s/src/providers/hover.ts index 06d3150ef..4ef875b74 100644 --- a/extensions/github1s/src/providers/hover.ts +++ b/extensions/github1s/src/providers/hover.ts @@ -7,6 +7,7 @@ import * as vscode from 'vscode'; import router from '@/router'; import { getSourcegraphUrl } from '@/helpers/urls'; import { adapterManager } from '@/adapters'; +import { mapScopeScheme } from './definition'; const getSemanticMarkdownSuffix = (sourcegraphUrl: string) => ` @@ -45,11 +46,10 @@ export class GitHub1sHoverProvider implements vscode.HoverProvider, vscode.Dispo symbol: string, ): Promise { const { line, character } = position; - const authority = document.uri.authority || (await router.getAuthority()); - const [repo, ref] = authority.split('+').filter(Boolean); - const dataSource = await adapterManager.getCurrentAdapter().resolveDataSource(); + const { scheme, repo, ref, path } = router.parseUri(document.uri); + const dataSource = await adapterManager.getAdapter(scheme).resolveDataSource(); - const requestParams = [repo, ref, document.uri.path, line, character, symbol] as const; + const requestParams = [repo, ref, path, line, character, symbol] as const; const definitions = await dataSource.provideSymbolDefinitions(...requestParams); if (!definitions.length) { @@ -58,16 +58,13 @@ export class GitHub1sHoverProvider implements vscode.HoverProvider, vscode.Dispo // use the information of first definition as hover context const target = definitions[0]; - const isSameRepo = !target.scope || (target.scope.scheme === document.uri.scheme && target.scope.repo === repo); + const toScheme = mapScopeScheme(target.scope?.scheme || ''); + const isSameRepo = !target.scope || (toScheme === document.uri.scheme && target.scope.repo === repo); // if the definition target and the searched symbol is in the same // repository, just replace the `document.uri.path` with targetPath const targetFileUri = isSameRepo - ? document.uri.with({ path: `/${target.path}` }) - : vscode.Uri.parse('').with({ - scheme: target.scope?.scheme, - authority: `${target.scope?.repo}+${target.scope?.ref}`, - path: `/${target.path}`, - }); + ? document.uri.with({ path: target.path }) + : router.buildUri({ scheme: toScheme, repo: target.scope?.repo, ref: target.scope?.ref, path: target.path }); // open corresponding file with target const textDocument = await vscode.workspace.openTextDocument(targetFileUri); // get the content in `[range.start.line - 2, range.end.line + 2]` lines @@ -91,9 +88,7 @@ export class GitHub1sHoverProvider implements vscode.HoverProvider, vscode.Dispo return null; } - const authority = document.uri.authority || (await router.getAuthority()); - const [repo, ref] = authority.split('+').filter(Boolean); - const path = document.uri.path; + const { scheme, repo, ref, path } = router.parseUri(document.uri); const { line, character } = position; // get the sourcegraph url for current symbol @@ -104,7 +99,7 @@ export class GitHub1sHoverProvider implements vscode.HoverProvider, vscode.Dispo const searchBasedMardownPromise = this.getSearchBasedHover(document, position, symbol); // get the hover result based on sourcegraph lsif - const dataSource = await adapterManager.getCurrentAdapter().resolveDataSource(); + const dataSource = await adapterManager.getAdapter(scheme).resolveDataSource(); const symbolHover = await dataSource.provideSymbolHover(...requestParams); const markdown = symbolHover ? symbolHover.markdown : await searchBasedMardownPromise; diff --git a/extensions/github1s/src/providers/index.ts b/extensions/github1s/src/providers/index.ts index a67ccf303..9da617be5 100644 --- a/extensions/github1s/src/providers/index.ts +++ b/extensions/github1s/src/providers/index.ts @@ -15,11 +15,10 @@ import { GitHub1sSourceControlDecorationProvider } from './decorations/source-co import { GitHub1sDefinitionProvider } from './definition'; import { GitHub1sReferenceProvider } from './reference'; import { GitHub1sHoverProvider } from './hover'; +import router from '@/router'; export const EMPTY_FILE_SCHEME = 'github1s-empty-file'; -export const emptyFileUri = vscode.Uri.parse('').with({ - scheme: EMPTY_FILE_SCHEME, -}); +export const emptyFileUri = vscode.Uri.from({ scheme: EMPTY_FILE_SCHEME }); export const registerVSCodeProviders = () => { const context = getExtensionContext(); diff --git a/extensions/github1s/src/providers/reference.ts b/extensions/github1s/src/providers/reference.ts index 1ef0b5824..9339d5244 100644 --- a/extensions/github1s/src/providers/reference.ts +++ b/extensions/github1s/src/providers/reference.ts @@ -7,6 +7,7 @@ import * as vscode from 'vscode'; import router from '@/router'; import { showSourcegraphSymbolMessage } from '@/messages'; import adapterManager from '@/adapters/manager'; +import { mapScopeScheme } from './definition'; export class GitHub1sReferenceProvider implements vscode.ReferenceProvider, vscode.Disposable { private static instance: GitHub1sReferenceProvider | null = null; @@ -38,12 +39,10 @@ export class GitHub1sReferenceProvider implements vscode.ReferenceProvider, vsco return []; } - const authority = document.uri.authority || (await router.getAuthority()); - const [repo, ref] = authority.split('+').filter(Boolean); - const { scheme, path } = document.uri; + const { scheme, repo, ref, path } = router.parseUri(document.uri); const { line, character } = position; - const dataSource = await adapterManager.getCurrentAdapter().resolveDataSource(); + const dataSource = await adapterManager.getAdapter(scheme).resolveDataSource(); const symbolReferences = await dataSource.provideSymbolReferences(repo, ref, path, line, character, symbol); if (symbolReferences.length) { @@ -51,17 +50,14 @@ export class GitHub1sReferenceProvider implements vscode.ReferenceProvider, vsco } return symbolReferences.map(({ scope, path, range }) => { - const isSameRepo = !scope || (scope.scheme === scheme && scope.repo === repo); + const toScheme = mapScopeScheme(scope?.scheme || ''); + const isSameRepo = !scope || (toScheme === scheme && scope.repo === repo); // if the reference target and the searched symbol is in the same // repository, just replace the `document.uri.path` with targetPath // (so that the target file will open with expanding the file explorer) const uri = isSameRepo - ? document.uri.with({ path: `/${path}` }) - : vscode.Uri.parse('').with({ - scheme: scope!.scheme, - authority: `${scope!.repo}+${scope!.ref}`, - path: `/${path}`, - }); + ? document.uri.with({ path }) + : router.buildUri({ scheme: toScheme, repo: scope?.repo, ref: scope?.ref, path }); const { start, end } = range; return { uri, diff --git a/extensions/github1s/src/providers/text-search.ts b/extensions/github1s/src/providers/text-search.ts index 12b0e3584..7898664d2 100644 --- a/extensions/github1s/src/providers/text-search.ts +++ b/extensions/github1s/src/providers/text-search.ts @@ -36,18 +36,17 @@ export class GitHub1sTextSearchProvider implements vscode.TextSearchProvider, vs progress: vscode.Progress, _token: vscode.CancellationToken, ) { - return router.getAuthority().then(async (authority) => { - const [repo, ref] = authority.split('+'); - const dataSource = await adapterManager.getCurrentAdapter().resolveDataSource(); + return Promise.resolve().then(async () => { + const { scheme, repo, ref } = router.getState(); + const dataSource = await adapterManager.getAdapter(scheme).resolveDataSource(); const searchOptions = { page: 1, pageSize: 100, includes: options.includes, excludes: options.excludes }; const searchResults = await dataSource.provideTextSearchResults(repo, ref, query, searchOptions); - const currentScheme = adapterManager.getCurrentScheme(); (searchResults.results || []).forEach((item) => { // because we set the authority of workspace as '' (on application start) // at src/vs/code/browser/workbench/workbench.ts // so don't specified authority here, or the VS Code won't use the results - const fileUri = vscode.Uri.parse('').with({ scheme: currentScheme, path: `/${item.path}` }); + const fileUri = router.buildUri({ path: item.path }); const ranges = ensureArray(item.ranges).map((range) => createVscodeRange(range)); const previewMatches = ensureArray(item.preview.matches).map((match) => createVscodeRange(match)); const preview = { text: item.preview.text, matches: previewMatches }; diff --git a/extensions/github1s/src/repository/commit-manager.ts b/extensions/github1s/src/repository/commit-manager.ts index 02685e413..ef87e8ad5 100644 --- a/extensions/github1s/src/repository/commit-manager.ts +++ b/extensions/github1s/src/repository/commit-manager.ts @@ -160,8 +160,8 @@ export class CommitManager { if (this._currentPage === 1 && commits.length) { this._latestCommitSha = commits[0].sha; - // also map `this._from` to the first commit if currentPage is 1 and filePath is empty - !this._filePath && CommitManager._commitMap.set(this._from, commits[0]); + // also map `this._from` to the first commit for repository history + this._filePath === '/' && CommitManager._commitMap.set(this._from, commits[0]); } commits.forEach((commit) => { CommitManager._commitMap.set(commit.sha, commit); diff --git a/extensions/github1s/src/repository/index.ts b/extensions/github1s/src/repository/index.ts index 541bf149a..c43109f0c 100644 --- a/extensions/github1s/src/repository/index.ts +++ b/extensions/github1s/src/repository/index.ts @@ -3,11 +3,13 @@ * @author netcon */ +import * as vscode from 'vscode'; import { adapterManager } from '@/adapters'; import { CommitManager } from './commit-manager'; import { CodeReviewManager } from './code-review-manager'; import { BranchTagManager } from './branch-tag-manager'; import { BlameRange } from '@/adapters/types'; +import router from '@/router'; export class Repository { private static instanceMap = new Map(); @@ -24,6 +26,16 @@ export class Repository { return Repository.instanceMap.get(mapKey)!; } + public static getInstanceByUri(uri: vscode.Uri) { + const { scheme, repo } = router.parseUri(uri); + return Repository.getInstance(scheme, repo); + } + + public static getCurrentInstance() { + const routerState = router.getState(); + return Repository.getInstance(routerState.scheme, routerState.repo); + } + private constructor( private _scheme: string, private _repo: string, @@ -65,32 +77,32 @@ export class Repository { return this._branchTagManager.hasMoreTags(...args); } - getCommitList(ref: string = 'HEAD', filePath: string = '', forceUpdate: boolean = false) { + getCommitList(ref: string = 'HEAD', filePath: string = '/', forceUpdate: boolean = false) { return CommitManager.getInstance(this._scheme, this._repo, ref, filePath).getList(forceUpdate); } getCommitItem(ref: string, forceUpdate: boolean = false) { - return CommitManager.getInstance(this._scheme, this._repo, ref, '').getItem(forceUpdate); + return CommitManager.getInstance(this._scheme, this._repo, ref, '/').getItem(forceUpdate); } - loadMoreCommits(ref: string = 'HEAD', filePath: string = '') { + loadMoreCommits(ref: string = 'HEAD', filePath: string = '/') { return CommitManager.getInstance(this._scheme, this._repo, ref, filePath).loadMore(); } - hasMoreCommits(ref: string = 'HEAD', filePath: string = '') { + hasMoreCommits(ref: string = 'HEAD', filePath: string = '/') { return CommitManager.getInstance(this._scheme, this._repo, ref, filePath).hasMore(); } getCommitChangedFiles(ref: string, forceUpdate: boolean = false) { - return CommitManager.getInstance(this._scheme, this._repo, ref, '').getChangedFiles(forceUpdate); + return CommitManager.getInstance(this._scheme, this._repo, ref, '/').getChangedFiles(forceUpdate); } loadMoreCommitChangedFiles(ref: string) { - return CommitManager.getInstance(this._scheme, this._repo, ref, '').loadMoreChangedFiles(); + return CommitManager.getInstance(this._scheme, this._repo, ref, '/').loadMoreChangedFiles(); } hasMoreCommitChangedFiles(ref: string) { - return CommitManager.getInstance(this._scheme, this._repo, ref, '').hasMoreChangedFiles(); + return CommitManager.getInstance(this._scheme, this._repo, ref, '/').hasMoreChangedFiles(); } getFileLatestCommit(ref: string, filePath: string) { diff --git a/extensions/github1s/src/router/index.ts b/extensions/github1s/src/router/index.ts index 43b3e390b..6fa564b1c 100644 --- a/extensions/github1s/src/router/index.ts +++ b/extensions/github1s/src/router/index.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; import { History, createMemoryHistory, parsePath, Action } from 'history'; -import { RouterParser, RouterState } from '@/adapters/types'; +import { Adapter, RouterParser, RouterState } from '@/adapters/types'; import { Barrier } from '@/helpers/async'; import adapterManager from '@/adapters/manager'; import { EventEmitter } from './events'; @@ -16,14 +16,20 @@ export interface UrlManager { replace: (url: string) => void | Promise; } +export interface UriState { + scheme: string; + repo: string; + ref: string; + path: string; +} + export class Router extends EventEmitter { private static instance: Router; private _state: RouterState | null = null; private _history: History | null = null; + private _adapter: Adapter | null = null; private _parser: RouterParser | null = null; - // ensure router has been initialized - private _barrier: Barrier = new Barrier(); private _manager: UrlManager | null = null; public static getInstance() { @@ -34,72 +40,88 @@ export class Router extends EventEmitter { } // initialize the router with current url in browser + // must be called before any other method is called async initialize(urlManager: UrlManager) { this._manager = urlManager; - this._parser = await adapterManager.getCurrentAdapter().resolveRouterParser(); + this._adapter = adapterManager.getCurrentAdapter(); const { path: pathname, query, fragment } = vscode.Uri.parse(await this._manager.href()); const path = pathname + (query ? `?${query}` : '') + (fragment ? `#${fragment}` : ''); + this._parser = await this._adapter.resolveRouterParser(); this._state = await this._parser.parsePath(path); this._history = createMemoryHistory({ initialEntries: [path] }); this._history.listen(async ({ action, location }) => { const prevState = this._state; const targetPath = `${location.pathname}${location.search}${location.hash}`; - const routerParser = await adapterManager.getCurrentAdapter().resolveRouterParser(); this._manager?.[action === Action.Push ? 'push' : 'replace'](targetPath); - this._state = await routerParser.parsePath(targetPath); + this._state = await this._parser!.parsePath(targetPath); super.notifyListeners(this._state, prevState); }); - this._barrier.open(); } // get the routerState for current url - public async getState(): Promise { - await this._barrier.wait(); - return this._state!; - } - - // compute the file URI authority of current routerState - public async getAuthority(): Promise { - const state = await this.getState(); - return `${state.repo}+${state.ref}`; + public getState(): RouterState & { scheme: string } { + return { ...this._state!, scheme: this._adapter!.scheme }; } - public async getHistory() { - await this._barrier.wait(); + public getHistory() { return this._history!; } - public async getPath() { - await this._barrier.wait(); + public getPath() { const { pathname, search, hash } = this._history!.location; return `${pathname}${search}${hash}`; } // push the url with current history - public async push(path: string) { - await this._barrier.wait(); + public push(path: string) { const emptyState = { pathname: '', search: '', hash: '' }; return this._history!.push({ ...emptyState, ...parsePath(encodeURI(path)) }); } // replace the url with current history - public async replace(path: string) { - await this._barrier.wait(); + public replace(path: string) { const emptyState = { pathname: '', search: '', hash: '' }; return this._history!.replace({ ...emptyState, ...parsePath(encodeURI(path)) }); } - public async resolveParser(): Promise { - await this._barrier.wait(); + public getParser(): RouterParser { return this._parser!; } public async href(): Promise { return this._manager?.href(); } + + public parseUri(uri: vscode.Uri): UriState { + const scheme = uri.scheme; + const [repo, ref] = uri.authority ? uri.authority.split('+') : [this._state!.repo, this._state!.ref]; + return { scheme, repo, ref, path: uri.path || '/' }; + } + + public buildUri(state?: Partial, base?: vscode.Uri): vscode.Uri { + const mergedState: Parameters['with']>[0] = {}; + + if (state?.hasOwnProperty('scheme')) { + mergedState.scheme = state.scheme || ''; + } + if (state && state.repo && !state.ref) { + throw new Error('ref is required when repo is provided'); + } + if (state?.hasOwnProperty('ref')) { + const repo = state.repo || base?.authority.split('+')[0] || this._state!.repo; + mergedState.authority = repo && state.ref ? `${repo}+${state.ref}` : ''; + } + if (state?.hasOwnProperty('path')) { + mergedState.path = `/${state.path?.split('/').filter(Boolean).join('/') || ''}`; + } + + return base + ? base.with(mergedState) + : vscode.Uri.from({ scheme: adapterManager.getCurrentScheme(), path: '/', ...mergedState }); + } } export default Router.getInstance(); diff --git a/extensions/github1s/src/statusbar/checkout.ts b/extensions/github1s/src/statusbar/checkout.ts index 2ef40f49f..0125e102f 100644 --- a/extensions/github1s/src/statusbar/checkout.ts +++ b/extensions/github1s/src/statusbar/checkout.ts @@ -10,7 +10,7 @@ export const updateCheckoutTo = (() => { const checkoutItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 100); const refreshItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 90); return async () => { - const { repo, ref } = await router.getState(); + const { repo, ref } = router.getState(); checkoutItem.text = `$(git-branch) ${ref}`; checkoutItem.tooltip = 'Checkout branch/tag/commit...'; diff --git a/extensions/github1s/src/statusbar/sponsors.ts b/extensions/github1s/src/statusbar/sponsors.ts index 6ec592f25..18cd0480d 100644 --- a/extensions/github1s/src/statusbar/sponsors.ts +++ b/extensions/github1s/src/statusbar/sponsors.ts @@ -9,7 +9,7 @@ import { adapterManager } from '@/adapters'; import { PlatformName } from '@/adapters/types'; const resolveSourcegraphLink = async () => { - const { repo, ref } = await router.getState(); + const { repo, ref } = router.getState(); switch (adapterManager.getCurrentAdapter().platformName) { case PlatformName.GitHub: return `https://sourcegraph.com/github.com/${repo}@${ref}`; diff --git a/extensions/github1s/src/views/code-review-list.ts b/extensions/github1s/src/views/code-review-list.ts index 40d79aea6..61c9f7957 100644 --- a/extensions/github1s/src/views/code-review-list.ts +++ b/extensions/github1s/src/views/code-review-list.ts @@ -13,6 +13,7 @@ import adapterManager from '@/adapters/manager'; import * as adapterTypes from '@/adapters/types'; import { getChangedFileDiffCommand, getCodeReviewChangedFiles } from '@/changes/files'; import { GitHub1sSourceControlDecorationProvider } from '@/providers/decorations/source-control'; +import { getFileTreeItemDescription } from '@/helpers/util'; enum CodeReviewState { OPEN = 'open', @@ -115,7 +116,7 @@ export class CodeReviewTreeDataProvider implements vscode.TreeDataProvider { this._loadingBarrier && (await this._loadingBarrier.wait()); const currentScheme = adapterManager.getCurrentScheme(); - const { repo } = await router.getState(); + const { repo } = router.getState(); const repository = Repository.getInstance(currentScheme, repo); const codeReviews = await repository.getCodeReviewList(this._forceUpdate); const codeReviewTreeItems = codeReviews.map((codeReview) => { @@ -152,7 +153,7 @@ export class CodeReviewTreeDataProvider implements vscode.TreeDataProvider { this._loadingBarrier && (await this._loadingBarrier.wait()); - const scheme = adapterManager.getCurrentScheme(); - const { repo } = await router.getState(); - const repository = Repository.getInstance(scheme, repo); + const repository = Repository.getCurrentInstance(); const _codeReview = await repository.getCodeReviewItem(codeReview.id); const changedFiles = _codeReview ? await getCodeReviewChangedFiles(_codeReview) : []; const changedFileItems = changedFiles.map((changedFile) => { @@ -179,7 +178,7 @@ export class CodeReviewTreeDataProvider implements vscode.TreeDataProvider { const shortCommitSha = commit.sha.slice(0, 7); @@ -61,7 +62,7 @@ export class CommitTreeDataProvider implements vscode.TreeDataProvider { @@ -113,7 +114,7 @@ export class CommitTreeDataProvider implements vscode.TreeDataProvider; From 71505a77212b83c90a9cf5e820a41797fd379b82 Mon Sep 17 00:00:00 2001 From: netcon Date: Tue, 11 Aug 2026 20:59:20 +0800 Subject: [PATCH 2/2] chore: optimize import codes (#715) * feat: simplify repository import * feat: simplify adapter import * feat: standardize router state * chore: fix ci * fix: submodule read file --- .github/workflows/build.yml | 2 +- .github/workflows/test-wtih-vscode-build.yml | 13 ++++++++-- extensions/github1s/src/adapters/index.ts | 10 +++++-- extensions/github1s/src/adapters/manager.ts | 10 +++---- extensions/github1s/src/changes/files.ts | 10 +++---- extensions/github1s/src/changes/quick-diff.ts | 6 ++--- extensions/github1s/src/commands/blame.ts | 4 +-- .../github1s/src/commands/code-review.ts | 8 +++--- extensions/github1s/src/commands/commit.ts | 7 +++-- extensions/github1s/src/commands/editor.ts | 13 +++++----- extensions/github1s/src/commands/global.ts | 7 +++-- extensions/github1s/src/commands/ref.ts | 7 ++--- extensions/github1s/src/extension.ts | 4 +-- extensions/github1s/src/helpers/submodule.ts | 1 - extensions/github1s/src/listeners/vscode.ts | 4 +-- .../src/providers/decorations/changed-file.ts | 12 +++++---- .../src/providers/decorations/submodule.ts | 4 +-- .../github1s/src/providers/definition.ts | 4 +-- .../github1s/src/providers/file-search.ts | 10 +++---- .../src/providers/file-system/index.ts | 19 +++++++------- extensions/github1s/src/providers/hover.ts | 6 ++--- extensions/github1s/src/providers/index.ts | 5 ++-- .../github1s/src/providers/reference.ts | 4 +-- .../github1s/src/providers/text-search.ts | 6 ++--- .../src/repository/branch-tag-manager.ts | 10 +++---- .../src/repository/code-review-manager.ts | 8 +++--- .../github1s/src/repository/commit-manager.ts | 8 +++--- extensions/github1s/src/repository/index.ts | 15 +++-------- extensions/github1s/src/router/index.ts | 17 +++++------- extensions/github1s/src/statusbar/sponsors.ts | 4 +-- .../github1s/src/views/code-review-list.ts | 20 +++++--------- extensions/github1s/src/views/commit-list.ts | 26 +++++++------------ extensions/github1s/src/views/index.ts | 4 +-- 33 files changed, 131 insertions(+), 157 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cc265cde4..ed83fe6ef 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,4 +29,4 @@ jobs: - run: npm run eslint - run: npm run build - uses: microsoft/playwright-github-action@v1 - - run: GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} npm run test:ci + - run: npm run test:ci diff --git a/.github/workflows/test-wtih-vscode-build.yml b/.github/workflows/test-wtih-vscode-build.yml index cc4fc5409..d04876a72 100644 --- a/.github/workflows/test-wtih-vscode-build.yml +++ b/.github/workflows/test-wtih-vscode-build.yml @@ -10,6 +10,9 @@ on: jobs: build-with-vscode-build: + permissions: + contents: read + strategy: matrix: os: [macos-14] @@ -19,6 +22,8 @@ jobs: steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 with: @@ -26,7 +31,11 @@ jobs: node-version: ${{ matrix.node-version }} - run: npm install && cd vscode-web && npm install - - run: cd vscode-web && npm run build + - name: Build VS Code web + working-directory: vscode-web + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: npm run build - run: npm run link && npm run build - uses: microsoft/playwright-github-action@v1 - - run: GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} npm run test:ci + - run: npm run test:ci diff --git a/extensions/github1s/src/adapters/index.ts b/extensions/github1s/src/adapters/index.ts index 4a80be861..87fbe9425 100644 --- a/extensions/github1s/src/adapters/index.ts +++ b/extensions/github1s/src/adapters/index.ts @@ -9,7 +9,7 @@ import { GitLab1sAdapter } from './gitlab1s'; import { BitbucketAdapter } from './bitbucket1s'; import { Npmjs1sAdapter } from './npmjs1s'; import { OSSInsightAdapter } from './ossinsight'; -import { DataSource, PlatformName, RouterParser } from './types'; +import { Adapter, DataSource, PlatformName, RouterParser } from './types'; const emptyAdapter = { scheme: 'empty', @@ -29,4 +29,10 @@ export const registerAdapters = async (): Promise => { ]); }; -export { adapterManager }; +export const getAdapter = (scheme?: string): Adapter => { + return adapterManager.getAdapter(scheme); +}; + +export const getAllAdapters = (): Adapter[] => { + return adapterManager.getAllAdapters(); +}; diff --git a/extensions/github1s/src/adapters/manager.ts b/extensions/github1s/src/adapters/manager.ts index 5bb38c430..82a91a060 100644 --- a/extensions/github1s/src/adapters/manager.ts +++ b/extensions/github1s/src/adapters/manager.ts @@ -40,21 +40,17 @@ export class AdapterManager { return Array.from(this.adaptersMap.values()); } - public getAdapter(scheme: string): Adapter { + public getAdapter(scheme?: string): Adapter { + scheme = scheme || this.getCurrentScheme(); if (!this.adaptersMap.has(scheme)) { throw new Error(`Adapter with scheme '${scheme}' can not found.`); } return this.adaptersMap.get(scheme)!; } - public getCurrentScheme(): string { + private getCurrentScheme(): string { return vscode.workspace.workspaceFolders?.[0]?.uri?.scheme || 'empty'; } - - public getCurrentAdapter(): Adapter { - const scheme = this.getCurrentScheme(); - return this.getAdapter(scheme); - } } export default AdapterManager.getInstance(); diff --git a/extensions/github1s/src/changes/files.ts b/extensions/github1s/src/changes/files.ts index 8d4b0ddd7..8ff463660 100644 --- a/extensions/github1s/src/changes/files.ts +++ b/extensions/github1s/src/changes/files.ts @@ -6,7 +6,6 @@ import * as vscode from 'vscode'; import * as queryString from 'query-string'; import * as adapterTypes from '@/adapters/types'; -import adapterManager from '@/adapters/manager'; import router from '@/router'; import { basename } from '@/helpers/util'; import { emptyFileUri } from '@/providers'; @@ -22,10 +21,9 @@ interface VSCodeChangedFile { export const getCodeReviewChangedFiles = async ( codeReview: adapterTypes.CodeReview & { sourceSha: string; targetSha: string }, ) => { + const repository = Repository.getCurrentInstance(); const baseRootUri = router.buildUri({ ref: codeReview.targetSha }); const headRootUri = router.buildUri({ ref: codeReview.sourceSha }, baseRootUri); - - const repository = Repository.getCurrentInstance(); const changedFiles = await repository.getCodeReviewChangedFiles(codeReview.id); return changedFiles.map((changedFile) => { @@ -42,14 +40,13 @@ export const getCodeReviewChangedFiles = async ( }; export const getCommitChangedFiles = async (commit: adapterTypes.Commit) => { + const repository = Repository.getCurrentInstance(); // if the commit.parents is more than one element // the parents[1].sha should be the merge source commitSha // so we use the parents[0].sha as the parent commitSha const parentCommitSha = commit?.parents?.[0] || ''; const baseRootUri = router.buildUri({ ref: parentCommitSha }); const headRootUri = router.buildUri({ ref: commit.sha }, baseRootUri); - - const repository = Repository.getCurrentInstance(); const changedFiles = await repository.getCommitChangedFiles(commit.sha); return changedFiles.map((commitFile) => { @@ -67,16 +64,15 @@ export const getCommitChangedFiles = async (commit: adapterTypes.Commit) => { export const getChangedFiles = async (): Promise => { const routerState = router.getState(); + const repository = Repository.getCurrentInstance(); // code review page if (routerState.pageType === adapterTypes.PageType.CodeReview) { - const repository = Repository.getInstance(routerState.scheme, routerState.repo); const codeReview = await repository.getCodeReviewItem(routerState.codeReviewId); return codeReview ? getCodeReviewChangedFiles(codeReview) : []; } // commit page else if (routerState.pageType === adapterTypes.PageType.Commit) { - const repository = Repository.getInstance(routerState.scheme, routerState.repo); const commit = await repository.getCommitItem(routerState.commitSha); return commit ? getCommitChangedFiles(commit) : []; } diff --git a/extensions/github1s/src/changes/quick-diff.ts b/extensions/github1s/src/changes/quick-diff.ts index d620b218c..a2b2afc88 100644 --- a/extensions/github1s/src/changes/quick-diff.ts +++ b/extensions/github1s/src/changes/quick-diff.ts @@ -5,14 +5,14 @@ import * as vscode from 'vscode'; import router from '@/router'; +import { getAdapter } from '@/adapters'; import { Repository } from '@/repository'; import { emptyFileUri } from '@/providers'; -import adapterManager from '@/adapters/manager'; import * as adapterTypes from '@/adapters/types'; // get the original source uri when the `routerState.pageType` is `PageType.PULL` const getOriginalResourceForPull = async (uri: vscode.Uri, codeReviewId: string): Promise => { - const repository = await Repository.getCurrentInstance(); + const repository = Repository.getCurrentInstance(); const codeReviewFiles = await repository.getCodeReviewChangedFiles(codeReviewId); const changedFile = codeReviewFiles?.find((changedFile) => changedFile.path === uri.path); @@ -65,7 +65,7 @@ export class GitHub1sQuickDiffProvider implements vscode.QuickDiffProvider { provideOriginalResource(uri: vscode.Uri, _token: vscode.CancellationToken): vscode.ProviderResult { const routerState = router.getState(); // only the file belong to current workspace could be provided a quick diff - if (uri.scheme !== routerState.scheme || uri.authority) { + if (uri.scheme !== getAdapter().scheme || uri.authority) { return null; } diff --git a/extensions/github1s/src/commands/blame.ts b/extensions/github1s/src/commands/blame.ts index 837a94997..967228a3e 100644 --- a/extensions/github1s/src/commands/blame.ts +++ b/extensions/github1s/src/commands/blame.ts @@ -8,9 +8,9 @@ import { relativeTimeTo } from '@/helpers/date'; import { last } from '@/helpers/util'; import { setVSCodeContext } from '@/helpers/vscode'; import router from '@/router'; +import { getAdapter } from '@/adapters'; import { Repository } from '@/repository'; import { BlameRange, PlatformName } from '@/adapters/types'; -import { adapterManager } from '@/adapters'; const ageColors = [ '#f66a0a', @@ -204,7 +204,7 @@ class EditorGitBlame { this.refreshDisposables.forEach((disposable) => disposable.dispose()); setVSCodeContext('github1s:features:gutterBlame:open', true); const { scheme } = router.parseUri(this.editor.document.uri); - const { platformName } = adapterManager.getAdapter(scheme); + const platformName = getAdapter(scheme).platformName; (await this.getBlameRanges()).forEach((blameRange) => { const hoverMessage = createCommitMessagePreviewMarkdown(blameRange, platformName); diff --git a/extensions/github1s/src/commands/code-review.ts b/extensions/github1s/src/commands/code-review.ts index 8c943d312..8664749d6 100644 --- a/extensions/github1s/src/commands/code-review.ts +++ b/extensions/github1s/src/commands/code-review.ts @@ -5,6 +5,7 @@ import * as vscode from 'vscode'; import router from '@/router'; +import { getAdapter } from '@/adapters'; import { CodeReviewTreeItem, getCodeReviewTreeItemLabel, @@ -12,7 +13,6 @@ import { } from '@/views/code-review-list'; import { codeReviewRequestTreeDataProvider } from '@/views'; import { CodeReviewType } from '@/adapters/types'; -import { adapterManager } from '@/adapters'; import { Repository } from '@/repository'; const CodeReviewTypeName = { @@ -23,7 +23,7 @@ const CodeReviewTypeName = { }; const checkCodeReviewExists = async (repo: string, codeReviewId: string) => { - const adapter = adapterManager.getCurrentAdapter(); + const adapter = getAdapter(); const dataSoruce = await adapter.resolveDataSource(); try { return !!(await dataSoruce.provideCodeReview(repo, codeReviewId)); @@ -44,10 +44,10 @@ const commandSwitchToCodeReview = async (codeReviewItemOrId?: string | CodeRevie ? codeReviewItemOrId : codeReviewItemOrId.codeReview.id : ''; + const adapter = getAdapter(); const { repo } = router.getState(); - const adapter = adapterManager.getCurrentAdapter(); - const typeName = CodeReviewTypeName[adapter.codeReviewType || CodeReviewType.CodeReview]; const repository = Repository.getCurrentInstance(); + const typeName = CodeReviewTypeName[adapter.codeReviewType || CodeReviewType.CodeReview]; // if the a codeReviewId isn't provided, use quickInput if (!codeReviewId) { diff --git a/extensions/github1s/src/commands/commit.ts b/extensions/github1s/src/commands/commit.ts index d7fa85d42..97e3df402 100644 --- a/extensions/github1s/src/commands/commit.ts +++ b/extensions/github1s/src/commands/commit.ts @@ -5,14 +5,13 @@ import * as vscode from 'vscode'; import router from '@/router'; +import { getAdapter } from '@/adapters'; +import { Repository } from '@/repository'; import { CommitTreeItem, getCommitTreeItemDescription } from '@/views/commit-list'; import { commitTreeDataProvider, fileHistoryTreeDataProvider } from '@/views'; -import { adapterManager } from '@/adapters'; -import { Repository } from '@/repository'; export const checkCommitExists = async (repo: string, commitSha: string) => { - const adapter = adapterManager.getCurrentAdapter(); - const dataSoruce = await adapter.resolveDataSource(); + const dataSoruce = await getAdapter().resolveDataSource(); try { return !!(await dataSoruce.provideCommit(repo, commitSha)); } catch (error) { diff --git a/extensions/github1s/src/commands/editor.ts b/extensions/github1s/src/commands/editor.ts index 24db76644..2eeeb9d94 100644 --- a/extensions/github1s/src/commands/editor.ts +++ b/extensions/github1s/src/commands/editor.ts @@ -54,8 +54,8 @@ const commandDiffViewOpenRightFile = async (fileUri: vscode.Uri) => { // get the file uri with the concrete commit sha, the `ref` in // `fileUri.authority` maybe newer but not related this file const getConcreteFileUri = async (fileUri: vscode.Uri) => { - const { ref, path } = router.parseUri(fileUri); - const repository = Repository.getInstanceByUri(fileUri); + const { scheme, repo, ref, path } = router.parseUri(fileUri); + const repository = Repository.getInstance(scheme, repo); const commit = await repository.getFileLatestCommit(ref, path); const latestCommitSha = commit?.sha || (await repository.getCommitItem(ref))?.sha; @@ -70,9 +70,8 @@ const commandOpenFilePreviousRevision = async (fileUri: vscode.Uri) => { // a normal file editor (not a diff editor), just use `fileUri` in this case queryBaseUriStr ? vscode.Uri.parse(queryBaseUriStr as string) : fileUri, ); - const { repo, ref: rightCommitSha } = router.parseUri(rightFileUri); - - const repository = Repository.getInstanceByUri(rightFileUri); + const { scheme, repo, ref: rightCommitSha } = router.parseUri(rightFileUri); + const repository = Repository.getInstance(scheme, repo); const leftCommit = await repository.getPreviousCommit(rightCommitSha, rightFileUri.path); // if we can't find previous commit, use the `emptyFileUri` as the leftFileUri const leftFileUri = leftCommit ? router.buildUri({ ref: leftCommit.sha }, rightFileUri) : emptyFileUri; @@ -101,8 +100,8 @@ const commandOpenFilePreviousRevision = async (fileUri: vscode.Uri) => { const commandOpenFileNextRevision = async (fileUri: vscode.Uri) => { const leftFileUri = await getConcreteFileUri(fileUri); - const { ref: leftCommitSha } = router.parseUri(leftFileUri); - const repository = Repository.getInstanceByUri(leftFileUri); + const { scheme, repo, ref: leftCommitSha } = router.parseUri(leftFileUri); + const repository = Repository.getInstance(scheme, repo); const rightCommit = await repository.getNextCommit(leftCommitSha, leftFileUri.path); if (!rightCommit) { diff --git a/extensions/github1s/src/commands/global.ts b/extensions/github1s/src/commands/global.ts index 72448990a..ccaa79cdc 100644 --- a/extensions/github1s/src/commands/global.ts +++ b/extensions/github1s/src/commands/global.ts @@ -5,9 +5,9 @@ import * as vscode from 'vscode'; import router from '@/router'; +import { getAdapter } from '@/adapters'; import { relativeTimeTo } from '@/helpers/date'; import { getRecentRepositories, removeRecentRepository } from '@/helpers/context'; -import { adapterManager } from '@/adapters'; export const commandOpenOnOfficialPage = async () => { const location = router.getHistory().location; @@ -68,14 +68,13 @@ export const commandOpenRepository = async () => { }; const commandOpenOnlineEditor = async () => { - const currentScheme = adapterManager.getCurrentScheme(); - const onlineEditorPath = ['github1s', 'ossinsight'].includes(currentScheme) ? '/editor' : '/'; + const onlineEditorPath = ['github1s', 'ossinsight'].includes(getAdapter().scheme) ? '/editor' : '/'; const targetLink = vscode.Uri.parse((await router.href()) || '').with({ path: onlineEditorPath }); return vscode.commands.executeCommand('vscode.open', targetLink); }; const commandRefreshRepository = async () => { - if (['github1s', 'gitlab1s'].includes(adapterManager.getCurrentScheme())) { + if (['github1s', 'gitlab1s'].includes(getAdapter().scheme)) { await vscode.commands.executeCommand('github1s.commands.syncSourcegraphRepository'); } vscode.commands.executeCommand('workbench.action.reloadWindow'); diff --git a/extensions/github1s/src/commands/ref.ts b/extensions/github1s/src/commands/ref.ts index 4d160cab4..fa1174a43 100644 --- a/extensions/github1s/src/commands/ref.ts +++ b/extensions/github1s/src/commands/ref.ts @@ -5,7 +5,6 @@ import * as vscode from 'vscode'; import router from '@/router'; -import { adapterManager } from '@/adapters'; import { Repository } from '@/repository'; const loadMorePickerItem: vscode.QuickPickItem = { @@ -20,14 +19,12 @@ const checkoutToItem: vscode.QuickPickItem = { // check out to branch/tag/commit const commandCheckoutTo = async () => { - const routerParser = router.getParser(); const routeState = router.getState(); + const repository = Repository.getCurrentInstance(); const quickPick = vscode.window.createQuickPick(); const loadMoreRefPickerItems = async () => { quickPick.busy = true; - const scheme = adapterManager.getCurrentScheme(); - const repository = Repository.getInstance(scheme, routeState.repo); await Promise.all([repository.loadMoreBranches(), repository.loadMoreTags()]); const [branchRefs, tagRefs] = await Promise.all([repository.getBranchList(), repository.getTagList()]); const refPickerItems = [...branchRefs, ...tagRefs].map((ref) => ({ @@ -51,7 +48,7 @@ const commandCheckoutTo = async () => { } const selectedRef = choice === checkoutToItem ? quickPick.value : choice?.label; const targetRef = selectedRef.toUpperCase() !== 'HEAD' ? selectedRef : undefined; - router.push(await routerParser.buildTreePath(routeState.repo, targetRef)); + router.push(await router.getParser().buildTreePath(routeState.repo, targetRef)); quickPick.hide(); }); }; diff --git a/extensions/github1s/src/extension.ts b/extensions/github1s/src/extension.ts index 15e742514..7be17e063 100644 --- a/extensions/github1s/src/extension.ts +++ b/extensions/github1s/src/extension.ts @@ -5,14 +5,14 @@ import router from '@/router'; import * as vscode from 'vscode'; -import { PageType } from './adapters/types'; +import { PageType } from '@/adapters/types'; +import { registerAdapters } from '@/adapters'; import { registerCustomViews } from '@/views'; import { decorateStatusBar } from '@/statusbar'; import { registerEventListeners } from '@/listeners'; import { registerVSCodeProviders } from '@/providers'; import { registerGitHub1sCommands } from '@/commands'; import { updateSourceControlChanges } from '@/changes'; -import { adapterManager, registerAdapters } from '@/adapters'; import { addRecentRepositories, setExtensionContext } from '@/helpers/context'; const browserUrlManager = { diff --git a/extensions/github1s/src/helpers/submodule.ts b/extensions/github1s/src/helpers/submodule.ts index 0ac890b40..8b6167937 100644 --- a/extensions/github1s/src/helpers/submodule.ts +++ b/extensions/github1s/src/helpers/submodule.ts @@ -3,7 +3,6 @@ * @author netcon */ -import { AdapterManager } from '@/adapters/manager'; import { FileSystemError, Uri } from 'vscode'; // the code below is come from https://github.com/microsoft/vscode/blob/1.52.1/extensions/git/src/git.ts#L661 diff --git a/extensions/github1s/src/listeners/vscode.ts b/extensions/github1s/src/listeners/vscode.ts index d0c867e85..9fbb311bf 100644 --- a/extensions/github1s/src/listeners/vscode.ts +++ b/extensions/github1s/src/listeners/vscode.ts @@ -5,11 +5,11 @@ import * as vscode from 'vscode'; import router from '@/router'; +import { getAdapter } from '@/adapters'; import { setVSCodeContext } from '@/helpers/vscode'; import { getChangedFileFromSourceControl } from '@/commands/editor'; import { debounce } from '@/helpers/func'; import { PageType } from '@/adapters/types'; -import { adapterManager } from '@/adapters'; const handleRouterOnActiveEditorChange = async (editor: vscode.TextEditor | undefined) => { // replace current url when user change active editor @@ -24,7 +24,7 @@ const handleRouterOnActiveEditorChange = async (editor: vscode.TextEditor | unde // if the file which not belong to current workspace is opened, or no file // is opened, only retain `repo` (and `ref` if need) in browser url - if (!activeFileUri || activeFileUri?.authority || activeFileUri?.scheme !== adapterManager.getCurrentScheme()) { + if (!activeFileUri || activeFileUri?.authority || activeFileUri?.scheme !== getAdapter().scheme) { const browserPath = await (ref.toUpperCase() === 'HEAD' ? routerParser.buildTreePath(repo) : routerParser.buildTreePath(repo, ref)); diff --git a/extensions/github1s/src/providers/decorations/changed-file.ts b/extensions/github1s/src/providers/decorations/changed-file.ts index b7a8cedbe..b891bb063 100644 --- a/extensions/github1s/src/providers/decorations/changed-file.ts +++ b/extensions/github1s/src/providers/decorations/changed-file.ts @@ -15,9 +15,9 @@ import { ThemeColor, } from 'vscode'; import router from '@/router'; -import { ChangedFile, FileChangeStatus, PageType } from '@/adapters/types'; -import { adapterManager } from '@/adapters'; +import { getAdapter } from '@/adapters'; import { Repository } from '@/repository'; +import { ChangedFile, FileChangeStatus, PageType } from '@/adapters/types'; export const changedFileDecorationDataMap: { [key: string]: FileDecoration } = { [FileChangeStatus.Added]: { @@ -63,13 +63,15 @@ const getFileDecorationFromChangeFiles = (uri: Uri, changedFiles: ChangedFile[]) }; const getFileDecorationForCodeReview = async (uri: Uri, codeReviewId: string): Promise => { - const repository = Repository.getInstanceByUri(uri); + const { scheme, repo } = router.parseUri(uri); + const repository = Repository.getInstance(scheme, repo); const changedFiles = await repository.getCodeReviewChangedFiles(codeReviewId); return getFileDecorationFromChangeFiles(uri, changedFiles); }; const getFileDecorationForCommit = async (uri: Uri, commitSha: string): Promise => { - const repository = Repository.getInstanceByUri(uri); + const { scheme, repo } = router.parseUri(uri); + const repository = Repository.getInstance(scheme, repo); const changedFiles = await repository.getCommitChangedFiles(commitSha); return getFileDecorationFromChangeFiles(uri, changedFiles); }; @@ -99,7 +101,7 @@ export class GitHub1sChangedFileDecorationProvider implements FileDecorationProv } provideFileDecoration(uri: Uri, _token: CancellationToken): ProviderResult { - if (uri.scheme !== adapterManager.getCurrentScheme()) { + if (uri.scheme !== getAdapter().scheme) { return null; } diff --git a/extensions/github1s/src/providers/decorations/submodule.ts b/extensions/github1s/src/providers/decorations/submodule.ts index 080852ad0..7940fe4f5 100644 --- a/extensions/github1s/src/providers/decorations/submodule.ts +++ b/extensions/github1s/src/providers/decorations/submodule.ts @@ -14,9 +14,9 @@ import { ThemeColor, Uri, } from 'vscode'; +import { getAllAdapters } from '@/adapters'; import { GitHub1sFileSystemProvider } from '../file-system'; import { Directory } from '../file-system/types'; -import adapterManager from '@/adapters/manager'; export class GitHub1sSubmoduleDecorationProvider implements FileDecorationProvider, Disposable { private static instance: GitHub1sSubmoduleDecorationProvider | null = null; @@ -50,7 +50,7 @@ export class GitHub1sSubmoduleDecorationProvider implements FileDecorationProvid } provideFileDecoration(uri: Uri, _token: CancellationToken): ProviderResult { - if (!adapterManager.getAllAdapters().some((adapter) => adapter.scheme === uri.scheme)) { + if (!getAllAdapters().some((adapter) => adapter.scheme === uri.scheme)) { return null; } return GitHub1sFileSystemProvider.getInstance() diff --git a/extensions/github1s/src/providers/definition.ts b/extensions/github1s/src/providers/definition.ts index f78b027ce..3123f085e 100644 --- a/extensions/github1s/src/providers/definition.ts +++ b/extensions/github1s/src/providers/definition.ts @@ -5,8 +5,8 @@ import * as vscode from 'vscode'; import router from '@/router'; +import { getAdapter } from '@/adapters'; import { showSourcegraphSymbolMessage } from '@/messages'; -import adapterManager from '@/adapters/manager'; export const mapScopeScheme = (scopeScheme: string) => { if (scopeScheme === 'github') { @@ -52,7 +52,7 @@ export class GitHub1sDefinitionProvider implements vscode.DefinitionProvider, vs const { scheme, repo, ref, path } = router.parseUri(document.uri); const { line, character } = position; - const dataSource = await adapterManager.getAdapter(scheme).resolveDataSource(); + const dataSource = await getAdapter(scheme).resolveDataSource(); const symbolDefinitions = await dataSource.provideSymbolDefinitions(repo, ref, path, line, character, symbol); if (symbolDefinitions.length) { diff --git a/extensions/github1s/src/providers/file-search.ts b/extensions/github1s/src/providers/file-search.ts index 15a5ca48c..fd3ae75fa 100644 --- a/extensions/github1s/src/providers/file-search.ts +++ b/extensions/github1s/src/providers/file-search.ts @@ -13,12 +13,12 @@ import { Uri, window, } from 'vscode'; +import { getAdapter } from '@/adapters'; import { matchSorter } from 'match-sorter'; import { reuseable } from '@/helpers/func'; import router from '@/router'; import * as adapterTypes from '@/adapters/types'; import { GitHub1sFileSystemProvider } from './file-system'; -import adapterManager from '@/adapters/manager'; export class GitHub1sFileSearchProvider implements FileSearchProvider, Disposable { private static instance: GitHub1sFileSearchProvider | null = null; @@ -56,18 +56,16 @@ export class GitHub1sFileSearchProvider implements FileSearchProvider, Disposabl * cache, and the fuzzy search maybe not work fine */ getFileUris = reuseable(async (): Promise => { - const currentAdapter = adapterManager.getCurrentAdapter(); - const scheme = currentAdapter.scheme; const { repo, ref } = router.getState(); - const cacheKey = `${scheme}:${repo}+${ref}`; + const cacheKey = `${repo}+${ref}`; if (this.fileUrisMap.has(cacheKey)) { return this.fileUrisMap.get(cacheKey)!; } - const dataSource = await currentAdapter.resolveDataSource(); + const dataSource = await getAdapter().resolveDataSource(); const rootDirectoryData = await dataSource.provideDirectory(repo, ref, '/', true); - const rootDirectoryUri = router.buildUri({ scheme, repo, ref, path: '/' }); + const rootDirectoryUri = router.buildUri({ repo, ref, path: '/' }); // the number of items in the tree array maybe exceeded maximum limit, only // insert the data to fileSystemProvider's cache if `treeData.truncated` is false diff --git a/extensions/github1s/src/providers/file-system/index.ts b/extensions/github1s/src/providers/file-system/index.ts index 1ef1c2a97..508dc2f24 100644 --- a/extensions/github1s/src/providers/file-system/index.ts +++ b/extensions/github1s/src/providers/file-system/index.ts @@ -13,12 +13,11 @@ import { FileStat, FileType, Uri, - workspace, } from 'vscode'; -import adapterManager from '@/adapters/manager'; +import { getAdapter } from '@/adapters'; import * as adapterTypes from '@/adapters/types'; import router from '@/router'; -import { noop, trimStart, basename, dirname, joinPath } from '@/helpers/util'; +import { noop, trimStart, basename, dirname } from '@/helpers/util'; import { parseGitmodules, parseSubmoduleUrl } from '@/helpers/submodule'; import { reuseable } from '@/helpers/func'; import { File, Directory, Entry } from './types'; @@ -58,10 +57,6 @@ export class GitHub1sFileSystemProvider implements FileSystemProvider, Disposabl this.disposable?.dispose(); } - private async _resolveDataSource(scheme: string) { - return adapterManager.getAdapter(scheme).resolveDataSource(); - } - // insert DirectoryEntry into the cache `this.root` public async populateWithDirectoryEntities(base: Uri, entries: adapterTypes.DirectoryEntry[]) { const baseDirectory = await this.lookupAsDirectory(base.with({ path: '/' }), true); @@ -201,7 +196,7 @@ export class GitHub1sFileSystemProvider implements FileSystemProvider, Disposabl } const { scheme, repo, ref } = router.parseUri(parent.uri); const path = Uri.joinPath(parent.uri, parent.name).path; - const dataSource = await adapterManager.getAdapter(scheme).resolveDataSource(); + const dataSource = await getAdapter(scheme).resolveDataSource(); const data = await dataSource.provideDirectory(repo, ref, path, false); data?.entries && (await this.populateWithDirectoryEntities(parent.uri, data.entries)); return parent.getNameTypePairs(); @@ -214,11 +209,15 @@ export class GitHub1sFileSystemProvider implements FileSystemProvider, Disposabl // If a file belongs to the current workspace, // check its existence to avoid unnecessary content requests. // It is efficient for some built-in files like `.vscode/...` - !uri.authority && (await this.lookupAsFile(uri, false)); + // The uri is also reset to the correct one for the submodule. + if (!uri.authority) { + const file = (await this.lookupAsFile(uri, false))!; + uri = Uri.joinPath(file.uri, file.name); + } const { scheme, repo, ref, path } = router.parseUri(uri); const cacheKey = `${scheme}:${repo}+${ref}${path}`; if (!this.contentCache.has(cacheKey)) { - const dataSource = await adapterManager.getAdapter(scheme).resolveDataSource(); + const dataSource = await getAdapter(scheme).resolveDataSource(); const data = await dataSource.provideFile(repo, ref, path); data && this.contentCache.set(cacheKey, data.content); } diff --git a/extensions/github1s/src/providers/hover.ts b/extensions/github1s/src/providers/hover.ts index 4ef875b74..38a1a9170 100644 --- a/extensions/github1s/src/providers/hover.ts +++ b/extensions/github1s/src/providers/hover.ts @@ -5,8 +5,8 @@ import * as vscode from 'vscode'; import router from '@/router'; +import { getAdapter } from '@/adapters'; import { getSourcegraphUrl } from '@/helpers/urls'; -import { adapterManager } from '@/adapters'; import { mapScopeScheme } from './definition'; const getSemanticMarkdownSuffix = (sourcegraphUrl: string) => ` @@ -47,7 +47,7 @@ export class GitHub1sHoverProvider implements vscode.HoverProvider, vscode.Dispo ): Promise { const { line, character } = position; const { scheme, repo, ref, path } = router.parseUri(document.uri); - const dataSource = await adapterManager.getAdapter(scheme).resolveDataSource(); + const dataSource = await getAdapter(scheme).resolveDataSource(); const requestParams = [repo, ref, path, line, character, symbol] as const; const definitions = await dataSource.provideSymbolDefinitions(...requestParams); @@ -99,7 +99,7 @@ export class GitHub1sHoverProvider implements vscode.HoverProvider, vscode.Dispo const searchBasedMardownPromise = this.getSearchBasedHover(document, position, symbol); // get the hover result based on sourcegraph lsif - const dataSource = await adapterManager.getAdapter(scheme).resolveDataSource(); + const dataSource = await getAdapter(scheme).resolveDataSource(); const symbolHover = await dataSource.provideSymbolHover(...requestParams); const markdown = symbolHover ? symbolHover.markdown : await searchBasedMardownPromise; diff --git a/extensions/github1s/src/providers/index.ts b/extensions/github1s/src/providers/index.ts index 9da617be5..331d8f521 100644 --- a/extensions/github1s/src/providers/index.ts +++ b/extensions/github1s/src/providers/index.ts @@ -4,7 +4,7 @@ */ import * as vscode from 'vscode'; -import adapterManager from '@/adapters/manager'; +import { getAllAdapters } from '@/adapters'; import { getExtensionContext } from '@/helpers/context'; import { GitHub1sFileSystemProvider } from './file-system'; import { GitHub1sFileSearchProvider } from './file-search'; @@ -22,8 +22,7 @@ export const emptyFileUri = vscode.Uri.from({ scheme: EMPTY_FILE_SCHEME }); export const registerVSCodeProviders = () => { const context = getExtensionContext(); - - const allSchemes = adapterManager.getAllAdapters().map((item) => item.scheme); + const allSchemes = getAllAdapters().map((item) => item.scheme); allSchemes.forEach((scheme) => { context.subscriptions.push( diff --git a/extensions/github1s/src/providers/reference.ts b/extensions/github1s/src/providers/reference.ts index 9339d5244..0a0b259c5 100644 --- a/extensions/github1s/src/providers/reference.ts +++ b/extensions/github1s/src/providers/reference.ts @@ -5,8 +5,8 @@ import * as vscode from 'vscode'; import router from '@/router'; +import { getAdapter } from '@/adapters'; import { showSourcegraphSymbolMessage } from '@/messages'; -import adapterManager from '@/adapters/manager'; import { mapScopeScheme } from './definition'; export class GitHub1sReferenceProvider implements vscode.ReferenceProvider, vscode.Disposable { @@ -42,7 +42,7 @@ export class GitHub1sReferenceProvider implements vscode.ReferenceProvider, vsco const { scheme, repo, ref, path } = router.parseUri(document.uri); const { line, character } = position; - const dataSource = await adapterManager.getAdapter(scheme).resolveDataSource(); + const dataSource = await getAdapter(scheme).resolveDataSource(); const symbolReferences = await dataSource.provideSymbolReferences(repo, ref, path, line, character, symbol); if (symbolReferences.length) { diff --git a/extensions/github1s/src/providers/text-search.ts b/extensions/github1s/src/providers/text-search.ts index 7898664d2..455059546 100644 --- a/extensions/github1s/src/providers/text-search.ts +++ b/extensions/github1s/src/providers/text-search.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; import router from '@/router'; -import adapterManager from '@/adapters/manager'; +import { getAdapter } from '@/adapters'; import { showSourcegraphSearchMessage } from '@/messages'; import * as adapterTypes from '@/adapters/types'; @@ -37,8 +37,8 @@ export class GitHub1sTextSearchProvider implements vscode.TextSearchProvider, vs _token: vscode.CancellationToken, ) { return Promise.resolve().then(async () => { - const { scheme, repo, ref } = router.getState(); - const dataSource = await adapterManager.getAdapter(scheme).resolveDataSource(); + const { repo, ref } = router.getState(); + const dataSource = await getAdapter().resolveDataSource(); const searchOptions = { page: 1, pageSize: 100, includes: options.includes, excludes: options.excludes }; const searchResults = await dataSource.provideTextSearchResults(repo, ref, query, searchOptions); diff --git a/extensions/github1s/src/repository/branch-tag-manager.ts b/extensions/github1s/src/repository/branch-tag-manager.ts index f1cbe22ed..d04bacf31 100644 --- a/extensions/github1s/src/repository/branch-tag-manager.ts +++ b/extensions/github1s/src/repository/branch-tag-manager.ts @@ -5,7 +5,7 @@ import { reuseable } from '@/helpers/func'; import { Branch, Tag } from '@/adapters/types'; -import { adapterManager } from '@/adapters'; +import { getAdapter } from '@/adapters'; export class BranchTagManager { private static instancesMap = new Map(); @@ -46,7 +46,7 @@ export class BranchTagManager { getBranchItem = reuseable(async (branchName: string, forceUpdate = false): Promise => { if (forceUpdate || !this._branchMap.has(branchName)) { - const dataSource = await adapterManager.getAdapter(this._scheme).resolveDataSource(); + const dataSource = await getAdapter(this._scheme).resolveDataSource(); const branch = await dataSource.provideBranch(this._repo, branchName); branch && this._branchMap.set(branchName, branch); } @@ -54,7 +54,7 @@ export class BranchTagManager { }); loadMoreBranches = reuseable(async (): Promise => { - const dataSource = await adapterManager.getAdapter(this._scheme).resolveDataSource(); + const dataSource = await getAdapter(this._scheme).resolveDataSource(); const queryOptions = { pageSize: this._branchPageSize, page: this._branchCurrentPage }; const branches = await dataSource.provideBranches(this._repo, queryOptions); @@ -81,7 +81,7 @@ export class BranchTagManager { getTagItem = reuseable(async (tagName: string, forceUpdate = false): Promise => { if (forceUpdate || !this._tagMap.has(tagName)) { - const dataSource = await adapterManager.getAdapter(this._scheme).resolveDataSource(); + const dataSource = await getAdapter(this._scheme).resolveDataSource(); const tag = await dataSource.provideTag(this._repo, tagName); tag && this._tagMap.set(tagName, tag); } @@ -89,7 +89,7 @@ export class BranchTagManager { }); loadMoreTags = reuseable(async (): Promise => { - const dataSource = await adapterManager.getAdapter(this._scheme).resolveDataSource(); + const dataSource = await getAdapter(this._scheme).resolveDataSource(); const queryOptions = { pageSize: this._tagPageSize, page: this._tagCurrentPage }; const tags = await dataSource.provideTags(this._repo, queryOptions); diff --git a/extensions/github1s/src/repository/code-review-manager.ts b/extensions/github1s/src/repository/code-review-manager.ts index 844df6705..36eddcf58 100644 --- a/extensions/github1s/src/repository/code-review-manager.ts +++ b/extensions/github1s/src/repository/code-review-manager.ts @@ -3,9 +3,9 @@ * @author netcon */ +import { getAdapter } from '@/adapters'; import { reuseable } from '@/helpers/func'; import { ChangedFile, CodeReview } from '@/adapters/types'; -import { adapterManager } from '@/adapters'; // manage changed files for a code review class CodeReviewChangedFilesManager { @@ -41,7 +41,7 @@ class CodeReviewChangedFilesManager { }); loadMore = reuseable(async (): Promise => { - const dataSource = await adapterManager.getAdapter(this._scheme).resolveDataSource(); + const dataSource = await getAdapter(this._scheme).resolveDataSource(); const changedFiles = await dataSource.provideCodeReviewChangedFiles(this._repo, this._codeReviewId, { pageSize: this._pageSize, page: this._currentPage, @@ -106,7 +106,7 @@ export class CodeReviewManager { !this._codeReviewMap.has(codeReviewId) || !isShaExists(this._codeReviewMap.get(codeReviewId)!) ) { - const dataSource = await adapterManager.getAdapter(this._scheme).resolveDataSource(); + const dataSource = await getAdapter(this._scheme).resolveDataSource(); const codeReview = await dataSource.provideCodeReview(this._repo, codeReviewId); codeReview && this._codeReviewMap.set(codeReviewId, codeReview); if (codeReview?.files) { @@ -121,7 +121,7 @@ export class CodeReviewManager { ); loadMore = reuseable(async (): Promise => { - const dataSource = await adapterManager.getAdapter(this._scheme).resolveDataSource(); + const dataSource = await getAdapter(this._scheme).resolveDataSource(); const queryOptions = { pageSize: this._pageSize, page: this._currentPage }; const codeReviews = await dataSource.provideCodeReviews(this._repo, queryOptions); diff --git a/extensions/github1s/src/repository/commit-manager.ts b/extensions/github1s/src/repository/commit-manager.ts index ef87e8ad5..951652a7c 100644 --- a/extensions/github1s/src/repository/commit-manager.ts +++ b/extensions/github1s/src/repository/commit-manager.ts @@ -3,9 +3,9 @@ * @author netcon */ +import { getAdapter } from '@/adapters'; import { reuseable } from '@/helpers/func'; import { ChangedFile, Commit } from '@/adapters/types'; -import { adapterManager } from '@/adapters'; // manage changed files for a commit class CommitChangedFilesManager { @@ -41,7 +41,7 @@ class CommitChangedFilesManager { }); loadMore = reuseable(async (): Promise => { - const dataSource = await adapterManager.getAdapter(this._scheme).resolveDataSource(); + const dataSource = await getAdapter(this._scheme).resolveDataSource(); const changedFiles = await dataSource.provideCommitChangedFiles(this._repo, this._commitSha, { pageSize: this._pageSize, page: this._currentPage, @@ -134,7 +134,7 @@ export class CommitManager { getItem = reuseable(async (forceUpdate: boolean = false): Promise => { if (forceUpdate || !CommitManager._commitMap.has(this._from)) { - const dataSource = await adapterManager.getAdapter(this._scheme).resolveDataSource(); + const dataSource = await getAdapter(this._scheme).resolveDataSource(); const commit = await dataSource.provideCommit(this._repo, this._from); commit && CommitManager._commitMap.set(this._from, commit); @@ -149,7 +149,7 @@ export class CommitManager { loadMore = reuseable(async (): Promise => { const commitList = this.resolveCommitList(); - const dataSource = await adapterManager.getAdapter(this._scheme).resolveDataSource(); + const dataSource = await getAdapter(this._scheme).resolveDataSource(); const queryOptions = { page: this._currentPage, pageSize: this._pageSize, diff --git a/extensions/github1s/src/repository/index.ts b/extensions/github1s/src/repository/index.ts index c43109f0c..2379cffa3 100644 --- a/extensions/github1s/src/repository/index.ts +++ b/extensions/github1s/src/repository/index.ts @@ -3,13 +3,12 @@ * @author netcon */ -import * as vscode from 'vscode'; -import { adapterManager } from '@/adapters'; +import router from '@/router'; +import { getAdapter } from '@/adapters'; import { CommitManager } from './commit-manager'; import { CodeReviewManager } from './code-review-manager'; import { BranchTagManager } from './branch-tag-manager'; import { BlameRange } from '@/adapters/types'; -import router from '@/router'; export class Repository { private static instanceMap = new Map(); @@ -26,14 +25,8 @@ export class Repository { return Repository.instanceMap.get(mapKey)!; } - public static getInstanceByUri(uri: vscode.Uri) { - const { scheme, repo } = router.parseUri(uri); - return Repository.getInstance(scheme, repo); - } - public static getCurrentInstance() { - const routerState = router.getState(); - return Repository.getInstance(routerState.scheme, routerState.repo); + return Repository.getInstance(getAdapter().scheme, router.getState().repo); } private constructor( @@ -148,7 +141,7 @@ export class Repository { async getFileBlameRanges(ref: string, path: string) { const cacheKey = `${ref} ${path}`; if (!this._blameRangesCache.has(cacheKey)) { - const dataSource = await adapterManager.getAdapter(this._scheme).resolveDataSource(); + const dataSource = await getAdapter(this._scheme).resolveDataSource(); const blameRanges = await dataSource.provideFileBlameRanges(this._repo, ref, path); this._blameRangesCache.set(cacheKey, blameRanges); } diff --git a/extensions/github1s/src/router/index.ts b/extensions/github1s/src/router/index.ts index 6fa564b1c..9062c360f 100644 --- a/extensions/github1s/src/router/index.ts +++ b/extensions/github1s/src/router/index.ts @@ -4,10 +4,9 @@ */ import * as vscode from 'vscode'; +import { getAdapter } from '@/adapters'; import { History, createMemoryHistory, parsePath, Action } from 'history'; -import { Adapter, RouterParser, RouterState } from '@/adapters/types'; -import { Barrier } from '@/helpers/async'; -import adapterManager from '@/adapters/manager'; +import { RouterParser, RouterState } from '@/adapters/types'; import { EventEmitter } from './events'; export interface UrlManager { @@ -28,7 +27,6 @@ export class Router extends EventEmitter { private _state: RouterState | null = null; private _history: History | null = null; - private _adapter: Adapter | null = null; private _parser: RouterParser | null = null; private _manager: UrlManager | null = null; @@ -43,11 +41,10 @@ export class Router extends EventEmitter { // must be called before any other method is called async initialize(urlManager: UrlManager) { this._manager = urlManager; - this._adapter = adapterManager.getCurrentAdapter(); const { path: pathname, query, fragment } = vscode.Uri.parse(await this._manager.href()); const path = pathname + (query ? `?${query}` : '') + (fragment ? `#${fragment}` : ''); - this._parser = await this._adapter.resolveRouterParser(); + this._parser = await getAdapter().resolveRouterParser(); this._state = await this._parser.parsePath(path); this._history = createMemoryHistory({ initialEntries: [path] }); @@ -62,8 +59,8 @@ export class Router extends EventEmitter { } // get the routerState for current url - public getState(): RouterState & { scheme: string } { - return { ...this._state!, scheme: this._adapter!.scheme }; + public getState(): RouterState { + return { ...this._state! }; } public getHistory() { @@ -118,9 +115,7 @@ export class Router extends EventEmitter { mergedState.path = `/${state.path?.split('/').filter(Boolean).join('/') || ''}`; } - return base - ? base.with(mergedState) - : vscode.Uri.from({ scheme: adapterManager.getCurrentScheme(), path: '/', ...mergedState }); + return base ? base.with(mergedState) : vscode.Uri.from({ scheme: getAdapter().scheme, path: '/', ...mergedState }); } } diff --git a/extensions/github1s/src/statusbar/sponsors.ts b/extensions/github1s/src/statusbar/sponsors.ts index 18cd0480d..60e78e32b 100644 --- a/extensions/github1s/src/statusbar/sponsors.ts +++ b/extensions/github1s/src/statusbar/sponsors.ts @@ -5,12 +5,12 @@ import * as vscode from 'vscode'; import router from '@/router'; -import { adapterManager } from '@/adapters'; +import { getAdapter } from '@/adapters'; import { PlatformName } from '@/adapters/types'; const resolveSourcegraphLink = async () => { const { repo, ref } = router.getState(); - switch (adapterManager.getCurrentAdapter().platformName) { + switch (getAdapter().platformName) { case PlatformName.GitHub: return `https://sourcegraph.com/github.com/${repo}@${ref}`; case PlatformName.GitLab: diff --git a/extensions/github1s/src/views/code-review-list.ts b/extensions/github1s/src/views/code-review-list.ts index 61c9f7957..9412be960 100644 --- a/extensions/github1s/src/views/code-review-list.ts +++ b/extensions/github1s/src/views/code-review-list.ts @@ -5,11 +5,9 @@ import * as vscode from 'vscode'; import * as queryString from 'query-string'; -import router from '@/router'; -import { Barrier } from '@/helpers/async'; import { Repository } from '@/repository'; +import { Barrier } from '@/helpers/async'; import { relativeTimeTo, toISOString } from '@/helpers/date'; -import adapterManager from '@/adapters/manager'; import * as adapterTypes from '@/adapters/types'; import { getChangedFileDiffCommand, getCodeReviewChangedFiles } from '@/changes/files'; import { GitHub1sSourceControlDecorationProvider } from '@/providers/decorations/source-control'; @@ -115,9 +113,8 @@ export class CodeReviewTreeDataProvider implements vscode.TreeDataProvider { + const repository = Repository.getCurrentInstance(); this._loadingBarrier && (await this._loadingBarrier.wait()); - const currentScheme = adapterManager.getCurrentScheme(); - const { repo } = router.getState(); - const repository = Repository.getInstance(currentScheme, repo); const codeReviews = await repository.getCodeReviewList(this._forceUpdate); const codeReviewTreeItems = codeReviews.map((codeReview) => { const label = getCodeReviewTreeItemLabel(codeReview); @@ -166,8 +160,8 @@ export class CodeReviewTreeDataProvider implements vscode.TreeDataProvider { - this._loadingBarrier && (await this._loadingBarrier.wait()); const repository = Repository.getCurrentInstance(); + this._loadingBarrier && (await this._loadingBarrier.wait()); const _codeReview = await repository.getCodeReviewItem(codeReview.id); const changedFiles = _codeReview ? await getCodeReviewChangedFiles(_codeReview) : []; const changedFileItems = changedFiles.map((changedFile) => { diff --git a/extensions/github1s/src/views/commit-list.ts b/extensions/github1s/src/views/commit-list.ts index e41a138d6..42392f8b8 100644 --- a/extensions/github1s/src/views/commit-list.ts +++ b/extensions/github1s/src/views/commit-list.ts @@ -5,11 +5,11 @@ import * as vscode from 'vscode'; import router from '@/router'; -import { Barrier } from '@/helpers/async'; +import { getAdapter } from '@/adapters'; import { Repository } from '@/repository'; +import { Barrier } from '@/helpers/async'; import * as queryString from 'query-string'; import { relativeTimeTo, toISOString } from '@/helpers/date'; -import adapterManager from '@/adapters/manager'; import * as adapterTypes from '@/adapters/types'; import { getChangedFileDiffCommand, getCommitChangedFiles } from '@/changes/files'; import { GitHub1sSourceControlDecorationProvider } from '@/providers/decorations/source-control'; @@ -74,9 +74,8 @@ export class CommitTreeDataProvider implements vscode.TreeDataProvider { this._loadingBarrier && (await this._loadingBarrier.wait()); const filePath = await this.resolveFilePath(); - const currentAdapter = adapterManager.getCurrentAdapter(); - const { repo, ref } = router.getState(); - const repository = Repository.getInstance(currentAdapter.scheme, repo); + const { ref } = router.getState(); + const repository = Repository.getCurrentInstance(); const repositoryCommits = await repository.getCommitList(ref, filePath, this._forceUpdate); const commitTreeItems = repositoryCommits.map((commit) => { const label = commit.message.split(/[\r\n]/)[0]; @@ -127,6 +124,7 @@ export class CommitTreeDataProvider implements vscode.TreeDataProvider { + const repository = Repository.getCurrentInstance(); const changedFiles = await getCommitChangedFiles(commit); const changedFileItems = changedFiles.map((changedFile) => { const filePath = changedFile.headFileUri.path; @@ -143,9 +141,6 @@ export class CommitTreeDataProvider implements vscode.TreeDataProvider { treeDataProvider: codeReviewRequestTreeDataProvider, }); // set code view list view title according code review type - const codeReviewType = adapterManager.getCurrentAdapter().codeReviewType || CodeReviewType.CodeReview; + const codeReviewType = getAdapter().codeReviewType || CodeReviewType.CodeReview; codeReviewTreeView.title = codeReviewViewTitle[codeReviewType]; context.subscriptions.push(