Switch to recommended form of GPLv3 permissions notice.
[emacs.git] / lisp / textmodes / table.el
blob3d0f0f354b96d69d1a904dc77332a138d8a40b37
1 ;;; table.el --- create and edit WYSIWYG text based embedded tables
3 ;; Copyright (C) 2000, 2001, 2002, 2003, 2004,
4 ;; 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
6 ;; Keywords: wp, convenience
7 ;; Author: Takaaki Ota <Takaaki.Ota@am.sony.com>
8 ;; Created: Sat Jul 08 2000 13:28:45 (PST)
9 ;; Revised: Wed Jan 03 2007 13:23:46 (PST)
11 ;; This file is part of GNU Emacs.
13 ;; GNU Emacs is free software: you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation, either version 3 of the License, or
16 ;; (at your option) any later version.
18 ;; GNU Emacs is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;; GNU General Public License for more details.
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
26 ;;; Commentary:
28 ;; -------------
29 ;; Introduction:
30 ;; -------------
32 ;; This package provides text based table creation and editing
33 ;; feature. With this package Emacs is capable of editing tables that
34 ;; are embedded inside a text document, the feature similar to the
35 ;; ones seen in modern WYSIWYG word processors. A table is a
36 ;; rectangular text area consisting from a surrounding frame and
37 ;; content inside the frame. The content is usually subdivided into
38 ;; multiple rectangular cells, see the actual tables used below in
39 ;; this document. Once a table is recognized, editing operation
40 ;; inside a table cell is confined into that specific cell's
41 ;; rectangular area. This means that typing and deleting characters
42 ;; inside a cell do not affect any outside text but introduces
43 ;; appropriate formatting only to the cell contents. If necessary for
44 ;; accommodating added text in the cell, the cell automatically grows
45 ;; vertically and/or horizontally. The package uses no major mode nor
46 ;; minor mode for its implementation because the subject text is
47 ;; localized within a buffer. Therefore the special behaviors inside
48 ;; a table cells are implemented by using keymap text property
49 ;; instead of buffer wide mode-map.
52 ;; -----------
53 ;; Background:
54 ;; -----------
56 ;; Paul Georgief is one of my best friends. He became an Emacs
57 ;; convert after I recommended him trying it several years ago. Now
58 ;; we both are devoted disciples of Emacsism and elisp cult. One day
59 ;; in his Emacs exploration he asked me "Tak, what is a command to
60 ;; edit tables in Emacs?". This question started my journey of this
61 ;; table package development. May the code be with me! In the
62 ;; software world Emacs is probably one of the longest lifetime record
63 ;; holders. Amazingly there have been no direct support for WYSIWYG
64 ;; table editing tasks in Emacs. Many people must have experienced
65 ;; manipulating existing overwrite-mode and picture-mode for this task
66 ;; and only dreamed of having such a lisp package which supports this
67 ;; specific task directly. Certainly, I have been one of them. The
68 ;; most difficult part of dealing with table editing in Emacs probably
69 ;; is how to realize localized rectangular editing effect. Emacs has
70 ;; no rectangular narrowing mechanism. Existing rect package provides
71 ;; basically kill, delete and yank operations of a rectangle, which
72 ;; internally is a mere list of strings. A simple approach for
73 ;; realizing the localized virtual rectangular operation is combining
74 ;; rect package capability with a temporary buffer. Insertion and
75 ;; deletion of a character to a table cell can be trapped by a
76 ;; function that copies the cell rectangle to a temporary buffer then
77 ;; apply the insertion/deletion to the temporary contents. Then it
78 ;; formats the contents by filling the paragraphs in order to fit it
79 ;; into the original rectangular area and finally copy it back to the
80 ;; original buffer. This simplistic approach has to bear with
81 ;; significant performance hit. As cell grows larger the copying
82 ;; rectangle back and forth between the original buffer and the
83 ;; temporary buffer becomes expensive and unbearably slow. It was
84 ;; completely impractical and an obvious failure. An idea has been
85 ;; borrowed from the original Emacs design to overcome this
86 ;; shortcoming. When the terminal screen update was slow and
87 ;; expensive Emacs employed a clever algorithm to reduce actual screen
88 ;; update by removing redundant redrawing operations. Also the actual
89 ;; redrawing was done only when there was enough idling time. This
90 ;; technique significantly improved the previously mentioned
91 ;; undesirable situation. Now the original buffer's rectangle is
92 ;; copied into a cache buffer only once. Any cell editing operation
93 ;; is done only to the cache contents. When there is enough idling
94 ;; time the original buffer's rectangle is updated with the current
95 ;; cache contents. This delayed operation is implemented by using
96 ;; Emacs's timer function. To reduce the visual awkwardness
97 ;; introduced by the delayed effect the cursor location is updated in
98 ;; real-time as a user types while the cell contents remains the same
99 ;; until the next idling time. A key to the success of this approach
100 ;; is how to maintain cache coherency. As a user moves point in and
101 ;; out of a cell the table buffer contents and the cache buffer
102 ;; contents must be synchronized without a mistake. By observing user
103 ;; action carefully this is possible however not easy. Once this
104 ;; mechanism is firmly implemented the rest of table features grew in
105 ;; relatively painless progression. Those users who are familiar with
106 ;; Emacs internals appreciate this table package more. Because it
107 ;; demonstrates how extensible Emacs is by showing something that
108 ;; appears like a magic. It lets you re-discover the potential of
109 ;; Emacs.
112 ;; -------------
113 ;; Entry Points:
114 ;; -------------
116 ;; If this is the first time for you to try this package, go ahead and
117 ;; load the package by M-x `load-file' RET. Specify the package file
118 ;; name "table.el". Then switch to a new test buffer and issue the
119 ;; command M-x `table-insert' RET. It'll ask you number of columns,
120 ;; number of rows, cell width and cell height. Give some small
121 ;; numbers for each of them. Play with the resulted table for a
122 ;; while. If you have menu system find the item "Table" under "Tools"
123 ;; and "Table" in the menu bar when the point is in a table cell.
124 ;; Some of them are pretty intuitive and you can easily guess what
125 ;; they do. M-x `describe-function' and get the documentation of
126 ;; `table-insert'. The document includes a short tutorial. When you
127 ;; are tired of guessing how it works come back to this document
128 ;; again.
130 ;; To use the package regularly place this file in the site library
131 ;; directory and add the next expression in your .emacs file. Make
132 ;; sure that directory is included in the `load-path'.
134 ;; (require 'table)
136 ;; Have the next expression also, if you want always be ready to edit
137 ;; tables inside text files. This mechanism is analogous to
138 ;; fontification in a sense that tables are recognized at editing time
139 ;; without having table information saved along with the text itself.
141 ;; (add-hook 'text-mode-hook 'table-recognize)
143 ;; Following is a table of entry points and brief description of each
144 ;; of them. The tables below are of course generated and edited by
145 ;; using this package. Not all the commands are bound to keys. Many
146 ;; of them must be invoked by "M-x" (`execute-extended-command')
147 ;; command. Refer to the section "Keymap" below for the commands
148 ;; available from keys.
150 ;; +------------------------------------------------------------------+
151 ;; | User Visible Entry Points |
152 ;; +-------------------------------+----------------------------------+
153 ;; | Function | Description |
154 ;; +-------------------------------+----------------------------------+
155 ;; |`table-insert' |Insert a table consisting of grid |
156 ;; | |of cells by specifying the number |
157 ;; | |of COLUMNS, number of ROWS, cell |
158 ;; | |WIDTH and cell HEIGHT. |
159 ;; +-------------------------------+----------------------------------+
160 ;; |`table-insert-row' |Insert row(s) of cells before the |
161 ;; | |current row that matches the |
162 ;; | |current row structure. |
163 ;; +-------------------------------+----------------------------------+
164 ;; |`table-insert-column' |Insert column(s) of cells before |
165 ;; | |the current column that matches |
166 ;; | |the current column structure. |
167 ;; +-------------------------------+----------------------------------+
168 ;; |`table-delete-row' |Delete row(s) of cells. The row |
169 ;; | |must consist from cells of the |
170 ;; | |same height. |
171 ;; +-------------------------------+----------------------------------+
172 ;; |`table-delete-column' |Delete column(s) of cells. The |
173 ;; | |column must consist from cells of |
174 ;; | |the same width. |
175 ;; +-------------------------------+----------------------------------+
176 ;; |`table-recognize' |Recognize all tables in the |
177 ;; |`table-unrecognize' |current buffer and |
178 ;; | |activate/inactivate them. |
179 ;; +-------------------------------+----------------------------------+
180 ;; |`table-recognize-region' |Recognize all the cells in a |
181 ;; |`table-unrecognize-region' |region and activate/inactivate |
182 ;; | |them. |
183 ;; +-------------------------------+----------------------------------+
184 ;; |`table-recognize-table' |Recognize all the cells in a |
185 ;; |`table-unrecognize-table' |single table and |
186 ;; | |activate/inactivate them. |
187 ;; +-------------------------------+----------------------------------+
188 ;; |`table-recognize-cell' |Recognize a cell. Find a cell |
189 ;; |`table-unrecognize-cell' |which contains the current point |
190 ;; | |and activate/inactivate that cell.|
191 ;; +-------------------------------+----------------------------------+
192 ;; |`table-forward-cell' |Move point to the next Nth cell in|
193 ;; | |a table. |
194 ;; +-------------------------------+----------------------------------+
195 ;; |`table-backward-cell' |Move point to the previous Nth |
196 ;; | |cell in a table. |
197 ;; +-------------------------------+----------------------------------+
198 ;; |`table-span-cell' |Span the current cell toward the |
199 ;; | |specified direction and merge it |
200 ;; | |with the adjacent cell. The |
201 ;; | |direction is right, left, above or|
202 ;; | |below. |
203 ;; +-------------------------------+----------------------------------+
204 ;; |`table-split-cell-vertically' |Split the current cell vertically |
205 ;; | |and create a cell above and a cell|
206 ;; | |below the point location. |
207 ;; +-------------------------------+----------------------------------+
208 ;; |`table-split-cell-horizontally'|Split the current cell |
209 ;; | |horizontally and create a cell on |
210 ;; | |the left and a cell on the right |
211 ;; | |of the point location. |
212 ;; +-------------------------------+----------------------------------+
213 ;; |`table-split-cell' |Split the current cell vertically |
214 ;; | |or horizontally. This is a |
215 ;; | |wrapper command to the other two |
216 ;; | |orientation specific commands. |
217 ;; +-------------------------------+----------------------------------+
218 ;; |`table-heighten-cell' |Heighten the current cell. |
219 ;; +-------------------------------+----------------------------------+
220 ;; |`table-shorten-cell' |Shorten the current cell. |
221 ;; +-------------------------------+----------------------------------+
222 ;; |`table-widen-cell' |Widen the current cell. |
223 ;; +-------------------------------+----------------------------------+
224 ;; |`table-narrow-cell' |Narrow the current cell. |
225 ;; +-------------------------------+----------------------------------+
226 ;; |`table-fixed-width-mode' |Toggle fixed width mode. In the |
227 ;; | |fixed width mode, typing inside a |
228 ;; | |cell never changes the cell width,|
229 ;; | |while in the normal mode the cell |
230 ;; | |width expands automatically in |
231 ;; | |order to prevent a word being |
232 ;; | |folded into multiple lines. Fixed|
233 ;; | |width mode reverses video or |
234 ;; | |underline the cell contents for |
235 ;; | |its indication. |
236 ;; +-------------------------------+----------------------------------+
237 ;; |`table-query-dimension' |Compute and report the current |
238 ;; | |cell dimension, current table |
239 ;; | |dimension and the number of |
240 ;; | |columns and rows in the table. |
241 ;; +-------------------------------+----------------------------------+
242 ;; |`table-generate-source' |Generate the source of the current|
243 ;; | |table in the specified language |
244 ;; | |and insert it into a specified |
245 ;; | |buffer. |
246 ;; +-------------------------------+----------------------------------+
247 ;; |`table-insert-sequence' |Travel cells forward while |
248 ;; | |inserting a specified sequence |
249 ;; | |string into each cell. |
250 ;; +-------------------------------+----------------------------------+
251 ;; |`table-capture' |Convert plain text into a table by|
252 ;; | |capturing the text in the region. |
253 ;; +-------------------------------+----------------------------------+
254 ;; |`table-release' |Convert a table into plain text by|
255 ;; | |removing the frame from a table. |
256 ;; +-------------------------------+----------------------------------+
257 ;; |`table-justify' |Justify the contents of cell(s). |
258 ;; +-------------------------------+----------------------------------+
261 ;; *Note*
263 ;; You may find that some of commonly expected table commands are
264 ;; missing such as copying a row/column and yanking it. Those
265 ;; functions can be obtained through existing Emacs text editing
266 ;; commands. Rows are easily manipulated with region commands and
267 ;; columns can be copied and pasted through rectangle commands. After
268 ;; all a table is still a part of text in the buffer. Only the
269 ;; special behaviors exist inside each cell through text properties.
271 ;; `table-generate-html' which appeared in earlier releases is
272 ;; deprecated in favor of `table-generate-source'. Now HTML is
273 ;; treated as one of the languages used for describing the table's
274 ;; logical structure.
277 ;; -------
278 ;; Keymap:
279 ;; -------
281 ;; Although this package does not use a mode it does use its own
282 ;; keymap inside a table cell by way of keymap text property. Some of
283 ;; the standard basic editing commands bound to certain keys are
284 ;; replaced with the table specific version of corresponding commands.
285 ;; This replacement combination is listed in the constant alist
286 ;; `table-command-remap-alist' declared below. This alist is
287 ;; not meant to be user configurable but mentioned here for your
288 ;; better understanding of using this package. In addition, table
289 ;; cells have some table specific bindings for cell navigation and
290 ;; cell reformation. You can find these additional bindings in the
291 ;; constant `table-cell-bindings'. Those key bound functions are
292 ;; considered as internal functions instead of normal commands,
293 ;; therefore they have special prefix, *table-- instead of table-, for
294 ;; symbols. The purpose of this is to make it easier for a user to
295 ;; use command name completion. There is a "normal hooks" variable
296 ;; `table-cell-map-hook' prepared for users to override the default
297 ;; table cell bindings. Following is the table of predefined default
298 ;; key bound commands inside a table cell. Remember these bindings
299 ;; exist only inside a table cell. When your terminal is a tty, the
300 ;; control modifier may not be available or applicable for those
301 ;; special characters. In this case use "C-cC-c", which is
302 ;; customizable via `table-command-prefix', as the prefix key
303 ;; sequence. This should preceding the following special character
304 ;; without the control modifier. For example, use "C-cC-c|" instead
305 ;; of "C-|".
307 ;; +------------------------------------------------------------------+
308 ;; | Default Bindings in a Table Cell |
309 ;; +-------+----------------------------------------------------------+
310 ;; | Key | Function |
311 ;; +-------+----------------------------------------------------------+
312 ;; | TAB |Move point forward to the beginning of the next cell. |
313 ;; +-------+----------------------------------------------------------+
314 ;; | "C->" |Widen the current cell. |
315 ;; +-------+----------------------------------------------------------+
316 ;; | "C-<" |Narrow the current cell. |
317 ;; +-------+----------------------------------------------------------+
318 ;; | "C-}" |Heighten the current cell. |
319 ;; +-------+----------------------------------------------------------+
320 ;; | "C-{" |Shorten the current cell. |
321 ;; +-------+----------------------------------------------------------+
322 ;; | "C--" |Split current cell vertically. (one above and one below) |
323 ;; +-------+----------------------------------------------------------+
324 ;; | "C-|" |Split current cell horizontally. (one left and one right) |
325 ;; +-------+----------------------------------------------------------+
326 ;; | "C-*" |Span current cell into adjacent one. |
327 ;; +-------+----------------------------------------------------------+
328 ;; | "C-+" |Insert row(s)/column(s). |
329 ;; +-------+----------------------------------------------------------+
330 ;; | "C-!" |Toggle between normal mode and fixed width mode. |
331 ;; +-------+----------------------------------------------------------+
332 ;; | "C-#" |Report cell and table dimension. |
333 ;; +-------+----------------------------------------------------------+
334 ;; | "C-^" |Generate the source in a language from the current table. |
335 ;; +-------+----------------------------------------------------------+
336 ;; | "C-:" |Justify the contents of cell(s). |
337 ;; +-------+----------------------------------------------------------+
339 ;; *Note*
341 ;; When using `table-cell-map-hook' do not use `local-set-key'.
343 ;; (add-hook 'table-cell-map-hook
344 ;; (function (lambda ()
345 ;; (local-set-key [<key sequence>] '<function>))))
347 ;; Above code is well known ~/.emacs idiom for customizing a mode
348 ;; specific keymap however it does not work for this package. This is
349 ;; because there is no table mode in effect. This package does not
350 ;; use a local map therefor you must modify `table-cell-map'
351 ;; explicitly. The correct way of achieving above task is:
353 ;; (add-hook 'table-cell-map-hook
354 ;; (function (lambda ()
355 ;; (define-key table-cell-map [<key sequence>] '<function>))))
357 ;; -----
358 ;; Menu:
359 ;; -----
361 ;; If a menu system is available a group of table specific menu items,
362 ;; "Table" under "Tools" section of the menu bar, is globally added
363 ;; after this package is loaded. The commands in this group are
364 ;; limited to the ones that are related to creation and initialization
365 ;; of tables, such as to insert a table, to insert rows and columns,
366 ;; or recognize and unrecognize tables. Once tables are created and
367 ;; point is placed inside of a table cell a table specific menu item
368 ;; "Table" appears directly on the menu bar. The commands in this
369 ;; menu give full control on table manipulation that include cell
370 ;; navigation, insertion, splitting, spanning, shrinking, expansion
371 ;; and unrecognizing. In addition to above two types of menu there is
372 ;; a pop-up menu available within a table cell. The content of pop-up
373 ;; menu is identical to the full table menu. [mouse-3] is the default
374 ;; button, defined in `table-cell-bindings', to bring up the pop-up
375 ;; menu. It can be reconfigured via `table-cell-map-hook'. The
376 ;; benefit of a pop-up menu is that it combines selection of the
377 ;; location (which cell, where in the cell) and selection of the
378 ;; desired operation into a single clicking action.
381 ;; -------------------------------
382 ;; Definition of tables and cells:
383 ;; -------------------------------
385 ;; There is no artificial-intelligence magic in this package. The
386 ;; definition of a table and the cells inside the table is reasonably
387 ;; limited in order to achieve acceptable performance in the
388 ;; interactive operation under Emacs lisp implementation. A valid
389 ;; table is a rectangular text area completely filled with valid
390 ;; cells. A valid cell is a rectangle text area, which four borders
391 ;; consist of valid border characters. Cells can not be nested one to
392 ;; another or overlapped to each other except sharing the border
393 ;; lines. A valid character of a cell's vertical border is either
394 ;; table-cell-vertical-char `|' or table-cell-intersection-char `+'.
395 ;; A valid character of a cell's horizontal border is either
396 ;; one of table-cell-horizontal-chars (`-' or `=')
397 ;; or table-cell-intersection-char `+'.
398 ;; A valid character of the four corners of a cell must be
399 ;; table-cell-intersection-char `+'. A cell must contain at least one
400 ;; character space inside. There is no restriction about the contents
401 ;; of a table cell, however it is advised if possible to avoid using
402 ;; any of the border characters inside a table cell. Normally a few
403 ;; boarder characters inside a table cell are harmless. But it is
404 ;; possible that they accidentally align up to emulate a bogus cell
405 ;; corner on which software relies on for cell recognition. When this
406 ;; happens the software may be fooled by it and fail to determine
407 ;; correct cell dimension.
409 ;; Following are the examples of valid tables.
411 ;; +--+----+---+ +-+ +--+-----+
412 ;; | | | | | | | | |
413 ;; +--+----+---+ +-+ | +--+--+
414 ;; | | | | | | | |
415 ;; +--+----+---+ +--+--+ |
416 ;; | | |
417 ;; +-----+--+
419 ;; The next five tables are the examples of invalid tables. (From
420 ;; left to right, 1. nested cells 2. overlapped cells and a
421 ;; non-rectangle cell 3. non-rectangle table 4. zero width/height
422 ;; cells 5. zero sized cell)
424 ;; +-----+ +-----+ +--+ +-++--+ ++
425 ;; | | | | | | | || | ++
426 ;; | +-+ | | | | | | || |
427 ;; | | | | +--+ | +--+--+ +-++--+
428 ;; | +-+ | | | | | | | +-++--+
429 ;; | | | | | | | | | || |
430 ;; +-----+ +--+--+ +--+--+ +-++--+
432 ;; Although the program may recognizes some of these invalid tables,
433 ;; results from the subsequent editing operations inside those cells
434 ;; are not predictable and will most likely start destroying the table
435 ;; structures.
437 ;; It is strongly recommended to have at least one blank line above
438 ;; and below a table. For a table to coexist peacefully with
439 ;; surrounding environment table needs to be separated from unrelated
440 ;; text. This is necessary for the left table to grow or shrink
441 ;; horizontally without breaking the right table in the following
442 ;; example.
444 ;; +-----+-----+-----+
445 ;; +-----+-----+ | | | |
446 ;; | | | +-----+-----+-----+
447 ;; +-----+-----+ | | | |
448 ;; +-----+-----+-----+
451 ;; -------------------------
452 ;; Cell contents formatting:
453 ;; -------------------------
455 ;; The cell contents are formatted by filling a paragraph immediately
456 ;; after characters are inserted into or deleted from a cell. Because
457 ;; of this, cell contents always remain fit inside a cell neatly. One
458 ;; drawback of this is that users do not have full control over
459 ;; spacing between words and line breaking. Only one space can be
460 ;; entered between words and up to two spaces between sentences. For
461 ;; a newline to be effective the new line must form a beginning of
462 ;; paragraph, otherwise it'll automatically be merged with the
463 ;; previous line in a same paragraph. To form a new paragraph the
464 ;; line must start with some space characters or immediately follow a
465 ;; blank line. Here is a typical example of how to list items within
466 ;; a cell. Without a space at the beginning of each line the items
467 ;; can not stand on their own.
469 ;; +---------------------------------+
470 ;; |Each one of the following three |
471 ;; |items starts with a space |
472 ;; |character thus forms a paragraph |
473 ;; |of its own. Limitations in cell |
474 ;; |contents formatting are: |
475 ;; | |
476 ;; | 1. Only one space between words.|
477 ;; | 2. Up to two spaces between |
478 ;; |sentences. |
479 ;; | 3. A paragraph must start with |
480 ;; |spaces or follow a blank line. |
481 ;; | |
482 ;; |This paragraph stays away from |
483 ;; |the item 3 because there is a |
484 ;; |blank line between them. |
485 ;; +---------------------------------+
487 ;; In the normal operation table cell width grows automatically when
488 ;; certain word has to be folded into the next line if the width had
489 ;; not been increased. This normal operation is useful and
490 ;; appropriate for most of the time, however, it is sometimes useful
491 ;; or necessary to fix the width of table and width of table cells.
492 ;; For this purpose the package provides fixed width mode. You can
493 ;; toggle between fixed width mode and normal mode by "C-!".
495 ;; Here is a simple example of the fixed width mode. Suppose we have
496 ;; a table like this one.
498 ;; +-----+
499 ;; | |
500 ;; +-----+
502 ;; In normal mode if you type a word "antidisestablishmentarianism" it
503 ;; grows the cell horizontally like this.
505 ;; +----------------------------+
506 ;; |antidisestablishmentarianism|
507 ;; +----------------------------+
509 ;; In the fixed width mode the same action produces the following
510 ;; result. The folded locations are indicated by a continuation
511 ;; character (`\' is the default). The continuation character is
512 ;; treated specially so it is recommended to choose a character that
513 ;; does not appear elsewhere in table cells. This character is
514 ;; configurable via customization and is kept in the variable
515 ;; `table-word-continuation-char'. The continuation character is
516 ;; treated specially only in the fixed width mode and has no special
517 ;; meaning in the normal mode however.
519 ;; +-----+
520 ;; |anti\|
521 ;; |dise\|
522 ;; |stab\|
523 ;; |lish\|
524 ;; |ment\|
525 ;; |aria\|
526 ;; |nism |
527 ;; +-----+
530 ;; -------------------
531 ;; Cell Justification:
532 ;; -------------------
534 ;; By default the cell contents are filled with left justification and
535 ;; no vertical justification. A paragraph can be justified
536 ;; individually but only horizontally. Paragraph justification is for
537 ;; appearance only and does not change any structural information
538 ;; while cell justification affects table's structural information.
539 ;; For cell justification a user can select horizontal justification
540 ;; and vertical justification independently. Horizontal justification
541 ;; must be one of the three 'left, 'center or 'right. Vertical
542 ;; justification can be 'top, 'middle, 'bottom or 'none. When a cell
543 ;; is justified, that information is recorded as a part of text
544 ;; property therefore the information is persistent as long as the
545 ;; cell remains within the Emacs world. Even copying tables by region
546 ;; and rectangle manipulation commands preserve this information.
547 ;; However, once the table text is saved as a file and the buffer is
548 ;; killed the justification information vanishes permanently. To
549 ;; alleviate this shortcoming without forcing users to save and
550 ;; maintain a separate attribute file, the table code detects
551 ;; justification of each cell when recognizing a table. This
552 ;; detection is done by guessing the justification by looking at the
553 ;; appearance of the cell contents. Since it is a guessing work it
554 ;; does not guarantee the perfectness but it is designed to be
555 ;; practically good enough. The guessing algorithm is implemented in
556 ;; the function `table--detect-cell-alignment'. If you have better
557 ;; algorithm or idea any suggestion is welcome.
560 ;; -----
561 ;; Todo: (in the order of priority, some are just possibility)
562 ;; -----
564 ;; Fix compatibilities with other input method than quail
565 ;; Resolve conflict with flyspell
566 ;; Use mouse for resizing cells
567 ;; A mechanism to link cells internally
568 ;; Consider the use of variable width font under Emacs 21
569 ;; Consider the use of `:box' face attribute under Emacs 21
570 ;; Consider the use of `modification-hooks' text property instead of
571 ;; rebinding the keymap
572 ;; Maybe provide complete XEmacs support in the future however the
573 ;; "extent" is the single largest obstacle lying ahead, read the
574 ;; document in Emacs info.
575 ;; (eval '(progn (require 'info) (Info-find-node "elisp" "Not Intervals")))
578 ;; ---------------
579 ;; Acknowledgment:
580 ;; ---------------
582 ;; Table would not have been possible without the help and
583 ;; encouragement of the following spirited contributors.
585 ;; Paul Georgief <georgief@igpp.ucsd.edu> has been the best tester
586 ;; of the code as well as the constructive criticizer.
588 ;; Gerd Moellmann <gerd@gnu.org> gave me useful suggestions from Emacs
589 ;; 21 point of view.
591 ;; Richard Stallman <rms@gnu.org> showed the initial interest in this
592 ;; attempt of implementing the table feature to Emacs. This greatly
593 ;; motivated me to follow through to its completion.
595 ;; Kenichi Handa <handa@etl.go.jp> kindly guided me through to
596 ;; overcome many technical issues while I was struggling with quail
597 ;; related internationalization problems.
599 ;; Christoph Conrad <christoph.conrad@gmx.de> suggested making symbol
600 ;; names consistent as well as fixing several bugs.
602 ;; Paul Lew <paullew@cisco.com> suggested implementing fixed width
603 ;; mode as well as multi column width (row height) input interface.
605 ;; Michael Smith <smith@xml-doc.org> a well-informed DocBook user
606 ;; asked for CALS table source generation and helped me following
607 ;; through the work by offering valuable suggestions and testing out
608 ;; the code. Jorge Godoy <godoy@conectiva.com> has also suggested
609 ;; supporting for DocBook tables.
611 ;; And many other individuals who reported bugs and suggestions.
613 ;;; Code:
616 (require 'regexp-opt)
618 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
620 ;;; Compatibility:
623 ;; hush up the byte-compiler
624 (defvar quail-translating)
625 (defvar quail-converting)
626 (defvar flyspell-mode)
627 (defvar real-last-command)
628 (defvar delete-selection-mode)
629 ;; This is evil!!
630 ;; (eval-when-compile
631 ;; (unless (fboundp 'set-face-property)
632 ;; (defun set-face-property (face prop value)))
633 ;; (unless (fboundp 'unibyte-char-to-multibyte)
634 ;; (defun unibyte-char-to-multibyte (char)))
635 ;; (defun table--point-in-cell-p (&optional location)))
637 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
639 ;;; Customization:
642 (defgroup table nil
643 "Text based table manipulation utilities.
644 See `table-insert' for examples about how to use."
645 :tag "Table"
646 :prefix "table-"
647 :group 'editing
648 :group 'wp
649 :group 'paragraphs
650 :group 'fill
651 :version "22.1")
653 (defgroup table-hooks nil
654 "Hooks for table manipulation utilities."
655 :group 'table)
657 (defcustom table-time-before-update 0.2
658 "*Time in seconds before updating the cell contents after typing.
659 Updating the cell contents on the screen takes place only after this
660 specified amount of time has passed after the last modification to the
661 cell contents. When the contents of a table cell changes repetitively
662 and frequently the updating the cell contents on the screen is
663 deferred until at least this specified amount of quiet time passes. A
664 smaller number wastes more computation resource by unnecessarily
665 frequent screen update. A large number presents noticeable and
666 annoying delay before the typed result start appearing on the screen."
667 :tag "Time Before Cell Update"
668 :type 'number
669 :group 'table)
671 (defcustom table-time-before-reformat 0.2
672 "*Time in seconds before reformatting the table.
673 This many seconds must pass in addition to `table-time-before-update'
674 before the table is updated with newly widened width or heightened
675 height."
676 :tag "Time Before Cell Reformat"
677 :type 'number
678 :group 'table)
680 (defcustom table-command-prefix [(control c) (control c)]
681 "*Key sequence to be used as prefix for table command key bindings."
682 :type '(vector (repeat :inline t sexp))
683 :tag "Table Command Prefix"
684 :group 'table)
686 (defface table-cell
687 '((((min-colors 88) (class color))
688 (:foreground "gray90" :background "blue1"))
689 (((class color))
690 (:foreground "gray90" :background "blue"))
691 (t (:bold t)))
692 "*Face used for table cell contents."
693 :tag "Cell Face"
694 :group 'table)
696 (defcustom table-cell-horizontal-chars "-="
697 "*Characters that may be used for table cell's horizontal border line."
698 :tag "Cell Horizontal Boundary Characters"
699 :type 'string
700 :group 'table)
702 (defcustom table-cell-vertical-char ?\|
703 "*Character that forms table cell's vertical border line."
704 :tag "Cell Vertical Boundary Character"
705 :type 'character
706 :group 'table)
708 (defcustom table-cell-intersection-char ?\+
709 "*Character that forms table cell's corner."
710 :tag "Cell Intersection Character"
711 :type 'character
712 :group 'table)
714 (defcustom table-word-continuation-char ?\\
715 "*Character that indicates word continuation into the next line.
716 This character has a special meaning only in the fixed width mode,
717 that is when `table-fixed-width-mode' is non-nil . In the fixed width
718 mode this character indicates that the location is continuing into the
719 next line. Be careful about the choice of this character. It is
720 treated substantially different manner than ordinary characters. Try
721 select a character that is unlikely to appear in your document."
722 :tag "Cell Word Continuation Character"
723 :type 'character
724 :group 'table)
726 (defun table-set-table-fixed-width-mode (variable value)
727 (if (fboundp variable)
728 (funcall variable (if value 1 -1))))
730 (defun table-initialize-table-fixed-width-mode (variable value)
731 (set variable value))
733 (defcustom table-fixed-width-mode nil
734 "*Cell width is fixed when this is non-nil.
735 Normally it should be nil for allowing automatic cell width expansion
736 that widens a cell when it is necessary. When non-nil, typing in a
737 cell does not automatically expand the cell width. A word that is too
738 long to fit in a cell is chopped into multiple lines. The chopped
739 location is indicated by `table-word-continuation-char'. This
740 variable's value can be toggled by \\[table-fixed-width-mode] at
741 run-time."
742 :tag "Fix Cell Width"
743 :type 'boolean
744 :initialize 'table-initialize-table-fixed-width-mode
745 :set 'table-set-table-fixed-width-mode
746 :group 'table)
748 (defcustom table-detect-cell-alignment t
749 "*Detect cell contents alignment automatically.
750 When non-nil cell alignment is automatically determined by the
751 appearance of the current cell contents when recognizing tables as a
752 whole. This applies to `table-recognize', `table-recognize-region'
753 and `table-recognize-table' but not to `table-recognize-cell'."
754 :tag "Detect Cell Alignment"
755 :type 'boolean
756 :group 'table)
758 (defcustom table-dest-buffer-name "table"
759 "*Default buffer name (without a suffix) for source generation."
760 :tag "Source Buffer Name"
761 :type 'string
762 :group 'table)
764 (defcustom table-html-delegate-spacing-to-user-agent nil
765 "*Non-nil delegates cell contents spacing entirely to user agent.
766 Otherwise, when nil, it preserves the original spacing and line breaks."
767 :tag "HTML delegate spacing"
768 :type 'boolean
769 :group 'table)
771 (defcustom table-html-th-rows 0
772 "*Number of top rows to become header cells automatically in HTML generation."
773 :tag "HTML Header Rows"
774 :type 'integer
775 :group 'table)
777 (defcustom table-html-th-columns 0
778 "*Number of left columns to become header cells automatically in HTML generation."
779 :tag "HTML Header Columns"
780 :type 'integer
781 :group 'table)
783 (defcustom table-html-table-attribute "border=\"1\""
784 "*Table attribute that applies to the table in HTML generation."
785 :tag "HTML table attribute"
786 :type 'string
787 :group 'table)
789 (defcustom table-html-cell-attribute ""
790 "*Cell attribute that applies to all cells in HTML generation.
791 Do not specify \"align\" and \"valign\" because they are determined by
792 the cell contents dynamically."
793 :tag "HTML cell attribute"
794 :type 'string
795 :group 'table)
797 (defcustom table-cals-thead-rows 1
798 "*Number of top rows to become header rows in CALS table."
799 :tag "CALS Header Rows"
800 :type 'integer
801 :group 'table)
803 ;;;###autoload
804 (defcustom table-cell-map-hook nil
805 "*Normal hooks run when finishing construction of `table-cell-map'.
806 User can modify `table-cell-map' by adding custom functions here."
807 :tag "Cell Keymap Hooks"
808 :type 'hook
809 :group 'table-hooks)
811 (defcustom table-disable-incompatibility-warning nil
812 "*Disable compatibility warning notice.
813 When nil user is reminded of known incompatible issues."
814 :tag "Disable Incompatibility Warning"
815 :type 'boolean
816 :group 'table)
818 (defcustom table-abort-recognition-when-input-pending t
819 "*Abort current recognition process when input pending.
820 Abort current recognition process when we are not sure that no input
821 is available. When non-nil lengthy recognition process is aborted
822 simply by any key input."
823 :tag "Abort Recognition When Input Pending"
824 :type 'boolean
825 :group 'table)
827 ;;;###autoload
828 (defcustom table-load-hook nil
829 "*List of functions to be called after the table is first loaded."
830 :type 'hook
831 :group 'table-hooks)
833 ;;;###autoload
834 (defcustom table-point-entered-cell-hook nil
835 "*List of functions to be called after point entered a table cell."
836 :type 'hook
837 :group 'table-hooks)
839 ;;;###autoload
840 (defcustom table-point-left-cell-hook nil
841 "*List of functions to be called after point left a table cell."
842 :type 'hook
843 :group 'table-hooks)
845 (defvar table-yank-handler '(nil nil t nil)
846 "Yank handler for tables.")
848 (setplist 'table-disable-incompatibility-warning nil)
850 (defvar table-disable-menu (null (and (locate-library "easymenu")
851 (require 'easymenu)
852 (fboundp 'easy-menu-add-item)))
853 "*When non-nil, use of menu by table package is disabled.
854 It must be set before loading this package `table.el' for the first
855 time.")
858 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
860 ;;; Implementation:
863 ;;; Internal variables and constants
864 ;;; No need of user configuration
866 (defconst table-paragraph-start "[ \t\n\f]"
867 "*Regexp for beginning of a line that starts OR separates paragraphs.")
868 (defconst table-cache-buffer-name " *table cell cache*"
869 "Cell cache buffer name.")
870 (defvar table-cell-info-lu-coordinate nil
871 "Zero based coordinate of the cached cell's left upper corner.")
872 (defvar table-cell-info-rb-coordinate nil
873 "Zero based coordinate of the cached cell's right bottom corner.")
874 (defvar table-cell-info-width nil
875 "Number of characters per cached cell width.")
876 (defvar table-cell-info-height nil
877 "Number of lines per cached cell height.")
878 (defvar table-cell-info-justify nil
879 "Justification information of the cached cell.")
880 (defvar table-cell-info-valign nil
881 "Vertical alignment information of the cached cell.")
882 (defvar table-cell-self-insert-command-count 0
883 "Counter for undo control.")
884 (defvar table-cell-map nil
885 "Keymap for table cell contents.")
886 (defvar table-cell-global-map-alist nil
887 "Alist of copy of global maps that are substituted in `table-cell-map'.")
888 (defvar table-global-menu-map nil
889 "Menu map created via `easy-menu-define'.")
890 (defvar table-cell-menu-map nil
891 "Menu map created via `easy-menu-define'.")
892 (defvar table-cell-buffer nil
893 "Buffer that contains the table cell.")
894 (defvar table-cell-cache-point-coordinate nil
895 "Cache point coordinate based from the cell origin.")
896 (defvar table-cell-cache-mark-coordinate nil
897 "Cache mark coordinate based from the cell origin.")
898 (defvar table-cell-entered-state nil
899 "Records the state whether currently in a cell or nor.")
900 (defvar table-update-timer nil
901 "Timer id for deferred cell update.")
902 (defvar table-widen-timer nil
903 "Timer id for deferred cell update.")
904 (defvar table-heighten-timer nil
905 "Timer id for deferred cell update.")
906 (defvar table-inhibit-update nil
907 "Non-nil inhibits implicit cell and cache updates.
908 It inhibits `table-with-cache-buffer' to update data in both direction, cell to cache and cache to cell.")
909 (defvar table-inhibit-auto-fill-paragraph nil
910 "Non-nil inhibits auto fill paragraph when `table-with-cache-buffer' exits.
911 This is always set to nil at the entry to `table-with-cache-buffer' before executing body forms.")
912 (defvar table-mode-indicator nil
913 "For mode line indicator")
914 ;; This is not a real minor-mode but placed in the minor-mode-alist
915 ;; so that we can show the indicator on the mode line handy.
916 (make-variable-buffer-local 'table-mode-indicator)
917 (unless (assq table-mode-indicator minor-mode-alist)
918 (push '(table-mode-indicator (table-fixed-width-mode " Fixed-Table" " Table"))
919 minor-mode-alist))
921 (defconst table-source-languages '(html latex cals)
922 "Supported source languages.")
923 (defvar table-source-info-plist nil
924 "General storage for temporary information used while generating source.")
926 ;;; The following history containers not only keep the history of user
927 ;;; entries but also serve as the default value providers. When an
928 ;;; interactive command is invoked it offers a user the latest entry
929 ;;; of the history as a default selection. Therefore the values below
930 ;;; are the first default value when a command is invoked for the very
931 ;;; first time when there is no real history existing yet.
932 (defvar table-cell-span-direction-history '("right"))
933 (defvar table-cell-split-orientation-history '("horizontally"))
934 (defvar table-cell-split-contents-to-history '("split"))
935 (defvar table-insert-row-column-history '("row"))
936 (defvar table-justify-history '("center"))
937 (defvar table-columns-history '("3"))
938 (defvar table-rows-history '("3"))
939 (defvar table-cell-width-history '("5"))
940 (defvar table-cell-height-history '("1"))
941 (defvar table-source-caption-history '("Table"))
942 (defvar table-sequence-string-history '("0"))
943 (defvar table-sequence-count-history '("0"))
944 (defvar table-sequence-increment-history '("1"))
945 (defvar table-sequence-interval-history '("1"))
946 (defvar table-sequence-justify-history '("left"))
947 (defvar table-source-language-history '("html"))
948 (defvar table-col-delim-regexp-history '(""))
949 (defvar table-row-delim-regexp-history '(""))
950 (defvar table-capture-justify-history '("left"))
951 (defvar table-capture-min-cell-width-history '("5"))
952 (defvar table-capture-columns-history '(""))
953 (defvar table-target-history '("cell"))
955 ;;; Some entries in `table-cell-bindings' are duplicated in
956 ;;; `table-command-remap-alist'. There is a good reason for
957 ;;; this. Common key like return key may be taken by some other
958 ;;; function than normal `newline' function. Thus binding return key
959 ;;; directly for `*table--cell-newline' ensures that the correct enter
960 ;;; operation in a table cell. However
961 ;;; `table-command-remap-alist' has an additional role than
962 ;;; replacing commands. It is also used to construct a table command
963 ;;; list. This list is very important because it is used to check if
964 ;;; the previous command was one of them in this list or not. If the
965 ;;; previous command is found in the list the current command will not
966 ;;; refill the table cache. If the command were not listed fast
967 ;;; typing can cause unwanted cache refill.
968 (defconst table-cell-bindings
969 '(([(control i)] . table-forward-cell)
970 ([(control I)] . table-backward-cell)
971 ([tab] . table-forward-cell)
972 ([(shift backtab)] . table-backward-cell) ; for HPUX console keyboard
973 ([(shift iso-lefttab)] . table-backward-cell) ; shift-tab on a microsoft natural keyboard and redhat linux
974 ([(shift tab)] . table-backward-cell)
975 ([return] . *table--cell-newline)
976 ([(control m)] . *table--cell-newline)
977 ([(control j)] . *table--cell-newline-and-indent)
978 ([mouse-3] . *table--present-cell-popup-menu)
979 ([(control ?>)] . table-widen-cell)
980 ([(control ?<)] . table-narrow-cell)
981 ([(control ?})] . table-heighten-cell)
982 ([(control ?{)] . table-shorten-cell)
983 ([(control ?-)] . table-split-cell-vertically)
984 ([(control ?|)] . table-split-cell-horizontally)
985 ([(control ?*)] . table-span-cell)
986 ([(control ?+)] . table-insert-row-column)
987 ([(control ?!)] . table-fixed-width-mode)
988 ([(control ?#)] . table-query-dimension)
989 ([(control ?^)] . table-generate-source)
990 ([(control ?:)] . table-justify)
992 "Bindings for table cell commands.")
994 (defvar table-command-remap-alist
995 '((self-insert-command . *table--cell-self-insert-command)
996 (completion-separator-self-insert-autofilling . *table--cell-self-insert-command)
997 (completion-separator-self-insert-command . *table--cell-self-insert-command)
998 (delete-char . *table--cell-delete-char)
999 (delete-backward-char . *table--cell-delete-backward-char)
1000 (backward-delete-char . *table--cell-delete-backward-char)
1001 (backward-delete-char-untabify . *table--cell-delete-backward-char)
1002 (newline . *table--cell-newline)
1003 (newline-and-indent . *table--cell-newline-and-indent)
1004 (open-line . *table--cell-open-line)
1005 (quoted-insert . *table--cell-quoted-insert)
1006 (describe-mode . *table--cell-describe-mode)
1007 (describe-bindings . *table--cell-describe-bindings)
1008 (dabbrev-expand . *table--cell-dabbrev-expand)
1009 (dabbrev-completion . *table--cell-dabbrev-completion))
1010 "List of cons cells consisting of (ORIGINAL-COMMAND . TABLE-VERSION-OF-THE-COMMAND).")
1012 (defvar table-command-list nil
1013 "List of commands that override original commands.")
1014 ;; construct the real contents of the `table-command-list'
1015 (let ((remap-alist table-command-remap-alist))
1016 (setq table-command-list nil)
1017 (while remap-alist
1018 (setq table-command-list (cons (cdar remap-alist) table-command-list))
1019 (setq remap-alist (cdr remap-alist))))
1021 (defconst table-global-menu
1022 '("Table"
1023 ("Insert"
1024 ["a Table..." table-insert
1025 :active (and (not buffer-read-only) (not (table--probe-cell)))
1026 :help "Insert a text based table at point"]
1027 ["Row" table-insert-row
1028 :active (table--row-column-insertion-point-p)
1029 :help "Insert row(s) of cells in table"]
1030 ["Column" table-insert-column
1031 :active (table--row-column-insertion-point-p 'column)
1032 :help "Insert column(s) of cells in table"])
1033 "----"
1034 ("Recognize"
1035 ["in Buffer" table-recognize
1036 :active t
1037 :help "Recognize all tables in the current buffer"]
1038 ["in Region" table-recognize-region
1039 :active (and mark-active (not (eq (mark t) (point))))
1040 :help "Recognize all tables in the current region"]
1041 ["a Table" table-recognize-table
1042 :active (table--probe-cell)
1043 :help "Recognize a table at point"]
1044 ["a Cell" table-recognize-cell
1045 :active (let ((cell (table--probe-cell)))
1046 (and cell (null (table--at-cell-p (car cell)))))
1047 :help "Recognize a cell at point"])
1048 ("Unrecognize"
1049 ["in Buffer" table-unrecognize
1050 :active t
1051 :help "Unrecognize all tables in the current buffer"]
1052 ["in Region" table-unrecognize-region
1053 :active (and mark-active (not (eq (mark t) (point))))
1054 :help "Unrecognize all tables in the current region"]
1055 ["a Table" table-unrecognize-table
1056 :active (table--probe-cell)
1057 :help "Unrecognize the current table"]
1058 ["a Cell" table-unrecognize-cell
1059 :active (let ((cell (table--probe-cell)))
1060 (and cell (table--at-cell-p (car cell))))
1061 :help "Unrecognize the current cell"])
1062 "----"
1063 ["Capture Region" table-capture
1064 :active (and (not buffer-read-only) mark-active (not (eq (mark t) (point))) (not (table--probe-cell)))
1065 :help "Capture text in the current region as a table"]
1066 ["Release" table-release
1067 :active (table--editable-cell-p)
1068 :help "Release the current table as plain text"]))
1070 (defconst table-cell-menu
1071 '("Table"
1072 ("Insert"
1073 ["Row" table-insert-row
1074 :active (table--row-column-insertion-point-p)
1075 :help "Insert row(s) of cells in table"]
1076 ["Column" table-insert-column
1077 :active (table--row-column-insertion-point-p 'column)
1078 :help "Insert column(s) of cells in table"])
1079 ("Delete"
1080 ["Row" table-delete-row
1081 :active (table--editable-cell-p)
1082 :help "Delete row(s) of cells in table"]
1083 ["Column" table-delete-column
1084 :active (table--editable-cell-p)
1085 :help "Delete column(s) of cells in table"])
1086 "----"
1087 ("Split a Cell"
1088 ["Horizontally" table-split-cell-horizontally
1089 :active (table--cell-can-split-horizontally-p)
1090 :help "Split the current cell horizontally at point"]
1091 ["Vertically" table-split-cell-vertically
1092 :active (table--cell-can-split-vertically-p)
1093 :help "Split the current cell vertical at point"])
1094 ("Span a Cell to"
1095 ["Right" (table-span-cell 'right)
1096 :active (table--cell-can-span-p 'right)
1097 :help "Span the current cell into the right cell"]
1098 ["Left" (table-span-cell 'left)
1099 :active (table--cell-can-span-p 'left)
1100 :help "Span the current cell into the left cell"]
1101 ["Above" (table-span-cell 'above)
1102 :active (table--cell-can-span-p 'above)
1103 :help "Span the current cell into the cell above"]
1104 ["Below" (table-span-cell 'below)
1105 :active (table--cell-can-span-p 'below)
1106 :help "Span the current cell into the cell below"])
1107 "----"
1108 ("Shrink Cells"
1109 ["Horizontally" table-narrow-cell
1110 :active (table--editable-cell-p)
1111 :help "Shrink the current cell horizontally"]
1112 ["Vertically" table-shorten-cell
1113 :active (table--editable-cell-p)
1114 :help "Shrink the current cell vertically"])
1115 ("Expand Cells"
1116 ["Horizontally" table-widen-cell
1117 :active (table--editable-cell-p)
1118 :help "Expand the current cell horizontally"]
1119 ["Vertically" table-heighten-cell
1120 :active (table--editable-cell-p)
1121 :help "Expand the current cell vertically"])
1122 "----"
1123 ("Justify"
1124 ("a Cell"
1125 ["Left" (table-justify-cell 'left)
1126 :active (table--editable-cell-p)
1127 :help "Left justify the contents of the current cell"]
1128 ["Center" (table-justify-cell 'center)
1129 :active (table--editable-cell-p)
1130 :help "Center justify the contents of the current cell"]
1131 ["Right" (table-justify-cell 'right)
1132 :active (table--editable-cell-p)
1133 :help "Right justify the contents of the current cell"]
1134 "----"
1135 ["Top" (table-justify-cell 'top)
1136 :active (table--editable-cell-p)
1137 :help "Top align the contents of the current cell"]
1138 ["Middle" (table-justify-cell 'middle)
1139 :active (table--editable-cell-p)
1140 :help "Middle align the contents of the current cell"]
1141 ["Bottom" (table-justify-cell 'bottom)
1142 :active (table--editable-cell-p)
1143 :help "Bottom align the contents of the current cell"]
1144 ["None" (table-justify-cell 'none)
1145 :active (table--editable-cell-p)
1146 :help "Remove vertical alignment from the current cell"])
1147 ("a Row"
1148 ["Left" (table-justify-row 'left)
1149 :active (table--editable-cell-p)
1150 :help "Left justify the contents of all cells in the current row"]
1151 ["Center" (table-justify-row 'center)
1152 :active (table--editable-cell-p)
1153 :help "Center justify the contents of all cells in the current row"]
1154 ["Right" (table-justify-row 'right)
1155 :active (table--editable-cell-p)
1156 :help "Right justify the contents of all cells in the current row"]
1157 "----"
1158 ["Top" (table-justify-row 'top)
1159 :active (table--editable-cell-p)
1160 :help "Top align the contents of all cells in the current row"]
1161 ["Middle" (table-justify-row 'middle)
1162 :active (table--editable-cell-p)
1163 :help "Middle align the contents of all cells in the current row"]
1164 ["Bottom" (table-justify-row 'bottom)
1165 :active (table--editable-cell-p)
1166 :help "Bottom align the contents of all cells in the current row"]
1167 ["None" (table-justify-cell 'none)
1168 :active (table--editable-cell-p)
1169 :help "Remove vertical alignment from all cells in the current row"])
1170 ("a Column"
1171 ["Left" (table-justify-column 'left)
1172 :active (table--editable-cell-p)
1173 :help "Left justify the contents of all cells in the current column"]
1174 ["Center" (table-justify-column 'center)
1175 :active (table--editable-cell-p)
1176 :help "Center justify the contents of all cells in the current column"]
1177 ["Right" (table-justify-column 'right)
1178 :active (table--editable-cell-p)
1179 :help "Right justify the contents of all cells in the current column"]
1180 "----"
1181 ["Top" (table-justify-column 'top)
1182 :active (table--editable-cell-p)
1183 :help "Top align the contents of all cells in the current column"]
1184 ["Middle" (table-justify-column 'middle)
1185 :active (table--editable-cell-p)
1186 :help "Middle align the contents of all cells in the current column"]
1187 ["Bottom" (table-justify-column 'bottom)
1188 :active (table--editable-cell-p)
1189 :help "Bottom align the contents of all cells in the current column"]
1190 ["None" (table-justify-cell 'none)
1191 :active (table--editable-cell-p)
1192 :help "Remove vertical alignment from all cells in the current column"])
1193 ("a Paragraph"
1194 ["Left" (table-justify-cell 'left t)
1195 :active (table--editable-cell-p)
1196 :help "Left justify the current paragraph"]
1197 ["Center" (table-justify-cell 'center t)
1198 :active (table--editable-cell-p)
1199 :help "Center justify the current paragraph"]
1200 ["Right" (table-justify-cell 'right t)
1201 :active (table--editable-cell-p)
1202 :help "Right justify the current paragraph"]))
1203 "----"
1204 ["Query Dimension" table-query-dimension
1205 :active (table--probe-cell)
1206 :help "Get the dimension of the current cell and the current table"]
1207 ["Generate Source" table-generate-source
1208 :active (table--probe-cell)
1209 :help "Generate source of the current table in the specified language"]
1210 ["Insert Sequence" table-insert-sequence
1211 :active (table--editable-cell-p)
1212 :help "Travel cells forward while inserting a specified sequence string in each cell"]
1213 ("Unrecognize"
1214 ["a Table" table-unrecognize-table
1215 :active (table--probe-cell)
1216 :help "Unrecognize the current table"]
1217 ["a Cell" table-unrecognize-cell
1218 :active (let ((cell (table--probe-cell)))
1219 (and cell (table--at-cell-p (car cell))))
1220 :help "Unrecognize the current cell"])
1221 ["Release" table-release
1222 :active (table--editable-cell-p)
1223 :help "Release the current table as plain text"]
1224 ("Configure Width to"
1225 ["Auto Expand Mode" (table-fixed-width-mode -1)
1226 :active t
1227 :style radio
1228 :selected (not table-fixed-width-mode)
1229 :help "A mode that allows automatic horizontal cell expansion"]
1230 ["Fixed Width Mode" (table-fixed-width-mode 1)
1231 :active t
1232 :style radio
1233 :selected table-fixed-width-mode
1234 :help "A mode that does not allow automatic horizontal cell expansion"])
1235 ("Navigate"
1236 ["Forward Cell" table-forward-cell
1237 :active (table--probe-cell)
1238 :help "Move point forward by cell(s)"]
1239 ["Backward Cell" table-backward-cell
1240 :active (table--probe-cell)
1241 :help "Move point backward by cell(s)"])
1244 ;; XEmacs causes an error when encountering unknown keywords in the
1245 ;; menu definition. Specifically the :help keyword is new in Emacs 21
1246 ;; and causes error for the XEmacs function `check-menu-syntax'. IMHO
1247 ;; it is unwise to generate an error for unknown keywords because it
1248 ;; kills the nice backward compatible extensibility of keyword use.
1249 ;; Unknown keywords should be quietly ignore so that future extension
1250 ;; does not cause a problem in the old implementation. Sigh...
1251 (when (featurep 'xemacs)
1252 (mapcar
1253 (defun table--tweak-menu-for-xemacs (menu)
1254 (cond
1255 ((listp menu)
1256 (mapcar 'table--tweak-menu-for-xemacs menu))
1257 ((vectorp menu)
1258 (let ((i 0) (len (length menu)))
1259 (while (< i len)
1260 ;; replace :help with something harmless.
1261 (if (eq (aref menu i) :help) (aset menu i :included))
1262 (setq i (1+ i)))))))
1263 (list table-global-menu table-cell-menu))
1264 (defvar mark-active t))
1266 ;; register table menu under global tools menu
1267 (unless table-disable-menu
1268 (easy-menu-define table-global-menu-map nil "Table global menu" table-global-menu)
1269 (if (featurep 'xemacs)
1270 (progn
1271 (easy-menu-add-item nil '("Tools") table-global-menu-map))
1272 (easy-menu-add-item (current-global-map) '("menu-bar" "tools") "--")
1273 (easy-menu-add-item (current-global-map) '("menu-bar" "tools") table-global-menu-map)))
1275 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1277 ;; Macros
1280 (defmacro table-with-cache-buffer (&rest body)
1281 "Execute the forms in BODY with table cache buffer as the current buffer.
1282 This macro simplifies the rest of the work greatly by condensing the
1283 common idiom used in many of the cell manipulation functions. It does
1284 not return any meaningful value.
1286 Save the current buffer and set the cache buffer as the current
1287 buffer. Move the point to the cache buffer coordinate
1288 `table-cell-cache-point-coordinate'. After BODY forms are executed,
1289 the paragraph is filled as long as `table-inhibit-auto-fill-paragraph'
1290 remains nil. BODY can set it to t when it does not want to fill the
1291 paragraph. If necessary the cell width and height are extended as the
1292 consequence of cell content modification by the BODY. Then the
1293 current buffer is restored to the original one. The last cache point
1294 coordinate is stored in `table-cell-cache-point-coordinate'. The
1295 original buffer's point is moved to the location that corresponds to
1296 the last cache point coordinate."
1297 (let ((height-expansion (make-symbol "height-expansion-var-symbol"))
1298 (width-expansion (make-symbol "width-expansion-var-symbol")))
1299 `(let (,height-expansion ,width-expansion)
1300 ;; make sure cache has valid data unless it is explicitly inhibited.
1301 (unless table-inhibit-update
1302 (table-recognize-cell))
1303 (with-current-buffer (get-buffer-create table-cache-buffer-name)
1304 ;; goto the cell coordinate based on `table-cell-cache-point-coordinate'.
1305 (set-mark (table--goto-coordinate table-cell-cache-mark-coordinate))
1306 (table--goto-coordinate table-cell-cache-point-coordinate)
1307 (table--untabify-line)
1308 ;; always reset before executing body forms because auto-fill behavior is the default.
1309 (setq table-inhibit-auto-fill-paragraph nil)
1310 ;; do the body
1311 ,@body
1312 ;; fill paragraph unless the body does not want to by setting `table-inhibit-auto-fill-paragraph'.
1313 (unless table-inhibit-auto-fill-paragraph
1314 (if (and table-cell-info-justify
1315 (not (eq table-cell-info-justify 'left)))
1316 (table--fill-region (point-min) (point-max))
1317 (table--fill-region
1318 (save-excursion (forward-paragraph -1) (point))
1319 (save-excursion (forward-paragraph 1) (point)))))
1320 ;; keep the updated cell coordinate.
1321 (setq table-cell-cache-point-coordinate (table--get-coordinate))
1322 ;; determine the cell width expansion.
1323 (setq ,width-expansion (table--measure-max-width))
1324 (if (<= ,width-expansion table-cell-info-width) nil
1325 (table--fill-region (point-min) (point-max) ,width-expansion)
1326 ;; keep the updated cell coordinate.
1327 (setq table-cell-cache-point-coordinate (table--get-coordinate)))
1328 (setq ,width-expansion (- ,width-expansion table-cell-info-width))
1329 ;; determine the cell height expansion.
1330 (if (looking-at "\\s *\\'") nil
1331 (goto-char (point-min))
1332 (if (re-search-forward "\\(\\s *\\)\\'" nil t)
1333 (goto-char (match-beginning 1))))
1334 (setq ,height-expansion (- (cdr (table--get-coordinate)) (1- table-cell-info-height))))
1335 ;; now back to the table buffer.
1336 ;; expand the cell width in the table buffer if necessary.
1337 (if (> ,width-expansion 0)
1338 (table-widen-cell ,width-expansion 'no-copy 'no-update))
1339 ;; expand the cell height in the table buffer if necessary.
1340 (if (> ,height-expansion 0)
1341 (table-heighten-cell ,height-expansion 'no-copy 'no-update))
1342 ;; do valign
1343 (with-current-buffer (get-buffer-create table-cache-buffer-name)
1344 (table--goto-coordinate table-cell-cache-point-coordinate)
1345 (setq table-cell-cache-point-coordinate (table--valign)))
1346 ;; move the point in the table buffer to the location that corresponds to
1347 ;; the location in the cell cache buffer
1348 (table--goto-coordinate (table--transcoord-cache-to-table table-cell-cache-point-coordinate))
1349 ;; set up the update timer unless it is explicitly inhibited.
1350 (unless table-inhibit-update
1351 (table--update-cell)))))
1353 ;; for debugging the body form of the macro
1354 (put 'table-with-cache-buffer 'edebug-form-spec '(body))
1355 ;; for neat presentation use the same indentation as `progn'
1356 (put 'table-with-cache-buffer 'lisp-indent-function 0)
1357 (if (or (featurep 'xemacs)
1358 (null (fboundp 'font-lock-add-keywords))) nil
1359 ;; color it as a keyword
1360 (font-lock-add-keywords
1361 'emacs-lisp-mode
1362 '("\\<table-with-cache-buffer\\>")))
1364 (defmacro table-put-source-info (prop value)
1365 "Register source generation information."
1366 `(put 'table-source-info-plist ,prop ,value))
1368 (defmacro table-get-source-info (prop)
1369 "Retrieve source generation information."
1370 `(get 'table-source-info-plist ,prop))
1372 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1374 ;; Modified commands for cell operation
1377 ;; Point Motion Only Group
1378 (mapc
1379 (lambda (command)
1380 (let ((func-symbol (intern (format "*table--cell-%s" command)))
1381 (doc-string (format "Table remapped function for `%s'." command)))
1382 (fset func-symbol
1383 `(lambda
1384 (&rest args)
1385 ,doc-string
1386 (interactive)
1387 (let ((table-inhibit-update t)
1388 (deactivate-mark nil))
1389 (table--finish-delayed-tasks)
1390 (table-recognize-cell 'force)
1391 (table-with-cache-buffer
1392 (call-interactively ',command)
1393 (setq table-inhibit-auto-fill-paragraph t)))))
1394 (setq table-command-remap-alist
1395 (cons (cons command func-symbol)
1396 table-command-remap-alist))))
1397 '(move-beginning-of-line
1398 beginning-of-line
1399 move-end-of-line
1400 end-of-line
1401 beginning-of-buffer
1402 end-of-buffer
1403 forward-word
1404 backward-word
1405 forward-sentence
1406 backward-sentence
1407 forward-paragraph
1408 backward-paragraph))
1410 ;; Extraction Group
1411 (mapc
1412 (lambda (command)
1413 (let ((func-symbol (intern (format "*table--cell-%s" command)))
1414 (doc-string (format "Table remapped function for `%s'." command)))
1415 (fset func-symbol
1416 `(lambda
1417 (&rest args)
1418 ,doc-string
1419 (interactive)
1420 (table--finish-delayed-tasks)
1421 (table-recognize-cell 'force)
1422 (table-with-cache-buffer
1423 (table--remove-cell-properties (point-min) (point-max))
1424 (table--remove-eol-spaces (point-min) (point-max))
1425 (call-interactively ',command))
1426 (table--finish-delayed-tasks)))
1427 (setq table-command-remap-alist
1428 (cons (cons command func-symbol)
1429 table-command-remap-alist))))
1430 '(kill-region
1431 kill-ring-save
1432 delete-region
1433 copy-region-as-kill
1434 kill-line
1435 kill-word
1436 backward-kill-word
1437 kill-sentence
1438 backward-kill-sentence
1439 kill-paragraph
1440 backward-kill-paragraph
1441 kill-sexp
1442 backward-kill-sexp))
1444 ;; Pasting Group
1445 (mapc
1446 (lambda (command)
1447 (let ((func-symbol (intern (format "*table--cell-%s" command)))
1448 (doc-string (format "Table remapped function for `%s'." command)))
1449 (fset func-symbol
1450 `(lambda
1451 (&rest args)
1452 ,doc-string
1453 (interactive)
1454 (table--finish-delayed-tasks)
1455 (table-recognize-cell 'force)
1456 (table-with-cache-buffer
1457 (call-interactively ',command)
1458 (table--untabify (point-min) (point-max))
1459 (table--fill-region (point-min) (point-max))
1460 (setq table-inhibit-auto-fill-paragraph t))
1461 (table--finish-delayed-tasks)))
1462 (setq table-command-remap-alist
1463 (cons (cons command func-symbol)
1464 table-command-remap-alist))))
1465 '(yank
1466 clipboard-yank
1467 yank-clipboard-selection
1468 insert))
1470 ;; Formatting Group
1471 (mapc
1472 (lambda (command)
1473 (let ((func-symbol (intern (format "*table--cell-%s" command)))
1474 (doc-string (format "Table remapped function for `%s'." command)))
1475 (fset func-symbol
1476 `(lambda
1477 (&rest args)
1478 ,doc-string
1479 (interactive)
1480 (table--finish-delayed-tasks)
1481 (table-recognize-cell 'force)
1482 (table-with-cache-buffer
1483 (let ((fill-column table-cell-info-width))
1484 (call-interactively ',command))
1485 (setq table-inhibit-auto-fill-paragraph t))
1486 (table--finish-delayed-tasks)))
1487 (setq table-command-remap-alist
1488 (cons (cons command func-symbol)
1489 table-command-remap-alist))))
1490 '(center-line
1491 conter-region
1492 center-paragraph
1493 fill-paragraph))
1495 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1497 ;; Commands
1500 ;;;###autoload
1501 (defun table-insert (columns rows &optional cell-width cell-height)
1502 "Insert an editable text table.
1503 Insert a table of specified number of COLUMNS and ROWS. Optional
1504 parameter CELL-WIDTH and CELL-HEIGHT can specify the size of each
1505 cell. The cell size is uniform across the table if the specified size
1506 is a number. They can be a list of numbers to specify different size
1507 for each cell. When called interactively, the list of number is
1508 entered by simply listing all the numbers with space characters
1509 delimiting them.
1511 Examples:
1513 \\[table-insert] inserts a table at the current point location.
1515 Suppose we have the following situation where `-!-' indicates the
1516 location of point.
1520 Type \\[table-insert] and hit ENTER key. As it asks table
1521 specification, provide 3 for number of columns, 1 for number of rows,
1522 5 for cell width and 1 for cell height. Now you shall see the next
1523 table and the point is automatically moved to the beginning of the
1524 first cell.
1526 +-----+-----+-----+
1527 |-!- | | |
1528 +-----+-----+-----+
1530 Inside a table cell, there are special key bindings. \\<table-cell-map>
1532 M-9 \\[table-widen-cell] (or \\[universal-argument] 9 \\[table-widen-cell]) widens the first cell by 9 character
1533 width, which results as
1535 +--------------+-----+-----+
1536 |-!- | | |
1537 +--------------+-----+-----+
1539 Type TAB \\[table-widen-cell] then type TAB M-2 M-7 \\[table-widen-cell] (or \\[universal-argument] 2 7 \\[table-widen-cell]). Typing
1540 TAB moves the point forward by a cell. The result now looks like this:
1542 +--------------+------+--------------------------------+
1543 | | |-!- |
1544 +--------------+------+--------------------------------+
1546 If you knew each width of the columns prior to the table creation,
1547 what you could have done better was to have had given the complete
1548 width information to `table-insert'.
1550 Cell width(s): 14 6 32
1552 instead of
1554 Cell width(s): 5
1556 This would have eliminated the previously mentioned width adjustment
1557 work all together.
1559 If the point is in the last cell type S-TAB S-TAB to move it to the
1560 first cell. Now type \\[table-heighten-cell] which heighten the row by a line.
1562 +--------------+------+--------------------------------+
1563 |-!- | | |
1564 | | | |
1565 +--------------+------+--------------------------------+
1567 Type \\[table-insert-row-column] and tell it to insert a row.
1569 +--------------+------+--------------------------------+
1570 |-!- | | |
1571 | | | |
1572 +--------------+------+--------------------------------+
1573 | | | |
1574 | | | |
1575 +--------------+------+--------------------------------+
1577 Move the point under the table as shown below.
1579 +--------------+------+--------------------------------+
1580 | | | |
1581 | | | |
1582 +--------------+------+--------------------------------+
1583 | | | |
1584 | | | |
1585 +--------------+------+--------------------------------+
1588 Type M-x table-insert-row instead of \\[table-insert-row-column]. \\[table-insert-row-column] does not work
1589 when the point is outside of the table. This insertion at
1590 outside of the table effectively appends a row at the end.
1592 +--------------+------+--------------------------------+
1593 | | | |
1594 | | | |
1595 +--------------+------+--------------------------------+
1596 | | | |
1597 | | | |
1598 +--------------+------+--------------------------------+
1599 |-!- | | |
1600 | | | |
1601 +--------------+------+--------------------------------+
1603 Text editing inside the table cell produces reasonably expected
1604 results.
1606 +--------------+------+--------------------------------+
1607 | | | |
1608 | | | |
1609 +--------------+------+--------------------------------+
1610 | | |Text editing inside the table |
1611 | | |cell produces reasonably |
1612 | | |expected results.-!- |
1613 +--------------+------+--------------------------------+
1614 | | | |
1615 | | | |
1616 +--------------+------+--------------------------------+
1618 Inside a table cell has a special keymap.
1620 \\{table-cell-map}
1622 (interactive
1623 (progn
1624 (barf-if-buffer-read-only)
1625 (if (table--probe-cell)
1626 (error "Can't insert a table inside a table"))
1627 (mapcar (function table--read-from-minibuffer)
1628 '(("Number of columns" . table-columns-history)
1629 ("Number of rows" . table-rows-history)
1630 ("Cell width(s)" . table-cell-width-history)
1631 ("Cell height(s)" . table-cell-height-history)))))
1632 (table--make-cell-map)
1633 ;; reform the arguments.
1634 (if (null cell-width) (setq cell-width (car table-cell-width-history)))
1635 (if (null cell-height) (setq cell-height (car table-cell-height-history)))
1636 (if (stringp columns) (setq columns (string-to-number columns)))
1637 (if (stringp rows) (setq rows (string-to-number rows)))
1638 (if (stringp cell-width) (setq cell-width (table--string-to-number-list cell-width)))
1639 (if (stringp cell-height) (setq cell-height (table--string-to-number-list cell-height)))
1640 (if (numberp cell-width) (setq cell-width (cons cell-width nil)))
1641 (if (numberp cell-height) (setq cell-height (cons cell-height nil)))
1642 ;; test validity of the arguments.
1643 (mapc (lambda (arg)
1644 (let* ((value (symbol-value arg))
1645 (error-handler
1646 (function (lambda ()
1647 (error "%s must be a positive integer%s" arg
1648 (if (listp value) " or a list of positive integers" ""))))))
1649 (if (null value) (funcall error-handler))
1650 (mapcar (function (lambda (arg1)
1651 (if (or (not (integerp arg1))
1652 (< arg1 1))
1653 (funcall error-handler))))
1654 (if (listp value) value
1655 (cons value nil)))))
1656 '(columns rows cell-width cell-height))
1657 (let ((orig-coord (table--get-coordinate))
1658 (coord (table--get-coordinate))
1659 r i cw ch cell-str border-str)
1660 ;; prefabricate the building blocks border-str and cell-str.
1661 (with-temp-buffer
1662 ;; construct border-str
1663 (insert table-cell-intersection-char)
1664 (setq cw cell-width)
1665 (setq i 0)
1666 (while (< i columns)
1667 (insert (make-string (car cw) (string-to-char table-cell-horizontal-chars)) table-cell-intersection-char)
1668 (if (cdr cw) (setq cw (cdr cw)))
1669 (setq i (1+ i)))
1670 (setq border-str (buffer-substring (point-min) (point-max)))
1671 ;; construct cell-str
1672 (erase-buffer)
1673 (insert table-cell-vertical-char)
1674 (setq cw cell-width)
1675 (setq i 0)
1676 (while (< i columns)
1677 (let ((beg (point)))
1678 (insert (make-string (car cw) ?\s))
1679 (insert table-cell-vertical-char)
1680 (table--put-cell-line-property beg (1- (point))))
1681 (if (cdr cw) (setq cw (cdr cw)))
1682 (setq i (1+ i)))
1683 (setq cell-str (buffer-substring (point-min) (point-max))))
1684 ;; if the construction site has an empty border push that border down.
1685 (save-excursion
1686 (beginning-of-line)
1687 (if (looking-at "\\s *$")
1688 (progn
1689 (setq border-str (concat border-str "\n"))
1690 (setq cell-str (concat cell-str "\n")))))
1691 ;; now build the table using the prefabricated building blocks
1692 (setq r 0)
1693 (setq ch cell-height)
1694 (while (< r rows)
1695 (if (> r 0) nil
1696 (table--goto-coordinate coord) (setcdr coord (1+ (cdr coord)))
1697 (table--untabify-line (point))
1698 (insert border-str))
1699 (setq i 0)
1700 (while (< i (car ch))
1701 (table--goto-coordinate coord) (setcdr coord (1+ (cdr coord)))
1702 (table--untabify-line (point))
1703 (insert cell-str)
1704 (setq i (1+ i)))
1705 (table--goto-coordinate coord) (setcdr coord (1+ (cdr coord)))
1706 (table--untabify-line (point))
1707 (insert border-str)
1708 (if (cdr ch) (setq ch (cdr ch)))
1709 (setq r (1+ r)))
1710 ;; stand by at the first cell
1711 (table--goto-coordinate (table--offset-coordinate orig-coord '(1 . 1)))
1712 (table-recognize-cell 'force)))
1714 ;;;###autoload
1715 (defun table-insert-row (n)
1716 "Insert N table row(s).
1717 When point is in a table the newly inserted row(s) are placed above
1718 the current row. When point is outside of the table it must be below
1719 the table within the table width range, then the newly created row(s)
1720 are appended at the bottom of the table."
1721 (interactive "*p")
1722 (if (< n 0) (setq n 1))
1723 (let* ((current-coordinate (table--get-coordinate))
1724 (coord-list (table--cell-list-to-coord-list (table--horizontal-cell-list t nil 'top)))
1725 (append-row (if coord-list nil (setq coord-list (table--find-row-column))))
1726 (cell-height (cdr (table--min-coord-list coord-list)))
1727 (left-list nil)
1728 (this-list coord-list)
1729 (right-list (cdr coord-list))
1730 (bottom-border-y (1+ (cdr (table--get-coordinate (cdr (table--vertical-cell-list nil t))))))
1731 (vertical-str (string table-cell-vertical-char))
1732 (vertical-str-with-properties (let ((str (string table-cell-vertical-char)))
1733 (table--put-cell-keymap-property 0 (length str) str)
1734 (table--put-cell-rear-nonsticky 0 (length str) str) str))
1735 (first-time t))
1736 ;; create the space below for the table to grow
1737 (table--create-growing-space-below (* n (+ 1 cell-height)) coord-list bottom-border-y)
1738 ;; vertically expand each cell from left to right
1739 (while this-list
1740 (let* ((left (prog1 (car left-list) (setq left-list (if left-list (cdr left-list) coord-list))))
1741 (this (prog1 (car this-list) (setq this-list (cdr this-list))))
1742 (right (prog1 (car right-list) (setq right-list (cdr right-list))))
1743 (exclude-left (and left (< (cdar left) (cdar this))))
1744 (exclude-right (and right (<= (cdar right) (cdar this))))
1745 (beg (table--goto-coordinate
1746 (cons (if exclude-left (caar this) (1- (caar this)))
1747 (cdar this))))
1748 (end (table--goto-coordinate
1749 (cons (if exclude-right (cadr this) (1+ (cadr this)))
1750 bottom-border-y)))
1751 (rect (if append-row nil (extract-rectangle beg end))))
1752 ;; prepend blank cell lines to the extracted rectangle
1753 (let ((i n))
1754 (while (> i 0)
1755 (setq rect (cons
1756 (concat (if exclude-left "" (char-to-string table-cell-intersection-char))
1757 (make-string (- (cadr this) (caar this)) (string-to-char table-cell-horizontal-chars))
1758 (if exclude-right "" (char-to-string table-cell-intersection-char)))
1759 rect))
1760 (let ((j cell-height))
1761 (while (> j 0)
1762 (setq rect (cons
1763 (concat (if exclude-left ""
1764 (if first-time vertical-str vertical-str-with-properties))
1765 (table--cell-blank-str (- (cadr this) (caar this)))
1766 (if exclude-right "" vertical-str-with-properties))
1767 rect))
1768 (setq j (1- j))))
1769 (setq i (1- i))))
1770 (setq first-time nil)
1771 (if append-row
1772 (table--goto-coordinate (cons (if exclude-left (caar this) (1- (caar this)))
1773 (1+ bottom-border-y)))
1774 (delete-rectangle beg end)
1775 (goto-char beg))
1776 (table--insert-rectangle rect)))
1777 ;; fix up the intersections
1778 (setq this-list (if append-row nil coord-list))
1779 (while this-list
1780 (let ((this (prog1 (car this-list) (setq this-list (cdr this-list))))
1781 (i 0))
1782 (while (< i n)
1783 (let ((y (1- (* i (+ 1 cell-height)))))
1784 (table--goto-coordinate (table--offset-coordinate (car this) (cons -1 y)))
1785 (delete-char 1) (insert table-cell-intersection-char)
1786 (table--goto-coordinate (table--offset-coordinate (cons (cadr this) (cdar this)) (cons 0 y)))
1787 (delete-char 1) (insert table-cell-intersection-char)
1788 (setq i (1+ i))))))
1789 ;; move the point to the beginning of the first newly inserted cell.
1790 (if (table--goto-coordinate
1791 (if append-row (cons (car (caar coord-list)) (1+ bottom-border-y))
1792 (caar coord-list))) nil
1793 (table--goto-coordinate current-coordinate))
1794 ;; re-recognize the current cell's new dimension
1795 (table-recognize-cell 'force)))
1797 ;;;###autoload
1798 (defun table-insert-column (n)
1799 "Insert N table column(s).
1800 When point is in a table the newly inserted column(s) are placed left
1801 of the current column. When point is outside of the table it must be
1802 right side of the table within the table height range, then the newly
1803 created column(s) are appended at the right of the table."
1804 (interactive "*p")
1805 (if (< n 0) (setq n 1))
1806 (let* ((current-coordinate (table--get-coordinate))
1807 (coord-list (table--cell-list-to-coord-list (table--vertical-cell-list t nil 'left)))
1808 (append-column (if coord-list nil (setq coord-list (table--find-row-column 'column))))
1809 (cell-width (car (table--min-coord-list coord-list)))
1810 (border-str (table--multiply-string (concat (make-string cell-width (string-to-char table-cell-horizontal-chars))
1811 (char-to-string table-cell-intersection-char)) n))
1812 (cell-str (table--multiply-string (concat (table--cell-blank-str cell-width)
1813 (let ((str (string table-cell-vertical-char)))
1814 (table--put-cell-keymap-property 0 (length str) str)
1815 (table--put-cell-rear-nonsticky 0 (length str) str) str)) n))
1816 (columns-to-extend (* n (+ 1 cell-width)))
1817 (above-list nil)
1818 (this-list coord-list)
1819 (below-list (cdr coord-list))
1820 (right-border-x (car (table--get-coordinate (cdr (table--horizontal-cell-list nil t))))))
1821 ;; push back the affected area above and below this table
1822 (table--horizontally-shift-above-and-below columns-to-extend coord-list)
1823 ;; process each cell vertically from top to bottom
1824 (while this-list
1825 (let* ((above (prog1 (car above-list) (setq above-list (if above-list (cdr above-list) coord-list))))
1826 (this (prog1 (car this-list) (setq this-list (cdr this-list))))
1827 (below (prog1 (car below-list) (setq below-list (cdr below-list))))
1828 (exclude-above (and above (<= (caar above) (caar this))))
1829 (exclude-below (and below (< (caar below) (caar this))))
1830 (beg-coord (cons (if append-column (1+ right-border-x) (caar this))
1831 (if exclude-above (cdar this) (1- (cdar this)))))
1832 (end-coord (cons (1+ right-border-x)
1833 (if exclude-below (cddr this) (1+ (cddr this)))))
1834 rect)
1835 ;; untabify the area right of the bar that is about to be inserted
1836 (let ((coord (table--copy-coordinate beg-coord))
1837 (i 0)
1838 (len (length rect)))
1839 (while (< i len)
1840 (if (table--goto-coordinate coord 'no-extension)
1841 (table--untabify-line (point)))
1842 (setcdr coord (1+ (cdr coord)))
1843 (setq i (1+ i))))
1844 ;; extract and delete the rectangle area including the current
1845 ;; cell and to the right border of the table.
1846 (setq rect (extract-rectangle (table--goto-coordinate beg-coord)
1847 (table--goto-coordinate end-coord)))
1848 (delete-rectangle (table--goto-coordinate beg-coord)
1849 (table--goto-coordinate end-coord))
1850 ;; prepend the empty column string at the beginning of each
1851 ;; rectangle string extracted before.
1852 (let ((rect-str rect)
1853 (first t))
1854 (while rect-str
1855 (if (and first (null exclude-above))
1856 (setcar rect-str (concat border-str (car rect-str)))
1857 (if (and (null (cdr rect-str)) (null exclude-below))
1858 (setcar rect-str (concat border-str (car rect-str)))
1859 (setcar rect-str (concat cell-str (car rect-str)))))
1860 (setq first nil)
1861 (setq rect-str (cdr rect-str))))
1862 ;; insert the extended rectangle
1863 (table--goto-coordinate beg-coord)
1864 (table--insert-rectangle rect)))
1865 ;; fix up the intersections
1866 (setq this-list (if append-column nil coord-list))
1867 (while this-list
1868 (let ((this (prog1 (car this-list) (setq this-list (cdr this-list))))
1869 (i 0))
1870 (while (< i n)
1871 (let ((x (1- (* (1+ i) (+ 1 cell-width)))))
1872 (table--goto-coordinate (table--offset-coordinate (car this) (cons x -1)))
1873 (delete-char 1) (insert table-cell-intersection-char)
1874 (table--goto-coordinate (table--offset-coordinate (cons (caar this) (cddr this)) (cons x 1)))
1875 (delete-char 1) (insert table-cell-intersection-char)
1876 (setq i (1+ i))))))
1877 ;; move the point to the beginning of the first newly inserted cell.
1878 (if (table--goto-coordinate
1879 (if append-column
1880 (cons (1+ right-border-x)
1881 (cdar (car coord-list)))
1882 (caar coord-list))) nil
1883 (table--goto-coordinate current-coordinate))
1884 ;; re-recognize the current cell's new dimension
1885 (table-recognize-cell 'force)))
1887 ;;;###autoload
1888 (defun table-insert-row-column (row-column n)
1889 "Insert row(s) or column(s).
1890 See `table-insert-row' and `table-insert-column'."
1891 (interactive
1892 (let ((n (prefix-numeric-value current-prefix-arg)))
1893 (if (< n 0) (setq n 1))
1894 (list (intern (let ((completion-ignore-case t)
1895 (default (car table-insert-row-column-history)))
1896 (downcase (completing-read
1897 (format "Insert %s row%s/column%s (default %s): "
1898 (if (> n 1) (format "%d" n) "a")
1899 (if (> n 1) "s" "")
1900 (if (> n 1) "s" "")
1901 default)
1902 '(("row") ("column"))
1903 nil t nil 'table-insert-row-column-history default))))
1904 n)))
1905 (cond ((eq row-column 'row)
1906 (table-insert-row n))
1907 ((eq row-column 'column)
1908 (table-insert-column n))))
1910 ;;;###autoload
1911 (defun table-recognize (&optional arg)
1912 "Recognize all tables within the current buffer and activate them.
1913 Scans the entire buffer and recognizes valid table cells. If the
1914 optional numeric prefix argument ARG is negative the tables in the
1915 buffer become inactive, meaning the tables become plain text and loses
1916 all the table specific features."
1917 (interactive "P")
1918 (setq arg (prefix-numeric-value arg))
1919 (let* ((inhibit-read-only t))
1920 (table-recognize-region (point-min) (point-max) -1)
1921 (if (>= arg 0)
1922 (save-excursion
1923 (goto-char (point-min))
1924 (let* ((border (format "[%s%c%c]"
1925 table-cell-horizontal-chars
1926 table-cell-vertical-char
1927 table-cell-intersection-char))
1928 (border3 (concat border border border))
1929 (non-border (format "^[^%s%c%c]*$"
1930 table-cell-horizontal-chars
1931 table-cell-vertical-char
1932 table-cell-intersection-char)))
1933 ;; `table-recognize-region' is an expensive function so minimize
1934 ;; the search area. A minimum table at least consists of three consecutive
1935 ;; table border characters to begin with such as
1936 ;; +-+
1937 ;; |A|
1938 ;; +-+
1939 ;; and any tables end with a line containing no table border characters
1940 ;; or the end of buffer.
1941 (while (and (re-search-forward border3 (point-max) t)
1942 (not (and (input-pending-p)
1943 table-abort-recognition-when-input-pending)))
1944 (message "Recognizing tables...(%d%%)" (/ (* 100 (match-beginning 0)) (- (point-max) (point-min))))
1945 (let ((beg (match-beginning 0))
1946 end)
1947 (if (re-search-forward non-border (point-max) t)
1948 (setq end (match-beginning 0))
1949 (setq end (goto-char (point-max))))
1950 (table-recognize-region beg end arg)))
1951 (message "Recognizing tables...done"))))))
1953 ;;;###autoload
1954 (defun table-unrecognize ()
1955 (interactive)
1956 (table-recognize -1))
1958 ;;;###autoload
1959 (defun table-recognize-region (beg end &optional arg)
1960 "Recognize all tables within region.
1961 BEG and END specify the region to work on. If the optional numeric
1962 prefix argument ARG is negative the tables in the region become
1963 inactive, meaning the tables become plain text and lose all the table
1964 specific features."
1965 (interactive "r\nP")
1966 (setq arg (prefix-numeric-value arg))
1967 (let ((inhibit-read-only t)
1968 (modified-flag (buffer-modified-p)))
1969 (if (< arg 0)
1970 (table--remove-cell-properties beg end)
1971 (save-excursion
1972 (goto-char beg)
1973 (let* ((border (format "[%s%c%c]"
1974 table-cell-horizontal-chars
1975 table-cell-vertical-char
1976 table-cell-intersection-char))
1977 (non-border (format "[^%s%c%c]"
1978 table-cell-horizontal-chars
1979 table-cell-vertical-char
1980 table-cell-intersection-char))
1981 (inhibit-read-only t))
1982 (unwind-protect
1983 (progn
1984 (remove-text-properties beg end '(table-cell nil))
1985 (while (and (< (point) end)
1986 (not (and (input-pending-p)
1987 table-abort-recognition-when-input-pending)))
1988 (cond
1989 ((looking-at "\n")
1990 (forward-char 1))
1991 ((looking-at border)
1992 (if (re-search-forward non-border end t)
1993 (goto-char (match-beginning 0))
1994 (goto-char end)))
1995 ((table--at-cell-p (point))
1996 (goto-char (next-single-property-change (point) 'table-cell nil end)))
1998 (let ((cell (table-recognize-cell 'force 'no-copy)))
1999 (if (and cell table-detect-cell-alignment)
2000 (table--detect-cell-alignment cell)))
2001 (unless (re-search-forward border end t)
2002 (goto-char end))))))))))
2003 (restore-buffer-modified-p modified-flag)))
2005 ;;;###autoload
2006 (defun table-unrecognize-region (beg end)
2007 (interactive "r")
2008 (table-recognize-region beg end -1))
2010 ;;;###autoload
2011 (defun table-recognize-table (&optional arg)
2012 "Recognize a table at point.
2013 If the optional numeric prefix argument ARG is negative the table
2014 becomes inactive, meaning the table becomes plain text and loses all
2015 the table specific features."
2016 (interactive "P")
2017 (setq arg (prefix-numeric-value arg))
2018 (let ((unrecognize (< arg 0))
2019 (origin-cell (table--probe-cell))
2020 (inhibit-read-only t))
2021 (if origin-cell
2022 (save-excursion
2023 (while
2024 (progn
2025 (table-forward-cell 1 nil unrecognize)
2026 (let ((cell (table--probe-cell)))
2027 (if (and cell table-detect-cell-alignment)
2028 (table--detect-cell-alignment cell))
2029 (and cell (not (equal cell origin-cell))))))))))
2031 ;;;###autoload
2032 (defun table-unrecognize-table ()
2033 (interactive)
2034 (table-recognize-table -1))
2036 ;;;###autoload
2037 (defun table-recognize-cell (&optional force no-copy arg)
2038 "Recognize a table cell that contains current point.
2039 Probe the cell dimension and prepare the cell information. The
2040 optional two arguments FORCE and NO-COPY are for internal use only and
2041 must not be specified. When the optional numeric prefix argument ARG
2042 is negative the cell becomes inactive, meaning that the cell becomes
2043 plain text and loses all the table specific features."
2044 (interactive "i\ni\np")
2045 (table--make-cell-map)
2046 (if (or force (not (memq (table--get-last-command) table-command-list)))
2047 (let* ((cell (table--probe-cell (interactive-p)))
2048 (cache-buffer (get-buffer-create table-cache-buffer-name))
2049 (modified-flag (buffer-modified-p))
2050 (inhibit-read-only t))
2051 (unwind-protect
2052 (unless (null cell)
2053 ;; initialize the cell info variables
2054 (let ((lu-coordinate (table--get-coordinate (car cell)))
2055 (rb-coordinate (table--get-coordinate (cdr cell))))
2056 ;; update the previous cell if this cell is different from the previous one.
2057 ;; care only lu but ignore rb since size change does not matter.
2058 (unless (equal table-cell-info-lu-coordinate lu-coordinate)
2059 (table--finish-delayed-tasks))
2060 (setq table-cell-info-lu-coordinate lu-coordinate)
2061 (setq table-cell-info-rb-coordinate rb-coordinate)
2062 (setq table-cell-info-width (- (car table-cell-info-rb-coordinate)
2063 (car table-cell-info-lu-coordinate)))
2064 (setq table-cell-info-height (+ (- (cdr table-cell-info-rb-coordinate)
2065 (cdr table-cell-info-lu-coordinate)) 1))
2066 (setq table-cell-info-justify (table--get-cell-justify-property cell))
2067 (setq table-cell-info-valign (table--get-cell-valign-property cell)))
2068 ;; set/remove table cell properties
2069 (if (< (prefix-numeric-value arg) 0)
2070 (let ((coord (table--get-coordinate (car cell)))
2071 (n table-cell-info-height))
2072 (save-excursion
2073 (while (> n 0)
2074 (table--remove-cell-properties
2075 (table--goto-coordinate coord)
2076 (table--goto-coordinate (cons (+ (car coord) table-cell-info-width 1) (cdr coord))))
2077 (setq n (1- n))
2078 (setcdr coord (1+ (cdr coord))))))
2079 (table--put-cell-property cell))
2080 ;; copy the cell contents to the cache buffer
2081 ;; only if no-copy is nil and timers are not set
2082 (unless no-copy
2083 (setq table-cell-cache-point-coordinate (table--transcoord-table-to-cache))
2084 (setq table-cell-cache-mark-coordinate (table--transcoord-table-to-cache
2085 (table--get-coordinate (marker-position (mark-marker)))))
2086 (setq table-cell-buffer (current-buffer))
2087 (let ((rectangle (extract-rectangle (car cell)
2088 (cdr cell))))
2089 (save-current-buffer
2090 (set-buffer cache-buffer)
2091 (erase-buffer)
2092 (table--insert-rectangle rectangle)))))
2093 (restore-buffer-modified-p modified-flag))
2094 (if (featurep 'xemacs)
2095 (table--warn-incompatibility))
2096 cell)))
2098 ;;;###autoload
2099 (defun table-unrecognize-cell ()
2100 (interactive)
2101 (table-recognize-cell nil nil -1))
2103 ;;;###autoload
2104 (defun table-heighten-cell (n &optional no-copy no-update)
2105 "Heighten the current cell by N lines by expanding the cell vertically.
2106 Heightening is done by adding blank lines at the bottom of the current
2107 cell. Other cells aligned horizontally with the current one are also
2108 heightened in order to keep the rectangular table structure. The
2109 optional argument NO-COPY is internal use only and must not be
2110 specified."
2111 (interactive "*p")
2112 (if (< n 0) (setq n 1))
2113 (let* ((coord-list (table--cell-list-to-coord-list (table--horizontal-cell-list t)))
2114 (left-list nil)
2115 (this-list coord-list)
2116 (right-list (cdr coord-list))
2117 (bottom-border-y (1+ (cdr (table--get-coordinate (cdr (table--vertical-cell-list nil t))))))
2118 (vertical-str (string table-cell-vertical-char))
2119 (vertical-str-with-properties (string table-cell-vertical-char))
2120 (first-time t)
2121 (current-coordinate (table--get-coordinate)))
2122 ;; prepare the right vertical string with appropriate properties put
2123 (table--put-cell-keymap-property 0 (length vertical-str-with-properties) vertical-str-with-properties)
2124 ;; create the space below for the table to grow
2125 (table--create-growing-space-below n coord-list bottom-border-y)
2126 ;; vertically expand each cell from left to right
2127 (while this-list
2128 (let* ((left (prog1 (car left-list) (setq left-list (if left-list (cdr left-list) coord-list))))
2129 (this (prog1 (car this-list) (setq this-list (cdr this-list))))
2130 (right (prog1 (car right-list) (setq right-list (cdr right-list))))
2131 (exclude-left (and left (< (cddr left) (cddr this))))
2132 (exclude-right (and right (<= (cddr right) (cddr this))))
2133 (beg (table--goto-coordinate
2134 (cons (if exclude-left (caar this) (1- (caar this)))
2135 (1+ (cddr this)))))
2136 (end (table--goto-coordinate
2137 (cons (if exclude-right (cadr this) (1+ (cadr this)))
2138 bottom-border-y)))
2139 (rect (extract-rectangle beg end)))
2140 ;; prepend blank cell lines to the extracted rectangle
2141 (let ((i n))
2142 (while (> i 0)
2143 (setq rect (cons
2144 (concat (if exclude-left ""
2145 (if first-time vertical-str vertical-str-with-properties))
2146 (table--cell-blank-str (- (cadr this) (caar this)))
2147 (if exclude-right "" vertical-str-with-properties))
2148 rect))
2149 (setq i (1- i))))
2150 (setq first-time nil)
2151 (delete-rectangle beg end)
2152 (goto-char beg)
2153 (table--insert-rectangle rect)))
2154 (table--goto-coordinate current-coordinate)
2155 ;; re-recognize the current cell's new dimension
2156 (table-recognize-cell 'force no-copy)
2157 (unless no-update
2158 (table--update-cell-heightened))))
2160 ;;;###autoload
2161 (defun table-shorten-cell (n)
2162 "Shorten the current cell by N lines by shrinking the cell vertically.
2163 Shortening is done by removing blank lines from the bottom of the cell
2164 and possibly from the top of the cell as well. Therefor, the cell
2165 must have some bottom/top blank lines to be shorten effectively. This
2166 is applicable to all the cells aligned horizontally with the current
2167 one because they are also shortened in order to keep the rectangular
2168 table structure."
2169 (interactive "*p")
2170 (if (< n 0) (setq n 1))
2171 (table--finish-delayed-tasks)
2172 (let* ((table-inhibit-update t)
2173 (coord-list (table--cell-list-to-coord-list (table--horizontal-cell-list t)))
2174 (left-list nil)
2175 (this-list coord-list)
2176 (right-list (cdr coord-list))
2177 (bottom-budget-list nil)
2178 (bottom-border-y (1+ (cdr (table--get-coordinate (cdr (table--vertical-cell-list nil t))))))
2179 (current-coordinate (table--get-coordinate))
2180 (current-cell-coordinate (table--cell-to-coord (table--probe-cell)))
2181 (blank-line-regexp "\\s *$"))
2182 (message "Shortening...");; this operation may be lengthy
2183 ;; for each cell calculate the maximum number of blank lines we can delete
2184 ;; and adjust the argument n. n is adjusted so that the total number of
2185 ;; blank lines from top and bottom of a cell do not exceed n, all cell has
2186 ;; at least one line height after blank line deletion.
2187 (while this-list
2188 (let ((this (prog1 (car this-list) (setq this-list (cdr this-list)))))
2189 (table--goto-coordinate (car this))
2190 (table-recognize-cell 'force)
2191 (table-with-cache-buffer
2192 (catch 'end-count
2193 (let ((blank-line-count 0))
2194 (table--goto-coordinate (cons 0 (1- table-cell-info-height)))
2195 ;; count bottom
2196 (while (and (looking-at blank-line-regexp)
2197 (setq blank-line-count (1+ blank-line-count))
2198 ;; need to leave at least one blank line
2199 (if (> blank-line-count n) (throw 'end-count nil) t)
2200 (if (zerop (forward-line -1)) t
2201 (setq n (if (zerop blank-line-count) 0
2202 (1- blank-line-count)))
2203 (throw 'end-count nil))))
2204 (table--goto-coordinate (cons 0 0))
2205 ;; count top
2206 (while (and (looking-at blank-line-regexp)
2207 (setq blank-line-count (1+ blank-line-count))
2208 ;; can consume all blank lines
2209 (if (>= blank-line-count n) (throw 'end-count nil) t)
2210 (zerop (forward-line 1))))
2211 (setq n blank-line-count))))))
2212 ;; construct the bottom-budget-list which is a list of numbers where each number
2213 ;; corresponds to how many lines to be deleted from the bottom of each cell. If
2214 ;; this number, say bb, is smaller than n (bb < n) that means the difference (n - bb)
2215 ;; number of lines must be deleted from the top of the cell in addition to deleting
2216 ;; bb lines from the bottom of the cell.
2217 (setq this-list coord-list)
2218 (while this-list
2219 (let ((this (prog1 (car this-list) (setq this-list (cdr this-list)))))
2220 (table--goto-coordinate (car this))
2221 (table-recognize-cell 'force)
2222 (table-with-cache-buffer
2223 (setq bottom-budget-list
2224 (cons
2225 (let ((blank-line-count 0))
2226 (table--goto-coordinate (cons 0 (1- table-cell-info-height)))
2227 (while (and (looking-at blank-line-regexp)
2228 (< blank-line-count n)
2229 (setq blank-line-count (1+ blank-line-count))
2230 (zerop (forward-line -1))))
2231 blank-line-count)
2232 bottom-budget-list)))))
2233 (setq bottom-budget-list (nreverse bottom-budget-list))
2234 ;; vertically shorten each cell from left to right
2235 (setq this-list coord-list)
2236 (while this-list
2237 (let* ((left (prog1 (car left-list) (setq left-list (if left-list (cdr left-list) coord-list))))
2238 (this (prog1 (car this-list) (setq this-list (cdr this-list))))
2239 (right (prog1 (car right-list) (setq right-list (cdr right-list))))
2240 (bottom-budget (prog1 (car bottom-budget-list) (setq bottom-budget-list (cdr bottom-budget-list))))
2241 (exclude-left (and left (< (cddr left) (cddr this))))
2242 (exclude-right (and right (<= (cddr right) (cddr this))))
2243 (beg (table--goto-coordinate (cons (caar this) (cdar this))))
2244 (end (table--goto-coordinate (cons (cadr this) bottom-border-y)))
2245 (rect (extract-rectangle beg end))
2246 (height (+ (- (cddr this) (cdar this)) 1))
2247 (blank-line (make-string (- (cadr this) (caar this)) ?\s)))
2248 ;; delete lines from the bottom of the cell
2249 (setcdr (nthcdr (- height bottom-budget 1) rect) (nthcdr height rect))
2250 ;; delete lines from the top of the cell
2251 (if (> n bottom-budget)
2252 (let ((props (text-properties-at 0 (car rect))))
2253 (setq rect (nthcdr (- n bottom-budget) rect))
2254 (set-text-properties 0 1 props (car rect))))
2255 ;; append blank lines below the table
2256 (setq rect (append rect (make-list n blank-line)))
2257 ;; now swap the area with the prepared rect of the same size
2258 (delete-rectangle beg end)
2259 (goto-char beg)
2260 (table--insert-rectangle rect)
2261 ;; for the left and right borders always delete lines from the bottom of the cell
2262 (unless exclude-left
2263 (let* ((beg (table--goto-coordinate (cons (1- (caar this)) (cdar this))))
2264 (end (table--goto-coordinate (cons (caar this) bottom-border-y)))
2265 (rect (extract-rectangle beg end)))
2266 (setcdr (nthcdr (- height n 1) rect) (nthcdr height rect))
2267 (setq rect (append rect (make-list n " ")))
2268 (delete-rectangle beg end)
2269 (goto-char beg)
2270 (table--insert-rectangle rect)))
2271 (unless exclude-right
2272 (let* ((beg (table--goto-coordinate (cons (cadr this) (cdar this))))
2273 (end (table--goto-coordinate (cons (1+ (cadr this)) bottom-border-y)))
2274 (rect (extract-rectangle beg end)))
2275 (setcdr (nthcdr (- height n 1) rect) (nthcdr height rect))
2276 (setq rect (append rect (make-list n " ")))
2277 (delete-rectangle beg end)
2278 (goto-char beg)
2279 (table--insert-rectangle rect)))
2280 ;; if this is the cell where the original point was in, adjust the point location
2281 (if (null (equal this current-cell-coordinate)) nil
2282 (let ((y (- (cdr current-coordinate) (cdar this))))
2283 (if (< y (- n bottom-budget))
2284 (setcdr current-coordinate (cdar this))
2285 (if (< (- y (- n bottom-budget)) (- height n))
2286 (setcdr current-coordinate (+ (cdar this) (- y (- n bottom-budget))))
2287 (setcdr current-coordinate (+ (cdar this) (- height n 1)))))))))
2288 ;; remove the appended blank lines below the table if they are unnecessary
2289 (table--goto-coordinate (cons 0 (1+ (- bottom-border-y n))))
2290 (table--remove-blank-lines n)
2291 ;; re-recognize the current cell's new dimension
2292 (table--goto-coordinate current-coordinate)
2293 (table-recognize-cell 'force)
2294 (table--update-cell-heightened)
2295 (message "")))
2297 ;;;###autoload
2298 (defun table-widen-cell (n &optional no-copy no-update)
2299 "Widen the current cell by N columns and expand the cell horizontally.
2300 Some other cells in the same table are widen as well to keep the
2301 table's rectangle structure."
2302 (interactive "*p")
2303 (if (< n 0) (setq n 1))
2304 (let* ((coord-list (table--cell-list-to-coord-list (table--vertical-cell-list)))
2305 (below-list nil)
2306 (this-list coord-list)
2307 (above-list (cdr coord-list)))
2308 (save-excursion
2309 ;; push back the affected area above and below this table
2310 (table--horizontally-shift-above-and-below n (reverse coord-list))
2311 ;; now widen vertically for each cell
2312 (while this-list
2313 (let* ((below (prog1 (car below-list) (setq below-list (if below-list (cdr below-list) coord-list))))
2314 (this (prog1 (car this-list) (setq this-list (cdr this-list))))
2315 (above (prog1 (car above-list) (setq above-list (cdr above-list))))
2316 (beg (table--goto-coordinate
2317 (cons (car (cdr this))
2318 (if (or (null above) (<= (car (cdr this)) (car (cdr above))))
2319 (1- (cdr (car this)))
2320 (cdr (car this))))))
2321 (end (table--goto-coordinate
2322 (cons (1+ (car (cdr this)))
2323 (if (or (null below) (< (car (cdr this)) (car (cdr below))))
2324 (1+ (cdr (cdr this)))
2325 (cdr (cdr this))))))
2326 (tmp (extract-rectangle (1- beg) end))
2327 (border (format "[%s%c]\\%c"
2328 table-cell-horizontal-chars
2329 table-cell-intersection-char
2330 table-cell-intersection-char))
2331 (blank (table--cell-blank-str))
2332 rectangle)
2333 ;; create a single wide vertical bar of empty cell fragment
2334 (while tmp
2335 ; (message "tmp is %s" tmp)
2336 (setq rectangle (cons
2337 (if (string-match border (car tmp))
2338 (substring (car tmp) 0 1)
2339 blank)
2340 rectangle))
2341 ; (message "rectangle is %s" rectangle)
2342 (setq tmp (cdr tmp)))
2343 (setq rectangle (nreverse rectangle))
2344 ;; untabify the area right of the bar that is about to be inserted
2345 (let ((coord (table--get-coordinate beg))
2346 (i 0)
2347 (len (length rectangle)))
2348 (while (< i len)
2349 (if (table--goto-coordinate coord 'no-extension)
2350 (table--untabify-line (point)))
2351 (setcdr coord (1+ (cdr coord)))
2352 (setq i (1+ i))))
2353 ;; insert the bar n times
2354 (goto-char beg)
2355 (let ((i 0))
2356 (while (< i n)
2357 (save-excursion
2358 (table--insert-rectangle rectangle))
2359 (setq i (1+ i)))))))
2360 (table-recognize-cell 'force no-copy)
2361 (unless no-update
2362 (table--update-cell-widened))))
2364 ;;;###autoload
2365 (defun table-narrow-cell (n)
2366 "Narrow the current cell by N columns and shrink the cell horizontally.
2367 Some other cells in the same table are narrowed as well to keep the
2368 table's rectangle structure."
2369 (interactive "*p")
2370 (if (< n 0) (setq n 1))
2371 (table--finish-delayed-tasks)
2372 (let* ((coord-list (table--cell-list-to-coord-list (table--vertical-cell-list)))
2373 (current-cell (table--cell-to-coord (table--probe-cell)))
2374 (current-coordinate (table--get-coordinate))
2375 tmp-list)
2376 (message "Narrowing...");; this operation may be lengthy
2377 ;; determine the doable n by try narrowing each cell.
2378 (setq tmp-list coord-list)
2379 (while tmp-list
2380 (let ((cell (prog1 (car tmp-list) (setq tmp-list (cdr tmp-list))))
2381 (table-inhibit-update t)
2382 cell-n)
2383 (table--goto-coordinate (car cell))
2384 (table-recognize-cell 'force)
2385 (table-with-cache-buffer
2386 (table--fill-region (point-min) (point-max) (- table-cell-info-width n))
2387 (if (< (setq cell-n (- table-cell-info-width (table--measure-max-width))) n)
2388 (setq n cell-n))
2389 (erase-buffer)
2390 (setq table-inhibit-auto-fill-paragraph t))))
2391 (if (< n 1) nil
2392 ;; narrow only the contents of each cell but leave the cell frame as is because
2393 ;; we need to have valid frame structure in order for table-with-cache-buffer
2394 ;; to work correctly.
2395 (setq tmp-list coord-list)
2396 (while tmp-list
2397 (let* ((cell (prog1 (car tmp-list) (setq tmp-list (cdr tmp-list))))
2398 (table-inhibit-update t)
2399 (currentp (equal cell current-cell))
2400 old-height)
2401 (if currentp (table--goto-coordinate current-coordinate)
2402 (table--goto-coordinate (car cell)))
2403 (table-recognize-cell 'force)
2404 (setq old-height table-cell-info-height)
2405 (table-with-cache-buffer
2406 (let ((out-of-bound (>= (- (car current-coordinate) (car table-cell-info-lu-coordinate))
2407 (- table-cell-info-width n)))
2408 (sticky (and currentp
2409 (save-excursion
2410 (unless (bolp) (forward-char -1))
2411 (looking-at ".*\\S ")))))
2412 (table--fill-region (point-min) (point-max) (- table-cell-info-width n))
2413 (if (or sticky (and currentp (looking-at ".*\\S ")))
2414 (setq current-coordinate (table--transcoord-cache-to-table))
2415 (if out-of-bound (setcar current-coordinate
2416 (+ (car table-cell-info-lu-coordinate) (- table-cell-info-width n 1))))))
2417 (setq table-inhibit-auto-fill-paragraph t))
2418 (table--update-cell 'now)
2419 ;; if this cell heightens and pushes the current cell below, move
2420 ;; the current-coordinate (point location) down accordingly.
2421 (if currentp (setq current-coordinate (table--get-coordinate))
2422 (if (and (> table-cell-info-height old-height)
2423 (> (cdr current-coordinate) (cdr table-cell-info-lu-coordinate)))
2424 (setcdr current-coordinate (+ (cdr current-coordinate)
2425 (- table-cell-info-height old-height)))))
2427 ;; coord-list is now possibly invalid since some cells may have already
2428 ;; been heightened so recompute them by table--vertical-cell-list.
2429 (table--goto-coordinate current-coordinate)
2430 (setq coord-list (table--cell-list-to-coord-list (table--vertical-cell-list)))
2431 ;; push in the affected area above and below this table so that things
2432 ;; on the right side of the table are shifted horizontally neatly.
2433 (table--horizontally-shift-above-and-below (- n) (reverse coord-list))
2434 ;; finally narrow the frames for each cell.
2435 (let* ((below-list nil)
2436 (this-list coord-list)
2437 (above-list (cdr coord-list)))
2438 (while this-list
2439 (let* ((below (prog1 (car below-list) (setq below-list (if below-list (cdr below-list) coord-list))))
2440 (this (prog1 (car this-list) (setq this-list (cdr this-list))))
2441 (above (prog1 (car above-list) (setq above-list (cdr above-list)))))
2442 (delete-rectangle
2443 (table--goto-coordinate
2444 (cons (- (cadr this) n)
2445 (if (or (null above) (<= (cadr this) (cadr above)))
2446 (1- (cdar this))
2447 (cdar this))))
2448 (table--goto-coordinate
2449 (cons (cadr this)
2450 (if (or (null below) (< (cadr this) (cadr below)))
2451 (1+ (cddr this))
2452 (cddr this)))))))))
2453 (table--goto-coordinate current-coordinate)
2454 ;; re-recognize the current cell's new dimension
2455 (table-recognize-cell 'force)
2456 (message "")))
2458 ;;;###autoload
2459 (defun table-forward-cell (&optional arg no-recognize unrecognize)
2460 "Move point forward to the beginning of the next cell.
2461 With argument ARG, do it ARG times;
2462 a negative argument ARG = -N means move backward N cells.
2463 Do not specify NO-RECOGNIZE and UNRECOGNIZE. They are for internal use only.
2465 Sample Cell Traveling Order (In Irregular Table Cases)
2467 You can actually try how it works in this buffer. Press
2468 \\[table-recognize] and go to cells in the following tables and press
2469 \\[table-forward-cell] or TAB key.
2471 +-----+--+ +--+-----+ +--+--+--+ +--+--+--+ +---------+ +--+---+--+
2472 |0 |1 | |0 |1 | |0 |1 |2 | |0 |1 |2 | |0 | |0 |1 |2 |
2473 +--+--+ | | +--+--+ +--+ | | | | +--+ +----+----+ +--+-+-+--+
2474 |2 |3 | | | |2 |3 | |3 +--+ | | +--+3 | |1 |2 | |3 |4 |
2475 | +--+--+ +--+--+ | +--+4 | | | |4 +--+ +--+-+-+--+ +----+----+
2476 | |4 | |4 | | |5 | | | | | |5 | |3 |4 |5 | |5 |
2477 +--+-----+ +-----+--+ +--+--+--+ +--+--+--+ +--+---+--+ +---------+
2479 +--+--+--+ +--+--+--+ +--+--+--+ +--+--+--+
2480 |0 |1 |2 | |0 |1 |2 | |0 |1 |2 | |0 |1 |2 |
2481 | | | | | +--+ | | | | | +--+ +--+
2482 +--+ +--+ +--+3 +--+ | +--+ | |3 +--+4 |
2483 |3 | |4 | |4 +--+5 | | |3 | | +--+5 +--+
2484 | | | | | |6 | | | | | | |6 | |7 |
2485 +--+--+--+ +--+--+--+ +--+--+--+ +--+--+--+
2487 +--+--+--+ +--+--+--+ +--+--+--+--+ +--+-----+--+ +--+--+--+--+
2488 |0 |1 |2 | |0 |1 |2 | |0 |1 |2 |3 | |0 |1 |2 | |0 |1 |2 |3 |
2489 | +--+ | | +--+ | | +--+--+ | | | | | | +--+--+ |
2490 | |3 +--+ +--+3 | | +--+4 +--+ +--+ +--+ +--+4 +--+
2491 +--+ |4 | |4 | +--+ |5 +--+--+6 | |3 +--+--+4 | |5 | |6 |
2492 |5 +--+ | | +--+5 | | |7 |8 | | | |5 |6 | | | | | |
2493 | |6 | | | |6 | | +--+--+--+--+ +--+--+--+--+ +--+-----+--+
2494 +--+--+--+ +--+--+--+
2496 ;; After modifying this function, test against the above tables in
2497 ;; the doc string. It is quite tricky. The tables above do not
2498 ;; mean to cover every possible cases of cell layout, of course.
2499 ;; They are examples of tricky cases from implementation point of
2500 ;; view and provided for simple regression test purpose.
2501 (interactive "p")
2502 (or arg (setq arg 1))
2503 (table--finish-delayed-tasks)
2504 (while (null (zerop arg))
2505 (let* ((pivot (table--probe-cell 'abort-on-error))
2506 (cell pivot) edge tip)
2507 ;; go to the beginning of the first right/left cell with same height if exists
2508 (while (and (setq cell (table--goto-coordinate
2509 (cons (if (> arg 0) (1+ (car (table--get-coordinate (cdr cell))))
2510 (1- (car (table--get-coordinate (car cell)))))
2511 (cdr (table--get-coordinate (car pivot)))) 'no-extension))
2512 (setq cell (table--probe-cell))
2513 (/= (cdr (table--get-coordinate (car cell)))
2514 (cdr (table--get-coordinate (car pivot))))))
2515 (if cell (goto-char (car cell)) ; done
2516 ;; if the horizontal move fails search the most left/right edge cell below/above the pivot
2517 ;; but first find the edge cell
2518 (setq edge pivot)
2519 (while (and (table--goto-coordinate
2520 (cons (if (> arg 0) (1- (car (table--get-coordinate (car edge))))
2521 (1+ (car (table--get-coordinate (cdr edge)))))
2522 (cdr (table--get-coordinate (car pivot)))) 'no-extension)
2523 (setq cell (table--probe-cell))
2524 (setq edge cell)))
2525 (setq cell (if (> arg 0) edge
2526 (or (and (table--goto-coordinate
2527 (cons (car (table--get-coordinate (cdr edge)))
2528 (1- (cdr (table--get-coordinate (car edge))))))
2529 (table--probe-cell))
2530 edge)))
2531 ;; now search for the tip which is the highest/lowest below/above cell
2532 (while cell
2533 (let (below/above)
2534 (and (table--goto-coordinate
2535 (cons (car (table--get-coordinate (if (> arg 0) (car cell)
2536 (cdr cell))))
2537 (if (> arg 0) (+ 2 (cdr (table--get-coordinate (cdr cell))))
2538 (1- (cdr (table--get-coordinate (car pivot)))))) 'no-extension)
2539 (setq below/above (table--probe-cell))
2540 (or (null tip)
2541 (if (> arg 0)
2542 (< (cdr (table--get-coordinate (car below/above)))
2543 (cdr (table--get-coordinate (car tip))))
2544 (> (cdr (table--get-coordinate (car below/above)))
2545 (cdr (table--get-coordinate (car tip))))))
2546 (setq tip below/above)))
2547 (and (setq cell (table--goto-coordinate
2548 (cons (if (> arg 0) (1+ (car (table--get-coordinate (cdr cell))))
2549 (1- (car (table--get-coordinate (car cell)))))
2550 (if (> arg 0) (cdr (table--get-coordinate (car pivot)))
2551 (1- (cdr (table--get-coordinate (car pivot)))))) 'no-extension))
2552 (setq cell (table--probe-cell))))
2553 (if tip (goto-char (car tip)) ; done
2554 ;; let's climb up/down to the top/bottom from the edge
2555 (while (and (table--goto-coordinate
2556 (cons (if (> arg 0) (car (table--get-coordinate (car edge)))
2557 (car (table--get-coordinate (cdr edge))))
2558 (if (> arg 0) (1- (cdr (table--get-coordinate (car edge))))
2559 (+ 2 (cdr (table--get-coordinate (cdr edge)))))) 'no-extension)
2560 (setq cell (table--probe-cell))
2561 (setq edge cell)))
2562 (if (< arg 0)
2563 (progn
2564 (setq cell edge)
2565 (while (and (table--goto-coordinate
2566 (cons (1- (car (table--get-coordinate (car cell))))
2567 (cdr (table--get-coordinate (cdr cell)))) 'no-extension)
2568 (setq cell (table--probe-cell)))
2569 (if (> (cdr (table--get-coordinate (car cell)))
2570 (cdr (table--get-coordinate (car edge))))
2571 (setq edge cell)))))
2572 (goto-char (car edge))))) ; the top left cell
2573 (setq arg (if (> arg 0) (1- arg) (1+ arg))))
2574 (unless no-recognize
2575 (table-recognize-cell 'force nil (if unrecognize -1 nil)))) ; refill the cache with new cell contents
2577 ;;;###autoload
2578 (defun table-backward-cell (&optional arg)
2579 "Move backward to the beginning of the previous cell.
2580 With argument ARG, do it ARG times;
2581 a negative argument ARG = -N means move forward N cells."
2582 (interactive "p")
2583 (or arg (setq arg 1))
2584 (table-forward-cell (- arg)))
2586 ;;;###autoload
2587 (defun table-span-cell (direction)
2588 "Span current cell into adjacent cell in DIRECTION.
2589 DIRECTION is one of symbols; right, left, above or below."
2590 (interactive
2591 (list
2592 (let* ((dummy (barf-if-buffer-read-only))
2593 (direction-list
2594 (let* ((tmp (delete nil
2595 (mapcar (lambda (d)
2596 (if (table--cell-can-span-p d)
2597 (list (symbol-name d))))
2598 '(right left above below)))))
2599 (if (null tmp)
2600 (error "Can't span this cell"))
2601 tmp))
2602 (default-direction (if (member (list (car table-cell-span-direction-history)) direction-list)
2603 (car table-cell-span-direction-history)
2604 (caar direction-list)))
2605 (completion-ignore-case t))
2606 (intern (downcase (completing-read
2607 (format "Span into (default %s): " default-direction)
2608 direction-list
2609 nil t nil 'table-cell-span-direction-history default-direction))))))
2610 (unless (memq direction '(right left above below))
2611 (error "Invalid direction %s, must be right, left, above or below"
2612 (symbol-name direction)))
2613 (table-recognize-cell 'force)
2614 (unless (table--cell-can-span-p direction)
2615 (error "Can't span %s" (symbol-name direction)))
2616 ;; prepare beginning and ending positions of the border bar to strike through
2617 (let ((beg (cond
2618 ((eq direction 'right)
2619 (save-excursion
2620 (table--goto-coordinate
2621 (cons (car table-cell-info-rb-coordinate)
2622 (1- (cdr table-cell-info-lu-coordinate))) 'no-extension)))
2623 ((eq direction 'below)
2624 (save-excursion
2625 (table--goto-coordinate
2626 (cons (1- (car table-cell-info-lu-coordinate))
2627 (1+ (cdr table-cell-info-rb-coordinate))) 'no-extension)))
2629 (save-excursion
2630 (table--goto-coordinate
2631 (cons (1- (car table-cell-info-lu-coordinate))
2632 (1- (cdr table-cell-info-lu-coordinate))) 'no-extension)))))
2633 (end (cond
2634 ((eq direction 'left)
2635 (save-excursion
2636 (table--goto-coordinate
2637 (cons (car table-cell-info-lu-coordinate)
2638 (1+ (cdr table-cell-info-rb-coordinate))) 'no-extension)))
2639 ((eq direction 'above)
2640 (save-excursion
2641 (table--goto-coordinate
2642 (cons (1+ (car table-cell-info-rb-coordinate))
2643 (1- (cdr table-cell-info-lu-coordinate))) 'no-extension)))
2645 (save-excursion
2646 (table--goto-coordinate
2647 (cons (1+ (car table-cell-info-rb-coordinate))
2648 (1+ (cdr table-cell-info-rb-coordinate))) 'no-extension))))))
2649 ;; replace the bar with blank space while taking care of edges to be border or intersection
2650 (save-excursion
2651 (goto-char beg)
2652 (if (memq direction '(left right))
2653 (let* ((column (current-column))
2654 rectangle
2655 (n-element (- (length (extract-rectangle beg end)) 2))
2656 (above-contp (and (goto-char beg)
2657 (zerop (forward-line -1))
2658 (= (move-to-column column) column)
2659 (looking-at (regexp-quote (char-to-string table-cell-vertical-char)))))
2660 (below-contp (and (goto-char end)
2661 (progn (forward-char -1) t)
2662 (zerop (forward-line 1))
2663 (= (move-to-column column) column)
2664 (looking-at (regexp-quote (char-to-string table-cell-vertical-char))))))
2665 (setq rectangle
2666 (cons (if below-contp
2667 (char-to-string table-cell-intersection-char)
2668 (substring table-cell-horizontal-chars 0 1))
2669 rectangle))
2670 (while (> n-element 0)
2671 (setq rectangle (cons (table--cell-blank-str 1) rectangle))
2672 (setq n-element (1- n-element)))
2673 (setq rectangle
2674 (cons (if above-contp
2675 (char-to-string table-cell-intersection-char)
2676 (substring table-cell-horizontal-chars 0 1))
2677 rectangle))
2678 (delete-rectangle beg end)
2679 (goto-char beg)
2680 (table--insert-rectangle rectangle))
2681 (delete-region beg end)
2682 (insert (if (and (> (point) (point-min))
2683 (save-excursion
2684 (forward-char -1)
2685 (looking-at (regexp-opt-charset
2686 (string-to-list table-cell-horizontal-chars)))))
2687 table-cell-intersection-char
2688 table-cell-vertical-char)
2689 (table--cell-blank-str (- end beg 2))
2690 (if (looking-at (regexp-opt-charset
2691 (string-to-list table-cell-horizontal-chars)))
2692 table-cell-intersection-char
2693 table-cell-vertical-char))))
2694 ;; recognize the newly created spanned cell
2695 (table-recognize-cell 'force)
2696 (if (member direction '(right left))
2697 (table-with-cache-buffer
2698 (table--fill-region (point-min) (point-max))
2699 (setq table-inhibit-auto-fill-paragraph t)))))
2701 ;;;###autoload
2702 (defun table-split-cell-vertically ()
2703 "Split current cell vertically.
2704 Creates a cell above and a cell below the current point location."
2705 (interactive "*")
2706 (table-recognize-cell 'force)
2707 (let ((point-y (cdr (table--get-coordinate))))
2708 (unless (table--cell-can-split-vertically-p)
2709 (error "Can't split here"))
2710 (let* ((old-coordinate (table--get-coordinate))
2711 (column (current-column))
2712 (beg (table--goto-coordinate
2713 (cons (1- (car table-cell-info-lu-coordinate))
2714 point-y)))
2715 (end (table--goto-coordinate
2716 (cons (1+ (car table-cell-info-rb-coordinate))
2717 point-y)))
2718 (line (buffer-substring (1+ beg) (1- end))))
2719 (when (= (cdr old-coordinate) (cdr table-cell-info-rb-coordinate))
2720 (table--goto-coordinate old-coordinate)
2721 (table-heighten-cell 1 'no-copy 'no-update))
2722 (goto-char beg)
2723 (delete-region beg end)
2724 (insert table-cell-intersection-char
2725 (make-string table-cell-info-width (string-to-char table-cell-horizontal-chars))
2726 table-cell-intersection-char)
2727 (table--goto-coordinate old-coordinate)
2728 (forward-line 1)
2729 (move-to-column column)
2730 (setq old-coordinate (table--get-coordinate))
2731 (table-recognize-cell 'force)
2732 (unless (string-match "^\\s *$" line)
2733 (table-with-cache-buffer
2734 (goto-char (point-min))
2735 (insert line ?\n)
2736 (goto-char (point-min)) ;; don't heighten cell unnecessarily
2737 (setq table-inhibit-auto-fill-paragraph t)))
2738 (table--update-cell 'now) ;; can't defer this operation
2739 (table--goto-coordinate old-coordinate)
2740 (move-to-column column)
2741 (table-recognize-cell 'force))))
2743 ;;;###autoload
2744 (defun table-split-cell-horizontally ()
2745 "Split current cell horizontally.
2746 Creates a cell on the left and a cell on the right of the current point location."
2747 (interactive "*")
2748 (table-recognize-cell 'force)
2749 (let* ((o-coordinate (table--get-coordinate))
2750 (point-x (car o-coordinate))
2751 cell-empty cell-contents cell-coordinate
2752 contents-to beg end rectangle strip-rect
2753 (right-edge (= (car o-coordinate) (1- (car table-cell-info-rb-coordinate)))))
2754 (unless (table--cell-can-split-horizontally-p)
2755 (error "Can't split here"))
2756 (let ((table-inhibit-update t))
2757 (table-with-cache-buffer
2758 (setq cell-coordinate (table--get-coordinate))
2759 (save-excursion
2760 (goto-char (point-min))
2761 (setq cell-empty (null (re-search-forward "\\S " nil t))))
2762 (setq cell-contents (buffer-substring (point-min) (point-max)))
2763 (setq table-inhibit-auto-fill-paragraph t)))
2764 (setq contents-to
2765 (if cell-empty 'left
2766 (let* ((completion-ignore-case t)
2767 (default (car table-cell-split-contents-to-history)))
2768 (intern
2769 (if (member 'click (event-modifiers last-input-event))
2770 (x-popup-menu last-input-event
2771 '("Existing cell contents to:"
2772 ("Title"
2773 ("Split" . "split") ("Left" . "left") ("Right" . "right"))))
2774 (downcase (completing-read
2775 (format "Existing cell contents to (default %s): " default)
2776 '(("split") ("left") ("right"))
2777 nil t nil 'table-cell-split-contents-to-history default)))))))
2778 (unless (eq contents-to 'split)
2779 (table-with-cache-buffer
2780 (erase-buffer)
2781 (setq table-inhibit-auto-fill-paragraph t)))
2782 (table--update-cell 'now)
2783 (setq beg (table--goto-coordinate
2784 (cons point-x
2785 (1- (cdr table-cell-info-lu-coordinate)))))
2786 (setq end (table--goto-coordinate
2787 (cons (1+ point-x)
2788 (1+ (cdr table-cell-info-rb-coordinate)))))
2789 (setq rectangle (cons (char-to-string table-cell-intersection-char) nil))
2790 (let ((n table-cell-info-height))
2791 (while (prog1 (> n 0) (setq n (1- n)))
2792 (setq rectangle (cons (char-to-string table-cell-vertical-char) rectangle))))
2793 (setq rectangle (cons (char-to-string table-cell-intersection-char) rectangle))
2794 (if (eq contents-to 'split)
2795 (setq strip-rect (extract-rectangle beg end)))
2796 (delete-rectangle beg end)
2797 (goto-char beg)
2798 (table--insert-rectangle rectangle)
2799 (table--goto-coordinate o-coordinate)
2800 (if cell-empty
2801 (progn
2802 (forward-char 1)
2803 (if right-edge
2804 (table-widen-cell 1)))
2805 (unless (eq contents-to 'left)
2806 (forward-char 1))
2807 (table-recognize-cell 'force)
2808 (table-with-cache-buffer
2809 (if (eq contents-to 'split)
2810 ;; split inserts strip-rect after removing
2811 ;; top and bottom borders
2812 (let ((o-coord (table--get-coordinate))
2813 (l (setq strip-rect (cdr strip-rect))))
2814 (while (cddr l) (setq l (cdr l)))
2815 (setcdr l nil)
2816 ;; insert the strip only when it is not a completely blank one
2817 (unless (let ((cl (mapcar (lambda (s) (string= s " ")) strip-rect)))
2818 (and (car cl)
2819 (table--uniform-list-p cl)))
2820 (goto-char (point-min))
2821 (table--insert-rectangle strip-rect)
2822 (table--goto-coordinate o-coord)))
2823 ;; left or right inserts original contents
2824 (erase-buffer)
2825 (insert cell-contents)
2826 (table--goto-coordinate cell-coordinate)
2827 (table--fill-region (point-min) (point-max))
2828 ;; avoid unnecessary vertical cell expansion
2829 (and (looking-at "\\s *\\'")
2830 (re-search-backward "\\S \\(\\s *\\)\\=" nil t)
2831 (goto-char (match-beginning 1))))
2832 ;; in either case do not fill paragraph
2833 (setq table-inhibit-auto-fill-paragraph t))
2834 (table--update-cell 'now)) ;; can't defer this operation
2835 (table-recognize-cell 'force)))
2837 ;;;###autoload
2838 (defun table-split-cell (orientation)
2839 "Split current cell in ORIENTATION.
2840 ORIENTATION is a symbol either horizontally or vertically."
2841 (interactive
2842 (list
2843 (let* ((dummy (barf-if-buffer-read-only))
2844 (completion-ignore-case t)
2845 (default (car table-cell-split-orientation-history)))
2846 (intern (downcase (completing-read
2847 (format "Split orientation (default %s): " default)
2848 '(("horizontally") ("vertically"))
2849 nil t nil 'table-cell-split-orientation-history default))))))
2850 (unless (memq orientation '(horizontally vertically))
2851 (error "Invalid orientation %s, must be horizontally or vertically"
2852 (symbol-name orientation)))
2853 (if (eq orientation 'horizontally)
2854 (table-split-cell-horizontally)
2855 (table-split-cell-vertically)))
2857 ;;;###autoload
2858 (defun table-justify (what justify)
2859 "Justify contents of a cell, a row of cells or a column of cells.
2860 WHAT is a symbol 'cell, 'row or 'column. JUSTIFY is a symbol 'left,
2861 'center, 'right, 'top, 'middle, 'bottom or 'none."
2862 (interactive
2863 (list (let* ((dummy (barf-if-buffer-read-only))
2864 (completion-ignore-case t)
2865 (default (car table-target-history)))
2866 (intern (downcase (completing-read
2867 (format "Justify what (default %s): " default)
2868 '(("cell") ("row") ("column"))
2869 nil t nil 'table-target-history default))))
2870 (table--query-justification)))
2871 (funcall (intern (concat "table-justify-" (symbol-name what))) justify))
2873 ;;;###autoload
2874 (defun table-justify-cell (justify &optional paragraph)
2875 "Justify cell contents.
2876 JUSTIFY is a symbol 'left, 'center or 'right for horizontal, or 'top,
2877 'middle, 'bottom or 'none for vertical. When optional PARAGRAPH is
2878 non-nil the justify operation is limited to the current paragraph,
2879 otherwise the entire cell contents is justified."
2880 (interactive
2881 (list (table--query-justification)))
2882 (table--finish-delayed-tasks)
2883 (table-recognize-cell 'force)
2884 (table--justify-cell-contents justify paragraph))
2886 ;;;###autoload
2887 (defun table-justify-row (justify)
2888 "Justify cells of a row.
2889 JUSTIFY is a symbol 'left, 'center or 'right for horizontal, or top,
2890 'middle, 'bottom or 'none for vertical."
2891 (interactive
2892 (list (table--query-justification)))
2893 (let((cell-list (table--horizontal-cell-list nil nil 'top)))
2894 (table--finish-delayed-tasks)
2895 (save-excursion
2896 (while cell-list
2897 (let ((cell (car cell-list)))
2898 (setq cell-list (cdr cell-list))
2899 (goto-char (car cell))
2900 (table-recognize-cell 'force)
2901 (table--justify-cell-contents justify))))))
2903 ;;;###autoload
2904 (defun table-justify-column (justify)
2905 "Justify cells of a column.
2906 JUSTIFY is a symbol 'left, 'center or 'right for horizontal, or top,
2907 'middle, 'bottom or 'none for vertical."
2908 (interactive
2909 (list (table--query-justification)))
2910 (let((cell-list (table--vertical-cell-list nil nil 'left)))
2911 (table--finish-delayed-tasks)
2912 (save-excursion
2913 (while cell-list
2914 (let ((cell (car cell-list)))
2915 (setq cell-list (cdr cell-list))
2916 (goto-char (car cell))
2917 (table-recognize-cell 'force)
2918 (table--justify-cell-contents justify))))))
2920 ;;;###autoload
2921 (defun table-fixed-width-mode (&optional arg)
2922 "Toggle fixing width mode.
2923 In the fixed width mode, typing inside a cell never changes the cell
2924 width where in the normal mode the cell width expands automatically in
2925 order to prevent a word being folded into multiple lines."
2926 (interactive "P")
2927 (table--finish-delayed-tasks)
2928 (setq table-fixed-width-mode
2929 (if (null arg)
2930 (not table-fixed-width-mode)
2931 (> (prefix-numeric-value arg) 0)))
2932 (table--update-cell-face))
2934 ;;;###autoload
2935 (defun table-query-dimension (&optional where)
2936 "Return the dimension of the current cell and the current table.
2937 The result is a list (cw ch tw th c r cells) where cw is the cell
2938 width, ch is the cell height, tw is the table width, th is the table
2939 height, c is the number of columns, r is the number of rows and cells
2940 is the total number of cells. The cell dimension excludes the cell
2941 frame while the table dimension includes the table frame. The columns
2942 and the rows are counted by the number of cell boundaries. Therefore
2943 the number tends to be larger than it appears for the tables with
2944 non-uniform cell structure (heavily spanned and split). When optional
2945 WHERE is provided the cell and table at that location is reported."
2946 (interactive)
2947 (save-excursion
2948 (if where (goto-char where))
2949 (let ((starting-cell (table--probe-cell))
2950 cell table-lu table-rb col-list row-list (cells 0))
2951 (if (null starting-cell) nil
2952 (setq table-lu (car starting-cell))
2953 (setq table-rb (cdr starting-cell))
2954 (setq col-list (cons (car (table--get-coordinate (car starting-cell))) nil))
2955 (setq row-list (cons (cdr (table--get-coordinate (car starting-cell))) nil))
2956 (and (interactive-p)
2957 (message "Computing cell dimension..."))
2958 (while
2959 (progn
2960 (table-forward-cell 1 t)
2961 (setq cells (1+ cells))
2962 (and (setq cell (table--probe-cell))
2963 (not (equal cell starting-cell))))
2964 (if (< (car cell) table-lu)
2965 (setq table-lu (car cell)))
2966 (if (> (cdr cell) table-rb)
2967 (setq table-rb (cdr cell)))
2968 (let ((lu-coordinate (table--get-coordinate (car cell))))
2969 (if (memq (car lu-coordinate) col-list) nil
2970 (setq col-list (cons (car lu-coordinate) col-list)))
2971 (if (memq (cdr lu-coordinate) row-list) nil
2972 (setq row-list (cons (cdr lu-coordinate) row-list)))))
2973 (let* ((cell-lu-coordinate (table--get-coordinate (car starting-cell)))
2974 (cell-rb-coordinate (table--get-coordinate (cdr starting-cell)))
2975 (table-lu-coordinate (table--get-coordinate table-lu))
2976 (table-rb-coordinate (table--get-coordinate table-rb))
2977 (cw (- (car cell-rb-coordinate) (car cell-lu-coordinate)))
2978 (ch (1+ (- (cdr cell-rb-coordinate) (cdr cell-lu-coordinate))))
2979 (tw (+ 2 (- (car table-rb-coordinate) (car table-lu-coordinate))))
2980 (th (+ 3 (- (cdr table-rb-coordinate) (cdr table-lu-coordinate))))
2981 (c (length col-list))
2982 (r (length row-list)))
2983 (and (interactive-p)
2984 (message "Cell: (%dw, %dh), Table: (%dw, %dh), Dim: (%dc, %dr), Total Cells: %d" cw ch tw th c r cells))
2985 (list cw ch tw th c r cells))))))
2987 ;;;###autoload
2988 (defun table-generate-source (language &optional dest-buffer caption)
2989 "Generate source of the current table in the specified language.
2990 LANGUAGE is a symbol that specifies the language to describe the
2991 structure of the table. It must be either 'html, 'latex or 'cals.
2992 The resulted source text is inserted into DEST-BUFFER and the buffer
2993 object is returned. When DEST-BUFFER is omitted or nil the default
2994 buffer specified in `table-dest-buffer-name' is used. In this case
2995 the content of the default buffer is erased prior to the generation.
2996 When DEST-BUFFER is non-nil it is expected to be either a destination
2997 buffer or a name of the destination buffer. In this case the
2998 generated result is inserted at the current point in the destination
2999 buffer and the previously existing contents in the buffer are
3000 untouched.
3002 References used for this implementation:
3004 HTML:
3005 http://www.w3.org
3007 LaTeX:
3008 http://www.maths.tcd.ie/~dwilkins/LaTeXPrimer/Tables.html
3010 CALS (DocBook DTD):
3011 http://www.oasis-open.org/html/a502.htm
3012 http://www.oreilly.com/catalog/docbook/chapter/book/table.html#AEN114751
3014 (interactive
3015 (let* ((dummy (unless (table--probe-cell) (error "Table not found here")))
3016 (completion-ignore-case t)
3017 (default (car table-source-language-history))
3018 (language (downcase (completing-read
3019 (format "Language (default %s): " default)
3020 (mapcar (lambda (s) (list (symbol-name s)))
3021 table-source-languages)
3022 nil t nil 'table-source-language-history default))))
3023 (list
3024 (intern language)
3025 (read-buffer "Destination buffer: " (concat table-dest-buffer-name "." language))
3026 (table--read-from-minibuffer '("Table Caption" . table-source-caption-history)))))
3027 (let ((default-buffer-name (concat table-dest-buffer-name "." (symbol-name language))))
3028 (unless (or (interactive-p) (table--probe-cell)) (error "Table not found here"))
3029 (unless (bufferp dest-buffer)
3030 (setq dest-buffer (get-buffer-create (or dest-buffer default-buffer-name))))
3031 (if (string= (buffer-name dest-buffer) default-buffer-name)
3032 (with-current-buffer dest-buffer
3033 (erase-buffer)))
3034 (save-excursion
3035 (let ((starting-cell (table--probe-cell))
3036 cell origin-cell tail-cell col-list row-list (n 0) i)
3037 ;; first analyze the table structure and prepare:
3038 ;; 1. origin cell (left up corner cell)
3039 ;; 2. tail cell (right bottom corner cell)
3040 ;; 3. column boundary list
3041 ;; 4. row boundary list
3042 (setq origin-cell starting-cell)
3043 (setq tail-cell starting-cell)
3044 (setq col-list (cons (car (table--get-coordinate (car starting-cell))) nil))
3045 (setq row-list (cons (cdr (table--get-coordinate (car starting-cell))) nil))
3046 (setq i 0)
3047 (let ((wheel [?- ?\\ ?| ?/]))
3048 (while
3049 (progn
3050 (if (interactive-p)
3051 (progn
3052 (message "Analyzing table...%c" (aref wheel i))
3053 (if (eq (setq i (1+ i)) (length wheel))
3054 (setq i 0))
3055 (setq n (1+ n))))
3056 (table-forward-cell 1 t)
3057 (and (setq cell (table--probe-cell))
3058 (not (equal cell starting-cell))))
3059 (if (< (car cell) (car origin-cell))
3060 (setq origin-cell cell))
3061 (if (> (cdr cell) (cdr tail-cell))
3062 (setq tail-cell cell))
3063 (let ((lu-coordinate (table--get-coordinate (car cell))))
3064 (unless (memq (car lu-coordinate) col-list)
3065 (setq col-list (cons (car lu-coordinate) col-list)))
3066 (unless (memq (cdr lu-coordinate) row-list)
3067 (setq row-list (cons (cdr lu-coordinate) row-list))))))
3068 (setq col-list (sort col-list '<))
3069 (setq row-list (sort row-list '<))
3070 (message "Generating source...")
3071 ;; clear the source generation property list
3072 (setplist 'table-source-info-plist nil)
3073 ;; prepare to start from the origin cell
3074 (goto-char (car origin-cell))
3075 ;; first put some header information
3076 (table--generate-source-prologue dest-buffer language caption col-list row-list)
3077 (cond
3078 ((eq language 'latex)
3079 ;; scan by character lines
3080 (table--generate-source-scan-lines dest-buffer language origin-cell tail-cell col-list row-list))
3082 ;; scan by table cells
3083 (table--generate-source-scan-rows dest-buffer language origin-cell col-list row-list)))
3084 ;; insert closing
3085 (table--generate-source-epilogue dest-buffer language col-list row-list))
3086 ;; lastly do some convenience work
3087 (if (interactive-p)
3088 (save-selected-window
3089 (pop-to-buffer dest-buffer t)
3090 (goto-char (point-min))
3091 (and (string= (buffer-name dest-buffer) default-buffer-name)
3092 (buffer-file-name dest-buffer)
3093 (save-buffer))
3094 (message "Generating source...done")
3095 (let ((mode
3096 (if (memq language '(cals)) 'sgml-mode
3097 (intern (concat (symbol-name language) "-mode")))))
3098 (if (fboundp mode)
3099 (call-interactively mode)))
3101 dest-buffer))
3103 (defun table--generate-source-prologue (dest-buffer language caption col-list row-list)
3104 "Generate and insert source prologue into DEST-BUFFER."
3105 (with-current-buffer dest-buffer
3106 (cond
3107 ((eq language 'html)
3108 (insert (format "<!-- This HTML table template is generated by emacs %s -->\n" emacs-version)
3109 (format "<table %s>\n" table-html-table-attribute)
3110 (if (and (stringp caption)
3111 (not (string= caption "")))
3112 (format " <caption>%s</caption>\n" caption)
3113 "")))
3114 ((eq language 'latex)
3115 (insert (format "%% This LaTeX table template is generated by emacs %s\n" emacs-version)
3116 "\\begin{tabular}{|" (apply 'concat (make-list (length col-list) "l|")) "}\n"
3117 "\\hline\n"))
3118 ((eq language 'cals)
3119 (insert (format "<!-- This CALS table template is generated by emacs %s -->\n" emacs-version)
3120 "<table frame=\"all\">\n")
3121 (if (and (stringp caption)
3122 (not (string= caption "")))
3123 (insert " <title>" caption "</title>\n"))
3124 (insert (format " <tgroup cols=\"%d\" align=\"left\" colsep=\"1\" rowsep=\"1\">\n" (length col-list)))
3125 (table-put-source-info 'colspec-marker (point-marker))
3126 (table-put-source-info 'row-type (if (zerop table-cals-thead-rows) "tbody" "thead"))
3127 (set-marker-insertion-type (table-get-source-info 'colspec-marker) nil) ;; insert after
3128 (insert (format " <%s valign=\"top\">\n" (table-get-source-info 'row-type))))
3131 (defun table--generate-source-epilogue (dest-buffer language col-list row-list)
3132 "Generate and insert source epilogue into DEST-BUFFER."
3133 (with-current-buffer dest-buffer
3134 (cond
3135 ((eq language 'html)
3136 (insert "</table>\n"))
3137 ((eq language 'latex)
3138 (insert "\\end{tabular}\n"))
3139 ((eq language 'cals)
3140 (set-marker-insertion-type (table-get-source-info 'colspec-marker) t) ;; insert before
3141 (save-excursion
3142 (goto-char (table-get-source-info 'colspec-marker))
3143 (mapc
3144 (lambda (col)
3145 (insert (format " <colspec colnum=\"%d\" colname=\"c%d\"/>\n" col col)))
3146 (sort (table-get-source-info 'colnum-list) '<)))
3147 (insert (format " </%s>\n </tgroup>\n</table>\n" (table-get-source-info 'row-type))))
3150 (defun table--generate-source-scan-rows (dest-buffer language origin-cell col-list row-list)
3151 "Generate and insert source rows into DEST-BUFFER."
3152 (table-put-source-info 'current-row 1)
3153 (while row-list
3154 (with-current-buffer dest-buffer
3155 (cond
3156 ((eq language 'html)
3157 (insert " <tr>\n"))
3158 ((eq language 'cals)
3159 (insert " <row>\n"))
3161 (table--generate-source-cells-in-a-row dest-buffer language col-list row-list)
3162 (with-current-buffer dest-buffer
3163 (cond
3164 ((eq language 'html)
3165 (insert " </tr>\n"))
3166 ((eq language 'cals)
3167 (insert " </row>\n")
3168 (unless (/= (table-get-source-info 'current-row) table-cals-thead-rows)
3169 (insert (format " </%s>\n" (table-get-source-info 'row-type)))
3170 (insert (format " <%s valign=\"top\">\n" (table-put-source-info 'row-type "tbody")))))))
3171 (table-put-source-info 'current-row (1+ (table-get-source-info 'current-row)))
3172 (setq row-list (cdr row-list))))
3174 (defun table--generate-source-cells-in-a-row (dest-buffer language col-list row-list)
3175 "Generate and insert source cells into DEST-BUFFER."
3176 (table-put-source-info 'current-column 1)
3177 (while col-list
3178 (let* ((cell (table--probe-cell))
3179 (lu (table--get-coordinate (car cell)))
3180 (rb (table--get-coordinate (cdr cell)))
3181 (alignment (table--get-cell-justify-property cell))
3182 (valign (table--get-cell-valign-property cell))
3183 (row-list row-list)
3184 (colspan 1)
3185 (rowspan 1))
3186 (if (< (car lu) (car col-list))
3187 (setq col-list nil)
3188 (while (and col-list
3189 (> (car lu) (car col-list)))
3190 (setq col-list (cdr col-list))
3191 (table-put-source-info 'current-column (1+ (table-get-source-info 'current-column))))
3192 (setq col-list (cdr col-list))
3193 (table-put-source-info 'next-column (1+ (table-get-source-info 'current-column)))
3194 (while (and col-list
3195 (> (1+ (car rb)) (car col-list)))
3196 (setq colspan (1+ colspan))
3197 (setq col-list (cdr col-list))
3198 (table-put-source-info 'next-column (1+ (table-get-source-info 'next-column))))
3199 (setq row-list (cdr row-list))
3200 (while (and row-list
3201 (> (+ (cdr rb) 2) (car row-list)))
3202 (setq rowspan (1+ rowspan))
3203 (setq row-list (cdr row-list)))
3204 (with-current-buffer dest-buffer
3205 (cond
3206 ((eq language 'html)
3207 (insert (format " <%s"
3208 (table-put-source-info
3209 'cell-type
3210 (if (or (<= (table-get-source-info 'current-row) table-html-th-rows)
3211 (<= (table-get-source-info 'current-column) table-html-th-columns))
3212 "th" "td"))))
3213 (if (and table-html-cell-attribute (not (string= table-html-cell-attribute "")))
3214 (insert " " table-html-cell-attribute))
3215 (if (> colspan 1) (insert (format " colspan=\"%d\"" colspan)))
3216 (if (> rowspan 1) (insert (format " rowspan=\"%d\"" rowspan)))
3217 (insert (format " align=\"%s\"" (if alignment (symbol-name alignment) "left")))
3218 (insert (format " valign=\"%s\"" (if valign (symbol-name valign) "top")))
3219 (insert ">\n"))
3220 ((eq language 'cals)
3221 (insert " <entry")
3222 (if (> colspan 1)
3223 (let ((scol (table-get-source-info 'current-column))
3224 (ecol (+ (table-get-source-info 'current-column) colspan -1)))
3225 (mapc (lambda (col)
3226 (unless (memq col (table-get-source-info 'colnum-list))
3227 (table-put-source-info 'colnum-list
3228 (cons col (table-get-source-info 'colnum-list)))))
3229 (list scol ecol))
3230 (insert (format " namest=\"c%d\" nameend=\"c%d\"" scol ecol))))
3231 (if (> rowspan 1) (insert (format " morerows=\"%d\"" (1- rowspan))))
3232 (if (and alignment
3233 (not (memq alignment '(left none))))
3234 (insert " align=\"" (symbol-name alignment) "\""))
3235 (if (and valign
3236 (not (memq valign '(top none))))
3237 (insert " valign=\"" (symbol-name valign) "\""))
3238 (insert ">\n"))
3240 (table--generate-source-cell-contents dest-buffer language cell)
3241 (with-current-buffer dest-buffer
3242 (cond
3243 ((eq language 'html)
3244 (insert (format" </%s>\n" (table-get-source-info 'cell-type))))
3245 ((eq language 'cals)
3246 (insert " </entry>\n"))
3248 (table-forward-cell 1 t)
3249 (table-put-source-info 'current-column (table-get-source-info 'next-column))
3250 ))))
3252 (defun table--generate-source-cell-contents (dest-buffer language cell)
3253 "Generate and insert source cell contents of a CELL into DEST-BUFFER."
3254 (let ((cell-contents (extract-rectangle (car cell) (cdr cell))))
3255 (with-temp-buffer
3256 (table--insert-rectangle cell-contents)
3257 (table--remove-cell-properties (point-min) (point-max))
3258 (goto-char (point-min))
3259 (cond
3260 ((eq language 'html)
3261 (if table-html-delegate-spacing-to-user-agent
3262 (progn
3263 (table--remove-eol-spaces (point-min) (point-max))
3264 (if (re-search-forward "\\s +\\'" nil t)
3265 (replace-match "")))
3266 (while (search-forward " " nil t)
3267 (replace-match "&nbsp;"))
3268 (goto-char (point-min))
3269 (while (and (re-search-forward "$" nil t)
3270 (not (eobp)))
3271 (insert "<br />")
3272 (forward-char 1)))
3273 (unless (and table-html-delegate-spacing-to-user-agent
3274 (progn
3275 (goto-char (point-min))
3276 (looking-at "\\s *\\'")))))
3277 ((eq language 'cals)
3278 (table--remove-eol-spaces (point-min) (point-max))
3279 (if (re-search-forward "\\s +\\'" nil t)
3280 (replace-match "")))
3282 (setq cell-contents (buffer-substring (point-min) (point-max))))
3283 (with-current-buffer dest-buffer
3284 (let ((beg (point)))
3285 (insert cell-contents)
3286 (indent-rigidly beg (point)
3287 (cond
3288 ((eq language 'html) 6)
3289 ((eq language 'cals) 10)))
3290 (insert ?\n)))))
3292 (defun table--cell-horizontal-char-p (c)
3293 "Test if character C is one of the horizontal characters"
3294 (memq c (string-to-list table-cell-horizontal-chars)))
3296 (defun table--generate-source-scan-lines (dest-buffer language origin-cell tail-cell col-list row-list)
3297 "Scan the table line by line.
3298 Currently this method is for LaTeX only."
3299 (let* ((lu-coord (table--get-coordinate (car origin-cell)))
3300 (rb-coord (table--get-coordinate (cdr tail-cell)))
3301 (x0 (car lu-coord))
3302 (x1 (car rb-coord))
3303 (y (cdr lu-coord))
3304 (y1 (cdr rb-coord)))
3305 (while (<= y y1)
3306 (let* ((border-p (memq (1+ y) row-list))
3307 (border-char-list
3308 (mapcar (lambda (x)
3309 (if border-p (char-after (table--goto-coordinate (cons x y)))
3310 (char-before (table--goto-coordinate (cons x y)))))
3311 col-list))
3312 start i c)
3313 (if border-p
3314 ;; horizontal cell border processing
3315 (if (and (table--cell-horizontal-char-p (car border-char-list))
3316 (table--uniform-list-p border-char-list))
3317 (with-current-buffer dest-buffer
3318 (insert "\\hline\n"))
3319 (setq i 0)
3320 (while (setq c (nth i border-char-list))
3321 (if (and start (not (table--cell-horizontal-char-p c)))
3322 (progn
3323 (with-current-buffer dest-buffer
3324 (insert (format "\\cline{%d-%d}\n" (1+ start) i)))
3325 (setq start nil)))
3326 (if (and (not start) (table--cell-horizontal-char-p c))
3327 (setq start i))
3328 (setq i (1+ i)))
3329 (if start
3330 (with-current-buffer dest-buffer
3331 (insert (format "\\cline{%d-%d}\n" (1+ start) i)))))
3332 ;; horizontal cell contents processing
3333 (let* ((span 1) ;; spanning length
3334 (first-p t) ;; first in a row
3335 (insert-column ;; a function that processes one column/multicolumn
3336 (function
3337 (lambda (from to)
3338 (let ((line (table--buffer-substring-and-trim
3339 (table--goto-coordinate (cons from y))
3340 (table--goto-coordinate (cons to y)))))
3341 ;; escape special characters
3342 (with-temp-buffer
3343 (insert line)
3344 (goto-char (point-min))
3345 (while (re-search-forward "\\([#$~_^%{}]\\)\\|\\(\\\\\\)\\|\\([<>|]\\)" nil t)
3346 (if (match-beginning 1)
3347 (save-excursion
3348 (goto-char (match-beginning 1))
3349 (insert "\\"))
3350 (if (match-beginning 2)
3351 (replace-match "$\\backslash$" t t)
3352 (replace-match (concat "$" (match-string 3) "$")) t t)))
3353 (setq line (buffer-substring (point-min) (point-max))))
3354 ;; insert a column separator and column/multicolumn contents
3355 (with-current-buffer dest-buffer
3356 (unless first-p
3357 (insert (if (eq (char-before) ?\s) "" " ") "& "))
3358 (if (> span 1)
3359 (insert (format "\\multicolumn{%d}{%sl|}{%s}" span (if first-p "|" "") line))
3360 (insert line)))
3361 (setq first-p nil)
3362 (setq span 1)
3363 (setq start (nth i col-list)))))))
3364 (setq start x0)
3365 (setq i 1)
3366 (while (setq c (nth i border-char-list))
3367 (if (eq c table-cell-vertical-char)
3368 (funcall insert-column start (1- (nth i col-list)))
3369 (setq span (1+ span)))
3370 (setq i (1+ i)))
3371 (funcall insert-column start x1))
3372 (with-current-buffer dest-buffer
3373 (insert (if (eq (char-before) ?\s) "" " ") "\\\\\n"))))
3374 (setq y (1+ y)))
3375 (with-current-buffer dest-buffer
3376 (insert "\\hline\n"))
3379 ;;;###autoload
3380 (defun table-insert-sequence (str n increment interval justify)
3381 "Travel cells forward while inserting a specified sequence string in each cell.
3382 STR is the base string from which the sequence starts. When STR is an
3383 empty string then each cell content is erased. When STR ends with
3384 numerical characters (they may optionally be surrounded by a pair of
3385 parentheses) they are incremented as a decimal number. Otherwise the
3386 last character in STR is incremented in ASCII code order. N is the
3387 number of sequence elements to insert. When N is negative the cell
3388 traveling direction is backward. When N is zero it travels forward
3389 entire table. INCREMENT is the increment between adjacent sequence
3390 elements and can be a negative number for effectively decrementing.
3391 INTERVAL is the number of cells to travel between sequence element
3392 insertion which is normally 1. When zero or less is given for
3393 INTERVAL it is interpreted as number of cells per row so that sequence
3394 is placed straight down vertically as long as the table's cell
3395 structure is uniform. JUSTIFY is one of the symbol 'left, 'center or
3396 'right, that specifies justification of the inserted string.
3398 Example:
3400 (progn
3401 (table-insert 16 3 5 1)
3402 (table-forward-cell 15)
3403 (table-insert-sequence \"D0\" -16 1 1 'center)
3404 (table-forward-cell 16)
3405 (table-insert-sequence \"A[0]\" -16 1 1 'center)
3406 (table-forward-cell 1)
3407 (table-insert-sequence \"-\" 16 0 1 'center))
3409 (progn
3410 (table-insert 16 8 5 1)
3411 (table-insert-sequence \"@\" 0 1 2 'right)
3412 (table-forward-cell 1)
3413 (table-insert-sequence \"64\" 0 1 2 'left))
3415 (interactive
3416 (progn
3417 (barf-if-buffer-read-only)
3418 (unless (table--probe-cell) (error "Table not found here"))
3419 (list (read-from-minibuffer
3420 "Sequence base string: " (car table-sequence-string-history) nil nil 'table-sequence-string-history)
3421 (string-to-number
3422 (table--read-from-minibuffer
3423 '("How many elements (0: maximum, negative: backward traveling)" . table-sequence-count-history)))
3424 (string-to-number
3425 (table--read-from-minibuffer
3426 '("Increment element by" . table-sequence-increment-history)))
3427 (string-to-number
3428 (table--read-from-minibuffer
3429 '("Cell interval (0: vertical, 1:horizontal)" . table-sequence-interval-history)))
3430 (let* ((completion-ignore-case t)
3431 (default (car table-sequence-justify-history)))
3432 (intern (downcase (completing-read
3433 (format "Justify (default %s): " default)
3434 '(("left") ("center") ("right"))
3435 nil t nil 'table-sequence-justify-history default)))))))
3436 (unless (or (interactive-p) (table--probe-cell)) (error "Table not found here"))
3437 (string-match "\\([0-9]*\\)\\([]})>]*\\)\\'" str)
3438 (if (interactive-p)
3439 (message "Sequencing..."))
3440 (let* ((prefix (substring str 0 (match-beginning 1)))
3441 (index (match-string 1 str))
3442 (fmt (format "%%%s%dd" (if (eq (string-to-char index) ?0) "0" "") (length index)))
3443 (postfix (match-string 2 str))
3444 (dim (table-query-dimension))
3445 (cells (nth 6 dim))
3446 (direction (if (< n 0) -1 1))
3447 (interval-count 0))
3448 (if (string= index "")
3449 (progn
3450 (setq index nil)
3451 (if (string= prefix "")
3452 (setq prefix nil)))
3453 (setq index (string-to-number index)))
3454 (if (< n 0) (setq n (- n)))
3455 (if (or (zerop n) (> n cells)) (setq n cells))
3456 (if (< interval 0) (setq interval (- interval)))
3457 (if (zerop interval) (setq interval (nth 4 dim)))
3458 (save-excursion
3459 (while (progn
3460 (if (> interval-count 0) nil
3461 (setq interval-count interval)
3462 (table-with-cache-buffer
3463 (goto-char (point-min))
3464 (if (not (or prefix index))
3465 (erase-buffer)
3466 (insert prefix)
3467 (if index (insert (format fmt index)))
3468 (insert postfix)
3469 (table--fill-region (point-min) (point) table-cell-info-width justify)
3470 (setq table-cell-info-justify justify))
3471 (setq table-inhibit-auto-fill-paragraph t))
3472 (table--update-cell 'now)
3473 (if index
3474 (setq index (+ index increment))
3475 (if (and prefix (string= postfix ""))
3476 (let ((len-1 (1- (length prefix))))
3477 (setq prefix (concat (substring prefix 0 len-1)
3478 (char-to-string
3479 (+ (string-to-char (substring prefix len-1)) increment)))))))
3480 (setq n (1- n)))
3481 (table-forward-cell direction t)
3482 (setq interval-count (1- interval-count))
3483 (setq cells (1- cells))
3484 (and (> n 0) (> cells 0)))))
3485 (table-recognize-cell 'force)
3486 (if (interactive-p)
3487 (message "Sequencing...done"))
3490 ;;;###autoload
3491 (defun table-delete-row (n)
3492 "Delete N row(s) of cells.
3493 Delete N rows of cells from current row. The current row is the row
3494 contains the current cell where point is located. Each row must
3495 consists from cells of same height."
3496 (interactive "*p")
3497 (let ((orig-coord (table--get-coordinate))
3498 (bt-coord (table--get-coordinate (cdr (table--vertical-cell-list nil 'first-only))))
3499 lu-coord rb-coord rect)
3500 ;; determine the area to delete while testing row height uniformity
3501 (while (> n 0)
3502 (setq n (1- n))
3503 (unless (table--probe-cell)
3504 (error "Table not found"))
3505 (let ((cell-list (table--horizontal-cell-list 'left-to-right)))
3506 (unless
3507 (and (table--uniform-list-p
3508 (mapcar (lambda (cell) (cdr (table--get-coordinate (car cell)))) cell-list))
3509 (table--uniform-list-p
3510 (mapcar (lambda (cell) (cdr (table--get-coordinate (cdr cell)))) cell-list)))
3511 (error "Cells in this row are not in uniform height"))
3512 (unless lu-coord
3513 (setq lu-coord (table--get-coordinate (caar cell-list))))
3514 (setq rb-coord (table--get-coordinate (cdar (last cell-list))))
3515 (table--goto-coordinate (cons (car orig-coord) (+ 2 (cdr rb-coord))))))
3516 ;; copy the remaining area (below the deleting area)
3517 (setq rect (extract-rectangle
3518 (table--goto-coordinate (cons (1- (car lu-coord)) (1+ (cdr rb-coord))))
3519 (table--goto-coordinate (cons (1+ (car rb-coord)) (1+ (cdr bt-coord))))))
3520 ;; delete the deleting area and below together
3521 (delete-rectangle
3522 (table--goto-coordinate (cons (1- (car lu-coord)) (1- (cdr lu-coord))))
3523 (table--goto-coordinate (cons (1+ (car rb-coord)) (1+ (cdr bt-coord)))))
3524 (table--goto-coordinate (cons (1- (car lu-coord)) (1- (cdr lu-coord))))
3525 ;; insert the remaining area while appending blank lines below it
3526 (table--insert-rectangle
3527 (append rect (make-list (+ 2 (- (cdr rb-coord) (cdr lu-coord)))
3528 (make-string (+ 2 (- (car rb-coord) (car lu-coord))) ?\s))))
3529 ;; remove the appended blank lines below the table if they are unnecessary
3530 (table--goto-coordinate (cons 0 (- (cdr bt-coord) (- (cdr rb-coord) (cdr lu-coord)))))
3531 (table--remove-blank-lines (+ 2 (- (cdr rb-coord) (cdr lu-coord))))
3532 ;; fix up intersections
3533 (let ((coord (cons (car lu-coord) (1- (cdr lu-coord))))
3534 (n (1+ (- (car rb-coord) (car lu-coord)))))
3535 (while (> n 0)
3536 (table--goto-coordinate coord)
3537 (if (save-excursion
3538 (or (and (table--goto-coordinate (cons (car coord) (1- (cdr coord))) 'no-extension)
3539 (looking-at (regexp-quote (char-to-string table-cell-vertical-char))))
3540 (and (table--goto-coordinate (cons (car coord) (1+ (cdr coord))) 'no-extension)
3541 (looking-at (regexp-quote (char-to-string table-cell-vertical-char))))))
3542 (progn
3543 (delete-char 1)
3544 (insert table-cell-intersection-char))
3545 (delete-char 1)
3546 (insert (string-to-char table-cell-horizontal-chars)))
3547 (setq n (1- n))
3548 (setcar coord (1+ (car coord)))))
3549 ;; goto appropriate end point
3550 (table--goto-coordinate (cons (car orig-coord) (cdr lu-coord)))))
3552 ;;;###autoload
3553 (defun table-delete-column (n)
3554 "Delete N column(s) of cells.
3555 Delete N columns of cells from current column. The current column is
3556 the column contains the current cell where point is located. Each
3557 column must consists from cells of same width."
3558 (interactive "*p")
3559 (let ((orig-coord (table--get-coordinate))
3560 lu-coord rb-coord)
3561 ;; determine the area to delete while testing column width uniformity
3562 (while (> n 0)
3563 (setq n (1- n))
3564 (unless (table--probe-cell)
3565 (error "Table not found"))
3566 (let ((cell-list (table--vertical-cell-list 'top-to-bottom)))
3567 (unless
3568 (and (table--uniform-list-p
3569 (mapcar (function (lambda (cell) (car (table--get-coordinate (car cell))))) cell-list))
3570 (table--uniform-list-p
3571 (mapcar (function (lambda (cell) (car (table--get-coordinate (cdr cell))))) cell-list)))
3572 (error "Cells in this column are not in uniform width"))
3573 (unless lu-coord
3574 (setq lu-coord (table--get-coordinate (caar cell-list))))
3575 (setq rb-coord (table--get-coordinate (cdar (last cell-list))))
3576 (table--goto-coordinate (cons (1+ (car rb-coord)) (cdr orig-coord)))))
3577 ;; delete the area
3578 (delete-rectangle
3579 (table--goto-coordinate (cons (car lu-coord) (1- (cdr lu-coord))))
3580 (table--goto-coordinate (cons (1+ (car rb-coord)) (1+ (cdr rb-coord)))))
3581 ;; fix up the intersections
3582 (let ((coord (cons (1- (car lu-coord)) (cdr lu-coord)))
3583 (n (1+ (- (cdr rb-coord) (cdr lu-coord)))))
3584 (while (> n 0)
3585 (table--goto-coordinate coord)
3586 (if (save-excursion
3587 (or (and (table--goto-coordinate (cons (1- (car coord)) (cdr coord)) 'no-extension)
3588 (looking-at (regexp-opt-charset
3589 (string-to-list table-cell-horizontal-chars))))
3590 (and (table--goto-coordinate (cons (1+ (car coord)) (cdr coord)) 'no-extension)
3591 (looking-at (regexp-opt-charset
3592 (string-to-list table-cell-horizontal-chars))))))
3593 (progn
3594 (delete-char 1)
3595 (insert table-cell-intersection-char))
3596 (delete-char 1)
3597 (insert table-cell-vertical-char))
3598 (setq n (1- n))
3599 (setcdr coord (1+ (cdr coord)))))
3600 ;; goto appropriate end point
3601 (table--goto-coordinate (cons (car lu-coord) (cdr orig-coord)))))
3603 ;;;###autoload
3604 (defun table-capture (beg end &optional col-delim-regexp row-delim-regexp justify min-cell-width columns)
3605 "Convert plain text into a table by capturing the text in the region.
3606 Create a table with the text in region as cell contents. BEG and END
3607 specify the region. The text in the region is replaced with a table.
3608 The removed text is inserted in the table. When optional
3609 COL-DELIM-REGEXP and ROW-DELIM-REGEXP are provided the region contents
3610 is parsed and separated into individual cell contents by using the
3611 delimiter regular expressions. This parsing determines the number of
3612 columns and rows of the table automatically. If COL-DELIM-REGEXP and
3613 ROW-DELIM-REGEXP are omitted the result table has only one cell and
3614 the entire region contents is placed in that cell. Optional JUSTIFY
3615 is one of 'left, 'center or 'right, which specifies the cell
3616 justification. Optional MIN-CELL-WIDTH specifies the minimum cell
3617 width. Optional COLUMNS specify the number of columns when
3618 ROW-DELIM-REGEXP is not specified.
3621 Example 1:
3623 1, 2, 3, 4
3624 5, 6, 7, 8
3625 , 9, 10
3627 Running `table-capture' on above 3 line region with COL-DELIM-REGEXP
3628 \",\" and ROW-DELIM-REGEXP \"\\n\" creates the following table. In
3629 this example the cells are centered and minimum cell width is
3630 specified as 5.
3632 +-----+-----+-----+-----+
3633 | 1 | 2 | 3 | 4 |
3634 +-----+-----+-----+-----+
3635 | 5 | 6 | 7 | 8 |
3636 +-----+-----+-----+-----+
3637 | | 9 | 10 | |
3638 +-----+-----+-----+-----+
3640 Note:
3642 In case the function is called interactively user must use \\[quoted-insert] `quoted-insert'
3643 in order to enter \"\\n\" successfully. COL-DELIM-REGEXP at the end
3644 of each row is optional.
3647 Example 2:
3649 This example shows how a table can be used for text layout editing.
3650 Let `table-capture' capture the following region starting from
3651 -!- and ending at -*-, that contains three paragraphs and two item
3652 name headers. This time specify empty string for both
3653 COL-DELIM-REGEXP and ROW-DELIM-REGEXP.
3655 -!-`table-capture' is a powerful command however mastering its power
3656 requires some practice. Here is a list of items what it can do.
3658 Parse Cell Items By using column delimiter regular
3659 expression and raw delimiter regular
3660 expression, it parses the specified text
3661 area and extracts cell items from
3662 non-table text and then forms a table out
3663 of them.
3665 Capture Text Area When no delimiters are specified it
3666 creates a single cell table. The text in
3667 the specified region is placed in that
3668 cell.-*-
3670 Now the entire content is captured in a cell which is itself a table
3671 like this.
3673 +-----------------------------------------------------------------+
3674 |`table-capture' is a powerful command however mastering its power|
3675 |requires some practice. Here is a list of items what it can do. |
3677 |Parse Cell Items By using column delimiter regular |
3678 | expression and raw delimiter regular |
3679 | expression, it parses the specified text |
3680 | area and extracts cell items from |
3681 | non-table text and then forms a table out |
3682 | of them. |
3684 |Capture Text Area When no delimiters are specified it |
3685 | creates a single cell table. The text in |
3686 | the specified region is placed in that |
3687 | cell. |
3688 +-----------------------------------------------------------------+
3690 By splitting the cell appropriately we now have a table consisting of
3691 paragraphs occupying its own cell. Each cell can now be edited
3692 independently.
3694 +-----------------------------------------------------------------+
3695 |`table-capture' is a powerful command however mastering its power|
3696 |requires some practice. Here is a list of items what it can do. |
3697 +---------------------+-------------------------------------------+
3698 |Parse Cell Items |By using column delimiter regular |
3699 | |expression and raw delimiter regular |
3700 | |expression, it parses the specified text |
3701 | |area and extracts cell items from |
3702 | |non-table text and then forms a table out |
3703 | |of them. |
3704 +---------------------+-------------------------------------------+
3705 |Capture Text Area |When no delimiters are specified it |
3706 | |creates a single cell table. The text in |
3707 | |the specified region is placed in that |
3708 | |cell. |
3709 +---------------------+-------------------------------------------+
3711 By applying `table-release', which does the opposite process, the
3712 contents become once again plain text. `table-release' works as
3713 companion command to `table-capture' this way.
3715 (interactive
3716 (let ((col-delim-regexp)
3717 (row-delim-regexp))
3718 (barf-if-buffer-read-only)
3719 (if (table--probe-cell)
3720 (error "Can't insert a table inside a table"))
3721 (list
3722 (mark) (point)
3723 (setq col-delim-regexp
3724 (read-from-minibuffer "Column delimiter regexp: "
3725 (car table-col-delim-regexp-history) nil nil 'table-col-delim-regexp-history))
3726 (setq row-delim-regexp
3727 (read-from-minibuffer "Row delimiter regexp: "
3728 (car table-row-delim-regexp-history) nil nil 'table-row-delim-regexp-history))
3729 (let* ((completion-ignore-case t)
3730 (default (car table-capture-justify-history)))
3731 (if (and (string= col-delim-regexp "") (string= row-delim-regexp "")) 'left
3732 (intern
3733 (downcase (completing-read
3734 (format "Justify (default %s): " default)
3735 '(("left") ("center") ("right"))
3736 nil t nil 'table-capture-justify-history default)))))
3737 (if (and (string= col-delim-regexp "") (string= row-delim-regexp "")) "1"
3738 (table--read-from-minibuffer '("Minimum cell width" . table-capture-min-cell-width-history)))
3739 (if (and (not (string= col-delim-regexp "")) (string= row-delim-regexp ""))
3740 (string-to-number
3741 (table--read-from-minibuffer '("Number of columns" . 'table-capture-columns-history)))
3742 nil)
3744 (if (> beg end) (let ((tmp beg)) (setq beg end) (setq end tmp)))
3745 (if (string= col-delim-regexp "") (setq col-delim-regexp nil))
3746 (if (string= row-delim-regexp "") (setq row-delim-regexp nil))
3747 (if (and columns (< columns 1)) (setq columns nil))
3748 (unless min-cell-width (setq min-cell-width "5"))
3749 (let ((contents (buffer-substring beg end))
3750 (cols 0) (rows 0) c r cell-list
3751 (delim-pattern
3752 (if (and col-delim-regexp row-delim-regexp)
3753 (format "\\(\\(%s\\)?\\s *\\(%s\\)\\s *\\)\\|\\(\\(%s\\)\\s *\\)"
3754 col-delim-regexp row-delim-regexp col-delim-regexp)
3755 (if col-delim-regexp
3756 (format "\\(\\)\\(\\)\\(\\)\\(\\(%s\\)\\s *\\)" col-delim-regexp))))
3757 (contents-list))
3758 ;; when delimiters are specified extract cells and determine the cell dimension
3759 (if delim-pattern
3760 (with-temp-buffer
3761 (insert contents)
3762 ;; make sure the contents ends with a newline
3763 (goto-char (point-max))
3764 (unless (zerop (current-column))
3765 (insert ?\n))
3766 ;; skip the preceding white spaces
3767 (goto-char (point-min))
3768 (if (looking-at "\\s +")
3769 (goto-char (match-end 0)))
3770 ;; extract cell contents
3771 (let ((from (point)))
3772 (setq cell-list nil)
3773 (setq c 0)
3774 (while (and (re-search-forward delim-pattern nil t)
3775 (cond
3776 ;; row delimiter
3777 ((and (match-string 1) (not (string= (match-string 1) "")))
3778 (setq rows (1+ rows))
3779 (setq cell-list
3780 (append cell-list (list (buffer-substring from (match-beginning 1)))))
3781 (setq from (match-end 1))
3782 (setq contents-list
3783 (append contents-list (list cell-list)))
3784 (setq cell-list nil)
3785 (setq c (1+ c))
3786 (if (> c cols) (setq cols c))
3787 (setq c 0)
3789 ;; column delimiter
3790 ((and (match-string 4) (not (string= (match-string 4) "")))
3791 (setq cell-list
3792 (append cell-list (list (buffer-substring from (match-beginning 4)))))
3793 (setq from (match-end 4))
3794 (setq c (1+ c))
3795 (if (> c cols) (setq cols c))
3797 (t nil))))
3798 ;; take care of the last element without a post delimiter
3799 (unless (null (looking-at ".+$"))
3800 (setq cell-list
3801 (append cell-list (list (match-string 0))))
3802 (setq cols (1+ cols)))
3803 ;; take care of the last row without a terminating delimiter
3804 (unless (null cell-list)
3805 (setq rows (1+ rows))
3806 (setq contents-list
3807 (append contents-list (list cell-list)))))))
3808 ;; finalize the table dimension
3809 (if (and columns contents-list)
3810 ;; when number of columns are specified and cells are parsed determine the dimension
3811 (progn
3812 (setq cols columns)
3813 (setq rows (/ (+ (length (car contents-list)) columns -1) columns)))
3814 ;; when dimensions are not specified default to a single cell table
3815 (if (zerop rows) (setq rows 1))
3816 (if (zerop cols) (setq cols 1)))
3817 ;; delete the region and reform line breaks
3818 (delete-region beg end)
3819 (goto-char beg)
3820 (unless (zerop (current-column))
3821 (insert ?\n))
3822 (unless (looking-at "\\s *$")
3823 (save-excursion
3824 (insert ?\n)))
3825 ;; insert the table
3826 ;; insert the cell contents
3827 (if (null contents-list)
3828 ;; single cell
3829 (let ((width) (height))
3830 (with-temp-buffer
3831 (insert contents)
3832 (table--remove-eol-spaces (point-min) (point-max))
3833 (table--untabify (point-min) (point-max))
3834 (setq width (table--measure-max-width))
3835 (setq height (1+ (table--current-line (point-max))))
3836 (setq contents (buffer-substring (point-min) (point-max))))
3837 (table-insert cols rows width height)
3838 (table-with-cache-buffer
3839 (insert contents)
3840 (setq table-inhibit-auto-fill-paragraph t)))
3841 ;; multi cells
3842 (table-insert cols rows min-cell-width 1)
3843 (setq r 0)
3844 (setq cell-list nil)
3845 (while (< r rows)
3846 (setq r (1+ r))
3847 (setq c 0)
3848 (unless cell-list
3849 (setq cell-list (car contents-list))
3850 (setq contents-list (cdr contents-list)))
3851 (while (< c cols)
3852 (setq c (1+ c))
3853 (if (car cell-list)
3854 (table-with-cache-buffer
3855 (insert (car cell-list))
3856 (setq cell-list (cdr cell-list))
3857 (setq table-cell-info-justify justify)))
3858 (table-forward-cell 1))))))
3860 ;;;###autoload
3861 (defun table-release ()
3862 "Convert a table into plain text by removing the frame from a table.
3863 Remove the frame from a table and inactivate the table. This command
3864 converts a table into plain text without frames. It is a companion to
3865 `table-capture' which does the opposite process."
3866 (interactive)
3867 (let ((origin-cell (table--probe-cell))
3868 table-lu table-rb)
3869 (if origin-cell
3870 (let ((old-point (point-marker)))
3871 ;; save-excursion is not sufficient for this
3872 ;; because untabify operation moves point
3873 (set-marker-insertion-type old-point t)
3874 (unwind-protect
3875 (progn
3876 (while
3877 (progn
3878 (table-forward-cell 1 nil 'unrecognize)
3879 (let ((cell (table--probe-cell)))
3880 (if (or (null table-lu)
3881 (< (car cell) table-lu))
3882 (setq table-lu (car cell)))
3883 (if (or (null table-rb)
3884 (> (cdr cell) table-rb))
3885 (setq table-rb (cdr cell)))
3886 (and cell (not (equal cell origin-cell))))))
3887 (let* ((lu-coord (table--get-coordinate table-lu))
3888 (rb-coord (table--get-coordinate table-rb))
3889 (lu (table--goto-coordinate (table--offset-coordinate lu-coord '(-1 . -1)))))
3890 (table--spacify-frame)
3891 (setcdr rb-coord (1+ (cdr rb-coord)))
3892 (delete-rectangle lu (table--goto-coordinate (cons (car lu-coord) (cdr rb-coord))))
3893 (table--remove-eol-spaces
3894 (table--goto-coordinate (cons 0 (1- (cdr lu-coord))))
3895 (table--goto-coordinate rb-coord) nil t)))
3896 (goto-char old-point))))))
3898 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3900 ;; Worker functions (executed implicitly)
3903 (defun table--make-cell-map ()
3904 "Make the table cell keymap if it does not exist yet."
3905 ;; this is irrelevant to keymap but good place to make sure to be executed
3906 (table--update-cell-face)
3907 (unless table-cell-map
3908 (let ((map (make-sparse-keymap))
3909 (remap-alist table-command-remap-alist))
3910 ;; table-command-prefix mode specific bindings
3911 (if (vectorp table-command-prefix)
3912 (mapc (lambda (binding)
3913 (let ((seq (copy-sequence (car binding))))
3914 (and (vectorp seq)
3915 (listp (aref seq 0))
3916 (eq (car (aref seq 0)) 'control)
3917 (progn
3918 (aset seq 0 (cadr (aref seq 0)))
3919 (define-key map (vconcat table-command-prefix seq) (cdr binding))))))
3920 table-cell-bindings))
3921 ;; shorthand control bindings
3922 (mapc (lambda (binding)
3923 (define-key map (car binding) (cdr binding)))
3924 table-cell-bindings)
3925 ;; remap normal commands to table specific version
3926 (while remap-alist
3927 (define-key map (vector 'remap (caar remap-alist)) (cdar remap-alist))
3928 (setq remap-alist (cdr remap-alist)))
3930 (setq table-cell-map map)
3931 (fset 'table-cell-map map)))
3932 ;; add menu for table cells
3933 (unless table-disable-menu
3934 (easy-menu-define table-cell-menu-map table-cell-map "Table cell menu" table-cell-menu)
3935 (if (featurep 'xemacs)
3936 (easy-menu-add table-cell-menu)))
3937 (run-hooks 'table-cell-map-hook))
3939 ;; Create the keymap after running the user init file so that the user
3940 ;; modification to the global-map is accounted.
3941 (add-hook 'after-init-hook 'table--make-cell-map t)
3943 (defun *table--cell-self-insert-command ()
3944 "Table cell version of `self-insert-command'."
3945 (interactive "*")
3946 (let ((char (table--unibyte-char-to-multibyte last-command-char)))
3947 (if (eq buffer-undo-list t) nil
3948 (if (not (eq last-command this-command))
3949 (setq table-cell-self-insert-command-count 0)
3950 (if (car buffer-undo-list) nil
3951 (if (>= table-cell-self-insert-command-count 19)
3952 (setq table-cell-self-insert-command-count 0)
3953 (setq buffer-undo-list (cdr buffer-undo-list))
3954 (setq table-cell-self-insert-command-count (1+ table-cell-self-insert-command-count))))))
3955 (table--cell-insert-char char overwrite-mode)))
3957 (defun *table--cell-delete-backward-char (n)
3958 "Table cell version of `delete-backward-char'."
3959 (interactive "*p")
3960 (*table--cell-delete-char (- n)))
3962 (defun *table--cell-newline (&optional indent)
3963 "Table cell version of `newline'."
3964 (interactive "*")
3965 (table-with-cache-buffer
3966 (let ((column (current-column)))
3967 (insert ?\n)
3968 (if indent (indent-to-column column))
3969 ;; fill only when at the beginning of paragraph
3970 (if (= (point)
3971 (save-excursion
3972 (forward-paragraph -1)
3973 (if (looking-at "\\s *$")
3974 (forward-line 1))
3975 (point)))
3976 nil ; yes, at the beginning of the paragraph
3977 (setq table-inhibit-auto-fill-paragraph t)))))
3979 (defun *table--cell-open-line (n)
3980 "Table cell version of `open-line'."
3981 (interactive "*p")
3982 (table-with-cache-buffer
3983 (save-excursion
3984 (insert (make-string n ?\n))
3985 (table--fill-region (point) (point))
3986 (setq table-inhibit-auto-fill-paragraph t))))
3988 (defun *table--cell-newline-and-indent ()
3989 "Table cell version of `newline-and-indent'."
3990 (interactive)
3991 (*table--cell-newline t))
3993 (defun *table--cell-delete-char (n)
3994 "Table cell version of `delete-char'."
3995 (interactive "*p")
3996 (let ((overwrite overwrite-mode))
3997 (table-with-cache-buffer
3998 (if (and overwrite (< n 0))
3999 (progn
4000 (while (not (zerop n))
4001 (let ((coordinate (table--get-coordinate)))
4002 (if (zerop (car coordinate))
4003 (unless (zerop (cdr coordinate))
4004 (table--goto-coordinate (cons (1- table-cell-info-width) (1- (cdr coordinate))))
4005 (unless (eolp)
4006 (delete-char 1)))
4007 (delete-char -1)
4008 (insert ?\s)
4009 (forward-char -1)))
4010 (setq n (1+ n)))
4011 (setq table-inhibit-auto-fill-paragraph t))
4012 (let ((coordinate (table--get-coordinate))
4013 (end-marker (copy-marker (+ (point) n)))
4014 (deleted))
4015 (if (or (< end-marker (point-min))
4016 (> end-marker (point-max))) nil
4017 (table--remove-eol-spaces (point-min) (point-max))
4018 (setq deleted (buffer-substring (point) end-marker))
4019 (delete-char n)
4020 ;; in fixed width mode when two lines are concatenated
4021 ;; remove continuation character if there is one.
4022 (and table-fixed-width-mode
4023 (string-match "^\n" deleted)
4024 (equal (char-before) table-word-continuation-char)
4025 (delete-char -2))
4026 ;; see if the point is placed at the right tip of the previous
4027 ;; blank line, if so get rid of the preceding blanks.
4028 (if (and (not (bolp))
4029 (/= (cdr coordinate) (cdr (table--get-coordinate)))
4030 (let ((end (point)))
4031 (save-excursion
4032 (beginning-of-line)
4033 (re-search-forward "\\s +" end t)
4034 (= (point) end))))
4035 (replace-match ""))
4036 ;; do not fill the paragraph if the point is already at the end
4037 ;; of this paragraph and is following a blank character
4038 ;; (otherwise the filling squeezes the preceding blanks)
4039 (if (and (looking-at "\\s *$")
4040 (or (bobp)
4041 (save-excursion
4042 (backward-char)
4043 (looking-at "\\s "))))
4044 (setq table-inhibit-auto-fill-paragraph t))
4046 (set-marker end-marker nil))))))
4048 (defun *table--cell-quoted-insert (arg)
4049 "Table cell version of `quoted-insert'."
4050 (interactive "*p")
4051 (let ((char (table--unibyte-char-to-multibyte (read-quoted-char))))
4052 (while (> arg 0)
4053 (table--cell-insert-char char nil)
4054 (setq arg (1- arg)))))
4056 (defun *table--cell-describe-mode ()
4057 "Table cell version of `describe-mode'."
4058 (interactive)
4059 (if (not (table--point-in-cell-p))
4060 (call-interactively 'describe-mode)
4061 (with-output-to-temp-buffer "*Help*"
4062 (princ "Table mode: (in ")
4063 (princ (format-mode-line mode-name nil nil (current-buffer)))
4064 (princ " mode)
4066 Table is not a mode technically. You can regard it as a pseudo mode
4067 which exists locally within a buffer. It overrides some standard
4068 editing behaviors. Editing operations in a table produces confined
4069 effects to the current cell. It may grow the cell horizontally and/or
4070 vertically depending on the newly entered or deleted contents of the
4071 cell, and also depending on the current mode of cell.
4073 In the normal mode the table preserves word continuity. Which means
4074 that a word never gets folded into multiple lines. For this purpose
4075 table will occasionally grow the cell width. On the other hand, when
4076 in a fixed width mode all cell width are fixed. When a word can not
4077 fit in the cell width the word is folded into the next line. The
4078 folded location is marked by a continuation character which is
4079 specified in the variable `table-word-continuation-char'.
4081 (print-help-return-message))))
4083 (defun *table--cell-describe-bindings ()
4084 "Table cell version of `describe-bindings'."
4085 (interactive)
4086 (if (not (table--point-in-cell-p))
4087 (call-interactively 'describe-bindings)
4088 (with-output-to-temp-buffer "*Help*"
4089 (princ "Table Bindings:
4090 key binding
4091 --- -------
4094 (mapc (lambda (binding)
4095 (princ (format "%-16s%s\n"
4096 (key-description (car binding))
4097 (cdr binding))))
4098 table-cell-bindings)
4099 (print-help-return-message))))
4101 (defun *table--cell-dabbrev-expand (arg)
4102 "Table cell version of `dabbrev-expand'."
4103 (interactive "*P")
4104 (let ((dabbrev-abbrev-char-regexp (concat "[^"
4105 (char-to-string table-cell-vertical-char)
4106 (char-to-string table-cell-intersection-char)
4107 " \n]")))
4108 (table-with-cache-buffer
4109 (dabbrev-expand arg))))
4111 (defun *table--cell-dabbrev-completion (&optional arg)
4112 "Table cell version of `dabbrev-completion'."
4113 (interactive "*P")
4114 (error "`dabbrev-completion' is incompatible with table")
4115 (let ((dabbrev-abbrev-char-regexp (concat "[^"
4116 (char-to-string table-cell-vertical-char)
4117 (char-to-string table-cell-intersection-char)
4118 " \n]")))
4119 (table-with-cache-buffer
4120 (dabbrev-completion arg))))
4122 (defun *table--present-cell-popup-menu (event)
4123 "Present and handle cell popup menu."
4124 (interactive "e")
4125 (unless table-disable-menu
4126 (select-window (posn-window (event-start event)))
4127 (goto-char (posn-point (event-start event)))
4128 (let ((item-list (x-popup-menu event table-cell-menu-map))
4129 (func table-cell-menu-map))
4130 (while item-list
4131 (setq func (nth 3 (assoc (car item-list) func)))
4132 (setq item-list (cdr item-list)))
4133 (if (and (symbolp func) (fboundp func))
4134 (call-interactively func)))))
4136 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4138 ;; Cell updating functions
4141 (defun table--update-cell (&optional now)
4142 "Update the table cell contents.
4143 When the optional parameter NOW is nil it only sets up the update
4144 timer. If it is non-nil the function copies the contents of the cell
4145 cache buffer into the designated cell in the table buffer."
4146 (if (null table-update-timer) nil
4147 (table--cancel-timer table-update-timer)
4148 (setq table-update-timer nil))
4149 (if (or (not now)
4150 (and (boundp 'quail-converting)
4151 quail-converting) ;; defer operation while current quail work is not finished.
4152 (and (boundp 'quail-translating)
4153 quail-translating))
4154 (setq table-update-timer
4155 (table--set-timer table-time-before-update
4156 (function table--update-cell)
4157 'now))
4158 (save-current-buffer
4159 (set-buffer table-cell-buffer)
4160 (let ((cache-buffer (get-buffer-create table-cache-buffer-name))
4161 (org-coord (table--get-coordinate))
4162 (in-cell (equal (table--cell-to-coord (table--probe-cell))
4163 (cons table-cell-info-lu-coordinate table-cell-info-rb-coordinate)))
4164 rectangle)
4165 (set-buffer cache-buffer)
4166 (setq rectangle
4167 (extract-rectangle
4169 (table--goto-coordinate (cons table-cell-info-width (1- table-cell-info-height)))))
4170 (set-buffer table-cell-buffer)
4171 (delete-rectangle (table--goto-coordinate table-cell-info-lu-coordinate)
4172 (table--goto-coordinate table-cell-info-rb-coordinate))
4173 (table--goto-coordinate table-cell-info-lu-coordinate)
4174 (table--insert-rectangle rectangle)
4175 (let* ((cell (table--probe-cell))) ; must probe again in case of wide characters
4176 (table--put-cell-property cell)
4177 (table--put-cell-justify-property cell table-cell-info-justify)
4178 (table--put-cell-valign-property cell table-cell-info-valign))
4179 (table--goto-coordinate
4180 (if in-cell
4181 (table--transcoord-cache-to-table table-cell-cache-point-coordinate)
4182 org-coord))))
4183 ;; simulate undo behavior under overwrite-mode
4184 (if (and overwrite-mode (not (eq buffer-undo-list t)))
4185 (setq buffer-undo-list (cons nil buffer-undo-list)))))
4187 (defun table--update-cell-widened (&optional now)
4188 "Update the contents of the cells that are affected by widening operation."
4189 (if (null table-widen-timer) nil
4190 (table--cancel-timer table-widen-timer)
4191 (setq table-widen-timer nil))
4192 (if (not now)
4193 (setq table-widen-timer
4194 (table--set-timer (+ table-time-before-update table-time-before-reformat)
4195 (function table--update-cell-widened)
4196 'now))
4197 (save-current-buffer
4198 (if table-update-timer
4199 (table--update-cell 'now))
4200 (set-buffer table-cell-buffer)
4201 (let* ((current-coordinate (table--get-coordinate))
4202 (current-cell-coordinate (table--cell-to-coord (table--probe-cell)))
4203 (cell-coord-list (progn
4204 (table--goto-coordinate table-cell-info-lu-coordinate)
4205 (table--cell-list-to-coord-list (table--vertical-cell-list)))))
4206 (while cell-coord-list
4207 (let* ((cell-coord (prog1 (car cell-coord-list) (setq cell-coord-list (cdr cell-coord-list))))
4208 (currentp (equal cell-coord current-cell-coordinate)))
4209 (if currentp (table--goto-coordinate current-coordinate)
4210 (table--goto-coordinate (car cell-coord)))
4211 (table-recognize-cell 'froce)
4212 (let ((table-inhibit-update t))
4213 (table-with-cache-buffer
4214 (let ((sticky (and currentp
4215 (save-excursion
4216 (unless (bolp) (forward-char -1))
4217 (looking-at ".*\\S ")))))
4218 (table--fill-region (point-min) (point-max))
4219 (if sticky
4220 (setq current-coordinate (table--transcoord-cache-to-table))))))
4221 (table--update-cell 'now)
4223 (table--goto-coordinate current-coordinate)
4224 (table-recognize-cell 'froce)))))
4226 (defun table--update-cell-heightened (&optional now)
4227 "Update the contents of the cells that are affected by heightening operation."
4228 (if (null table-heighten-timer) nil
4229 (table--cancel-timer table-heighten-timer)
4230 (setq table-heighten-timer nil))
4231 (if (not now)
4232 (setq table-heighten-timer
4233 (table--set-timer (+ table-time-before-update table-time-before-reformat)
4234 (function table--update-cell-heightened)
4235 'now))
4236 (save-current-buffer
4237 (if table-update-timer
4238 (table--update-cell 'now))
4239 (if table-widen-timer
4240 (table--update-cell-widened 'now))
4241 (set-buffer table-cell-buffer)
4242 (let* ((current-coordinate (table--get-coordinate))
4243 (current-cell-coordinate (table--cell-to-coord (table--probe-cell)))
4244 (cell-coord-list (progn
4245 (table--goto-coordinate table-cell-info-lu-coordinate)
4246 (table--cell-list-to-coord-list (table--horizontal-cell-list)))))
4247 (while cell-coord-list
4248 (let* ((cell-coord (prog1 (car cell-coord-list) (setq cell-coord-list (cdr cell-coord-list))))
4249 (currentp (equal cell-coord current-cell-coordinate)))
4250 (if currentp (table--goto-coordinate current-coordinate)
4251 (table--goto-coordinate (car cell-coord)))
4252 (table-recognize-cell 'froce)
4253 (let ((table-inhibit-update t))
4254 (table-with-cache-buffer
4255 (let ((sticky (and currentp
4256 (save-excursion
4257 (unless (bolp) (forward-char -1))
4258 (looking-at ".*\\S ")))))
4259 (table--valign)
4260 (if sticky
4261 (setq current-coordinate (table--transcoord-cache-to-table))))))
4262 (table--update-cell 'now)
4264 (table--goto-coordinate current-coordinate)
4265 (table-recognize-cell 'froce)))))
4267 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4269 ;; Service functions (for external packages)
4272 (defun table-goto-top-left-corner ()
4273 "Move point to top left corner of the current table and return the char position."
4274 (table--goto-coordinate
4275 (cons
4276 (1- (car (table--get-coordinate (car (table--horizontal-cell-list t t)))))
4277 (1- (cdr (table--get-coordinate (car (table--vertical-cell-list t t))))))))
4279 (defun table-goto-top-right-corner ()
4280 "Move point to top right corner of the current table and return the char position."
4281 (table--goto-coordinate
4282 (cons
4283 (car (table--get-coordinate (cdr (table--horizontal-cell-list nil t))))
4284 (1- (cdr (table--get-coordinate (car (table--vertical-cell-list t t))))))))
4286 (defun table-goto-bottom-left-corner ()
4287 "Move point to bottom left corner of the current table and return the char position."
4288 (table--goto-coordinate
4289 (cons
4290 (1- (car (table--get-coordinate (car (table--horizontal-cell-list t t)))))
4291 (1+ (cdr (table--get-coordinate (cdr (table--vertical-cell-list nil t))))))))
4293 (defun table-goto-bottom-right-corner ()
4294 "Move point to bottom right corner of the current table and return the char position."
4295 (table--goto-coordinate
4296 (cons
4297 (car (table--get-coordinate (cdr (table--horizontal-cell-list nil t))))
4298 (1+ (cdr (table--get-coordinate (cdr (table--vertical-cell-list nil t))))))))
4300 (defun table-call-interactively (function &optional recoard-flag keys)
4301 "Call FUNCTION, or a table version of it if applicable.
4302 See `call-interactively' for full description of the arguments."
4303 (let ((table-func (intern-soft (format "*table--cell-%s" function))))
4304 (call-interactively
4305 (if (and table-func
4306 (table--point-in-cell-p))
4307 table-func
4308 function) recoard-flag keys)))
4310 (defun table-funcall (function &rest arguments)
4311 "Call FUNCTION, or a table version of it if applicable.
4312 See `funcall' for full description of the arguments."
4313 (let ((table-func (intern-soft (format "*table--cell-%s" function))))
4314 (apply
4315 (if (and table-func
4316 (table--point-in-cell-p))
4317 table-func
4318 function)
4319 arguments)))
4321 (defmacro table-apply (function &rest arguments)
4322 "Call FUNCTION, or a table version of it if applicable.
4323 See `apply' for full description of the arguments."
4324 (let ((table-func (make-symbol "table-func")))
4325 `(let ((,table-func (intern-soft (format "*table--cell-%s" ,function))))
4326 (apply
4327 (if (and ,table-func
4328 (table--point-in-cell-p))
4329 ,table-func
4330 ,function)
4331 ,@arguments))))
4333 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4335 ;; Utility functions
4338 (defun table--read-from-minibuffer (prompt-history)
4339 "A wrapper to `read-from-minibuffer'.
4340 PROMPT-HISTORY is a cons cell which car is the prompt string and the
4341 cdr is the history symbol."
4342 (let ((default (car (symbol-value (cdr prompt-history)))))
4343 (read-from-minibuffer
4344 (format "%s (default %s): " (car prompt-history) default)
4345 "" nil nil (cdr prompt-history) default))
4346 (and (featurep 'xemacs)
4347 (equal (car (symbol-value (cdr prompt-history))) "")
4348 (set (cdr prompt-history)
4349 (cdr (symbol-value (cdr prompt-history)))))
4350 (car (symbol-value (cdr prompt-history))))
4352 (defun table--unibyte-char-to-multibyte (char)
4353 "Convert CHAR by `unibyte-char-to-multibyte' when possible and necessary."
4354 ;; This part is take from `quoted-insert'.
4355 ;; Assume character codes 0240 - 0377 stand for characters in some
4356 ;; single-byte character set, and convert them to Emacs
4357 ;; characters.
4358 (if (and enable-multibyte-characters
4359 (fboundp 'unibyte-char-to-multibyte)
4360 (>= char ?\240)
4361 (<= char ?\377))
4362 (unibyte-char-to-multibyte char)
4363 char))
4365 (defun table--buffer-substring-and-trim (beg end)
4366 "Extract buffer substring and remove blanks from front and the rear of it."
4367 (save-excursion
4368 (save-restriction
4369 (narrow-to-region (goto-char beg) end)
4370 (if (re-search-forward "\\s *")
4371 (setq beg (match-end 0)))
4372 (if (re-search-forward "\\s *\\'" end t)
4373 (setq end (match-beginning 0)))
4374 (table--remove-cell-properties
4375 0 (- end beg)
4376 (buffer-substring beg end)))))
4378 (defun table--valign ()
4379 "Vertically align the cache cell contents.
4380 Current buffer must be the cache buffer at the entry to this function.
4381 Returns the coordinate of the final point location."
4382 (if (or (null table-cell-info-valign)
4383 (eq table-cell-info-valign 'none))
4384 (table--get-coordinate)
4385 (let ((saved-point (point-marker)))
4386 ;;(set-marker-insertion-type saved-point t)
4387 (goto-char (point-min))
4388 (let* ((from (and (re-search-forward "^.*\\S " nil t)
4389 (table--current-line)))
4390 (to (let ((tmp from))
4391 (while (re-search-forward "^.*\\S " nil t)
4392 (setq tmp (table--current-line)))
4393 tmp))
4394 (content-height (and from to (1+ (- to from)))))
4395 (unless (null content-height)
4396 (goto-char (point-min))
4397 (if (looking-at "\\s *\n")
4398 (replace-match ""))
4399 (cond ((eq table-cell-info-valign 'middle)
4400 (insert (make-string (/ (- table-cell-info-height content-height) 2) ?\n)))
4401 ((eq table-cell-info-valign 'bottom)
4402 (insert (make-string (- table-cell-info-height content-height) ?\n))))
4403 (table--goto-coordinate (cons table-cell-info-width (1- table-cell-info-height)))
4404 (if (re-search-forward "\\s +\\'" nil t)
4405 (replace-match ""))))
4406 (goto-char saved-point)
4407 (set-marker saved-point nil)
4408 (let ((coord (table--get-coordinate)))
4409 (unless (< (cdr coord) table-cell-info-height)
4410 (setcdr coord (1- table-cell-info-height))
4411 (table--goto-coordinate coord))
4412 coord))))
4414 (defun table--query-justification ()
4415 (barf-if-buffer-read-only)
4416 (let* ((completion-ignore-case t)
4417 (default (car table-justify-history)))
4418 (intern (downcase (completing-read
4419 (format "Justify (default %s): " default)
4420 '(("left") ("center") ("right") ("top") ("middle") ("bottom") ("none"))
4421 nil t nil 'table-justify-history default)))))
4423 (defun table--spacify-frame ()
4424 "Spacify table frame.
4425 Replace frame characters with spaces."
4426 (let ((frame-char
4427 (append (string-to-list table-cell-horizontal-chars)
4428 (list table-cell-intersection-char table-cell-vertical-char))))
4429 (while
4430 (progn
4431 (cond
4432 ((eq (char-after) table-cell-intersection-char)
4433 (save-excursion
4434 (let ((col (current-column)))
4435 (and (zerop (forward-line 1))
4436 (zerop (current-column))
4437 (move-to-column col)
4438 (table--spacify-frame))))
4439 (delete-char 1)
4440 (insert-before-markers ?\s))
4441 ((table--cell-horizontal-char-p (char-after))
4442 (while (progn
4443 (delete-char 1)
4444 (insert-before-markers ?\s)
4445 (table--cell-horizontal-char-p (char-after)))))
4446 ((eq (char-after) table-cell-vertical-char)
4447 (while (let ((col (current-column)))
4448 (delete-char 1)
4449 (insert-before-markers ?\s)
4450 (and (zerop (forward-line 1))
4451 (zerop (current-column))
4452 (move-to-column col)
4453 (eq (char-after) table-cell-vertical-char))))))
4454 (memq (char-after) frame-char)))))
4456 (defun table--remove-blank-lines (n)
4457 "Delete N blank lines from the current line.
4458 For adjusting below area of the table when the table is shortened."
4459 (move-to-column 0)
4460 (let ((first-blank t))
4461 (while (> n 0)
4462 (setq n (1- n))
4463 (cond ((looking-at "\\s *\\'")
4464 (delete-region (match-beginning 0) (match-end 0))
4465 (setq n 0))
4466 ((and (looking-at "\\([ \t]*\n[ \t]*\\)\n") first-blank)
4467 (delete-region (match-beginning 1) (match-end 1)))
4468 ((looking-at "[ \t]*$")
4469 (delete-region (match-beginning 0) (match-end 0))
4470 (forward-line 1))
4472 (setq first-blank nil)
4473 (forward-line 1))))))
4475 (defun table--uniform-list-p (l)
4476 "Return nil when LIST contains non equal elements. Otherwise return t."
4477 (if (null l) t
4478 (catch 'end
4479 (while (cdr l)
4480 (if (not (equal (car l) (cadr l))) (throw 'end nil))
4481 (setq l (cdr l)))
4482 t)))
4484 (defun table--detect-cell-alignment (cell)
4485 "Detect CELL contents alignment.
4486 Guess CELL contents alignment both horizontally and vertically by
4487 looking at the appearance of the CELL contents."
4488 (let ((cell-contents (extract-rectangle (car cell) (cdr cell)))
4489 (left-margin 0)
4490 (right-margin 0)
4491 (top-margin 0)
4492 (bottom-margin 0)
4493 (margin-diff 0)
4494 (margin-info-available nil)
4495 justify valign)
4496 (with-temp-buffer
4497 (table--insert-rectangle cell-contents)
4498 ;; determine the horizontal justification
4499 (goto-char (point-min))
4500 (while (re-search-forward "^\\( *\\).*[^ \n]\\( *\\)$" nil t)
4501 (setq margin-info-available t)
4502 (let* ((lm (- (match-end 1) (match-beginning 1)))
4503 (rm (- (match-end 2) (match-beginning 2)))
4504 (md (abs (- lm rm))))
4505 (if (> lm left-margin)
4506 (setq left-margin lm))
4507 (if (> rm right-margin)
4508 (setq right-margin rm))
4509 (if (> md margin-diff)
4510 (setq margin-diff md))))
4511 (setq justify
4512 (cond
4513 ((and margin-info-available
4514 (<= margin-diff 1)
4515 (> left-margin 0)) 'center)
4516 ((and margin-info-available
4517 (zerop right-margin)
4518 (> left-margin 0)) 'right)
4519 (t 'left)))
4520 ;; determine the vertical justification
4521 (goto-char (point-min))
4522 (if (and (re-search-forward "\\s *\\S " nil t)
4523 (/= (match-beginning 0) (match-end 0)))
4524 (setq top-margin (1- (count-lines (match-beginning 0) (match-end 0)))))
4525 (if (and (re-search-forward "\\s *\\'" nil t)
4526 (/= (match-beginning 0) (match-end 0)))
4527 (setq bottom-margin (1- (count-lines (match-beginning 0) (match-end 0)))))
4528 (setq valign
4529 (cond
4530 ((and (> top-margin 0)
4531 (> bottom-margin 0)
4532 (<= (abs (- top-margin bottom-margin)) 1)) 'middle)
4533 ((and (> top-margin 0)
4534 (zerop bottom-margin)) 'bottom)
4535 (t nil))))
4536 (table--put-cell-justify-property cell justify)
4537 (table--put-cell-valign-property cell valign)))
4539 (defun table--string-to-number-list (str)
4540 "Return a list of numbers in STR."
4541 (let ((idx 0)
4542 (nl nil))
4543 (while (string-match "[-0-9.]+" str idx)
4544 (setq idx (match-end 0))
4545 (setq nl (cons (string-to-number (match-string 0 str)) nl)))
4546 (nreverse nl)))
4548 (defun table--justify-cell-contents (justify &optional paragraph)
4549 "Justify the current cell contents.
4550 JUSTIFY is a symbol 'left, 'center or 'right for horizontal, or 'top,
4551 'middle, 'bottom or 'none for vertical. When PARAGRAPH is non-nil the
4552 justify operation is limited to the current paragraph."
4553 (table-with-cache-buffer
4554 (let ((beg (point-min))
4555 (end (point-max-marker))
4556 (fill-column table-cell-info-width)
4557 (adaptive-fill-mode nil)
4558 (valign-symbols '(top middle bottom none)))
4559 (unless paragraph
4560 (if (memq justify valign-symbols)
4561 (setq table-cell-info-valign
4562 (if (eq justify 'none) nil justify))
4563 (setq table-cell-info-justify justify)))
4564 (save-excursion
4565 (if paragraph
4566 (let ((paragraph-start "\n"))
4567 (forward-paragraph)
4568 (or (bolp) (newline 1))
4569 (set-marker end (point))
4570 (setq beg (progn (forward-paragraph -1) (point)))))
4571 (if (memq justify valign-symbols)
4572 (table--valign)
4573 (table--remove-eol-spaces beg end 'bol)
4574 (let ((paragraph-start table-paragraph-start))
4575 (fill-region beg end table-cell-info-justify))))
4576 (setq table-inhibit-auto-fill-paragraph t)
4577 (set-marker end nil)))
4578 (table--update-cell 'now))
4580 (defun table--horizontally-shift-above-and-below (columns-to-extend top-to-bottom-coord-list)
4581 "Horizontally shift outside contents right above and right below of the table.
4582 This function moves the surrounding text outside of the table so that
4583 they match the horizontal growth/shrink of the table. It also
4584 untabify the shift affected area including the right side of the table
4585 so that tab related uneven shifting is avoided. COLUMNS-TO-EXTEND
4586 specifies the number of columns the table grows, or shrinks if
4587 negative. TOP-TO-BOTTOM-COORD-LIST is the vertical cell coordinate
4588 list. This list can be any vertical list within the table."
4589 (save-excursion
4590 (let (beg-coord end-coord)
4591 (table--goto-coordinate (caar top-to-bottom-coord-list))
4592 (let* ((cell (table--horizontal-cell-list nil 'first-only 'top))
4593 (coord (cons (car (table--get-coordinate (cdr cell)))
4594 (cdr (table--get-coordinate (car cell))))))
4595 (setcar coord (1+ (car coord)))
4596 (setcdr coord (- (cdr coord) 2))
4597 (setq beg-coord (cons (car coord) (1+ (cdr coord))))
4598 (while (and (table--goto-coordinate coord 'no-extension)
4599 (not (looking-at "\\s *$")))
4600 (if (< columns-to-extend 0)
4601 (progn
4602 (table--untabify-line)
4603 (delete-char columns-to-extend))
4604 (table--untabify-line (point))
4605 (insert (make-string columns-to-extend ?\s)))
4606 (setcdr coord (1- (cdr coord)))))
4607 (table--goto-coordinate (caar (last top-to-bottom-coord-list)))
4608 (let ((coord (table--get-coordinate (cdr (table--horizontal-cell-list nil 'first-only 'bottom)))))
4609 (setcar coord (1+ (car coord)))
4610 (setcdr coord (+ (cdr coord) 2))
4611 (setq end-coord (cons (car coord) (1- (cdr coord))))
4612 (while (and (table--goto-coordinate coord 'no-extension)
4613 (not (looking-at "\\s *$")))
4614 (if (< columns-to-extend 0)
4615 (progn
4616 (table--untabify-line)
4617 (delete-char columns-to-extend))
4618 (table--untabify-line (point))
4619 (insert (make-string columns-to-extend ?\s)))
4620 (setcdr coord (1+ (cdr coord)))))
4621 (while (<= (cdr beg-coord) (cdr end-coord))
4622 (table--untabify-line (table--goto-coordinate beg-coord 'no-extension))
4623 (setcdr beg-coord (1+ (cdr beg-coord)))))))
4625 (defun table--create-growing-space-below (lines-to-extend left-to-right-coord-list bottom-border-y)
4626 "Create growing space below the table.
4627 This function creates growing space below the table slightly
4628 intelligent fashion. Following is the cases it handles for each
4629 growing line:
4630 1. When the first line below the table is a complete blank line it
4631 inserts a blank line.
4632 2. When the line starts with a prefix that matches the prefix of the
4633 bottom line of the table it inserts a line consisting of prefix alone.
4634 3. Otherwise it deletes the rectangular contents where table will
4635 grow into."
4636 (save-excursion
4637 (let ((i 0)
4638 (prefix (and (table--goto-coordinate (cons 0 bottom-border-y))
4639 (re-search-forward
4640 ".*\\S "
4641 (save-excursion
4642 (table--goto-coordinate
4643 (cons (1- (caar (car left-to-right-coord-list))) bottom-border-y)))
4645 (buffer-substring (match-beginning 0) (match-end 0)))))
4646 (while (< i lines-to-extend)
4647 (let ((y (+ i bottom-border-y 1)))
4648 (table--goto-coordinate (cons 0 y))
4649 (cond
4650 ((looking-at "\\s *$")
4651 (insert ?\n))
4652 ((and prefix (looking-at (concat (regexp-quote prefix) "\\s *$")))
4653 (insert prefix ?\n))
4655 (delete-rectangle
4656 (table--goto-coordinate (cons (1- (caar (car left-to-right-coord-list))) y))
4657 (table--goto-coordinate (cons (1+ (cadr (car (last left-to-right-coord-list)))) y))))))
4658 (setq i (1+ i))))))
4660 (defun table--untabify-line (&optional from)
4661 "Untabify current line.
4662 Unlike save-excursion this guarantees preserving the cursor location
4663 even when the point is on a tab character which is to be removed.
4664 Optional FROM narrows the subject operation from this point to the end
4665 of line."
4666 (let ((current-coordinate (table--get-coordinate)))
4667 (table--untabify (or from (progn (beginning-of-line) (point)))
4668 (progn (end-of-line) (point)))
4669 (table--goto-coordinate current-coordinate)))
4671 (defun table--untabify (beg end)
4672 "Wrapper to raw untabify."
4673 (untabify beg end)
4674 (if (featurep 'xemacs)
4675 ;; Cancel strange behavior of xemacs
4676 (message "")))
4678 (defun table--multiply-string (string multiplier)
4679 "Multiply string and return it."
4680 (let ((ret-str ""))
4681 (while (> multiplier 0)
4682 (setq ret-str (concat ret-str string))
4683 (setq multiplier (1- multiplier)))
4684 ret-str))
4686 (defun table--line-column-position (line column)
4687 "Return the location of LINE forward at COLUMN."
4688 (save-excursion
4689 (forward-line line)
4690 (move-to-column column)
4691 (point)))
4693 (defun table--row-column-insertion-point-p (&optional columnp)
4694 "Return non-nil if it makes sense to insert a row or a column at point."
4695 (and (not buffer-read-only)
4696 (or (get-text-property (point) 'table-cell)
4697 (let ((column (current-column)))
4698 (if columnp
4699 (or (text-property-any (line-beginning-position 0)
4700 (table--line-column-position -1 column)
4701 'table-cell t)
4702 (text-property-any (line-beginning-position) (point) 'table-cell t)
4703 (text-property-any (line-beginning-position 2)
4704 (table--line-column-position 1 column)
4705 'table-cell t))
4706 (text-property-any (table--line-column-position -2 column)
4707 (table--line-column-position -2 (+ 2 column))
4708 'table-cell t))))))
4710 (defun table--find-row-column (&optional columnp no-error)
4711 "Search table and return a cell coordinate list of row or column."
4712 (let ((current-coordinate (table--get-coordinate)))
4713 (catch 'end
4714 (catch 'error
4715 (let ((coord (table--get-coordinate)))
4716 (while
4717 (progn
4718 (if columnp (setcar coord (1- (car coord)))
4719 (setcdr coord (1- (cdr coord))))
4720 (>= (if columnp (car coord) (cdr coord)) 0))
4721 (while (progn
4722 (table--goto-coordinate coord 'no-extension 'no-tab-expansion)
4723 (not (looking-at (format "[%s%c%c]"
4724 table-cell-horizontal-chars
4725 table-cell-vertical-char
4726 table-cell-intersection-char))))
4727 (if columnp (setcar coord (1- (car coord)))
4728 (setcdr coord (1- (cdr coord))))
4729 (if (< (if columnp (car coord) (cdr coord)) 0)
4730 (throw 'error nil)))
4731 (if (table--probe-cell)
4732 (throw 'end (table--cell-list-to-coord-list (if columnp
4733 (table--vertical-cell-list t nil 'left)
4734 (table--horizontal-cell-list t nil 'top))))
4735 (table--goto-coordinate (table--offset-coordinate coord (if columnp '(0 . 1) '(1 . 0)))
4736 'no-extension 'no-tab-expansion)
4737 (if (table--probe-cell)
4738 (throw 'end (table--cell-list-to-coord-list (if columnp
4739 (table--vertical-cell-list t nil 'left)
4740 (table--horizontal-cell-list t nil 'top)))))))))
4741 (table--goto-coordinate current-coordinate)
4742 (if no-error nil
4743 (error "Table not found")))))
4745 (defun table--min-coord-list (coord-list)
4746 "Return minimum cell dimension of COORD-LIST.
4747 COORD-LIST is a list of coordinate pairs (lu-coord . rb-coord), where
4748 each pair in the list represents a cell. lu-coord is the left upper
4749 coordinate of a cell and rb-coord is the right bottom coordinate of a
4750 cell. A coordinate is a pair of x and y axis coordinate values. The
4751 return value is a cons cell (min-w . min-h), where min-w and min-h are
4752 respectively the minimum width and the minimum height of all the cells
4753 in the list."
4754 (if (null coord-list) nil
4755 (let ((min-width 134217727)
4756 (min-height 134217727))
4757 (while coord-list
4758 (let* ((coord (prog1 (car coord-list) (setq coord-list (cdr coord-list))))
4759 (width (- (cadr coord) (caar coord)))
4760 (height (1+ (- (cddr coord) (cdar coord)))))
4761 (if (< width min-width) (setq min-width width))
4762 (if (< height min-height) (setq min-height height))))
4763 (cons min-width min-height))))
4765 (defun table--cell-can-split-horizontally-p ()
4766 "Test if a cell can split at current location horizontally."
4767 (and (not buffer-read-only)
4768 (let ((point-x (car (table--get-coordinate))))
4769 (table-recognize-cell 'force)
4770 (and (> point-x (car table-cell-info-lu-coordinate))
4771 (<= point-x (1- (car table-cell-info-rb-coordinate)))))))
4773 (defun table--cell-can-split-vertically-p ()
4774 "Test if a cell can split at current location vertically."
4775 (and (not buffer-read-only)
4776 (let ((point-y (cdr (table--get-coordinate))))
4777 (table-recognize-cell 'force)
4778 (and (> point-y (cdr table-cell-info-lu-coordinate))
4779 (<= point-y (cdr table-cell-info-rb-coordinate))))))
4781 (defun table--cell-can-span-p (direction)
4782 "Test if the current cell can span to DIRECTION."
4783 (table-recognize-cell 'force)
4784 (and (not buffer-read-only)
4785 (table--probe-cell)
4786 ;; get two adjacent cells from each corner
4787 (let ((cell (save-excursion
4788 (and
4789 (table--goto-coordinate
4790 (cons (cond ((eq direction 'right) (1+ (car table-cell-info-rb-coordinate)))
4791 ((eq direction 'left) (1- (car table-cell-info-lu-coordinate)))
4792 (t (car table-cell-info-lu-coordinate)))
4793 (cond ((eq direction 'above) (- (cdr table-cell-info-lu-coordinate) 2))
4794 ((eq direction 'below) (+ (cdr table-cell-info-rb-coordinate) 2))
4795 (t (cdr table-cell-info-lu-coordinate)))) 'no-extension)
4796 (table--probe-cell))))
4797 (cell2 (save-excursion
4798 (and
4799 (table--goto-coordinate
4800 (cons (cond ((eq direction 'right) (1+ (car table-cell-info-rb-coordinate)))
4801 ((eq direction 'left) (1- (car table-cell-info-lu-coordinate)))
4802 (t (car table-cell-info-rb-coordinate)))
4803 (cond ((eq direction 'above) (- (cdr table-cell-info-lu-coordinate) 2))
4804 ((eq direction 'below) (+ (cdr table-cell-info-rb-coordinate) 2))
4805 (t (cdr table-cell-info-rb-coordinate)))) 'no-extension)
4806 (table--probe-cell)))))
4807 ;; make sure the two cells exist, and they are identical, that cell's size matches the current one
4808 (and cell
4809 (equal cell cell2)
4810 (if (or (eq direction 'right) (eq direction 'left))
4811 (and (= (cdr (table--get-coordinate (car cell)))
4812 (cdr table-cell-info-lu-coordinate))
4813 (= (cdr (table--get-coordinate (cdr cell)))
4814 (cdr table-cell-info-rb-coordinate)))
4815 (and (= (car (table--get-coordinate (car cell)))
4816 (car table-cell-info-lu-coordinate))
4817 (= (car (table--get-coordinate (cdr cell)))
4818 (car table-cell-info-rb-coordinate))))))))
4820 (defun table--cell-insert-char (char &optional overwrite)
4821 "Insert CHAR inside a table cell."
4822 (let ((delete-selection-p (and (boundp 'delete-selection-mode)
4823 delete-selection-mode
4824 transient-mark-mode mark-active
4825 (not buffer-read-only)))
4826 (mark-coordinate (table--transcoord-table-to-cache (table--get-coordinate (mark t)))))
4827 (table-with-cache-buffer
4828 (and delete-selection-p
4829 (>= (car mark-coordinate) 0)
4830 (<= (car mark-coordinate) table-cell-info-width)
4831 (>= (cdr mark-coordinate) 0)
4832 (<= (cdr mark-coordinate) table-cell-info-height)
4833 (save-excursion
4834 (delete-region (point) (table--goto-coordinate mark-coordinate))))
4835 (if overwrite
4836 (let ((coordinate (table--get-coordinate)))
4837 (setq table-inhibit-auto-fill-paragraph t)
4838 (if (>= (car coordinate) table-cell-info-width)
4839 (if (>= (cdr coordinate) (1- table-cell-info-height))
4840 (insert "\n" char)
4841 (forward-line 1)
4842 (insert char)
4843 (unless (eolp)
4844 (delete-char 1)))
4845 (insert char)
4846 (unless (eolp)
4847 (delete-char 1))))
4848 (if (not (eq char ?\s))
4849 (if char (insert char))
4850 (if (not (looking-at "\\s *$"))
4851 (if (and table-fixed-width-mode
4852 (> (point) 2)
4853 (save-excursion
4854 (forward-char -2)
4855 (looking-at (concat "\\("
4856 (regexp-quote (char-to-string table-word-continuation-char))
4857 "\\)\n"))))
4858 (save-excursion
4859 (replace-match " " nil nil nil 1))
4860 (insert char))
4861 (let ((coordinate (table--get-coordinate)))
4862 (if (< (car coordinate) table-cell-info-width)
4863 (move-to-column (1+ (car coordinate)) t)
4864 (insert (make-string (forward-line 1) ?\n))
4865 (unless (bolp) (insert ?\n))))
4866 (setq table-inhibit-auto-fill-paragraph t))
4867 (save-excursion
4868 (let ((o-point (point)))
4869 (if (and (bolp)
4870 (or (progn
4871 (forward-paragraph)
4872 (forward-paragraph -1)
4873 (= o-point (point)))
4874 (progn
4875 (goto-char o-point)
4876 (forward-line)
4877 (setq o-point (point))
4878 (forward-paragraph)
4879 (forward-paragraph -1)
4880 (= o-point (point)))))
4881 (insert ?\n)))))))))
4883 (defun table--finish-delayed-tasks ()
4884 "Finish all outstanding delayed tasks."
4885 (if table-update-timer
4886 (table--update-cell 'now))
4887 (if table-widen-timer
4888 (table--update-cell-widened 'now))
4889 (if table-heighten-timer
4890 (table--update-cell-heightened 'now)))
4892 (defmacro table--log (&rest body)
4893 "Debug logging macro."
4894 `(with-current-buffer (get-buffer-create "log")
4895 (goto-char (point-min))
4896 (let ((standard-output (current-buffer)))
4897 ,@body)))
4899 (defun table--measure-max-width (&optional unlimited)
4900 "Return maximum width of current buffer.
4901 Normally the current buffer is expected to be already the cache
4902 buffer. The width excludes following spaces at the end of each line.
4903 Unless UNLIMITED is non-nil minimum return value is 1."
4904 (save-excursion
4905 (let ((width 0))
4906 (goto-char (point-min))
4907 (while
4908 (progn
4909 ;; do not count the following white spaces
4910 (re-search-forward "\\s *$")
4911 (goto-char (match-beginning 0))
4912 (if (> (current-column) width)
4913 (setq width (current-column)))
4914 (forward-line)
4915 (not (eobp))))
4916 (if unlimited width
4917 (max 1 width)))))
4919 (defun table--cell-to-coord (cell)
4920 "Create a cell coordinate pair from cell location pair."
4921 (if cell
4922 (cons (table--get-coordinate (car cell))
4923 (table--get-coordinate (cdr cell)))
4924 nil))
4926 (defun table--cell-list-to-coord-list (cell-list)
4927 "Create and return a coordinate list that corresponds to CELL-LIST.
4928 CELL-LIST is a list of location pairs (lu . rb), where each pair
4929 represents a cell in the list. lu is the left upper location and rb
4930 is the right bottom location of a cell. The return value is a list of
4931 coordinate pairs (lu-coord . rb-coord), where lu-coord is the left
4932 upper coordinate and rb-coord is the right bottom coordinate of a
4933 cell."
4934 (let ((coord-list))
4935 (while cell-list
4936 (let ((cell (prog1 (car cell-list) (setq cell-list (cdr cell-list)))))
4937 (setq coord-list
4938 (cons (table--cell-to-coord cell) coord-list))))
4939 (nreverse coord-list)))
4941 (defun table--test-cell-list (&optional horizontal reverse first-only pivot)
4942 "For testing `table--vertical-cell-list' and `table--horizontal-cell-list'."
4943 (let* ((current-coordinate (table--get-coordinate))
4944 (cell-list (if horizontal
4945 (table--horizontal-cell-list reverse first-only pivot)
4946 (table--vertical-cell-list reverse first-only pivot)))
4947 (count 0))
4948 (while cell-list
4949 (let* ((cell (if first-only (prog1 cell-list (setq cell-list nil))
4950 (prog1 (car cell-list) (setq cell-list (cdr cell-list)))))
4951 (dig1-str (format "%1d" (prog1 (% count 10) (setq count (1+ count))))))
4952 (goto-char (car cell))
4953 (table-with-cache-buffer
4954 (while (re-search-forward "." nil t)
4955 (replace-match dig1-str nil nil))
4956 (setq table-inhibit-auto-fill-paragraph t))
4957 (table--finish-delayed-tasks)))
4958 (table--goto-coordinate current-coordinate)))
4960 (defun table--vertical-cell-list (&optional top-to-bottom first-only pivot internal-dir internal-list internal-px)
4961 "Return a vertical cell list from the table.
4962 The return value represents a list of cells including the current cell
4963 that align vertically. Each element of the list is a cons cell (lu
4964 . rb) where lu is the cell's left upper location and rb is the cell's
4965 right bottom location. The cell order in the list is from bottom to
4966 top of the table. If optional argument TOP-TO-BOTTOM is non-nil the
4967 order is reversed as from top to bottom of the table. If optional
4968 argument FIRST-ONLY is non-nil the return value is not a list of cells
4969 but a single cons cell that is the first cell of the list, if the list
4970 had been created. If optional argument PIVOT is a symbol `left' the
4971 vertical cell search is aligned with the left edge of the current
4972 cell, otherwise aligned with the right edge of the current cell. The
4973 arguments INTERNAL-DIR, INTERNAL-LIST and INTERNAL-PX are internal use
4974 only and must not be specified."
4975 (save-excursion
4976 (let* ((cell (table--probe-cell))
4977 (lu-coordinate (table--get-coordinate (car cell)))
4978 (rb-coordinate (table--get-coordinate (cdr cell)))
4979 (px (or internal-px (car (if (eq pivot 'left) lu-coordinate rb-coordinate))))
4980 (ty (- (cdr lu-coordinate) 2))
4981 (by (+ (cdr rb-coordinate) 2)))
4982 ;; in case of finding the the first cell, get the last adding item on the list
4983 (if (and (null internal-dir) first-only) (setq top-to-bottom (null top-to-bottom)))
4984 ;; travel up and process as recursion traces back (reverse order)
4985 (and cell
4986 (or (eq internal-dir 'up) (null internal-dir))
4987 (table--goto-coordinate (cons px (if top-to-bottom by ty)) 'no-extension 'no-tab-expansion)
4988 (setq internal-list (table--vertical-cell-list top-to-bottom first-only nil 'up nil px)))
4989 ;; return the last cell or add this cell to the list
4990 (if first-only (or internal-list cell)
4991 (setq internal-list (if cell (cons cell internal-list) internal-list))
4992 ;; travel down and process as entering each recursion (forward order)
4993 (and cell
4994 (or (eq internal-dir 'down) (null internal-dir))
4995 (table--goto-coordinate (cons px (if top-to-bottom ty by)) 'no-extension 'no-tab-expansion)
4996 (setq internal-list (table--vertical-cell-list top-to-bottom nil nil 'down internal-list px)))
4997 ;; return the result
4998 internal-list))))
5000 (defun table--horizontal-cell-list (&optional left-to-right first-only pivot internal-dir internal-list internal-py)
5001 "Return a horizontal cell list from the table.
5002 The return value represents a list of cells including the current cell
5003 that align horizontally. Each element of the list is a cons cells (lu
5004 . rb) where lu is the cell's left upper location and rb is the cell's
5005 right bottom location. The cell order in the list is from right to
5006 left of the table. If optional argument LEFT-TO-RIGHT is non-nil the
5007 order is reversed as from left to right of the table. If optional
5008 argument FIRST-ONLY is non-nil the return value is not a list of cells
5009 but a single cons cell that is the first cell of the list, if the
5010 list had been created. If optional argument PIVOT is a symbol `top'
5011 the horizontal cell search is aligned with the top edge of the current
5012 cell, otherwise aligned with the bottom edge of the current cell. The
5013 arguments INTERNAL-DIR, INTERNAL-LIST and INTERNAL-PY are internal use
5014 only and must not be specified."
5015 (save-excursion
5016 (let* ((cell (table--probe-cell))
5017 (lu-coordinate (table--get-coordinate (car cell)))
5018 (rb-coordinate (table--get-coordinate (cdr cell)))
5019 (py (or internal-py (if (eq pivot 'top) (cdr lu-coordinate) (1+ (cdr rb-coordinate)))))
5020 (lx (1- (car lu-coordinate)))
5021 (rx (1+ (car rb-coordinate))))
5022 ;; in case of finding the the first cell, get the last adding item on the list
5023 (if (and (null internal-dir) first-only) (setq left-to-right (null left-to-right)))
5024 ;; travel left and process as recursion traces back (reverse order)
5025 (and cell
5026 (or (eq internal-dir 'left) (null internal-dir))
5027 (table--goto-coordinate (cons (if left-to-right rx lx) py) 'no-extension 'no-tab-expansion)
5028 (setq internal-list (table--horizontal-cell-list left-to-right first-only nil 'left nil py)))
5029 ;; return the last cell or add this cell to the list
5030 (if first-only (or internal-list cell)
5031 (setq internal-list (if cell (cons cell internal-list) internal-list))
5032 ;; travel right and process as entering each recursion (forward order)
5033 (and cell
5034 (or (eq internal-dir 'right) (null internal-dir))
5035 (table--goto-coordinate (cons (if left-to-right lx rx) py) 'no-extension 'no-tab-expansion)
5036 (setq internal-list (table--horizontal-cell-list left-to-right nil nil 'right internal-list py)))
5037 ;; return the result
5038 internal-list))))
5040 (defun table--point-in-cell-p (&optional location)
5041 "Return t when point is in a valid table cell in the current buffer.
5042 When optional LOCATION is provided the test is performed at that location."
5043 (and (table--at-cell-p (or location (point)))
5044 (if location
5045 (save-excursion
5046 (goto-char location)
5047 (table--probe-cell))
5048 (table--probe-cell))))
5050 (defun table--region-in-cell-p (beg end)
5051 "Return t when location BEG and END are in a valid table cell in the current buffer."
5052 (and (table--at-cell-p (min beg end))
5053 (save-excursion
5054 (let ((cell-beg (progn (goto-char beg) (table--probe-cell))))
5055 (and cell-beg
5056 (equal cell-beg (progn (goto-char end) (table--probe-cell))))))))
5058 (defun table--at-cell-p (position &optional object at-column)
5059 "Returns non-nil if POSITION has table-cell property in OBJECT.
5060 OBJECT is optional and defaults to the current buffer.
5061 If POSITION is at the end of OBJECT, the value is nil."
5062 (if (and at-column (stringp object))
5063 (setq position (table--str-index-at-column object position)))
5064 (get-text-property position 'table-cell object))
5066 (defun table--probe-cell-left-up ()
5067 "Probe left up corner pattern of a cell.
5068 If it finds a valid corner returns a position otherwise returns nil.
5069 The position is the location before the first cell character.
5070 Focus only on the corner pattern. Further cell validity check is required."
5071 (save-excursion
5072 (let ((vertical-str (regexp-quote (char-to-string table-cell-vertical-char)))
5073 (intersection-str (regexp-quote (char-to-string table-cell-intersection-char)))
5074 (v-border (format "[%c%c]" table-cell-vertical-char table-cell-intersection-char))
5075 (h-border (format "[%s%c]" table-cell-horizontal-chars table-cell-intersection-char))
5076 (limit (save-excursion (beginning-of-line) (point))))
5077 (catch 'end
5078 (while t
5079 (catch 'retry-horizontal
5080 (if (not (search-backward-regexp v-border limit t))
5081 (throw 'end nil))
5082 (save-excursion
5083 (let ((column (current-column)))
5084 (while t
5085 (catch 'retry-vertical
5086 (if (zerop (forward-line -1)) nil (throw 'end nil))
5087 (move-to-column column)
5088 (while (and (looking-at vertical-str)
5089 (= column (current-column)))
5090 (if (zerop (forward-line -1)) nil (throw 'end nil))
5091 (move-to-column column))
5092 (cond
5093 ((/= column (current-column))
5094 (throw 'end nil))
5095 ((looking-at (concat intersection-str h-border))
5096 (forward-line 1)
5097 (move-to-column column)
5098 (forward-char 1)
5099 (throw 'end (point)))
5100 ((looking-at intersection-str)
5101 (throw 'retry-vertical nil))
5102 (t (throw 'retry-horizontal nil)))))))))))))
5104 (defun table--probe-cell-right-bottom ()
5105 "Probe right bottom corner pattern of a cell.
5106 If it finds a valid corner returns a position otherwise returns nil.
5107 The position is the location after the last cell character.
5108 Focus only on the corner pattern. Further cell validity check is required."
5109 (save-excursion
5110 (let ((vertical-str (regexp-quote (char-to-string table-cell-vertical-char)))
5111 (intersection-str (regexp-quote (char-to-string table-cell-intersection-char)))
5112 (v-border (format "[%c%c]" table-cell-vertical-char table-cell-intersection-char))
5113 (h-border (format "[%s%c]" table-cell-horizontal-chars table-cell-intersection-char))
5114 (limit (save-excursion (end-of-line) (point))))
5115 (catch 'end
5116 (while t
5117 (catch 'retry-horizontal
5118 (if (not (search-forward-regexp v-border limit t))
5119 (throw 'end nil))
5120 (save-excursion
5121 (forward-char -1)
5122 (let ((column (current-column)))
5123 (while t
5124 (catch 'retry-vertical
5125 (while (and (looking-at vertical-str)
5126 (= column (current-column)))
5127 (if (and (zerop (forward-line 1)) (zerop (current-column))) nil (throw 'end nil))
5128 (move-to-column column))
5129 (cond
5130 ((/= column (current-column))
5131 (throw 'end nil))
5132 ((save-excursion (forward-char -1) (looking-at (concat h-border intersection-str)))
5133 (save-excursion
5134 (and (zerop (forward-line -1))
5135 (move-to-column column)
5136 (looking-at v-border)
5137 (throw 'end (point))))
5138 (forward-char 1)
5139 (throw 'retry-horizontal nil))
5140 ((looking-at intersection-str)
5141 (if (and (zerop (forward-line 1)) (zerop (current-column))) nil (throw 'end nil))
5142 (move-to-column column)
5143 (throw 'retry-vertical nil))
5144 (t (throw 'retry-horizontal nil)))))))))))))
5146 (defun table--editable-cell-p (&optional abort-on-error)
5147 (and (not buffer-read-only)
5148 (get-text-property (point) 'table-cell)))
5150 (defun table--probe-cell (&optional abort-on-error)
5151 "Probes a table cell around the point.
5152 Searches for the left upper corner and the right bottom corner of a table
5153 cell which contains the current point location.
5155 The result is a cons cell (left-upper . right-bottom) where
5156 the left-upper is the position before the cell's left upper corner character,
5157 the right-bottom is the position after the cell's right bottom corner character.
5159 When it fails to find either one of the cell corners it returns nil or
5160 signals error if the optional ABORT-ON-ERROR is non-nil."
5161 (let (lu rb
5162 (border (format "^[%s%c%c]+$"
5163 table-cell-horizontal-chars
5164 table-cell-vertical-char
5165 table-cell-intersection-char)))
5166 (if (and (condition-case nil
5167 (progn
5168 (and (setq lu (table--probe-cell-left-up))
5169 (setq rb (table--probe-cell-right-bottom))))
5170 (error nil))
5171 (< lu rb)
5172 (let ((lu-coordinate (table--get-coordinate lu))
5173 (rb-coordinate (table--get-coordinate rb)))
5174 ;; test for valid upper and lower borders
5175 (and (string-match
5176 border
5177 (buffer-substring
5178 (save-excursion
5179 (table--goto-coordinate
5180 (cons (1- (car lu-coordinate))
5181 (1- (cdr lu-coordinate)))))
5182 (save-excursion
5183 (table--goto-coordinate
5184 (cons (1+ (car rb-coordinate))
5185 (1- (cdr lu-coordinate)))))))
5186 (string-match
5187 border
5188 (buffer-substring
5189 (save-excursion
5190 (table--goto-coordinate
5191 (cons (1- (car lu-coordinate))
5192 (1+ (cdr rb-coordinate)))))
5193 (save-excursion
5194 (table--goto-coordinate
5195 (cons (1+ (car rb-coordinate))
5196 (1+ (cdr rb-coordinate))))))))))
5197 (cons lu rb)
5198 (if abort-on-error
5199 (error "Table cell not found")
5200 nil))))
5202 (defun table--insert-rectangle (rectangle)
5203 "Insert text of RECTANGLE with upper left corner at point.
5204 Same as insert-rectangle except that mark operation is eliminated."
5205 (let ((lines rectangle)
5206 (insertcolumn (current-column))
5207 (first t))
5208 (while lines
5209 (or first
5210 (progn
5211 (forward-line 1)
5212 (or (bolp) (insert ?\n))
5213 (move-to-column insertcolumn t)))
5214 (setq first nil)
5215 (insert (car lines))
5216 (setq lines (cdr lines)))))
5218 (defun table--put-cell-property (cell)
5219 "Put standard text properties to the CELL.
5220 The CELL is a cons cell (left-upper . right-bottom) where the
5221 left-upper is the position before the cell's left upper corner
5222 character, the right-bottom is the position after the cell's right
5223 bottom corner character."
5224 (let ((lu (table--get-coordinate (car cell)))
5225 (rb (table--get-coordinate (cdr cell))))
5226 (save-excursion
5227 (while (<= (cdr lu) (cdr rb))
5228 (let ((beg (table--goto-coordinate lu 'no-extension))
5229 (end (table--goto-coordinate (cons (car rb) (cdr lu)))))
5230 (table--put-cell-line-property beg end))
5231 (setcdr lu (1+ (cdr lu))))
5232 (table--put-cell-justify-property cell table-cell-info-justify)
5233 (table--put-cell-valign-property cell table-cell-info-valign))))
5235 (defun table--put-cell-line-property (beg end &optional object)
5236 "Put standard text properties to a line of a cell.
5237 BEG is the beginning of the line that is the location between left
5238 cell border character and the first content character. END is the end
5239 of the line that is the location between the last content character
5240 and the right cell border character."
5241 (table--put-cell-content-property beg end object)
5242 (table--put-cell-keymap-property end (1+ end) object)
5243 (table--put-cell-indicator-property end (1+ end) object)
5244 (table--put-cell-rear-nonsticky end (1+ end) object))
5246 (defun table--put-cell-content-property (beg end &optional object)
5247 "Put cell content text properties."
5248 (table--put-cell-keymap-property beg end object)
5249 (table--put-cell-indicator-property beg end object)
5250 (table--put-cell-face-property beg end object)
5251 (table--put-cell-point-entered/left-property beg end object))
5253 (defun table--put-cell-indicator-property (beg end &optional object)
5254 "Put cell property which indicates that the location is within a table cell."
5255 (put-text-property beg end 'table-cell t object)
5256 (put-text-property beg end 'yank-handler table-yank-handler object))
5258 (defun table--put-cell-face-property (beg end &optional object)
5259 "Put cell face property."
5260 (put-text-property beg end 'face 'table-cell object))
5262 (defun table--put-cell-keymap-property (beg end &optional object)
5263 "Put cell keymap property."
5264 (put-text-property beg end 'keymap 'table-cell-map object))
5266 (defun table--put-cell-rear-nonsticky (beg end &optional object)
5267 "Put rear-nonsticky property."
5268 (put-text-property beg end 'rear-nonsticky t object))
5270 (defun table--put-cell-point-entered/left-property (beg end &optional object)
5271 "Put point-entered/left property."
5272 (put-text-property beg end 'point-entered 'table--point-entered-cell-function object)
5273 (put-text-property beg end 'point-left 'table--point-left-cell-function object))
5275 (defun table--remove-cell-properties (beg end &optional object)
5276 "Remove all cell properties.
5277 If OBJECT is non-nil cell properties are removed from the OBJECT
5278 instead of the current buffer and returns the OBJECT."
5279 (while (< beg end)
5280 (let ((next (next-single-property-change beg 'table-cell object end)))
5281 (if (get-text-property beg 'table-cell object)
5282 (remove-text-properties beg next
5283 (list
5284 'table-cell nil
5285 'table-justify nil
5286 'table-valign nil
5287 'face nil
5288 'rear-nonsticky nil
5289 'point-entered nil
5290 'point-left nil
5291 'keymap nil)
5292 object))
5293 (setq beg next)))
5294 object)
5296 (defun table--update-cell-face ()
5297 "Update cell face according to the current mode."
5298 (if (featurep 'xemacs)
5299 (set-face-property 'table-cell 'underline table-fixed-width-mode)
5300 (set-face-inverse-video-p 'table-cell table-fixed-width-mode)))
5302 (table--update-cell-face)
5304 (defun table--get-property (cell property)
5305 "Get CELL's PROPERTY."
5306 (or (get-text-property (car cell) property)
5307 (get-text-property (1- (cdr cell)) property)))
5309 (defun table--get-cell-justify-property (cell)
5310 "Get cell's justify property."
5311 (table--get-property cell 'table-justify))
5313 (defun table--get-cell-valign-property (cell)
5314 "Get cell's vertical alignment property."
5315 (table--get-property cell 'table-valign))
5317 (defun table--put-property (cell property value)
5318 "Put CELL's PROPERTY the VALUE."
5319 (let ((beg (car cell))
5320 (end (cdr cell)))
5321 (put-text-property beg (1+ beg) property value)
5322 (put-text-property (1- end) end property value)))
5324 (defun table--put-cell-justify-property (cell justify)
5325 "Put cell's justify property."
5326 (table--put-property cell 'table-justify justify))
5328 (defun table--put-cell-valign-property (cell valign)
5329 "Put cell's vertical alignment property."
5330 (table--put-property cell 'table-valign valign))
5332 (defun table--point-entered-cell-function (&optional old-point new-point)
5333 "Point has entered a cell.
5334 Refresh the menu bar."
5335 ;; Avoid calling point-motion-hooks recursively.
5336 (let ((inhibit-point-motion-hooks t))
5337 (unless table-cell-entered-state
5338 (setq table-cell-entered-state t)
5339 (setq table-mode-indicator t)
5340 (force-mode-line-update)
5341 (table--warn-incompatibility)
5342 (run-hooks 'table-point-entered-cell-hook))))
5344 (defun table--point-left-cell-function (&optional old-point new-point)
5345 "Point has left a cell.
5346 Refresh the menu bar."
5347 ;; Avoid calling point-motion-hooks recursively.
5348 (let ((inhibit-point-motion-hooks t))
5349 (when table-cell-entered-state
5350 (setq table-cell-entered-state nil)
5351 (setq table-mode-indicator nil)
5352 (force-mode-line-update)
5353 (run-hooks 'table-point-left-cell-hook))))
5355 (defun table--warn-incompatibility ()
5356 "If called from interactive operation warn the know incompatibilities.
5357 This feature is disabled when `table-disable-incompatibility-warning'
5358 is non-nil. The warning is done only once per session for each item."
5359 (unless (and table-disable-incompatibility-warning
5360 (not (interactive-p)))
5361 (cond ((and (featurep 'xemacs)
5362 (not (get 'table-disable-incompatibility-warning 'xemacs)))
5363 (put 'table-disable-incompatibility-warning 'xemacs t)
5364 (display-warning 'table
5366 *** Warning ***
5368 Table package mostly works fine under XEmacs, however, due to the
5369 peculiar implementation of text property under XEmacs, cell splitting
5370 and any undo operation of table exhibit some known strange problems,
5371 such that a border characters dissolve into adjacent cells. Please be
5372 aware of this.
5375 :warning))
5376 ((and (boundp 'flyspell-mode)
5377 flyspell-mode
5378 (not (get 'table-disable-incompatibility-warning 'flyspell)))
5379 (put 'table-disable-incompatibility-warning 'flyspell t)
5380 (display-warning 'table
5382 *** Warning ***
5384 Flyspell minor mode is known to be incompatible with this table
5385 package. The flyspell version 1.5d at http://kaolin.unice.fr/~serrano
5386 works better than the previous versions however not fully compatible.
5389 :warning))
5392 (defun table--cell-blank-str (&optional n)
5393 "Return blank table cell string of length N."
5394 (let ((str (make-string (or n 1) ?\s)))
5395 (table--put-cell-content-property 0 (length str) str)
5396 str))
5398 (defun table--remove-eol-spaces (beg end &optional bol force)
5399 "Remove spaces at the end of each line in the BEG END region of the current buffer.
5400 When optional BOL is non-nil spaces at the beginning of line are
5401 removed. When optional FORCE is non-nil removal operation is enforced
5402 even when point is within the removal area."
5403 (if (> beg end)
5404 (let ((tmp beg))
5405 (setq beg end)
5406 (setq end tmp)))
5407 (let ((saved-point (point-marker))
5408 (end-marker (copy-marker end)))
5409 (save-excursion
5410 (goto-char beg)
5411 (while (if bol (re-search-forward "^\\( +\\)" end-marker t)
5412 (re-search-forward "\\( +\\)$" end-marker t))
5413 ;; avoid removal that causes the saved point to lose its location.
5414 (if (and (null bol)
5415 (<= (match-beginning 1) saved-point)
5416 (<= saved-point (match-end 1))
5417 (not force))
5418 (delete-region saved-point (match-end 1))
5419 (delete-region (match-beginning 1) (match-end 1)))))
5420 (set-marker saved-point nil)
5421 (set-marker end-marker nil)))
5423 (defun table--fill-region (beg end &optional col justify)
5424 "Fill paragraphs in table cell cache.
5425 Current buffer must already be set to the cache buffer."
5426 (let ((fill-column (or col table-cell-info-width))
5427 (fill-prefix nil)
5428 (enable-kinsoku nil)
5429 (adaptive-fill-mode nil)
5430 (marker-beg (copy-marker beg))
5431 (marker-end (copy-marker end))
5432 (marker-point (point-marker)))
5433 (setq justify (or justify table-cell-info-justify))
5434 (and justify
5435 (not (eq justify 'left))
5436 (not (featurep 'xemacs))
5437 (set-marker-insertion-type marker-point t))
5438 (table--remove-eol-spaces (point-min) (point-max))
5439 (if table-fixed-width-mode
5440 (table--fill-region-strictly marker-beg marker-end)
5441 (let ((paragraph-start table-paragraph-start))
5442 (fill-region marker-beg marker-end justify nil t)))
5443 (goto-char marker-point)
5444 (set-marker marker-beg nil)
5445 (set-marker marker-end nil)
5446 (set-marker marker-point nil)))
5448 (defun table--fill-region-strictly (beg end)
5449 "Fill region strictly so that no line exceeds fill-column.
5450 When a word exceeds fill-column the word is chopped into pieces. The
5451 chopped location is indicated with table-word-continuation-char."
5452 (or (and (markerp beg) (markerp end))
5453 (error "markerp"))
5454 (if (< fill-column 2)
5455 (setq fill-column 2))
5456 ;; first remove all continuation characters.
5457 (goto-char beg)
5458 (while (re-search-forward (concat
5459 (format "[^%c ]\\(" table-word-continuation-char)
5460 (regexp-quote (char-to-string table-word-continuation-char))
5461 "\\s +\\)")
5462 end t)
5463 (delete-region (match-beginning 1) (match-end 1)))
5464 ;; then fill as normal
5465 (let ((paragraph-start table-paragraph-start))
5466 (fill-region beg end nil nil t))
5467 ;; now fix up
5468 (goto-char beg)
5469 (while (let ((col (move-to-column fill-column t)))
5470 (cond
5471 ((and (<= col fill-column)
5472 (looking-at " *$"))
5473 (delete-region (match-beginning 0) (match-end 0))
5474 (and (zerop (forward-line 1))
5475 (< (point) end)))
5476 (t (forward-char -1)
5477 (insert-before-markers (if (equal (char-before) ?\s) ?\s table-word-continuation-char)
5478 "\n")
5479 t)))))
5481 (defun table--goto-coordinate (coordinate &optional no-extension no-tab-expansion)
5482 "Move point to the given COORDINATE and return the location.
5483 When optional NO-EXTENSION is non-nil and the specified coordinate is
5484 not reachable returns nil otherwise the blanks are added if necessary
5485 to achieve the goal coordinate and returns the goal point. It
5486 intentionally does not preserve the original point in case it fails
5487 achieving the goal. When optional NO-TAB-EXPANSION is non-nil and the
5488 goad happens to be in a tab character the tab is not expanded but the
5489 goal ends at the beginning of tab."
5490 (if (or (null coordinate)
5491 (< (car coordinate) 0)
5492 (< (cdr coordinate) 0)) nil
5493 (goto-char (point-min))
5494 (let ((x (car coordinate))
5495 (more-lines (forward-line (cdr coordinate))))
5496 (catch 'exit
5497 (if (zerop (current-column)) nil
5498 (if no-extension
5499 (progn
5500 (move-to-column x)
5501 (throw 'exit nil))
5502 (setq more-lines (1+ more-lines))))
5503 (if (zerop more-lines) nil
5504 (newline more-lines))
5505 (if no-extension
5506 (if (/= (move-to-column x) x)
5507 (if (> (move-to-column x) x)
5508 (if no-tab-expansion
5509 (progn
5510 (while (> (move-to-column x) x)
5511 (setq x (1- x)))
5512 (point))
5513 (throw 'exit (move-to-column x t)))
5514 (throw 'exit nil)))
5515 (move-to-column x t))
5516 (point)))))
5518 (defun table--copy-coordinate (coord)
5519 "Copy coordinate in a new cons cell."
5520 (cons (car coord) (cdr coord)))
5522 (defun table--get-coordinate (&optional where)
5523 "Return the coordinate of point in current buffer.
5524 When optional WHERE is given it returns the coordinate of that
5525 location instead of point in the current buffer. It does not move the
5526 point"
5527 (save-excursion
5528 (if where (goto-char where))
5529 (cons (current-column)
5530 (table--current-line))))
5532 (defun table--current-line (&optional location)
5533 "Return zero based line count of current line or if non-nil LOCATION line."
5534 (save-excursion
5535 (if location (goto-char location))
5536 (beginning-of-line)
5537 (count-lines (point-min) (point))))
5539 (defun table--transcoord-table-to-cache (&optional coordinate)
5540 "Transpose COORDINATE from table coordinate system to cache coordinate system.
5541 When COORDINATE is omitted or nil the point in current buffer is assumed in place."
5542 (table--offset-coordinate
5543 (or coordinate (table--get-coordinate))
5544 table-cell-info-lu-coordinate
5545 'negative))
5547 (defun table--transcoord-cache-to-table (&optional coordinate)
5548 "Transpose COORDINATE from cache coordinate system to table coordinate system.
5549 When COORDINATE is omitted or nil the point in current buffer is assumed in place."
5550 (table--offset-coordinate
5551 (or coordinate (table--get-coordinate))
5552 table-cell-info-lu-coordinate))
5554 (defun table--offset-coordinate (coordinate offset &optional negative)
5555 "Return the offseted COORDINATE by OFFSET.
5556 When optional NEGATIVE is non-nil offsetting direction is negative."
5557 (cons (if negative (- (car coordinate) (car offset))
5558 (+ (car coordinate) (car offset)))
5559 (if negative (- (cdr coordinate) (cdr offset))
5560 (+ (cdr coordinate) (cdr offset)))))
5562 (defun table--char-in-str-at-column (str column)
5563 "Return the character in STR at COLUMN location.
5564 When COLUMN is out of range it returns null character."
5565 (let ((idx (table--str-index-at-column str column)))
5566 (if idx (aref str idx)
5567 ?\0)))
5569 (defun table--str-index-at-column (str column)
5570 "Return the character index in STR that corresponds to COLUMN location.
5571 It returns COLUMN unless STR contains some wide characters."
5572 (let ((col 0)
5573 (idx 0)
5574 (len (length str)))
5575 (while (and (< col column) (< idx len))
5576 (setq col (+ col (char-width (aref str idx))))
5577 (setq idx (1+ idx)))
5578 (if (< idx len)
5580 nil)))
5582 (defun table--set-timer (seconds func args)
5583 "Generic wrapper for setting up a timer."
5584 (if (featurep 'xemacs)
5585 ;; the picky xemacs refuses to accept zero
5586 (add-timeout (if (zerop seconds) 0.01 seconds) func args nil)
5587 ;;(run-at-time seconds nil func args)))
5588 ;; somehow run-at-time causes strange problem under Emacs 20.7
5589 ;; this problem does not show up under Emacs 21.0.90
5590 (run-with-idle-timer seconds nil func args)))
5592 (defun table--cancel-timer (timer)
5593 "Generic wrapper for canceling a timer."
5594 (if (featurep 'xemacs)
5595 (disable-timeout timer)
5596 (cancel-timer timer)))
5598 (defun table--get-last-command ()
5599 "Generic wrapper for getting the real last command."
5600 (if (boundp 'real-last-command)
5601 real-last-command
5602 last-command))
5604 (run-hooks 'table-load-hook)
5606 (provide 'table)
5608 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
5609 ;; Local Variables: ***
5610 ;; time-stamp-line-limit: 16 ***
5611 ;; time-stamp-start: ";; Revised:[ \t]+" ***
5612 ;; time-stamp-end: "$" ***
5613 ;; time-stamp-format: "%3a %3b %02d %:y %02H:%02M:%02S (%Z)" ***
5614 ;; End: ***
5615 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
5617 ;; arch-tag: 0d69b03e-aa5f-4e72-8806-5727217617e0
5618 ;;; table.el ends here