summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorClyybber <darkmine956@gmail.com>2020-09-22 10:43:12 +0200
committerGitHub <noreply@github.com>2020-09-22 10:43:12 +0200
commitd67c5cb75171fcbf1ca9078452e8fcbe36fc79bc (patch)
tree461841c502d8921b1b4b42c46f66930ad17f6f79
parentaca9c5fb30fe2de12671e9103e93b81201bb8e71 (diff)
downloadNim-d67c5cb75171fcbf1ca9078452e8fcbe36fc79bc.tar.gz
Add strutils.indentation and make unindent use it (#15264)
* Add strutils.indentation and make unindent use it

* Code style

* Fix bootstrapping

* Improve wording

* Fix test

* Introduce without breaking change

* Fix

* Reduce diff

* Fix docs link

* Add since annotation

* Update changelog
-rw-r--r--changelog.md9
-rw-r--r--lib/pure/strutils.nim47
-rw-r--r--tests/stdlib/tstrutil.nim22
3 files changed, 67 insertions, 11 deletions
diff --git a/changelog.md b/changelog.md
index e7afaa229..1bc1d7303 100644
--- a/changelog.md
+++ b/changelog.md
@@ -187,6 +187,15 @@
 
 - Add missing attributes and methods to `dom.Navigator` like `deviceMemory`, `onLine`, `vibrate()`, etc.
 
+- Added `strutils.indentation` and `strutils.dedent` which enable indented string literals:
+  ```nim
+  import strutils
+  echo dedent """
+    This
+      is
+        cool!
+    """
+  ``` 
 
 ## Language changes
 
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 156929dee..d97ed9a07 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -82,6 +82,7 @@ when defined(nimVmExportFixed):
   export toLower, toUpper
 
 include "system/inclrtl"
+import std/private/since
 
 const
   Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'}
@@ -1511,6 +1512,7 @@ proc indent*(s: string, count: Natural, padding: string = " "): string
   ## * `alignLeft proc<#alignLeft,string,Natural,char>`_
   ## * `spaces proc<#spaces,Natural>`_
   ## * `unindent proc<#unindent,string,Natural,string>`_
+  ## * `dedent proc<#dedent,string,Natural,string>`_
   runnableExamples:
     doAssert indent("First line\c\l and second line.", 2) ==
              "  First line\l   and second line."
@@ -1524,21 +1526,25 @@ proc indent*(s: string, count: Natural, padding: string = " "): string
     result.add(line)
     i.inc
 
-proc unindent*(s: string, count: Natural, padding: string = " "): string
+proc unindent*(s: string, count: Natural = int.high, padding: string = " "): string
     {.noSideEffect, rtl, extern: "nsuUnindent".} =
   ## Unindents each line in ``s`` by ``count`` amount of ``padding``.
-  ## Sometimes called `dedent`:idx:
   ##
   ## **Note:** This does not preserve the new line characters used in ``s``.
   ##
   ## See also:
+  ## * `dedent proc<#dedent,string,Natural,string>`
   ## * `align proc<#align,string,Natural,char>`_
   ## * `alignLeft proc<#alignLeft,string,Natural,char>`_
   ## * `spaces proc<#spaces,Natural>`_
   ## * `indent proc<#indent,string,Natural,string>`_
   runnableExamples:
-    doAssert unindent("  First line\l   and second line", 3) ==
-             "First line\land second line"
+    let x = """
+      Hello
+        There
+    """.unindent()
+
+    doAssert x == "Hello\nThere\n"
   result = ""
   var i = 0
   for line in s.splitLines():
@@ -1553,11 +1559,30 @@ proc unindent*(s: string, count: Natural, padding: string = " "): string
     result.add(line[indentCount*padding.len .. ^1])
     i.inc
 
-proc unindent*(s: string): string
-    {.noSideEffect, rtl, extern: "nsuUnindentAll".} =
-  ## Removes all indentation composed of whitespace from each line in ``s``.
+proc indentation*(s: string): Natural {.since: (1, 3).} =
+  ## Returns the amount of indentation all lines of ``s`` have in common,
+  ## ignoring lines that consist only of whitespace.
+  result = int.high
+  for line in s.splitLines:
+    for i, c in line:
+      if i >= result: break
+      elif c != ' ':
+        result = i
+        break
+  if result == int.high:
+    result = 0
+
+proc dedent*(s: string, count: Natural = indentation(s)): string
+    {.noSideEffect, rtl, extern: "nsuDedent", since: (1, 3).} =
+  ## Unindents each line in ``s`` by ``count`` amount of ``padding``.
+  ## The only difference between this and `unindent proc<#unindent,string,Natural,string>`
+  ## is that this by default only cuts off the amount of indentation that all
+  ## lines of ``s`` share as opposed to all indentation. It only supports spcaes as padding.
+  ##
+  ## **Note:** This does not preserve the new line characters used in ``s``.
   ##
   ## See also:
+  ## * `unindent proc<#unindent,string,Natural,string>`
   ## * `align proc<#align,string,Natural,char>`_
   ## * `alignLeft proc<#alignLeft,string,Natural,char>`_
   ## * `spaces proc<#spaces,Natural>`_
@@ -1565,11 +1590,11 @@ proc unindent*(s: string): string
   runnableExamples:
     let x = """
       Hello
-      There
-    """.unindent()
+        There
+    """.dedent()
 
-    doAssert x == "Hello\nThere\n"
-  unindent(s, 1000) # TODO: Passing a 1000 is a bit hackish.
+    doAssert x == "Hello\n  There\n"
+  unindent(s, count, " ")
 
 proc delete*(s: var string, first, last: int) {.noSideEffect,
   rtl, extern: "nsuDelete".} =
diff --git a/tests/stdlib/tstrutil.nim b/tests/stdlib/tstrutil.nim
index ef5b041be..162b8ea1b 100644
--- a/tests/stdlib/tstrutil.nim
+++ b/tests/stdlib/tstrutil.nim
@@ -435,3 +435,25 @@ block:
   doAssert a == f1
   doAssert b == f2
   doAssert c == f3
+
+block:
+  assert 0 == indentation """
+hey
+  low
+    there
+"""
+  assert 2 == indentation """
+  hey
+    low
+      there
+"""
+  assert 2 == indentation """  hey
+    low
+      there
+"""
+  assert 2 == indentation """  hey
+    low
+      there"""
+  assert 0 == indentation ""
+  assert 0 == indentation "  \n  \n"
+  assert 0 == indentation "    "