The long lost Emacs string manipulation library.
Installation
It's available on Melpa:
M-x package-install s
Or you can just dump s.el
in your load path somewhere.
Functions
Tweak whitespace
- s-trim
(s)
- s-trim-left
(s)
- s-trim-right
(s)
- s-chomp
(s)
- s-collapse-whitespace
(s)
- s-word-wrap
(len s)
- s-center
(len s)
- s-pad-left
(len padding s)
- s-pad-right
(len padding s)
To shorter string
- s-truncate
(len s)
- s-left
(len s)
- s-right
(len s)
- s-chop-suffix
(suffix s)
- s-chop-suffixes
(suffixes s)
- s-chop-prefix
(prefix s)
- s-chop-prefixes
(prefixes s)
- s-shared-start
(s1 s2)
- s-shared-end
(s1 s2)
To longer string
To and from lists
- s-lines
(s)
- s-match
(regexp s &optional start)
- s-match-strings-all
(regex string)
- s-matched-positions-all
(regexp string &optional subexp-depth)
- s-slice-at
(regexp s)
- s-split
(separator s &optional omit-nulls)
- s-split-up-to
(separator s n &optional omit-nulls)
- s-join
(separator strings)
Predicates
- s-equals?
(s1 s2)
- s-less?
(s1 s2)
- s-matches?
(regexp s &optional start)
- s-blank?
(s)
- s-present?
(s)
- s-ends-with?
(suffix s &optional ignore-case)
- s-starts-with?
(prefix s &optional ignore-case)
- s-contains?
(needle s &optional ignore-case)
- s-lowercase?
(s)
- s-uppercase?
(s)
- s-mixedcase?
(s)
- s-capitalized?
(s)
- s-numeric?
(s)
The misc bucket
- s-replace
(old new s)
- s-replace-all
(replacements s)
- s-downcase
(s)
- s-upcase
(s)
- s-capitalize
(s)
- s-titleize
(s)
- s-with
(s form &rest more)
- s-index-of
(needle s &optional ignore-case)
- s-reverse
(s)
- s-presence
(s)
- s-format
(template replacer &optional extra)
- s-lex-format
(format-str)
- s-count-matches
(regexp s &optional start end)
- s-wrap
(s prefix &optional suffix)
Pertaining to words
- s-split-words
(s)
- s-lower-camel-case
(s)
- s-upper-camel-case
(s)
- s-snake-case
(s)
- s-dashed-words
(s)
- s-capitalized-words
(s)
- s-titleized-words
(s)
- s-word-initials
(s)
Documentation and examples
(s)
s-trim Remove whitespace at the beginning and end of s
.
(s-trim "trim ") ;; => "trim"
(s-trim " this") ;; => "this"
(s-trim " only trims beg and end ") ;; => "only trims beg and end"
(s)
s-trim-left Remove whitespace at the beginning of s
.
(s-trim-left "trim ") ;; => "trim "
(s-trim-left " this") ;; => "this"
(s)
s-trim-right Remove whitespace at the end of s
.
(s-trim-right "trim ") ;; => "trim"
(s-trim-right " this") ;; => " this"
(s)
s-chomp Remove one trailing \n
, \r
or \r\n
from s
.
(s-chomp "no newlines\n") ;; => "no newlines"
(s-chomp "no newlines\r\n") ;; => "no newlines"
(s-chomp "some newlines\n\n") ;; => "some newlines\n"
(s)
s-collapse-whitespace Convert all adjacent whitespace characters to a single space.
(s-collapse-whitespace "only one space please") ;; => "only one space please"
(s-collapse-whitespace "collapse \n all \t sorts of \r whitespace") ;; => "collapse all sorts of whitespace"
(len s)
s-word-wrap If s
is longer than len
, wrap the words with newlines.
(s-word-wrap 10 "This is too long") ;; => "This is\ntoo long"
(s-word-wrap 10 "This is way way too long") ;; => "This is\nway way\ntoo long"
(s-word-wrap 10 "It-wraps-words-but-does-not-break-them") ;; => "It-wraps-words-but-does-not-break-them"
(len s)
s-center If s
is shorter than len
, pad it with spaces so it is centered.
(s-center 5 "a") ;; => " a "
(s-center 5 "ab") ;; => " ab "
(s-center 1 "abc") ;; => "abc"
(len padding s)
s-pad-left If s
is shorter than len
, pad it with padding
on the left.
(s-pad-left 3 "0" "3") ;; => "003"
(s-pad-left 3 "0" "23") ;; => "023"
(s-pad-left 3 "0" "1234") ;; => "1234"
(len padding s)
s-pad-right If s
is shorter than len
, pad it with padding
on the right.
(s-pad-right 3 "." "3") ;; => "3.."
(s-pad-right 3 "." "23") ;; => "23."
(s-pad-right 3 "." "1234") ;; => "1234"
(len s)
s-truncate If s
is longer than len
, cut it down to len
- 3 and add ... at the end.
(s-truncate 6 "This is too long") ;; => "Thi..."
(s-truncate 16 "This is also too long") ;; => "This is also ..."
(s-truncate 16 "But this is not!") ;; => "But this is not!"
(len s)
s-left Returns up to the len
first chars of s
.
(s-left 3 "lib/file.js") ;; => "lib"
(s-left 3 "li") ;; => "li"
(len s)
s-right Returns up to the len
last chars of s
.
(s-right 3 "lib/file.js") ;; => ".js"
(s-right 3 "li") ;; => "li"
(suffix s)
s-chop-suffix Remove suffix
if it is at end of s
.
(s-chop-suffix "-test.js" "penguin-test.js") ;; => "penguin"
(s-chop-suffix "\n" "no newlines\n") ;; => "no newlines"
(s-chop-suffix "\n" "some newlines\n\n") ;; => "some newlines\n"
(suffixes s)
s-chop-suffixes Remove suffixes
one by one in order, if they are at the end of s
.
(s-chop-suffixes '("_test.js" "-test.js" "Test.js") "penguin-test.js") ;; => "penguin"
(s-chop-suffixes '("\r" "\n") "penguin\r\n") ;; => "penguin\r"
(s-chop-suffixes '("\n" "\r") "penguin\r\n") ;; => "penguin"
(prefix s)
s-chop-prefix Remove prefix
if it is at the start of s
.
(s-chop-prefix "/tmp" "/tmp/file.js") ;; => "/file.js"
(s-chop-prefix "/tmp" "/tmp/tmp/file.js") ;; => "/tmp/file.js"
(prefixes s)
s-chop-prefixes Remove prefixes
one by one in order, if they are at the start of s
.
(s-chop-prefixes '("/tmp" "/my") "/tmp/my/file.js") ;; => "/file.js"
(s-chop-prefixes '("/my" "/tmp") "/tmp/my/file.js") ;; => "/my/file.js"
(s1 s2)
s-shared-start Returns the longest prefix s1
and s2
have in common.
(s-shared-start "bar" "baz") ;; => "ba"
(s-shared-start "foobar" "foo") ;; => "foo"
(s-shared-start "bar" "foo") ;; => ""
(s1 s2)
s-shared-end Returns the longest suffix s1
and s2
have in common.
(s-shared-end "bar" "var") ;; => "ar"
(s-shared-end "foo" "foo") ;; => "foo"
(s-shared-end "bar" "foo") ;; => ""
(num s)
s-repeat Make a string of s
repeated num
times.
(s-repeat 10 " ") ;; => " "
(s-concat (s-repeat 8 "Na") " Batman!") ;; => "NaNaNaNaNaNaNaNa Batman!"
(&rest strings)
s-concat Join all the string arguments into one string.
(s-concat "abc" "def" "ghi") ;; => "abcdefghi"
(prefix s)
s-prepend Concatenate prefix
and s
.
(s-prepend "abc" "def") ;; => "abcdef"
(suffix s)
s-append Concatenate s
and suffix
.
(s-append "abc" "def") ;; => "defabc"
(s)
s-lines Splits s
into a list of strings on newline characters.
(s-lines "abc\ndef\nghi") ;; => '("abc" "def" "ghi")
(s-lines "abc\rdef\rghi") ;; => '("abc" "def" "ghi")
(s-lines "abc\r\ndef\r\nghi") ;; => '("abc" "def" "ghi")
(regexp s &optional start)
s-match When the given expression matches the string, this function returns a list of the whole matching string and a string for each matched subexpressions. If it did not match the returned value is an empty list (nil).
When start
is non-nil the search will start at that index.
(s-match "^def" "abcdefg") ;; => nil
(s-match "^abc" "abcdefg") ;; => '("abc")
(s-match "^/.*/\\([a-z]+\\)\\.\\([a-z]+\\)" "/some/weird/file.html") ;; => '("/some/weird/file.html" "file" "html")
(regex string)
s-match-strings-all Return a list of matches for regex
in string
.
Each element itself is a list of matches, as per
match-string
. Multiple matches at the same position will be
ignored after the first.
(s-match-strings-all "{\\([^}]+\\)}" "x is {x} and y is {y}") ;; => '(("{x}" "x") ("{y}" "y"))
(s-match-strings-all "ab." "abXabY") ;; => '(("abX") ("abY"))
(s-match-strings-all "\\<" "foo bar baz") ;; => '(("") ("") (""))
(regexp string &optional subexp-depth)
s-matched-positions-all Return a list of matched positions for regexp
in string
.
subexp-depth
is 0 by default.
(s-matched-positions-all "l+" "{{Hello}} World, {{Emacs}}!" 0) ;; => '((4 . 6) (13 . 14))
(s-matched-positions-all "{{\\(.+?\\)}}" "{{Hello}} World, {{Emacs}}!" 0) ;; => '((0 . 9) (17 . 26))
(s-matched-positions-all "{{\\(.+?\\)}}" "{{Hello}} World, {{Emacs}}!" 1) ;; => '((2 . 7) (19 . 24))
(regexp s)
s-slice-at Slices s
up at every index matching regexp
.
(s-slice-at "-" "abc") ;; => '("abc")
(s-slice-at "-" "abc-def") ;; => '("abc" "-def")
(s-slice-at "[.#]" "abc.def.ghi#id") ;; => '("abc" ".def" ".ghi" "#id")
(separator s &optional omit-nulls)
s-split Split s
into substrings bounded by matches for regexp separator
.
If omit-nulls
is non-nil, zero-length substrings are omitted.
This is a simple wrapper around the built-in split-string
.
(s-split "|" "a|bc|12|3") ;; => '("a" "bc" "12" "3")
(s-split ":" "a,c,d") ;; => '("a,c,d")
(s-split "\n" "z\nefg\n") ;; => '("z" "efg" "")
(separator s n &optional omit-nulls)
s-split-up-to Split s
up to n
times into substrings bounded by matches for regexp separator
.
If omit-nulls
is non-nil, zero-length substrings are omitted.
See also s-split
.
(s-split-up-to "\\s-*-\\s-*" "Author - Track-number-one" 1) ;; => '("Author" "Track-number-one")
(s-split-up-to "\\s-*-\\s-*" "Author - Track-number-one" 2) ;; => '("Author" "Track" "number-one")
(s-split-up-to "|" "foo||bar|baz|qux" 3 t) ;; => '("foo" "bar" "baz|qux")
(separator strings)
s-join Join all the strings in strings
with separator
in between.
(s-join "+" '("abc" "def" "ghi")) ;; => "abc+def+ghi"
(s-join "\n" '("abc" "def" "ghi")) ;; => "abc\ndef\nghi"
(s1 s2)
s-equals? Is s1
equal to s2
?
This is a simple wrapper around the built-in string-equal
.
(s-equals? "abc" "ABC") ;; => nil
(s-equals? "abc" "abc") ;; => t
(s1 s2)
s-less? Is s1
less than s2
?
This is a simple wrapper around the built-in string-lessp
.
(s-less? "abc" "abd") ;; => t
(s-less? "abd" "abc") ;; => nil
(s-less? "abc" "abc") ;; => nil
(regexp s &optional start)
s-matches? Does regexp
match s
?
If start
is non-nil the search starts at that index.
This is a simple wrapper around the built-in string-match-p
.
(s-matches? "^[0-9]+$" "123") ;; => t
(s-matches? "^[0-9]+$" "a123") ;; => nil
(s-matches? "1" "1a" 1) ;; => nil
(s)
s-blank? Is s
nil or the empty string?
(s-blank? "") ;; => t
(s-blank? nil) ;; => t
(s-blank? " ") ;; => nil
(s)
s-present? Is s
anything but nil or the empty string?
(s-present? "") ;; => nil
(s-present? nil) ;; => nil
(s-present? " ") ;; => t
(suffix s &optional ignore-case)
s-ends-with? Does s
end with suffix
?
If ignore-case
is non-nil, the comparison is done without paying
attention to case differences.
Alias: s-suffix?
(s-ends-with? ".md" "readme.md") ;; => t
(s-ends-with? ".MD" "readme.md") ;; => nil
(s-ends-with? ".MD" "readme.md" t) ;; => t
(prefix s &optional ignore-case)
s-starts-with? Does s
start with prefix
?
If ignore-case
is non-nil, the comparison is done without paying
attention to case differences.
Alias: s-prefix?
. This is a simple wrapper around the built-in
string-prefix-p
.
(s-starts-with? "lib/" "lib/file.js") ;; => t
(s-starts-with? "LIB/" "lib/file.js") ;; => nil
(s-starts-with? "LIB/" "lib/file.js" t) ;; => t
(needle s &optional ignore-case)
s-contains? Does s
contain needle
?
If ignore-case
is non-nil, the comparison is done without paying
attention to case differences.
(s-contains? "file" "lib/file.js") ;; => t
(s-contains? "nope" "lib/file.js") ;; => nil
(s-contains? "^a" "it's not ^a regexp") ;; => t
(s)
s-lowercase? Are all the letters in s
in lower case?
(s-lowercase? "file") ;; => t
(s-lowercase? "File") ;; => nil
(s-lowercase? "filä") ;; => t
(s)
s-uppercase? Are all the letters in s
in upper case?
(s-uppercase? "HULK SMASH") ;; => t
(s-uppercase? "Bruce no smash") ;; => nil
(s-uppercase? "FöB") ;; => nil
(s)
s-mixedcase? Are there both lower case and upper case letters in s
?
(s-mixedcase? "HULK SMASH") ;; => nil
(s-mixedcase? "Bruce no smash") ;; => t
(s-mixedcase? "BRÜCE") ;; => nil
(s)
s-capitalized? In s
, is the first letter upper case, and all other letters lower case?
(s-capitalized? "Capitalized") ;; => t
(s-capitalized? "I am capitalized") ;; => t
(s-capitalized? "I Am Titleized") ;; => nil
(s)
s-numeric? Is s
a number?
(s-numeric? "123") ;; => t
(s-numeric? "onetwothree") ;; => nil
(s-numeric? "7a") ;; => nil
(old new s)
s-replace Replaces old
with new
in s
.
(s-replace "file" "nope" "lib/file.js") ;; => "lib/nope.js"
(s-replace "^a" "\\1" "it's not ^a regexp") ;; => "it's not \\1 regexp"
(replacements s)
s-replace-all replacements
is a list of cons-cells. Each car
is replaced with cdr
in s
.
(s-replace-all '(("lib" . "test") ("file" . "file_test")) "lib/file.js") ;; => "test/file_test.js"
(s-replace-all '(("lib" . "test") ("test" . "lib")) "lib/test.js") ;; => "test/lib.js"
(s)
s-downcase Convert s
to lower case.
This is a simple wrapper around the built-in downcase
.
(s-downcase "ABC") ;; => "abc"
(s)
s-upcase Convert s
to upper case.
This is a simple wrapper around the built-in upcase
.
(s-upcase "abc") ;; => "ABC"
(s)
s-capitalize Convert the first word's first character to upper case and the rest to lower case in s
.
(s-capitalize "abc DEF") ;; => "Abc def"
(s-capitalize "abc.DEF") ;; => "Abc.def"
(s)
s-titleize Convert each word's first character to upper case and the rest to lower case in s
.
This is a simple wrapper around the built-in capitalize
.
(s-titleize "abc DEF") ;; => "Abc Def"
(s-titleize "abc.DEF") ;; => "Abc.Def"
(s form &rest more)
s-with Threads s
through the forms. Inserts s
as the last item
in the first form, making a list of it if it is not a list
already. If there are more forms, inserts the first form as the
last item in second form, etc.
(s-with " hulk smash " s-trim s-upcase) ;; => "HULK SMASH"
(s-with "My car is a Toyota" (s-replace "car" "name") (s-replace "a Toyota" "Bond") (s-append ", James Bond")) ;; => "My name is Bond, James Bond"
(s-with "abc \ndef \nghi" s-lines (mapcar 's-trim) (s-join "-") s-reverse) ;; => "ihg-fed-cba"
(needle s &optional ignore-case)
s-index-of Returns first index of needle
in s
, or nil.
If ignore-case
is non-nil, the comparison is done without paying
attention to case differences.
(s-index-of "abc" "abcdef") ;; => 0
(s-index-of "CDE" "abcdef" t) ;; => 2
(s-index-of "n.t" "not a regexp") ;; => nil
(s)
s-reverse Return the reverse of s
.
(s-reverse "abc") ;; => "cba"
(s-reverse "ab xyz") ;; => "zyx ba"
(s-reverse "") ;; => ""
(s)
s-presence Return s
if it's s-present?
, otherwise return nil.
(s-presence nil) ;; => nil
(s-presence "") ;; => nil
(s-presence "foo") ;; => "foo"
(template replacer &optional extra)
s-format Format template
with the function replacer
.
replacer
takes an argument of the format variable and optionally
an extra argument which is the extra
value from the call to
s-format
.
Several standard s-format
helper functions are recognized and
adapted for this:
(s-format "${name}" 'gethash hash-table)
(s-format "${name}" 'aget alist)
(s-format "$0" 'elt sequence)
The replacer
function may be used to do any other kind of
transformation.
(s-format "help ${name}! I'm ${malady}" 'aget '(("name" . "nic") ("malady" . "on fire"))) ;; => "help nic! I'm on fire"
(s-format "hello ${name}, nice day" (lambda (var-name) "nic")) ;; => "hello nic, nice day"
(s-format "hello $0, nice $1" 'elt '("nic" "day")) ;; => "hello nic, nice day"
(format-str)
s-lex-format s-format
with the current environment.
format-str
may use the s-format
variable reference to refer to
any variable:
(let ((x 1)) (s-lex-format "x is: ${x}"))
The values of the variables are interpolated with "%s" unless
the variable s-lex-value-as-lisp
is t
and then they are
interpolated with "%S".
(let ((x 1)) (s-lex-format "x is ${x}")) ;; => "x is 1"
(let ((str1 "this") (str2 "that")) (s-lex-format "${str1} and ${str2}")) ;; => "this and that"
(let ((foo "Hello\\nWorld")) (s-lex-format "${foo}")) ;; => "Hello\\nWorld"
(regexp s &optional start end)
s-count-matches Count occurrences of regexp
in `s'.
start
, inclusive, and end
, exclusive, delimit the part of s
to match.
(s-count-matches "a" "aba") ;; => 2
(s-count-matches "a" "aba" 0 2) ;; => 1
(s-count-matches "\\w\\{2\\}[0-9]+" "ab1bab2frobinator") ;; => 2
(s prefix &optional suffix)
s-wrap Wrap string s
with prefix
and optionally suffix
.
Return string s
with prefix
prepended. If suffix
is present, it
is appended, otherwise prefix
is used as both prefix and
suffix.
(s-wrap "foo" "\"") ;; => "\"foo\""
(s-wrap "foo" "(" ")") ;; => "(foo)"
(s-wrap "foo" "bar") ;; => "barfoobar"
(s)
s-split-words Split s
into list of words.
(s-split-words "under_score") ;; => '("under" "score")
(s-split-words "some-dashed-words") ;; => '("some" "dashed" "words")
(s-split-words "evenCamelCase") ;; => '("even" "Camel" "Case")
(s)
s-lower-camel-case Convert s
to lowerCamelCase.
(s-lower-camel-case "some words") ;; => "someWords"
(s-lower-camel-case "dashed-words") ;; => "dashedWords"
(s-lower-camel-case "under_scored_words") ;; => "underScoredWords"
(s)
s-upper-camel-case Convert s
to UpperCamelCase.
(s-upper-camel-case "some words") ;; => "SomeWords"
(s-upper-camel-case "dashed-words") ;; => "DashedWords"
(s-upper-camel-case "under_scored_words") ;; => "UnderScoredWords"
(s)
s-snake-case Convert s
to snake_case.
(s-snake-case "some words") ;; => "some_words"
(s-snake-case "dashed-words") ;; => "dashed_words"
(s-snake-case "camelCasedWords") ;; => "camel_cased_words"
(s)
s-dashed-words Convert s
to dashed-words.
(s-dashed-words "some words") ;; => "some-words"
(s-dashed-words "under_scored_words") ;; => "under-scored-words"
(s-dashed-words "camelCasedWords") ;; => "camel-cased-words"
(s)
s-capitalized-words Convert s
to Capitalized words.
(s-capitalized-words "some words") ;; => "Some words"
(s-capitalized-words "under_scored_words") ;; => "Under scored words"
(s-capitalized-words "camelCasedWords") ;; => "Camel cased words"
(s)
s-titleized-words Convert s
to Titleized Words.
(s-titleized-words "some words") ;; => "Some Words"
(s-titleized-words "under_scored_words") ;; => "Under Scored Words"
(s-titleized-words "camelCasedWords") ;; => "Camel Cased Words"
(s)
s-word-initials Convert s
to its initials.
(s-word-initials "some words") ;; => "sw"
(s-word-initials "under_scored_words") ;; => "usw"
(s-word-initials "camelCasedWords") ;; => "cCW"
What's with the built-in wrappers?
Imagine looking through the function list and seeing s-ends-with?
, but
s-starts-with?
is nowhere to be found. Why? Well, because Emacs already has
string-prefix-p
. Now you're starting out slightly confused, then have to go
somewhere else to dig for the command you were looking for.
The wrapping functions serve as both documentation for existing functions and makes for a consistent API.
Other string related libraries
-
inflections package provides functions for strings pluralization and singularization.
-
levenshtein package provides a function to calculate the Levenshtein distance between two strings.
-
string-utils is another general string manipulation library.
Changelist
From 1.11.0 to 1.12.0
- Alias all functions ending in
?
(Tianxiang Xiong) - Add
s-blank-str?
(Aborn Jiang) - Several bugfixes
From 1.10.0 to 1.11.0
- Add
s-matched-positions-all
(ono hiroko)
From 1.9.0 to 1.10.0
- Add
s-wrap
(Johan Andersson) - Add
s-split-up-to
(Matus Goljer) - Fix
s-reverse
for Unicode combining characters. (Christopher Wellons)
From 1.8.0 to 1.9.0
- Add
s-count-matches
(Lars Andersen)
From 1.7.0 to 1.8.0
- Add
s-present?
ands-present?
(Johan Andersson) - Better handling of international characters
From 1.6.0 to 1.7.0
- Add
s-word-initials
(Sylvain Rousseau) - Better handling of camel cased strings (@Bruce-Connor)
From 1.5.0 to 1.6.0
- Add
s-pad-left
ands-pad-right
- Bugfixes for
s-format
(Nic Ferrier)
From 1.4.0 to 1.5.0
- Add
s-all-match-strings
(Geoff Gole) - Add
s-lex-format
(Nic Ferrier)
From 1.3.1 to 1.4.0
- Add
s-capitalized?
- Add
s-replace-all
- Add
s-slice-at
- Add
s-split
alias forsplit-string
(Rüdiger Sonderfeld) - Add
s-less?
predicate (Rüdiger Sonderfeld) - Add START parameter to
s-matches?
(Rüdiger Sonderfeld) - Bugfixes
From 1.3.0 to 1.3.1
- Add
s-numeric?
- Add
s-match
(Arthur Andersen) - Add
s-format
(Nic Ferrier) - Move .el files out of root to avoid problems with require.
From 1.2.1 to 1.3.0
-
Breaking change:
s-capitalize
now converts the first word's first character to upper case and the rest to lower case.s-titleize
works like the olds-capitalize
and capitalizes each word. (Johan Andersson) -
s-capitalized-words
ands-titleized-words
mirror this change.
Contributors
- Arthur Andersen contributed
s-match
- Rolando contributed
s-shared-start
ands-shared-end
- Johan Andersson contributed
s-presence
,s-present?
and fixeds-titleize
vss-capitalize
- Nic Ferrier added
s-format
ands-lex-format
- Rüdiger Sonderfeld contributed
s-less?
,s-split
and several bugfixes. - Geoff Gole contributed
s-all-match-strings
- Sylvain Rousseau contributed
s-word-initials
- Lars Andersen contributed
s-count-matches
- ono hiroko contributed
s-matched-positions-all
Thanks!
Contribute
Yes, please do. Pure functions in the string manipulation realm only,
please. There's a suite of tests in dev/examples.el
, so remember to add
tests for your function, or I might break it later.
You'll find the repo at:
https://github.com/magnars/s.el
Run the tests with
./run-tests.sh
Create the docs with
./create-docs.sh
I highly recommend that you install these as a pre-commit hook, so that the tests are always running and the docs are always in sync:
cp pre-commit.sh .git/hooks/pre-commit
Oh, and don't edit README.md
directly, it is auto-generated.
Change readme-template.md
or examples-to-docs.el
instead.
License
Copyright (C) 2012-2015 Magnar Sveen
Authors: Magnar Sveen magnars@gmail.com Keywords: strings
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.