First attempt at Python port, expand tests.

This commit is contained in:
Joshua Bemenderfer 2023-02-07 16:38:23 -05:00
parent ac821e448d
commit e72ff2eccf
11 changed files with 365 additions and 82 deletions

View File

@ -6,7 +6,7 @@
"build:repo": "node ./repo/build.js",
"build": "turbo run build --cache-dir=.turbo",
"dev": "turbo run dev --no-cache --force",
"test": "turbo run test --no-cache --force"
"test": "turbo run test --filter=@terrace/test --no-cache --force"
},
"devDependencies": {
"@terrace/core": "workspace:*",

View File

@ -6,15 +6,36 @@ const testName = process.argv[2]
const tests = {
'linedata:basic': async () => {
const { level, line, head, tail, next } = useDocument(createStdinReader())
while(await next()) {
console.log(`level: ${level()} | head: ${head()} | tail: ${tail()} | line: ${line()}`)
const lineData = createLineData('')
const next = createStdinReader()
while ((lineData.line = await next()) != null) {
parseLine(lineData)
const { level, indent, offsetHead, offsetTail, line } = lineData
console.log(`| level ${level} | indent ${indent} | offsetHead ${offsetHead} | offsetTail ${offsetTail} | line ${line} |`)
}
},
'linedata:tabs': async () => {
const { level, line, head, tail, next } = useDocument(createStdinReader(), '\t')
while(await next()) {
console.log(`level: ${level()} | head: ${head()} | tail: ${tail()} | line: ${line()}`)
const lineData = createLineData('', '\t')
const next = createStdinReader()
while ((lineData.line = await next()) != null) {
parseLine(lineData)
const { level, indent, offsetHead, offsetTail, line } = lineData
console.log(`| level ${level} | indent ${indent} | offsetHead ${offsetHead} | offsetTail ${offsetTail} | line ${line} |`)
}
},
'linedata:head-tail': async () => {
const lineData = createLineData('')
const next = createStdinReader()
while ((lineData.line = await next()) != null) {
parseLine(lineData)
const { level, indent, offsetHead, offsetTail, line } = lineData
const head = line.slice(offsetHead, offsetTail)
const tail = line.slice(offsetTail + 1)
console.log(`| head ${head} | tail ${tail} |`)
}
}
}

1
packages/python/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
__pycache__

View File

@ -0,0 +1,8 @@
{
"name": "@terrace/python",
"version": "0.0.1",
"license": "MIT",
"scripts": {
"test": "python3 ./test/index.py"
}
}

35
packages/python/parser.py Normal file
View File

@ -0,0 +1,35 @@
from typing import TypedDict
class LineData(TypedDict):
line: str
indent: str
level: int
offsetHead: int
offsetTail: int
def createLineData(line: str = '', indent: str = ' ') -> LineData:
return { "line": line, "indent": indent, "level": 0, "offsetHead": 0, "offsetTail": 0 }
def parseLine(lineData: LineData) -> LineData:
# if ((typeof lineData !== 'object' || !lineData) || typeof lineData.level !== 'number') throw new Error(`'lineData' must be an object with string line and numeric level properties`)
# if (typeof lineData.indent !== 'string' || lineData.indent.length === 0 || lineData.indent.length > 1) throw new Error(`'lineData.indent' must be a single-character string`)
# if (typeof lineData.line !== 'string') throw new Error(`'lineData.line' must be a string`)
level = 0
# Repeat previous level for blank lines.
if len(lineData['line']) == 0:
lineData['level'] = lineData['level']
lineData['offsetHead'] = 0
lineData['offsetTail'] = 0
else:
while level < len(lineData['line']) and lineData['line'][level] == lineData['indent'] and level <= lineData['level'] + 1:
level += 1
lineData['level'] = level
lineData['offsetHead'] = level
lineData['offsetTail'] = level
while lineData['offsetTail'] < len(lineData['line']) and lineData['line'][lineData['offsetTail']] != ' ':
lineData['offsetTail'] += 1
return lineData

View File

@ -0,0 +1,58 @@
import sys
import os
sys.path.insert(1, os.path.join(sys.path[0], '..'))
from parser import createLineData, parseLine
def next():
return sys.stdin.readline().rstrip('\n')
def linedata_basic ():
lineData = createLineData('')
while l := next():
lineData['line'] = l
parseLine(lineData)
print("| level {level} | indent {indent} | offsetHead {offsetHead} | offsetTail {offsetTail} | line {line} |".format(
level = lineData['level'], indent = lineData['indent'], offsetHead = lineData['offsetHead'], offsetTail = lineData['offsetTail'], line = lineData['line']
))
def linedata_tabs ():
lineData = createLineData('', '\t')
while l := next():
lineData['line'] = l
parseLine(lineData)
print("| level {level} | indent {indent} | offsetHead {offsetHead} | offsetTail {offsetTail} | line {line} |".format(
level = lineData['level'], indent = lineData['indent'], offsetHead = lineData['offsetHead'], offsetTail = lineData['offsetTail'], line = lineData['line']
))
def linedata_head_tail ():
lineData = createLineData('')
while l := next():
lineData['line'] = l
parseLine(lineData)
head = lineData['line'][lineData['offsetHead']:lineData['offsetTail']] if len(lineData['line']) > lineData['offsetTail'] else ''
tail = lineData['line'][lineData['offsetTail'] + 1:] if len(lineData['line']) > lineData['offsetTail'] + 1 else ''
print("| head {head} | tail {tail} |".format(
head = head, tail = tail
))
tests = {
'linedata:basic': linedata_basic,
'linedata:tabs': linedata_tabs,
'linedata:head-tail': linedata_head_tail
}
def main():
testName = sys.argv[1]
test = tests[testName]
test()
if __name__ == "__main__":
main()

102
pnpm-lock.yaml generated
View File

@ -12,6 +12,20 @@ importers:
jest: 29.4.1
turbo: 1.7.3
experiments/lesson-plans:
specifiers:
'@terrace/core': workspace:*
ejs: ^3.1.8
humanize-duration: ^3.27.3
nunjucks: ^3.2.3
parse-duration: ^1.0.2
dependencies:
'@terrace/core': link:../../packages/js
ejs: 3.1.8
humanize-duration: 3.28.0
nunjucks: 3.2.3
parse-duration: 1.0.2
packages/js:
specifiers:
vite: ^3.2.3
@ -752,6 +766,10 @@ packages:
'@types/yargs-parser': 21.0.0
dev: true
/a-sync-waterfall/1.0.1:
resolution: {integrity: sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==}
dev: false
/acorn/8.8.2:
resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==}
engines: {node: '>=0.4.0'}
@ -782,7 +800,6 @@ packages:
engines: {node: '>=8'}
dependencies:
color-convert: 2.0.1
dev: true
/ansi-styles/5.2.0:
resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
@ -803,10 +820,18 @@ packages:
sprintf-js: 1.0.3
dev: true
/asap/2.0.6:
resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
dev: false
/assertion-error/1.1.0:
resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
dev: true
/async/3.2.4:
resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==}
dev: false
/babel-jest/29.4.1_@babel+core@7.20.12:
resolution: {integrity: sha512-xBZa/pLSsF/1sNpkgsiT3CmY7zV1kAsZ9OxxtrFqYucnOuRftXAfcJqcDVyOPeN4lttWTwhLdu0T9f8uvoPEUg==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@ -881,14 +906,18 @@ packages:
/balanced-match/1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
dev: true
/brace-expansion/1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
dependencies:
balanced-match: 1.0.2
concat-map: 0.0.1
dev: true
/brace-expansion/2.0.1:
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
dependencies:
balanced-match: 1.0.2
dev: false
/braces/3.0.2:
resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
@ -965,7 +994,6 @@ packages:
dependencies:
ansi-styles: 4.3.0
supports-color: 7.2.0
dev: true
/char-regex/1.0.2:
resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
@ -1014,7 +1042,6 @@ packages:
engines: {node: '>=7.0.0'}
dependencies:
color-name: 1.1.4
dev: true
/color-name/1.1.3:
resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
@ -1022,11 +1049,14 @@ packages:
/color-name/1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
dev: true
/commander/5.1.0:
resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==}
engines: {node: '>= 6'}
dev: false
/concat-map/0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
dev: true
/convert-source-map/1.9.0:
resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==}
@ -1083,6 +1113,14 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dev: true
/ejs/3.1.8:
resolution: {integrity: sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==}
engines: {node: '>=0.10.0'}
hasBin: true
dependencies:
jake: 10.8.5
dev: false
/electron-to-chromium/1.4.286:
resolution: {integrity: sha512-Vp3CVhmYpgf4iXNKAucoQUDcCrBQX3XLBtwgFqP9BUXuucgvAV9zWp1kYU7LL9j4++s9O+12cb3wMtN4SJy6UQ==}
dev: true
@ -1374,6 +1412,12 @@ packages:
bser: 2.1.1
dev: true
/filelist/1.0.4:
resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
dependencies:
minimatch: 5.1.6
dev: false
/fill-range/7.0.1:
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
engines: {node: '>=8'}
@ -1457,7 +1501,6 @@ packages:
/has-flag/4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
dev: true
/has/1.0.3:
resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
@ -1475,6 +1518,10 @@ packages:
engines: {node: '>=10.17.0'}
dev: true
/humanize-duration/3.28.0:
resolution: {integrity: sha512-jMAxraOOmHuPbffLVDKkEKi/NeG8dMqP8lGRd6Tbf7JgAeG33jjgPWDbXXU7ypCI0o+oNKJFgbSB9FKVdWNI2A==}
dev: false
/import-local/3.1.0:
resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==}
engines: {node: '>=8'}
@ -1580,6 +1627,17 @@ packages:
istanbul-lib-report: 3.0.0
dev: true
/jake/10.8.5:
resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==}
engines: {node: '>=10'}
hasBin: true
dependencies:
async: 3.2.4
chalk: 4.1.2
filelist: 1.0.4
minimatch: 3.1.2
dev: false
/jest-changed-files/29.4.0:
resolution: {integrity: sha512-rnI1oPxgFghoz32Y8eZsGJMjW54UlqT17ycQeCEktcxxwqqKdlj9afl8LNeO0Pbu+h2JQHThQP0BzS67eTRx4w==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@ -2135,7 +2193,13 @@ packages:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
dependencies:
brace-expansion: 1.1.11
dev: true
/minimatch/5.1.6:
resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
engines: {node: '>=10'}
dependencies:
brace-expansion: 2.0.1
dev: false
/ms/2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
@ -2171,6 +2235,21 @@ packages:
path-key: 3.1.1
dev: true
/nunjucks/3.2.3:
resolution: {integrity: sha512-psb6xjLj47+fE76JdZwskvwG4MYsQKXUtMsPh6U0YMvmyjRtKRFcxnlXGWglNybtNTNVmGdp94K62/+NjF5FDQ==}
engines: {node: '>= 6.9.0'}
hasBin: true
peerDependencies:
chokidar: ^3.3.0
peerDependenciesMeta:
chokidar:
optional: true
dependencies:
a-sync-waterfall: 1.0.1
asap: 2.0.6
commander: 5.1.0
dev: false
/once/1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
dependencies:
@ -2210,6 +2289,10 @@ packages:
engines: {node: '>=6'}
dev: true
/parse-duration/1.0.2:
resolution: {integrity: sha512-Dg27N6mfok+ow1a2rj/nRjtCfaKrHUZV2SJpEn/s8GaVUSlf4GGRCRP1c13Hj+wfPKVMrFDqLMLITkYKgKxyyg==}
dev: false
/parse-json/5.2.0:
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
engines: {node: '>=8'}
@ -2456,7 +2539,6 @@ packages:
engines: {node: '>=8'}
dependencies:
has-flag: 4.0.0
dev: true
/supports-color/8.1.1:
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}

