feat: wiki search using git-grep
+ add release note
This commit is contained in:
		
							parent
							
								
									d6915f4d5f
								
							
						
					
					
						commit
						ec4f5495ba
					
				
					 10 changed files with 107 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -27,6 +27,7 @@ type GrepResult struct {
 | 
			
		|||
type GrepOptions struct {
 | 
			
		||||
	RefName           string
 | 
			
		||||
	MaxResultLimit    int
 | 
			
		||||
	MatchesPerFile    int
 | 
			
		||||
	ContextLineNumber int
 | 
			
		||||
	IsFuzzy           bool
 | 
			
		||||
	PathSpec          []setting.Glob
 | 
			
		||||
| 
						 | 
				
			
			@ -54,6 +55,9 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO
 | 
			
		|||
	var results []*GrepResult
 | 
			
		||||
	cmd := NewCommand(ctx, "grep", "--null", "--break", "--heading", "--fixed-strings", "--line-number", "--ignore-case", "--full-name")
 | 
			
		||||
	cmd.AddOptionValues("--context", fmt.Sprint(opts.ContextLineNumber))
 | 
			
		||||
	if opts.MatchesPerFile > 0 {
 | 
			
		||||
		cmd.AddOptionValues("--max-count", fmt.Sprint(opts.MatchesPerFile))
 | 
			
		||||
	}
 | 
			
		||||
	if opts.IsFuzzy {
 | 
			
		||||
		words := strings.Fields(search)
 | 
			
		||||
		for _, word := range words {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,6 +44,31 @@ func TestGrepSearch(t *testing.T) {
 | 
			
		|||
		},
 | 
			
		||||
	}, res)
 | 
			
		||||
 | 
			
		||||
	res, err = GrepSearch(context.Background(), repo, "world", GrepOptions{MatchesPerFile: 1})
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.Equal(t, []*GrepResult{
 | 
			
		||||
		{
 | 
			
		||||
			Filename:    "i-am-a-python.p",
 | 
			
		||||
			LineNumbers: []int{1},
 | 
			
		||||
			LineCodes:   []string{"## This is a simple file to do a hello world"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Filename:    "java-hello/main.java",
 | 
			
		||||
			LineNumbers: []int{1},
 | 
			
		||||
			LineCodes:   []string{"public class HelloWorld"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Filename:    "main.vendor.java",
 | 
			
		||||
			LineNumbers: []int{1},
 | 
			
		||||
			LineCodes:   []string{"public class HelloWorld"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Filename:    "python-hello/hello.py",
 | 
			
		||||
			LineNumbers: []int{1},
 | 
			
		||||
			LineCodes:   []string{"## This is a simple file to do a hello world"},
 | 
			
		||||
		},
 | 
			
		||||
	}, res)
 | 
			
		||||
 | 
			
		||||
	res, err = GrepSearch(context.Background(), repo, "no-such-content", GrepOptions{})
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.Len(t, res, 0)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2016,6 +2016,8 @@ wiki.pages = Pages
 | 
			
		|||
wiki.last_updated = Last updated %s
 | 
			
		||||
wiki.page_name_desc = Enter a name for this Wiki page. Some special names are: "Home", "_Sidebar" and "_Footer".
 | 
			
		||||
wiki.original_git_entry_tooltip = View original Git file instead of using friendly link.
 | 
			
		||||
wiki.search = Search wiki
 | 
			
		||||
wiki.no_search_results = No results
 | 
			
		||||
 | 
			
		||||
activity = Activity
 | 
			
		||||
activity.navbar.pulse = Pulse
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										3
									
								
								release-notes/8.0.0/feat/3847.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								release-notes/8.0.0/feat/3847.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
Basic wiki content search using git-grep
 | 
			
		||||
    - The search results include the first ten matched files
 | 
			
		||||
    - Only the first three matches per file are displayed
 | 
			
		||||
| 
						 | 
				
			
			@ -40,6 +40,7 @@ const (
 | 
			
		|||
	tplWikiRevision base.TplName = "repo/wiki/revision"
 | 
			
		||||
	tplWikiNew      base.TplName = "repo/wiki/new"
 | 
			
		||||
	tplWikiPages    base.TplName = "repo/wiki/pages"
 | 
			
		||||
	tplWikiSearch   base.TplName = "repo/wiki/search"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// MustEnableWiki check if wiki is enabled, if external then redirect
 | 
			
		||||
| 
						 | 
				
			
			@ -795,3 +796,20 @@ func DeleteWikiPagePost(ctx *context.Context) {
 | 
			
		|||
 | 
			
		||||
	ctx.JSONRedirect(ctx.Repo.RepoLink + "/wiki/")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func WikiSearchContent(ctx *context.Context) {
 | 
			
		||||
	keyword := ctx.FormTrim("q")
 | 
			
		||||
	if keyword == "" {
 | 
			
		||||
		ctx.HTML(http.StatusOK, tplWikiSearch)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	res, err := wiki_service.SearchWikiContents(ctx, ctx.Repo.Repository, keyword)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.ServerError("SearchWikiContents", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.Data["Results"] = res
 | 
			
		||||
	ctx.HTML(http.StatusOK, tplWikiSearch)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1417,6 +1417,7 @@ func registerRoutes(m *web.Route) {
 | 
			
		|||
		})
 | 
			
		||||
 | 
			
		||||
		m.Group("/wiki", func() {
 | 
			
		||||
			m.Get("/search", repo.WikiSearchContent)
 | 
			
		||||
			m.Get("/raw/*", repo.WikiRaw)
 | 
			
		||||
		}, repo.MustEnableWiki)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -407,3 +407,19 @@ func DeleteWiki(ctx context.Context, repo *repo_model.Repository) error {
 | 
			
		|||
	system_model.RemoveAllWithNotice(ctx, "Delete repository wiki", repo.WikiPath())
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func SearchWikiContents(ctx context.Context, repo *repo_model.Repository, keyword string) ([]*git.GrepResult, error) {
 | 
			
		||||
	gitRepo, err := git.OpenRepository(ctx, repo.WikiPath())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	return git.GrepSearch(ctx, gitRepo, keyword, git.GrepOptions{
 | 
			
		||||
		ContextLineNumber: 0,
 | 
			
		||||
		IsFuzzy:           true,
 | 
			
		||||
		RefName:           repo.GetWikiBranchName(),
 | 
			
		||||
		MaxResultLimit:    10,
 | 
			
		||||
		MatchesPerFile:    3,
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										12
									
								
								templates/repo/wiki/search.tmpl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								templates/repo/wiki/search.tmpl
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
{{if .Results}}
 | 
			
		||||
    {{range .Results}}
 | 
			
		||||
        <a class="item" href="{{$.RepoLink}}/wiki/{{.Filename}}">
 | 
			
		||||
            <b class="tw-block tw-mb-2">{{.Filename}}</b>
 | 
			
		||||
            {{range .LineCodes}}
 | 
			
		||||
                <p class="tw-my-0">{{.}}</p>
 | 
			
		||||
            {{end}}
 | 
			
		||||
        </a>
 | 
			
		||||
    {{end}}
 | 
			
		||||
{{else}}
 | 
			
		||||
    <div class="item muted">{{ctx.Locale.Tr "repo.wiki.no_search_results"}}</div>
 | 
			
		||||
{{end}}
 | 
			
		||||
| 
						 | 
				
			
			@ -32,6 +32,15 @@
 | 
			
		|||
				{{template "repo/clone_buttons" .}}
 | 
			
		||||
				{{template "repo/clone_script" .}}
 | 
			
		||||
			</div>
 | 
			
		||||
			<div class="ui floating dropdown jump">
 | 
			
		||||
				<div class="ui icon search input">
 | 
			
		||||
					<i class="icon">{{svg "octicon-search"}}</i>
 | 
			
		||||
					<input type="search" name="q" hx-get="{{$.RepoLink}}/wiki/search" hx-target="#wiki-search" hx-swap="innerHTML" hx-trigger="keyup changed delay:.5s" placeholder="{{ctx.Locale.Tr "repo.wiki.search"}}..." />
 | 
			
		||||
				</div>
 | 
			
		||||
				<div id="wiki-search" class="menu tw-absolute tw-mt-3 tw-rounded right">
 | 
			
		||||
					<div class="item muted">{{ctx.Locale.Tr "repo.wiki.no_search_results"}}</div>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div class="ui dividing header">
 | 
			
		||||
			<div class="ui stackable grid">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,9 +15,26 @@ import (
 | 
			
		|||
	api "code.gitea.io/gitea/modules/structs"
 | 
			
		||||
	"code.gitea.io/gitea/tests"
 | 
			
		||||
 | 
			
		||||
	"github.com/PuerkitoBio/goquery"
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestWikiSearchContent(t *testing.T) {
 | 
			
		||||
	defer tests.PrepareTestEnv(t)()
 | 
			
		||||
 | 
			
		||||
	req := NewRequest(t, "GET", "/user2/repo1/wiki/search?q=This")
 | 
			
		||||
	resp := MakeRequest(t, req, http.StatusOK)
 | 
			
		||||
	doc := NewHTMLParser(t, resp.Body)
 | 
			
		||||
	res := doc.Find(".item > b").Map(func(_ int, el *goquery.Selection) string {
 | 
			
		||||
		return el.Text()
 | 
			
		||||
	})
 | 
			
		||||
	assert.Equal(t, []string{
 | 
			
		||||
		"Home.md",
 | 
			
		||||
		"Page-With-Spaced-Name.md",
 | 
			
		||||
		"Unescaped File.md",
 | 
			
		||||
	}, res)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestWikiBranchNormalize(t *testing.T) {
 | 
			
		||||
	defer tests.PrepareTestEnv(t)()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue