@sasjs/lint 5.3.0 → 5.4.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 +45 -0
- package/format/adjustLineLength.d.ts +34 -0
- package/format/adjustLineLength.js +159 -0
- package/format/adjustLineLength.js.map +1 -0
- 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 +10 -23
- package/rules/file/hasMacroParentheses.js +6 -16
- package/rules/file/hasMacroParentheses.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 +12 -0
- package/utils/getLintConfig.js +31 -5
- 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
|
@@ -4,6 +4,7 @@ exports.hasMacroParentheses = void 0;
|
|
|
4
4
|
const LintRuleType_1 = require("../../types/LintRuleType");
|
|
5
5
|
const Severity_1 = require("../../types/Severity");
|
|
6
6
|
const getColumnNumber_1 = require("../../utils/getColumnNumber");
|
|
7
|
+
const getIndex_1 = require("../../utils/getIndex");
|
|
7
8
|
const parseMacros_1 = require("../../utils/parseMacros");
|
|
8
9
|
const utils_1 = require("../../utils");
|
|
9
10
|
const getSeverity_1 = require("../../utils/getSeverity");
|
|
@@ -20,19 +21,6 @@ const message = 'Macro definition missing parentheses';
|
|
|
20
21
|
const parameterlessDeclaration = (macroName) => new RegExp(`(%macro\\s+${(0, utils_1.escapeRegExp)(macroName)})((?:\\s*/\\*.*?\\*/)?\\s*;)`, 'i');
|
|
21
22
|
/** The line of the declaration that carries the macro name. */
|
|
22
23
|
const nameLine = (macro) => macro.declarationLines.findIndex((declarationLine) => declarationLine.includes(macro.name));
|
|
23
|
-
/**
|
|
24
|
-
* The offset at which each line of the text starts, measured on the text as it
|
|
25
|
-
* stands. Used to edit one line without re-joining every other one, which would
|
|
26
|
-
* rewrite line endings this rule has no business touching.
|
|
27
|
-
*/
|
|
28
|
-
const lineStarts = (text) => {
|
|
29
|
-
const starts = [0];
|
|
30
|
-
for (let index = 0; index < text.length; index++) {
|
|
31
|
-
if (text[index] === '\n')
|
|
32
|
-
starts.push(index + 1);
|
|
33
|
-
}
|
|
34
|
-
return starts;
|
|
35
|
-
};
|
|
36
24
|
const test = (value, config) => {
|
|
37
25
|
const diagnostics = [];
|
|
38
26
|
const macros = (0, parseMacros_1.parseMacros)(value, config);
|
|
@@ -89,9 +77,11 @@ const fix = (value, config) => {
|
|
|
89
77
|
.filter(({ index }) => index !== -1);
|
|
90
78
|
return declarations.reduce((text, { macro, index }) => {
|
|
91
79
|
const lineNumber = macro.startLineNumbers[index];
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
80
|
+
// The offsets of this line and of the next one, measured on the text as it
|
|
81
|
+
// stands. `getIndex` clamps to the end of the file, so the last line ends
|
|
82
|
+
// there.
|
|
83
|
+
const start = (0, getIndex_1.getIndex)(text, lineNumber, 1);
|
|
84
|
+
const end = (0, getIndex_1.getIndex)(text, lineNumber + 1, 1);
|
|
95
85
|
// The line is taken with its terminator and put back with it, so every byte
|
|
96
86
|
// of the file other than the two this fix inserts is left alone.
|
|
97
87
|
const line = text.slice(start, end);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hasMacroParentheses.js","sourceRoot":"","sources":["../../../src/rules/file/hasMacroParentheses.ts"],"names":[],"mappings":";;;AAEA,2DAAuD;AACvD,mDAA+C;AAC/C,iEAA6D;AAC7D,yDAAqD;AAGrD,uCAA0C;AAC1C,yDAAqD;AAErD,MAAM,IAAI,GAAG,qBAAqB,CAAA;AAClC,MAAM,WAAW,GAAG,4DAA4D,CAAA;AAChF,MAAM,OAAO,GAAG,sCAAsC,CAAA;AAEtD;;;;;;GAMG;AACH,MAAM,wBAAwB,GAAG,CAAC,SAAiB,EAAE,EAAE,CACrD,IAAI,MAAM,CACR,cAAc,IAAA,oBAAY,EAAC,SAAS,CAAC,8BAA8B,EACnE,GAAG,CACJ,CAAA;AAEH,+DAA+D;AAC/D,MAAM,QAAQ,GAAG,CAAC,KAAY,EAAU,EAAE,CACxC,KAAK,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,eAAe,EAAE,EAAE,CACnD,eAAe,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CACrC,CAAA;AAEH
|
|
1
|
+
{"version":3,"file":"hasMacroParentheses.js","sourceRoot":"","sources":["../../../src/rules/file/hasMacroParentheses.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,20 @@ interface DataSectionsDetail {
|
|
|
3
3
|
start: number;
|
|
4
4
|
end: number;
|
|
5
5
|
}
|
|
6
|
+
/**
|
|
7
|
+
* A data section: the statement that opens it, and the record list it holds.
|
|
8
|
+
*
|
|
9
|
+
* The opener is matched against the line's CODE, with literals and comments
|
|
10
|
+
* blanked, so a `;cards;` inside a quoted string or a comment opens nothing -
|
|
11
|
+
* and against statement boundaries rather than the line start, so
|
|
12
|
+
* `input a b; cards;` on one line still opens one. The records themselves are
|
|
13
|
+
* not code: a quote in a record is data, so the scan state is reset at the
|
|
14
|
+
* opener and the records are matched as plain lines.
|
|
15
|
+
*
|
|
16
|
+
* @param {string} text - the text to read.
|
|
17
|
+
* @param {LintConfig} config - the configuration in force.
|
|
18
|
+
* @returns {DataSectionsDetail[]} the line ranges each section covers.
|
|
19
|
+
*/
|
|
6
20
|
export declare const getDataSectionsDetail: (text: string, config: LintConfig) => DataSectionsDetail[];
|
|
7
21
|
export declare const checkIsDataLine: (dataSections: DataSectionsDetail[], lineIndex: number) => boolean;
|
|
8
22
|
export {};
|
|
@@ -1,14 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.checkIsDataLine = exports.getDataSectionsDetail = void 0;
|
|
4
|
+
const scanLine_1 = require("./scanLine");
|
|
4
5
|
const splitText_1 = require("./splitText");
|
|
6
|
+
/**
|
|
7
|
+
* A data section: the statement that opens it, and the record list it holds.
|
|
8
|
+
*
|
|
9
|
+
* The opener is matched against the line's CODE, with literals and comments
|
|
10
|
+
* blanked, so a `;cards;` inside a quoted string or a comment opens nothing -
|
|
11
|
+
* and against statement boundaries rather than the line start, so
|
|
12
|
+
* `input a b; cards;` on one line still opens one. The records themselves are
|
|
13
|
+
* not code: a quote in a record is data, so the scan state is reset at the
|
|
14
|
+
* opener and the records are matched as plain lines.
|
|
15
|
+
*
|
|
16
|
+
* @param {string} text - the text to read.
|
|
17
|
+
* @param {LintConfig} config - the configuration in force.
|
|
18
|
+
* @returns {DataSectionsDetail[]} the line ranges each section covers.
|
|
19
|
+
*/
|
|
5
20
|
const getDataSectionsDetail = (text, config) => {
|
|
6
21
|
const dataSections = [];
|
|
7
22
|
const lines = (0, splitText_1.splitText)(text, config);
|
|
8
|
-
const
|
|
23
|
+
const opener = /(?:^|;)\s*(datalines|cards|parmcards)(4?);/i;
|
|
9
24
|
const dataSectionEndRegex1 = new RegExp(';');
|
|
10
|
-
const dataSectionStartRegex2 = new RegExp('^(datalines4;)|(cards4;)|(parmcards4;)');
|
|
11
25
|
const dataSectionEndRegex2 = new RegExp(';;;;');
|
|
26
|
+
let state = (0, scanLine_1.cleanState)();
|
|
12
27
|
let dataSectionStarted = false;
|
|
13
28
|
let dataSectionStartIndex = -1;
|
|
14
29
|
let dataSectionEndRegex = dataSectionEndRegex1;
|
|
@@ -17,19 +32,20 @@ const getDataSectionsDetail = (text, config) => {
|
|
|
17
32
|
if (dataSectionEndRegex.test(line)) {
|
|
18
33
|
dataSections.push({ start: dataSectionStartIndex, end: index });
|
|
19
34
|
dataSectionStarted = false;
|
|
35
|
+
state = (0, scanLine_1.cleanState)();
|
|
20
36
|
}
|
|
37
|
+
return;
|
|
21
38
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
39
|
+
const scanned = (0, scanLine_1.scanLine)(line, state);
|
|
40
|
+
state = scanned.endState;
|
|
41
|
+
const match = opener.exec(scanned.code);
|
|
42
|
+
if (match) {
|
|
43
|
+
dataSectionStarted = true;
|
|
44
|
+
dataSectionStartIndex = index;
|
|
45
|
+
dataSectionEndRegex = match[2]
|
|
46
|
+
? dataSectionEndRegex2
|
|
47
|
+
: dataSectionEndRegex1;
|
|
48
|
+
state = (0, scanLine_1.cleanState)();
|
|
33
49
|
}
|
|
34
50
|
});
|
|
35
51
|
return dataSections;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getDataSectionsDetail.js","sourceRoot":"","sources":["../../src/utils/getDataSectionsDetail.ts"],"names":[],"mappings":";;;AACA,2CAAuC;
|
|
1
|
+
{"version":3,"file":"getDataSectionsDetail.js","sourceRoot":"","sources":["../../src/utils/getDataSectionsDetail.ts"],"names":[],"mappings":";;;AACA,yCAAiD;AACjD,2CAAuC;AAOvC;;;;;;;;;;;;;GAaG;AACI,MAAM,qBAAqB,GAAG,CAAC,IAAY,EAAE,MAAkB,EAAE,EAAE;IACxE,MAAM,YAAY,GAAyB,EAAE,CAAA;IAC7C,MAAM,KAAK,GAAG,IAAA,qBAAS,EAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IAErC,MAAM,MAAM,GAAG,6CAA6C,CAAA;IAC5D,MAAM,oBAAoB,GAAG,IAAI,MAAM,CAAC,GAAG,CAAC,CAAA;IAC5C,MAAM,oBAAoB,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,CAAA;IAE/C,IAAI,KAAK,GAAG,IAAA,qBAAU,GAAE,CAAA;IACxB,IAAI,kBAAkB,GAAG,KAAK,CAAA;IAC9B,IAAI,qBAAqB,GAAG,CAAC,CAAC,CAAA;IAC9B,IAAI,mBAAmB,GAAG,oBAAoB,CAAA;IAE9C,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAC5B,IAAI,kBAAkB,EAAE,CAAC;YACvB,IAAI,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACnC,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAA;gBAC/D,kBAAkB,GAAG,KAAK,CAAA;gBAC1B,KAAK,GAAG,IAAA,qBAAU,GAAE,CAAA;YACtB,CAAC;YACD,OAAM;QACR,CAAC;QAED,MAAM,OAAO,GAAG,IAAA,mBAAQ,EAAC,IAAI,EAAE,KAAK,CAAC,CAAA;QACrC,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAA;QAExB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QAEvC,IAAI,KAAK,EAAE,CAAC;YACV,kBAAkB,GAAG,IAAI,CAAA;YACzB,qBAAqB,GAAG,KAAK,CAAA;YAC7B,mBAAmB,GAAG,KAAK,CAAC,CAAC,CAAC;gBAC5B,CAAC,CAAC,oBAAoB;gBACtB,CAAC,CAAC,oBAAoB,CAAA;YACxB,KAAK,GAAG,IAAA,qBAAU,GAAE,CAAA;QACtB,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,OAAO,YAAY,CAAA;AACrB,CAAC,CAAA;AAvCY,QAAA,qBAAqB,yBAuCjC;AAEM,MAAM,eAAe,GAAG,CAC7B,YAAkC,EAClC,SAAiB,EACjB,EAAE;IACF,KAAK,MAAM,WAAW,IAAI,YAAY,EAAE,CAAC;QACvC,IAAI,SAAS,IAAI,WAAW,CAAC,KAAK,IAAI,SAAS,IAAI,WAAW,CAAC,GAAG;YAChE,OAAO,IAAI,CAAA;IACf,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC,CAAA;AAVY,QAAA,eAAe,mBAU3B"}
|
package/utils/getLintConfig.d.ts
CHANGED
|
@@ -50,6 +50,18 @@ export declare const DefaultLintConfiguration: {
|
|
|
50
50
|
/**
|
|
51
51
|
* Fetches the config from the .sasjslint file (at project root or home directory) and creates a LintConfig object.
|
|
52
52
|
* Returns the default configuration when a .sasjslint file is unavailable.
|
|
53
|
+
*
|
|
54
|
+
* An absent file means the defaults, but a file that IS there and does not parse
|
|
55
|
+
* is a mistake in the project rather than a missing configuration: reporting it
|
|
56
|
+
* as "no file" applies a different rule set without saying so, which is how a
|
|
57
|
+
* commented-out rule or a stray trailing comma comes to look like it works.
|
|
53
58
|
* @returns {Promise<LintConfig>} resolves with an object representing the current lint configuration.
|
|
54
59
|
*/
|
|
55
60
|
export declare function getLintConfig(): Promise<LintConfig>;
|
|
61
|
+
/**
|
|
62
|
+
* Reads the configuration file, turning a parse failure into a message that
|
|
63
|
+
* names the file and says what to do about it. A raw `JSON.parse` error reads
|
|
64
|
+
* `Unexpected token } in JSON at position 12`, which leaves the reader to work
|
|
65
|
+
* out which file was read and why JSON is involved at all.
|
|
66
|
+
*/
|
|
67
|
+
export declare const parseLintConfig: (configuration: string, filePath: string) => any;
|
package/utils/getLintConfig.js
CHANGED
|
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.DefaultLintConfiguration = exports.getDefaultHeader = void 0;
|
|
15
|
+
exports.parseLintConfig = exports.DefaultLintConfiguration = exports.getDefaultHeader = void 0;
|
|
16
16
|
exports.getLintConfig = getLintConfig;
|
|
17
17
|
const path_1 = __importDefault(require("path"));
|
|
18
18
|
const os_1 = __importDefault(require("os"));
|
|
@@ -73,16 +73,42 @@ exports.DefaultLintConfiguration = {
|
|
|
73
73
|
/**
|
|
74
74
|
* Fetches the config from the .sasjslint file (at project root or home directory) and creates a LintConfig object.
|
|
75
75
|
* Returns the default configuration when a .sasjslint file is unavailable.
|
|
76
|
+
*
|
|
77
|
+
* An absent file means the defaults, but a file that IS there and does not parse
|
|
78
|
+
* is a mistake in the project rather than a missing configuration: reporting it
|
|
79
|
+
* as "no file" applies a different rule set without saying so, which is how a
|
|
80
|
+
* commented-out rule or a stray trailing comma comes to look like it works.
|
|
76
81
|
* @returns {Promise<LintConfig>} resolves with an object representing the current lint configuration.
|
|
77
82
|
*/
|
|
78
83
|
function getLintConfig() {
|
|
79
84
|
return __awaiter(this, void 0, void 0, function* () {
|
|
80
85
|
const projectRoot = yield (0, getProjectRoot_1.getProjectRoot)();
|
|
81
86
|
const lintFileLocation = projectRoot || os_1.default.homedir();
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
87
|
+
const lintFilePath = path_1.default.join(lintFileLocation, '.sasjslint');
|
|
88
|
+
const configuration = yield (0, file_1.readFile)(lintFilePath).catch((_) => null);
|
|
89
|
+
if (configuration === null) {
|
|
90
|
+
return new LintConfig_1.LintConfig(exports.DefaultLintConfiguration);
|
|
91
|
+
}
|
|
92
|
+
return new LintConfig_1.LintConfig((0, exports.parseLintConfig)(configuration, lintFilePath));
|
|
86
93
|
});
|
|
87
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Reads the configuration file, turning a parse failure into a message that
|
|
97
|
+
* names the file and says what to do about it. A raw `JSON.parse` error reads
|
|
98
|
+
* `Unexpected token } in JSON at position 12`, which leaves the reader to work
|
|
99
|
+
* out which file was read and why JSON is involved at all.
|
|
100
|
+
*/
|
|
101
|
+
const parseLintConfig = (configuration, filePath) => {
|
|
102
|
+
try {
|
|
103
|
+
return JSON.parse(configuration);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
107
|
+
throw new Error(`${filePath} is not valid JSON (${reason}).\n` +
|
|
108
|
+
'The file is read as strict JSON, so it cannot carry comments. ' +
|
|
109
|
+
'Every setting, its type and its default are documented in the schema: ' +
|
|
110
|
+
'https://cli.sasjs.io/sasjslint-schema.json');
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
exports.parseLintConfig = parseLintConfig;
|
|
88
114
|
//# sourceMappingURL=getLintConfig.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getLintConfig.js","sourceRoot":"","sources":["../../src/utils/getLintConfig.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"getLintConfig.js","sourceRoot":"","sources":["../../src/utils/getLintConfig.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads one line of a SAS program, carrying the state a break decision needs
|
|
3
|
+
* from one physical line to the next.
|
|
4
|
+
*
|
|
5
|
+
* A quoted literal, a block comment and a masking macro's argument text all
|
|
6
|
+
* continue onto the next line, so a caller that decides where a line may be
|
|
7
|
+
* broken - or where a statement really ends - needs to know which of them is
|
|
8
|
+
* open. The scan is one pass with those three states, because each affects the
|
|
9
|
+
* reading of the others: `%str(%')` is an apostrophe, and a scanner that read it
|
|
10
|
+
* as the start of a literal would carry a phantom string into every line below.
|
|
11
|
+
*/
|
|
12
|
+
export interface ScanState {
|
|
13
|
+
/** The quote character a literal is still open with, or `''`. */
|
|
14
|
+
quote: string;
|
|
15
|
+
/** Whether a block comment is still open. */
|
|
16
|
+
inComment: boolean;
|
|
17
|
+
/** Depth of the masking call the scan is inside. */
|
|
18
|
+
maskedDepth: number;
|
|
19
|
+
}
|
|
20
|
+
export declare const cleanState: () => ScanState;
|
|
21
|
+
/** A block comment's span on one line, as absolute column indices. */
|
|
22
|
+
export interface CommentSpan {
|
|
23
|
+
/** The column at which the comment opens. */
|
|
24
|
+
start: number;
|
|
25
|
+
/** The column of the closing marker's asterisk, or the line length. */
|
|
26
|
+
end: number;
|
|
27
|
+
}
|
|
28
|
+
/** A physical line, as the scanner reads it. */
|
|
29
|
+
export interface ScannedLine {
|
|
30
|
+
/**
|
|
31
|
+
* The columns at which a newline may be inserted: every space that is not
|
|
32
|
+
* inside a quoted literal, inside a masking macro's parentheses, or inside a
|
|
33
|
+
* block comment.
|
|
34
|
+
*/
|
|
35
|
+
positions: number[];
|
|
36
|
+
/** The line with every literal and comment character blanked to a space. */
|
|
37
|
+
code: string;
|
|
38
|
+
/** Whether the line carries any block comment, open or closed. */
|
|
39
|
+
hasComment: boolean;
|
|
40
|
+
/** Each block comment the line opens, with the span it covers. */
|
|
41
|
+
comments: CommentSpan[];
|
|
42
|
+
endState: ScanState;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The scan of a line that is not code, and so opens and closes nothing.
|
|
46
|
+
*
|
|
47
|
+
* A `datalines` record is data: a comment marker or an apostrophe in one is a
|
|
48
|
+
* character in a value, not the start of a comment or a literal. Reading such a
|
|
49
|
+
* line with the code scanner would carry that state into every line below it.
|
|
50
|
+
*/
|
|
51
|
+
export declare const noScan: () => ScannedLine;
|
|
52
|
+
/**
|
|
53
|
+
* Reads one line, starting from the state the previous line ended in.
|
|
54
|
+
*
|
|
55
|
+
* A break is never taken inside a literal, because a SAS literal really does
|
|
56
|
+
* continue onto the next line (`"ab` / `cd"` is one value) and an inserted
|
|
57
|
+
* newline would become part of the string.
|
|
58
|
+
*/
|
|
59
|
+
export declare const scanLine: (line: string, state: ScanState) => ScannedLine;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.scanLine = exports.noScan = exports.cleanState = void 0;
|
|
4
|
+
const cleanState = () => ({
|
|
5
|
+
quote: '',
|
|
6
|
+
inComment: false,
|
|
7
|
+
maskedDepth: 0
|
|
8
|
+
});
|
|
9
|
+
exports.cleanState = cleanState;
|
|
10
|
+
/**
|
|
11
|
+
* The calls whose argument text is carried as DATA rather than as tokens.
|
|
12
|
+
*
|
|
13
|
+
* The masking macros - `%str(`, `%nrstr(`, `%bquote(`, `%nrbquote(`,
|
|
14
|
+
* `%superq(`, `%nrsuperq(`, `%quote(`, `%nrquote(`, `%unquote(` - return their
|
|
15
|
+
* argument as a value, so a newline inserted inside one becomes part of it.
|
|
16
|
+
* `%sysfunc(` and `%qsysfunc(` are the same problem one level down: the argument
|
|
17
|
+
* text is handed to a DATA step function, and for the text functions the
|
|
18
|
+
* whitespace is data. Measured on a live engine, breaking inside
|
|
19
|
+
* `%sysfunc(fput(&fid,%str(&name): %str(&value)))` wrote
|
|
20
|
+
* `Content-Type: application/csv` where the macro meant one space - so no
|
|
21
|
+
* space inside either form is a break point.
|
|
22
|
+
*/
|
|
23
|
+
const maskStart = /%n?r?(?:str|bquote|superq|quote|unquote)\s*\(|%q?sysfunc\s*\(/iy;
|
|
24
|
+
/**
|
|
25
|
+
* The scan of a line that is not code, and so opens and closes nothing.
|
|
26
|
+
*
|
|
27
|
+
* A `datalines` record is data: a comment marker or an apostrophe in one is a
|
|
28
|
+
* character in a value, not the start of a comment or a literal. Reading such a
|
|
29
|
+
* line with the code scanner would carry that state into every line below it.
|
|
30
|
+
*/
|
|
31
|
+
const noScan = () => ({
|
|
32
|
+
positions: [],
|
|
33
|
+
code: '',
|
|
34
|
+
hasComment: false,
|
|
35
|
+
comments: [],
|
|
36
|
+
endState: (0, exports.cleanState)()
|
|
37
|
+
});
|
|
38
|
+
exports.noScan = noScan;
|
|
39
|
+
/**
|
|
40
|
+
* Reads one line, starting from the state the previous line ended in.
|
|
41
|
+
*
|
|
42
|
+
* A break is never taken inside a literal, because a SAS literal really does
|
|
43
|
+
* continue onto the next line (`"ab` / `cd"` is one value) and an inserted
|
|
44
|
+
* newline would become part of the string.
|
|
45
|
+
*/
|
|
46
|
+
const scanLine = (line, state) => {
|
|
47
|
+
const positions = [];
|
|
48
|
+
const code = new Array(line.length).fill(' ');
|
|
49
|
+
const comments = [];
|
|
50
|
+
let { quote, inComment, maskedDepth } = state;
|
|
51
|
+
let hasComment = inComment;
|
|
52
|
+
let commentStart = inComment ? 0 : -1;
|
|
53
|
+
for (let index = 0; index < line.length; index++) {
|
|
54
|
+
const character = line[index];
|
|
55
|
+
if (inComment) {
|
|
56
|
+
if (character === '*' && line[index + 1] === '/') {
|
|
57
|
+
inComment = false;
|
|
58
|
+
comments.push({ start: commentStart, end: index });
|
|
59
|
+
index++;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
// A space inside a block comment is prose, not a break point: the
|
|
63
|
+
// contract above states it, and no caller wants one - the wrap refuses a
|
|
64
|
+
// comment-bearing line and `reflowComments` owns its text.
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
// Masked text is data, so it is read before anything that OPENS a literal
|
|
68
|
+
// or a comment: `%str(%')` is how a single quote is written, and reading
|
|
69
|
+
// that apostrophe as the start of a literal would carry a phantom string
|
|
70
|
+
// into every line below it.
|
|
71
|
+
if (maskedDepth) {
|
|
72
|
+
if (character === '(')
|
|
73
|
+
maskedDepth++;
|
|
74
|
+
else if (character === ')')
|
|
75
|
+
maskedDepth--;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (quote) {
|
|
79
|
+
if (character === quote)
|
|
80
|
+
quote = '';
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (character === "'" || character === '"') {
|
|
84
|
+
quote = character;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (character === '/' && line[index + 1] === '*') {
|
|
88
|
+
inComment = true;
|
|
89
|
+
hasComment = true;
|
|
90
|
+
commentStart = index;
|
|
91
|
+
index++;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
maskStart.lastIndex = index;
|
|
95
|
+
if (maskStart.test(line)) {
|
|
96
|
+
maskedDepth = 1;
|
|
97
|
+
index = maskStart.lastIndex - 1;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (character === ' ')
|
|
101
|
+
positions.push(index);
|
|
102
|
+
code[index] = character;
|
|
103
|
+
}
|
|
104
|
+
if (inComment)
|
|
105
|
+
comments.push({ start: commentStart, end: line.length });
|
|
106
|
+
return {
|
|
107
|
+
positions,
|
|
108
|
+
code: code.join(''),
|
|
109
|
+
hasComment,
|
|
110
|
+
comments,
|
|
111
|
+
endState: { quote, inComment, maskedDepth }
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
exports.scanLine = scanLine;
|
|
115
|
+
//# sourceMappingURL=scanLine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanLine.js","sourceRoot":"","sources":["../../src/utils/scanLine.ts"],"names":[],"mappings":"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"}
|