Skip to content

Latest commit

 

History

History
203 lines (172 loc) · 6.48 KB

pg-s.org

File metadata and controls

203 lines (172 loc) · 6.48 KB

pg-s: String functions not in s.el

A string manipulation library to catch one or two things that aren’t (yet?) implemented in s.el.

Package Header

;;; pg-s.el --- String functions not in s.el

;; Copyright (C) 2017 Phil Groce

;; Author: Phil Groce <[email protected]>
;; Version: 0.1
;; Package-Requires: ((s "1.12.0"))
;; Keywords: string

Requires

(require 's)

Code

Substrings

s.el includes s-left and s-right to take the n-most left or right substring of a string. It does not, however, contain inverses of those functions. While they are easy enough to write oneself, it’s a lot more convenient to have them available as functions. Hence these functions.

If the descriptions below are insufficiently clear, here is how they work on the string “abcde”:

Coderesult
(s-left 2 “abcde”)“ab”
(s-right 2 “abcde”)“dc”
(pg-s-except-left 2 “abcde”)“cde”
(pg-s-except-right 2 “abcde”)“abc”

The omission of these functions in s.el is puzzling to me; it’s analogous to s-chop-prefix and s-chop-suffix but with a fixed length instead of a string. I’ve probably missed it.

pg-s-except-left

(defun pg-s-except-left (num-chars s)
  "Returns everything after the first NUM-CHARS characters of S.
This function is the inverse of `s-left'."
  (let ((to-trim (abs (- num-chars (string-width s)))))
    (s-right to-trim s)))

Tests

(ert-deftest pg-s-except-left-test ()
  (should (string=
           "cde"
           (pg-s-except-left 2 "abcde")))
  (should (string=
           "abcde"
           (pg-s-except-left 0 "abcde")))
  (should (string=
           ""
           (pg-s-except-left 2 ""))))

pg-s-except-right

(defun pg-s-except-right (num-chars s)
  "Returns everything until the last NUM-CHARS characters of S.
This function is the inverse of `s-right'"
  (let ((to-trim (abs (- num-chars (string-width s)))))
    (s-left to-trim s)))

Tests

(ert-deftest pg-s-except-right-test ()
  (should (string=
           "abc"
           (pg-s-except-right 2 "abcde")))
  (should (string=
           "abcde"
           (pg-s-except-right 0 "abcde")))
  (should (string=
           ""
           (pg-s-except-right 2 ""))))

pg-s-truncate-middle

This function is handy when displaying strings in space-conscious ways (such as on the modeline or in the echo area.) It keeps the beginning and end of the string, putting a fill string (by default “…”) in the middle to indicate that it has been omitted.

(defun pg-s--halves (len)
   "Divide LEN in two. Allocate the remainer on odd strings to
   the right half. Return a list '(left-len right-len)"
   (let ((half (/ len 2)))
     (if (evenp len)
         (list half half)
       (list half (+ 1 half)))))

(defun pg-s-truncate-middle (str len &optional fill-str)
   (let* ((fill-str (if fill-str fill-str "..."))
          (fill-len (string-width fill-str)))
     (cond
      ;; If the string is shorter than the max length, just return it
      ((<= (string-width str) len) str)
      ;; Ignore negative vales of len
      ((< len 0) str)
      ;; If the length is 0, return empty string
      ((= len 0) "")
      ;; Return first character if length is 1
      ((= len 1) (s-left 1 str))
      ;; Return first and last character if length is 2
      ((= len 2) (concat (s-left 1 str) (s-right 1 str)))
      ;; If the desired length is less than the fill-string length
      ;; plus two characters, (1) wtf, and (2) take characters off the
      ;; front and back of the fill until it is
      ((< len (+ 2 fill-len))
       (let* ((target-fill-len (- len 2))
              ;; The difference between the target length and the fill
              ;; length (IOW, what we have to cut from fill-len)
              (gap (abs (- target-fill-len fill-len))))
         (cl-destructuring-bind (fl fr) (pg-s--halves gap)
           (message "taking left %s and right %s of %s"
                    fl fr fill-str)
           (setq fill-str
                 (pg-s-except-left fl (pg-s-except-right fr fill-str)))
           ;; Note: fill-len is wrong at this point, but it doesn't
           ;; matter.
           (concat (s-trim-right (s-left 1 str))
                   fill-str
                   (s-trim-left (s-right 1 str))))))
      ;; Base case
      (t
       (cl-destructuring-bind (l r) (pg-s--halves (- len fill-len))
         (concat
          (s-trim-right (s-left l str))
          fill-str
          (s-trim-left (s-right r str))))))))

Tests

(ert-deftest pg-s-truncate-middle-test ()
  (let ((9-str "abcdefghi"))
    (should (=
             8
             (string-width
              (pg-s-truncate-middle 9-str 8))))
    (should (string-equal
             ""
             (pg-s-truncate-middle "" 8)))
    (should (string-equal
             "abcdefghi"
             (pg-s-truncate-middle 9-str -1)))
    (should (string-equal
             ""
             (pg-s-truncate-middle 9-str 0)))
    (should (string-equal
             "a"
             (pg-s-truncate-middle 9-str 1)))
    (should (string-equal
             "ai"
             (pg-s-truncate-middle 9-str 2)))
    (should (string-equal
             "a.i"
             (pg-s-truncate-middle 9-str 3)))
    (should (string-equal
             "a..i"
             (pg-s-truncate-middle 9-str 4)))
    (should (string-equal
             "a...i"
             (pg-s-truncate-middle 9-str 5)))
    (should (string-equal
             "a...hi"
             (pg-s-truncate-middle 9-str 6)))
    (should (string-equal
             "ab...hi"
             (pg-s-truncate-middle 9-str 7)))
    (should (string-equal
             "ab...ghi"
             (pg-s-truncate-middle 9-str 8)))
    (should (string-equal
             "abcdefghi"
             (pg-s-truncate-middle 9-str 9)))
    (should (string-equal
             "abcdefghi"
             (pg-s-truncate-middle 9-str 10)))))

Provides

(provide 'pg-s)
;;; pg-s.el ends here