Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRémy Coutable <remy@rymai.me>2016-06-30 18:34:19 +0300
committerRémy Coutable <remy@rymai.me>2016-08-13 01:05:57 +0300
commit0eea8c885743575b0e93a98846b3663e9903aa66 (patch)
tree9b1903bcb03789d15ed255b76be5d683f3b1e547 /lib/gitlab/slash_commands
parent11eefba891f214eefc1efa334adbcc9e979c0ce3 (diff)
Support slash commands in noteable description and notes
Some important things to note: - commands are removed from noteable.description / note.note - commands are translated to params so that they are treated as normal params in noteable Creation services - the logic is not in the models but in the Creation services, which is the right place for advanced logic that has nothing to do with what models should be responsible of! - UI/JS needs to be updated to handle notes which consist of commands only - the `/merge` command is not handled yet Other improvements: - Don't process commands in commit notes and display a flash is note is only commands - Add autocomplete for slash commands - Add description and params to slash command DSL methods - Ensure replying by email with a commands-only note works - Use :subscription_event instead of calling noteable.subscribe - Support :todo_event in IssuableBaseService Signed-off-by: Rémy Coutable <remy@rymai.me>
Diffstat (limited to 'lib/gitlab/slash_commands')
-rw-r--r--lib/gitlab/slash_commands/dsl.rb76
-rw-r--r--lib/gitlab/slash_commands/extractor.rb59
2 files changed, 135 insertions, 0 deletions
diff --git a/lib/gitlab/slash_commands/dsl.rb b/lib/gitlab/slash_commands/dsl.rb
new file mode 100644
index 00000000000..3ded4109f2e
--- /dev/null
+++ b/lib/gitlab/slash_commands/dsl.rb
@@ -0,0 +1,76 @@
+module Gitlab
+ module SlashCommands
+ module Dsl
+ extend ActiveSupport::Concern
+
+ included do
+ @command_definitions = []
+ end
+
+ module ClassMethods
+ def command_definitions
+ @command_definitions
+ end
+
+ def command_names
+ command_definitions.flat_map do |command_definition|
+ [command_definition[:name], command_definition[:aliases]].flatten
+ end
+ end
+
+ # Allows to give a description to the next slash command
+ def desc(text)
+ @description = text
+ end
+
+ # Allows to define params for the next slash command
+ def params(*params)
+ @params = params
+ end
+
+ # Registers a new command which is recognizeable
+ # from body of email or comment.
+ # Example:
+ #
+ # command :command_key do |arguments|
+ # # Awesome code block
+ # end
+ #
+ def command(*command_names, &block)
+ command_name, *aliases = command_names
+ proxy_method_name = "__#{command_name}__"
+
+ # This proxy method is needed because calling `return` from inside a
+ # block/proc, causes a `return` from the enclosing method or lambda,
+ # otherwise a LocalJumpError error is raised.
+ define_method(proxy_method_name, &block)
+
+ define_method(command_name) do |*args|
+ proxy_method = method(proxy_method_name)
+
+ if proxy_method.arity == -1 || proxy_method.arity == args.size
+ instance_exec(*args, &proxy_method)
+ end
+ end
+
+ private command_name
+ aliases.each do |alias_command|
+ alias_method alias_command, command_name
+ private alias_command
+ end
+
+ command_definition = {
+ name: command_name,
+ aliases: aliases,
+ description: @description || '',
+ params: @params || []
+ }
+ @command_definitions << command_definition
+
+ @description = nil
+ @params = nil
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/slash_commands/extractor.rb b/lib/gitlab/slash_commands/extractor.rb
new file mode 100644
index 00000000000..1a854b81aca
--- /dev/null
+++ b/lib/gitlab/slash_commands/extractor.rb
@@ -0,0 +1,59 @@
+module Gitlab
+ module SlashCommands
+ # This class takes an array of commands that should be extracted from a
+ # given text.
+ #
+ # ```
+ # extractor = Gitlab::SlashCommands::Extractor.new([:open, :assign, :labels])
+ # ```
+ class Extractor
+ attr_reader :command_names
+
+ def initialize(command_names)
+ @command_names = command_names
+ end
+
+ # Extracts commands from content and return an array of commands.
+ # The array looks like the following:
+ # [
+ # ['command1'],
+ # ['command3', 'arg1 arg2'],
+ # ]
+ # The command and the arguments are stripped.
+ # The original command text is removed from the given `content`.
+ #
+ # Usage:
+ # ```
+ # extractor = Gitlab::SlashCommands::Extractor.new([:open, :assign, :labels])
+ # msg = %(hello\n/labels ~foo ~"bar baz"\nworld)
+ # commands = extractor.extract_commands! #=> [['labels', '~foo ~"bar baz"']]
+ # msg #=> "hello\nworld"
+ # ```
+ def extract_commands!(content)
+ return [] unless content
+
+ commands = []
+
+ content.gsub!(commands_regex) do
+ commands << [$1, $2].flatten.reject(&:blank?)
+ ''
+ end
+
+ commands
+ end
+
+ private
+
+ # Builds a regular expression to match known commands.
+ # First match group captures the command name and
+ # second match group captures its arguments.
+ #
+ # It looks something like:
+ #
+ # /^\/(?<cmd>close|reopen|...)(?:( |$))(?<args>[^\/\n]*)(?:\n|$)/
+ def commands_regex
+ /^\/(?<cmd>#{command_names.join('|')})(?:( |$))(?<args>[^\/\n]*)(?:\n|$)/
+ end
+ end
+ end
+end