tabify.el 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. ;;; tabify.el --- tab conversion commands for Emacs
  2. ;; Copyright (C) 1985, 1994, 2001-2017 Free Software Foundation, Inc.
  3. ;; Maintainer: emacs-devel@gnu.org
  4. ;; Package: emacs
  5. ;; This file is part of GNU Emacs.
  6. ;; GNU Emacs is free software: you can redistribute it and/or modify
  7. ;; it under the terms of the GNU General Public License as published by
  8. ;; the Free Software Foundation, either version 3 of the License, or
  9. ;; (at your option) any later version.
  10. ;; GNU Emacs is distributed in the hope that it will be useful,
  11. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ;; GNU General Public License for more details.
  14. ;; You should have received a copy of the GNU General Public License
  15. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  16. ;;; Commentary:
  17. ;; Commands to optimize spaces to tabs or expand tabs to spaces in a region
  18. ;; (`tabify' and `untabify'). The variable tab-width does the obvious.
  19. ;;; Code:
  20. ;;;###autoload
  21. (defun untabify (start end &optional _arg)
  22. "Convert all tabs in region to multiple spaces, preserving columns.
  23. If called interactively with prefix ARG, convert for the entire
  24. buffer.
  25. Called non-interactively, the region is specified by arguments
  26. START and END, rather than by the position of point and mark.
  27. The variable `tab-width' controls the spacing of tab stops."
  28. (interactive (if current-prefix-arg
  29. (list (point-min) (point-max) current-prefix-arg)
  30. (list (region-beginning) (region-end) nil)))
  31. (let ((c (current-column)))
  32. (save-excursion
  33. (save-restriction
  34. (narrow-to-region (point-min) end)
  35. (goto-char start)
  36. (while (search-forward "\t" nil t) ; faster than re-search
  37. (forward-char -1)
  38. (let ((tab-beg (point))
  39. (indent-tabs-mode nil)
  40. column)
  41. (skip-chars-forward "\t")
  42. (setq column (current-column))
  43. (delete-region tab-beg (point))
  44. (indent-to column)))))
  45. (move-to-column c)))
  46. (defvar tabify-regexp " [ \t]+"
  47. "Regexp matching whitespace that tabify should consider.
  48. Usually this will be \" [ \\t]+\" to match a space followed by whitespace.
  49. \"^\\t* [ \\t]+\" is also useful, for tabifying only initial whitespace.")
  50. ;;;###autoload
  51. (defun tabify (start end &optional _arg)
  52. "Convert multiple spaces in region to tabs when possible.
  53. A group of spaces is partially replaced by tabs
  54. when this can be done without changing the column they end at.
  55. If called interactively with prefix ARG, convert for the entire
  56. buffer.
  57. Called non-interactively, the region is specified by arguments
  58. START and END, rather than by the position of point and mark.
  59. The variable `tab-width' controls the spacing of tab stops."
  60. (interactive (if current-prefix-arg
  61. (list (point-min) (point-max) current-prefix-arg)
  62. (list (region-beginning) (region-end) nil)))
  63. (save-excursion
  64. (save-restriction
  65. ;; Include the beginning of the line in the narrowing
  66. ;; since otherwise it will throw off current-column.
  67. (goto-char start)
  68. (beginning-of-line)
  69. (narrow-to-region (point) end)
  70. (goto-char start)
  71. (let ((indent-tabs-mode t))
  72. (while (re-search-forward tabify-regexp nil t)
  73. ;; The region between (match-beginning 0) and (match-end 0) is just
  74. ;; spacing which we want to adjust to use TABs where possible.
  75. (let ((end-col (current-column))
  76. (beg-col (save-excursion (goto-char (match-beginning 0))
  77. (skip-chars-forward "\t")
  78. (current-column))))
  79. (if (= (/ end-col tab-width) (/ beg-col tab-width))
  80. ;; The spacing (after some leading TABs which we wouldn't
  81. ;; want to touch anyway) does not straddle a TAB boundary,
  82. ;; so it neither contains a TAB, nor will we be able to use
  83. ;; a TAB here anyway: there's nothing to do.
  84. nil
  85. (delete-region (match-beginning 0) (point))
  86. (indent-to end-col))))))))
  87. (provide 'tabify)
  88. ;;; tabify.el ends here