| # Copyright 2022 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Use with an IDE to copy a link to the current file/line to your clipboard. |
| |
| Public and private repos are supported. The internal/public arguments are only |
| used if the repo is public, private repos always link to the internal code |
| search. |
| |
| By default always generates a link to the public code search, but can also |
| generate links to the internal code search. |
| |
| Currently only works locally and if using X11. |
| |
| To allow use over SSH, enable X11 forwarding. |
| Client side: -X, or add "X11Forwarding yes" to your ~/.ssh/config. |
| Server side: "X11Forwarding yes" must be in /etc/ssh/sshd_config. |
| |
| IDE integration instructions. Please add your IDE if it's not listed, or update |
| the instructions if they're vague or out of date! |
| |
| For Intellij: |
| Create a new External Tool (Settings > Tools > External Tools). |
| Tool Settings: |
| Program: ~/chromiumos/chromite/contrib/generate_cs_path |
| Arguments: $FilePath$ -l $LineNumber$ |
| |
| For VSCode: |
| Create a custom task (code.visualstudio.com/docs/editor/tasks#_custom-tasks) |
| with the command: |
| ~/chromiumos/chromite/contrib/generate_cs_path ${file} -l ${lineNumber} |
| """ |
| |
| import os |
| from pathlib import Path |
| import sys |
| |
| from chromite.lib import commandline |
| from chromite.lib import constants |
| from chromite.lib import cros_build_lib |
| from chromite.lib import git |
| |
| |
| class CodeSearch(object): |
| """format returns a url to the code specified""" |
| @classmethod |
| def format(cls, attrs, opts, checkout_path, relative_path): |
| raise NotImplementedError() |
| |
| class Gitiles(CodeSearch): |
| """format returns a url to the code specified""" |
| @classmethod |
| def format(cls, attrs, opts, checkout_path, relative_path): |
| line = f'#{opts.line}' if opts.line else '' |
| return f'{attrs["push_url"]}/+/{attrs["sha"]}/{relative_path}{line}' |
| |
| |
| class PublicCS(CodeSearch): |
| """format returns a url to the code specified""" |
| @classmethod |
| def format(cls, attrs, opts, checkout_path, relative_path): |
| line = f';l={opts.line}' if opts.line else '' |
| sha = attrs['sha'] if opts.upstream_sha else 'HEAD' |
| # HEAD is the ref for the codesearch repo, not the project itself. |
| return ('https://source.chromium.org/chromiumos/chromiumos/codesearch/+/' |
| + f'{sha}:{checkout_path}/{relative_path}{line}') |
| |
| |
| class InternalCS(CodeSearch): |
| """format returns a url to the code specified""" |
| @classmethod |
| def format(cls, attrs, opts, checkout_path, relative_path): |
| line = f';l={opts.line}' if opts.line else '' |
| sha = f';rcl={attrs["sha"]}' if opts.upstream_sha else '' |
| return ('http://cs/chromeos_public/' |
| + f'{checkout_path}/{relative_path}{line}{sha}') |
| |
| |
| class PrivateCS(CodeSearch): |
| """format returns a url to the code specified""" |
| @classmethod |
| def format(cls, attrs, opts, checkout_path, relative_path): |
| line = f';l={opts.line}' if opts.line else '' |
| sha = f';rcl={attrs["sha"]}' if opts.upstream_sha else '' |
| return ('http://cs/chromeos_internal/' |
| + f'{checkout_path}/{relative_path}{line}{sha}') |
| |
| |
| def GetParser(): |
| """Build the argument parser.""" |
| parser = commandline.ArgumentParser(description=__doc__) |
| |
| # Public/internal code search selection arguments. |
| type_group = parser.add_mutually_exclusive_group() |
| type_group.add_argument( |
| '-p', |
| '--public', |
| dest='public_link', |
| action='store_true', |
| default=True, |
| help='Generate a link to the public code search.') |
| type_group.add_argument( |
| '-i', |
| '--internal', |
| dest='public_link', |
| action='store_false', |
| help='Generate a link to the internal code search.') |
| type_group.add_argument( |
| '-g', |
| '--gitiles', |
| action='store_true', |
| default=False, |
| help='Generate a gitiles link rather than a code search link.' |
| ) |
| |
| parser.add_argument('-l', '--line', type=int, help='Line number.') |
| parser.add_argument('--upstream-sha', action='store_true', |
| help='Link to the upstream sha.') |
| |
| |
| action_group = parser.add_mutually_exclusive_group() |
| action_group.add_argument( |
| '-o', |
| '--open', |
| action='store_true', |
| default=False, |
| help='Open the link in a browser rather than copying it to the clipboard.' |
| ) |
| action_group.add_argument( |
| '-s', |
| '--show', |
| action='store_true', |
| default=False, |
| help='Output the link to stdout rather than copying it to the clipboard.' |
| ) |
| |
| parser.add_argument('path', type='path', help='Path to a file.') |
| |
| return parser |
| |
| |
| def ParseArguments(argv): |
| """Parse and validate arguments.""" |
| parser = GetParser() |
| opts = parser.parse_args(argv) |
| |
| opts.path = Path(opts.path).relative_to(constants.SOURCE_ROOT) |
| |
| opts.Freeze() |
| return opts |
| |
| |
| def GenerateLink(attrs, opts, checkout_path, relative_path): |
| """Generate link to CS based on on parsed arguments and checkout.""" |
| if opts.gitiles: |
| base = Gitiles |
| elif attrs.get('remote_alias') == 'cros-internal': |
| # Private repos not on public CS, so force internal private. |
| base = PrivateCS |
| elif opts.public_link: |
| base = PublicCS |
| else: |
| base = InternalCS |
| |
| return base.format(attrs, opts, checkout_path, relative_path) |
| |
| def main(argv): |
| opts = ParseArguments(argv) |
| |
| checkout = git.ManifestCheckout.Cached(opts.path) |
| |
| # Find the project. |
| checkout_path = None |
| attrs = {} |
| for checkout_path, attrs in checkout.checkouts_by_path.items(): |
| try: |
| relative_path = opts.path.relative_to(checkout_path) |
| break |
| except ValueError: |
| continue |
| else: |
| cros_build_lib.Die('No project found for %s.', opts.path) |
| |
| if opts.upstream_sha: |
| attrs['sha'] = git.RunGit( |
| attrs['local_path'], ['rev-parse', attrs['tracking_branch']] |
| ).stdout.strip() |
| else: |
| attrs['sha'] = git.RunGit( |
| attrs['local_path'], ['rev-parse', 'HEAD'] |
| ).stdout.strip() |
| |
| final_link = GenerateLink(attrs, opts, checkout_path, relative_path) |
| |
| is_mac_os = sys.platform.startswith('darwin') |
| |
| if opts.open: |
| cmd = ['open' if is_mac_os else 'xdg-open', final_link] |
| os.execvp(cmd[0], cmd) |
| elif opts.show: |
| print(final_link) |
| else: |
| cmd = ['pbcopy'] if is_mac_os else ['xsel', '--clipboard', '--input'] |
| cros_build_lib.run(cmd, input=final_link) |