├── FciScreenshot.png ├── README.markdown └── fill-column-indicator.el /FciScreenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpaker/fill-column-indicator/HEAD/FciScreenshot.png -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | _The functionality provided by this package has now been implemented natively in 2 | Emacs as `display-fill-column-indicator-mode`, available as of version 27.0.90. 3 | Unless you are forced to use an older Emacs, you should use the native 4 | implementation instead of `fci-mode`. It's unaffected by most of the 5 | compatibility issues that this add-on has._ 6 | 7 | _This package is no longer actively maintained._ 8 | 9 | Many modern editors and IDEs can graphically indicate the location of the 10 | fill column by drawing a thin line (in design parlance, a "rule") down the 11 | length of the editing window. Fill-column-indicator implements this 12 | facility in Emacs: 13 | 14 | ![screenshot](https://github.com/alpaker/Fill-Column-Indicator/raw/master/FciScreenshot.png) 15 | 16 | #### Please Note 17 | 18 | There is a small incompatibility between this package and the current stable 19 | Emacs release (v24.3). See [issue #31](https://github.com/alpaker/Fill-Column-Indicator/issues/31) for more 20 | information. 21 | 22 | #### Installation and Usage 23 | 24 | Put the package file in your load path and put 25 | 26 | `(require 'fill-column-indicator)` 27 | 28 | in your .emacs. 29 | 30 | To toggle graphical indication of the fill column in a buffer, use the 31 | command `fci-mode`. 32 | 33 | #### Configuration 34 | 35 | * By default fci-mode draws a vertical line at the fill column. If you'd 36 | like it to be drawn at a different location, set `fci-rule-column` to the 37 | desired column number. (A case in which this might be useful is when you 38 | want to fill comments at, for example, column 70, but want a vertical rule 39 | at column 80 or 100 to indicate the maximum line length for code.) The 40 | default behavior (showing the indicator at the fill column) is specified by 41 | setting fci-rule-column to nil. Note that this variable becomes buffer 42 | local when set. 43 | 44 | * On graphical displays the fill-column rule is drawn using a bitmap 45 | image. Its color is controlled by the variable `fci-rule-color`, whose 46 | value can be any valid color name. The rule's width in pixels is 47 | determined by the variable `fci-rule-width`; the default value is 1. 48 | 49 | * The rule can be drawn as a solid or dashed line, as specified by the 50 | variable `fci-rule-use-dashes`; the default is nil. The length of the 51 | dashes is controlled by `fci-dash-pattern`, which is the ratio of dash 52 | length to line height; the default value is 0.75. (The value should be a 53 | number between 0 and 1; values outside that interval are coerced to the 54 | nearest endpoint.) 55 | 56 | * The image formats fci-mode can use are XPM and PBM. If Emacs has 57 | been compiled with the appropriate library it uses XPM images by default; 58 | if not it uses PBM images, which are natively supported. You can specify a 59 | particular format by setting `fci-rule-image-format` to either `xpm` or `pbm`. 60 | 61 | * On character terminals the rule is drawn using the character specified by 62 | `fci-rule-character`; the default is \`|' (ascii 124). If 63 | `fci-rule-character-color` is nil, then it is drawn using fci-rule-color 64 | (or the closest approximation thereto that the terminal is capable of); if 65 | it is a color name, then that color is used instead. 66 | 67 | * If you'd like the rule to be drawn using fci-rule-character even on 68 | graphical displays, set `fci-always-use-textual-rule` to a non-nil value. 69 | 70 | These variables (as well as those in the next section) can be given 71 | buffer-local bindings. 72 | 73 | 74 | #### Other Options 75 | 76 | When `truncate-lines` is nil, the effect of drawing a fill-column rule is 77 | very odd looking. Indeed, it makes little sense to use a rule to indicate 78 | the position of the fill column in that case (the positions at which the 79 | fill column falls in the visual display space won't in general be 80 | collinear). For this reason, fci-mode sets truncate-lines to t in buffers 81 | in which it is enabled and restores it to its previous value when 82 | disabled. You can turn this feature off by setting 83 | `fci-handle-truncate-lines` to nil. 84 | 85 | If `line-move-visual` is t, then vertical navigation can behave oddly in 86 | several edge cases while fci-mode is enabled (this is due to a bug in Emacs's 87 | C code). Accordingly, fci-mode sets line-move-visual to nil in buffers in 88 | which it is enabled and restores it to its previous value when 89 | disabled. This can be suppressed by setting `fci-handle-line-move-visual` to 90 | nil. (But you shouldn't want to do this. There's no reason to use 91 | line-move-visual if truncate-lines is t, and it doesn't make sense to use 92 | something like fci-mode when truncate-lines is nil.) 93 | 94 | Fci-mode needs free use of two characters (specifically, it needs the use 95 | of two characters whose display table entries it can change 96 | arbitrarily). By default, it uses the first two characters of the Private 97 | Use Area of the Unicode BMP, viz. U+E000 and U+E001. If you need to use 98 | those characters for some other purpose, set `fci-eol-char` and 99 | `fci-blank-char` to different values. 100 | 101 | #### Troubleshooting 102 | 103 | * Fci-mode is intended to be used with monospaced fonts. If you're using 104 | a monospaced font and the fill-column rule is missing or misaligned on a 105 | few lines but otherwise appears normal, then most likely (a) there are 106 | non-ascii characters on those lines that are being displayed using a 107 | non-monospaced font, or (b) your font-lock settings use bold or italics 108 | and those font variants aren't monospaced. 109 | 110 | * Fci-mode in not currently compatible with Emacs's 111 | `show-trailing-whitespace` feature (given the way the latter is 112 | implemented, such compatilibility is going to be hard to achieve). A 113 | workaround is to use `whitespace-mode` with an appropriate 114 | configuration. This will provide the same functionality as 115 | show-trailing-whitespace while remaning compatible with fci-mode. The 116 | appropriate whitespace setting is: 117 | 118 | (setq whitespace-style '(face trailing)) 119 | 120 | #### Known Issues 121 | 122 | * The indicator extends only to end of the buffer contents (as opposed to 123 | running the full length of the editing window). 124 | 125 | * When portions of a buffer are invisible, such as when outline mode is 126 | used to hide certain lines, the fill-column rule is hidden as 127 | well. 128 | 129 | * Fci-mode should work smoothly when simultaneously displaying the same 130 | buffer on both a graphical display and on a character terminal. It does 131 | not currently support simultaneous display of the same buffer on window 132 | frames with different default font sizes. (It would be feasible to 133 | support this use case, but thus far there seems to be no demand for 134 | it.) 135 | 136 | * An issue specific to the Mac OS X (NextStep) port, versions 23.0-23.2: 137 | Emacs won't, in these particular versions, draw a cursor on top of an 138 | image. Thus on graphical displays the cursor will disappear when 139 | positioned directly on top of the fill-column rule. The best way to deal 140 | with this is to upgrade to v23.3 or v24 (or downgrade to v22). If that 141 | isn't practical, a fix is available via the mini-package `fci-osx-23-fix.el`, 142 | which can be downloaded from this page. Directions for its use are given 143 | in the file header. 144 | 145 | #### Todo 146 | 147 | * Accommodate non-nil values of `hl-line-sticky-flag` and similar cases. 148 | 149 | * Accommodate linum-mode more robustly. 150 | 151 | * Compatibility with non-nil `show-trailing-whitespace`. 152 | 153 | 154 | #### Acknowledgements 155 | 156 | Thanks to Ami Fischman, Christopher Genovese, Michael Hoffman, José Alfredo 157 | Romero L., R. Lange, Joe Lisee, José Lombera, Frank Meffert, Mitchell 158 | Peabody, sheijk, and an anonymous BT subscriber for bug reports and 159 | suggestions. Special thanks to lomew, David Röthlisberger, and Pär 160 | Wieslander for code contributions. 161 | 162 | -------------------------------------------------------------------------------- /fill-column-indicator.el: -------------------------------------------------------------------------------- 1 | ;;; fill-column-indicator.el --- Graphically indicate the fill column 2 | 3 | ;; Copyright (c) 2011-2017 Alp Aker 4 | 5 | ;; Author: Alp Aker 6 | ;; Version: 1.91 7 | ;; Keywords: convenience 8 | 9 | ;; This program is free software; you can redistribute it and/or 10 | ;; modify it under the terms of the GNU General Public License as 11 | ;; published by the Free Software Foundation; either version 2 of the 12 | ;; License, or (at your option) any later version. 13 | 14 | ;; This program is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | ;; General Public License for more details. 18 | 19 | ;; A copy of the GNU General Public License can be obtained from the 20 | ;; Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, 21 | ;; MA 02111-1307 USA 22 | 23 | ;;; Commentary: 24 | 25 | ;; NB: The functionality provided by this package has now been implemented 26 | ;; natively in Emacs as `display-fill-column-indicator-mode`, available as of 27 | ;; version 27.0.90. Unless you are forced to use an older Emacs, you should use 28 | ;; the native implementation instead of `fci-mode`. It's unaffected by most of the 29 | ;; compatibility issues that this add-on has. This package is no longer actively 30 | ;; maintained. 31 | 32 | ;; Many modern editors and IDEs can graphically indicate the location of the 33 | ;; fill column by drawing a thin line (in design parlance, a `rule') down the 34 | ;; length of the editing window. Fill-column-indicator implements this 35 | ;; facility in Emacs. 36 | 37 | ;; Installation and Usage 38 | ;; ====================== 39 | 40 | ;; Put this file in your load path and put: 41 | ;; 42 | ;; (require 'fill-column-indicator) 43 | ;; 44 | ;; in your init file. 45 | 46 | ;; To toggle graphical indication of the fill column in a buffer, use the 47 | ;; command `fci-mode'. 48 | 49 | ;; Configuration 50 | ;; ============= 51 | 52 | ;; By default, fci-mode draws its vertical indicator at the fill column. If 53 | ;; you'd like it to be drawn at another column, set `fci-rule-column' to the 54 | ;; column number. (A case in which this might be useful is when you want to 55 | ;; fill comments at, for example, column 70, but want a vertical rule at 56 | ;; column 80 or 100 to indicate the maximum line length for code.) The 57 | ;; default behavior (showing the indicator at the fill column) is specified 58 | ;; by setting fci-rule-column to nil. 59 | 60 | ;; On graphical displays the fill-column rule is drawn using a bitmap 61 | ;; image. Its color is controlled by the variable `fci-rule-color', whose 62 | ;; value can be any valid color name. The rule's width in pixels is 63 | ;; determined by the variable `fci-rule-width'; the default value is 1. 64 | 65 | ;; The rule can be drawn as a solid or dashed line, controlled by the 66 | ;; variable `fci-rule-use-dashes'; the default is nil. The dash appearance is 67 | ;; controlled by `fci-dash-pattern', which is the ratio of dash length to 68 | ;; line height; the default is 0.75. (The value should be a number between 0 69 | ;; and 1; values outside that interval are coerced to the nearest endpoint.) 70 | 71 | ;; The image formats fci-mode can use are XPM and PBM. If Emacs has been 72 | ;; compiled with the appropriate library it uses XPM images by default; if 73 | ;; not it uses PBM images, which are natively supported. You can specify a 74 | ;; particular choice of format by setting `fci-rule-image-format' explicitly 75 | ;; to xpm or pbm. 76 | 77 | ;; On character terminals the rule is drawn using the character specified by 78 | ;; `fci-rule-character'; the default is `|' (ascii 124). If 79 | ;; `fci-rule-character-color' is nil, then it is drawn using fci-rule-color 80 | ;; (or the closest approximation thereto that the terminal is capable of); if 81 | ;; it is a color name, then that color is used instead. 82 | 83 | ;; If you'd like the rule to be drawn using fci-rule-character even on 84 | ;; graphical displays, set `fci-always-use-textual-rule' to a non-nil value. 85 | 86 | ;; These variables (as well as those described in the next section) can be 87 | ;; given buffer-local bindings. 88 | 89 | ;; Other Options 90 | ;; ============= 91 | 92 | ;; When `truncate-lines' is nil, the effect of drawing a fill-column rule is 93 | ;; very odd looking. Indeed, it makes little sense to use a rule to indicate 94 | ;; the position of the fill column in that case (the positions at which the 95 | ;; fill column falls in the visual display space won't, in general, be 96 | ;; collinear). For this reason, fci-mode sets truncate-lines to t in buffers 97 | ;; in which it is enabled and restores it to its previous value when 98 | ;; disabled. You can turn this feature off by setting 99 | ;; `fci-handle-truncate-lines' to nil. 100 | 101 | ;; If `line-move-visual' is t, then vertical navigation can behave oddly in 102 | ;; several edge cases while fci-mode is enabled (this is due to a bug in 103 | ;; Emacs's C code). Accordingly, fci-mode sets line-move-visual to nil in 104 | ;; buffers in which it is enabled and restores it to its previous value when 105 | ;; disabled. This can be suppressed by setting `fci-handle-line-move-visual' 106 | ;; to nil. (But you shouldn't want to do this. There's no reason to use 107 | ;; line-move-visual if truncate-lines is t, and it doesn't make sense to use 108 | ;; something like fci-mode when truncate-lines is nil.) 109 | 110 | ;; Fci-mode needs free use of two characters (specifically, it needs the use 111 | ;; of two characters whose display table entries it can change 112 | ;; arbitrarily). Its default is to use the first two characters of the 113 | ;; Private Use Area of the Unicode BMP, viz. U+E000 and U+E001. If you need 114 | ;; to use those characters for some other purpose, set `fci-eol-char' and 115 | ;; `fci-blank-char' to different values. 116 | 117 | ;; Troubleshooting 118 | ;; =============== 119 | 120 | ;; o Fci-mode is intended to be used with monospaced fonts. If you're using 121 | ;; a monospaced font and the fill-column rule is missing or misaligned on a 122 | ;; few lines but otherwise appears normal, then most likely (a) there are 123 | ;; non-ascii characters on those lines that are being displayed using a 124 | ;; non-monospaced font, or (b) your font-lock settings use bold or italics 125 | ;; and those font variants aren't monospaced. 126 | 127 | ;; o Fci-mode in not currently compatible with Emacs's 128 | ;; `show-trailing-whitespace' feature (given the way the latter is 129 | ;; implemented, such compatibility is going to be hard to achieve). A 130 | ;; workaround is to configure `whitespace-mode' to replicate the 131 | ;; functionality of show-trailing-whitespace. This can be done with the 132 | ;; following setting: 133 | ;; 134 | ;; (setq whitespace-style '(face trailing)) 135 | ;; 136 | ;; With this, whitespace-mode produces the same basic effect as a non-nil 137 | ;; value of show-trailing-whitespace, and compatibility with fci-mode is not 138 | ;; a problem. 139 | 140 | ;; Known Issues 141 | ;; ============ 142 | 143 | ;; o The indicator extends only to end of the buffer contents (as opposed to 144 | ;; running the full length of the editing window). 145 | 146 | ;; o When portions of a buffer are invisible, such as when outline-mode is 147 | ;; used to hide certain lines, the fill-column rule is hidden as well. 148 | 149 | ;; o Fci-mode should work smoothly when simultaneously displaying the same 150 | ;; buffer on both a graphical display and on a character terminal. It does 151 | ;; not currently support simultaneous display of the same buffer on window 152 | ;; frames with different default font sizes. (It would be feasible to 153 | ;; support this use case, but thus far there seems to be no demand for 154 | ;; it.) 155 | 156 | ;; Todo 157 | ;; ==== 158 | 159 | ;; o Accommodate non-nil values of `hl-line-sticky-flag' and similar cases. 160 | 161 | ;; o Accommodate linum-mode more robustly. 162 | 163 | ;; o Compatibility with non-nil `show-trailing-whitespace.' 164 | 165 | ;; Acknowledgements 166 | ;; ================ 167 | 168 | ;; Thanks to Ami Fischman, Christopher Genovese, Michael Hoffman, José 169 | ;; Alfredo Romero L., R. Lange, Joe Lisee, José Lombera, Frank Meffert, 170 | ;; Mitchell Peabody, sheijk, and an anonymous BT subscriber for bug reports 171 | ;; and suggestions. Special thanks for code contributions: lomew, John Lamp, 172 | ;; Sean Perry, David Röthlisberger, Pär Wieslander. 173 | 174 | ;;; Code: 175 | 176 | (unless (version<= "25" emacs-version) 177 | (error "Fill-column-indicator requires version 25 or later")) 178 | 179 | ;;; --------------------------------------------------------------------- 180 | ;;; User Options 181 | ;;; --------------------------------------------------------------------- 182 | 183 | (defgroup fill-column-indicator nil 184 | "Graphically indicate the fill-column." 185 | :tag "Fill-Column Indicator" 186 | :group 'convenience 187 | :group 'fill) 188 | 189 | ;; We should be using :validate instead of :match, but that seems not to 190 | ;; work with defcustom widgets. 191 | (defcustom fci-rule-column nil 192 | "Controls where fci-mode displays a vertical line (rule). 193 | 194 | If nil, the rule is drawn at the fill column. Otherwise, it is 195 | drawn at the column given by this variable. 196 | 197 | Changes to this variable do not take effect until the mode 198 | function `fci-mode' is run." 199 | :group 'fill-column-indicator 200 | :tag "Fill-Column rule column" 201 | :type '(choice (const :tag "Use the fill column" nil) 202 | (integer :tag "Use a custom column" 203 | :match (lambda (w val) (fci-posint-p val))))) 204 | 205 | (defcustom fci-rule-color "#cccccc" 206 | "Color used to draw the fill-column rule. 207 | 208 | Changes to this variable do not take effect until the mode 209 | function `fci-mode' is run." 210 | :group 'fill-column-indicator 211 | :tag "Fill-column rule color" 212 | :type 'color) 213 | 214 | (defcustom fci-rule-width 1 215 | "Width in pixels of the fill-column rule on graphical displays. 216 | Note that a value greater than the default character width is 217 | treated as equivalent to the default character width. 218 | 219 | Changes to this variable do not take effect until the mode 220 | function `fci-mode' is run." 221 | :tag "Fill-Column Rule Width" 222 | :group 'fill-column-indicator 223 | :type '(integer :match (lambda (w val) (fci-posint-p val)))) 224 | 225 | (defcustom fci-rule-image-format 226 | (if (image-type-available-p 'xpm) 'xpm 'pbm) 227 | "Image format used for the fill-column rule on graphical displays. 228 | 229 | Changes to this variable do not take effect until the mode 230 | function `fci-mode' is run." 231 | :tag "Fill-Column Rule Image Format" 232 | :group 'fill-column-indicator 233 | :type '(choice (symbol :tag "XPM" 'xpm) 234 | (symbol :tag "PBM" 'pbm))) 235 | 236 | (defcustom fci-rule-use-dashes nil 237 | "Whether to show the fill-column rule as dashes or as a solid line. 238 | This has no effect on non-graphical displays. 239 | 240 | Changes to this variable do not take effect until the mode 241 | function `fci-mode' is run." 242 | :tag "Fill-Column Rule Use Dashes" 243 | :group 'fill-column-indicator 244 | :type 'boolean) 245 | 246 | (defcustom fci-dash-pattern 0.75 247 | "When using a dashed rule, ratio of dash length to line height. 248 | Values less than 0 or greater than 1 are coerced to the nearest 249 | endpoint of that interval. 250 | 251 | Changes to this variable do not take effect until the mode 252 | function `fci-mode' is run." 253 | :tag "Fill-Column Rule Use Dashes" 254 | :group 'fill-column-indicator 255 | :type 'float) 256 | 257 | (defcustom fci-rule-character ?| 258 | "Character used to draw the fill-column rule on character terminals. 259 | 260 | Changes to this variable do not take effect until the mode 261 | function `fci-mode' is run." 262 | :tag "Fill-Column Rule Character" 263 | :group 'fill-column-indicator 264 | :type 'character) 265 | 266 | (defcustom fci-rule-character-color nil 267 | "Color used to draw the fill-column rule on character terminals. 268 | If nil, the same color is used as for the graphical rule. 269 | 270 | Changes to this variable do not take effect until the mode 271 | function `fci-mode' is run." 272 | :group 'fill-column-indicator 273 | :tag "Fill-column rule color" 274 | :type '(choice (const :tag "Use same color as graphical rule" nil) 275 | (color :tag "Specify a color"))) 276 | 277 | (defcustom fci-always-use-textual-rule nil 278 | "When non-nil, the rule is always drawn using textual characters. 279 | Specifically, fci-mode will use `fci-rule-character' instead of 280 | bitmap images to draw the rule on graphical displays. 281 | 282 | Changes to this variable do not take effect until the mode 283 | function `fci-mode' is run." 284 | :tag "Don't Use Image for Fill-Column Rule" 285 | :group 'fill-column-indicator 286 | :type 'boolean) 287 | 288 | (defcustom fci-handle-truncate-lines t 289 | "Whether fci-mode should set truncate-lines to t while enabled. 290 | If non-nil, fci-mode will set truncate-lines to t in buffers in 291 | which it is enabled, and restore it to its previous value when 292 | disabled. 293 | 294 | Leaving this option set to the default value is recommended." 295 | :group 'fill-column-indicator 296 | :tag "Locally set truncate-lines to t during fci-mode" 297 | :type 'boolean) 298 | 299 | (defcustom fci-handle-line-move-visual t 300 | "Whether fci-mode should set line-move-visual to nil while enabled. 301 | If non-nil, fci-mode will set line-move-visual to nil in buffers 302 | in which it is enabled, and restore t to its previous value when 303 | disabled. 304 | 305 | Leaving this option set to the default value is recommended." 306 | :group 'fill-column-indicator 307 | :tag "Locally set line-move-visual to nil during fci-mode" 308 | :type 'boolean) 309 | 310 | (defcustom fci-eol-char ?\uE000 311 | "Character used for internal purposes by fci-mode. 312 | If you need to use this character, set this variable's value to a 313 | character you do not care about (a good choice is a character 314 | from the Private Use Area of the Unicode BMP, i.e., the range 315 | U+E000-U+F8FF, inclusive)." 316 | :group 'fill-column-indicator 317 | :type 'character) 318 | 319 | (defcustom fci-blank-char ?\uE001 320 | "Character used for internal purposes by fci-mode. 321 | If you need to use this character, set this variable's value to a 322 | character you do not care about (a good choice is a character 323 | from the Private Use Area of the Unicode BMP, i.e., the the range 324 | U+E000-U+F8FF, inclusive)." 325 | :group 'fill-column-indicator 326 | :type 'character) 327 | 328 | ;;; --------------------------------------------------------------------- 329 | ;;; Internal Variables and Constants 330 | ;;; --------------------------------------------------------------------- 331 | 332 | ;; Record prior state of buffer. 333 | (defvar fci-saved-line-move-visual) 334 | (defvar fci-line-move-visual-was-buffer-local) 335 | (defvar fci-saved-truncate-lines) 336 | (defvar fci-saved-eol) 337 | (defvar fci-made-display-table) 338 | 339 | ;; Record state of fci initialization in this buffer. 340 | (defvar fci-display-table-processed) 341 | (defvar fci-local-vars-set) 342 | 343 | ;; Record current state of some quantities, so we can detect changes to them. 344 | (defvar fci-column) 345 | (defvar fci-newline) 346 | (defvar fci-tab-width) 347 | (defvar fci-char-width) 348 | (defvar fci-char-height) 349 | 350 | ;; Data used in setting the fill-column rule that only need to be 351 | ;; occasionally updated in a given buffer. 352 | (defvar fci-limit) 353 | (defvar fci-pre-limit-string) 354 | (defvar fci-at-limit-string) 355 | (defvar fci-post-limit-string) 356 | 357 | ;; The preceding internal variables need to be buffer local and reset when 358 | ;; the mode is disabled. 359 | (defconst fci-internal-vars '(fci-saved-line-move-visual 360 | fci-line-move-visual-was-buffer-local 361 | fci-saved-truncate-lines 362 | fci-saved-eol 363 | fci-made-display-table 364 | fci-display-table-processed 365 | fci-local-vars-set 366 | fci-column 367 | fci-newline 368 | fci-tab-width 369 | fci-char-width 370 | fci-char-height 371 | fci-limit 372 | fci-pre-limit-string 373 | fci-at-limit-string 374 | fci-post-limit-string)) 375 | 376 | (dolist (var fci-internal-vars) 377 | (make-variable-buffer-local var)) 378 | 379 | ;; Hooks we use. 380 | (defconst fci-hook-assignments 381 | '((after-change-functions fci-redraw-region t t) 382 | (before-change-functions fci-extend-rule-for-deletion nil t) 383 | (window-scroll-functions fci-update-window-for-scroll nil t) 384 | (window-configuration-change-hook fci-redraw-frame) 385 | (post-command-hook fci-post-command-check nil t) 386 | (change-major-mode-hook turn-off-fci-mode nil t) 387 | (longlines-mode-hook fci-update-all-windows nil t))) 388 | 389 | ;;; --------------------------------------------------------------------- 390 | ;;; Miscellany 391 | ;;; --------------------------------------------------------------------- 392 | 393 | (defun fci-get-buffer-windows (&optional all-frames) 394 | "Return a list of windows displaying the current buffer." 395 | (get-buffer-window-list (current-buffer) 'no-minibuf all-frames)) 396 | 397 | (defun fci-posint-p (x) 398 | "Return true if X is an integer greater than zero." 399 | (and (wholenump x) 400 | (/= 0 x))) 401 | 402 | ;;; --------------------------------------------------------------------- 403 | ;;; Mode Definition 404 | ;;; --------------------------------------------------------------------- 405 | 406 | ;;;###autoload 407 | (define-minor-mode fci-mode 408 | "Toggle fci-mode on and off. 409 | Fci-mode indicates the location of the fill column by drawing a 410 | thin line (a `rule') at the fill column. 411 | 412 | With prefix ARG, turn fci-mode on if and only if ARG is positive. 413 | 414 | The following options control the appearance of the fill-column 415 | rule: `fci-rule-column', `fci-rule-width', `fci-rule-color', 416 | `fci-rule-use-dashes', `fci-dash-pattern', `fci-rule-character', 417 | and `fci-rule-character-color'. For further options, see the 418 | Customization menu or the package file. (See the latter for tips 419 | on troubleshooting.)" 420 | 421 | nil nil nil 422 | 423 | (if fci-mode 424 | ;; Enabling. 425 | (condition-case error 426 | (progn 427 | (fci-check-user-options) 428 | (fci-process-display-table) 429 | (fci-set-local-vars) 430 | (fci-get-frame-dimens) 431 | (dolist (hook fci-hook-assignments) 432 | (apply 'add-hook hook)) 433 | (setq fci-column (or fci-rule-column fill-column) 434 | fci-tab-width tab-width 435 | fci-limit (if fci-newline 436 | (1+ (- fci-column (length fci-saved-eol))) 437 | fci-column)) 438 | (fci-make-overlay-strings) 439 | (fci-update-all-windows t)) 440 | (error 441 | (fci-mode 0) 442 | (signal (car error) (cdr error)))) 443 | ;; Disabling. 444 | (fci-restore-display-table) 445 | (fci-restore-local-vars) 446 | (dolist (hook fci-hook-assignments) 447 | (remove-hook (car hook) (nth 1 hook) (nth 3 hook))) 448 | (fci-delete-overlays-buffer) 449 | (dolist (var fci-internal-vars) 450 | (set var nil)))) 451 | 452 | ;;;###autoload 453 | (defun turn-on-fci-mode () 454 | "Turn on fci-mode unconditionally." 455 | (interactive) 456 | (fci-mode 1)) 457 | 458 | (defun turn-off-fci-mode () 459 | "Turn off fci-mode unconditionally." 460 | (interactive) 461 | (fci-mode 0)) 462 | 463 | ;;; --------------------------------------------------------------------- 464 | ;;; Display Property Specs 465 | ;;; --------------------------------------------------------------------- 466 | 467 | (defun fci-overlay-fills-background-p (olay) 468 | "Return true if OLAY specifies a background color." 469 | (let ((olay-face (overlay-get olay 'face))) 470 | (when olay-face 471 | (if (facep olay-face) 472 | (not (eq (face-attribute olay-face :background nil t) 'unspecified)) 473 | (if (consp olay-face) 474 | (if (listp (cdr olay-face)) 475 | (if (facep (car olay-face)) 476 | (not (memq t (mapcar #'(lambda (f) (eq (face-attribute f :background nil t) 'unspecified)) 477 | olay-face))) 478 | (plist-member olay-face :background)) 479 | (eq (car olay-face) 'background-color))))))) 480 | 481 | (defun fci-competing-overlay-p (posn) 482 | "Return true if there is an overlay at POSN that fills the background." 483 | (memq t (mapcar #'fci-overlay-fills-background-p (overlays-at posn)))) 484 | 485 | ;; The display spec used in overlay before strings to pad out the rule to the 486 | ;; fill-column. 487 | (defconst fci-padding-display 488 | '((when (not (fci-competing-overlay-p buffer-position)) 489 | . (space :align-to fci-column)) 490 | (space :width 0))) 491 | 492 | ;; Generate the display spec for the rule. Basic idea is to use a "cascading 493 | ;; display property" to display the textual rule if the display doesn't 494 | ;; support images and the graphical rule if it does, but in either case only 495 | ;; display a rule if no other overlay wants to fill the background at the 496 | ;; relevant buffer position. 497 | (defun fci-rule-display (blank rule-img rule-str for-pre-string) 498 | "Generate a display specification for a fill-column rule overlay string." 499 | (let* ((cursor-prop (if (and (not for-pre-string) (not fci-newline)) t)) 500 | (propertized-rule-str (propertize rule-str 'cursor cursor-prop)) 501 | (display-prop (if rule-img 502 | `((when (not (or (display-images-p) 503 | (fci-competing-overlay-p buffer-position))) 504 | . ,propertized-rule-str) 505 | (when (not (fci-competing-overlay-p buffer-position)) 506 | . ,rule-img) 507 | (space :width 0)) 508 | `((when (not (fci-competing-overlay-p buffer-position)) 509 | . ,propertized-rule-str) 510 | (space :width 0))))) 511 | (propertize blank 'cursor cursor-prop 'display display-prop))) 512 | 513 | ;;; --------------------------------------------------------------------- 514 | ;;; Enabling 515 | ;;; --------------------------------------------------------------------- 516 | 517 | (defun fci-check-user-options () 518 | "Check that all user options for fci-mode have valid values." 519 | (unless (memq fci-rule-image-format '(xpm pbm)) 520 | (error "Unrecognized value of `fci-rule-image-format'")) 521 | ;; If the third element of a binding form is t, then nil is an acceptable 522 | ;; value for the variable; otherwise, the variable value must satisfy the 523 | ;; given predicate. 524 | (let ((checks '((fci-rule-color color-defined-p) 525 | (fci-rule-column fci-posint-p t) 526 | (fci-rule-width fci-posint-p t) 527 | (fci-rule-character-color color-defined-p t) 528 | (fci-rule-character characterp) 529 | (fci-blank-char characterp) 530 | (fci-dash-pattern floatp) 531 | (fci-eol-char characterp)))) 532 | (dolist (check checks) 533 | (let ((value (symbol-value (nth 0 check))) 534 | (pred (nth 1 check)) 535 | (nil-is-ok (nth 2 check))) 536 | (unless (or (and nil-is-ok (null value)) 537 | (funcall pred value)) 538 | (signal 'wrong-type-argument (list pred value))))))) 539 | 540 | (defun fci-process-display-table () 541 | "Set up a buffer-local display table for fci-mode." 542 | (unless fci-display-table-processed 543 | (unless buffer-display-table 544 | (setq buffer-display-table (make-display-table) 545 | fci-made-display-table t)) 546 | (aset buffer-display-table fci-blank-char [32]) 547 | (setq fci-saved-eol (aref buffer-display-table 10)) 548 | ;; Assumption: the display-table entry for character 10 is either nil or 549 | ;; a vector whose last element is the newline glyph. 550 | (let ((glyphs (butlast (append fci-saved-eol nil))) 551 | eol) 552 | (if glyphs 553 | (setq fci-newline [10] 554 | eol (vconcat glyphs)) 555 | (setq fci-newline nil 556 | eol [32])) 557 | (aset buffer-display-table 10 fci-newline) 558 | (aset buffer-display-table fci-eol-char eol)) 559 | (setq fci-display-table-processed t))) 560 | 561 | (defun fci-set-local-vars () 562 | "Set miscellaneous local variables when fci-mode is enabled." 563 | (unless fci-local-vars-set 564 | (when (and fci-handle-line-move-visual 565 | (boundp 'line-move-visual)) 566 | (if (local-variable-p 'line-move-visual) 567 | (setq fci-line-move-visual-was-buffer-local t 568 | fci-saved-line-move-visual line-move-visual 569 | line-move-visual nil) 570 | (set (make-local-variable 'line-move-visual) nil))) 571 | (when fci-handle-truncate-lines 572 | (setq fci-saved-truncate-lines truncate-lines 573 | truncate-lines t)) 574 | (setq fci-local-vars-set t))) 575 | 576 | (defun fci-make-rule-string () 577 | "Return a string for drawing the fill-column rule." 578 | (let ((color (or fci-rule-character-color 579 | fci-rule-color))) 580 | ;; Make sure we don't inherit weight or slant from font-lock. 581 | (propertize (char-to-string fci-rule-character) 582 | 'face `(:foreground ,color :weight normal :slant normal)))) 583 | 584 | (defun fci-make-img-descriptor () 585 | "Make an image descriptor for the fill-column rule." 586 | (unless (or (= 0 fci-char-width) 587 | fci-always-use-textual-rule) 588 | ;; No point passing width, height, color etc. directly to the image 589 | ;; functions: those variables have either global or buffer-local 590 | ;; scope, so the image-generating functions can access them directly. 591 | (if (eq fci-rule-image-format 'xpm) 592 | (fci-make-xpm-img) 593 | (fci-make-pbm-img)))) 594 | 595 | (defun fci-get-frame-dimens () 596 | "Determine the frame character height and width. 597 | 598 | If the selected frame cannot display images, use the character 599 | height and width of the first graphic frame in the frame list 600 | displaying the current buffer. (This fallback behavior is just a 601 | rough heuristic.)" 602 | (let ((frame (catch 'found-graphic 603 | (if (display-images-p) 604 | (selected-frame) 605 | (dolist (win (fci-get-buffer-windows t)) 606 | (when (display-images-p (window-frame win)) 607 | (throw 'found-graphic (window-frame win)))))))) 608 | (setq fci-char-width (frame-char-width frame) 609 | fci-char-height (frame-char-height frame)))) 610 | 611 | (defmacro fci-with-rule-parameters (&rest body) 612 | "Define various quantites used in generating rule image descriptors." 613 | (declare (indent defun)) 614 | `(let* ((height-str (number-to-string fci-char-height)) 615 | (width-str (number-to-string fci-char-width)) 616 | (rule-width (min fci-rule-width fci-char-width)) 617 | (hmargin (/ (- fci-char-width rule-width) 2.0)) 618 | (left-margin (floor hmargin)) 619 | (right-margin (ceiling hmargin)) 620 | (segment-ratio (if fci-rule-use-dashes fci-dash-pattern 1)) 621 | (segment-ratio-coerced (min 1 (max 0 segment-ratio))) 622 | (segment-length (round (* segment-ratio-coerced fci-char-height))) 623 | (vmargin (/ (- fci-char-height segment-length) 2.0)) 624 | (top-margin (floor vmargin)) 625 | (bottom-margin (ceiling vmargin))) 626 | ,@body)) 627 | 628 | (defun fci-mapconcat (sep &rest lists) 629 | "Concatenate the strings in LISTS, using SEP as separator." 630 | (mapconcat #'identity (apply 'nconc lists) sep)) 631 | 632 | (defun fci-make-pbm-img () 633 | "Return an image descriptor for the fill-column rule in PBM format." 634 | (fci-with-rule-parameters 635 | (let* ((magic-number "P1\n") 636 | (dimens (concat width-str " " height-str "\n")) 637 | (on-pixels (fci-mapconcat " " 638 | (make-list left-margin "0") 639 | (make-list rule-width "1") 640 | (make-list right-margin "0"))) 641 | (off-pixels (fci-mapconcat " " (make-list fci-char-width "0"))) 642 | (raster (fci-mapconcat "\n" 643 | (make-list top-margin off-pixels) 644 | (make-list segment-length on-pixels) 645 | (make-list bottom-margin off-pixels))) 646 | (data (concat magic-number dimens raster))) 647 | `(image :type pbm 648 | :data ,data 649 | :mask heuristic 650 | :foreground ,fci-rule-color 651 | :ascent center)))) 652 | 653 | (defun fci-make-xpm-img () 654 | "Return an image descriptor for the fill-column rule in XPM format." 655 | (fci-with-rule-parameters 656 | (let* ((identifier "/* XPM */\nstatic char *rule[] = {") 657 | (dimens (concat "\"" width-str " " height-str " 2 1\",")) 658 | (color-spec (concat "\"1 c " fci-rule-color "\",\"0 c None\",")) 659 | (on-pixels (concat "\"" 660 | (make-string left-margin ?0) 661 | (make-string rule-width ?1) 662 | (make-string right-margin ?0) 663 | "\",")) 664 | (off-pixels (concat "\"" (make-string fci-char-width ?0) "\",")) 665 | (raster (fci-mapconcat "" 666 | (make-list top-margin off-pixels) 667 | (make-list segment-length on-pixels) 668 | (make-list bottom-margin off-pixels))) 669 | (end "};") 670 | (data (concat identifier dimens color-spec raster end))) 671 | `(image :type xpm 672 | :data ,data 673 | :mask heuristic 674 | :ascent center)))) 675 | 676 | (defun fci-make-overlay-strings () 677 | "Generate the overlay strings used to display the fill-column rule." 678 | (let* ((str (fci-make-rule-string)) 679 | (img (fci-make-img-descriptor)) 680 | (blank-str (char-to-string fci-blank-char)) 681 | (eol-str (char-to-string fci-eol-char)) 682 | (end-cap (propertize blank-str 'display '(space :width 0))) 683 | (pre-or-post-eol (propertize eol-str 684 | 'cursor t 685 | 'display (propertize eol-str 'cursor t))) 686 | (pre-padding (propertize blank-str 'display fci-padding-display)) 687 | (pre-rule (fci-rule-display blank-str img str t)) 688 | (at-rule (fci-rule-display blank-str img str fci-newline)) 689 | (at-eol (if fci-newline pre-or-post-eol ""))) 690 | (setq fci-pre-limit-string (concat pre-or-post-eol pre-padding pre-rule) 691 | fci-at-limit-string (concat at-eol at-rule) 692 | fci-post-limit-string (concat pre-or-post-eol end-cap)))) 693 | 694 | ;;; --------------------------------------------------------------------- 695 | ;;; Disabling 696 | ;;; --------------------------------------------------------------------- 697 | 698 | (defun fci-restore-local-vars () 699 | "Restore miscellaneous local variables when fci-mode is disabled." 700 | (when fci-local-vars-set 701 | (when (and fci-handle-line-move-visual 702 | (boundp 'line-move-visual)) 703 | (if fci-line-move-visual-was-buffer-local 704 | (setq line-move-visual fci-saved-line-move-visual) 705 | (kill-local-variable 'line-move-visual))) 706 | (when fci-handle-truncate-lines 707 | (setq truncate-lines fci-saved-truncate-lines)))) 708 | 709 | (defun fci-restore-display-table () 710 | "Restore the buffer display table when fci-mode is disabled." 711 | (when (and buffer-display-table 712 | fci-display-table-processed) 713 | (aset buffer-display-table 10 fci-saved-eol) 714 | ;; Don't set buffer-display-table to nil even if we created the display 715 | ;; table; only do so if nothing else has changed it. 716 | (when (and fci-made-display-table 717 | (equal buffer-display-table (make-display-table))) 718 | (setq buffer-display-table nil)))) 719 | 720 | ;;; --------------------------------------------------------------------- 721 | ;;; Drawing and Erasing 722 | ;;; --------------------------------------------------------------------- 723 | 724 | (defun fci-get-overlays-region (start end) 725 | "Return all overlays between START and END displaying the fill-column rule." 726 | (delq nil (mapcar #'(lambda (o) (if (overlay-get o 'fci) o)) 727 | (overlays-in start end)))) 728 | 729 | (defun fci-delete-overlays-region (start end) 730 | "Delete overlays displaying the fill-column rule between START and END." 731 | (mapc #'(lambda (o) (if (overlay-get o 'fci) (delete-overlay o))) 732 | (overlays-in start end))) 733 | 734 | (defun fci-delete-overlays-buffer () 735 | "Delete all overlays displaying the fill-column rule in the current buffer." 736 | (save-restriction 737 | (widen) 738 | (fci-delete-overlays-region (point-min) (point-max)))) 739 | 740 | (defsubst fci-posn-visible-p (posn ranges) 741 | "Return true if POSN falls within an interval in RANGES." 742 | (memq t (mapcar #'(lambda (range) (and (<= (car range) posn) 743 | (< posn (cdr range)))) 744 | ranges))) 745 | 746 | (defsubst fci-get-visible-ranges () 747 | "Return the window start and end for each window on the current buffer." 748 | (mapcar #'(lambda (w) (cons (window-start w) (window-end w 'updated))) 749 | (fci-get-buffer-windows t))) 750 | 751 | (defun fci-delete-unneeded () 752 | "Erase the fill-column rule at buffer positions not visible in any window." 753 | (let ((olays (fci-get-overlays-region (point-min) (point-max))) 754 | (ranges (fci-get-visible-ranges))) 755 | (dolist (o olays) 756 | (unless (fci-posn-visible-p (overlay-start o) ranges) 757 | (delete-overlay o))))) 758 | 759 | ;; It would be slightly faster to run this backwards from END to START, but 760 | ;; only if we maintained the overlay center at an early position in the 761 | ;; buffer. Since other packages that use overlays typically place them while 762 | ;; traversing the buffer in a forward direction, that would be a bad idea. 763 | (defun fci-put-overlays-region (start end) 764 | "Place overlays displaying the fill-column rule between START and END." 765 | (goto-char start) 766 | (let (o cc) 767 | (while (search-forward "\n" end t) 768 | (goto-char (match-beginning 0)) 769 | (setq cc (current-column) 770 | o (make-overlay (match-beginning 0) (match-beginning 0))) 771 | (overlay-put o 'fci t) 772 | (cond 773 | ((< cc fci-limit) 774 | (overlay-put o 'after-string fci-pre-limit-string)) 775 | ((> cc fci-limit) 776 | (overlay-put o 'after-string fci-post-limit-string)) 777 | (t 778 | (overlay-put o 'after-string fci-at-limit-string))) 779 | (goto-char (match-end 0))))) 780 | 781 | (defun fci-redraw-region (start end _ignored) 782 | "Erase and redraw the fill-column rule between START and END." 783 | (save-match-data 784 | (save-excursion 785 | (let ((inhibit-point-motion-hooks t)) 786 | (goto-char end) 787 | (setq end (line-beginning-position 2)) 788 | (fci-delete-overlays-region start end) 789 | (fci-put-overlays-region start end))))) 790 | 791 | (defun fci-redraw-window (win &optional start) 792 | "Redraw the fill-column rule in WIN starting from START." 793 | (fci-redraw-region (or start (window-start win)) (window-end win t) 'ignored)) 794 | 795 | ;; This doesn't determine the strictly minimum amount by which the rule needs 796 | ;; to be extended, but the amount used is always sufficient, and determining 797 | ;; the genuine minimum is more expensive than doing the extra drawing. 798 | (defun fci-extend-rule-for-deletion (start end) 799 | "Extend the fill-column rule after a deletion that spans newlines." 800 | (unless (= start end) 801 | (let ((delenda (fci-get-overlays-region start end))) 802 | (when delenda 803 | (let ((lossage (1+ (length delenda))) 804 | (max-end 0) 805 | win-end) 806 | (mapc #'delete-overlay delenda) 807 | (dolist (win (fci-get-buffer-windows t)) 808 | ;; Do not ask for an updated value of window-end. 809 | (setq win-end (window-end win)) 810 | (when (and (< 0 (- (min win-end end) 811 | (max (window-start win) start))) 812 | (< max-end win-end)) 813 | (setq max-end win-end))) 814 | (unless (= max-end (point-max)) 815 | (fci-redraw-region max-end 816 | (save-excursion 817 | (goto-char max-end) 818 | (line-beginning-position lossage)) 819 | nil))))))) 820 | 821 | (defun fci-update-window-for-scroll (win start) 822 | "Redraw the fill-column rule in WIN after it has been been scrolled." 823 | (fci-delete-unneeded) 824 | (fci-redraw-window win start)) 825 | 826 | (defun fci-update-all-windows (&optional all-frames) 827 | "Redraw the fill-column rule in all windows showing the current buffer." 828 | (dolist (win (fci-get-buffer-windows all-frames)) 829 | (fci-redraw-window win))) 830 | 831 | (defun fci-redraw-frame () 832 | "Redraw the fill-column rule in all windows on the selected frame." 833 | (let* ((wins (window-list (selected-frame) 'no-minibuf)) 834 | (bufs (delete-dups (mapcar #'window-buffer wins)))) 835 | (dolist (buf bufs) 836 | (with-current-buffer buf 837 | (when fci-mode 838 | (fci-delete-unneeded) 839 | (fci-update-all-windows)))))) 840 | 841 | ;;; --------------------------------------------------------------------- 842 | ;;; Workarounds 843 | ;;; --------------------------------------------------------------------- 844 | 845 | ;; This in placed in post-command-hook and does four things: 846 | ;; 1. If the display table has been deleted or something has changed the 847 | ;; display table for newline chars, we regenerate overlay strings after 848 | ;; reprocessing the display table. 849 | ;; 2. If the default char width or height has changed, we regenerate the rule 850 | ;; image. (This handles both font changes and also cases where we 851 | ;; activate the mode while displaying on a char terminal then subsequently 852 | ;; display the buffer on a window frame.) 853 | ;; 3. If the value of `tab-width' or `fill-column' has changed, we reset the 854 | ;; rule. 855 | ;; 4. Cursor properties are ignored when they're out of sight because of 856 | ;; horizontal scrolling. We detect such situations and force a return 857 | ;; from hscrolling to bring our requested cursor position back into view. 858 | ;; These are all fast tests, so despite the large remit this function 859 | ;; shouldn't noticeably affect editing speed. 860 | (defun fci-post-command-check () 861 | "This function is a gross hack." 862 | (cond 863 | ((not (and buffer-display-table 864 | (equal (aref buffer-display-table 10) fci-newline))) 865 | (setq fci-display-table-processed nil) 866 | (fci-mode 1)) 867 | ((and (< 1 (frame-char-width)) 868 | (not fci-always-use-textual-rule) 869 | (not (and (= (frame-char-width) fci-char-width) 870 | (= (frame-char-height) fci-char-height)))) 871 | (fci-mode 1)) 872 | ((not (and (= (or fci-rule-column fill-column) fci-column) 873 | (= tab-width fci-tab-width))) 874 | (fci-mode 1)) 875 | ((and (< 0 (window-hscroll)) 876 | auto-hscroll-mode 877 | (<= (current-column) (window-hscroll))) 878 | ;; Fix me: Rather than setting hscroll to 0, this should reproduce the 879 | ;; relevant part of the auto-hscrolling algorithm. Most people won't 880 | ;; notice the difference in behavior, though. 881 | (set-window-hscroll (selected-window) 0)))) 882 | 883 | (provide 'fill-column-indicator) 884 | 885 | ;; Local Variables: 886 | ;; indent-tabs-mode: nil 887 | ;; End: 888 | ;;; fill-column-indicator.el ends here 889 | --------------------------------------------------------------------------------