@@ -293,6 +293,24 @@ argument also offer closed pull-requests."
293293 (interactive (list (forge-read-repository " Browse repository" )))
294294 (browse-url (forge-get-url repository)))
295295
296+ ;;;### autoload
297+ (defun forge-browse-blob (commit file &optional line end force-hash )
298+ " Visit a blob using a browser.
299+
300+ When invoked from a blob- or file-visiting buffer, visit that blob
301+ without prompting. If the region is active, try to jump to the marked
302+ line or lines, and highlight them in the browser. To what extend that
303+ is possible depends on the forge. When the region is not active just
304+ visit the blob, without trying to jump to the current line. When
305+ jumping to a line, always use a commit hash as part of the URL. From
306+ a file in the worktree with no active region, instead use the branch
307+ name as part of the URL, unless a prefix argument is used.
308+
309+ When invoked from any other buffer, prompt the user for a branch or
310+ commit, and for a file."
311+ (interactive (forge--browse-blob-args))
312+ (browse-url (forge-get-url :blob commit file line end force-hash)))
313+
296314;;;### autoload (autoload 'forge-browse-this-topic "forge-commands" nil t)
297315(transient-define-suffix forge-browse-this-topic ()
298316 " Visit the topic at point using a browser."
@@ -308,7 +326,7 @@ argument also offer closed pull-requests."
308326
309327;;;### autoload
310328(defun forge-copy-url-at-point-as-kill ()
311- " Copy the url of the thing at point."
329+ " Copy the url of thing at point or the thing visited in the current buffer ."
312330 (interactive )
313331 (if-let ((target (forge--browse-target)))
314332 (let ((url (if (stringp target) target (forge-get-url target))))
@@ -337,12 +355,32 @@ argument also offer closed pull-requests."
337355 (forge-get-url :branch branch))
338356 (and-let* ((remote (magit-remote-at-point)))
339357 (forge-get-url :remote remote))
358+ (and-let* ((file (magit-file-at-point)))
359+ (forge-get-url :blob nil file))
340360 (forge-post-at-point)
341361 (forge-current-topic)
362+ (and (or magit-buffer-file-name buffer-file-name)
363+ (apply #'forge-get-url :blob (forge--browse-blob-args)))
342364 (and magit-buffer-revision
343365 (forge-get-url :commit magit-buffer-revision))
344366 (forge-get-repository :stub? )))
345367
368+ (defun forge--browse-blob-args ()
369+ (cond
370+ (magit-buffer-file-name
371+ `(,(or magit-buffer-refname magit-buffer-revision)
372+ ,(magit-file-relative-name magit-buffer-file-name)
373+ ,@(magit-file-region-line-numbers)
374+ , current-prefix-arg ))
375+ (buffer-file-name
376+ `(nil
377+ ,(magit-file-relative-name buffer-file-name)
378+ ,@(magit-file-region-line-numbers)
379+ , current-prefix-arg ))
380+ ((let ((commit (magit-read-local-branch-or-commit
381+ " Browse file from commit" )))
382+ (list commit (magit-read-file-from-rev commit " Browse file" ))))))
383+
346384; ;;; Urls
347385
348386(cl-defgeneric forge-get-url (obj)
@@ -369,6 +407,23 @@ argument also offer closed pull-requests."
369407 (forge--format repo 'commit-url-format
370408 `((?r . ,(magit-commit-p commit))))))
371409
410+ (cl-defmethod forge-get-url ((_(eql :blob )) commit file
411+ &optional line end force-hash)
412+ (let* ((commit (or (and (magit-branch-p commit)
413+ (cdr (magit-split-branch-name commit)))
414+ (and commit (magit-commit-p commit))
415+ (and (not (or line force-hash))
416+ (magit-get-current-branch))
417+ (magit-rev-parse " HEAD" )))
418+ (repo (forge-get-repository :stub ))
419+ (format (oref repo blob-url-format)))
420+ (when (cl-typep repo 'forge-gitweb-repository )
421+ (setq commit (concat (if (magit-branch-p commit) " hb=" " h=" ) commit)))
422+ (concat
423+ (forge--format repo format `((?r . , commit ) (?f . , file )))
424+ (and line (forge-format-blob-lines repo line
425+ (and (not (equal line end)) end))))))
426+
372427(cl-defmethod forge-get-url ((_(eql :branch )) branch)
373428 (let (remote)
374429 (if (magit-remote-branch-p branch)
@@ -393,6 +448,27 @@ argument also offer closed pull-requests."
393448(cl-defmethod forge-get-url ((notify forge-notification))
394449 (oref notify url))
395450
451+ (cl-defmethod forge-format-blob-lines ((repo forge-repository) line end)
452+ (cl-etypecase repo ; Third-party classes require separate methods.
453+ ((or forge-github-repository
454+ forge-gitlab-repository ; Also supports "#L%s-%s".
455+ forge-forgejo-repository
456+ forge-gitea-repository
457+ forge-gogs-repository)
458+ (format (if end " #L%s-L%s" " #L%s" ) line end))
459+ (forge-bitbucket-repository
460+ (format (if end " #lines-%s:%s" " #lines-%s" ) line end))
461+ ((or forge-cgit-repository
462+ forge-cgit*-repository
463+ forge-cgit**-repository)
464+ (format " #n%s " line))
465+ ((or forge-gitweb-repository
466+ forge-repoorcz-repository
467+ forge-stagit-repository)
468+ (format " #l%s " line))
469+ (forge-srht-repository
470+ (format " #L%s " line))))
471+
396472; ;; Visit
397473
398474;;;### autoload
0 commit comments