mirror of https://git.jolheiser.com/ugit.git
parent
a56081be17
commit
1f8b18f963
|
@ -0,0 +1,150 @@
|
||||||
|
package git
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-git/go-git/v5"
|
||||||
|
"github.com/go-git/go-git/v5/plumbing/object"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GrepResult is the result of a search
|
||||||
|
type GrepResult struct {
|
||||||
|
File string
|
||||||
|
StartLine int
|
||||||
|
Line int
|
||||||
|
Content string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grep performs a naive "code search" via git grep
|
||||||
|
func (r Repo) Grep(search string) ([]GrepResult, error) {
|
||||||
|
// Plain-text search only
|
||||||
|
re, err := regexp.Compile(regexp.QuoteMeta(search))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
repo, err := r.Git()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loosely modifed from
|
||||||
|
// https://github.com/go-git/go-git/blob/fb04aa392c8d4c259cb5b21c1cb4c6f8076e600b/options.go#L736-L740
|
||||||
|
// https://github.com/go-git/go-git/blob/fb04aa392c8d4c259cb5b21c1cb4c6f8076e600b/worktree.go#L753-L760
|
||||||
|
ref, err := repo.Head()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
commit, err := repo.CommitObject(ref.Hash())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tree, err := commit.Tree()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return findMatchInFiles(tree.Files(), ref.Hash().String(), &git.GrepOptions{
|
||||||
|
Patterns: []*regexp.Regexp{re},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lines below are copied and modifed from https://github.com/go-git/go-git/blob/fb04aa392c8d4c259cb5b21c1cb4c6f8076e600b/worktree.go#L961-L1045
|
||||||
|
|
||||||
|
// findMatchInFiles takes a FileIter, worktree name and GrepOptions, and
|
||||||
|
// returns a slice of GrepResult containing the result of regex pattern matching
|
||||||
|
// in content of all the files.
|
||||||
|
func findMatchInFiles(fileiter *object.FileIter, treeName string, opts *git.GrepOptions) ([]GrepResult, error) {
|
||||||
|
var results []GrepResult
|
||||||
|
|
||||||
|
err := fileiter.ForEach(func(file *object.File) error {
|
||||||
|
var fileInPathSpec bool
|
||||||
|
|
||||||
|
// When no pathspecs are provided, search all the files.
|
||||||
|
if len(opts.PathSpecs) == 0 {
|
||||||
|
fileInPathSpec = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the file name matches with the pathspec. Break out of the
|
||||||
|
// loop once a match is found.
|
||||||
|
for _, pathSpec := range opts.PathSpecs {
|
||||||
|
if pathSpec != nil && pathSpec.MatchString(file.Name) {
|
||||||
|
fileInPathSpec = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the file does not match with any of the pathspec, skip it.
|
||||||
|
if !fileInPathSpec {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
grepResults, err := findMatchInFile(file, treeName, opts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
results = append(results, grepResults...)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// findMatchInFile takes a single File, worktree name and GrepOptions,
|
||||||
|
// and returns a slice of GrepResult containing the result of regex pattern
|
||||||
|
// matching in the given file.
|
||||||
|
func findMatchInFile(file *object.File, treeName string, opts *git.GrepOptions) ([]GrepResult, error) {
|
||||||
|
var grepResults []GrepResult
|
||||||
|
|
||||||
|
content, err := file.Contents()
|
||||||
|
if err != nil {
|
||||||
|
return grepResults, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split the file content and parse line-by-line.
|
||||||
|
contentByLine := strings.Split(content, "\n")
|
||||||
|
for lineNum, cnt := range contentByLine {
|
||||||
|
addToResult := false
|
||||||
|
|
||||||
|
// Match the patterns and content. Break out of the loop once a
|
||||||
|
// match is found.
|
||||||
|
for _, pattern := range opts.Patterns {
|
||||||
|
if pattern != nil && pattern.MatchString(cnt) {
|
||||||
|
// Add to result only if invert match is not enabled.
|
||||||
|
if !opts.InvertMatch {
|
||||||
|
addToResult = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else if opts.InvertMatch {
|
||||||
|
// If matching fails, and invert match is enabled, add to
|
||||||
|
// results.
|
||||||
|
addToResult = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if addToResult {
|
||||||
|
startLine := lineNum + 1
|
||||||
|
ctx := []string{cnt}
|
||||||
|
if lineNum != 0 {
|
||||||
|
startLine -= 1
|
||||||
|
ctx = append([]string{contentByLine[lineNum-1]}, ctx...)
|
||||||
|
}
|
||||||
|
if lineNum != len(contentByLine)-1 {
|
||||||
|
ctx = append(ctx, contentByLine[lineNum+1])
|
||||||
|
}
|
||||||
|
grepResults = append(grepResults, GrepResult{
|
||||||
|
File: file.Name,
|
||||||
|
StartLine: startLine,
|
||||||
|
Line: lineNum + 1,
|
||||||
|
Content: strings.Join(ctx, "\n"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return grepResults, nil
|
||||||
|
}
|
|
@ -43,7 +43,7 @@
|
||||||
background: rgb(var(--ctp-surface0)) !important;
|
background: rgb(var(--ctp-surface0)) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.commit .chroma {
|
.code>.chroma {
|
||||||
border-radius: .25rem;
|
border-radius: .25rem;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
}
|
}
|
|
@ -26,7 +26,7 @@ var (
|
||||||
|
|
||||||
type code struct{}
|
type code struct{}
|
||||||
|
|
||||||
func (c code) setup(source []byte, fileName string) (chroma.Iterator, *chroma.Style, error) {
|
func setup(source []byte, fileName string) (chroma.Iterator, *chroma.Style, error) {
|
||||||
lexer := lexers.Match(fileName)
|
lexer := lexers.Match(fileName)
|
||||||
if lexer == nil {
|
if lexer == nil {
|
||||||
lexer = lexers.Fallback
|
lexer = lexers.Fallback
|
||||||
|
@ -48,7 +48,7 @@ func (c code) setup(source []byte, fileName string) (chroma.Iterator, *chroma.St
|
||||||
|
|
||||||
// Basic formats code without any extras
|
// Basic formats code without any extras
|
||||||
func (c code) Basic(source []byte, fileName string, writer io.Writer) error {
|
func (c code) Basic(source []byte, fileName string, writer io.Writer) error {
|
||||||
iter, style, err := c.setup(source, fileName)
|
iter, style, err := setup(source, fileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -57,9 +57,24 @@ func (c code) Basic(source []byte, fileName string, writer io.Writer) error {
|
||||||
|
|
||||||
// Convert formats code with line numbers, links, etc.
|
// Convert formats code with line numbers, links, etc.
|
||||||
func (c code) Convert(source []byte, fileName string, writer io.Writer) error {
|
func (c code) Convert(source []byte, fileName string, writer io.Writer) error {
|
||||||
iter, style, err := c.setup(source, fileName)
|
iter, style, err := setup(source, fileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Formatter.Format(writer, style, iter)
|
return Formatter.Format(writer, style, iter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Snippet formats code with line numbers starting at a specific line
|
||||||
|
func Snippet(source []byte, fileName string, line int, writer io.Writer) error {
|
||||||
|
iter, style, err := setup(source, fileName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
formatter := html.New(
|
||||||
|
html.WithLineNumbers(true),
|
||||||
|
html.WithClasses(true),
|
||||||
|
html.LineNumbersInTable(true),
|
||||||
|
html.BaseLineNumber(line),
|
||||||
|
)
|
||||||
|
return formatter.Format(writer, style, iter)
|
||||||
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ templ repoHeaderComponent(rhcc RepoHeaderComponentContext) {
|
||||||
{ " - " }
|
{ " - " }
|
||||||
<a class="underline decoration-text/50 decoration-dashed hover:decoration-solid" href={ templ.SafeURL(fmt.Sprintf("/%s/log/%s", rhcc.Name, rhcc.Ref)) }>log</a>
|
<a class="underline decoration-text/50 decoration-dashed hover:decoration-solid" href={ templ.SafeURL(fmt.Sprintf("/%s/log/%s", rhcc.Name, rhcc.Ref)) }>log</a>
|
||||||
{ " - " }
|
{ " - " }
|
||||||
|
<a class="underline decoration-text/50 decoration-dashed hover:decoration-solid" href={ templ.SafeURL(fmt.Sprintf("/%s/search", rhcc.Name)) }>search</a>
|
||||||
|
{ " - " }
|
||||||
<pre class="text-text inline select-all bg-base dark:bg-base/50 p-1 rounded">{ fmt.Sprintf("%s/%s.git", rhcc.CloneURL, rhcc.Name) }</pre>
|
<pre class="text-text inline select-all bg-base dark:bg-base/50 p-1 rounded">{ fmt.Sprintf("%s/%s.git", rhcc.CloneURL, rhcc.Name) }</pre>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-text/80 mb-1">{ rhcc.Description }</div>
|
<div class="text-text/80 mb-1">{ rhcc.Description }</div>
|
||||||
|
|
|
@ -34,7 +34,7 @@ templ RepoCommit(rcc RepoCommitContext) {
|
||||||
<a class="underline decoration-text/50 decoration-dashed hover:decoration-solid" href={ templ.SafeURL(fmt.Sprintf("/%s/tree/%s/%s", rcc.RepoHeaderComponentContext.Name, file.To.Commit, file.To.Path)) }>{ file.To.Path }</a>
|
<a class="underline decoration-text/50 decoration-dashed hover:decoration-solid" href={ templ.SafeURL(fmt.Sprintf("/%s/tree/%s/%s", rcc.RepoHeaderComponentContext.Name, file.To.Commit, file.To.Path)) }>{ file.To.Path }</a>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="whitespace-pre commit">@templ.Raw(file.Patch)</div>
|
<div class="whitespace-pre code">@templ.Raw(file.Patch)</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -324,7 +324,7 @@ func RepoCommit(rcc RepoCommitContext) templ.Component {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><div class=\"whitespace-pre commit\">")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><div class=\"whitespace-pre code\">")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,11 @@ type RepoFileContext struct {
|
||||||
templ RepoFile(rfc RepoFileContext) {
|
templ RepoFile(rfc RepoFileContext) {
|
||||||
@base(rfc.BaseContext) {
|
@base(rfc.BaseContext) {
|
||||||
@repoHeaderComponent(rfc.RepoHeaderComponentContext)
|
@repoHeaderComponent(rfc.RepoHeaderComponentContext)
|
||||||
<div class="mt-2 text-text"><a class="text-text underline decoration-text/50 decoration-dashed hover:decoration-solid" href="?raw">Raw</a><span>{ " - " }{ rfc.Path }</span>@templ.Raw(rfc.Code)</div>
|
<div class="mt-2 text-text">
|
||||||
|
<a class="text-text underline decoration-text/50 decoration-dashed hover:decoration-solid" href="?raw">Raw</a>
|
||||||
|
<span>{ " - " }{ rfc.Path }</span>
|
||||||
|
<div class="code">@templ.Raw(rfc.Code)</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
<script>
|
<script>
|
||||||
const lineRe = /#L(\d+)(?:-L(\d+))?/g
|
const lineRe = /#L(\d+)(?:-L(\d+))?/g
|
||||||
|
|
|
@ -56,7 +56,7 @@ func RepoFile(rfc RepoFileContext) templ.Component {
|
||||||
var templ_7745c5c3_Var4 string
|
var templ_7745c5c3_Var4 string
|
||||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(" - ")
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(" - ")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_file.templ`, Line: 12, Col: 153}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_file.templ`, Line: 14, Col: 16}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
|
@ -65,13 +65,13 @@ func RepoFile(rfc RepoFileContext) templ.Component {
|
||||||
var templ_7745c5c3_Var5 string
|
var templ_7745c5c3_Var5 string
|
||||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(rfc.Path)
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(rfc.Path)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_file.templ`, Line: 12, Col: 165}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_file.templ`, Line: 14, Col: 28}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span>")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span><div class=\"code\">")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ func RepoFile(rfc RepoFileContext) templ.Component {
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
package html
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
import "go.jolheiser.com/ugit/internal/git"
|
||||||
|
|
||||||
|
type SearchContext struct {
|
||||||
|
BaseContext
|
||||||
|
RepoHeaderComponentContext
|
||||||
|
Results []git.GrepResult
|
||||||
|
}
|
||||||
|
|
||||||
|
templ RepoSearch(sc SearchContext) {
|
||||||
|
@base(sc.BaseContext) {
|
||||||
|
@repoHeaderComponent(sc.RepoHeaderComponentContext)
|
||||||
|
<form method="get"><label class="text-text">Search <input class="rounded p-1 mt-2 bg-mantle focus:border-lavender" id="search" type="text" name="q" placeholder="search"/></label></form>
|
||||||
|
for _, result := range sc.Results {
|
||||||
|
<div class="text-text mt-5"><a class="underline decoration-text/50 decoration-dashed hover:decoration-solid" href={ templ.SafeURL(fmt.Sprintf("/%s/tree/%s/%s#L%d", sc.RepoHeaderComponentContext.Name, sc.RepoHeaderComponentContext.Ref, result.File, result.Line)) }>{ result.File }</a></div>
|
||||||
|
<div class="code">@templ.Raw(result.Content)</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<script>
|
||||||
|
const search = new URLSearchParams(window.location.search).get("q");
|
||||||
|
if (search !== "") document.querySelector("#search").value = search;
|
||||||
|
</script>
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.2.501
|
||||||
|
package html
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import "context"
|
||||||
|
import "io"
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
import "go.jolheiser.com/ugit/internal/git"
|
||||||
|
|
||||||
|
type SearchContext struct {
|
||||||
|
BaseContext
|
||||||
|
RepoHeaderComponentContext
|
||||||
|
Results []git.GrepResult
|
||||||
|
}
|
||||||
|
|
||||||
|
func RepoSearch(sc SearchContext) templ.Component {
|
||||||
|
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||||
|
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Var2 := templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||||
|
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = repoHeaderComponent(sc.RepoHeaderComponentContext).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <form method=\"get\"><label class=\"text-text\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var3 := `Search `
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<input class=\"rounded p-1 mt-2 bg-mantle focus:border-lavender\" id=\"search\" type=\"text\" name=\"q\" placeholder=\"search\"></label></form>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
for _, result := range sc.Results {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"text-text mt-5\"><a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/%s/tree/%s/%s#L%d", sc.RepoHeaderComponentContext.Name, sc.RepoHeaderComponentContext.Ref, result.File, result.Line))
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var4)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var5 string
|
||||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(result.File)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_search.templ`, Line: 16, Col: 280}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></div><div class=\"code\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(result.Content).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
_, templ_7745c5c3_Err = io.Copy(templ_7745c5c3_W, templ_7745c5c3_Buffer)
|
||||||
|
}
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
})
|
||||||
|
templ_7745c5c3_Err = base(sc.BaseContext).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<script>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var6 := `
|
||||||
|
const search = new URLSearchParams(window.location.search).get("q");
|
||||||
|
if (search !== "") document.querySelector("#search").value = search;
|
||||||
|
`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var6)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</script>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||||
|
}
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
})
|
||||||
|
}
|
|
@ -166,16 +166,47 @@ func repoHeaderComponent(rhcc RepoHeaderComponentContext) templ.Component {
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var14 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/%s/search", rhcc.Name))
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var14)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var15 := `search`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var15)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var16 string
|
||||||
|
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(" - ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo.templ`, Line: 24, Col: 9}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<pre class=\"text-text inline select-all bg-base dark:bg-base/50 p-1 rounded\">")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<pre class=\"text-text inline select-all bg-base dark:bg-base/50 p-1 rounded\">")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var14 string
|
var templ_7745c5c3_Var17 string
|
||||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s/%s.git", rhcc.CloneURL, rhcc.Name))
|
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s/%s.git", rhcc.CloneURL, rhcc.Name))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo.templ`, Line: 23, Col: 131}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo.templ`, Line: 25, Col: 131}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
@ -183,12 +214,12 @@ func repoHeaderComponent(rhcc RepoHeaderComponentContext) templ.Component {
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var15 string
|
var templ_7745c5c3_Var18 string
|
||||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(rhcc.Description)
|
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(rhcc.Description)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo.templ`, Line: 25, Col: 50}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo.templ`, Line: 27, Col: 50}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -85,6 +85,7 @@ func New(settings Settings) Server {
|
||||||
r.Get("/log/{ref}", httperr.Handler(rh.repoLog))
|
r.Get("/log/{ref}", httperr.Handler(rh.repoLog))
|
||||||
r.Get("/commit/{commit}", httperr.Handler(rh.repoCommit))
|
r.Get("/commit/{commit}", httperr.Handler(rh.repoCommit))
|
||||||
r.Get("/commit/{commit}.patch", httperr.Handler(rh.repoPatch))
|
r.Get("/commit/{commit}.patch", httperr.Handler(rh.repoPatch))
|
||||||
|
r.Get("/search", httperr.Handler(rh.repoSearch))
|
||||||
|
|
||||||
// Protocol
|
// Protocol
|
||||||
r.Get("/info/refs", httperr.Handler(rh.infoRefs))
|
r.Get("/info/refs", httperr.Handler(rh.infoRefs))
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"mime"
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"go.jolheiser.com/ugit/internal/html/markup"
|
"go.jolheiser.com/ugit/internal/html/markup"
|
||||||
|
|
||||||
|
@ -182,3 +183,35 @@ func (rh repoHandler) repoPatch(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rh repoHandler) repoSearch(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
repo := r.Context().Value(repoCtxKey).(*git.Repo)
|
||||||
|
|
||||||
|
var results []git.GrepResult
|
||||||
|
search := r.URL.Query().Get("q")
|
||||||
|
if q := strings.TrimSpace(search); q != "" {
|
||||||
|
var err error
|
||||||
|
results, err = repo.Grep(q)
|
||||||
|
if err != nil {
|
||||||
|
return httperr.Error(err)
|
||||||
|
}
|
||||||
|
for idx, result := range results {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := markup.Snippet([]byte(result.Content), filepath.Base(result.File), result.StartLine, &buf); err != nil {
|
||||||
|
return httperr.Error(err)
|
||||||
|
}
|
||||||
|
results[idx].Content = buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := html.RepoSearch(html.SearchContext{
|
||||||
|
BaseContext: rh.baseContext(),
|
||||||
|
RepoHeaderComponentContext: rh.repoHeaderContext(repo, r),
|
||||||
|
Results: results,
|
||||||
|
}).Render(r.Context(), w); err != nil {
|
||||||
|
return httperr.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue