@dprint/core
Advanced tools
Comparing version 0.4.4 to 0.5.0
@@ -6,2 +6,4 @@ 'use strict'; | ||
var os = require('os'); | ||
var types = require('@dprint/types'); | ||
var wasmPrinter = require('./wasm/dprint_rust_printer'); | ||
@@ -12,3 +14,3 @@ const defaultValues = { | ||
useTabs: false, | ||
newlineKind: "auto" | ||
newLineKind: "auto" | ||
}; | ||
@@ -22,3 +24,3 @@ function resolveConfiguration(config) { | ||
useTabs: getValue("useTabs", defaultValues.useTabs, ensureBoolean), | ||
newlineKind: getNewLineKind() | ||
newLineKind: getNewLineKind() | ||
}; | ||
@@ -31,5 +33,5 @@ addExcessPropertyDiagnostics(); | ||
function getNewLineKind() { | ||
const newlineKind = config.newlineKind; | ||
delete config.newlineKind; | ||
switch (newlineKind) { | ||
const newLineKind = config.newLineKind; | ||
delete config.newLineKind; | ||
switch (newLineKind) { | ||
case "auto": | ||
@@ -43,12 +45,12 @@ return "auto"; | ||
case undefined: | ||
return defaultValues.newlineKind; | ||
return defaultValues.newLineKind; | ||
case "system": | ||
return os.EOL === "\r\n" ? "\r\n" : "\n"; | ||
default: | ||
const propertyName = "newlineKind"; | ||
const propertyName = "newLineKind"; | ||
diagnostics.push({ | ||
propertyName, | ||
message: `Unknown configuration specified for '${propertyName}': ${newlineKind}` | ||
message: `Unknown configuration specified for '${propertyName}': ${newLineKind}` | ||
}); | ||
return defaultValues["newlineKind"]; | ||
return defaultValues["newLineKind"]; | ||
} | ||
@@ -89,3 +91,3 @@ } | ||
propertyName: propertyName, | ||
message: `Unexpected property in configuration: ${propertyName}` | ||
message: `Unknown property in configuration: ${propertyName}` | ||
}); | ||
@@ -96,20 +98,2 @@ } | ||
(function (PrintItemKind) { | ||
PrintItemKind[PrintItemKind["RawString"] = 0] = "RawString"; | ||
PrintItemKind[PrintItemKind["Condition"] = 1] = "Condition"; | ||
PrintItemKind[PrintItemKind["Info"] = 2] = "Info"; | ||
})(exports.PrintItemKind || (exports.PrintItemKind = {})); | ||
(function (Signal) { | ||
Signal[Signal["NewLine"] = 0] = "NewLine"; | ||
Signal[Signal["SpaceOrNewLine"] = 1] = "SpaceOrNewLine"; | ||
Signal[Signal["ExpectNewLine"] = 2] = "ExpectNewLine"; | ||
Signal[Signal["StartIndent"] = 3] = "StartIndent"; | ||
Signal[Signal["FinishIndent"] = 4] = "FinishIndent"; | ||
Signal[Signal["StartNewlineGroup"] = 5] = "StartNewlineGroup"; | ||
Signal[Signal["FinishNewLineGroup"] = 6] = "FinishNewLineGroup"; | ||
Signal[Signal["SingleIndent"] = 7] = "SingleIndent"; | ||
Signal[Signal["StartIgnoringIndent"] = 8] = "StartIgnoringIndent"; | ||
Signal[Signal["FinishIgnoringIndent"] = 9] = "FinishIgnoringIndent"; | ||
})(exports.Signal || (exports.Signal = {})); | ||
(function (conditionResolvers) { | ||
@@ -145,3 +129,3 @@ function isStartOfNewLine(conditionContext) { | ||
function getResolvedEndInfo() { | ||
if (endInfo.kind === exports.PrintItemKind.Info) | ||
if (endInfo.kind === types.PrintItemKind.Info) | ||
return conditionContext.getResolvedInfo(endInfo); | ||
@@ -163,5 +147,2 @@ return endInfo; | ||
function assertNever(value) { | ||
return throwError(`Unhandled value: ${JSON.stringify(value)}`); | ||
} | ||
function throwError(message) { | ||
@@ -214,11 +195,11 @@ throw getError(message); | ||
function* withIndent(item) { | ||
yield exports.Signal.StartIndent; | ||
yield types.Signal.StartIndent; | ||
yield* item; | ||
yield exports.Signal.FinishIndent; | ||
yield types.Signal.FinishIndent; | ||
} | ||
parserHelpers.withIndent = withIndent; | ||
function* newlineGroup(item) { | ||
yield exports.Signal.StartNewlineGroup; | ||
yield types.Signal.StartNewLineGroup; | ||
yield* item; | ||
yield exports.Signal.FinishNewLineGroup; | ||
yield types.Signal.FinishNewLineGroup; | ||
} | ||
@@ -241,6 +222,6 @@ parserHelpers.newlineGroup = newlineGroup; | ||
parserHelpers.toPrintItemIterable = toPrintItemIterable; | ||
function* surroundWithNewLines(item, context) { | ||
yield context.newlineKind; | ||
function* surroundWithNewLines(item) { | ||
yield types.Signal.NewLine; | ||
yield* item; | ||
yield context.newlineKind; | ||
yield types.Signal.NewLine; | ||
} | ||
@@ -265,3 +246,3 @@ parserHelpers.surroundWithNewLines = surroundWithNewLines; | ||
return { | ||
kind: exports.PrintItemKind.Info, | ||
kind: types.PrintItemKind.Info, | ||
name | ||
@@ -271,2 +252,27 @@ }; | ||
parserHelpers.createInfo = createInfo; | ||
function* parseRawString(text) { | ||
let hasIgnoredIndent = false; | ||
const lines = text.split(/\r?\n/); | ||
for (let i = 0; i < lines.length; i++) { | ||
if (i > 0) { | ||
if (!hasIgnoredIndent) { | ||
yield types.Signal.StartIgnoringIndent; | ||
hasIgnoredIndent = true; | ||
} | ||
yield types.Signal.NewLine; | ||
} | ||
yield* parseLine(lines[i]); | ||
} | ||
if (hasIgnoredIndent) | ||
yield types.Signal.FinishIgnoringIndent; | ||
function* parseLine(line) { | ||
const parts = line.split("\t"); | ||
for (let i = 0; i < parts.length; i++) { | ||
if (i > 0) | ||
yield types.Signal.Tab; | ||
yield* parts[i]; | ||
} | ||
} | ||
} | ||
parserHelpers.parseRawString = parseRawString; | ||
})(exports.parserHelpers || (exports.parserHelpers = {})); | ||
@@ -277,5 +283,5 @@ | ||
function newlineIfHangingSpaceOtherwise(options) { | ||
const { context, startInfo, endInfo, spaceChar = " " } = options; | ||
const { startInfo, endInfo, spaceChar = " " } = options; | ||
return { | ||
kind: exports.PrintItemKind.Condition, | ||
kind: types.PrintItemKind.Condition, | ||
name: "newLineIfHangingSpaceOtherwise", | ||
@@ -285,3 +291,3 @@ condition: conditionContext => { | ||
}, | ||
true: [context.newlineKind], | ||
true: [types.Signal.NewLine], | ||
false: [spaceChar] | ||
@@ -292,9 +298,9 @@ }; | ||
function newlineIfMultipleLinesSpaceOrNewlineOtherwise(options) { | ||
const { context, startInfo, endInfo } = options; | ||
const { startInfo, endInfo } = options; | ||
return { | ||
name: "newlineIfMultipleLinesSpaceOrNewlineOtherwise", | ||
kind: exports.PrintItemKind.Condition, | ||
kind: types.PrintItemKind.Condition, | ||
condition: conditionContext => exports.conditionResolvers.isMultipleLines(conditionContext, startInfo, endInfo || conditionContext.writerInfo, false), | ||
true: [context.newlineKind], | ||
false: [exports.Signal.SpaceOrNewLine] | ||
true: [types.Signal.NewLine], | ||
false: [types.Signal.SpaceOrNewLine] | ||
}; | ||
@@ -305,6 +311,6 @@ } | ||
return { | ||
kind: exports.PrintItemKind.Condition, | ||
kind: types.PrintItemKind.Condition, | ||
name: "singleIndentIfStartOfLine", | ||
condition: exports.conditionResolvers.isStartOfNewLine, | ||
true: [exports.Signal.SingleIndent] | ||
true: [types.Signal.SingleIndent] | ||
}; | ||
@@ -316,3 +322,3 @@ } | ||
yield { | ||
kind: exports.PrintItemKind.Condition, | ||
kind: types.PrintItemKind.Condition, | ||
name: "indentIfStartOfLine", | ||
@@ -328,3 +334,3 @@ condition: exports.conditionResolvers.isStartOfNewLine, | ||
yield { | ||
kind: exports.PrintItemKind.Condition, | ||
kind: types.PrintItemKind.Condition, | ||
name: "withIndentIfStartOfLineIndented", | ||
@@ -341,3 +347,3 @@ condition: context => { | ||
return { | ||
kind: exports.PrintItemKind.Condition, | ||
kind: types.PrintItemKind.Condition, | ||
name: "forceReevaluationOnceInfoResolved", | ||
@@ -364,476 +370,130 @@ condition: conditionContext => { | ||
class Writer { | ||
constructor(options) { | ||
this.options = options; | ||
this.singleIndentationText = this.options.useTabs ? "\t" : " ".repeat(options.indentWidth); | ||
this.state = { | ||
currentLineColumn: 0, | ||
currentLineNumber: 0, | ||
lastLineIndentLevel: 0, | ||
indentLevel: 0, | ||
indentText: "", | ||
expectNewLineNext: false, | ||
items: [], | ||
indentLevelStates: [], | ||
ignoreIndent: false | ||
}; | ||
} | ||
onNewLine(action) { | ||
if (this.fireOnNewLine != null) | ||
throwError(`Cannot call ${"onNewLine"} multiple times.`); | ||
this.fireOnNewLine = action; | ||
} | ||
getState() { | ||
return Writer.cloneState(this.state); | ||
} | ||
setState(state) { | ||
this.state = Writer.cloneState(state); | ||
} | ||
static cloneState(state) { | ||
const newState = { | ||
currentLineColumn: state.currentLineColumn, | ||
currentLineNumber: state.currentLineNumber, | ||
lastLineIndentLevel: state.lastLineIndentLevel, | ||
expectNewLineNext: state.expectNewLineNext, | ||
indentLevel: state.indentLevel, | ||
indentText: state.indentText, | ||
items: [...state.items], | ||
indentLevelStates: [...state.indentLevelStates], | ||
ignoreIndent: state.ignoreIndent | ||
}; | ||
return newState; | ||
} | ||
get currentLineColumn() { | ||
return this.state.currentLineColumn; | ||
} | ||
set currentLineColumn(value) { | ||
this.state.currentLineColumn = value; | ||
} | ||
get currentLineNumber() { | ||
return this.state.currentLineNumber; | ||
} | ||
set currentLineNumber(value) { | ||
this.state.currentLineNumber = value; | ||
} | ||
get lastLineIndentLevel() { | ||
return this.state.lastLineIndentLevel; | ||
} | ||
set lastLineIndentLevel(value) { | ||
this.state.lastLineIndentLevel = value; | ||
} | ||
get expectNewLineNext() { | ||
return this.state.expectNewLineNext; | ||
} | ||
set expectNewLineNext(value) { | ||
this.state.expectNewLineNext = value; | ||
} | ||
get indentLevel() { | ||
return this.state.indentLevel; | ||
} | ||
set indentLevel(level) { | ||
if (this.indentLevel === level) | ||
return; | ||
this.state.indentLevel = level; | ||
this.state.indentText = this.singleIndentationText.repeat(level); | ||
if (this.currentLineColumn === 0) | ||
this.lastLineIndentLevel = level; | ||
} | ||
get indentText() { | ||
return this.state.indentText; | ||
} | ||
get ignoreIndent() { | ||
return this.state.ignoreIndent; | ||
} | ||
set ignoreIndent(value) { | ||
this.state.ignoreIndent = value; | ||
} | ||
get indentLevelStates() { | ||
return this.state.indentLevelStates; | ||
} | ||
get items() { | ||
return this.state.items; | ||
} | ||
singleIndent() { | ||
this.write(this.singleIndentationText); | ||
} | ||
write(text) { | ||
this.validateText(text); | ||
this.baseWrite(text); | ||
} | ||
validateText(text) { | ||
if (text === "\n" || text === "\r\n") | ||
return; | ||
if (text.includes("\n")) | ||
throwError("Printer error: The parser should write"); | ||
} | ||
baseWrite(text) { | ||
const startsWithNewLine = text[0] === "\n" || text[0] === "\r" && text[1] === "\n"; | ||
if (this.expectNewLineNext) { | ||
this.expectNewLineNext = false; | ||
if (!startsWithNewLine) { | ||
this.baseWrite(this.options.newlineKind); | ||
this.baseWrite(text); | ||
return; | ||
} | ||
function preparePrintItems(items) { | ||
const getNextId = (id => () => id++)(0); | ||
const rustContext = new class { | ||
getResolvedCondition(condition) { | ||
updateItem(condition); | ||
return this.innerContext.getResolvedCondition(condition); | ||
} | ||
if (this.currentLineColumn === 0 && !startsWithNewLine && this.indentLevel > 0 && !this.ignoreIndent) | ||
text = this.indentText + text; | ||
for (let i = 0; i < text.length; i++) { | ||
if (text[i] === "\n") { | ||
this.currentLineColumn = 0; | ||
this.currentLineNumber++; | ||
this.lastLineIndentLevel = this.indentLevel; | ||
this.fireOnNewLine(); | ||
} | ||
else { | ||
if (this.currentLineColumn === 0) | ||
this.lastLineIndentLevel = this.indentLevel; | ||
this.currentLineColumn++; | ||
} | ||
getResolvedInfo(info) { | ||
updateItem(info); | ||
return this.innerContext.getResolvedInfo(info); | ||
} | ||
this.state.items.push(text); | ||
} | ||
startIndent() { | ||
this.indentLevelStates.push(this.indentLevel); | ||
this.indentLevel++; | ||
} | ||
finishIndent() { | ||
const originalIndentLevel = this.indentLevelStates.pop(); | ||
if (originalIndentLevel == null) | ||
return throwError(`For some reason ${"finishIndent"} was called without a corresponding ${"startIndent"}.`); | ||
this.indentLevel = originalIndentLevel; | ||
} | ||
startIgnoringIndent() { | ||
this.ignoreIndent = true; | ||
} | ||
finishIgnoringIndent() { | ||
this.ignoreIndent = false; | ||
} | ||
markExpectNewLine() { | ||
this.expectNewLineNext = true; | ||
} | ||
getLineStartIndentLevel() { | ||
return this.lastLineIndentLevel; | ||
} | ||
getIndentationLevel() { | ||
return this.indentLevel; | ||
} | ||
getLineStartColumnNumber() { | ||
return this.singleIndentationText.length * this.lastLineIndentLevel; | ||
} | ||
getLineColumn() { | ||
if (this.currentLineColumn === 0) | ||
return this.indentText.length; | ||
return this.currentLineColumn; | ||
} | ||
getLineNumber() { | ||
return this.currentLineNumber; | ||
} | ||
toString() { | ||
return this.items.join(""); | ||
} | ||
} | ||
const exitSymbol = Symbol("Used in certain cases when a save point is restored."); | ||
class RepeatableConditionCache { | ||
constructor() { | ||
this.repeatableConditions = new Map(); | ||
} | ||
getOrCreate(condition) { | ||
let repeatableCondition = this.repeatableConditions.get(condition); | ||
if (repeatableCondition == null) { | ||
repeatableCondition = this.create(condition); | ||
this.repeatableConditions.set(condition, repeatableCondition); | ||
get writerInfo() { | ||
return this.innerContext.writerInfo; | ||
} | ||
return repeatableCondition; | ||
} | ||
create(condition) { | ||
return { | ||
name: condition.name, | ||
originalCondition: condition, | ||
condition: condition.condition, | ||
true: condition.true && Array.from(condition.true), | ||
false: condition.false && Array.from(condition.false) | ||
}; | ||
} | ||
} | ||
function deepIterableToContainer(iterable) { | ||
const repeatableConditionCache = new RepeatableConditionCache(); | ||
return getContainer(iterable, undefined); | ||
function getContainer(items, parent) { | ||
const container = { | ||
items: [], | ||
parent | ||
}; | ||
for (const item of items) { | ||
if (typeof item === "object" && item.kind === exports.PrintItemKind.Condition) | ||
container.items.push(createConditionContainer(repeatableConditionCache.getOrCreate(item), container)); | ||
else | ||
container.items.push(item); | ||
setInnerContext(context) { | ||
if (context === this) | ||
throw new Error("Something went wrong and the inner context was set to the same object as the outer context."); | ||
this.innerContext = context; | ||
} | ||
return container; | ||
}(); | ||
return innerGetItemsAsArray(items); | ||
function innerGetItemsAsArray(items) { | ||
items = items instanceof Array ? items : Array.from(items); | ||
for (const item of items) | ||
updateItem(item); | ||
return items; | ||
} | ||
function createConditionContainer(repeatableCondition, parent) { | ||
return { | ||
kind: exports.PrintItemKind.Condition, | ||
name: repeatableCondition.name, | ||
condition: repeatableCondition.condition, | ||
originalCondition: repeatableCondition.originalCondition, | ||
get true() { | ||
const value = repeatableCondition.true && getContainer(repeatableCondition.true, parent); | ||
Object.defineProperty(this, "true", { value }); | ||
return value; | ||
}, | ||
get false() { | ||
const value = repeatableCondition.false && getContainer(repeatableCondition.false, parent); | ||
Object.defineProperty(this, "false", { value }); | ||
return value; | ||
} | ||
}; | ||
} | ||
} | ||
function print(iterable, options) { | ||
const writer = new Writer(options); | ||
const resolvedConditions = new Map(); | ||
const resolvedInfos = new Map(); | ||
const lookAheadSavePoints = new Map(); | ||
let possibleNewLineSavePoint; | ||
let newlineGroupDepth = 0; | ||
let currentIndexes = [0]; | ||
let container = deepIterableToContainer(iterable); | ||
writer.onNewLine(() => { | ||
possibleNewLineSavePoint = undefined; | ||
}); | ||
printItems(); | ||
return writer.toString(); | ||
function printItems() { | ||
while (true) { | ||
while (currentIndexes[currentIndexes.length - 1] < container.items.length) { | ||
handlePrintItem(container.items[currentIndexes[currentIndexes.length - 1]]); | ||
currentIndexes[currentIndexes.length - 1]++; | ||
} | ||
if (container.parent == null) | ||
return; | ||
container = container.parent; | ||
currentIndexes.pop(); | ||
currentIndexes[currentIndexes.length - 1]++; | ||
} | ||
} | ||
function handlePrintItem(printItem) { | ||
if (typeof printItem === "number") | ||
handleSignal(printItem); | ||
else if (typeof printItem === "string") | ||
handleString(printItem); | ||
else if (printItem.kind === exports.PrintItemKind.RawString) | ||
handleRawString(printItem); | ||
else if (printItem.kind === exports.PrintItemKind.Condition) | ||
handleCondition(printItem); | ||
else if (printItem.kind === exports.PrintItemKind.Info) | ||
resolveInfo(printItem); | ||
else | ||
assertNever(printItem); | ||
function handleSignal(signal) { | ||
switch (signal) { | ||
case exports.Signal.ExpectNewLine: | ||
writer.markExpectNewLine(); | ||
break; | ||
case exports.Signal.NewLine: | ||
markPossibleNewLineIfAble(); | ||
break; | ||
case exports.Signal.SpaceOrNewLine: | ||
if (isAboveMaxWidth(1)) { | ||
const saveState = possibleNewLineSavePoint; | ||
if (saveState == null || saveState.newlineGroupDepth >= newlineGroupDepth) | ||
writer.write(options.newlineKind); | ||
else if (possibleNewLineSavePoint != null) | ||
updateStateToSavePoint(possibleNewLineSavePoint); | ||
} | ||
else { | ||
markPossibleNewLineIfAble(); | ||
writer.write(" "); | ||
} | ||
break; | ||
case exports.Signal.StartIndent: | ||
writer.startIndent(); | ||
break; | ||
case exports.Signal.FinishIndent: | ||
writer.finishIndent(); | ||
break; | ||
case exports.Signal.StartNewlineGroup: | ||
newlineGroupDepth++; | ||
break; | ||
case exports.Signal.FinishNewLineGroup: | ||
newlineGroupDepth--; | ||
break; | ||
case exports.Signal.SingleIndent: | ||
writer.singleIndent(); | ||
break; | ||
case exports.Signal.StartIgnoringIndent: | ||
writer.startIgnoringIndent(); | ||
break; | ||
case exports.Signal.FinishIgnoringIndent: | ||
writer.finishIgnoringIndent(); | ||
break; | ||
default: | ||
assertNever(signal); | ||
break; | ||
} | ||
} | ||
function handleString(text) { | ||
const isNewLine = text === "\n" || text === "\r\n"; | ||
if (!isNewLine && text.includes("\n")) | ||
throw new Error("Praser error: Cannot parse text that includes newlines. Newlines must be in their own string."); | ||
if (!isNewLine && possibleNewLineSavePoint != null && isAboveMaxWidth(text.length)) | ||
updateStateToSavePoint(possibleNewLineSavePoint); | ||
else | ||
writer.write(text); | ||
} | ||
function handleRawString(unknown) { | ||
if (possibleNewLineSavePoint != null && isAboveMaxWidth(getLineWidth())) | ||
updateStateToSavePoint(possibleNewLineSavePoint); | ||
else | ||
writer.baseWrite(unknown.text); | ||
function getLineWidth() { | ||
const index = unknown.text.indexOf("\n"); | ||
if (index === -1) | ||
return unknown.text.length; | ||
else if (unknown.text[index - 1] === "\r") | ||
return index - 1; | ||
return index; | ||
} | ||
} | ||
function handleCondition(condition) { | ||
try { | ||
const conditionValue = getConditionValue(condition.originalCondition, condition.originalCondition); | ||
if (conditionValue) { | ||
if (condition.true) { | ||
container = condition.true; | ||
currentIndexes.push(-1); | ||
} | ||
} | ||
else { | ||
if (condition.false) { | ||
container = condition.false; | ||
currentIndexes.push(-1); | ||
} | ||
} | ||
} | ||
catch (err) { | ||
if (err !== exitSymbol) | ||
throw err; | ||
} | ||
} | ||
} | ||
function markPossibleNewLineIfAble() { | ||
if (possibleNewLineSavePoint != null && newlineGroupDepth > possibleNewLineSavePoint.newlineGroupDepth) | ||
function updateItem(item) { | ||
if (!isInfoOrCondition(item)) | ||
return; | ||
possibleNewLineSavePoint = createSavePoint("newline"); | ||
} | ||
function updateStateToSavePoint(savePoint) { | ||
const isForNewLine = possibleNewLineSavePoint === savePoint; | ||
writer.setState(savePoint.writerState); | ||
possibleNewLineSavePoint = isForNewLine ? undefined : savePoint.possibleNewLineSavePoint; | ||
container = savePoint.container; | ||
currentIndexes = [...savePoint.currentIndexes]; | ||
newlineGroupDepth = savePoint.newlineGroupDepth; | ||
if (isForNewLine) | ||
writer.write(options.newlineKind); | ||
} | ||
function getConditionValue(condition, printingCondition) { | ||
if (typeof condition.condition === "object") { | ||
const result = resolvedConditions.get(condition.condition); | ||
if (result == null) { | ||
if (!lookAheadSavePoints.has(condition)) { | ||
const savePoint = createSavePointForRestoringCondition(condition.name); | ||
lookAheadSavePoints.set(condition, savePoint); | ||
} | ||
if (hasId(item)) | ||
return; | ||
addId(item); | ||
if (item.kind === types.PrintItemKind.Condition) { | ||
addPath(item, "truePath", item.true); | ||
addPath(item, "falsePath", item.false); | ||
if (!(item.condition instanceof Function)) { | ||
const checkingCondition = item.condition; | ||
item.condition = (context) => context.getResolvedCondition(checkingCondition); | ||
} | ||
else { | ||
const savePoint = lookAheadSavePoints.get(condition); | ||
if (savePoint != null) { | ||
lookAheadSavePoints.delete(condition); | ||
updateStateToSavePoint(savePoint); | ||
throw exitSymbol; | ||
} | ||
} | ||
return result; | ||
const originalResolver = item.condition; | ||
item.condition = (context) => { | ||
rustContext.setInnerContext(context); | ||
return originalResolver(rustContext); | ||
}; | ||
} | ||
else if (condition.condition instanceof Function) { | ||
const result = condition.condition({ | ||
getResolvedCondition, | ||
writerInfo: getWriterInfo(), | ||
getResolvedInfo: info => getResolvedInfo(info) | ||
function addPath(condition, pathName, pathIterator) { | ||
Object.defineProperty(condition, pathName, { | ||
get: (() => { | ||
let pastIterator; | ||
return () => { | ||
if (pathIterator == null) | ||
return; | ||
if (pastIterator != null) | ||
return pastIterator; | ||
pastIterator = innerGetItemsAsArray(pathIterator); | ||
return pastIterator; | ||
}; | ||
})(), | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
if (result != null) | ||
resolvedConditions.set(condition, result); | ||
return result; | ||
} | ||
else { | ||
return assertNever(condition.condition); | ||
function isInfoOrCondition(item) { | ||
return item.kind != null; | ||
} | ||
function getResolvedCondition(c, defaultValue) { | ||
const conditionValue = getConditionValue(c); | ||
if (conditionValue == null) | ||
return defaultValue; | ||
return conditionValue; | ||
function hasId(item) { | ||
return item.id != null; | ||
} | ||
function getResolvedInfo(info) { | ||
const resolvedInfo = resolvedInfos.get(info); | ||
if (resolvedInfo == null && !lookAheadSavePoints.has(info)) { | ||
const savePoint = createSavePointForRestoringCondition(info.name); | ||
lookAheadSavePoints.set(info, savePoint); | ||
} | ||
return resolvedInfo; | ||
function addId(item) { | ||
const id = getNextId(); | ||
Object.defineProperty(item, "id", { | ||
get: () => { | ||
return id; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
return true; | ||
} | ||
} | ||
function resolveInfo(info) { | ||
resolvedInfos.set(info, getWriterInfo()); | ||
const savePoint = lookAheadSavePoints.get(info); | ||
if (savePoint != null) { | ||
lookAheadSavePoints.delete(info); | ||
updateStateToSavePoint(savePoint); | ||
} | ||
} | ||
function printWriteItems(writeItems, options) { | ||
const finalItems = []; | ||
const indentationText = options.useTabs ? "\t" : " ".repeat(options.indentWidth); | ||
for (const item of writeItems) { | ||
if (typeof item === "string") | ||
finalItems.push(item); | ||
else if (item instanceof Array) | ||
finalItems.push(indentationText.repeat(item[0])); | ||
else if (item === 0) | ||
finalItems.push(options.newLineKind); | ||
else if (item === 1) | ||
finalItems.push("\t"); | ||
else if (item === 2) | ||
finalItems.push(" "); | ||
else | ||
throw new Error(`Unhandled write item: ${item}`); | ||
} | ||
function getWriterInfo() { | ||
return { | ||
lineStartIndentLevel: writer.getLineStartIndentLevel(), | ||
lineStartColumnNumber: writer.getLineStartColumnNumber(), | ||
lineNumber: writer.getLineNumber(), | ||
columnNumber: writer.getLineColumn(), | ||
indentLevel: writer.getIndentationLevel() | ||
}; | ||
} | ||
function isAboveMaxWidth(offset = 0) { | ||
return (writer.getLineColumn() + 1 + offset) > options.maxWidth; | ||
} | ||
function createSavePointForRestoringCondition(conditionName) { | ||
const savePoint = createSavePoint(conditionName); | ||
savePoint.currentIndexes[savePoint.currentIndexes.length - 1]--; | ||
return savePoint; | ||
} | ||
function createSavePoint(name) { | ||
return { | ||
name, | ||
currentIndexes: [...currentIndexes], | ||
newlineGroupDepth, | ||
writerState: writer.getState(), | ||
possibleNewLineSavePoint, | ||
container | ||
}; | ||
} | ||
return finalItems.join(""); | ||
} | ||
function print(items, options) { | ||
const writeItems = wasmPrinter.get_write_items(preparePrintItems(items), options.maxWidth, options.indentWidth); | ||
return printWriteItems(writeItems, options); | ||
} | ||
function formatFileText(options) { | ||
const { filePath, fileText, plugins } = options; | ||
const plugin = getPlugin(); | ||
const parseResult = plugin.parseFile(filePath, fileText); | ||
if (!parseResult) | ||
return options.fileText; | ||
const config = plugin.getConfiguration(); | ||
return print(parseResult, { | ||
newlineKind: config.newlineKind === "auto" ? resolveNewLineKindFromText(fileText) : config.newlineKind, | ||
maxWidth: config.lineWidth, | ||
indentWidth: config.indentWidth, | ||
useTabs: config.useTabs | ||
}); | ||
return types.isJsPlugin(plugin) ? handleJsPlugin(plugin) : handleWebAssemblyPlugin(plugin); | ||
function handleJsPlugin(plugin) { | ||
const parseResult = plugin.parseFile(filePath, fileText); | ||
if (!parseResult) | ||
return options.fileText; | ||
const config = plugin.getConfiguration(); | ||
return print(parseResult, { | ||
newLineKind: config.newLineKind === "auto" ? resolveNewLineKindFromText(fileText) : config.newLineKind, | ||
maxWidth: config.lineWidth, | ||
indentWidth: config.indentWidth, | ||
useTabs: config.useTabs | ||
}); | ||
} | ||
function handleWebAssemblyPlugin(plugin) { | ||
const formattedText = plugin.formatText(filePath, fileText); | ||
return formattedText === false ? options.fileText : formattedText; | ||
} | ||
function getPlugin() { | ||
@@ -843,3 +503,3 @@ if (plugins.length === 0) | ||
for (const plugin of plugins) { | ||
if (plugin.shouldParseFile(filePath, fileText)) | ||
if (plugin.shouldFormatFile(filePath, fileText)) | ||
return plugin; | ||
@@ -857,4 +517,5 @@ } | ||
exports.makeIterableRepeatable = makeIterableRepeatable; | ||
exports.print = print; | ||
exports.resolveConfiguration = resolveConfiguration; | ||
exports.resolveNewLineKindFromText = resolveNewLineKindFromText; | ||
exports.version = version; |
// dprint-ignore-file | ||
import { Condition, Signal, Info, PrintItem, PrintItemIterable, WriterInfo, Plugin, Configuration, ConfigurationDiagnostic, ResolvedConfiguration, ResolveConditionContext, BaseResolvedConfiguration, LoggingEnvironment } from "@dprint/types"; | ||
export declare const version = "0.4.4"; | ||
export declare const version = "0.5.0"; | ||
@@ -17,60 +18,20 @@ export declare function makeIterableRepeatable<T>(iterable: Iterable<T>): Iterable<T>; | ||
/** | ||
* Dprint's configuration. | ||
* Print out the provided print items using the rust printer. | ||
* @param items - Items to print. | ||
* @param options - Options for printing. | ||
*/ | ||
export interface Configuration { | ||
/** | ||
* Specify the type of project this is. You may specify any of the allowed values here according to your conscience. | ||
* @value "openSource" - Dprint is formatting an open source project. | ||
* @value "commercialSponsored" - Dprint is formatting a closed source commercial project and your company sponsored dprint. | ||
* @value "commercialDidNotSponsor" - Dprint is formatting a closed source commercial project and you want to forever enshrine your name in source control for having specified this. | ||
*/ | ||
projectType: "openSource" | "commercialSponsored" | "commercialDidNotSponsor"; | ||
/** | ||
* The width of a line the printer will try to stay under. Note that the printer may exceed this width in certain cases. | ||
* @default 120 | ||
*/ | ||
lineWidth?: number; | ||
/** | ||
* The number of spaces for an indent. This option is ignored when using tabs. | ||
* @default 4 | ||
*/ | ||
indentWidth?: number; | ||
/** | ||
* Whether to use tabs (true) or spaces (false). | ||
* @default false | ||
*/ | ||
useTabs?: boolean; | ||
/** | ||
* The kind of newline to use. | ||
* @default "auto" | ||
* @value "auto" - For each file, uses the newline kind found at the end of the last line. | ||
* @value "crlf" - Uses carriage return, line feed. | ||
* @value "lf" - Uses line feed. | ||
* @value "system" - Uses the system standard (ex. crlf on Windows). | ||
*/ | ||
newlineKind?: "auto" | "crlf" | "lf" | "system"; | ||
/** | ||
* Collection of plugins to use. | ||
*/ | ||
plugins: Plugin[]; | ||
} | ||
export declare function print(items: PrintItemIterable, options: PrintOptions): string; | ||
export interface ResolvedConfiguration extends BaseResolvedConfiguration { | ||
/** Options for printing. */ | ||
export interface PrintOptions { | ||
/** The width the printer will attempt to keep the line under. */ | ||
maxWidth: number; | ||
/** The number of spaces to use when indenting (unless useTabs is true). */ | ||
indentWidth: number; | ||
/** Whether to use tabs for indenting. */ | ||
useTabs: boolean; | ||
/** The newline character to use when doing a new line. */ | ||
newLineKind: "\r\n" | "\n"; | ||
} | ||
export interface BaseResolvedConfiguration { | ||
readonly lineWidth: number; | ||
readonly indentWidth: number; | ||
readonly useTabs: boolean; | ||
readonly newlineKind: "auto" | "\r\n" | "\n"; | ||
} | ||
/** Represents a problem with a configuration. */ | ||
export interface ConfigurationDiagnostic { | ||
/** The property name the problem occurred on. */ | ||
propertyName: string; | ||
/** The diagnostic's message that should be displayed to the user. */ | ||
message: string; | ||
} | ||
/** The result of resolving configuration. */ | ||
@@ -91,145 +52,2 @@ export interface ResolveConfigurationResult<ResolvedConfiguration extends BaseResolvedConfiguration> { | ||
/** | ||
* The different items that the printer could encounter. | ||
*/ | ||
export declare type PrintItem = Signal | string | RawString | Condition | Info; | ||
/** | ||
* An iterable of print items. | ||
*/ | ||
export interface PrintItemIterable extends Iterable<PrintItem> { | ||
} | ||
/** | ||
* The kind of print item. | ||
*/ | ||
export declare enum PrintItemKind { | ||
RawString = 0, | ||
Condition = 1, | ||
Info = 2 | ||
} | ||
/** | ||
* Represents a string that should be formatted as-is. | ||
*/ | ||
export interface RawString { | ||
kind: PrintItemKind.RawString; | ||
text: string; | ||
} | ||
/** | ||
* Signals for the printer. | ||
*/ | ||
export declare enum Signal { | ||
/** | ||
* Signal that the current location could be a newline when | ||
* exceeding the print width. | ||
*/ | ||
NewLine = 0, | ||
/** | ||
* Signal that the current location should be a space, but | ||
* could be a newline if exceeding the print width. | ||
*/ | ||
SpaceOrNewLine = 1, | ||
/** | ||
* Expect the next character to be a newline. If it's not, force a newline. | ||
*/ | ||
ExpectNewLine = 2, | ||
/** | ||
* Signal the start of a section that should be indented. | ||
*/ | ||
StartIndent = 3, | ||
/** | ||
* Signal the end of a section that should be indented. | ||
*/ | ||
FinishIndent = 4, | ||
/** | ||
* Signal the start of a group of print items that have a lower precedence | ||
* for being broken up with a newline for exceeding the line width. | ||
*/ | ||
StartNewlineGroup = 5, | ||
/** | ||
* Signal the end of a newline group. | ||
*/ | ||
FinishNewLineGroup = 6, | ||
/** | ||
* Signal that a single indent should occur based on the printer settings. | ||
*/ | ||
SingleIndent = 7, | ||
/** | ||
* Signal to the writer that it should stop using indentation. | ||
*/ | ||
StartIgnoringIndent = 8, | ||
/** | ||
* Signal to the writer that it should start using indentation again. | ||
*/ | ||
FinishIgnoringIndent = 9 | ||
} | ||
/** | ||
* Conditionally print items based on a condition. | ||
* | ||
* These conditions are extremely flexible and could be resolved based on | ||
* information found later on in the file. | ||
*/ | ||
export interface Condition { | ||
kind: PrintItemKind.Condition; | ||
/** Name for debugging purposes. */ | ||
name: string; | ||
/** The condition to resolve or another condition to base this condition on. */ | ||
condition: ResolveCondition | Condition; | ||
/** The items to print when the condition is true. */ | ||
true?: PrintItemIterable; | ||
/** The items to print when the condition is false or undefined (not yet resolved). */ | ||
false?: PrintItemIterable; | ||
} | ||
export declare type ResolveCondition = (context: ResolveConditionContext) => boolean | undefined; | ||
export interface ResolveConditionContext { | ||
/** | ||
* Gets if a condition was true, false, or returns undefined when not yet resolved. | ||
*/ | ||
getResolvedCondition(condition: Condition): boolean | undefined; | ||
/** | ||
* Gets if a condition was true, false, or returns the provided default value when | ||
* not yet resolved. | ||
*/ | ||
getResolvedCondition(condition: Condition, defaultValue: boolean): boolean; | ||
/** | ||
* Gets the writer info at a specified info or returns undefined when not yet resolved. | ||
*/ | ||
getResolvedInfo(info: Info): WriterInfo | undefined; | ||
/** | ||
* Gets the writer info at the condition's location. | ||
*/ | ||
writerInfo: WriterInfo; | ||
} | ||
/** | ||
* Can be used to get information at a certain location being printed. These can be resolved | ||
* by providing the info object to a condition context's getResolvedInfo method. | ||
*/ | ||
export interface Info { | ||
kind: PrintItemKind.Info; | ||
/** Name for debugging purposes. */ | ||
name: string; | ||
} | ||
/** | ||
* Information about a certain location being printed. | ||
*/ | ||
export interface WriterInfo { | ||
lineNumber: number; | ||
columnNumber: number; | ||
indentLevel: number; | ||
lineStartIndentLevel: number; | ||
lineStartColumnNumber: number; | ||
} | ||
export interface BaseContext { | ||
fileText: string; | ||
newlineKind: "\r\n" | "\n"; | ||
} | ||
export declare namespace conditionResolvers { | ||
@@ -245,3 +63,2 @@ function isStartOfNewLine(conditionContext: ResolveConditionContext): boolean; | ||
interface NewlineIfHangingSpaceOtherwiseOptions { | ||
context: BaseContext; | ||
startInfo: Info; | ||
@@ -253,3 +70,2 @@ endInfo?: Info; | ||
interface NewlineIfMultipleLinesSpaceOrNewlineOtherwiseOptions { | ||
context: BaseContext; | ||
startInfo: Info; | ||
@@ -273,5 +89,5 @@ endInfo?: Info; | ||
function newlineGroup(item: PrintItemIterable): PrintItemIterable; | ||
function prependToIterableIfHasItems<T>(iterable: Iterable<T>, ...items: T[]): IterableIterator<T>; | ||
function prependToIterableIfHasItems<T>(iterable: Iterable<T>, ...items: T[]): Generator<T, void, undefined>; | ||
function toPrintItemIterable(printItem: PrintItem): PrintItemIterable; | ||
function surroundWithNewLines(item: PrintItemIterable, context: BaseContext): PrintItemIterable; | ||
function surroundWithNewLines(item: PrintItemIterable): PrintItemIterable; | ||
/** | ||
@@ -283,2 +99,8 @@ * Reusable function for parsing a js-like single line comment (ex. // comment) | ||
function createInfo(name: string): Info; | ||
/** | ||
* Takes a string that could contain tabs or newlines and breaks it up into | ||
* the correct newlines and tabs. | ||
* @param text - Text to break up. | ||
*/ | ||
function parseRawString(text: string): PrintItemIterable; | ||
} | ||
@@ -295,57 +117,20 @@ | ||
/** Represents an execution environment. */ | ||
export interface LoggingEnvironment { | ||
log(text: string): void; | ||
warn(text: string): void; | ||
error(text: string): void; | ||
} | ||
/** Options for initializing a plugin. */ | ||
export interface PluginInitializeOptions { | ||
/** Environment to use for logging. */ | ||
environment: LoggingEnvironment; | ||
/** The resolved global configuration. */ | ||
globalConfig: ResolvedConfiguration; | ||
} | ||
/** Base interface a plugin must implement. */ | ||
export interface Plugin<ResolvedPluginConfiguration extends BaseResolvedConfiguration = BaseResolvedConfiguration> { | ||
/** | ||
* The package version of the plugin. | ||
*/ | ||
version: string; | ||
/** | ||
* Name of this plugin. | ||
*/ | ||
name: string; | ||
/** | ||
* Initializes the plugin for use. | ||
* @remarks Plugins should be resilient to this never being called. | ||
*/ | ||
initialize(options: PluginInitializeOptions): void; | ||
/** | ||
* Gets whether the plugin should parse the file. | ||
*/ | ||
shouldParseFile(filePath: string, fileText: string): boolean; | ||
/** | ||
* Gets the resolved configuration for the plugin. | ||
*/ | ||
getConfiguration(): ResolvedPluginConfiguration; | ||
/** | ||
* Gets the configuration diagnostics. | ||
*/ | ||
getConfigurationDiagnostics(): ConfigurationDiagnostic[]; | ||
/** | ||
* Parses the file to an iterable of print items. | ||
* @returns An iterable of print items or false if the file said to skip parsing (ex. it had an ignore comment). | ||
*/ | ||
parseFile(filePath: string, fileText: string): PrintItemIterable | false; | ||
} | ||
/** | ||
* Formats the provided file's text. | ||
* @param options - Options to use. | ||
*/ | ||
export declare function formatFileText(options: FormatFileTextOptions): string; | ||
/** Options for formatting. */ | ||
export interface FormatFileTextOptions { | ||
/** File path of the file to format. This will help select the plugin to use. */ | ||
filePath: string; | ||
/** File text of the file to format. */ | ||
fileText: string; | ||
/** | ||
* Plugins to use. | ||
* @remarks This function does not assume ownership of the plugins and so if there are | ||
* any web assembly plugins you should dispose of them after you no longer need them. | ||
*/ | ||
plugins: Plugin[]; | ||
} |
{ | ||
"name": "@dprint/core", | ||
"version": "0.4.4", | ||
"version": "0.5.0", | ||
"description": "Core functionality for dprint.", | ||
@@ -8,8 +8,10 @@ "main": "dist/dprint-core.js", | ||
"scripts": { | ||
"build": "rimraf dist && yarn rollup && yarn build:declarations", | ||
"build": "rimraf dist && yarn build:rust && yarn rollup && yarn build:declarations", | ||
"build:rust": "rimraf src/wasm && wasm-pack build wasm --release --target nodejs --out-dir ../src/wasm", | ||
"build:rust-debug": "rimraf src/wasm && wasm-pack build wasm --dev --target nodejs --out-dir ../src/wasm", | ||
"build:declarations": "ts-node --compiler ttypescript --project scripts/tsconfig.json --transpile-only scripts/buildDeclarations.ts", | ||
"test": "cross-env TS_NODE_COMPILER=\"ttypescript\" TS_NODE_TRANSPILE_ONLY=\"true\" mocha --opts mocha.opts", | ||
"test:debug": "cross-env TS_NODE_COMPILER=\"ttypescript\" TS_NODE_TRANSPILE_ONLY=\"true\" mocha --opts mocha.opts --inspect-brk", | ||
"test": "cross-env TS_NODE_COMPILER=\"ttypescript\" TS_NODE_TRANSPILE_ONLY=\"true\" mocha", | ||
"test:debug": "yarn test --inspect-brk", | ||
"test:watch": "yarn test --watch-extensions ts --watch", | ||
"rollup": "rollup --config" | ||
"rollup": "copyfiles -u 1 src/wasm/**/* dist/ && rollup --config" | ||
}, | ||
@@ -33,19 +35,24 @@ "repository": { | ||
}, | ||
"dependencies": { | ||
"@dprint/types": "~0.2.0" | ||
}, | ||
"devDependencies": { | ||
"@types/chai": "^4.2.3", | ||
"@types/chai": "^4.2.7", | ||
"@types/mocha": "^5.2.7", | ||
"@types/ts-nameof": "^3.2.0", | ||
"@types/ts-nameof": "^4.2.1", | ||
"chai": "^4.2.0", | ||
"cross-env": "^5.2.0", | ||
"mocha": "^6.2.0", | ||
"copyfiles": "^2.2.0", | ||
"cross-env": "^6.0.3", | ||
"mocha": "^7.0.0", | ||
"rimraf": "^3.0.0", | ||
"rollup": "^1.20.3", | ||
"rollup": "^1.29.0", | ||
"rollup-plugin-replace": "^2.2.0", | ||
"rollup-plugin-typescript2": "^0.24.0", | ||
"ts-morph": "^3.1.3", | ||
"ts-nameof": "^3.2.0", | ||
"ts-node": "^8.3.0", | ||
"ttypescript": "^1.5.7", | ||
"typescript": "^3.6.2" | ||
"rollup-plugin-typescript2": "^0.25.3", | ||
"ts-morph": "^6.0.2", | ||
"ts-nameof": "^4.2.2", | ||
"ts-node": "^8.6.2", | ||
"ttypescript": "^1.5.10", | ||
"typescript": "^3.7.5", | ||
"wasm-pack": "^0.8.1" | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
73186
1
17
596
1
+ Added@dprint/types@~0.2.0
+ Added@dprint/types@0.2.0(transitive)