From 1bf78712d45e391e5882e3618845443fe76c8e5e Mon Sep 17 00:00:00 2001 From: aaronknudtson Date: Thu, 27 Jun 2024 22:11:59 -0400 Subject: [PATCH 1/4] add explore/code api route --- modules/indexer/code/internal/indexer.go | 1 + modules/indexer/code/search.go | 42 ++++++-- modules/structs/explore.go | 20 ++++ routers/api/v1/api.go | 9 ++ routers/api/v1/explore/code.go | 117 +++++++++++++++++++++++ routers/web/explore/code.go | 1 + routers/web/repo/search.go | 1 + routers/web/user/code.go | 1 + services/convert/explore.go | 27 ++++++ 9 files changed, 212 insertions(+), 7 deletions(-) create mode 100644 modules/structs/explore.go create mode 100644 routers/api/v1/explore/code.go create mode 100644 services/convert/explore.go diff --git a/modules/indexer/code/internal/indexer.go b/modules/indexer/code/internal/indexer.go index c259fcd26eb6f..440bd678f6405 100644 --- a/modules/indexer/code/internal/indexer.go +++ b/modules/indexer/code/internal/indexer.go @@ -26,6 +26,7 @@ type SearchOptions struct { Language string IsKeywordFuzzy bool + IsHtmlSafe bool db.Paginator } diff --git a/modules/indexer/code/search.go b/modules/indexer/code/search.go index 74c957dde65e4..266eb81de3ce9 100644 --- a/modules/indexer/code/search.go +++ b/modules/indexer/code/search.go @@ -28,18 +28,19 @@ type Result struct { type ResultLine struct { Num int FormattedContent template.HTML + RawContent string } type SearchResultLanguages = internal.SearchResultLanguages type SearchOptions = internal.SearchOptions -func indices(content string, selectionStartIndex, selectionEndIndex int) (int, int) { +func indices(content string, selectionStartIndex, selectionEndIndex, numLinesBuffer int) (int, int) { startIndex := selectionStartIndex numLinesBefore := 0 for ; startIndex > 0; startIndex-- { if content[startIndex-1] == '\n' { - if numLinesBefore == 1 { + if numLinesBefore == numLinesBuffer { break } numLinesBefore++ @@ -50,7 +51,7 @@ func indices(content string, selectionStartIndex, selectionEndIndex int) (int, i numLinesAfter := 0 for ; endIndex < len(content); endIndex++ { if content[endIndex] == '\n' { - if numLinesAfter == 1 { + if numLinesAfter == numLinesBuffer { break } numLinesAfter++ @@ -86,7 +87,22 @@ func HighlightSearchResultCode(filename, language string, lineNums []int, code s return lines } -func searchResult(result *internal.SearchResult, startIndex, endIndex int) (*Result, error) { +func RawSearchResultCode(filename, language string, lineNums []int, code string) []*ResultLine { + // we should highlight the whole code block first, otherwise it doesn't work well with multiple line highlighting + rawLines := strings.Split(code, "\n") + + // The lineNums outputted by highlight.Code might not match the original lineNums, because "highlight" removes the last `\n` + lines := make([]*ResultLine, min(len(rawLines), len(lineNums))) + for i := 0; i < len(lines); i++ { + lines[i] = &ResultLine{ + Num: lineNums[i], + RawContent: rawLines[i], + } + } + return lines +} + +func searchResult(result *internal.SearchResult, startIndex, endIndex int, escapeHtml bool) (*Result, error) { startLineNum := 1 + strings.Count(result.Content[:startIndex], "\n") var formattedLinesBuffer bytes.Buffer @@ -117,6 +133,13 @@ func searchResult(result *internal.SearchResult, startIndex, endIndex int) (*Res index += len(line) } + var lines []*ResultLine + if escapeHtml { + lines = HighlightSearchResultCode(result.Filename, result.Language, lineNums, formattedLinesBuffer.String()) + } else { + lines = RawSearchResultCode(result.Filename, result.Language, lineNums, formattedLinesBuffer.String()) + } + return &Result{ RepoID: result.RepoID, Filename: result.Filename, @@ -124,7 +147,7 @@ func searchResult(result *internal.SearchResult, startIndex, endIndex int) (*Res UpdatedUnix: result.UpdatedUnix, Language: result.Language, Color: result.Color, - Lines: HighlightSearchResultCode(result.Filename, result.Language, lineNums, formattedLinesBuffer.String()), + Lines: lines, }, nil } @@ -142,9 +165,14 @@ func PerformSearch(ctx context.Context, opts *SearchOptions) (int, []*Result, [] displayResults := make([]*Result, len(results)) + nLinesBuffer := 0 + if opts.IsHtmlSafe { + nLinesBuffer = 1 + } + for i, result := range results { - startIndex, endIndex := indices(result.Content, result.StartIndex, result.EndIndex) - displayResults[i], err = searchResult(result, startIndex, endIndex) + startIndex, endIndex := indices(result.Content, result.StartIndex, result.EndIndex, nLinesBuffer) + displayResults[i], err = searchResult(result, startIndex, endIndex, opts.IsHtmlSafe) if err != nil { return 0, nil, nil, err } diff --git a/modules/structs/explore.go b/modules/structs/explore.go new file mode 100644 index 0000000000000..db1250cd566af --- /dev/null +++ b/modules/structs/explore.go @@ -0,0 +1,20 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package structs // import "code.gitea.io/gitea/modules/structs" + +// ExploreCodeSearchItem A single search match +// swagger:model +type ExploreCodeSearchItem struct { + RepoName string `json:"repoName"` + FilePath string `json:"path"` + LineNumber int `json:"lineNumber"` + LineText string `json:"lineText"` +} + +// ExploreCodeResult all returned search results +// swagger:model +type ExploreCodeResult struct { + Total int `json:"total"` + Results []ExploreCodeSearchItem `json:"results"` +} diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index be67ec1695b3b..c1ca119c7edd6 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -92,6 +92,7 @@ import ( "code.gitea.io/gitea/routers/api/v1/repo" "code.gitea.io/gitea/routers/api/v1/settings" "code.gitea.io/gitea/routers/api/v1/user" + "code.gitea.io/gitea/routers/api/v1/explore" "code.gitea.io/gitea/routers/common" "code.gitea.io/gitea/services/actions" "code.gitea.io/gitea/services/auth" @@ -890,6 +891,14 @@ func Routes() *web.Router { // Misc (public accessible) m.Group("", func() { m.Get("/version", misc.Version) + m.Group("/explore", func() { + m.Get("/code", func(ctx *context.APIContext) { + if unit.TypeCode.UnitGlobalDisabled() { + ctx.NotFound("Repo unit code is disabled", nil) + return + } + }, explore.Code) + }) m.Get("/signing-key.gpg", misc.SigningKey) m.Post("/markup", reqToken(), bind(api.MarkupOption{}), misc.Markup) m.Post("/markdown", reqToken(), bind(api.MarkdownOption{}), misc.Markdown) diff --git a/routers/api/v1/explore/code.go b/routers/api/v1/explore/code.go new file mode 100644 index 0000000000000..2f9e2922c3e67 --- /dev/null +++ b/routers/api/v1/explore/code.go @@ -0,0 +1,117 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package explore + +import ( + "net/http" + + "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" + code_indexer "code.gitea.io/gitea/modules/indexer/code" + "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/convert" +) + +// Code explore code +func Code(ctx *context.APIContext) { + if !setting.Indexer.RepoIndexerEnabled { + ctx.NotFound("Indexer not enabled") + return + } + + language := ctx.FormTrim("l") + keyword := ctx.FormTrim("q") + + isFuzzy := ctx.FormOptionalBool("fuzzy").ValueOrDefault(true) + + if keyword == "" { + ctx.JSON(http.StatusInternalServerError, api.SearchError{OK: false, Error: "No keyword provided"}) + return + } + + page := ctx.FormInt("page") + if page <= 0 { + page = 1 + } + + var ( + repoIDs []int64 + err error + isAdmin bool + ) + if ctx.Doer != nil { + isAdmin = ctx.Doer.IsAdmin + } + + // guest user or non-admin user + if ctx.Doer == nil || !isAdmin { + repoIDs, err = repo_model.FindUserCodeAccessibleRepoIDs(ctx, ctx.Doer) + if err != nil { + ctx.ServerError("FindUserCodeAccessibleRepoIDs", err) + return + } + } + + var ( + total int + searchResults []*code_indexer.Result + repoMaps map[int64]*repo_model.Repository + ) + + if (len(repoIDs) > 0) || isAdmin { + total, searchResults, _, err = code_indexer.PerformSearch(ctx, &code_indexer.SearchOptions{ + RepoIDs: repoIDs, + Keyword: keyword, + IsKeywordFuzzy: isFuzzy, + IsHtmlSafe: false, + Language: language, + Paginator: &db.ListOptions{ + Page: page, + PageSize: setting.API.DefaultPagingNum, + }, + }) + if err != nil { + if code_indexer.IsAvailable(ctx) { + ctx.ServerError("SearchResults", err) + return + } + } + + loadRepoIDs := make([]int64, 0, len(searchResults)) + for _, result := range searchResults { + var find bool + for _, id := range loadRepoIDs { + if id == result.RepoID { + find = true + break + } + } + if !find { + loadRepoIDs = append(loadRepoIDs, result.RepoID) + } + } + + repoMaps, err = repo_model.GetRepositoriesMapByIDs(ctx, loadRepoIDs) + if err != nil { + ctx.ServerError("GetRepositoriesMapByIDs", err) + return + } + + if len(loadRepoIDs) != len(repoMaps) { + // Remove deleted repos from search results + cleanedSearchResults := make([]*code_indexer.Result, 0, len(repoMaps)) + for _, sr := range searchResults { + if _, found := repoMaps[sr.RepoID]; found { + cleanedSearchResults = append(cleanedSearchResults, sr) + } + } + + searchResults = cleanedSearchResults + } + } + + ctx.JSON(http.StatusOK, convert.ToExploreCodeSearchResults(total, searchResults, repoMaps)) +} diff --git a/routers/web/explore/code.go b/routers/web/explore/code.go index ecd7c33e016f9..a4c14d3c0df04 100644 --- a/routers/web/explore/code.go +++ b/routers/web/explore/code.go @@ -81,6 +81,7 @@ func Code(ctx *context.Context) { RepoIDs: repoIDs, Keyword: keyword, IsKeywordFuzzy: isFuzzy, + IsHtmlSafe: true, Language: language, Paginator: &db.ListOptions{ Page: page, diff --git a/routers/web/repo/search.go b/routers/web/repo/search.go index 920a865555bec..667bce249b8e3 100644 --- a/routers/web/repo/search.go +++ b/routers/web/repo/search.go @@ -59,6 +59,7 @@ func Search(ctx *context.Context) { RepoIDs: []int64{ctx.Repo.Repository.ID}, Keyword: keyword, IsKeywordFuzzy: isFuzzy, + IsHtmlSafe: true, Language: language, Paginator: &db.ListOptions{ Page: page, diff --git a/routers/web/user/code.go b/routers/web/user/code.go index 785c37b1243c6..c3ac724accd7a 100644 --- a/routers/web/user/code.go +++ b/routers/web/user/code.go @@ -80,6 +80,7 @@ func CodeSearch(ctx *context.Context) { Keyword: keyword, IsKeywordFuzzy: isFuzzy, Language: language, + IsHtmlSafe: true, Paginator: &db.ListOptions{ Page: page, PageSize: setting.UI.RepoSearchPagingNum, diff --git a/services/convert/explore.go b/services/convert/explore.go new file mode 100644 index 0000000000000..ae548ee1f8e22 --- /dev/null +++ b/services/convert/explore.go @@ -0,0 +1,27 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package convert + +import ( + repo_model "code.gitea.io/gitea/models/repo" + code_indexer "code.gitea.io/gitea/modules/indexer/code" + api "code.gitea.io/gitea/modules/structs" +) + +func ToExploreCodeSearchResults(total int, results []*code_indexer.Result, repoMaps map[int64]*repo_model.Repository) api.ExploreCodeResult { + out := api.ExploreCodeResult{Total: total} + for _, res := range results { + if repo := repoMaps[res.RepoID]; repo != nil { + for _, r := range res.Lines { + out.Results = append(out.Results, api.ExploreCodeSearchItem{ + RepoName: repo.Name, + FilePath: res.Filename, + LineNumber: r.Num, + LineText: r.RawContent, + }) + } + } + } + return out +} From ed8d76be3b5f597e994b03454d99224a196c6bc9 Mon Sep 17 00:00:00 2001 From: aaronknudtson Date: Thu, 27 Jun 2024 22:56:41 -0400 Subject: [PATCH 2/4] update swagger for explore code --- modules/structs/explore.go | 4 +- routers/api/v1/explore/code.go | 45 +++++++++++++-- routers/api/v1/swagger/explore.go | 15 +++++ services/convert/explore.go | 2 +- templates/swagger/v1_json.tmpl | 96 ++++++++++++++++++++++++++++++- 5 files changed, 152 insertions(+), 10 deletions(-) create mode 100644 routers/api/v1/swagger/explore.go diff --git a/modules/structs/explore.go b/modules/structs/explore.go index db1250cd566af..abb5727baa690 100644 --- a/modules/structs/explore.go +++ b/modules/structs/explore.go @@ -3,7 +3,7 @@ package structs // import "code.gitea.io/gitea/modules/structs" -// ExploreCodeSearchItem A single search match +// ExploreCodeSearchItem A code search match // swagger:model type ExploreCodeSearchItem struct { RepoName string `json:"repoName"` @@ -12,7 +12,7 @@ type ExploreCodeSearchItem struct { LineText string `json:"lineText"` } -// ExploreCodeResult all returned search results +// ExploreCodeResult all returned code search results // swagger:model type ExploreCodeResult struct { Total int `json:"total"` diff --git a/routers/api/v1/explore/code.go b/routers/api/v1/explore/code.go index 2f9e2922c3e67..11525bddb4ef9 100644 --- a/routers/api/v1/explore/code.go +++ b/routers/api/v1/explore/code.go @@ -17,18 +17,43 @@ import ( // Code explore code func Code(ctx *context.APIContext) { + // swagger:operation GET /explore/code explore codeSearch + // --- + // summary: Search for code + // produces: + // - application/json + // parameters: + // - name: q + // in: query + // description: keyword + // type: string + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: fuzzy + // in: query + // description: whether to search fuzzy or strict + // type: boolean + // responses: + // "200": + // description: "SearchResults of a successful search" + // schema: + // "$ref": "#/definitions/ExploreCodeResult" if !setting.Indexer.RepoIndexerEnabled { ctx.NotFound("Indexer not enabled") return } - language := ctx.FormTrim("l") keyword := ctx.FormTrim("q") isFuzzy := ctx.FormOptionalBool("fuzzy").ValueOrDefault(true) if keyword == "" { - ctx.JSON(http.StatusInternalServerError, api.SearchError{OK: false, Error: "No keyword provided"}) + ctx.JSON(http.StatusInternalServerError, api.SearchError{ + OK: false, + Error: "No keyword provided", + }) return } @@ -50,7 +75,10 @@ func Code(ctx *context.APIContext) { if ctx.Doer == nil || !isAdmin { repoIDs, err = repo_model.FindUserCodeAccessibleRepoIDs(ctx, ctx.Doer) if err != nil { - ctx.ServerError("FindUserCodeAccessibleRepoIDs", err) + ctx.JSON(http.StatusInternalServerError, api.SearchError{ + OK: false, + Error: err.Error(), + }) return } } @@ -67,7 +95,6 @@ func Code(ctx *context.APIContext) { Keyword: keyword, IsKeywordFuzzy: isFuzzy, IsHtmlSafe: false, - Language: language, Paginator: &db.ListOptions{ Page: page, PageSize: setting.API.DefaultPagingNum, @@ -75,7 +102,10 @@ func Code(ctx *context.APIContext) { }) if err != nil { if code_indexer.IsAvailable(ctx) { - ctx.ServerError("SearchResults", err) + ctx.JSON(http.StatusInternalServerError, api.SearchError{ + OK: false, + Error: err.Error(), + }) return } } @@ -96,7 +126,10 @@ func Code(ctx *context.APIContext) { repoMaps, err = repo_model.GetRepositoriesMapByIDs(ctx, loadRepoIDs) if err != nil { - ctx.ServerError("GetRepositoriesMapByIDs", err) + ctx.JSON(http.StatusInternalServerError, api.SearchError{ + OK: false, + Error: err.Error(), + }) return } diff --git a/routers/api/v1/swagger/explore.go b/routers/api/v1/swagger/explore.go new file mode 100644 index 0000000000000..f95c04d551f50 --- /dev/null +++ b/routers/api/v1/swagger/explore.go @@ -0,0 +1,15 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package swagger + +import ( + api "code.gitea.io/gitea/modules/structs" +) + +// ExploreCode +// swagger:response ExploreCode +type swaggerResponseExploreCode struct { + // out:body + Body api.ExploreCodeResult `json:"body"` +} diff --git a/services/convert/explore.go b/services/convert/explore.go index ae548ee1f8e22..5a2eeb08ed8a3 100644 --- a/services/convert/explore.go +++ b/services/convert/explore.go @@ -15,7 +15,7 @@ func ToExploreCodeSearchResults(total int, results []*code_indexer.Result, repoM if repo := repoMaps[res.RepoID]; repo != nil { for _, r := range res.Lines { out.Results = append(out.Results, api.ExploreCodeSearchItem{ - RepoName: repo.Name, + RepoName: repo.FullName(), FilePath: res.Filename, LineNumber: r.Num, LineText: r.RawContent, diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 4aa64c537611c..228b44e8f2dcd 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -1009,6 +1009,46 @@ } } }, + "/explore/code": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "explore" + ], + "summary": "Search for code", + "operationId": "codeSearch", + "parameters": [ + { + "type": "string", + "description": "keyword", + "name": "q", + "in": "query" + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "boolean", + "description": "whether to search fuzzy or strict", + "name": "fuzzy", + "in": "query" + } + ], + "responses": { + "200": { + "description": "SearchResults of a successful search", + "schema": { + "$ref": "#/definitions/ExploreCodeResult" + } + } + } + } + }, "/gitignore/templates": { "get": { "produces": [ @@ -21331,6 +21371,49 @@ }, "x-go-package": "code.gitea.io/gitea/modules/structs" }, + "ExploreCodeResult": { + "description": "ExploreCodeResult all returned code search results", + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "$ref": "#/definitions/ExploreCodeSearchItem" + }, + "x-go-name": "Results" + }, + "total": { + "type": "integer", + "format": "int64", + "x-go-name": "Total" + } + }, + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ExploreCodeSearchItem": { + "description": "ExploreCodeSearchItem A code search match", + "type": "object", + "properties": { + "lineNumber": { + "type": "integer", + "format": "int64", + "x-go-name": "LineNumber" + }, + "lineText": { + "type": "string", + "x-go-name": "LineText" + }, + "path": { + "type": "string", + "x-go-name": "FilePath" + }, + "repoName": { + "type": "string", + "x-go-name": "RepoName" + } + }, + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, "ExternalTracker": { "description": "ExternalTracker represents settings for external tracker", "type": "object", @@ -25397,6 +25480,17 @@ "$ref": "#/definitions/APIError" } }, + "ExploreCode": { + "description": "ExploreCode", + "schema": { + "$ref": "#/definitions/ExploreCodeResult" + }, + "headers": { + "body": { + "description": "out:body" + } + } + }, "FileDeleteResponse": { "description": "FileDeleteResponse", "schema": { @@ -26243,4 +26337,4 @@ "TOTPHeader": [] } ] -} +} \ No newline at end of file From a3a47886c86703eeef546083e341f7e62342a8f9 Mon Sep 17 00:00:00 2001 From: aaronknudtson Date: Thu, 27 Jun 2024 23:07:00 -0400 Subject: [PATCH 3/4] fix formatting --- modules/indexer/code/internal/indexer.go | 2 +- modules/indexer/code/search.go | 8 ++++---- routers/api/v1/api.go | 2 +- routers/api/v1/explore/code.go | 2 +- routers/api/v1/swagger/explore.go | 4 ++-- routers/web/explore/code.go | 2 +- routers/web/repo/search.go | 2 +- routers/web/user/code.go | 2 +- templates/swagger/v1_json.tmpl | 7 +------ 9 files changed, 13 insertions(+), 18 deletions(-) diff --git a/modules/indexer/code/internal/indexer.go b/modules/indexer/code/internal/indexer.go index 440bd678f6405..16e5772f100b6 100644 --- a/modules/indexer/code/internal/indexer.go +++ b/modules/indexer/code/internal/indexer.go @@ -26,7 +26,7 @@ type SearchOptions struct { Language string IsKeywordFuzzy bool - IsHtmlSafe bool + IsHTMLSafe bool db.Paginator } diff --git a/modules/indexer/code/search.go b/modules/indexer/code/search.go index 266eb81de3ce9..fbce8b189d285 100644 --- a/modules/indexer/code/search.go +++ b/modules/indexer/code/search.go @@ -102,7 +102,7 @@ func RawSearchResultCode(filename, language string, lineNums []int, code string) return lines } -func searchResult(result *internal.SearchResult, startIndex, endIndex int, escapeHtml bool) (*Result, error) { +func searchResult(result *internal.SearchResult, startIndex, endIndex int, escapeHTML bool) (*Result, error) { startLineNum := 1 + strings.Count(result.Content[:startIndex], "\n") var formattedLinesBuffer bytes.Buffer @@ -134,7 +134,7 @@ func searchResult(result *internal.SearchResult, startIndex, endIndex int, escap } var lines []*ResultLine - if escapeHtml { + if escapeHTML { lines = HighlightSearchResultCode(result.Filename, result.Language, lineNums, formattedLinesBuffer.String()) } else { lines = RawSearchResultCode(result.Filename, result.Language, lineNums, formattedLinesBuffer.String()) @@ -166,13 +166,13 @@ func PerformSearch(ctx context.Context, opts *SearchOptions) (int, []*Result, [] displayResults := make([]*Result, len(results)) nLinesBuffer := 0 - if opts.IsHtmlSafe { + if opts.IsHTMLSafe { nLinesBuffer = 1 } for i, result := range results { startIndex, endIndex := indices(result.Content, result.StartIndex, result.EndIndex, nLinesBuffer) - displayResults[i], err = searchResult(result, startIndex, endIndex, opts.IsHtmlSafe) + displayResults[i], err = searchResult(result, startIndex, endIndex, opts.IsHTMLSafe) if err != nil { return 0, nil, nil, err } diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index c1ca119c7edd6..df7d60dc75258 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -85,6 +85,7 @@ import ( "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/activitypub" "code.gitea.io/gitea/routers/api/v1/admin" + "code.gitea.io/gitea/routers/api/v1/explore" "code.gitea.io/gitea/routers/api/v1/misc" "code.gitea.io/gitea/routers/api/v1/notify" "code.gitea.io/gitea/routers/api/v1/org" @@ -92,7 +93,6 @@ import ( "code.gitea.io/gitea/routers/api/v1/repo" "code.gitea.io/gitea/routers/api/v1/settings" "code.gitea.io/gitea/routers/api/v1/user" - "code.gitea.io/gitea/routers/api/v1/explore" "code.gitea.io/gitea/routers/common" "code.gitea.io/gitea/services/actions" "code.gitea.io/gitea/services/auth" diff --git a/routers/api/v1/explore/code.go b/routers/api/v1/explore/code.go index 11525bddb4ef9..48a83f4b9c4b4 100644 --- a/routers/api/v1/explore/code.go +++ b/routers/api/v1/explore/code.go @@ -94,7 +94,7 @@ func Code(ctx *context.APIContext) { RepoIDs: repoIDs, Keyword: keyword, IsKeywordFuzzy: isFuzzy, - IsHtmlSafe: false, + IsHTMLSafe: false, Paginator: &db.ListOptions{ Page: page, PageSize: setting.API.DefaultPagingNum, diff --git a/routers/api/v1/swagger/explore.go b/routers/api/v1/swagger/explore.go index f95c04d551f50..6c8ccd2cec3a9 100644 --- a/routers/api/v1/swagger/explore.go +++ b/routers/api/v1/swagger/explore.go @@ -10,6 +10,6 @@ import ( // ExploreCode // swagger:response ExploreCode type swaggerResponseExploreCode struct { - // out:body - Body api.ExploreCodeResult `json:"body"` + // in:body + Body api.ExploreCodeResult `json:"body"` } diff --git a/routers/web/explore/code.go b/routers/web/explore/code.go index a4c14d3c0df04..c1de9e8bfad9d 100644 --- a/routers/web/explore/code.go +++ b/routers/web/explore/code.go @@ -81,7 +81,7 @@ func Code(ctx *context.Context) { RepoIDs: repoIDs, Keyword: keyword, IsKeywordFuzzy: isFuzzy, - IsHtmlSafe: true, + IsHTMLSafe: true, Language: language, Paginator: &db.ListOptions{ Page: page, diff --git a/routers/web/repo/search.go b/routers/web/repo/search.go index 667bce249b8e3..7b5dbf9b3bb3e 100644 --- a/routers/web/repo/search.go +++ b/routers/web/repo/search.go @@ -59,7 +59,7 @@ func Search(ctx *context.Context) { RepoIDs: []int64{ctx.Repo.Repository.ID}, Keyword: keyword, IsKeywordFuzzy: isFuzzy, - IsHtmlSafe: true, + IsHTMLSafe: true, Language: language, Paginator: &db.ListOptions{ Page: page, diff --git a/routers/web/user/code.go b/routers/web/user/code.go index c3ac724accd7a..be357d3604bac 100644 --- a/routers/web/user/code.go +++ b/routers/web/user/code.go @@ -80,7 +80,7 @@ func CodeSearch(ctx *context.Context) { Keyword: keyword, IsKeywordFuzzy: isFuzzy, Language: language, - IsHtmlSafe: true, + IsHTMLSafe: true, Paginator: &db.ListOptions{ Page: page, PageSize: setting.UI.RepoSearchPagingNum, diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 228b44e8f2dcd..011836925b0f6 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -25484,11 +25484,6 @@ "description": "ExploreCode", "schema": { "$ref": "#/definitions/ExploreCodeResult" - }, - "headers": { - "body": { - "description": "out:body" - } } }, "FileDeleteResponse": { @@ -26337,4 +26332,4 @@ "TOTPHeader": [] } ] -} \ No newline at end of file +} From 043f8cc7d75df08278a0a07ff97fb8052b92c9eb Mon Sep 17 00:00:00 2001 From: aaronknudtson Date: Tue, 9 Jul 2024 23:11:52 -0400 Subject: [PATCH 4/4] update from PR comments; cleanup comments in files --- modules/indexer/code/search.go | 6 ++---- modules/structs/explore.go | 2 +- routers/api/v1/explore/code.go | 21 +++++++-------------- services/convert/explore.go | 5 ++++- templates/swagger/v1_json.tmpl | 2 +- 5 files changed, 15 insertions(+), 21 deletions(-) diff --git a/modules/indexer/code/search.go b/modules/indexer/code/search.go index fbce8b189d285..9f18d633217be 100644 --- a/modules/indexer/code/search.go +++ b/modules/indexer/code/search.go @@ -87,11 +87,9 @@ func HighlightSearchResultCode(filename, language string, lineNums []int, code s return lines } -func RawSearchResultCode(filename, language string, lineNums []int, code string) []*ResultLine { - // we should highlight the whole code block first, otherwise it doesn't work well with multiple line highlighting +func rawSearchResultCode(lineNums []int, code string) []*ResultLine { rawLines := strings.Split(code, "\n") - // The lineNums outputted by highlight.Code might not match the original lineNums, because "highlight" removes the last `\n` lines := make([]*ResultLine, min(len(rawLines), len(lineNums))) for i := 0; i < len(lines); i++ { lines[i] = &ResultLine{ @@ -137,7 +135,7 @@ func searchResult(result *internal.SearchResult, startIndex, endIndex int, escap if escapeHTML { lines = HighlightSearchResultCode(result.Filename, result.Language, lineNums, formattedLinesBuffer.String()) } else { - lines = RawSearchResultCode(result.Filename, result.Language, lineNums, formattedLinesBuffer.String()) + lines = rawSearchResultCode(lineNums, formattedLinesBuffer.String()) } return &Result{ diff --git a/modules/structs/explore.go b/modules/structs/explore.go index abb5727baa690..6fc708da089e0 100644 --- a/modules/structs/explore.go +++ b/modules/structs/explore.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package structs // import "code.gitea.io/gitea/modules/structs" +package structs // ExploreCodeSearchItem A code search match // swagger:model diff --git a/routers/api/v1/explore/code.go b/routers/api/v1/explore/code.go index 48a83f4b9c4b4..6a8c21271bd05 100644 --- a/routers/api/v1/explore/code.go +++ b/routers/api/v1/explore/code.go @@ -1,10 +1,11 @@ -// Copyright 2021 The Gitea Authors. All rights reserved. +// Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package explore import ( "net/http" + "slices" "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" @@ -33,7 +34,7 @@ func Code(ctx *context.APIContext) { // type: integer // - name: fuzzy // in: query - // description: whether to search fuzzy or strict + // description: whether to search fuzzy or strict (defaults to true) // type: boolean // responses: // "200": @@ -50,9 +51,9 @@ func Code(ctx *context.APIContext) { isFuzzy := ctx.FormOptionalBool("fuzzy").ValueOrDefault(true) if keyword == "" { - ctx.JSON(http.StatusInternalServerError, api.SearchError{ - OK: false, - Error: "No keyword provided", + ctx.JSON(http.StatusOK, api.ExploreCodeResult{ + Total: 0, + Results: make([]api.ExploreCodeSearchItem, 0), }) return } @@ -71,7 +72,6 @@ func Code(ctx *context.APIContext) { isAdmin = ctx.Doer.IsAdmin } - // guest user or non-admin user if ctx.Doer == nil || !isAdmin { repoIDs, err = repo_model.FindUserCodeAccessibleRepoIDs(ctx, ctx.Doer) if err != nil { @@ -112,14 +112,7 @@ func Code(ctx *context.APIContext) { loadRepoIDs := make([]int64, 0, len(searchResults)) for _, result := range searchResults { - var find bool - for _, id := range loadRepoIDs { - if id == result.RepoID { - find = true - break - } - } - if !find { + if !slices.Contains(loadRepoIDs, result.RepoID) { loadRepoIDs = append(loadRepoIDs, result.RepoID) } } diff --git a/services/convert/explore.go b/services/convert/explore.go index 5a2eeb08ed8a3..9983f6a93a89a 100644 --- a/services/convert/explore.go +++ b/services/convert/explore.go @@ -10,7 +10,10 @@ import ( ) func ToExploreCodeSearchResults(total int, results []*code_indexer.Result, repoMaps map[int64]*repo_model.Repository) api.ExploreCodeResult { - out := api.ExploreCodeResult{Total: total} + out := api.ExploreCodeResult{ + Total: total, + Results: make([]api.ExploreCodeSearchItem, 0, len(results)), + } for _, res := range results { if repo := repoMaps[res.RepoID]; repo != nil { for _, r := range res.Lines { diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 011836925b0f6..7bcb096aaf008 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -1034,7 +1034,7 @@ }, { "type": "boolean", - "description": "whether to search fuzzy or strict", + "description": "whether to search fuzzy or strict (defaults to true)", "name": "fuzzy", "in": "query" }