View File

@ -1,6 +1,6 @@
import fs from 'node:fs/promises'
import { useDocument } from '@terrace/core'
import { createReadlineReader } from '@terrace/core/readers/node-readline'
import { createFileReader } from '@terrace/core/readers/node-readline'
function useHelpers({ next, level, head, tail }) {
@ -18,7 +18,7 @@ function useHelpers({ next, level, head, tail }) {
}
async function buildPackage() {
const { next, level, head, tail, match } = useDocument(createReadlineReader('./repo/package.tce'))
const { next, level, head, tail, match } = useDocument(createFileReader('./repo/package.tce'))
const { kvObject } = useHelpers({ next, level, head, tail })
const pkg = {}
@ -38,7 +38,7 @@ async function buildPackage() {
}
async function buildTurbo() {
const { next, level, head, tail, match } = useDocument(createReadlineReader('./repo/turbo.tce'))
const { next, level, head, tail, match } = useDocument(createFileReader('./repo/turbo.tce'))
const { kvObject } = useHelpers({ next, level, head, tail })
const turbo = {}

View File

@ -8,7 +8,7 @@ scripts
build:repo node ./repo/build.js
build turbo run build --cache-dir=.turbo
dev turbo run dev --no-cache --force
test turbo run test --no-cache --force
test turbo run test --filter=@terrace/test --no-cache --force
devDependencies
@terrace/core workspace:*

View File

@ -28,29 +28,30 @@ async function loadTests(path) {
const propertyLevel = level()
if (match('input')) {
// TODO: Find a way to handle newlines better.
if (tail().startsWith('literal')) {
if (tail()) {
test.input = tail()
.split('literal ').join('')
.replaceAll('\\n', '\n')
.replaceAll('\\t', '\t')
continue
}
while (await next(propertyLevel)) test.input.push(line(propertyLevel))
test.input = test.input.join('\n').trim()
while (await next(propertyLevel)) test.input.push(line(propertyLevel + 1))
test.input = test.input.join('\n').trimEnd()
}
if (match('output')) {
// TODO: Find a way to handle newlines better.
if (tail().startsWith('literal')) {
test.output = tail().split('literal ').join('').replace('\\n', '\n')
continue
}
while (await next(propertyLevel)) test.output.push(line())
while (await next(propertyLevel)) test.output.push(line(propertyLevel + 1))
}
}
test.output = test.output.join('\n').trim()
test.input = test.input
.replaceAll('\\n', '\n')
.replaceAll('\\t', '\t')
.replaceAll('\\s', ' ')
test.output = test.output.join('\n').trimEnd()
.replaceAll('\\n', '\n')
.replaceAll('\\t', '\t')
.replaceAll('\\s', ' ')
}
}
@ -64,6 +65,7 @@ function callTest(pkg, name, input) {
input
})
// TODO: Find a way to clean all this trimming up. Most caused by VSCode happily trimming empty spaces off the ends of lines.
resolve(stdout.toString().trim())
})
}

