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