diff --git a/core/src/main/java/hudson/model/Computer.java b/core/src/main/java/hudson/model/Computer.java index 14f0b3de4f4d..3b9566d861ca 100644 --- a/core/src/main/java/hudson/model/Computer.java +++ b/core/src/main/java/hudson/model/Computer.java @@ -1111,7 +1111,7 @@ public String getSearchUrl() { } @Override - public SearchGroup getSearchGroup() { + public String getSearchGroup() { return SearchGroup.COMPUTER; } diff --git a/core/src/main/java/hudson/model/Job.java b/core/src/main/java/hudson/model/Job.java index 643e3d186e85..2abf1677fe06 100644 --- a/core/src/main/java/hudson/model/Job.java +++ b/core/src/main/java/hudson/model/Job.java @@ -48,6 +48,7 @@ import hudson.scm.ChangeLogSet; import hudson.scm.SCM; import hudson.search.QuickSilver; +import hudson.search.SearchGroup; import hudson.search.SearchIndex; import hudson.search.SearchIndexBuilder; import hudson.search.SearchItem; @@ -524,6 +525,11 @@ public String getSearchIcon() { return "symbol-status-" + this.getIconColor().getIconName(); } + @Override + public String getSearchGroup() { + return SearchGroup.PROJECT; + } + @Override protected SearchIndexBuilder makeSearchIndex() { return super.makeSearchIndex().add(new SearchIndex() { diff --git a/core/src/main/java/hudson/model/User.java b/core/src/main/java/hudson/model/User.java index 85495f93ec0b..c877d3e1f8c8 100644 --- a/core/src/main/java/hudson/model/User.java +++ b/core/src/main/java/hudson/model/User.java @@ -40,6 +40,7 @@ import hudson.init.InitMilestone; import hudson.init.Initializer; import hudson.model.listeners.SaveableListener; +import hudson.search.SearchGroup; import hudson.security.ACL; import hudson.security.AccessControlled; import hudson.security.SecurityRealm; @@ -283,6 +284,11 @@ public String getSearchIcon() { return UserAvatarResolver.resolve(this, "48x48"); } + @Override + public String getSearchGroup() { + return SearchGroup.PEOPLE; + } + /** * The URL of the user page. */ diff --git a/core/src/main/java/hudson/model/View.java b/core/src/main/java/hudson/model/View.java index 722254572c0c..da7b04c09b96 100644 --- a/core/src/main/java/hudson/model/View.java +++ b/core/src/main/java/hudson/model/View.java @@ -42,6 +42,7 @@ import hudson.model.Descriptor.FormException; import hudson.model.listeners.ItemListener; import hudson.search.CollectionSearchIndex; +import hudson.search.SearchGroup; import hudson.search.SearchIndexBuilder; import hudson.security.ACL; import hudson.security.AccessControlled; @@ -565,6 +566,11 @@ public String getSearchIcon() { return "symbol-jobs"; } + @Override + public String getSearchGroup() { + return SearchGroup.VIEW; + } + /** * Returns the transient {@link Action}s associated with the top page. * diff --git a/core/src/main/java/hudson/search/Search.java b/core/src/main/java/hudson/search/Search.java index 6f1240560351..f7b1d8f82912 100644 --- a/core/src/main/java/hudson/search/Search.java +++ b/core/src/main/java/hudson/search/Search.java @@ -171,7 +171,7 @@ public void doSuggest(StaplerRequest2 req, StaplerResponse2 rsp, @QueryParameter if (iconName.startsWith("symbol")) { r.suggestions.add(new Item(curItem.getPath(), curItem.getUrl(), - Symbol.get(new SymbolRequest.Builder().withRaw(iconName).build()))); + Symbol.get(new SymbolRequest.Builder().withRaw(iconName).build()), "symbol", curItem.item.getSearchGroup())); } else { r.suggestions.add(new Item(curItem.getPath(), curItem.getUrl(), iconName, "image", curItem.item.getSearchGroup())); } @@ -279,21 +279,13 @@ public static class Item { private final String iconXml; - private final SearchGroup group; + private final String group; public Item(String name) { - this(name, null, null); + this(name, null, null, null, null); } - public Item(String name, String url, String iconXml) { - this.name = name; - this.url = url; - this.iconXml = iconXml; - this.type = "symbol"; - this.group = null; - } - - public Item(String name, String url, String iconXml, String type, SearchGroup group) { + public Item(String name, String url, String iconXml, String type, String group) { this.name = name; this.url = url; this.iconXml = iconXml; @@ -317,7 +309,7 @@ public String getType() { } @Exported - public SearchGroup getGroup() { + public String getGroup() { return group; } } diff --git a/core/src/main/java/hudson/search/SearchGroup.java b/core/src/main/java/hudson/search/SearchGroup.java index fc13f724a4c7..d716812aef3c 100644 --- a/core/src/main/java/hudson/search/SearchGroup.java +++ b/core/src/main/java/hudson/search/SearchGroup.java @@ -1,31 +1,16 @@ package hudson.search; -import org.kohsuke.stapler.export.Exported; -import org.kohsuke.stapler.export.ExportedBean; +public final class SearchGroup { -@ExportedBean(defaultVisibility = 999) -public class SearchGroup { + public static final String VIEW = Messages.SearchGroup_views(); - private final String displayName; + public static final String BUILD = Messages.SearchGroup_builds(); - public SearchGroup(String displayName) { - this.displayName = displayName; - } + public static final String COMPUTER = Messages.SearchGroup_nodes(); - @Exported - public String getDisplayName() { - return displayName; - } + public static final String PROJECT = Messages.SearchGroup_projects(); - public static final SearchGroup VIEW = new SearchGroup(Messages.SearchGroup_views()); + public static final String PEOPLE = Messages.SearchGroup_people(); - public static final SearchGroup BUILD = new SearchGroup(Messages.SearchGroup_builds()); - - public static final SearchGroup COMPUTER = new SearchGroup(Messages.SearchGroup_nodes()); - - public static final SearchGroup PROJECT = new SearchGroup(Messages.SearchGroup_projects()); - - public static final SearchGroup PEOPLE = new SearchGroup(Messages.SearchGroup_people()); - - public static final SearchGroup OTHER = new SearchGroup(Messages.SearchGroup_other()); + public static final String OTHER = Messages.SearchGroup_other(); } diff --git a/core/src/main/java/hudson/search/SearchItem.java b/core/src/main/java/hudson/search/SearchItem.java index 2cb70a3a897d..a42ea9f6a6e4 100644 --- a/core/src/main/java/hudson/search/SearchItem.java +++ b/core/src/main/java/hudson/search/SearchItem.java @@ -63,7 +63,7 @@ default String getSearchIcon() { return "symbol-search"; } - default SearchGroup getSearchGroup() { + default String getSearchGroup() { return SearchGroup.OTHER; } diff --git a/src/main/js/components/command-palette/datasources.js b/src/main/js/components/command-palette/datasources.js index c1666101d410..9dec1b01d91c 100644 --- a/src/main/js/components/command-palette/datasources.js +++ b/src/main/js/components/command-palette/datasources.js @@ -21,6 +21,7 @@ export const JenkinsSearchSource = { type: e.type, label: e.name, url: correctAddress(e.url), + group: e.group, }), ); }), diff --git a/src/main/js/components/command-palette/index.js b/src/main/js/components/command-palette/index.js index 8530ae3d4a30..a0935fd7a18c 100644 --- a/src/main/js/components/command-palette/index.js +++ b/src/main/js/components/command-palette/index.js @@ -5,6 +5,7 @@ import * as Symbols from "./symbols"; import makeKeyboardNavigable from "@/util/keyboard"; import { xmlEscape } from "@/util/security"; import { createElementFromHtml } from "@/util/dom"; +import { groupResultsByCategory } from "@/components/command-palette/utils"; const datasources = [JenkinsSearchSource]; @@ -63,6 +64,7 @@ function init() { label: i18n.dataset.getHelp, url: headerCommandPaletteButton.dataset.searchHelpUrl, isExternal: true, + group: null }), ]); } else { @@ -72,15 +74,26 @@ function init() { } results.then((results) => { + results = groupResultsByCategory(results); + // Clear current search results searchResults.innerHTML = ""; if (query.length === 0 || Object.keys(results).length > 0) { - results.forEach(function (obj) { - const link = createElementFromHtml(obj.render()); - link.addEventListener("mouseenter", (e) => itemMouseEnter(e)); - searchResults.append(link); - }); + for (const [group, items] of Object.entries(results)) { + if (group !== 'null') { + const heading = document.createElement("p"); + heading.className = "jenkins-command-palette__results__heading"; + heading.innerText = group; + searchResults.append(heading); + } + + items.forEach(function (obj) { + const link = createElementFromHtml(obj.render()); + link.addEventListener("mouseenter", (e) => itemMouseEnter(e)); + searchResults.append(link); + }); + } updateSelectedItem(0); } else { diff --git a/src/main/js/components/command-palette/models.js b/src/main/js/components/command-palette/models.js index b3b06f22714c..c6c53119e02e 100644 --- a/src/main/js/components/command-palette/models.js +++ b/src/main/js/components/command-palette/models.js @@ -7,12 +7,14 @@ import { xmlEscape } from "@/util/security"; * @param {string} params.label * @param {string} params.type * @param {string} params.url + * @param {string | null} params.group * @param {boolean | undefined} params.isExternal */ export function LinkResult(params) { return { label: params.label, url: params.url, + group: params.group, render: () => { return `