├── .gitignore ├── .prettierignore ├── prettier.config.js ├── CHANGELOG.md ├── images └── logo.png ├── src ├── log.ts ├── git │ ├── remoteUrl.test.ts │ ├── remoteUrl.ts │ ├── helpers.ts │ ├── index.ts │ ├── remoteNameAndBranch.ts │ └── remoteNameAndBranch.test.ts ├── config.ts └── extension.ts ├── renovate.json ├── .vscode ├── extensions.json ├── tasks.json ├── settings.json └── launch.json ├── tsconfig.eslint.json ├── .editorconfig ├── .vscodeignore ├── .eslintrc.json ├── README.md ├── tsconfig.json ├── .github ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── pr-auditor.yml │ └── build.yml ├── LICENSE └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | *.vsix 4 | coverage/ 5 | .nyc_output/ 6 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | package.json 2 | package-lock.json 3 | out/ 4 | .github 5 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@sourcegraph/prettierrc') 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # See [GitHub releases](https://github.com/sourcegraph/sourcegraph-vscode/releases) 2 | -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sourcegraph/sourcegraph-vscode-DEPRECATED/HEAD/images/logo.png -------------------------------------------------------------------------------- /src/log.ts: -------------------------------------------------------------------------------- 1 | import vscode from 'vscode' 2 | 3 | export const log = vscode.window.createOutputChannel('Sourcegraph') 4 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/renovate", 3 | "extends": ["github>sourcegraph/renovate-config"] 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint", "ms-vscode.vscode-typescript-tslint-plugin"], 3 | } 4 | -------------------------------------------------------------------------------- /tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["**/*.ts", "**/*.test.ts"], 4 | "exclude": ["node_modules", ".vscode-test"], 5 | } 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | 2 | [*] 3 | indent_size = 4 4 | indent_style = space 5 | charset = utf-8 6 | end_of_line = lf 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [*.{yml,json,js}] 11 | indent_size = 2 12 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .editorconfig 2 | .github/** 3 | .gitignore 4 | .prettierignore 5 | .vscode-test/** 6 | .vscode/** 7 | **/*.map 8 | out/test/** 9 | prettier.config.json 10 | src/** 11 | test/** 12 | tsconfig.json 13 | tslint.json 14 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@sourcegraph/eslint-config", 3 | "env": { 4 | "node": true, 5 | "es6": true 6 | }, 7 | "parserOptions": { 8 | "ecmaVersion": 2018, 9 | "sourceType": "module", 10 | "project": "tsconfig.eslint.json" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # THIS REPO IS DEPRECATED, USE [MAIN REPO](https://github.com/sourcegraph/sourcegraph) INSTEAD. 2 | 3 | DEPRECATED: This repository is now deprecated, as the VS Code extension now lives within the [main Sourcegraph repo](https://github.com/sourcegraph/sourcegraph) under the [client/vscode directory](https://github.com/sourcegraph/sourcegraph/tree/main/client/vscode). 4 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "npm", 6 | "script": "eslint", 7 | "problemMatcher": ["$eslint-stylish"], 8 | "presentation": { 9 | "reveal": "never", 10 | }, 11 | }, 12 | { 13 | "type": "npm", 14 | "script": "build", 15 | "label": "build", 16 | }, 17 | ], 18 | } 19 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false, // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true, // set this to false to include "out" folder in search results 8 | }, 9 | "eslint.validate": ["typescript"], 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@sourcegraph/tsconfig", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "target": "es6", 6 | "outDir": "out", 7 | "lib": ["es6"], 8 | "sourceMap": true, 9 | "rootDir": "src", 10 | "esModuleInterop": true, 11 | "resolveJsonModule": true, 12 | "strict": true, 13 | }, 14 | "exclude": ["node_modules", ".vscode-test", "**/*.test.ts"], 15 | } 16 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Test plan 4 | 5 | 12 | -------------------------------------------------------------------------------- /.github/workflows/pr-auditor.yml: -------------------------------------------------------------------------------- 1 | name: pr-auditor 2 | on: 3 | pull_request: 4 | types: [ closed, edited, opened ] 5 | 6 | jobs: 7 | run: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | with: { repository: 'sourcegraph/sourcegraph' } 12 | - uses: actions/setup-go@v2 13 | with: { go-version: '1.17' } 14 | 15 | - run: ./dev/pr-auditor/check-pr.sh 16 | env: 17 | GITHUB_EVENT_PATH: ${{ env.GITHUB_EVENT_PATH }} 18 | GITHUB_TOKEN: ${{ secrets.CODENOTIFY_GITHUB_TOKEN }} 19 | GITHUB_RUN_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} 20 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | FORCE_COLOR: 3 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Use Node.js 14 | uses: actions/setup-node@v2 15 | with: 16 | node-version: '14.x' 17 | - run: npm install 18 | - run: npm run prettier-check 19 | - run: npm run eslint 20 | - run: npm run build 21 | - run: npm run test 22 | - run: yarn nyc report --reporter json 23 | - run: 'bash <(curl -s https://codecov.io/bash)' 24 | - name: release 25 | if: github.repository_owner == 'sourcegraph' && github.event_name == 'push' && github.ref == 'refs/heads/master' 26 | env: 27 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 28 | VSCE_TOKEN: ${{ secrets.VSCE_TOKEN }} 29 | run: npm run semantic-release 30 | -------------------------------------------------------------------------------- /src/git/remoteUrl.test.ts: -------------------------------------------------------------------------------- 1 | import assert from 'assert' 2 | import { gitRemoteUrlWithReplacements } from './remoteUrl' 3 | 4 | describe('remoteUrl', () => { 5 | it('returns the remote URL given a remote name', async () => { 6 | const remoteUrl = await gitRemoteUrlWithReplacements('', 'origin', () => ({}), { 7 | remoteUrl: () => Promise.resolve('git@github.com:sourcegraph/sourcegraph-vscode.git'), 8 | }) 9 | 10 | assert.strictEqual(remoteUrl, 'git@github.com:sourcegraph/sourcegraph-vscode.git', 'incorrect remote URL') 11 | }) 12 | 13 | it('replaces values in URL', async () => { 14 | const remoteUrl = await gitRemoteUrlWithReplacements('', 'origin', () => ({ github: 'gitlab' }), { 15 | remoteUrl: () => Promise.resolve('git@github.com:sourcegraph/sourcegraph-vscode.git'), 16 | }) 17 | 18 | assert.strictEqual(remoteUrl, 'git@gitlab.com:sourcegraph/sourcegraph-vscode.git', 'incorrect remote URL') 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /src/git/remoteUrl.ts: -------------------------------------------------------------------------------- 1 | import { GitHelpers } from './helpers' 2 | 3 | /** 4 | * Returns the remote URL for the given remote name with remote URL replacements. 5 | * e.g. `origin` -> `git@github.com:foo/bar` 6 | */ 7 | export async function gitRemoteUrlWithReplacements( 8 | repoDirectory: string, 9 | remoteName: string, 10 | getRemoteUrlReplacements: () => Record, 11 | gitHelpers: Pick, 12 | log?: { appendLine: (value: string) => void } 13 | ): Promise { 14 | let stdout = await gitHelpers.remoteUrl(remoteName, repoDirectory) 15 | const replacementsList = getRemoteUrlReplacements() 16 | 17 | const stdoutBefore = stdout 18 | 19 | for (const replacement in replacementsList) { 20 | if (typeof replacement === 'string') { 21 | stdout = stdout.replace(replacement, replacementsList[replacement]) 22 | } 23 | } 24 | log?.appendLine(`${stdoutBefore} became ${stdout}`) 25 | return stdout 26 | } 27 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | { 3 | "version": "0.1.0", 4 | "configurations": [ 5 | { 6 | "name": "Launch Extension", 7 | "type": "extensionHost", 8 | "request": "launch", 9 | "runtimeExecutable": "${execPath}", 10 | "args": ["--extensionDevelopmentPath=${workspaceRoot}"], 11 | "stopOnEntry": false, 12 | "sourceMaps": true, 13 | "outFiles": ["${workspaceRoot}/out/**/*.js"], 14 | "preLaunchTask": "build", 15 | }, 16 | { 17 | "name": "Launch Tests", 18 | "type": "extensionHost", 19 | "request": "launch", 20 | "runtimeExecutable": "${execPath}", 21 | "args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test"], 22 | "stopOnEntry": false, 23 | "sourceMaps": true, 24 | "outFiles": ["${workspaceRoot}/out/test/**/*.js"], 25 | "preLaunchTask": "build", 26 | }, 27 | ], 28 | } 29 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | import vscode from 'vscode' 2 | 3 | export function getSourcegraphUrl(): string { 4 | // has default value 5 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 6 | const url = vscode.workspace.getConfiguration('sourcegraph').get('url')! 7 | if (url.endsWith('/')) { 8 | return url.slice(0, -1) 9 | } 10 | return url 11 | } 12 | 13 | export function getRemoteUrlReplacements(): Record { 14 | // has default value 15 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 16 | const replacements = vscode.workspace 17 | .getConfiguration('sourcegraph') 18 | .get>('remoteUrlReplacements')! 19 | return replacements 20 | } 21 | 22 | export function getDefaultBranch(): string { 23 | // has default value 24 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 25 | const branch = vscode.workspace.getConfiguration('sourcegraph').get('defaultBranch')! 26 | 27 | return branch 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Sourcegraph, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/git/helpers.ts: -------------------------------------------------------------------------------- 1 | import execa from 'execa' 2 | 3 | export const gitHelpers = { 4 | /** 5 | * Returns the repository root directory for any directory within the 6 | * repository. 7 | */ 8 | async rootDirectory(repoDirectory: string): Promise { 9 | const { stdout } = await execa('git', ['rev-parse', '--show-toplevel'], { cwd: repoDirectory }) 10 | return stdout 11 | }, 12 | 13 | /** 14 | * Returns the names of all git remotes, e.g. ["origin", "foobar"] 15 | */ 16 | async remotes(repoDirectory: string): Promise { 17 | const { stdout } = await execa('git', ['remote'], { cwd: repoDirectory }) 18 | return stdout.split('\n') 19 | }, 20 | 21 | /** 22 | * Returns the remote URL for the given remote name. 23 | * e.g. `origin` -> `git@github.com:foo/bar` 24 | */ 25 | async remoteUrl(remoteName: string, repoDirectory: string): Promise { 26 | const { stdout } = await execa('git', ['remote', 'get-url', remoteName], { cwd: repoDirectory }) 27 | return stdout 28 | }, 29 | 30 | /** 31 | * Returns either the current branch name of the repository OR in all 32 | * other cases (e.g. detached HEAD state), it returns "HEAD". 33 | */ 34 | async branch(repoDirectory: string): Promise { 35 | const { stdout } = await execa('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: repoDirectory }) 36 | return stdout 37 | }, 38 | 39 | /** 40 | * Returns a string in the format $UPSTREAM_REMOTE/$BRANCH_NAME, e.g. "origin/branch-name", throws if not found 41 | */ 42 | async upstreamAndBranch(repoDirectory: string): Promise { 43 | const { stdout } = await execa('git', ['rev-parse', '--abbrev-ref', 'HEAD@{upstream}'], { cwd: repoDirectory }) 44 | return stdout 45 | }, 46 | } 47 | 48 | export type GitHelpers = typeof gitHelpers 49 | -------------------------------------------------------------------------------- /src/git/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path' 2 | import { log } from '../log' 3 | import { getRemoteUrlReplacements, getDefaultBranch } from '../config' 4 | import { gitHelpers } from './helpers' 5 | import { Branch, gitRemoteNameAndBranch } from './remoteNameAndBranch' 6 | import { gitRemoteUrlWithReplacements } from './remoteUrl' 7 | 8 | interface RepositoryInfo extends Branch { 9 | /** Git repository remote URL */ 10 | remoteURL: string 11 | 12 | /** File path relative to the repository root */ 13 | fileRelative: string 14 | } 15 | 16 | /** 17 | * Returns the Git repository remote URL, the current branch, and the file path 18 | * relative to the repository root. Returns undefined if no remote is found 19 | */ 20 | export async function repoInfo(filePath: string): Promise { 21 | try { 22 | // Determine repository root directory. 23 | const fileDirectory = path.dirname(filePath) 24 | const repoRoot = await gitHelpers.rootDirectory(fileDirectory) 25 | 26 | // Determine file path relative to repository root. 27 | let fileRelative = filePath.slice(repoRoot.length + 1) 28 | 29 | let { branch, remoteName } = await gitRemoteNameAndBranch(repoRoot, gitHelpers, log) 30 | const remoteURL = await gitRemoteUrlWithReplacements( 31 | repoRoot, 32 | remoteName, 33 | getRemoteUrlReplacements, 34 | gitHelpers, 35 | log 36 | ) 37 | branch = getDefaultBranch() || branch 38 | 39 | if (process.platform === 'win32') { 40 | fileRelative = fileRelative.replace(/\\/g, '/') 41 | } 42 | log.appendLine(`repoInfo(${filePath}): remoteURL="${remoteURL}" branch="${branch}" fileRel="${fileRelative}"`) 43 | return { remoteURL, branch, fileRelative } 44 | } catch (error) { 45 | log.appendLine(`repoInfo(${filePath}): ${error as string}`) 46 | return undefined 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/git/remoteNameAndBranch.ts: -------------------------------------------------------------------------------- 1 | import { GitHelpers } from './helpers' 2 | 3 | export interface RemoteName { 4 | /** 5 | * Remote name of the upstream repository, 6 | * or the first found remote name if no upstream is found 7 | */ 8 | remoteName: string 9 | } 10 | 11 | export interface Branch { 12 | /** 13 | * Remote branch name, or 'HEAD' if it isn't found because 14 | * e.g. detached HEAD state, upstream branch points to a local branch 15 | */ 16 | branch: string 17 | } 18 | 19 | /** 20 | * Returns the remote name and branch 21 | * 22 | * @param repoDirectory the repository root directory 23 | * @param git gitHelpers object 24 | * 25 | */ 26 | export async function gitRemoteNameAndBranch( 27 | repoDirectory: string, 28 | git: Pick, 29 | log?: { 30 | appendLine: (value: string) => void 31 | } 32 | ): Promise { 33 | let remoteName: string | undefined 34 | 35 | // Used to determine which part of upstreamAndBranch is the remote name, or as fallback if no upstream is set 36 | const remotes = await git.remotes(repoDirectory) 37 | const branch = await git.branch(repoDirectory) 38 | 39 | try { 40 | const upstreamAndBranch = await git.upstreamAndBranch(repoDirectory) 41 | // Subtract $BRANCH_NAME from $UPSTREAM_REMOTE/$BRANCH_NAME. 42 | // We can't just split on the delineating `/`, since refnames can include `/`: 43 | // https://sourcegraph.com/github.com/git/git@454cb6bd52a4de614a3633e4f547af03d5c3b640/-/blob/refs.c#L52-67 44 | 45 | // Example: 46 | // stdout: remote/two/tj/feature 47 | // remoteName: remote/two, branch: tj/feature 48 | 49 | const branchPosition = upstreamAndBranch.lastIndexOf(branch) 50 | const maybeRemote = upstreamAndBranch.slice(0, branchPosition - 1) 51 | if (branchPosition !== -1 && maybeRemote) { 52 | remoteName = maybeRemote 53 | } 54 | } catch { 55 | // noop. upstream may not be set 56 | } 57 | 58 | // If we cannot find the remote name deterministically, we use the first 59 | // Git remote found. 60 | if (!remoteName) { 61 | if (remotes.length > 1) { 62 | log?.appendLine(`no upstream found, using first git remote: ${remotes[0]}`) 63 | } 64 | remoteName = remotes[0] 65 | } 66 | 67 | // Throw if a remote still isn't found 68 | if (!remoteName) { 69 | throw new Error('no configured git remotes') 70 | } 71 | 72 | return { remoteName, branch } 73 | } 74 | -------------------------------------------------------------------------------- /src/git/remoteNameAndBranch.test.ts: -------------------------------------------------------------------------------- 1 | import assert from 'assert' 2 | import { GitHelpers } from './helpers' 3 | import { gitRemoteNameAndBranch } from './remoteNameAndBranch' 4 | 5 | describe('git', () => { 6 | function createMockGitHelpers( 7 | remotes: string[], 8 | branch: string, 9 | upstreamAndBranch: string 10 | ): Pick { 11 | return { 12 | remotes: () => Promise.resolve(remotes), 13 | branch: () => Promise.resolve(branch), 14 | upstreamAndBranch: () => { 15 | if (!upstreamAndBranch) { 16 | throw new Error(`fatal: no upstream configured for branch ${branch}`) 17 | } 18 | 19 | return Promise.resolve(upstreamAndBranch) 20 | }, 21 | } 22 | } 23 | 24 | describe('gitRemoteNameAndBranch()', () => { 25 | it('handles simple upstream and branch names', async () => { 26 | const { remoteName, branch } = await gitRemoteNameAndBranch( 27 | '', 28 | createMockGitHelpers(['origin'], 'feature', 'origin/feature') 29 | ) 30 | 31 | assert.strictEqual(remoteName, 'origin', 'incorrect remote name') 32 | assert.strictEqual(branch, 'feature', 'incorrect branch name') 33 | }) 34 | 35 | it('handles branch names with slashes', async () => { 36 | const { remoteName, branch } = await gitRemoteNameAndBranch( 37 | '', 38 | createMockGitHelpers(['origin'], 'author/feature', 'origin/author/feature') 39 | ) 40 | 41 | assert.strictEqual(remoteName, 'origin', 'incorrect remote name') 42 | assert.strictEqual(branch, 'author/feature', 'incorrect branch name') 43 | }) 44 | 45 | it('handles remote and branch names with slashes', async () => { 46 | const { remoteName, branch } = await gitRemoteNameAndBranch( 47 | '', 48 | createMockGitHelpers(['remote/one', 'remote/two'], 'feature', 'remote/two/feature') 49 | ) 50 | 51 | assert.strictEqual(remoteName, 'remote/two', 'incorrect remote name') 52 | assert.strictEqual(branch, 'feature', 'incorrect branch name') 53 | }) 54 | 55 | it('falls back to first remote when no upstream is configured', async () => { 56 | const { remoteName, branch } = await gitRemoteNameAndBranch( 57 | '', 58 | createMockGitHelpers(['remote-one', 'remote-two', 'remote-three'], 'feature', '') 59 | ) 60 | 61 | assert.strictEqual(remoteName, 'remote-one', 'incorrect remote name') 62 | assert.strictEqual(branch, 'feature', 'incorrect branch name') 63 | }) 64 | 65 | it('falls back to first remote and branch when branch is not pushed to upstream', async () => { 66 | const { remoteName, branch } = await gitRemoteNameAndBranch( 67 | '', 68 | createMockGitHelpers(['origin'], 'feature', 'origin/master') 69 | ) 70 | 71 | assert.strictEqual(remoteName, 'origin', 'incorrect remote name') 72 | assert.strictEqual(branch, 'feature', 'incorrect branch name') 73 | }) 74 | }) 75 | }) 76 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import open from 'open' 2 | import * as vscode from 'vscode' 3 | import { getSourcegraphUrl } from './config' 4 | import { repoInfo } from './git' 5 | 6 | // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires 7 | const { version } = require('../package.json') 8 | 9 | /** 10 | * Displays an error message to the user. 11 | */ 12 | async function showError(error: Error): Promise { 13 | await vscode.window.showErrorMessage(error.message) 14 | } 15 | 16 | const handleCommandErrors =

(command: (...args: P) => Promise) => async ( 17 | ...args: P 18 | ): Promise => { 19 | try { 20 | return await command(...args) 21 | } catch (error) { 22 | await showError(error) 23 | } 24 | } 25 | 26 | /** 27 | * The command implementation for opening a cursor selection on Sourcegraph. 28 | */ 29 | async function openCommand(): Promise { 30 | const editor = vscode.window.activeTextEditor 31 | if (!editor) { 32 | throw new Error('No active editor') 33 | } 34 | const repositoryInfo = await repoInfo(editor.document.uri.fsPath) 35 | if (!repositoryInfo) { 36 | return 37 | } 38 | const { remoteURL, branch, fileRelative } = repositoryInfo 39 | 40 | // Open in browser. 41 | await open( 42 | `${getSourcegraphUrl()}/-/editor` + 43 | `?remote_url=${encodeURIComponent(remoteURL)}` + 44 | `&branch=${encodeURIComponent(branch)}` + 45 | `&file=${encodeURIComponent(fileRelative)}` + 46 | `&editor=${encodeURIComponent('VSCode')}` + 47 | `&version=${encodeURIComponent(version)}` + 48 | `&start_row=${encodeURIComponent(String(editor.selection.start.line))}` + 49 | `&start_col=${encodeURIComponent(String(editor.selection.start.character))}` + 50 | `&end_row=${encodeURIComponent(String(editor.selection.end.line))}` + 51 | `&end_col=${encodeURIComponent(String(editor.selection.end.character))}` 52 | ) 53 | } 54 | 55 | /** 56 | * The command implementation for searching a cursor selection on Sourcegraph. 57 | */ 58 | async function searchSelectionCommand(): Promise { 59 | const editor = vscode.window.activeTextEditor 60 | if (!editor) { 61 | throw new Error('No active editor') 62 | } 63 | const repositoryInfo = await repoInfo(editor.document.uri.fsPath) 64 | if (!repositoryInfo) { 65 | return 66 | } 67 | const { remoteURL, branch, fileRelative } = repositoryInfo 68 | 69 | const query = editor.document.getText(editor.selection) 70 | if (query === '') { 71 | return // nothing to query 72 | } 73 | 74 | // Search in browser. 75 | await open( 76 | `${getSourcegraphUrl()}/-/editor` + 77 | `?remote_url=${encodeURIComponent(remoteURL)}` + 78 | `&branch=${encodeURIComponent(branch)}` + 79 | `&file=${encodeURIComponent(fileRelative)}` + 80 | `&editor=${encodeURIComponent('VSCode')}` + 81 | `&version=${encodeURIComponent(version)}` + 82 | `&search=${encodeURIComponent(query)}` 83 | ) 84 | } 85 | 86 | /** 87 | * The command implementation for searching on Sourcegraph.com 88 | */ 89 | 90 | async function searchCommand(): Promise { 91 | await vscode.window.showInputBox().then(value => { 92 | if (!value) { 93 | return 94 | } 95 | return open(`${getSourcegraphUrl()}/search?patternType=literal&q=${encodeURIComponent(value)}`) 96 | }) 97 | } 98 | 99 | /** 100 | * Called when the extension is activated. 101 | */ 102 | export function activate(context: vscode.ExtensionContext): void { 103 | // Register our extension commands (see package.json). 104 | context.subscriptions.push(vscode.commands.registerCommand('extension.open', handleCommandErrors(openCommand))) 105 | context.subscriptions.push( 106 | vscode.commands.registerCommand('extension.search.selection', handleCommandErrors(searchSelectionCommand)) 107 | ) 108 | context.subscriptions.push(vscode.commands.registerCommand('extension.search', handleCommandErrors(searchCommand))) 109 | } 110 | 111 | export function deactivate(): void { 112 | // no-op 113 | } 114 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sourcegraph", 3 | "displayName": "Sourcegraph", 4 | "description": "Sourcegraph for VS Code", 5 | "version": "1.0.16", 6 | "publisher": "sourcegraph", 7 | "license": "MIT", 8 | "icon": "images/logo.png", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/sourcegraph/sourcegraph-vscode.git" 12 | }, 13 | "engines": { 14 | "vscode": "^1.52.0" 15 | }, 16 | "categories": [ 17 | "Other" 18 | ], 19 | "activationEvents": [ 20 | "onCommand:extension.open", 21 | "onCommand:extension.search.selection", 22 | "onCommand:extension.search" 23 | ], 24 | "main": "./out/extension", 25 | "contributes": { 26 | "commands": [ 27 | { 28 | "command": "extension.open", 29 | "title": "Sourcegraph: Open" 30 | }, 31 | { 32 | "command": "extension.search.selection", 33 | "title": "Sourcegraph: Search Selection" 34 | }, 35 | { 36 | "command": "extension.search", 37 | "title": "Sourcegraph: Search" 38 | } 39 | ], 40 | "keybindings": [ 41 | { 42 | "command": "extension.open", 43 | "key": "alt+a", 44 | "mac": "alt+a", 45 | "when": "editorFocus" 46 | }, 47 | { 48 | "command": "extension.search.selection", 49 | "key": "alt+s", 50 | "mac": "alt+s", 51 | "when": "editorFocus" 52 | }, 53 | { 54 | "command": "extension.search", 55 | "key": "alt+q", 56 | "mac": "alt+q" 57 | } 58 | ], 59 | "configuration": { 60 | "type": "object", 61 | "title": "Sourcegraph extension configuration", 62 | "properties": { 63 | "sourcegraph.url": { 64 | "type": [ 65 | "string" 66 | ], 67 | "default": "https://sourcegraph.com", 68 | "description": "The base URL of the Sourcegraph instance to use." 69 | }, 70 | "sourcegraph.remoteUrlReplacements": { 71 | "type": [ 72 | "object" 73 | ], 74 | "default": {}, 75 | "description": "For each item in this object, replace key with value in the remote url." 76 | }, 77 | "sourcegraph.defaultBranch": { 78 | "type": [ 79 | "string" 80 | ], 81 | "default": "", 82 | "description": "Always open files at this default branch." 83 | } 84 | } 85 | } 86 | }, 87 | "commitlint": { 88 | "extends": [ 89 | "@commitlint/config-conventional" 90 | ] 91 | }, 92 | "release": { 93 | "verifyConditions": [ 94 | "semantic-release-vsce", 95 | "@semantic-release/github" 96 | ], 97 | "prepare": { 98 | "path": "semantic-release-vsce", 99 | "packageVsix": "sourcegraph-vscode.vsix" 100 | }, 101 | "publish": [ 102 | "semantic-release-vsce", 103 | { 104 | "path": "@semantic-release/github", 105 | "assets": "sourcegraph-vscode.vsix" 106 | } 107 | ] 108 | }, 109 | "scripts": { 110 | "semantic-release": "semantic-release", 111 | "prettier": "prettier \"**/*.{ts,js,json,md,yml}\" --list-different --write", 112 | "prettier-check": "npm run prettier -- --write=false", 113 | "eslint": "eslint 'src/**/*.ts'", 114 | "build": "tsc -p .", 115 | "watch": "tsc -w -p .", 116 | "test": "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' nyc mocha --require source-map-support/register --package package.json" 117 | }, 118 | "nyc": { 119 | "require": "ts-node/register", 120 | "include": [ 121 | "src/**/*.ts" 122 | ], 123 | "exclude": [ 124 | "**/*.test.ts" 125 | ], 126 | "extension": [ 127 | ".ts" 128 | ] 129 | }, 130 | "mocha": { 131 | "recursive": true, 132 | "timeout": 200, 133 | "watch-extensions": [ 134 | "ts" 135 | ], 136 | "require": "ts-node/register", 137 | "spec": "src/**/*.test.ts" 138 | }, 139 | "devDependencies": { 140 | "@commitlint/cli": "^13.2.0", 141 | "@commitlint/config-conventional": "^13.2.0", 142 | "@sourcegraph/eslint-config": "^0.23.0", 143 | "@sourcegraph/prettierrc": "^3.0.3", 144 | "@sourcegraph/tsconfig": "^4.0.1", 145 | "@types/node": "16.11.26", 146 | "@types/vscode": "1.52.0", 147 | "eslint": "^7.32.0", 148 | "husky": "^4.3.6", 149 | "nyc": "^15.1.0", 150 | "prettier": "^2.2.1", 151 | "semantic-release": "^17.3.0", 152 | "semantic-release-vsce": "^3.0.1", 153 | "typescript": "^4.1.3", 154 | "vsce": "^1.81.1" 155 | }, 156 | "dependencies": { 157 | "@types/mocha": "8.2.0", 158 | "execa": "^5.0.0", 159 | "mocha": "^8.3.0", 160 | "open": "^7.4.2", 161 | "ts-node": "^9.1.1" 162 | }, 163 | "husky": { 164 | "hooks": { 165 | "commit-msg": "commitlint -e $HUSKY_GIT_PARAMS" 166 | } 167 | } 168 | } 169 | --------------------------------------------------------------------------------