branch: externals/ruby-end
commit 8787319fe325e8e56835147e13f96c5315e60652
Author: Johan Andersson <[email protected]>
Commit: Johan Andersson <[email protected]>
Added code with tests.
---
features/ruby-end.feature | 98 +++++++++++++++++++++++++++++
features/step-definitions/ruby-end-steps.el | 31 +++++++++
ruby-end.el | 43 +++++++++++--
3 files changed, 168 insertions(+), 4 deletions(-)
diff --git a/features/ruby-end.feature b/features/ruby-end.feature
index e69de29bb2..11b73f0d84 100644
--- a/features/ruby-end.feature
+++ b/features/ruby-end.feature
@@ -0,0 +1,98 @@
+Feature: Insert end
+ In order to be quicker in ruby-mode
+ As a ruby developer
+ I want to automatically have end inserted for blocks
+
+ Background:
+ Given I am in buffer "*ruby-end*"
+ And the buffer is empty
+ And ruby-mode is active
+ And ruby-end-mode is active
+
+ Scenario: Keyword at beginning of line
+ When I type "if"
+ And I press "SPC"
+ And I type "condition"
+ Then I should see:
+ """
+ if condition
+
+ end
+ """
+
+ Scenario: Whitespace before keyword
+ When I type " "
+ When I type "if"
+ And I press "SPC"
+ And I type "condition"
+ Then I should see:
+ """
+ if condition
+
+ end
+ """
+
+ Scenario: Text before keyword
+ When I type "x"
+ And I type "if"
+ And I press "SPC"
+ And I type "condition"
+ Then I should see:
+ """
+ xif condition
+ """
+
+ Scenario: Other character before keyword
+ When I type "!"
+ And I type "if"
+ And I press "SPC"
+ And I type "condition"
+ Then I should see:
+ """
+ !if condition
+ """
+
+ Scenario: Nested expansion
+ When I type "class"
+ And I press "SPC"
+ And I type "User"
+ And I go to the end of next line
+ And I type "def"
+ And I press "SPC"
+ And I type "email"
+ Then I should see:
+ """
+ class User
+ def email
+
+ end
+ end
+ """
+
+ Scenario: Only exact keywords expand
+ When I type "xif"
+ And I press "SPC"
+ And I type "condition"
+ Then I should see "xif condition"
+ And end should not be insterted
+
+
+ # NOTE:
+ # I have to hax these two scenarios, since running Emacs in batch
+ # mode does not set the text properties.
+
+ Scenario: In comment
+ Given I insert "# if "
+ And I set face to be comment
+ And I go back one character
+ And I press "SPC"
+ And I type "condition"
+ Then end should not be insterted
+
+ Scenario: In string
+ Given I insert "'if'"
+ And I set face to be string
+ And I go back one character
+ And I press "SPC"
+ And I type "condition"
+ Then end should not be insterted
diff --git a/features/step-definitions/ruby-end-steps.el
b/features/step-definitions/ruby-end-steps.el
index e69de29bb2..c0efdcf66e 100644
--- a/features/step-definitions/ruby-end-steps.el
+++ b/features/step-definitions/ruby-end-steps.el
@@ -0,0 +1,31 @@
+(Given "^ruby-mode is active$"
+ (lambda ()
+ (ruby-mode)))
+
+(Given "^ruby-end-mode is active$"
+ (lambda ()
+ (ruby-end-mode +1)))
+
+(Then "^end should not be insterted$"
+ (lambda ()
+ (save-excursion
+ (goto-char (point-min))
+ (should-not
+ (re-search-forward "end" nil t)))))
+
+(When "^I go to the end of next line$"
+ (lambda ()
+ (call-interactively 'next-line)
+ (call-interactively 'move-end-of-line)))
+
+(When "^I go back one character$"
+ (lambda ()
+ (backward-char 1)))
+
+(Given "^I set face to be comment$"
+ (lambda ()
+ (add-text-properties (point-min) (point-max) '(face
font-lock-string-face))))
+
+(Given "^I set face to be string$"
+ (lambda ()
+ (add-text-properties (point-min) (point-max) '(face
font-lock-comment-face))))
diff --git a/ruby-end.el b/ruby-end.el
index e0f5339224..dd7adf373f 100644
--- a/ruby-end.el
+++ b/ruby-end.el
@@ -33,19 +33,54 @@
;;; Code:
-(defvar ruby-end-mode-map (make-sparse-keymap)
+(defvar ruby-end-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map (kbd "SPC") 'ruby-end-space)
+ map)
"Keymap for `ruby-end-mode'.")
+(defvar ruby-end-keywords-re
+
"\\(?:^\\|\\s-+\\)\\(?:def\\|if\\|class\\|module\\|unless\\|case\\|while\\|do\\|until\\|for\\|begin\\)"
+ "Regular expression matching from point and backwards a valid keyword.")
+
+(defun ruby-end-space ()
+ "Called when SPC-key is pressed."
+ (interactive)
+ (when (ruby-end-expand-p)
+ (ruby-end-insert-end))
+ (insert " "))
+
+(defun ruby-end-insert-end ()
+ "Closes block by inserting end."
+ (let ((whites (save-excursion (back-to-indentation) (current-column))))
+ (save-excursion
+ (newline)
+ (indent-line-to (+ whites ruby-indent-level))
+ (newline)
+ (indent-line-to whites)
+ (insert "end"))))
+
+(defun ruby-end-expand-p ()
+ "Checks if expansion (insertion of end) should be done."
+ (and
+ (ruby-end-code-at-point-p)
+ (looking-back ruby-end-keywords-re)))
+
+(defun ruby-end-code-at-point-p ()
+ "Checks if point is code, or comment or string."
+ (let ((properties (text-properties-at (point))))
+ (and
+ (null (memq 'font-lock-string-face properties))
+ (null (memq 'font-lock-comment-face properties)))))
;;;###autoload
(define-minor-mode ruby-end-mode
"Automatic insertion of end for blocks"
:init-value nil
:lighter " end"
- :keymap ruby-end-mode-map
- (when ruby-end-mode
+ :keymap ruby-end-mode-map)
- ))
+(add-hook 'ruby-mode-hook 'ruby-end-mode)
(provide 'ruby-end)