From d67c5cb75171fcbf1ca9078452e8fcbe36fc79bc Mon Sep 17 00:00:00 2001 From: Clyybber Date: Tue, 22 Sep 2020 10:43:12 +0200 Subject: [PATCH] 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 --- changelog.md | 9 ++++++++ lib/pure/strutils.nim | 47 ++++++++++++++++++++++++++++++--------- tests/stdlib/tstrutil.nim | 22 ++++++++++++++++++ 3 files changed, 67 insertions(+), 11 deletions(-) diff --git a/changelog.md b/changelog.md index e7afaa229369..1bc1d7303a73 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 156929dee1f8..d97ed9a0715f 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 ef5b041be37c..162b8ea1b027 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 " "