[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] master dd6b689: * packages/csv-mode/csv-mode.el: More cvs-align-m

From: Stefan Monnier
Subject: [elpa] master dd6b689: * packages/csv-mode/csv-mode.el: More cvs-align-mode improvements
Date: Sat, 19 Oct 2019 16:59:57 -0400 (EDT)

branch: master
commit dd6b689b61fbae9275a1a8f4863a6bb10b87d80e
Author: Stefan Monnier <address@hidden>
Commit: Stefan Monnier <address@hidden>

    * packages/csv-mode/csv-mode.el: More cvs-align-mode improvements
    Rename csv-align-fields-* to cvs-align-*.
    (csv-transpose): Use split-string.
    (csv-split-string): Delete function.
    (csv--config-column-widths): New var.
    (csv-align--set-column): New function.
    (csv-align-set-column-width): New command.
    (csv--jit-align): Use them to obey the per-column width settings.
    Delay context refresh by jit-lock-context-time.
    Set cursor-sensor-functions to untruncate fields on-the-fly.
    (csv-align--cursor-truncated): New function.
    (csv-align-mode): Activate cursor-sensor-mode.
 packages/csv-mode/csv-mode.el | 155 ++++++++++++++++++++++++------------------
 1 file changed, 88 insertions(+), 67 deletions(-)

diff --git a/packages/csv-mode/csv-mode.el b/packages/csv-mode/csv-mode.el
index 92cd2c3..336f9d3 100644
--- a/packages/csv-mode/csv-mode.el
+++ b/packages/csv-mode/csv-mode.el
@@ -43,7 +43,9 @@
 ;;   multiple killed fields can be yanked only as a fixed group
 ;;   equivalent to a single field.
