Skip to content

Commit

Permalink
Improve performance
Browse files Browse the repository at this point in the history
The performance has been improved by eliminating one parses step.
And change excludes yaml-unist-parser from the dependencies.
  • Loading branch information
ota-meshi committed Jan 11, 2021
1 parent e78ce14 commit b085855
Show file tree
Hide file tree
Showing 18 changed files with 1,732 additions and 786 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

A YAML parser that produces output [compatible with ESLint](https://eslint.org/docs/developer-guide/working-with-custom-parsers#all-nodes).

This parser is backed by excellent [yaml](https://github.com/eemeli/yaml) and [yaml-unist-parser](https://github.com/ikatyang/yaml-unist-parser) packages.
*This parser is backed by excellent [yaml](https://github.com/eemeli/yaml) package and it is heavily inspired by [yaml-unist-parser](https://github.com/ikatyang/yaml-unist-parser) package.*

[![NPM license](https://img.shields.io/npm/l/yaml-eslint-parser.svg)](https://www.npmjs.com/package/yaml-eslint-parser)
[![NPM version](https://img.shields.io/npm/v/yaml-eslint-parser.svg)](https://www.npmjs.com/package/yaml-eslint-parser)
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"eslint-fix": "npm run lint -- --fix",
"test": "mocha --require ts-node/register \"tests/src/**/*.ts\" --reporter dot --timeout 60000",
"cover": "nyc --reporter=lcov npm run test",
"debug": "mocha --require ts-node/register --inspect \"tests/src/**/*.ts\" --reporter dot",
"debug": "mocha --require ts-node/register/transpile-only --inspect \"tests/src/**/*.ts\" --reporter dot",
"preversion": "npm run lint && npm test",
"update-fixtures": "ts-node ./tools/update-fixtures.ts"
},
Expand All @@ -35,13 +35,14 @@
"homepage": "https://github.com/ota-meshi/yaml-eslint-parser#readme",
"dependencies": {
"eslint-visitor-keys": "^1.3.0",
"yaml": "^1.10.0",
"yaml-unist-parser": "^1.3.1"
"lodash": "^4.17.20",
"yaml": "^1.10.0"
},
"devDependencies": {
"@ota-meshi/eslint-plugin": "^0.0.6",
"@types/eslint": "^7.2.0",
"@types/eslint-visitor-keys": "^1.0.0",
"@types/lodash": "^4.14.167",
"@types/mocha": "^7.0.2",
"@types/node": "^14.0.13",
"@typescript-eslint/eslint-plugin": "^4.9.1",
Expand Down
169 changes: 169 additions & 0 deletions src/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import type {
Comment,
Locations,
Position,
Range,
Token,
YAMLProgram,
} from "./ast"
import type { ASTNode } from "./yaml"
import lodash from "lodash"
import { traverseNodes } from "./traverse"

type CSTRangeData = {
start: number
end: number
}
export class Context {
public readonly code: string

public readonly tokens: Token[] = []

public readonly comments: Comment[] = []

public hasCR = false

private readonly locs: LinesAndColumns

private readonly locsMap = new Map<number, Position>()

private readonly crs: number[]

public constructor(origCode: string) {
const len = origCode.length
const lineStartIndices = [0]
const crs: number[] = []
let code = ""
for (let index = 0; index < len; ) {
const c = origCode[index++]
if (c === "\r") {
const next = origCode[index++] || ""
if (next === "\n") {
code += next
crs.push(index - 2)
} else {
code += `\n${next}`
}
lineStartIndices.push(code.length)
} else {
code += c
if (c === "\n") {
lineStartIndices.push(code.length)
}
}
}
this.code = code
this.locs = new LinesAndColumns(lineStartIndices)
this.hasCR = Boolean(crs.length)
this.crs = crs
}

public remapCR(ast: YAMLProgram): void {
const cache: Record<number, number> = {}
const remapIndex = (index: number): number => {
let result = cache[index]
if (result != null) {
return result
}
result = index
for (const cr of this.crs) {
if (cr < result) {
result++
} else {
break
}
}
return (cache[index] = result)
}
// eslint-disable-next-line func-style -- ignore
const remapRange = (range: [number, number]): [number, number] => {
return [remapIndex(range[0]), remapIndex(range[1])]
}

traverseNodes(ast, {
enterNode(node) {
node.range = remapRange(node.range)
},
leaveNode() {
// ignore
},
})
for (const token of ast.tokens) {
token.range = remapRange(token.range)
}
for (const comment of ast.comments) {
comment.range = remapRange(comment.range)
}
}

public getLocFromIndex(index: number): { line: number; column: number } {
let loc = this.locsMap.get(index)
if (!loc) {
loc = this.locs.getLocFromIndex(index)
this.locsMap.set(index, loc)
}
return {
line: loc.line,
column: loc.column,
}
}

/**
* Get the location information of the given node.
* @param node The node.
*/
public getConvertLocation(node: { range: Range } | ASTNode): Locations {
const [start, end] = node.range!

return {
range: [start, end],
loc: {
start: this.getLocFromIndex(start),
end: this.getLocFromIndex(end),
},
}
}

/**
* Get the location information of the given CSTRange.
* @param node The node.
*/
public getConvertLocationFromCSTRange(
range: CSTRangeData | undefined | null,
): Locations {
return this.getConvertLocation({ range: [range!.start, range!.end] })
}

public addComment(comment: Comment): void {
this.comments.push(comment)
}

/**
* Add token to tokens
*/
public addToken(type: Token["type"], range: Range): Token {
const token = {
type,
value: this.code.slice(...range),
...this.getConvertLocation({ range }),
}
this.tokens.push(token)
return token
}
}

class LinesAndColumns {
private readonly lineStartIndices: number[]

public constructor(lineStartIndices: number[]) {
this.lineStartIndices = lineStartIndices
}

public getLocFromIndex(index: number) {
const lineNumber = lodash.sortedLastIndex(this.lineStartIndices, index)
return {
line: lineNumber,
column: index - this.lineStartIndices[lineNumber - 1],
}
}
}
Loading

0 comments on commit b085855

Please sign in to comment.