Skip to content
This repository was archived by the owner on Oct 24, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 23 additions & 34 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
"categories": [
"External services",
"Reports and stats",
"Insights",
"Code analysis"
],
"tags": [
Expand Down Expand Up @@ -321,7 +320,7 @@
],
"devDependencies": {
"@sourcegraph/eslint-config": "^0.16.2",
"@sourcegraph/extension-api-stubs": "^1.3.0",
"@sourcegraph/extension-api-stubs": "^1.4.0",
"@types/mocha": "^5.2.7",
"@types/mock-require": "^2.0.0",
"@types/node": "^10.17.21",
Expand All @@ -336,7 +335,7 @@
"prettier": "^2.0.5",
"rxjs": "^6.5.5",
"source-map-support": "^0.5.19",
"sourcegraph": "^24.8.0",
"sourcegraph": "^25.2.0",
"ts-node": "^8.9.1",
"typescript": "^3.8.3"
}
Expand Down
83 changes: 81 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { combineLatest, from, Subscription } from 'rxjs'
import { concatMap, filter, map, startWith, switchMap, distinctUntilChanged } from 'rxjs/operators'
import { concatMap, filter, map, startWith, switchMap, distinctUntilChanged, debounceTime } from 'rxjs/operators'
import * as sourcegraph from 'sourcegraph'
import { getCommitCoverage, getTreeCoverage, Service } from './api'
import { codecovToDecorations } from './decoration'
import { createGraphViewProvider } from './insights'
import { getCommitCoverageRatio, getFileCoverageRatios, getFileLineCoverage } from './model'
import { configurationChanges, Endpoint, resolveEndpoint, resolveSettings, Settings, InsightSettings } from './settings'
import { codecovParamsForRepositoryCommit, resolveDocumentURI, resolveRootURI } from './uri'
import { codecovParamsForRepositoryCommit, resolveDocumentURI, ResolvedRootURI, resolveRootURI } from './uri'

const decorationType = sourcegraph.app.createDecorationType && sourcegraph.app.createDecorationType()

// Check if the Sourcegraph instance supports status bar items (minimum Sourcegraph version 3.26)
const supportsStatusBarAPI = !!sourcegraph.app.createStatusBarItemType as boolean
const statusBarItemType = sourcegraph.app.createStatusBarItemType && sourcegraph.app.createStatusBarItemType()

/** Entrypoint for the Codecov Sourcegraph extension. */
export function activate(
context: sourcegraph.ExtensionContext = {
Expand Down Expand Up @@ -124,9 +128,74 @@ export function activate(
} catch (err) {
console.error('Error loading Codecov file coverage:', err)
}

sourcegraph.internal.updateContext(context)
}

async function setStatusBars(
endpoints: readonly Endpoint[] | undefined,
editors: sourcegraph.ViewComponent[]
): Promise<void> {
if (supportsStatusBarAPI) {
/**
* Keys: repo + revision separated, repo: "sourcegraph/sourcegraph" + revision: "1234" => "sourcegraph/sourcegraph1234"
* Values: resolved root uri
* Used to find all unique pairs of repo + revision across editors
*/
const resolvedRootURIs = new Map<string, ResolvedRootURI>()
for (const editor of editors) {
if (editor.type === 'CodeEditor') {
const { repo, rev } = resolveRootURI(editor.document.uri)
resolvedRootURIs.set(repo + rev, { repo, rev })
}
}

// Fetch data
const endpoint = resolveEndpoint(endpoints)

const fileRatioByURI = new Map<string, { fileRatio: string; fileURL: string }>()

const results = await Promise.allSettled(
[...resolvedRootURIs].map(async ([, resolvedRootURI]) => ({
resolvedRootURI,
fileRatios: await getFileCoverageRatios(resolvedRootURI, endpoint, sourcegraph),
}))
)

for (const result of results) {
if (result.status === 'fulfilled') {
const { resolvedRootURI, fileRatios } = result.value

const p = codecovParamsForRepositoryCommit(resolvedRootURI, sourcegraph)
const repoURL = `${p.baseURL || 'https://codecov.io'}/${p.service}/${p.owner}/${p.repo}`

if (fileRatios) {
for (const [path, ratio] of Object.entries(fileRatios)) {
// Convert relative paths to URIs
const uri = `git://${resolvedRootURI.repo}?${resolvedRootURI.rev}#${path}`
const baseFileURL = `${repoURL}/src/${p.sha}`

fileRatioByURI.set(uri, { fileRatio: ratio.toFixed(0), fileURL: `${baseFileURL}/${path}` })
}
}
}
}

// Set status bars
for (const editor of editors) {
if (editor.type === 'CodeEditor') {
const ratio = fileRatioByURI.get(editor.document.uri)
if (ratio) {
editor.setStatusBarItem(statusBarItemType, {
text: `Code Coverage: ${ratio.fileRatio}%`,
command: { id: 'open', args: [ratio.fileURL] },
})
}
}
}
}
}

// Update the context when the configuration, workspace roots or active editors change.
context.subscriptions.add(
combineLatest([
Expand All @@ -141,8 +210,18 @@ export function activate(
editorsChanges,
])
.pipe(
// Debounce to avoid doing duplicate work for intermediate states
// for views with multiple editors added sequentially (e.g. commit page, PR)
debounceTime(30),
concatMap(async ([endpoints, roots, editors]) => {
try {
await setStatusBars(endpoints, editors)
} catch (err) {
console.error('Codecov: error setting status bars', err)
}

try {
// The commit data used for `updateContext` should be cached if Status Bar API is supported
await updateContext(endpoints, roots, editors)
} catch (err) {
console.error('Codecov: error updating context', err)
Expand Down