@zentryc5t/ts-plugin-for-bruno 3.0.6 → 3.2.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 +4 -0
- package/out/codeBlocks/bruCodeBlocks.js +71 -0
- package/out/codeBlocks/bruCodeBlocks.js.map +1 -0
- package/out/codeChanges/codeChanges.js +144 -0
- package/out/codeChanges/codeChanges.js.map +1 -0
- package/out/collection/collectionFormat.js +69 -0
- package/out/collection/collectionFormat.js.map +1 -0
- package/out/constants.js +36 -0
- package/out/constants.js.map +1 -0
- package/out/diagnostics/filterDefaultDiagnostics.js +81 -0
- package/out/diagnostics/filterDefaultDiagnostics.js.map +1 -0
- package/out/fileContent.js +28 -0
- package/out/fileContent.js.map +1 -0
- package/out/fileTypes.js +16 -0
- package/out/fileTypes.js.map +1 -0
- package/out/index.js +18 -260
- package/out/index.js.map +1 -1
- package/out/languageService/codeChangeOverrides.js +40 -0
- package/out/languageService/codeChangeOverrides.js.map +1 -0
- package/out/languageService/diagnosticsOverrides.js +41 -0
- package/out/languageService/diagnosticsOverrides.js.map +1 -0
- package/out/languageService/formattingOverrides.js +18 -0
- package/out/languageService/formattingOverrides.js.map +1 -0
- package/out/languageService/navigationOverrides.js +98 -0
- package/out/languageService/navigationOverrides.js.map +1 -0
- package/out/pluginConfiguration.js +29 -0
- package/out/pluginConfiguration.js.map +1 -0
- package/out/yamlFiles/getIndentationOfScripts.js +24 -0
- package/out/yamlFiles/getIndentationOfScripts.js.map +1 -0
- package/out/yamlFiles/maskYamlFileContent.js +126 -0
- package/out/yamlFiles/maskYamlFileContent.js.map +1 -0
- package/out/yamlFiles/maskYamlFilesForTypescriptServer.js +94 -0
- package/out/yamlFiles/maskYamlFilesForTypescriptServer.js.map +1 -0
- package/package.json +4 -3
- package/src/codeBlocks/bruCodeBlocks.ts +66 -0
- package/src/codeChanges/codeChanges.ts +182 -0
- package/src/collection/collectionFormat.ts +96 -0
- package/src/constants.test.ts +12 -0
- package/src/constants.ts +31 -0
- package/src/diagnostics/filterDefaultDiagnostics.ts +145 -0
- package/src/fileContent.ts +38 -0
- package/src/fileTypes.ts +13 -0
- package/src/index.test.ts +329 -0
- package/src/index.ts +24 -496
- package/src/languageService/codeChangeOverrides.ts +90 -0
- package/src/languageService/diagnosticsOverrides.ts +65 -0
- package/src/languageService/formattingOverrides.ts +42 -0
- package/src/languageService/navigationOverrides.ts +201 -0
- package/src/pluginConfiguration.ts +34 -0
- package/src/yamlFiles/getIndentationOfScripts.ts +34 -0
- package/src/yamlFiles/maskYamlFileContent.test.ts +213 -0
- package/src/yamlFiles/maskYamlFileContent.ts +179 -0
- package/src/yamlFiles/maskYamlFilesForTypescriptServer.ts +85 -0
- package/tsconfig.json +2 -1
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.maskYamlCollectionFileContent = maskYamlCollectionFileContent;
|
|
4
|
+
exports.maskYamlFileContent = maskYamlFileContent;
|
|
5
|
+
exports.getScriptContentRanges = getScriptContentRanges;
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
const yaml_1 = require("yaml");
|
|
8
|
+
const constants_1 = require("../constants");
|
|
9
|
+
/**
|
|
10
|
+
* Returns the content of a Yaml file from a Bruno collection the way it should be seen by the Typescript server.
|
|
11
|
+
*
|
|
12
|
+
* All characters that are not part of a script's code are replaced by spaces.
|
|
13
|
+
* Line breaks are kept, so that all offsets and line / character positions are equal to the ones in the original content.
|
|
14
|
+
* Each script is wrapped in a block (`{ ... }`), so that its declarations are scoped like the ones in code blocks of `.bru` files.
|
|
15
|
+
*
|
|
16
|
+
* Only the scripts returned by `getScriptContentRanges` are kept.
|
|
17
|
+
*/
|
|
18
|
+
function maskYamlCollectionFileContent(fileName, fileContent) {
|
|
19
|
+
const maskedCharacters = maskYamlFileContent(fileContent).split("");
|
|
20
|
+
const scriptRanges = getScriptContentRanges(fileName, fileContent);
|
|
21
|
+
let firstFreeIndex = 0;
|
|
22
|
+
let suffix = "";
|
|
23
|
+
for (const [index, { start, end }] of scriptRanges.entries()) {
|
|
24
|
+
const isLastScript = index == scriptRanges.length - 1;
|
|
25
|
+
const openingBracketIndex = findLastIndexWithoutLineBreak(fileContent, firstFreeIndex, start);
|
|
26
|
+
const closingBracketIndex = findFirstIndexWithoutLineBreak(fileContent, end, isLastScript ? fileContent.length : scriptRanges[index + 1].start);
|
|
27
|
+
if (openingBracketIndex == undefined ||
|
|
28
|
+
(closingBracketIndex == undefined && !isLastScript)) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
for (let i = start; i < end; i++) {
|
|
32
|
+
maskedCharacters[i] = fileContent[i];
|
|
33
|
+
}
|
|
34
|
+
maskedCharacters[openingBracketIndex] = "{";
|
|
35
|
+
if (closingBracketIndex == undefined) {
|
|
36
|
+
// Appending characters at the end does not shift any positions.
|
|
37
|
+
// A line break is needed if the script ends at the end of the file, since the closing bracket could otherwise be part of a line comment.
|
|
38
|
+
suffix = isLineBreak(fileContent[fileContent.length - 1])
|
|
39
|
+
? "}"
|
|
40
|
+
: "\n}";
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
maskedCharacters[closingBracketIndex] = "}";
|
|
44
|
+
firstFreeIndex = closingBracketIndex + 1;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return maskedCharacters.join("").concat(suffix);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Replaces all characters except for line breaks by spaces, so that the Typescript server does not provide any language features for the file.
|
|
51
|
+
*/
|
|
52
|
+
function maskYamlFileContent(fileContent) {
|
|
53
|
+
return fileContent.replace(/[^\r\n\u2028\u2029]/g, " ");
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Scripts are defined in `runtime.scripts` for request files and in `request.scripts` for folder and collection settings files.
|
|
57
|
+
*
|
|
58
|
+
* The same scripts are returned as by `getScriptBlocksFromYamlFile` in the shared folder of the extension:
|
|
59
|
+
* Only scripts with a valid type that are defined as literal block scalars (`code: |`) are included.
|
|
60
|
+
* For all other scalar styles, the raw text in the file is not equal to the code (e.g. due to quotes or line folding).
|
|
61
|
+
*/
|
|
62
|
+
function getScriptContentRanges(fileName, fileContent) {
|
|
63
|
+
const document = (0, yaml_1.parseDocument)(fileContent);
|
|
64
|
+
const scripts = document.getIn(isSettingsFile(fileName, document)
|
|
65
|
+
? ["request", "scripts"]
|
|
66
|
+
: ["runtime", "scripts"], true);
|
|
67
|
+
return ((0, yaml_1.isSeq)(scripts) ? scripts.items : [])
|
|
68
|
+
.flatMap((script) => {
|
|
69
|
+
if (!(0, yaml_1.isMap)(script) || !isValidScriptType(script.get("type"))) {
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
const code = script.get("code", true);
|
|
73
|
+
if (!(0, yaml_1.isScalar)(code) ||
|
|
74
|
+
code.type != yaml_1.Scalar.BLOCK_LITERAL ||
|
|
75
|
+
!code.range) {
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
const [headerStart, valueEnd] = code.range;
|
|
79
|
+
const range = getContentRangeForBlockScalar(fileContent, headerStart, valueEnd);
|
|
80
|
+
return range ? [range] : [];
|
|
81
|
+
})
|
|
82
|
+
.sort(({ start: start1 }, { start: start2 }) => start1 - start2);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Folder settings files are always named `folder.yml`.
|
|
86
|
+
* The collection settings file is the only file that defines the top-level key `opencollection`.
|
|
87
|
+
*/
|
|
88
|
+
function isSettingsFile(fileName, document) {
|
|
89
|
+
return (0, path_1.basename)(fileName) == "folder.yml" || document.has("opencollection");
|
|
90
|
+
}
|
|
91
|
+
function isValidScriptType(type) {
|
|
92
|
+
return Object.values(constants_1.ScriptType).includes(type);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The range of a block scalar node starts with the header (e.g. `|-`), which is followed by the content on the next lines.
|
|
96
|
+
*/
|
|
97
|
+
function getContentRangeForBlockScalar(fileContent, headerStart, valueEnd) {
|
|
98
|
+
const lineBreakAfterHeader = /\r\n|\r|\n|\u2028|\u2029/g;
|
|
99
|
+
lineBreakAfterHeader.lastIndex = headerStart;
|
|
100
|
+
const match = lineBreakAfterHeader.exec(fileContent);
|
|
101
|
+
if (!match) {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
const start = match.index + match[0].length;
|
|
105
|
+
return start < valueEnd ? { start, end: valueEnd } : undefined;
|
|
106
|
+
}
|
|
107
|
+
function findLastIndexWithoutLineBreak(text, startIndex, endIndexExclusive) {
|
|
108
|
+
for (let i = endIndexExclusive - 1; i >= startIndex; i--) {
|
|
109
|
+
if (!isLineBreak(text[i])) {
|
|
110
|
+
return i;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
function findFirstIndexWithoutLineBreak(text, startIndex, endIndexExclusive) {
|
|
116
|
+
for (let i = startIndex; i < endIndexExclusive; i++) {
|
|
117
|
+
if (!isLineBreak(text[i])) {
|
|
118
|
+
return i;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
function isLineBreak(character) {
|
|
124
|
+
return ["\r", "\n", "\u2028", "\u2029"].includes(character);
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=maskYamlFileContent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"maskYamlFileContent.js","sourceRoot":"","sources":["../../src/yamlFiles/maskYamlFileContent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.maskYamlFilesForTypescriptServer = maskYamlFilesForTypescriptServer;
|
|
37
|
+
const ts = __importStar(require("typescript/lib/tsserverlibrary"));
|
|
38
|
+
const collectionFormat_1 = require("../collection/collectionFormat");
|
|
39
|
+
const fileContent_1 = require("../fileContent");
|
|
40
|
+
const fileTypes_1 = require("../fileTypes");
|
|
41
|
+
const maskYamlFileContent_1 = require("./maskYamlFileContent");
|
|
42
|
+
/**
|
|
43
|
+
* The Typescript server parses Yaml files like Typescript files, so that the Yaml content would affect the meaning of the code within scripts.
|
|
44
|
+
* Therefore, the Typescript server only gets to see the code of scripts within Yaml collection files (see `maskYamlCollectionFileContent`).
|
|
45
|
+
* All other Yaml files are masked completely. The same applies to Yaml collection files, as long as Yaml collections are not supported.
|
|
46
|
+
*/
|
|
47
|
+
function maskYamlFilesForTypescriptServer(info, configuration) {
|
|
48
|
+
const host = info.languageServiceHost;
|
|
49
|
+
// Masking already masked content would remove all scripts.
|
|
50
|
+
if (fileContent_1.originalSnapshotGettersForHosts.has(host)) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const getOriginalSnapshot = host.getScriptSnapshot.bind(host);
|
|
54
|
+
const getOriginalScriptVersion = host.getScriptVersion.bind(host);
|
|
55
|
+
fileContent_1.originalSnapshotGettersForHosts.set(host, getOriginalSnapshot);
|
|
56
|
+
// A new snapshot is only created, if the content of a file or the configuration changes.
|
|
57
|
+
let maskedSnapshots = new WeakMap();
|
|
58
|
+
let configurationVersion = 0;
|
|
59
|
+
configuration.onDidChange(() => {
|
|
60
|
+
maskedSnapshots = new WeakMap();
|
|
61
|
+
configurationVersion++;
|
|
62
|
+
// The project is not set when the plugin is used without a Typescript server (e.g. in tests).
|
|
63
|
+
if (info.project) {
|
|
64
|
+
info.project.refreshDiagnostics();
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
// The Typescript server only requests a new snapshot for a file, if the project version and the version of the file change.
|
|
68
|
+
if (host.getProjectVersion) {
|
|
69
|
+
const getOriginalProjectVersion = host.getProjectVersion.bind(host);
|
|
70
|
+
host.getProjectVersion = () => `${getOriginalProjectVersion()}-${configurationVersion}`;
|
|
71
|
+
}
|
|
72
|
+
host.getScriptVersion = (fileName) => (0, fileTypes_1.isYamlFile)(fileName)
|
|
73
|
+
? `${getOriginalScriptVersion(fileName)}-${configurationVersion}`
|
|
74
|
+
: getOriginalScriptVersion(fileName);
|
|
75
|
+
host.getScriptSnapshot = (fileName) => {
|
|
76
|
+
const originalSnapshot = getOriginalSnapshot(fileName);
|
|
77
|
+
if (!originalSnapshot || !(0, fileTypes_1.isYamlFile)(fileName)) {
|
|
78
|
+
return originalSnapshot;
|
|
79
|
+
}
|
|
80
|
+
const cachedSnapshot = maskedSnapshots.get(originalSnapshot);
|
|
81
|
+
if (cachedSnapshot) {
|
|
82
|
+
return cachedSnapshot;
|
|
83
|
+
}
|
|
84
|
+
const originalContent = originalSnapshot.getText(0, originalSnapshot.getLength());
|
|
85
|
+
// Snapshots created from a string do not support incremental parsing, so the file is always parsed completely.
|
|
86
|
+
const maskedSnapshot = ts.ScriptSnapshot.fromString(configuration.isYamlCollectionSupportEnabled() &&
|
|
87
|
+
(0, collectionFormat_1.isFileFromYamlCollection)(info, fileName)
|
|
88
|
+
? (0, maskYamlFileContent_1.maskYamlCollectionFileContent)(fileName, originalContent)
|
|
89
|
+
: (0, maskYamlFileContent_1.maskYamlFileContent)(originalContent));
|
|
90
|
+
maskedSnapshots.set(originalSnapshot, maskedSnapshot);
|
|
91
|
+
return maskedSnapshot;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=maskYamlFilesForTypescriptServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"maskYamlFilesForTypescriptServer.js","sourceRoot":"","sources":["../../src/yamlFiles/maskYamlFilesForTypescriptServer.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAeA,4EAqEC;AApFD,mEAAqD;AACrD,qEAA0E;AAC1E,gDAAiE;AACjE,4CAA0C;AAE1C,+DAG+B;AAE/B;;;;GAIG;AACH,SAAgB,gCAAgC,CAC5C,IAAgC,EAChC,aAAkC;IAElC,MAAM,IAAI,GAAG,IAAI,CAAC,mBAAmB,CAAC;IAEtC,2DAA2D;IAC3D,IAAI,6CAA+B,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5C,OAAO;IACX,CAAC;IAED,MAAM,mBAAmB,GAAG,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9D,MAAM,wBAAwB,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClE,6CAA+B,CAAC,GAAG,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC;IAC/D,yFAAyF;IACzF,IAAI,eAAe,GAAG,IAAI,OAAO,EAA0C,CAAC;IAC5E,IAAI,oBAAoB,GAAG,CAAC,CAAC;IAE7B,aAAa,CAAC,WAAW,CAAC,GAAG,EAAE;QAC3B,eAAe,GAAG,IAAI,OAAO,EAAE,CAAC;QAChC,oBAAoB,EAAE,CAAC;QACvB,8FAA8F;QAC9F,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,CAAC;QACtC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,4HAA4H;IAC5H,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzB,MAAM,yBAAyB,GAAG,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpE,IAAI,CAAC,iBAAiB,GAAG,GAAG,EAAE,CAC1B,GAAG,yBAAyB,EAAE,IAAI,oBAAoB,EAAE,CAAC;IACjE,CAAC;IAED,IAAI,CAAC,gBAAgB,GAAG,CAAC,QAAQ,EAAE,EAAE,CACjC,IAAA,sBAAU,EAAC,QAAQ,CAAC;QAChB,CAAC,CAAC,GAAG,wBAAwB,CAAC,QAAQ,CAAC,IAAI,oBAAoB,EAAE;QACjE,CAAC,CAAC,wBAAwB,CAAC,QAAQ,CAAC,CAAC;IAE7C,IAAI,CAAC,iBAAiB,GAAG,CAAC,QAAQ,EAAE,EAAE;QAClC,MAAM,gBAAgB,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;QAEvD,IAAI,CAAC,gBAAgB,IAAI,CAAC,IAAA,sBAAU,EAAC,QAAQ,CAAC,EAAE,CAAC;YAC7C,OAAO,gBAAgB,CAAC;QAC5B,CAAC;QAED,MAAM,cAAc,GAAG,eAAe,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAE7D,IAAI,cAAc,EAAE,CAAC;YACjB,OAAO,cAAc,CAAC;QAC1B,CAAC;QAED,MAAM,eAAe,GAAG,gBAAgB,CAAC,OAAO,CAC5C,CAAC,EACD,gBAAgB,CAAC,SAAS,EAAE,CAC/B,CAAC;QAEF,+GAA+G;QAC/G,MAAM,cAAc,GAAG,EAAE,CAAC,cAAc,CAAC,UAAU,CAC/C,aAAa,CAAC,8BAA8B,EAAE;YAC1C,IAAA,2CAAwB,EAAC,IAAI,EAAE,QAAQ,CAAC;YACxC,CAAC,CAAC,IAAA,mDAA6B,EAAC,QAAQ,EAAE,eAAe,CAAC;YAC1D,CAAC,CAAC,IAAA,yCAAmB,EAAC,eAAe,CAAC,CAC7C,CAAC;QAEF,eAAe,CAAC,GAAG,CAAC,gBAAgB,EAAE,cAAc,CAAC,CAAC;QAEtD,OAAO,cAAc,CAAC;IAC1B,CAAC,CAAC;AACN,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zentryc5t/ts-plugin-for-bruno",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"main": "./out/index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"compile": "tsc --project tsconfig.json"
|
|
@@ -12,9 +12,10 @@
|
|
|
12
12
|
"url": "git+https://github.com/daniel-b92/bruno-vs-code-extension.git#main"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"typescript": "^
|
|
15
|
+
"typescript": "^6.0.3",
|
|
16
|
+
"yaml": "^2.9.0"
|
|
16
17
|
},
|
|
17
18
|
"devDependencies": {
|
|
18
|
-
"@types/node": "^
|
|
19
|
+
"@types/node": "^26.1.2"
|
|
19
20
|
}
|
|
20
21
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as ts from "typescript/lib/tsserverlibrary";
|
|
2
|
+
import { CodeBlockName } from "../constants";
|
|
3
|
+
|
|
4
|
+
export function getCodeBlockIndizesForBruFile(fileContent: string) {
|
|
5
|
+
const indizes: {
|
|
6
|
+
blockName: string;
|
|
7
|
+
startIndex: number;
|
|
8
|
+
endIndex: number;
|
|
9
|
+
}[] = [];
|
|
10
|
+
|
|
11
|
+
for (const blockName of Object.values(CodeBlockName)) {
|
|
12
|
+
const indizesForBlock = getCodeBlockStartAndEndIndex(
|
|
13
|
+
fileContent,
|
|
14
|
+
blockName,
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
if (indizesForBlock) {
|
|
18
|
+
indizes.push({ blockName, ...indizesForBlock });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return indizes;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function getCodeBlockStartAndEndIndex(
|
|
26
|
+
fullTextContent: string,
|
|
27
|
+
blockName: CodeBlockName,
|
|
28
|
+
) {
|
|
29
|
+
const openingBracketChar = "{";
|
|
30
|
+
|
|
31
|
+
const startPattern = new RegExp(
|
|
32
|
+
`^\\s*${blockName}\\s*${openingBracketChar}\\s*$`,
|
|
33
|
+
"m",
|
|
34
|
+
);
|
|
35
|
+
const startMatches = startPattern.exec(fullTextContent);
|
|
36
|
+
|
|
37
|
+
if (!startMatches || startMatches.length == 0) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const blockStartIndex = startMatches.index;
|
|
42
|
+
const remainingDoc = fullTextContent.substring(blockStartIndex);
|
|
43
|
+
|
|
44
|
+
const sourceFile = ts.createSourceFile(
|
|
45
|
+
"test.js",
|
|
46
|
+
remainingDoc,
|
|
47
|
+
ts.ScriptTarget.ES2020,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const blockNodeInSubDocument = (sourceFile as ts.Node)
|
|
51
|
+
.getChildAt(0, sourceFile)
|
|
52
|
+
.getChildren(sourceFile)
|
|
53
|
+
.find(({ kind }) => kind == ts.SyntaxKind.Block);
|
|
54
|
+
|
|
55
|
+
if (!blockNodeInSubDocument) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`Could not find code block within given subdocument: ${remainingDoc}`,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
startIndex:
|
|
63
|
+
blockStartIndex + startMatches[0].indexOf(openingBracketChar) + 1,
|
|
64
|
+
endIndex: blockStartIndex + blockNodeInSubDocument.end,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import * as ts from "typescript/lib/tsserverlibrary";
|
|
2
|
+
import { getCodeBlockIndizesForBruFile } from "../codeBlocks/bruCodeBlocks";
|
|
3
|
+
import { isFileFromYamlCollection } from "../collection/collectionFormat";
|
|
4
|
+
import { getOriginalFileContent } from "../fileContent";
|
|
5
|
+
import { hasEmbeddedCodeBlocks, isBruFile } from "../fileTypes";
|
|
6
|
+
import { getIndentationOfScripts } from "../yamlFiles/getIndentationOfScripts";
|
|
7
|
+
import { getScriptContentRanges } from "../yamlFiles/maskYamlFileContent";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The Typescript server indents the top-level statements of scripts in Yaml files relative to column 0,
|
|
11
|
+
* since each script is wrapped in a block that is not indented (see `maskYamlCollectionFileContent`).
|
|
12
|
+
* Inserted lines (e.g. when extracting a constant) would therefore end the Yaml block scalar.
|
|
13
|
+
* To avoid this, the base indentation is set to the indentation of the scripts minus one indentation level for the wrapping block.
|
|
14
|
+
*/
|
|
15
|
+
export function getFormatOptionsForFile(
|
|
16
|
+
info: ts.server.PluginCreateInfo,
|
|
17
|
+
fileName: string,
|
|
18
|
+
formatOptions: ts.FormatCodeSettings,
|
|
19
|
+
range: { start: number; end: number },
|
|
20
|
+
): ts.FormatCodeSettings {
|
|
21
|
+
if (!isFileFromYamlCollection(info, fileName)) {
|
|
22
|
+
return formatOptions;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const originalContent = getOriginalFileContent(info, fileName);
|
|
26
|
+
const scriptIndentation = originalContent
|
|
27
|
+
? getIndentationOfScripts(fileName, originalContent, range)
|
|
28
|
+
: undefined;
|
|
29
|
+
const indentSize =
|
|
30
|
+
formatOptions.indentSize ??
|
|
31
|
+
ts.getDefaultFormatCodeSettings().indentSize!;
|
|
32
|
+
|
|
33
|
+
if (scriptIndentation == undefined || scriptIndentation < indentSize) {
|
|
34
|
+
return formatOptions;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
...formatOptions,
|
|
39
|
+
baseIndentSize: scriptIndentation - indentSize,
|
|
40
|
+
// Tabs are not allowed for indenting Yaml content.
|
|
41
|
+
convertTabsToSpaces: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface CodeRange {
|
|
46
|
+
start: number;
|
|
47
|
+
end: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface OriginalFile {
|
|
51
|
+
content: string;
|
|
52
|
+
contentOutsideOfCode: string[];
|
|
53
|
+
getCodeRanges: (fileContent: string) => CodeRange[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Returns a function that checks whether the given changes only modify the code within code blocks, if they affect '.bru' or '.yml' files.
|
|
58
|
+
*
|
|
59
|
+
* The returned function determines the data for the original content of each file only once,
|
|
60
|
+
* since the changes of many code fixes usually affect the same file.
|
|
61
|
+
*/
|
|
62
|
+
export function createCheckForChangesWithinCodeBlocks(
|
|
63
|
+
info: ts.server.PluginCreateInfo,
|
|
64
|
+
) {
|
|
65
|
+
const originalFiles = new Map<string, OriginalFile | undefined>();
|
|
66
|
+
|
|
67
|
+
return (changes: readonly ts.FileTextChanges[]) =>
|
|
68
|
+
changes.every(({ fileName, textChanges, isNewFile }) => {
|
|
69
|
+
if (!hasEmbeddedCodeBlocks(fileName)) {
|
|
70
|
+
return true;
|
|
71
|
+
} else if (isNewFile) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!originalFiles.has(fileName)) {
|
|
76
|
+
originalFiles.set(fileName, getOriginalFile(info, fileName));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const originalFile = originalFiles.get(fileName);
|
|
80
|
+
|
|
81
|
+
if (!originalFile) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const { content, contentOutsideOfCode, getCodeRanges } =
|
|
86
|
+
originalFile;
|
|
87
|
+
const modifiedContent = applyTextChanges(content, textChanges);
|
|
88
|
+
|
|
89
|
+
// Comparing the content outside of code blocks also detects changes that end a code block early,
|
|
90
|
+
// e.g. an inserted line within a Yaml block scalar that is not indented enough.
|
|
91
|
+
const modifiedContentOutsideOfCode = getContentOutsideOfCodeRanges(
|
|
92
|
+
modifiedContent,
|
|
93
|
+
getCodeRanges(modifiedContent),
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
contentOutsideOfCode.length ==
|
|
98
|
+
modifiedContentOutsideOfCode.length &&
|
|
99
|
+
contentOutsideOfCode.every(
|
|
100
|
+
(part, index) =>
|
|
101
|
+
part == modifiedContentOutsideOfCode[index],
|
|
102
|
+
)
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function getOriginalFile(
|
|
108
|
+
info: ts.server.PluginCreateInfo,
|
|
109
|
+
fileName: string,
|
|
110
|
+
): OriginalFile | undefined {
|
|
111
|
+
const content = getOriginalFileContent(info, fileName);
|
|
112
|
+
|
|
113
|
+
if (!content) {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const getCodeRanges = isBruFile(fileName)
|
|
118
|
+
? getCodeRangesForBruFile
|
|
119
|
+
: isFileFromYamlCollection(info, fileName)
|
|
120
|
+
? (fileContent: string) =>
|
|
121
|
+
getScriptContentRanges(fileName, fileContent)
|
|
122
|
+
: () => [];
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
content,
|
|
126
|
+
contentOutsideOfCode: getContentOutsideOfCodeRanges(
|
|
127
|
+
content,
|
|
128
|
+
getCodeRanges(content),
|
|
129
|
+
),
|
|
130
|
+
getCodeRanges,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function getCodeRangesForBruFile(fileContent: string): CodeRange[] {
|
|
135
|
+
return getCodeBlockIndizesForBruFile(fileContent)
|
|
136
|
+
.map(({ startIndex, endIndex }) => ({
|
|
137
|
+
start: startIndex,
|
|
138
|
+
end: endIndex - 1, // index of the closing bracket
|
|
139
|
+
}))
|
|
140
|
+
.sort(({ start: start1 }, { start: start2 }) => start1 - start2);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function getContentOutsideOfCodeRanges(
|
|
144
|
+
fileContent: string,
|
|
145
|
+
codeRanges: CodeRange[],
|
|
146
|
+
) {
|
|
147
|
+
return codeRanges
|
|
148
|
+
.map(({ start }, index) =>
|
|
149
|
+
fileContent.substring(
|
|
150
|
+
index == 0 ? 0 : codeRanges[index - 1].end,
|
|
151
|
+
start,
|
|
152
|
+
),
|
|
153
|
+
)
|
|
154
|
+
.concat(
|
|
155
|
+
fileContent.substring(
|
|
156
|
+
codeRanges.length > 0
|
|
157
|
+
? codeRanges[codeRanges.length - 1].end
|
|
158
|
+
: 0,
|
|
159
|
+
),
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* The text changes for a file do not overlap and their spans refer to the original content.
|
|
165
|
+
*/
|
|
166
|
+
function applyTextChanges(
|
|
167
|
+
fileContent: string,
|
|
168
|
+
textChanges: readonly ts.TextChange[],
|
|
169
|
+
) {
|
|
170
|
+
return [...textChanges]
|
|
171
|
+
.sort(
|
|
172
|
+
({ span: { start: start1 } }, { span: { start: start2 } }) =>
|
|
173
|
+
start2 - start1,
|
|
174
|
+
)
|
|
175
|
+
.reduce(
|
|
176
|
+
(content, { span: { start, length }, newText }) =>
|
|
177
|
+
content.substring(0, start) +
|
|
178
|
+
newText +
|
|
179
|
+
content.substring(start + length),
|
|
180
|
+
fileContent,
|
|
181
|
+
);
|
|
182
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { dirname, extname } from "path";
|
|
2
|
+
import * as ts from "typescript/lib/tsserverlibrary";
|
|
3
|
+
import { CollectionFormat } from "../constants";
|
|
4
|
+
|
|
5
|
+
export function isInABrunoCollection(
|
|
6
|
+
info: ts.server.PluginCreateInfo,
|
|
7
|
+
fileName: string,
|
|
8
|
+
) {
|
|
9
|
+
return getFormatOfContainingCollection(info, fileName) != undefined;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Collection files in the Yaml format always use the extension `.yml`.
|
|
14
|
+
*/
|
|
15
|
+
export function isFileFromYamlCollection(
|
|
16
|
+
info: ts.server.PluginCreateInfo,
|
|
17
|
+
fileName: string,
|
|
18
|
+
) {
|
|
19
|
+
return (
|
|
20
|
+
extname(fileName) == ".yml" &&
|
|
21
|
+
getFormatOfContainingCollection(info, fileName) == CollectionFormat.Yaml
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function getFormatOfContainingCollection(
|
|
26
|
+
info: ts.server.PluginCreateInfo,
|
|
27
|
+
fileName: string,
|
|
28
|
+
) {
|
|
29
|
+
let currentDirectory = dirname(fileName);
|
|
30
|
+
let format = getFormatIfCollectionRootFolder(info, currentDirectory);
|
|
31
|
+
|
|
32
|
+
while (
|
|
33
|
+
info.serverHost.directoryExists(currentDirectory) &&
|
|
34
|
+
!format &&
|
|
35
|
+
dirname(currentDirectory) != currentDirectory // dirname of a base folder in the file system seems to return the input folder again
|
|
36
|
+
) {
|
|
37
|
+
currentDirectory = dirname(currentDirectory);
|
|
38
|
+
format = getFormatIfCollectionRootFolder(info, currentDirectory);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return format;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function getFormatIfCollectionRootFolder(
|
|
45
|
+
info: ts.server.PluginCreateInfo,
|
|
46
|
+
directoryPath: string,
|
|
47
|
+
) {
|
|
48
|
+
const collectionRootFile = info.serverHost
|
|
49
|
+
.readDirectory(directoryPath, undefined, undefined, undefined, 1)
|
|
50
|
+
.find((fileName) => {
|
|
51
|
+
const isBruCollectionRootFile = fileName.endsWith("bruno.json");
|
|
52
|
+
const isYamlCollectionRootFile =
|
|
53
|
+
fileName.endsWith("opencollection.yml");
|
|
54
|
+
|
|
55
|
+
if (!isBruCollectionRootFile && !isYamlCollectionRootFile) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const content = info.languageServiceHost.readFile(fileName);
|
|
60
|
+
|
|
61
|
+
if (!content) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return isBruCollectionRootFile
|
|
66
|
+
? isBrunoJsonForCollection(content)
|
|
67
|
+
: isOpenCollectionRootFileContent(content);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (!collectionRootFile) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return collectionRootFile.endsWith("bruno.json")
|
|
75
|
+
? CollectionFormat.Bru
|
|
76
|
+
: CollectionFormat.Yaml;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function isBrunoJsonForCollection(content: string) {
|
|
80
|
+
try {
|
|
81
|
+
const parsedContent = JSON.parse(content) as
|
|
82
|
+
{ type: string } | undefined;
|
|
83
|
+
|
|
84
|
+
return parsedContent && parsedContent.type == "collection";
|
|
85
|
+
} catch {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Request, folder and environment files can be named `opencollection.yml` as well.
|
|
92
|
+
* Only the actual collection root file defines the top-level key `opencollection`.
|
|
93
|
+
*/
|
|
94
|
+
function isOpenCollectionRootFileContent(content: string) {
|
|
95
|
+
return /^opencollection\s*:/m.test(content);
|
|
96
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { describe, it, expect } from "@jest/globals";
|
|
2
|
+
import { ScriptType as ScriptTypeFromExtension } from "../../shared/fileParsing/external/yamlFormat/constants/sharedConstants";
|
|
3
|
+
import { ScriptType } from "./constants";
|
|
4
|
+
|
|
5
|
+
describe("ScriptType", () => {
|
|
6
|
+
// The plugin is published as a separate package, so it cannot import the constants from the extension.
|
|
7
|
+
it("is equal to the script types known to the extension", () => {
|
|
8
|
+
expect(Object.values(ScriptType)).toEqual(
|
|
9
|
+
Object.values(ScriptTypeFromExtension),
|
|
10
|
+
);
|
|
11
|
+
});
|
|
12
|
+
});
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export enum CollectionFormat {
|
|
2
|
+
Bru = "bru",
|
|
3
|
+
Yaml = "yaml",
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export enum CodeBlockName {
|
|
7
|
+
Tests = "tests",
|
|
8
|
+
PreRequestScript = "script:pre-request",
|
|
9
|
+
PostResponseScript = "script:post-response",
|
|
10
|
+
GrpcBeforeCallStartScript = "script:grpc:before-call-start",
|
|
11
|
+
GrpcBeforeMessageSendScript = "script:grpc:before-message-send",
|
|
12
|
+
GrpcAfterMessageReceiveScript = "script:grpc:after-message-receive",
|
|
13
|
+
GrpcAfterCallEndScript = "script:grpc:after-call-end",
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getTempJsFileBaseNameWithoutExtension() {
|
|
17
|
+
return "__temp_bru_reference";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getNamesForInbuiltRuntimeVarsAndFunctions() {
|
|
21
|
+
return ["bru", "req", "res", "test", "expect"];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Same as `ScriptType` for Yaml files in the shared folder of the extension.
|
|
26
|
+
*/
|
|
27
|
+
export enum ScriptType {
|
|
28
|
+
BeforeRequest = "before-request",
|
|
29
|
+
AfterResponse = "after-response",
|
|
30
|
+
Tests = "tests",
|
|
31
|
+
}
|