View File

@ -1,54 +1,130 @@
#schema test
describe LineData
it Handles a blank line at indent level 0
key linedata:basic
packages js
input literal \n
output
level: 0 | head: | tail: | line:
it Handles a blank line at indent level 0
key linedata:basic
packages js python
input \n
output
| level 0 | indent | offsetHead 0 | offsetTail 0 | line |
it Handles a blank line with a single space at indent level 1
key linedata:basic
packages js
input literal \n
output
level: 1 | head: | tail: | line:
it Handles a blank line with a single space
key linedata:basic
packages js python
input \s
output
| level 1 | indent | offsetHead 1 | offsetTail 1 | line |
it Handles a blank line with two spaces
key linedata:basic
packages js
input literal \n
output
level: 2 | head: | tail: | line:
it Handles a blank line with two spaces
key linedata:basic
packages js python
input \s\s
output
| level 2 | indent | offsetHead 2 | offsetTail 2 | line |
it Handles a normal line at indent level 1
key linedata:basic
packages js
input literal line 1
output
level: 1 | head: line | tail: 1 | line: line 1
it Handles a normal line at indent level 0
key linedata:basic
packages js python
input
line 1
output
| level 0 | indent | offsetHead 0 | offsetTail 4 | line line 1 |
it Handles a normal line at indent level 1 indented with tabs
key linedata:tabs
packages js
input literal \tline 1
output
level: 1 | head: line | tail: 1 | line: line 1
it Handles a normal line at indent level 1
key linedata:basic
packages js python
input
line 1
output
| level 1 | indent | offsetHead 1 | offsetTail 5 | line line 1 |
it Handles a normal line at indent level 2 indented with tabs
key linedata:tabs
packages js
input literal \t\tline 1
output
level: 2 | head: line | tail: 1 | line: line 1
it Handles a normal line at indent level 2
key linedata:basic
packages js python
input
line 1
output
| level 2 | indent | offsetHead 2 | offsetTail 6 | line line 1 |
it Nests a normal line under a preceding normal line
key linedata:basic
packages js
input
line 1
line 2
output
level: 0 | head: line | tail: 1 | line: line 1
level: 1 | head: line | tail: 2 | line: line 2
it Handles a normal line at indent level 1 indented with tabs
key linedata:tabs
packages js python
input
\tline 1
output
| level 1 | indent \t | offsetHead 1 | offsetTail 5 | line \tline 1 |
it Handles a normal line at indent level 2 indented with tabs
key linedata:tabs
packages js python
input
\t\tline 1
output
| level 2 | indent \t | offsetHead 2 | offsetTail 6 | line \t\tline 1 |
it Nests a normal line under a preceding normal line
key linedata:basic
packages js python
input
line 1
line 2
output
| level 0 | indent | offsetHead 0 | offsetTail 4 | line line 1 |
| level 1 | indent | offsetHead 1 | offsetTail 5 | line line 2 |
it Nests multiple normal lines under a preceding normal line
key linedata:basic
packages js python
input
line 1
line 2
line 3
line 4
output
| level 0 | indent | offsetHead 0 | offsetTail 4 | line line 1 |
| level 1 | indent | offsetHead 1 | offsetTail 5 | line line 2 |
| level 1 | indent | offsetHead 1 | offsetTail 5 | line line 3 |
| level 1 | indent | offsetHead 1 | offsetTail 5 | line line 4 |
it Does not nest an empty line under a preceding normal line
key linedata:basic
packages js python
comment Two newlines are needed here. A single newline will look to readline as if the input is finished.
input line 1\n\n
output
| level 0 | indent | offsetHead 0 | offsetTail 4 | line line 1 |
| level 0 | indent | offsetHead 0 | offsetTail 0 | line |
it Does not nest multiple empty lines under a preceding normal line
key linedata:basic
packages js python
comment Four newlines are needed here. A single newline will look to readline as if the input is finished.
input line 1\n\n\n\n
output
| level 0 | indent | offsetHead 0 | offsetTail 4 | line line 1 |
| level 0 | indent | offsetHead 0 | offsetTail 0 | line |
| level 0 | indent | offsetHead 0 | offsetTail 0 | line |
| level 0 | indent | offsetHead 0 | offsetTail 0 | line |
it Handles head and tail matching for lines with head and tail
key linedata:head-tail
packages js python
input
head1 tail1 tail2 tail3
output
| head head1 | tail tail1 tail2 tail3 |
it Handles head and tail matching for lines with head but no tail
key linedata:head-tail
packages js python
input
head1
output
| head head1 | tail |
it Handles head and tail matching for lines with head and trailing space
key linedata:head-tail
packages js python
input head1 \n
output
| head head1 | tail |