diff --git a/src/main/scala/gitbucket/core/controller/IndexController.scala b/src/main/scala/gitbucket/core/controller/IndexController.scala index ddcaa6cd8..a4cf49f8b 100644 --- a/src/main/scala/gitbucket/core/controller/IndexController.scala +++ b/src/main/scala/gitbucket/core/controller/IndexController.scala @@ -121,7 +121,12 @@ trait IndexControllerBase extends ControllerBase { case (true, false) => !t.isGroupAccount case (false, true) => t.isGroupAccount case (false, false) => false - }}.map { t => t.userName } + }}.map { t => + Map( + "label" -> s"@${t.userName} ${t.fullName}", + "value" -> t.userName + ) + } )) ) }) diff --git a/src/main/scala/gitbucket/core/plugin/SuggestionProvider.scala b/src/main/scala/gitbucket/core/plugin/SuggestionProvider.scala index 3aafa11d3..e7d49df5c 100644 --- a/src/main/scala/gitbucket/core/plugin/SuggestionProvider.scala +++ b/src/main/scala/gitbucket/core/plugin/SuggestionProvider.scala @@ -3,15 +3,92 @@ package gitbucket.core.plugin import gitbucket.core.controller.Context import gitbucket.core.service.RepositoryService.RepositoryInfo +/** + * The base trait of suggestion providers which supplies completion proposals in some text areas. + */ trait SuggestionProvider { + /** + * The identifier of this suggestion provider. + * You must specify the unique identifier in the all suggestion providers. + */ val id: String + + /** + * The trigger of this suggestion provider. When user types this character, the proposal list would be displayed. + * Also this is used as the prefix of the replaced string. + */ val prefix: String + + /** + * The suffix of the replaced string. The default is `" "`. + */ val suffix: String = " " + + /** + * Which contexts is this suggestion provider enabled. Currently, available contexts are `"issues"` and `"wiki"`. + */ val context: Seq[String] - def values(repository: RepositoryInfo): Seq[String] - def template(implicit context: Context): String = "value" + /** + * If this suggestion provider has static proposal list, override this method to return it. + * + * The returned sequence is rendered as follows: + *
+ * [
+ * {
+ * "label" -> "value1",
+ * "value" -> "value1"
+ * },
+ * {
+ * "label" -> "value2",
+ * "value" -> "value2"
+ * },
+ * ]
+ *
+ *
+ * Each element can be accessed as `option` in `template()` or `replace()` method.
+ */
+ def values(repository: RepositoryInfo): Seq[String] = Nil
+
+ /**
+ * If this suggestion provider has static proposal list, override this method to return it.
+ *
+ * If your proposals have label and value, use this method instead of `values()`.
+ * The first element of tuple is used as a value, and the second element is used as a label.
+ *
+ * The returned sequence is rendered as follows:
+ *
+ * [
+ * {
+ * "label" -> "label1",
+ * "value" -> "value1"
+ * },
+ * {
+ * "label" -> "label2",
+ * "value" -> "value2"
+ * },
+ * ]
+ *
+ *
+ * Each element can be accessed as `option` in `template()` or `replace()` method.
+ */
+ def options(repository: RepositoryInfo): Seq[(String, String)] = values(repository).map { value => (value, value) }
+
+ /**
+ * JavaScript fragment to generate a label of completion proposal. The default is: `option.label`.
+ */
+ def template(implicit context: Context): String = "option.label"
+
+ /**
+ * JavaScript fragment to generate a replaced value of completion proposal. The default is: `option.value`
+ */
+ def replace(implicit context: Context): String = "option.value"
+
+ /**
+ * If this suggestion provider needs some additional process to assemble the proposal list (e.g. It need to use Ajax
+ * to get a proposal list from the server), then override this method and return any JavaScript code.
+ */
def additionalScript(implicit context: Context): String = ""
}
@@ -20,8 +97,6 @@ class UserNameSuggestionProvider extends SuggestionProvider {
override val id: String = "user"
override val prefix: String = "@"
override val context: Seq[String] = Seq("issues")
- override def values(repository: RepositoryInfo): Seq[String] = Nil
- override def template(implicit context: Context): String = "'@' + value"
override def additionalScript(implicit context: Context): String =
s"""$$.get('${context.path}/_user/proposals', { query: '', user: true, group: false }, function (data) { user = data.options; });"""
-}
\ No newline at end of file
+}
diff --git a/src/main/twirl/gitbucket/core/helper/attached.scala.html b/src/main/twirl/gitbucket/core/helper/attached.scala.html
index 26ade3fd5..c68c2bf23 100644
--- a/src/main/twirl/gitbucket/core/helper/attached.scala.html
+++ b/src/main/twirl/gitbucket/core/helper/attached.scala.html
@@ -11,7 +11,9 @@
$(function(){
@gitbucket.core.plugin.PluginRegistry().getSuggestionProviders.map { provider =>
@if(provider.context.contains(completionContext)){
- var @provider.id = @Html(helpers.json(provider.values(repository)));
+ var @provider.id = @Html(helpers.json(provider.options(repository).map { case (value, label) =>
+ Map("value" -> value, "label" -> label)
+ }));
@Html(provider.additionalScript)
}
}
@@ -23,14 +25,14 @@ $(function(){
match: /\B@{provider.prefix}([\-+\w]*)$/,
search: function (term, callback) {
callback($.map(@{provider.id}, function (proposal) {
- return proposal.indexOf(term) === 0 ? proposal : null;
+ return proposal.value.indexOf(term) === 0 ? proposal : null;
}));
},
- template: function (value) {
+ template: function (option) {
return @{Html(provider.template)};
},
- replace: function (value) {
- return '@{provider.prefix}' + value + '@{provider.suffix}';
+ replace: function (option) {
+ return '@{provider.prefix}' + @{Html(provider.replace)} + '@{provider.suffix}';
},
index: 1
},