diff --git a/src/main/scala/gitbucket/core/controller/IndexController.scala b/src/main/scala/gitbucket/core/controller/IndexController.scala index ddcaa6c..a4cf49f 100644 --- a/src/main/scala/gitbucket/core/controller/IndexController.scala +++ b/src/main/scala/gitbucket/core/controller/IndexController.scala @@ -121,7 +121,12 @@ 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 3aafa11..e7d49df 100644 --- a/src/main/scala/gitbucket/core/plugin/SuggestionProvider.scala +++ b/src/main/scala/gitbucket/core/plugin/SuggestionProvider.scala @@ -3,15 +3,92 @@ 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 @@ 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 26ade3f..c68c2bf 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 @@ 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 },