@sasjs/lint 5.3.1 → 5.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -0
- package/format/adjustLineLength.d.ts +34 -0
- package/format/adjustLineLength.js +159 -0
- package/format/adjustLineLength.js.map +1 -0
- package/format/formatFile.d.ts +3 -1
- package/format/formatFile.js +4 -4
- package/format/formatFile.js.map +1 -1
- package/format/formatFolder.d.ts +3 -1
- package/format/formatFolder.js +38 -7
- package/format/formatFolder.js.map +1 -1
- package/format/formatProject.d.ts +2 -1
- package/format/formatProject.js +2 -2
- package/format/formatProject.js.map +1 -1
- package/format/formatText.d.ts +19 -1
- package/format/formatText.js +23 -3
- package/format/formatText.js.map +1 -1
- package/format/longLines.d.ts +60 -0
- package/format/longLines.js +90 -0
- package/format/longLines.js.map +1 -0
- package/format/reflowComments.d.ts +28 -0
- package/format/reflowComments.js +305 -0
- package/format/reflowComments.js.map +1 -0
- package/format/rewrapText.d.ts +38 -0
- package/format/rewrapText.js +389 -0
- package/format/rewrapText.js.map +1 -0
- package/format/shared.js +34 -5
- package/format/shared.js.map +1 -1
- package/format/splitTrailingComments.d.ts +22 -0
- package/format/splitTrailingComments.js +68 -0
- package/format/splitTrailingComments.js.map +1 -0
- package/package.json +11 -24
- package/sasjslint-schema.json +10 -1
- package/types/FormatOptions.d.ts +29 -0
- package/types/FormatOptions.js +3 -0
- package/types/FormatOptions.js.map +1 -0
- package/types/KeywordCase.d.ts +10 -0
- package/types/KeywordCase.js +13 -0
- package/types/KeywordCase.js.map +1 -0
- package/types/LintConfig.d.ts +10 -0
- package/types/LintConfig.js +17 -0
- package/types/LintConfig.js.map +1 -1
- package/types/index.d.ts +2 -0
- package/types/index.js +2 -0
- package/types/index.js.map +1 -1
- package/utils/getDataSectionsDetail.d.ts +14 -0
- package/utils/getDataSectionsDetail.js +29 -13
- package/utils/getDataSectionsDetail.js.map +1 -1
- package/utils/getLintConfig.d.ts +1 -0
- package/utils/getLintConfig.js +1 -0
- package/utils/getLintConfig.js.map +1 -1
- package/utils/scanLine.d.ts +59 -0
- package/utils/scanLine.js +115 -0
- package/utils/scanLine.js.map +1 -0
package/README.md
CHANGED
|
@@ -29,6 +29,7 @@ Configuration is via a `.sasjslint` file with the following structure (these are
|
|
|
29
29
|
"ignoredLibnames": [],
|
|
30
30
|
"ignoredMacros": ["webout"],
|
|
31
31
|
"indentationMultiple": 2,
|
|
32
|
+
"keywordCase": "preserve",
|
|
32
33
|
"lineEndings": "off",
|
|
33
34
|
"lowerCaseFileNames": true,
|
|
34
35
|
"lowerCaseMacroNames": true,
|
|
@@ -162,6 +163,14 @@ If creating a new value, use `{lineEnding}` instead of `\n`, eg as follows:
|
|
|
162
163
|
}
|
|
163
164
|
```
|
|
164
165
|
|
|
166
|
+
### keywordCase
|
|
167
|
+
|
|
168
|
+
The case the formatter gives SAS keywords. `preserve` leaves them as written, which is what the printer does when nothing is asked of it; `upper` and `lower` rewrite them.
|
|
169
|
+
|
|
170
|
+
It is a formatting setting rather than a rule - casing is a house style, and reporting it on every line would bury the rules that describe defects - and it takes effect when the formatter is given a layout engine. Every caller with a SAS printer supplies one (`@sasjs/sas-language` is where ours comes from), because the words worth changing are the ones that engine's lexer identifies. Strings and comments are left alone; a name the lexer cannot tell from a keyword - a DATA step variable called `label` - is cased with it.
|
|
171
|
+
|
|
172
|
+
- Default: preserve
|
|
173
|
+
|
|
165
174
|
### hasDoxygenHeader
|
|
166
175
|
|
|
167
176
|
The SASjs framework recommends the use of Doxygen headers for describing all types of SAS program. This check identifies files where a Doxygen header does not begin on the first line, where it opens with a single asterisk, where it closes with one, where the close shares its line with the header's content, and where the block is never closed. A header is located by its close: the line that holds nothing but `**/`, which is the form `sasjs compile` and this rule set read. A block opened with `/**` whose close sits at the end of a content line, or which has no close at all, is therefore not yet a header.
|
|
@@ -333,6 +342,46 @@ In batch mode, long SAS code lines may also be truncated, causing hard-to-detect
|
|
|
333
342
|
|
|
334
343
|
We strongly recommend a line length limit, and set the bar at 80. To turn this feature off, set the value to 0.
|
|
335
344
|
|
|
345
|
+
Saving with the formatter does four things for this rule, in this order.
|
|
346
|
+
|
|
347
|
+
**It lifts a trailing comment onto its own line** when a line is over the limit and carries code before the comment. The code keeps the line it was on and the comment moves below it at the same indentation, which hands the code to the wrap below and the prose to the reflow - so `, var /* variable name from which to return the format */` becomes two lines, and each is handled by the pass that owns it. The lift happens even when the code alone overruns the limit: each half then reaches the pass that owns it, instead of the line being refused by both.
|
|
348
|
+
|
|
349
|
+
**It breaks a long line of code** at the last space that still fits, and indents the continuation by the next multiple of [`indentationMultiple`](#indentationmultiple), so a wrap never introduces an indentation warning of its own. A space that follows a `;` is preferred over one inside a statement: the semicolon ends the statement, so a newline there is inert and the line reads as SAS rather than as a slice of one, and for a `put` or `%let` it needs none of the `%trim(` machinery below. It also prefers a space that does not leave a macro keyword such as `%let` or `%then` at the end of the line, and a line is rewritten only when every line it becomes fits the limit - so the pass never trades one warning for another, which also keeps `fixedDiagnosticsCount` from going backwards. The shapes where a break would change what SAS reads, or what another rule parses, are left alone:
|
|
350
|
+
|
|
351
|
+
- `datalines` / `cards` / `parmcards` records, and the statement that opens them, where a newline ends the record
|
|
352
|
+
- a `%*` comment statement, which is prose
|
|
353
|
+
- any space inside `%str`-family masking or a `%sysfunc` / `%qsysfunc` call, whose argument text is carried as data: measured on a live engine, breaking inside `%sysfunc(fput(...))` wrote `Content-Type: application/csv` where the macro meant one space
|
|
354
|
+
|
|
355
|
+
A `put`, `putlog` or `%put` statement (including the `%&var.put` form), the window statements, and a `%let` assignment are broken differently, because a plain newline there is data rather than a delimiter: it is a line of the log for the `put` family, and part of the value for `%let`. The break ends the line with `%trim(` and opens the next with `)`, so the newline and the indentation become the argument of `%trim` - which resolves to nothing - and the blank the break replaced sits after the closing marker:
|
|
356
|
+
|
|
357
|
+
```sas
|
|
358
|
+
%put NOTE: the macro could not find the requested%trim(
|
|
359
|
+
) table and stopped;
|
|
360
|
+
%let exp8=MPE_DERIVED:CALLER:VAR_LOGIC may not call%trim(
|
|
361
|
+
) a function that;
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Leaving the blank at the end of the upper line instead would lose it: measured on a live engine, `requested %trim(` printed `requestedtable`, because SAS drops a blank that sits immediately before a macro call - and a trailing blank is what `noTrailingSpaces` reports anyway. The construct is verified end to end for both forms: a macro holding these statements writes byte-identical log lines, and the `%let` variables hold byte-identical values, before and after a format at a limit of 40 and at 60, several breaks per statement included. A `%let` value built over two statements, and one whose text already carries a `%trim(` of its own, are covered by the same check.
|
|
365
|
+
|
|
366
|
+
A `%macro` declaration is broken before its parameter commas, and the continuation line starts with the comma, so the parameters read as a list and the declaration stays the one statement the macro rules parse:
|
|
367
|
+
|
|
368
|
+
```sas
|
|
369
|
+
%macro mf_writefile(fpath
|
|
370
|
+
,mode=O,l1=,l2=,l3=,l4=,l5=,l6=,l7=,l8=,l9=,l10=);
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
**It reflows the prose in a block comment**, paragraph by paragraph: the lines of one paragraph are joined and rewrapped, so the block comes back even rather than ragged. A paragraph breaks at a blank line and before anything line-oriented - an `@li` entry, a `@sasjslint` tag, an `<h4>` heading, a list item, a table row or a macro trigger - and a `@brief` or `@param` line carries the lines under it. Blocks holding a `@example` / `@code` / `@verbatim` body, the `<h4> SAS Macros </h4>` section, a comment that shares a line with code, and a comment inside a `datalines` record are left byte-identical. Prose written with a `*` at the start of every line - the JavaDoc style - is read as a list and left as it is. A reflow normalises the runs of spaces inside the paragraph it rewrites, so two spaces after a full stop become one.
|
|
374
|
+
|
|
375
|
+
**It raises the file's own exception** for a line neither pass could shorten - a URL in a header, a long quoted literal that generates code, an indented data record - by writing or merging a [`@sasjslint`](#per-file-overrides) block at the end of the header:
|
|
376
|
+
|
|
377
|
+
```sas
|
|
378
|
+
@sasjslint {"maxHeaderLineLength": 123}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
The value is the exact length the longest such line needs and no more: a header line raises `maxHeaderLineLength`, a data record raises `maxDataLineLength`, and a body line raises `maxLineLength`. An existing exception keeps every key it had, and a value is never lowered.
|
|
382
|
+
|
|
383
|
+
Nothing is raised for a line whose author can still act on it: a `%*` comment, a `put` whose text holds a word longer than the limit, a `%macro` declaration whose single parameter is too long, or a comment line that a reflow cannot fit. Excusing one of those would relax `maxLineLength` for every line of code in the file, and each is a line the author can rewrite.
|
|
384
|
+
|
|
336
385
|
- Default: 80
|
|
337
386
|
- Severity: WARNING
|
|
338
387
|
|
|
@@ -690,6 +739,9 @@ We've already implemented the following rules:
|
|
|
690
739
|
- Remove unused macros from the `<h4> SAS Macros </h4>` section
|
|
691
740
|
- Remove trailing spaces
|
|
692
741
|
- Fix line endings, including adding a missing final newline when [`lineEndings`](#lineendings) is configured
|
|
742
|
+
- Break a line that exceeds [`maxLineLength`](#maxlinelength) at whitespace, where doing so cannot change what SAS reads
|
|
743
|
+
- Reflow the prose in a block comment, paragraph by paragraph
|
|
744
|
+
- Raise the file's own `@sasjslint` exception for a line neither of the above can shorten
|
|
693
745
|
|
|
694
746
|
The formatter only fixes rules that are in your configuration, so a rule that is off neither warns nor is repaired.
|
|
695
747
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { LintConfig } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Raises the file's own `@sasjslint` exception so that the lines the passes
|
|
4
|
+
* could not shorten stop being reported.
|
|
5
|
+
*
|
|
6
|
+
* The formatter can break most long lines, and `reflowComments` can reflow
|
|
7
|
+
* prose, but a single token wider than the limit - a URL in a header, a long
|
|
8
|
+
* quoted literal that generates code - cannot be shortened at all, and a data
|
|
9
|
+
* record is not code. The documented way to accept those is the per-file
|
|
10
|
+
* override, and projects do it by hand today (sasjs/core carries
|
|
11
|
+
* `{"maxLineLength": 123}` for one documentation URL). This writes it for them,
|
|
12
|
+
* at the exact length the longest such line needs and no more:
|
|
13
|
+
*
|
|
14
|
+
* - a header line raises `maxHeaderLineLength`, which affects header lines only
|
|
15
|
+
* - a data record raises `maxDataLineLength`, which affects records only
|
|
16
|
+
* - a body line raises `maxLineLength`, which affects the whole file
|
|
17
|
+
*
|
|
18
|
+
* Lines the wrapper declines on purpose - `put`-family statements, where a
|
|
19
|
+
* break is an extra line of output, and `%macro` / `%mend` declarations - do NOT
|
|
20
|
+
* raise the limit: they are fixable by hand, and excusing them would hide the
|
|
21
|
+
* finding for good.
|
|
22
|
+
*
|
|
23
|
+
* The value is never lowered, an existing exception keeps every key it had, and
|
|
24
|
+
* no header means nowhere to write, in which case the text is returned
|
|
25
|
+
* untouched.
|
|
26
|
+
*
|
|
27
|
+
* @param {string} text - the text to adjust.
|
|
28
|
+
* @param {LintConfig} configuration - the effective configuration, the file's
|
|
29
|
+
* own override included.
|
|
30
|
+
* @returns {string} the text, with its exception raised when one is needed.
|
|
31
|
+
*/
|
|
32
|
+
export declare const adjustLineLength: (text: string, configuration: LintConfig) => string;
|
|
33
|
+
/** The configured line ending, for the rare case a terminator has to be invented. */
|
|
34
|
+
export declare const lineEndingOf: (configuration: LintConfig) => string;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.lineEndingOf = exports.adjustLineLength = void 0;
|
|
4
|
+
const LineEndings_1 = require("../types/LineEndings");
|
|
5
|
+
const getDataSectionsDetail_1 = require("../utils/getDataSectionsDetail");
|
|
6
|
+
const getHeaderLinesCount_1 = require("../utils/getHeaderLinesCount");
|
|
7
|
+
const parseLintOverride_1 = require("../utils/parseLintOverride");
|
|
8
|
+
const scanLine_1 = require("../utils/scanLine");
|
|
9
|
+
const longLines_1 = require("./longLines");
|
|
10
|
+
const rewrapText_1 = require("./rewrapText");
|
|
11
|
+
/** The line that carries a file's own lint configuration. */
|
|
12
|
+
const overrideTag = /^(\s*)@sasjslint\s*(\{.*\})\s*$/;
|
|
13
|
+
/**
|
|
14
|
+
* Raises the file's own `@sasjslint` exception so that the lines the passes
|
|
15
|
+
* could not shorten stop being reported.
|
|
16
|
+
*
|
|
17
|
+
* The formatter can break most long lines, and `reflowComments` can reflow
|
|
18
|
+
* prose, but a single token wider than the limit - a URL in a header, a long
|
|
19
|
+
* quoted literal that generates code - cannot be shortened at all, and a data
|
|
20
|
+
* record is not code. The documented way to accept those is the per-file
|
|
21
|
+
* override, and projects do it by hand today (sasjs/core carries
|
|
22
|
+
* `{"maxLineLength": 123}` for one documentation URL). This writes it for them,
|
|
23
|
+
* at the exact length the longest such line needs and no more:
|
|
24
|
+
*
|
|
25
|
+
* - a header line raises `maxHeaderLineLength`, which affects header lines only
|
|
26
|
+
* - a data record raises `maxDataLineLength`, which affects records only
|
|
27
|
+
* - a body line raises `maxLineLength`, which affects the whole file
|
|
28
|
+
*
|
|
29
|
+
* Lines the wrapper declines on purpose - `put`-family statements, where a
|
|
30
|
+
* break is an extra line of output, and `%macro` / `%mend` declarations - do NOT
|
|
31
|
+
* raise the limit: they are fixable by hand, and excusing them would hide the
|
|
32
|
+
* finding for good.
|
|
33
|
+
*
|
|
34
|
+
* The value is never lowered, an existing exception keeps every key it had, and
|
|
35
|
+
* no header means nowhere to write, in which case the text is returned
|
|
36
|
+
* untouched.
|
|
37
|
+
*
|
|
38
|
+
* @param {string} text - the text to adjust.
|
|
39
|
+
* @param {LintConfig} configuration - the effective configuration, the file's
|
|
40
|
+
* own override included.
|
|
41
|
+
* @returns {string} the text, with its exception raised when one is needed.
|
|
42
|
+
*/
|
|
43
|
+
const adjustLineLength = (text, configuration) => {
|
|
44
|
+
if (!text)
|
|
45
|
+
return text;
|
|
46
|
+
if (!configuration.lineLintRules.some((rule) => rule.name === 'maxLineLength'))
|
|
47
|
+
return text;
|
|
48
|
+
const headerLinesCount = (0, getHeaderLinesCount_1.getHeaderLinesCount)(text, configuration);
|
|
49
|
+
if (!headerLinesCount)
|
|
50
|
+
return text;
|
|
51
|
+
const entries = (0, rewrapText_1.splitWithTerminators)(text);
|
|
52
|
+
const lines = entries.map((entry) => entry.line);
|
|
53
|
+
const dataSections = (0, getDataSectionsDetail_1.getDataSectionsDetail)(text, configuration);
|
|
54
|
+
const headerLimit = Math.max(configuration.maxLineLength, configuration.maxHeaderLineLength);
|
|
55
|
+
const dataLimit = Math.max(configuration.maxLineLength, configuration.maxDataLineLength);
|
|
56
|
+
const needed = {
|
|
57
|
+
maxLineLength: 0,
|
|
58
|
+
maxHeaderLineLength: 0,
|
|
59
|
+
maxDataLineLength: 0
|
|
60
|
+
};
|
|
61
|
+
let state = (0, scanLine_1.cleanState)();
|
|
62
|
+
let breakAwareOpen = false;
|
|
63
|
+
lines.forEach((line, index) => {
|
|
64
|
+
const isHeader = index < headerLinesCount;
|
|
65
|
+
const isData = (0, getDataSectionsDetail_1.checkIsDataLine)(dataSections, index);
|
|
66
|
+
// A data record is data, so it is not scanned: a comment marker or an
|
|
67
|
+
// apostrophe in one would otherwise stay open for the rest of the file, and
|
|
68
|
+
// every later line would read as a comment that no pass may touch.
|
|
69
|
+
const scanned = isData ? (0, scanLine_1.noScan)() : (0, scanLine_1.scanLine)(line, state);
|
|
70
|
+
state = scanned.endState;
|
|
71
|
+
const breakAware = isData
|
|
72
|
+
? { line: false, open: false }
|
|
73
|
+
: (0, longLines_1.breakAwareState)(scanned.code, breakAwareOpen);
|
|
74
|
+
breakAwareOpen = breakAware.open;
|
|
75
|
+
const limit = isData
|
|
76
|
+
? dataLimit
|
|
77
|
+
: isHeader
|
|
78
|
+
? headerLimit
|
|
79
|
+
: configuration.maxLineLength;
|
|
80
|
+
if (line.length <= limit)
|
|
81
|
+
return;
|
|
82
|
+
if (breakAware.line ||
|
|
83
|
+
longLines_1.macroComment.test(line) ||
|
|
84
|
+
longLines_1.macroDeclaration.test(line))
|
|
85
|
+
return;
|
|
86
|
+
// A comment carries no key of its own, so excusing one would raise the
|
|
87
|
+
// limit for every line of code in the file: `reflowComments` has already
|
|
88
|
+
// reflowed the prose, and a paragraph holding a word it cannot fit - a URL -
|
|
89
|
+
// is left to its author. A header line is the exception, because
|
|
90
|
+
// `maxHeaderLineLength` scopes the relaxation to the header.
|
|
91
|
+
//
|
|
92
|
+
// The guard turns on what makes the line long, not on the comment being
|
|
93
|
+
// there: a line of code that overruns the limit on its own, with a comment
|
|
94
|
+
// after it, is excused like any other, because no pass may break it either.
|
|
95
|
+
const codeEnd = scanned.code.trimEnd().length;
|
|
96
|
+
if (!isHeader && !isData && scanned.hasComment && codeEnd <= limit)
|
|
97
|
+
return;
|
|
98
|
+
const key = isData
|
|
99
|
+
? 'maxDataLineLength'
|
|
100
|
+
: isHeader
|
|
101
|
+
? 'maxHeaderLineLength'
|
|
102
|
+
: 'maxLineLength';
|
|
103
|
+
needed[key] = Math.max(needed[key], line.length);
|
|
104
|
+
});
|
|
105
|
+
const raised = Object.keys(needed).filter((key) => needed[key] > configuration[key]);
|
|
106
|
+
if (!raised.length)
|
|
107
|
+
return text;
|
|
108
|
+
const override = (0, parseLintOverride_1.getLintOverride)(text, configuration);
|
|
109
|
+
const existing = lines
|
|
110
|
+
.slice(0, headerLinesCount)
|
|
111
|
+
.find((line) => overrideTag.test(line));
|
|
112
|
+
// A tag that does not parse is reported by the linter and belongs to its
|
|
113
|
+
// author: rewriting it would discard whatever they were trying to say.
|
|
114
|
+
if (existing && !override)
|
|
115
|
+
return text;
|
|
116
|
+
const merged = Object.assign({}, (override !== null && override !== void 0 ? override : {}));
|
|
117
|
+
raised.forEach((key) => {
|
|
118
|
+
var _a;
|
|
119
|
+
merged[key] = Math.max((_a = merged[key]) !== null && _a !== void 0 ? _a : 0, needed[key]);
|
|
120
|
+
});
|
|
121
|
+
const json = '{' +
|
|
122
|
+
Object.keys(merged)
|
|
123
|
+
.map((key) => `"${key}": ${JSON.stringify(merged[key])}`)
|
|
124
|
+
.join(', ') +
|
|
125
|
+
'}';
|
|
126
|
+
if (existing) {
|
|
127
|
+
const match = overrideTag.exec(existing);
|
|
128
|
+
if (match[2] === json)
|
|
129
|
+
return text;
|
|
130
|
+
const at = lines.indexOf(existing);
|
|
131
|
+
const rewritten = [...lines];
|
|
132
|
+
rewritten[at] = `${match[1]}@sasjslint ${json}`;
|
|
133
|
+
return rewritten.map((line, index) => line + entries[index].ending).join('');
|
|
134
|
+
}
|
|
135
|
+
// No exception yet: write one as the last content line of the header, which
|
|
136
|
+
// is where sasjs/core keeps them, so the header's own layout is untouched.
|
|
137
|
+
const closingIndex = headerLinesCount - 1;
|
|
138
|
+
const closing = lines[closingIndex];
|
|
139
|
+
if (!/^\s*[*]{2}\/\s*$/.test(closing))
|
|
140
|
+
return text;
|
|
141
|
+
const previous = lines[closingIndex - 1];
|
|
142
|
+
const indent = /^\s*/.exec(previous)[0] || ' ';
|
|
143
|
+
const ending = entries[closingIndex].ending || (0, exports.lineEndingOf)(configuration);
|
|
144
|
+
const separator = previous.trim() === '' ? [] : [''];
|
|
145
|
+
const inserted = [...separator, `${indent}@sasjslint ${json}`];
|
|
146
|
+
const out = lines
|
|
147
|
+
.slice(0, closingIndex)
|
|
148
|
+
.map((line, index) => line + entries[index].ending);
|
|
149
|
+
inserted.forEach((line) => out.push(line + ending));
|
|
150
|
+
lines.slice(closingIndex).forEach((line, index) => {
|
|
151
|
+
out.push(line + entries[closingIndex + index].ending);
|
|
152
|
+
});
|
|
153
|
+
return out.join('');
|
|
154
|
+
};
|
|
155
|
+
exports.adjustLineLength = adjustLineLength;
|
|
156
|
+
/** The configured line ending, for the rare case a terminator has to be invented. */
|
|
157
|
+
const lineEndingOf = (configuration) => configuration.lineEndings === LineEndings_1.LineEndings.CRLF ? '\r\n' : '\n';
|
|
158
|
+
exports.lineEndingOf = lineEndingOf;
|
|
159
|
+
//# sourceMappingURL=adjustLineLength.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adjustLineLength.js","sourceRoot":"","sources":["../../src/format/adjustLineLength.ts"],"names":[],"mappings":"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"}
|
package/format/formatFile.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { FormatResult } from '../types';
|
|
2
2
|
import { LintConfig } from '../types/LintConfig';
|
|
3
|
+
import { FormatOptions } from '../types/FormatOptions';
|
|
3
4
|
/**
|
|
4
5
|
* Applies automatic formatting to the file at the given path.
|
|
5
6
|
* @param {string} filePath - the path to the file to be formatted.
|
|
6
7
|
* @param {LintConfig} configuration - an optional configuration. When not passed in, this is read from the .sasjslint file.
|
|
8
|
+
* @param {FormatOptions} options - an optional layout engine, used for the reprint pass.
|
|
7
9
|
* @returns {Promise<FormatResult>} Resolves successfully when the file has been formatted.
|
|
8
10
|
*/
|
|
9
|
-
export declare const formatFile: (filePath: string, configuration?: LintConfig) => Promise<FormatResult>;
|
|
11
|
+
export declare const formatFile: (filePath: string, configuration?: LintConfig, options?: FormatOptions) => Promise<FormatResult>;
|
package/format/formatFile.js
CHANGED
|
@@ -12,21 +12,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.formatFile = void 0;
|
|
13
13
|
const file_1 = require("@sasjs/utils/file");
|
|
14
14
|
const lint_1 = require("../lint");
|
|
15
|
-
const parseLintOverride_1 = require("../utils/parseLintOverride");
|
|
16
15
|
const getLintConfig_1 = require("../utils/getLintConfig");
|
|
17
|
-
const
|
|
16
|
+
const formatText_1 = require("./formatText");
|
|
18
17
|
/**
|
|
19
18
|
* Applies automatic formatting to the file at the given path.
|
|
20
19
|
* @param {string} filePath - the path to the file to be formatted.
|
|
21
20
|
* @param {LintConfig} configuration - an optional configuration. When not passed in, this is read from the .sasjslint file.
|
|
21
|
+
* @param {FormatOptions} options - an optional layout engine, used for the reprint pass.
|
|
22
22
|
* @returns {Promise<FormatResult>} Resolves successfully when the file has been formatted.
|
|
23
23
|
*/
|
|
24
|
-
const formatFile = (
|
|
24
|
+
const formatFile = (filePath_1, configuration_1, ...args_1) => __awaiter(void 0, [filePath_1, configuration_1, ...args_1], void 0, function* (filePath, configuration, options = {}) {
|
|
25
25
|
const config = configuration || (yield (0, getLintConfig_1.getLintConfig)());
|
|
26
26
|
const diagnosticsBeforeFormat = yield (0, lint_1.lintFile)(filePath, config);
|
|
27
27
|
const diagnosticsCountBeforeFormat = diagnosticsBeforeFormat.length;
|
|
28
28
|
const text = yield (0, file_1.readFile)(filePath);
|
|
29
|
-
const formattedText = (0,
|
|
29
|
+
const formattedText = yield (0, formatText_1.formatText)(text, config, options);
|
|
30
30
|
yield (0, file_1.createFile)(filePath, formattedText);
|
|
31
31
|
const diagnosticsAfterFormat = yield (0, lint_1.lintFile)(filePath, config);
|
|
32
32
|
const diagnosticsCountAfterFormat = diagnosticsAfterFormat.length;
|
package/format/formatFile.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatFile.js","sourceRoot":"","sources":["../../src/format/formatFile.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,4CAAwD;AACxD,kCAAkC;AAGlC,
|
|
1
|
+
{"version":3,"file":"formatFile.js","sourceRoot":"","sources":["../../src/format/formatFile.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,4CAAwD;AACxD,kCAAkC;AAGlC,0DAAsD;AAEtD,6CAAyC;AAEzC;;;;;;GAMG;AACI,MAAM,UAAU,GAAG,yCAID,EAAE,gFAHzB,QAAgB,EAChB,aAA0B,EAC1B,UAAyB,EAAE;IAE3B,MAAM,MAAM,GAAG,aAAa,IAAI,CAAC,MAAM,IAAA,6BAAa,GAAE,CAAC,CAAA;IACvD,MAAM,uBAAuB,GAAG,MAAM,IAAA,eAAQ,EAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IAChE,MAAM,4BAA4B,GAAG,uBAAuB,CAAC,MAAM,CAAA;IAEnE,MAAM,IAAI,GAAG,MAAM,IAAA,eAAQ,EAAC,QAAQ,CAAC,CAAA;IAErC,MAAM,aAAa,GAAG,MAAM,IAAA,uBAAU,EAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;IAE7D,MAAM,IAAA,iBAAU,EAAC,QAAQ,EAAE,aAAa,CAAC,CAAA;IAEzC,MAAM,sBAAsB,GAAG,MAAM,IAAA,eAAQ,EAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IAC/D,MAAM,2BAA2B,GAAG,sBAAsB,CAAC,MAAM,CAAA;IAEjE,MAAM,qBAAqB,GACzB,4BAA4B,GAAG,2BAA2B,CAAA;IAE5D,MAAM,gBAAgB,GAAa,EAAE,CAAA;IAErC,IAAI,qBAAqB,EAAE,CAAC;QAC1B,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IACjC,CAAC;IAED,OAAO;QACL,gBAAgB;QAChB,qBAAqB;QACrB,kBAAkB,EAAE,sBAAsB;KAC3C,CAAA;AACH,CAAC,CAAA,CAAA;AAhCY,QAAA,UAAU,cAgCtB"}
|
package/format/formatFolder.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { FormatResult } from '../types';
|
|
2
2
|
import { LintConfig } from '../types/LintConfig';
|
|
3
|
+
import { FormatOptions } from '../types/FormatOptions';
|
|
3
4
|
/**
|
|
4
5
|
* Automatically formats all SAS files in the folder at the given path.
|
|
5
6
|
* @param {string} folderPath - the path to the folder to be formatted.
|
|
6
7
|
* @param {LintConfig} configuration - an optional configuration. When not passed in, this is read from the .sasjslint file.
|
|
8
|
+
* @param {FormatOptions} options - an optional layout engine, passed to every file.
|
|
7
9
|
* @returns {Promise<FormatResult>} Resolves successfully when all SAS files in the given folder have been formatted.
|
|
8
10
|
*/
|
|
9
|
-
export declare const formatFolder: (folderPath: string, configuration?: LintConfig) => Promise<FormatResult>;
|
|
11
|
+
export declare const formatFolder: (folderPath: string, configuration?: LintConfig, options?: FormatOptions) => Promise<FormatResult>;
|
package/format/formatFolder.js
CHANGED
|
@@ -20,6 +20,14 @@ const asyncForEach_1 = require("../utils/asyncForEach");
|
|
|
20
20
|
const getLintConfig_1 = require("../utils/getLintConfig");
|
|
21
21
|
const listSasFiles_1 = require("../utils/listSasFiles");
|
|
22
22
|
const formatFile_1 = require("./formatFile");
|
|
23
|
+
/**
|
|
24
|
+
* One line of the aggregate raised when a walk finishes with failures.
|
|
25
|
+
*
|
|
26
|
+
* The engine is the caller's own function, so what it throws is not necessarily an
|
|
27
|
+
* Error - a string is enough - and `error.message` on one of those reads
|
|
28
|
+
* `undefined`, dropping the reason the rejection was passed on for.
|
|
29
|
+
*/
|
|
30
|
+
const describeFailure = (where, error) => `${where}: ${error instanceof Error ? error.message : String(error)}`;
|
|
23
31
|
const excludeFolders = [
|
|
24
32
|
'.git',
|
|
25
33
|
'.github',
|
|
@@ -32,32 +40,55 @@ const excludeFolders = [
|
|
|
32
40
|
* Automatically formats all SAS files in the folder at the given path.
|
|
33
41
|
* @param {string} folderPath - the path to the folder to be formatted.
|
|
34
42
|
* @param {LintConfig} configuration - an optional configuration. When not passed in, this is read from the .sasjslint file.
|
|
43
|
+
* @param {FormatOptions} options - an optional layout engine, passed to every file.
|
|
35
44
|
* @returns {Promise<FormatResult>} Resolves successfully when all SAS files in the given folder have been formatted.
|
|
36
45
|
*/
|
|
37
|
-
const formatFolder = (
|
|
46
|
+
const formatFolder = (folderPath_1, configuration_1, ...args_1) => __awaiter(void 0, [folderPath_1, configuration_1, ...args_1], void 0, function* (folderPath, configuration, options = {}) {
|
|
38
47
|
const config = configuration || (yield (0, getLintConfig_1.getLintConfig)());
|
|
39
|
-
|
|
48
|
+
// The counts have to be measured with the same configuration the files are
|
|
49
|
+
// formatted by, or a rule the caller switched off is still counted as unfixed.
|
|
50
|
+
const diagnosticsBeforeFormat = yield (0, lint_1.lintFolder)(folderPath, config);
|
|
40
51
|
const diagnosticsCountBeforeFormat = Array.from(diagnosticsBeforeFormat.values()).reduce((a, b) => a + b.length, 0);
|
|
41
52
|
const fileNames = yield (0, listSasFiles_1.listSasFiles)(folderPath);
|
|
53
|
+
// A file the engine refuses must not decide what happens to the rest of the
|
|
54
|
+
// tree: the walk continues and the failures are raised together at the end, so
|
|
55
|
+
// one unformattable buffer cannot leave a project half written.
|
|
56
|
+
const failures = [];
|
|
42
57
|
yield (0, asyncForEach_1.asyncForEach)(fileNames, (fileName) => __awaiter(void 0, void 0, void 0, function* () {
|
|
43
58
|
const filePath = path_1.default.join(folderPath, fileName);
|
|
44
|
-
|
|
59
|
+
try {
|
|
60
|
+
yield (0, formatFile_1.formatFile)(filePath, config, options);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
failures.push(describeFailure(filePath, error));
|
|
64
|
+
}
|
|
45
65
|
}));
|
|
46
66
|
const subFolders = (yield (0, file_1.listSubFoldersInFolder)(folderPath)).filter((f) => !excludeFolders.includes(f));
|
|
47
67
|
yield (0, asyncForEach_1.asyncForEach)(subFolders, (subFolder) => __awaiter(void 0, void 0, void 0, function* () {
|
|
48
|
-
|
|
68
|
+
try {
|
|
69
|
+
yield (0, exports.formatFolder)(path_1.default.join(folderPath, subFolder), config, options);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
failures.push(describeFailure(path_1.default.join(folderPath, subFolder), error));
|
|
73
|
+
}
|
|
49
74
|
}));
|
|
50
|
-
const diagnosticsAfterFormat = yield (0, lint_1.lintFolder)(folderPath);
|
|
75
|
+
const diagnosticsAfterFormat = yield (0, lint_1.lintFolder)(folderPath, config);
|
|
51
76
|
const diagnosticsCountAfterFormat = Array.from(diagnosticsAfterFormat.values()).reduce((a, b) => a + b.length, 0);
|
|
52
77
|
const fixedDiagnosticsCount = diagnosticsCountBeforeFormat - diagnosticsCountAfterFormat;
|
|
53
78
|
const updatedFilePaths = [];
|
|
79
|
+
// Both maps come from lintFolder over the same file list, so every path in
|
|
80
|
+
// before is in after as well - a fallback there would be a branch no run reaches.
|
|
54
81
|
Array.from(diagnosticsBeforeFormat.keys()).forEach((filePath) => {
|
|
55
|
-
const diagnosticsBefore = diagnosticsBeforeFormat.get(filePath)
|
|
56
|
-
const diagnosticsAfter = diagnosticsAfterFormat.get(filePath)
|
|
82
|
+
const diagnosticsBefore = diagnosticsBeforeFormat.get(filePath);
|
|
83
|
+
const diagnosticsAfter = diagnosticsAfterFormat.get(filePath);
|
|
57
84
|
if (diagnosticsBefore.length !== diagnosticsAfter.length) {
|
|
58
85
|
updatedFilePaths.push(filePath);
|
|
59
86
|
}
|
|
60
87
|
});
|
|
88
|
+
if (failures.length) {
|
|
89
|
+
throw new Error(`Could not format ${failures.length} file(s). Everything else was formatted.\n` +
|
|
90
|
+
failures.map((failure) => ` ${failure}`).join('\n'));
|
|
91
|
+
}
|
|
61
92
|
return {
|
|
62
93
|
updatedFilePaths,
|
|
63
94
|
fixedDiagnosticsCount,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatFolder.js","sourceRoot":"","sources":["../../src/format/formatFolder.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,4CAA0D;AAC1D,gDAAuB;AACvB,kCAAoC;AAGpC,wDAAoD;AACpD,0DAAsD;AACtD,wDAAoD;
|
|
1
|
+
{"version":3,"file":"formatFolder.js","sourceRoot":"","sources":["../../src/format/formatFolder.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { FormatResult } from '../types/FormatResult';
|
|
2
|
+
import { FormatOptions } from '../types/FormatOptions';
|
|
2
3
|
/**
|
|
3
4
|
* Automatically formats all SAS files in the current project.
|
|
4
5
|
* @returns {Promise<FormatResult>} Resolves successfully when all SAS files in the current project have been formatted.
|
|
5
6
|
*/
|
|
6
|
-
export declare const formatProject: () => Promise<FormatResult>;
|
|
7
|
+
export declare const formatProject: (options?: FormatOptions) => Promise<FormatResult>;
|
package/format/formatProject.js
CHANGED
|
@@ -16,13 +16,13 @@ const formatFolder_1 = require("./formatFolder");
|
|
|
16
16
|
* Automatically formats all SAS files in the current project.
|
|
17
17
|
* @returns {Promise<FormatResult>} Resolves successfully when all SAS files in the current project have been formatted.
|
|
18
18
|
*/
|
|
19
|
-
const formatProject = () => __awaiter(void 0,
|
|
19
|
+
const formatProject = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (options = {}) {
|
|
20
20
|
const projectRoot = (yield (0, getProjectRoot_1.getProjectRoot)()) || process.currentDir;
|
|
21
21
|
if (!projectRoot) {
|
|
22
22
|
throw new Error('SASjs Project Root was not found.');
|
|
23
23
|
}
|
|
24
24
|
console.info(`Formatting all .sas files under ${projectRoot}`);
|
|
25
|
-
return yield (0, formatFolder_1.formatFolder)(projectRoot);
|
|
25
|
+
return yield (0, formatFolder_1.formatFolder)(projectRoot, undefined, options);
|
|
26
26
|
});
|
|
27
27
|
exports.formatProject = formatProject;
|
|
28
28
|
//# sourceMappingURL=formatProject.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatProject.js","sourceRoot":"","sources":["../../src/format/formatProject.ts"],"names":[],"mappings":";;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"formatProject.js","sourceRoot":"","sources":["../../src/format/formatProject.ts"],"names":[],"mappings":";;;;;;;;;;;;AAGA,4DAAwD;AACxD,iDAA6C;AAE7C;;;GAGG;AACI,MAAM,aAAa,GAAG,YAEJ,EAAE,mDADzB,UAAyB,EAAE;IAE3B,MAAM,WAAW,GAAG,CAAC,MAAM,IAAA,+BAAc,GAAE,CAAC,IAAI,OAAO,CAAC,UAAU,CAAA;IAClE,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAA;IACtD,CAAC;IAED,OAAO,CAAC,IAAI,CAAC,mCAAmC,WAAW,EAAE,CAAC,CAAA;IAE9D,OAAO,MAAM,IAAA,2BAAY,EAAC,WAAW,EAAE,SAAS,EAAE,OAAO,CAAC,CAAA;AAC5D,CAAC,CAAA,CAAA;AAXY,QAAA,aAAa,iBAWzB"}
|
package/format/formatText.d.ts
CHANGED
|
@@ -1,2 +1,20 @@
|
|
|
1
|
+
import { FormatOptions } from '../types/FormatOptions';
|
|
1
2
|
import { LintConfig } from '../types';
|
|
2
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Formats SAS text: the caller's reprint pass if one was supplied, then the
|
|
5
|
+
* configured rules.
|
|
6
|
+
*
|
|
7
|
+
* The order is deliberate. A reprint decides where lines break and how blocks are
|
|
8
|
+
* indented, so the rules that wrap and trim have to run over its output - applying
|
|
9
|
+
* them to the original text and reprinting afterwards would undo both. The lint
|
|
10
|
+
* override comment travels with the text through the reprint (it is a comment),
|
|
11
|
+
* so `keywordCase` from an `@sasjslint` line applies to the same buffer.
|
|
12
|
+
*
|
|
13
|
+
* A rejection from the engine is not caught: its message is the one that says why
|
|
14
|
+
* (`@sasjs/sas-language` names the data file it could not read, for example), and
|
|
15
|
+
* a formatter that swallowed it would return a buffer that looks formatted and is
|
|
16
|
+
* not. `formatFolder` is the one place that catches, because there a single
|
|
17
|
+
* refusal must not stop the walk - it collects them and raises them together once
|
|
18
|
+
* the tree is done.
|
|
19
|
+
*/
|
|
20
|
+
export declare const formatText: (text: string, configuration?: LintConfig, options?: FormatOptions) => Promise<string>;
|
package/format/formatText.js
CHANGED
|
@@ -12,9 +12,29 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.formatText = void 0;
|
|
13
13
|
const utils_1 = require("../utils");
|
|
14
14
|
const shared_1 = require("./shared");
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Formats SAS text: the caller's reprint pass if one was supplied, then the
|
|
17
|
+
* configured rules.
|
|
18
|
+
*
|
|
19
|
+
* The order is deliberate. A reprint decides where lines break and how blocks are
|
|
20
|
+
* indented, so the rules that wrap and trim have to run over its output - applying
|
|
21
|
+
* them to the original text and reprinting afterwards would undo both. The lint
|
|
22
|
+
* override comment travels with the text through the reprint (it is a comment),
|
|
23
|
+
* so `keywordCase` from an `@sasjslint` line applies to the same buffer.
|
|
24
|
+
*
|
|
25
|
+
* A rejection from the engine is not caught: its message is the one that says why
|
|
26
|
+
* (`@sasjs/sas-language` names the data file it could not read, for example), and
|
|
27
|
+
* a formatter that swallowed it would return a buffer that looks formatted and is
|
|
28
|
+
* not. `formatFolder` is the one place that catches, because there a single
|
|
29
|
+
* refusal must not stop the walk - it collects them and raises them together once
|
|
30
|
+
* the tree is done.
|
|
31
|
+
*/
|
|
32
|
+
const formatText = (text_1, configuration_1, ...args_1) => __awaiter(void 0, [text_1, configuration_1, ...args_1], void 0, function* (text, configuration, options = {}) {
|
|
33
|
+
const config = (0, utils_1.applyLintOverride)(text, configuration || (yield (0, utils_1.getLintConfig)()));
|
|
34
|
+
const formatted = options.reprint
|
|
35
|
+
? yield options.reprint(text, { keywordCase: config.keywordCase })
|
|
36
|
+
: text;
|
|
37
|
+
return (0, shared_1.processText)(formatted, config);
|
|
18
38
|
});
|
|
19
39
|
exports.formatText = formatText;
|
|
20
40
|
//# sourceMappingURL=formatText.js.map
|
package/format/formatText.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatText.js","sourceRoot":"","sources":["../../src/format/formatText.ts"],"names":[],"mappings":";;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"formatText.js","sourceRoot":"","sources":["../../src/format/formatText.ts"],"names":[],"mappings":";;;;;;;;;;;;AAEA,oCAA2D;AAC3D,qCAAsC;AAEtC;;;;;;;;;;;;;;;;GAgBG;AACI,MAAM,UAAU,GAAG,qCAIxB,EAAE,4EAHF,IAAY,EACZ,aAA0B,EAC1B,UAAyB,EAAE;IAE3B,MAAM,MAAM,GAAG,IAAA,yBAAiB,EAC9B,IAAI,EACJ,aAAa,IAAI,CAAC,MAAM,IAAA,qBAAa,GAAE,CAAC,CACzC,CAAA;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO;QAC/B,CAAC,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC;QAClE,CAAC,CAAC,IAAI,CAAA;IAER,OAAO,IAAA,oBAAW,EAAC,SAAS,EAAE,MAAM,CAAC,CAAA;AACvC,CAAC,CAAA,CAAA;AAfY,QAAA,UAAU,cAetB"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { LintConfig } from '../types';
|
|
2
|
+
/** A macro declaration, which the macro rules read as one statement. */
|
|
3
|
+
export declare const macroDeclaration: RegExp;
|
|
4
|
+
/**
|
|
5
|
+
* A macro comment statement - `%* text ;` - which is prose that happens to start
|
|
6
|
+
* with a trigger. Breaking it would put the terminator on a line of its own, and
|
|
7
|
+
* like a block comment it belongs to its author.
|
|
8
|
+
*/
|
|
9
|
+
export declare const macroComment: RegExp;
|
|
10
|
+
/**
|
|
11
|
+
* A statement whose line breaks are data rather than space between tokens.
|
|
12
|
+
*
|
|
13
|
+
* For the DATA step's `put` and `putlog`, and the macro and window statements
|
|
14
|
+
* that write text a reader sees, a break adds a line to the log or to the file
|
|
15
|
+
* being written. For `%let` a break lands inside the value, and the value keeps
|
|
16
|
+
* the newline and the continuation indent - measured on a live engine, where a
|
|
17
|
+
* wrapped `%let` message compared against a single-spaced one failed.
|
|
18
|
+
*
|
|
19
|
+
* Both are broken with the `%trim(` idiom rather than by a plain newline, and
|
|
20
|
+
* neither is left as it is: a break that carries `%trim(` adds no whitespace and
|
|
21
|
+
* no output line.
|
|
22
|
+
*/
|
|
23
|
+
export declare const breakAwareStatement: RegExp;
|
|
24
|
+
/**
|
|
25
|
+
* The index where the text of a `put`-family statement begins, so a break is
|
|
26
|
+
* never taken before the keyword that names it.
|
|
27
|
+
*
|
|
28
|
+
* A continuation line carries no keyword of its own, and its whole content is
|
|
29
|
+
* text, so there the text begins at the first non-whitespace character.
|
|
30
|
+
*
|
|
31
|
+
* @param {string} line - the line to read.
|
|
32
|
+
* @returns {number} the index of the first character of the statement's text.
|
|
33
|
+
*/
|
|
34
|
+
export declare const putTextStart: (line: string) => number;
|
|
35
|
+
/** The index of the first non-whitespace character at or after `from`. */
|
|
36
|
+
export declare const firstNonSpace: (line: string, from: number) => number;
|
|
37
|
+
/**
|
|
38
|
+
* The indentation a continuation line takes: the next multiple of
|
|
39
|
+
* `indentationMultiple` after the line's own, so a break can never introduce an
|
|
40
|
+
* `indentationMultiple` diagnostic of its own.
|
|
41
|
+
*/
|
|
42
|
+
export declare const continuationIndentFor: (indent: string, multiple: number) => string;
|
|
43
|
+
/** The indentation multiple in force, with the rule's documented default. */
|
|
44
|
+
export declare const indentationMultipleOf: (configuration: LintConfig) => number;
|
|
45
|
+
/**
|
|
46
|
+
* Reads whether one line opens, or carries the continuation of, a statement
|
|
47
|
+
* whose line breaks SAS reads as output lines.
|
|
48
|
+
*
|
|
49
|
+
* Splitting the code view on `;` gives one entry per statement on the line, the
|
|
50
|
+
* last entry being the part that has not reached its own terminator.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} code - the line with literals and comments blanked.
|
|
53
|
+
* @param {boolean} open - whether the previous line left such a statement open.
|
|
54
|
+
* @returns {{ line: boolean, open: boolean }} whether this line belongs to one,
|
|
55
|
+
* and whether the next line still does.
|
|
56
|
+
*/
|
|
57
|
+
export declare const breakAwareState: (code: string, open: boolean) => {
|
|
58
|
+
line: boolean;
|
|
59
|
+
open: boolean;
|
|
60
|
+
};
|