-;; - `csv-align-fields-mode' keeps fields visually aligned, on-the-fly.
+;; - `csv-align-mode' keeps fields visually aligned, on-the-fly.
+;;   It truncates fields to a maximum width that can be changed per-column
+;;   with `csv-align-set-column-width'.
 ;;   Alternatively, C-c C-a (`csv-align-fields') aligns fields into columns
 ;;   and C-c C-u (`csv-unalign-fields') undoes such alignment;
 ;;   separators can be hidden within aligned records (controlled by
@@ -226,14 +228,14 @@ Changing this variable does not affect any existing CSV 
mode buffer."
 (defcustom csv-align-style 'left
   "Aligned field style: one of `left', `centre', `right' or `auto'.
-Alignment style used by `csv-align-fields'.
+Alignment style used by `csv-align-mode' and `csv-align-fields'.
 Auto-alignment means left align text and right align numbers."
   :type '(choice (const left) (const centre)
                 (const right) (const auto)))
 (defcustom csv-align-padding 1
   "Aligned field spacing: must be a positive integer.
-Number of spaces used by `csv-align-fields' after separators."
+Number of spaces used by `csv-align-mode' and `csv-align-fields' after 
   :type 'integer)
 (defcustom csv-header-lines 0
@@ -425,21 +427,21 @@ Usually they sort in order of ascending sort key.")
     ("Alignment Style"
      ["Left" (setq csv-align-style 'left) :active t
       :style radio :selected (eq csv-align-style 'left)
-      :help "If selected, `csv-align-fields' left aligns fields"]
+      :help "If selected, `csv-align' left aligns fields"]
      ["Centre" (setq csv-align-style 'centre) :active t
       :style radio :selected (eq csv-align-style 'centre)
-      :help "If selected, `csv-align-fields' centres fields"]
+      :help "If selected, `csv-align' centres fields"]
      ["Right" (setq csv-align-style 'right) :active t
       :style radio :selected (eq csv-align-style 'right)
-      :help "If selected, `csv-align-fields' right aligns fields"]
+      :help "If selected, `csv-align' right aligns fields"]
      ["Auto" (setq csv-align-style 'auto) :active t
       :style radio :selected (eq csv-align-style 'auto)
       :help "\
-If selected, `csv-align-fields' left aligns text and right aligns numbers"]
+If selected, `csv-align' left aligns text and right aligns numbers"]
     ["Set header line" csv-header-line :active t]
-    ["Auto-(re)align fields" csv-align-fields-mode
-     :style toggle :selected csv-align-fields-mode]
+    ["Auto-(re)align fields" csv-align-mode
+     :style toggle :selected csv-align-mode]
     ["Show Current Field Index" csv-field-index-mode :active t
      :style toggle :selected csv-field-index-mode
      :help "If selected, display current field index in mode line"]
@@ -1224,9 +1226,9 @@ When called non-interactively, BEG and END specify region 
to process."
            (let ((lep (line-end-position)))
-              (csv-split-string
+              (split-string
                (buffer-substring-no-properties (point) lep)
-               csv-separator-regexp nil t)
+               csv-separator-regexp)
              (delete-region (point) lep)
              (or (eobp) (delete-char 1)))))
@@ -1265,48 +1267,6 @@ When called non-interactively, BEG and END specify 
region to process."
        ;; Re-do soft alignment if necessary:
        (if align (csv-align-fields nil (point-min) (point-max)))))))
-;; The following generalised version of `split-string' is taken from
-;; the development version of WoMan and should probably replace the
-;; standard version in subr.el.  However, CSV mode (currently) needs
-;; only the `allowbeg' option.
-(defun csv-split-string
-  (string &optional separators subexp allowbeg allowend)
-  "Splits STRING into substrings where there are matches for SEPARATORS.
-Each match for SEPARATORS is a splitting point.
-The substrings between the splitting points are made into a list
-which is returned.
-If SEPARATORS is absent, it defaults to \"[ \\f\\t\\n\\r\\v]+\".
-SUBEXP specifies a subexpression of SEPARATORS to be the splitting
-point\; it defaults to 0.
-If there is a match for SEPARATORS at the beginning of STRING, we do
-not include a null substring for that, unless ALLOWBEG is non-nil.
-Likewise, if there is a match at the end of STRING, we do not include
-a null substring for that, unless ALLOWEND is non-nil.
-Modifies the match data; use `save-match-data' if necessary."
-  (or subexp (setq subexp 0))
-  (let ((rexp (or separators "[ \f\t\n\r\v]+"))
-       (start 0)
-       notfirst
-       (list nil))
-    (while (and (string-match rexp string
-                             (if (and notfirst
-                                      (= start (match-beginning subexp))
-                                      (< start (length string)))
-                                 (1+ start) start))
-               (< (match-beginning subexp) (length string)))
-      (setq notfirst t)
-      (or (and (not allowbeg) (eq (match-beginning subexp) 0))
-         (and (eq (match-beginning subexp) (match-end subexp))
-              (eq (match-beginning subexp) start))
-         (push (substring string start (match-beginning subexp)) list))
-      (setq start (match-end subexp)))
-    (or (and (not allowend) (eq start (length string)))
-       (push (substring string start) list))
-    (nreverse list)))
 (defvar-local csv--header-line nil)
 (defvar-local csv--header-hscroll nil)
 (defvar-local csv--header-string nil)
@@ -1375,12 +1335,40 @@ If there is already a header line, then unset the 
header line."
 ;;; Auto-alignment
-(defcustom csv-align-fields-max-width 40
-  "Maximum width of a column in `csv-align-fields-mode'.
+(defcustom csv-align-max-width 40
+  "Maximum width of a column in `csv-align-mode'.
 This does not apply to the last column (for which the usual `truncate-lines'
 setting works better)."
   :type 'integer)
+(defvar-local csv--config-column-widths nil
+  "Settings per column, stored as a list indexed by the column.")
+(defun csv-align--set-column (column value)
+  (let ((len (length csv--config-column-widths)))
+    (if (< len column)
+        (setq csv--config-column-widths
+              (nconc csv--config-column-widths (make-list (- column len) 
+    (setf (nth (1- column) csv--config-column-widths) value)))
+(defun csv-align-set-column-width (column width)
+  "Set the max WIDTH to use for COLUMN."
+  (interactive
+   (let* ((field (or (csv--field-index) 1))
+          (curwidth (nth (1- field) csv--config-column-widths)))
+     (list field
+           (cond
+            ((numberp current-prefix-arg)
+             current-prefix-arg)
+            (current-prefix-arg
+             (read-number (format "Column width (for field %d): " field)
+                          curwidth))
+            (t (if curwidth nil (csv--ellipsis-width)))))))
+  (when (eql width csv-align-max-width)
+    (setq width nil))
+  (csv-align--set-column column width)
+  (jit-lock-refontify))
 (defvar-local csv--jit-columns nil)
 (defun csv--jit-merge-columns (column-widths)
@@ -1402,7 +1390,9 @@ setting works better)."
 (defun csv--jit-unalign (beg end)
-  (remove-text-properties beg end '(display nil csv--jit nil invisible nil))
+  (remove-text-properties beg end
+                          '(display nil csv--jit nil invisible nil
+                            cursor-sensor-functions nil csv--revealed nil))
   (remove-overlays beg end 'csv--jit t))
 (defun csv--jit-flush (beg end)
@@ -1434,6 +1424,24 @@ setting works better)."
     (if ellipsis (length ellipsis) 3)))
+(defun csv-align--cursor-truncated (window _oldpos dir)
+  (let* ((prop (if (eq dir 'entered) 'invisible 'csv--revealed))
+         (pos (window-point window))
+         (start (cond
+                 ((and (> pos (point-min))
+                       (eq (get-text-property (1- pos) prop) 'csv-truncate))
+                  (or (previous-single-property-change pos prop) (point-min)))
+                 (t pos)))
+         (end (if (eq (get-text-property pos prop) 'csv-truncate)
+                  (or (next-single-property-change pos prop) (point-max))
+                pos)))
+    (unless (eql start end)
+      (with-silent-modifications
+        (put-text-property start end
+                           (if (eq dir 'entered) 'csv--revealed 'invisible)
+                           'csv-truncate)
+        (remove-text-properties start end (list prop))))))
 (defun csv--jit-align (beg end)
     ;; This is run with inhibit-modification-hooks set, so the overlays'
@@ -1455,26 +1463,28 @@ setting works better)."
                  (ellipsis-width (csv--ellipsis-width)))
       (when changed
         ;; Do it after the current redisplay is over.
-        ;; We could even defer it by a small amount of time.
-        (run-with-timer 0 nil #'csv--jit-flush beg end))
+        (run-with-timer jit-lock-context-time nil #'csv--jit-flush beg end))
       ;; Align fields:
       (goto-char beg)
       (while (< (point) end)
        (unless (csv-not-looking-at-record)
           (let ((w csv--jit-columns)
+                (widths-config csv--config-column-widths)
                 (column 0))      ;Desired position of left-side of this column.
             (while (and w (not (eolp)))
               (let* ((field-beg (point))
+                     (width-config (pop widths-config))
                      (align-padding (if (bolp) 0 csv-align-padding))
                      (left-padding 0) (right-padding 0)
                      (field-width (pop field-widths))
                       (min (pop w)
-                           ;; Don't apply csv-align-fields-max-width
-                           ;; to the last field!
-                           (if w csv-align-fields-max-width
-                             most-positive-fixnum)))
+                           (or width-config
+                               ;; Don't apply csv-align-max-width
+                               ;; to the last field!
+                               (if w csv-align-max-width
+                                 most-positive-fixnum))))
                      (x (- column-width field-width)) ; Required padding.
                      (truncate nil))
@@ -1550,9 +1560,7 @@ setting works better)."
                            'after-string (make-string right-padding ?\ )))))))
                 (setq column (+ column column-width align-padding))
                 ;; Do it after applying the property, so `move-to-column' can
                 ;; take it into account.
                 (when truncate
@@ -1572,20 +1580,33 @@ setting works better)."
                              (move-to-column truncate))
                     (put-text-property trunc-pos (point)
-                                       'invisible 'csv-truncate)))
+                                       'invisible 'csv-truncate)
+                    (when (> (- (point) trunc-pos) 1)
+                      ;; Arrange to temporarily untruncate the string when
+                      ;; cursor moves into it.
+                      ;; FIXME: This only works if
+                      ;; `global-disable-point-adjustment' is non-nil!
+                      ;; Arguably this should be fixed by making
+                      ;; point-adjustment code pay attention to
+                      ;; cursor-sensor-functions!
+                      (put-text-property
+                       (1+ trunc-pos) (point)
+                       'cursor-sensor-functions
+                       (list #'csv-align--cursor-truncated)))))
                 (unless (eolp) (forward-char)) ; Skip separator.
     `(jit-lock-bounds ,beg . ,end)))
-(define-minor-mode csv-align-fields-mode
+(define-minor-mode csv-align-mode
   "Align columns on the fly."
   :global nil
   (csv-unalign-fields nil (point-min) (point-max)) ;Just in case.
-   (csv-align-fields-mode
+   (csv-align-mode
     (add-to-invisibility-spec '(csv-truncate . t))
     (kill-local-variable 'csv--jit-columns)
+    (cursor-sensor-mode 1)
     (jit-lock-register #'csv--jit-align)

reply via email to

[Prev in Thread] Current Thread [Next in Thread]