aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--autoload/git.kak15
-rwxr-xr-xscripts/git.clj71
2 files changed, 84 insertions, 2 deletions
diff --git a/autoload/git.kak b/autoload/git.kak
index 59ac173..775cf0d 100644
--- a/autoload/git.kak
+++ b/autoload/git.kak
@@ -6,8 +6,6 @@ define-command gitui -params .. %{
gitu %arg{@}
}
-# TODO: Script to open git remote url (github/srht/gitlab/etc)
-
declare-user-mode git
declare-user-mode git-r
declare-user-mode git-d
@@ -16,6 +14,7 @@ map global git s ': gitui<ret>' -docstring 'Git tui'
map global git A ': git add %val{buffile}<ret>' -docstring 'Add file'
map global git m ': git-line-blame<ret>' -docstring 'Blame selection lines'
map global git M ': git-file-blame<ret>' -docstring 'Blame buffer file'
+map global git l ': git-link<ret>' -docstring 'Open remote link to selection in file'
map global git d ': enter-user-mode git-d<ret>' -docstring 'Diff mode'
map global git-d d ': git-open-diff<ret>' -docstring 'Open staged files'
@@ -53,3 +52,15 @@ define-command git-open-diff -params 0..1 %{
define-command git-open-commit -params 0..1 %{
eval %sh{ git show --name-only --pretty="" "$@" | sed 's/^/edit /' }
}
+
+define-command git-link %{
+ eval %sh{
+ line_start=${kak_selection_desc%%.*}
+ tmp=${kak_selection_desc#*,}
+ line_end=${tmp%%.*}
+ link=$("$kak_config/scripts/git.clj" link "$kak_buffile" "$line_start" "$line_end")
+ xdg-open "$link" || true >/dev/null
+ echo "info '$link'"
+ }
+}
+
diff --git a/scripts/git.clj b/scripts/git.clj
new file mode 100755
index 0000000..c1e450b
--- /dev/null
+++ b/scripts/git.clj
@@ -0,0 +1,71 @@
+#!/usr/bin/env bb
+
+(require '[clojure.java.shell :refer [sh]])
+(require '[clojure.string :as str])
+(require '[clojure.java.io :as io])
+
+(defn dirname [path] (.getParent (io/file path)))
+
+(defn git [dir & args]
+ (let [proc (apply sh "git" "-C" (or dir ".") args)]
+ (when (= (:exit proc) 0)
+ (str/trim (:out proc)))))
+
+(defn git-remote-origin [dir]
+ (not-empty (git dir "remote" "get-url" "origin")))
+
+(defn git-first-remote [dir]
+ (some->> (git dir "remote" "-v")
+ str/split-lines
+ (keep #(re-matches #"^\w+\s+(.*)\s+\(fetch\)" %))
+ first
+ second))
+
+(defn prepare-link [remote-url rev file-path & [line-start line-end]]
+ (let [parse-path #(some->> (re-matches % remote-url) second)
+ line-hash (cond
+ (nil? line-start) nil
+ (= line-start line-end) (str "#L" line-start)
+ (some? line-end) (str "#L" line-start "-L" line-end)
+ (nil? line-end) (str "#L" line-start))]
+ (some->>
+ (cond
+ (empty? remote-url) nil
+ (.contains remote-url "github.com:") ["https://github.com" (parse-path #".*github.com:(.*)\.git$") "blob" rev (str file-path line-hash)]
+ (.contains remote-url "github.com/") ["https://github.com" (parse-path #".*github.com/(.*)(\.git)?$") "blob" rev (str file-path line-hash)]
+ (.contains remote-url "gitlab.com:") ["https://gitlab.com" (parse-path #".*gitlab.com:(.*)\.git$") "-/blob" rev (str file-path line-hash)]
+ (.contains remote-url "gitlab.com/") ["https://gitlab.com" (parse-path #".*gitlab.com/(.*)(\.git)?$") "-/blob" rev (str file-path line-hash)]
+ (.contains remote-url "git.sr.ht:") ["https://git.sr.ht" (parse-path #".*git.sr.ht:(.*)\.git$") "tree" rev (str file-path line-hash)]
+ (.contains remote-url "git.sr.ht/") ["https://git.sr.ht" (parse-path #".*git.sr.ht/(.*)(\.git)?$") "tree" rev (str file-path line-hash)]
+ :else nil)
+ (remove nil?)
+ (str/join "/"))))
+
+(defn git-current-rev [dir] (git dir "rev-parse" "--short" "HEAD"))
+
+(defn git-remote-url [dir file-path revision line-start line-end]
+ (some-> (or (git-remote-origin dir) (git-first-remote dir))
+ (prepare-link revision file-path line-start line-end)))
+
+(defn git-root [path]
+ (let [file-dir (if (empty? path) "." (dirname path))]
+ (some->> (git file-dir "rev-parse" "--absolute-git-dir")
+ dirname)))
+
+(defn cmd-link [[file-path line-start line-end rev]]
+ (let [dir (git-root file-path)
+ revision (or rev (git-current-rev dir) "main")
+ relative-file-path (str/trim (:out (sh "realpath" "--relative-to" dir file-path)))
+ remote-url (git-remote-url dir relative-file-path revision line-start line-end)]
+ (println remote-url)))
+
+(def commands
+ {"link" cmd-link})
+
+(let [[cmd & args] *command-line-args*]
+ (if-let [command-fn (commands cmd)]
+ (command-fn args)
+ (binding [*out* *err*]
+ (println (str "invalid command: " (or cmd "")))
+ (println (str "Valid commands: " (keys commands))))))
+