Skip to content

Commit

Permalink
Add strutils.indentation and make unindent use it (#15264)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Clyybber authored Sep 22, 2020
1 parent aca9c5f commit d67c5cb
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 11 deletions.
9 changes: 9 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
47 changes: 36 additions & 11 deletions lib/pure/strutils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ when defined(nimVmExportFixed):
export toLower, toUpper

include "system/inclrtl"
import std/private/since

const
Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'}
Expand Down Expand Up @@ -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."
Expand All @@ -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():
Expand All @@ -1553,23 +1559,42 @@ 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>`_
## * `indent proc<#indent,string,Natural,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".} =
Expand Down
22 changes: 22 additions & 0 deletions tests/stdlib/tstrutil.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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 " "

0 comments on commit d67c5cb

Please sign in to comment.