branch: externals/ruby-end commit 8787319fe325e8e56835147e13f96c5315e60652 Author: Johan Andersson <johan.rej...@gmail.com> Commit: Johan Andersson <johan.rej...@gmail.com>
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)