tsifdef 1.1.8
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/CHANGELOG.md +25 -0
- package/CHANGELOG.zh-CN.md +22 -0
- package/CONTRIBUTING.md +52 -0
- package/INTEGRATION.md +166 -0
- package/INTEGRATION.zh-CN.md +159 -0
- package/LICENSE +210 -0
- package/README.md +150 -0
- package/README.zh-CN.md +141 -0
- package/SECURITY.md +15 -0
- package/assets/icon-mono.svg +15 -0
- package/assets/icon.png +0 -0
- package/assets/icon.svg +16 -0
- package/dist/cli/build.d.ts +70 -0
- package/dist/cli/build.js +298 -0
- package/dist/cli/config.d.ts +30 -0
- package/dist/cli/config.js +135 -0
- package/dist/cli/diagnostics.d.ts +3 -0
- package/dist/cli/diagnostics.js +31 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +35 -0
- package/dist/cli/main.d.ts +9 -0
- package/dist/cli/main.js +165 -0
- package/dist/cli/precompile.d.ts +38 -0
- package/dist/cli/precompile.js +192 -0
- package/dist/cli/source-files.d.ts +10 -0
- package/dist/cli/source-files.js +75 -0
- package/dist/cli/watch.d.ts +34 -0
- package/dist/cli/watch.js +183 -0
- package/dist/core/conditional.d.ts +18 -0
- package/dist/core/conditional.js +126 -0
- package/dist/core/expression.d.ts +44 -0
- package/dist/core/expression.js +224 -0
- package/dist/core/index.d.ts +7 -0
- package/dist/core/index.js +37 -0
- package/dist/core/projection.d.ts +15 -0
- package/dist/core/projection.js +99 -0
- package/dist/core/scanner.d.ts +33 -0
- package/dist/core/scanner.js +344 -0
- package/dist/eslint/package.d.ts +10 -0
- package/dist/eslint/package.js +55 -0
- package/dist/eslint/parser.d.ts +35 -0
- package/dist/eslint/parser.js +59 -0
- package/dist/eslint/plugin.d.ts +78 -0
- package/dist/eslint/plugin.js +229 -0
- package/dist/eslint/projection.d.ts +11 -0
- package/dist/eslint/projection.js +116 -0
- package/dist/tsserver/host-projection.d.ts +45 -0
- package/dist/tsserver/host-projection.js +160 -0
- package/dist/tsserver/index.d.ts +3 -0
- package/dist/tsserver/index.js +32 -0
- package/dist/tsserver/plugin.d.ts +15 -0
- package/dist/tsserver/plugin.js +129 -0
- package/dist/tsserver/project-controller.d.ts +37 -0
- package/dist/tsserver/project-controller.js +60 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.js +20 -0
- package/dist/vscode/document-analysis.d.ts +62 -0
- package/dist/vscode/document-analysis.js +111 -0
- package/dist/vscode/extension.d.ts +133 -0
- package/dist/vscode/extension.js +241 -0
- package/dist/vscode/host.d.ts +79 -0
- package/dist/vscode/host.js +16 -0
- package/dist/vscode/index.d.ts +7 -0
- package/dist/vscode/index.js +36 -0
- package/dist/vscode/macro-presentation.d.ts +43 -0
- package/dist/vscode/macro-presentation.js +130 -0
- package/dist/vscode/package-profile.d.ts +27 -0
- package/dist/vscode/package-profile.js +142 -0
- package/dist/vscode/profile-state.d.ts +22 -0
- package/dist/vscode/profile-state.js +81 -0
- package/package.json +129 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type * as ts from "typescript";
|
|
2
|
+
/**
|
|
3
|
+
* tsserver plugin entry. tsserver calls this with the live `typescript` module
|
|
4
|
+
* and, per project, invokes `create(info)` to let the plugin wrap the language
|
|
5
|
+
* service host.
|
|
6
|
+
*
|
|
7
|
+
* The plugin resolves one externally selected, read-only Profile and projects
|
|
8
|
+
* every macro file's snapshot through the shared core. It returns the original
|
|
9
|
+
* language service unchanged; only the host's view of the source is altered.
|
|
10
|
+
*/
|
|
11
|
+
declare function init(modules: {
|
|
12
|
+
typescript: typeof ts;
|
|
13
|
+
}): ts.server.PluginModule;
|
|
14
|
+
export = init;
|
|
15
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (C) 2026 Tencent. All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// you may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
//
|
|
8
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
//
|
|
10
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
// See the License for the specific language governing permissions and
|
|
14
|
+
// limitations under the License.
|
|
15
|
+
const node_fs_1 = require("node:fs");
|
|
16
|
+
const node_path_1 = require("node:path");
|
|
17
|
+
const config_js_1 = require("../cli/config.js");
|
|
18
|
+
const host_projection_js_1 = require("./host-projection.js");
|
|
19
|
+
const project_controller_js_1 = require("./project-controller.js");
|
|
20
|
+
/**
|
|
21
|
+
* tsserver plugin entry. tsserver calls this with the live `typescript` module
|
|
22
|
+
* and, per project, invokes `create(info)` to let the plugin wrap the language
|
|
23
|
+
* service host.
|
|
24
|
+
*
|
|
25
|
+
* The plugin resolves one externally selected, read-only Profile and projects
|
|
26
|
+
* every macro file's snapshot through the shared core. It returns the original
|
|
27
|
+
* language service unchanged; only the host's view of the source is altered.
|
|
28
|
+
*/
|
|
29
|
+
function init(modules) {
|
|
30
|
+
const typescript = modules.typescript;
|
|
31
|
+
let externalConfig = {};
|
|
32
|
+
const reloaders = new Set();
|
|
33
|
+
return {
|
|
34
|
+
create(info) {
|
|
35
|
+
const projectConfig = (info.config ?? {});
|
|
36
|
+
const config = () => ({ ...projectConfig, ...externalConfig });
|
|
37
|
+
const profilePath = () => resolveProfilePath(config(), info);
|
|
38
|
+
const log = (message) => info.project.projectService.logger.info(message);
|
|
39
|
+
const controller = new project_controller_js_1.ProfileProjectionController({
|
|
40
|
+
resolve: () => resolveProfile(profilePath(), log),
|
|
41
|
+
log,
|
|
42
|
+
});
|
|
43
|
+
const reloader = () => {
|
|
44
|
+
controller.reload();
|
|
45
|
+
};
|
|
46
|
+
reloaders.add(reloader);
|
|
47
|
+
(0, host_projection_js_1.wrapHostWithProjection)(typescript, info.languageServiceHost, {
|
|
48
|
+
getProfile: () => controller.getProfile(),
|
|
49
|
+
});
|
|
50
|
+
// Keep the in-process Profile current. The VSCode extension reloads
|
|
51
|
+
// TypeScript projects after a semantic Profile change.
|
|
52
|
+
const selectedPath = profilePath();
|
|
53
|
+
const configWatcher = selectedPath === undefined
|
|
54
|
+
? undefined
|
|
55
|
+
: watchConfigDirectory(info, selectedPath, reloader);
|
|
56
|
+
// Drop this project's reloader when its language service is disposed so
|
|
57
|
+
// stale controllers/watchers do not accumulate and reload on every change.
|
|
58
|
+
const languageService = info.languageService;
|
|
59
|
+
const originalDispose = languageService.dispose.bind(languageService);
|
|
60
|
+
languageService.dispose = () => {
|
|
61
|
+
reloaders.delete(reloader);
|
|
62
|
+
configWatcher?.close();
|
|
63
|
+
originalDispose();
|
|
64
|
+
};
|
|
65
|
+
return languageService;
|
|
66
|
+
},
|
|
67
|
+
onConfigurationChanged(config) {
|
|
68
|
+
externalConfig = config ?? {};
|
|
69
|
+
for (const reload of reloaders) {
|
|
70
|
+
reload();
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function resolveProfilePath(config, info) {
|
|
76
|
+
const projectName = info.project.getProjectName();
|
|
77
|
+
const projectRoot = projectName ? (0, node_path_1.dirname)(projectName) : info.project.getCurrentDirectory();
|
|
78
|
+
const selected = config.profileFile;
|
|
79
|
+
if (selected === undefined || selected.trim() === "")
|
|
80
|
+
return undefined;
|
|
81
|
+
return (0, node_path_1.isAbsolute)(selected) ? selected : (0, node_path_1.join)(projectRoot, selected);
|
|
82
|
+
}
|
|
83
|
+
function resolveProfile(profilePath, log) {
|
|
84
|
+
if (profilePath === undefined)
|
|
85
|
+
return { kind: "none" };
|
|
86
|
+
let text;
|
|
87
|
+
try {
|
|
88
|
+
text = (0, node_fs_1.readFileSync)(profilePath, "utf8");
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// The file could not be read this instant. An atomic writer (temp + rename)
|
|
92
|
+
// briefly hides it; treat this as transient so the controller keeps the
|
|
93
|
+
// current Profile instead of flapping to none and triggering a reload storm.
|
|
94
|
+
return { kind: "unavailable" };
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const definitions = (0, config_js_1.parseProfileFile)(text, profilePath).definitions;
|
|
98
|
+
// The version ties the AST cache to the Profile name and its content, so
|
|
99
|
+
// editing or switching the Profile produces a new version and invalidation.
|
|
100
|
+
return { kind: "profile", profile: { definitions, version: `${profilePath}:${text.length}:${hashText(text)}` } };
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
// A parse failure during an external rewrite is almost always a half-written
|
|
104
|
+
// file (the writer has not finished / renamed yet). Treat it as transient and
|
|
105
|
+
// keep the current Profile rather than flapping to none; the next watcher
|
|
106
|
+
// event re-reads the completed file. A genuinely malformed Profile simply
|
|
107
|
+
// keeps the last good one until it is fixed, which is the safe behavior.
|
|
108
|
+
log(`[tsifdef] Profile '${profilePath}' not parseable this read (likely mid-write); keeping current: ${error instanceof Error ? error.message : String(error)}`);
|
|
109
|
+
return { kind: "unavailable" };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function watchConfigDirectory(info, configPath, onChange) {
|
|
113
|
+
const serverHost = info.serverHost;
|
|
114
|
+
if (typeof serverHost.watchDirectory !== "function") {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
return serverHost.watchDirectory((0, node_path_1.dirname)(configPath), () => onChange(), /* recursive */ false);
|
|
118
|
+
}
|
|
119
|
+
/** Small deterministic content hash; only used for AST-cache versioning. */
|
|
120
|
+
function hashText(text) {
|
|
121
|
+
let hash = 2166136261;
|
|
122
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
123
|
+
hash ^= text.charCodeAt(index);
|
|
124
|
+
hash = Math.imul(hash, 16777619);
|
|
125
|
+
}
|
|
126
|
+
return (hash >>> 0).toString(16);
|
|
127
|
+
}
|
|
128
|
+
module.exports = init;
|
|
129
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ActiveProfile } from "./host-projection.js";
|
|
2
|
+
/** Outcome of resolving the Profile: a value, none configured, or a transient failure. */
|
|
3
|
+
export type ProfileResolution = {
|
|
4
|
+
readonly kind: "profile";
|
|
5
|
+
readonly profile: ActiveProfile;
|
|
6
|
+
} | {
|
|
7
|
+
readonly kind: "none";
|
|
8
|
+
} | {
|
|
9
|
+
readonly kind: "unavailable";
|
|
10
|
+
};
|
|
11
|
+
/** Hooks used to resolve and log Profile changes. */
|
|
12
|
+
export interface ProjectControllerHooks {
|
|
13
|
+
/**
|
|
14
|
+
* Re-resolve the active Profile. Return `{kind:"profile"}` with the loaded
|
|
15
|
+
* Profile, `{kind:"none"}` when no Profile is configured, or
|
|
16
|
+
* `{kind:"unavailable"}` when the file could not be read this instant (an
|
|
17
|
+
* atomic write may briefly hide it). On `unavailable` the controller keeps the
|
|
18
|
+
* current Profile rather than flapping to none.
|
|
19
|
+
*/
|
|
20
|
+
resolve: () => ProfileResolution;
|
|
21
|
+
log?: (message: string) => void;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Owns the current Profile for one project.
|
|
25
|
+
*/
|
|
26
|
+
export declare class ProfileProjectionController {
|
|
27
|
+
private readonly hooks;
|
|
28
|
+
private current;
|
|
29
|
+
constructor(hooks: ProjectControllerHooks);
|
|
30
|
+
/** The active Profile read live by the host wrapper. */
|
|
31
|
+
getProfile(): ActiveProfile | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Re-resolve the Profile. A transient read failure keeps the current value.
|
|
34
|
+
*/
|
|
35
|
+
reload(): boolean;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=project-controller.d.ts.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (C) 2026 Tencent. All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// you may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
//
|
|
8
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
//
|
|
10
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
// See the License for the specific language governing permissions and
|
|
14
|
+
// limitations under the License.
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.ProfileProjectionController = void 0;
|
|
17
|
+
/**
|
|
18
|
+
* Owns the current Profile for one project.
|
|
19
|
+
*/
|
|
20
|
+
class ProfileProjectionController {
|
|
21
|
+
hooks;
|
|
22
|
+
current;
|
|
23
|
+
constructor(hooks) {
|
|
24
|
+
this.hooks = hooks;
|
|
25
|
+
const initial = hooks.resolve();
|
|
26
|
+
this.current = initial.kind === "profile" ? initial.profile : undefined;
|
|
27
|
+
this.hooks.log?.(`[tsifdef] initial profile ${describe(this.current)}.`);
|
|
28
|
+
}
|
|
29
|
+
/** The active Profile read live by the host wrapper. */
|
|
30
|
+
getProfile() {
|
|
31
|
+
return this.current;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Re-resolve the Profile. A transient read failure keeps the current value.
|
|
35
|
+
*/
|
|
36
|
+
reload() {
|
|
37
|
+
const resolution = this.hooks.resolve();
|
|
38
|
+
if (resolution.kind === "unavailable") {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
const next = resolution.kind === "profile" ? resolution.profile : undefined;
|
|
42
|
+
if (sameProfile(this.current, next)) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
this.hooks.log?.(`[tsifdef] profile changed ${describe(this.current)} -> ${describe(next)}.`);
|
|
46
|
+
this.current = next;
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.ProfileProjectionController = ProfileProjectionController;
|
|
51
|
+
function sameProfile(left, right) {
|
|
52
|
+
if (left === undefined || right === undefined) {
|
|
53
|
+
return left === right;
|
|
54
|
+
}
|
|
55
|
+
return left.version === right.version;
|
|
56
|
+
}
|
|
57
|
+
function describe(profile) {
|
|
58
|
+
return profile === undefined ? "(none)" : `'${profile.version}'`;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=project-controller.js.map
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (C) 2026 Tencent. All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// you may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
//
|
|
8
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
//
|
|
10
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
// See the License for the specific language governing permissions and
|
|
14
|
+
// limitations under the License.
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.VERSION_TAG = exports.VERSION = void 0;
|
|
17
|
+
// Generated by scripts/sync-version.cjs from package.json. Do not edit.
|
|
18
|
+
exports.VERSION = "1.1.8";
|
|
19
|
+
exports.VERSION_TAG = `v${exports.VERSION}`;
|
|
20
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { type MacroDiagnostic } from "../core/index.js";
|
|
2
|
+
import type { MacroDefinitions } from "../core/expression.js";
|
|
3
|
+
import type { SourceRange } from "../core/scanner.js";
|
|
4
|
+
/** Zero-based editor position. `character` counts UTF-16 code units, like VSCode. */
|
|
5
|
+
export interface DocumentPosition {
|
|
6
|
+
readonly line: number;
|
|
7
|
+
readonly character: number;
|
|
8
|
+
}
|
|
9
|
+
export interface DocumentRange {
|
|
10
|
+
readonly start: DocumentPosition;
|
|
11
|
+
readonly end: DocumentPosition;
|
|
12
|
+
}
|
|
13
|
+
/** Severity levels mirroring `vscode.DiagnosticSeverity` ordinals. */
|
|
14
|
+
export declare const DiagnosticSeverity: {
|
|
15
|
+
readonly error: 0;
|
|
16
|
+
readonly warning: 1;
|
|
17
|
+
};
|
|
18
|
+
export type DiagnosticSeverityValue = (typeof DiagnosticSeverity)[keyof typeof DiagnosticSeverity];
|
|
19
|
+
export interface DocumentDiagnostic {
|
|
20
|
+
readonly code: MacroDiagnostic["code"];
|
|
21
|
+
readonly message: string;
|
|
22
|
+
readonly range: DocumentRange;
|
|
23
|
+
readonly severity: DiagnosticSeverityValue;
|
|
24
|
+
}
|
|
25
|
+
export interface DocumentAnalysis {
|
|
26
|
+
readonly diagnostics: readonly DocumentDiagnostic[];
|
|
27
|
+
readonly inactiveRanges: readonly DocumentRange[];
|
|
28
|
+
}
|
|
29
|
+
/** Inclusive, zero-based line range to collapse, mirroring `vscode.FoldingRange`. */
|
|
30
|
+
export interface FoldingRange {
|
|
31
|
+
readonly start: number;
|
|
32
|
+
readonly end: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Map UTF-16 source offsets onto zero-based editor positions.
|
|
36
|
+
*
|
|
37
|
+
* `\r\n`, lone `\r`, and lone `\n` each advance one line, matching how VSCode
|
|
38
|
+
* counts lines. `character` is a UTF-16 code-unit count, so surrogate pairs are
|
|
39
|
+
* preserved exactly as TypeScript and VSCode positions expect.
|
|
40
|
+
*/
|
|
41
|
+
export declare class PositionMapper {
|
|
42
|
+
private readonly source;
|
|
43
|
+
private readonly lineStarts;
|
|
44
|
+
constructor(source: string);
|
|
45
|
+
positionAt(offset: number): DocumentPosition;
|
|
46
|
+
rangeOf(range: SourceRange): DocumentRange;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Analyze one document's source for the selected Profile, reusing the shared
|
|
50
|
+
* core. Returns editor-ready diagnostics and inactive ranges without scanning,
|
|
51
|
+
* evaluating, or projecting macros independently.
|
|
52
|
+
*/
|
|
53
|
+
export declare function analyzeDocument(source: string, definitions: MacroDefinitions): DocumentAnalysis;
|
|
54
|
+
/**
|
|
55
|
+
* Compute foldable regions for inactive code in the selected Profile.
|
|
56
|
+
*
|
|
57
|
+
* Folds are inclusive zero-based line ranges. A range that begins and ends on
|
|
58
|
+
* the same editor line spans nothing foldable and is omitted, matching how
|
|
59
|
+
* VSCode treats single-line folds.
|
|
60
|
+
*/
|
|
61
|
+
export declare function computeFoldingRanges(source: string, definitions: MacroDefinitions): readonly FoldingRange[];
|
|
62
|
+
//# sourceMappingURL=document-analysis.d.ts.map
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (C) 2026 Tencent. All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// you may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
//
|
|
8
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
//
|
|
10
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
// See the License for the specific language governing permissions and
|
|
14
|
+
// limitations under the License.
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.PositionMapper = exports.DiagnosticSeverity = void 0;
|
|
17
|
+
exports.analyzeDocument = analyzeDocument;
|
|
18
|
+
exports.computeFoldingRanges = computeFoldingRanges;
|
|
19
|
+
const index_js_1 = require("../core/index.js");
|
|
20
|
+
/** Severity levels mirroring `vscode.DiagnosticSeverity` ordinals. */
|
|
21
|
+
exports.DiagnosticSeverity = {
|
|
22
|
+
error: 0,
|
|
23
|
+
warning: 1,
|
|
24
|
+
};
|
|
25
|
+
// Every current macro diagnostic is a hard structural or semantic error.
|
|
26
|
+
const warningCodes = new Set();
|
|
27
|
+
/**
|
|
28
|
+
* Map UTF-16 source offsets onto zero-based editor positions.
|
|
29
|
+
*
|
|
30
|
+
* `\r\n`, lone `\r`, and lone `\n` each advance one line, matching how VSCode
|
|
31
|
+
* counts lines. `character` is a UTF-16 code-unit count, so surrogate pairs are
|
|
32
|
+
* preserved exactly as TypeScript and VSCode positions expect.
|
|
33
|
+
*/
|
|
34
|
+
class PositionMapper {
|
|
35
|
+
source;
|
|
36
|
+
lineStarts = [0];
|
|
37
|
+
constructor(source) {
|
|
38
|
+
this.source = source;
|
|
39
|
+
for (let offset = 0; offset < source.length; offset += 1) {
|
|
40
|
+
const code = source.charCodeAt(offset);
|
|
41
|
+
if (code === 0x0d /* \r */) {
|
|
42
|
+
if (source.charCodeAt(offset + 1) === 0x0a /* \n */) {
|
|
43
|
+
offset += 1;
|
|
44
|
+
}
|
|
45
|
+
this.lineStarts.push(offset + 1);
|
|
46
|
+
}
|
|
47
|
+
else if (code === 0x0a /* \n */) {
|
|
48
|
+
this.lineStarts.push(offset + 1);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
positionAt(offset) {
|
|
53
|
+
const clamped = Math.max(0, Math.min(offset, this.source.length));
|
|
54
|
+
let low = 0;
|
|
55
|
+
let high = this.lineStarts.length - 1;
|
|
56
|
+
while (low < high) {
|
|
57
|
+
const mid = (low + high + 1) >> 1;
|
|
58
|
+
if (this.lineStarts[mid] <= clamped) {
|
|
59
|
+
low = mid;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
high = mid - 1;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return { line: low, character: clamped - this.lineStarts[low] };
|
|
66
|
+
}
|
|
67
|
+
rangeOf(range) {
|
|
68
|
+
return { start: this.positionAt(range.start), end: this.positionAt(range.end) };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.PositionMapper = PositionMapper;
|
|
72
|
+
/**
|
|
73
|
+
* Analyze one document's source for the selected Profile, reusing the shared
|
|
74
|
+
* core. Returns editor-ready diagnostics and inactive ranges without scanning,
|
|
75
|
+
* evaluating, or projecting macros independently.
|
|
76
|
+
*/
|
|
77
|
+
function analyzeDocument(source, definitions) {
|
|
78
|
+
const analysis = (0, index_js_1.analyzeConditionals)(source, definitions);
|
|
79
|
+
const mapper = new PositionMapper(source);
|
|
80
|
+
return {
|
|
81
|
+
diagnostics: analysis.diagnostics.map((diagnostic) => ({
|
|
82
|
+
code: diagnostic.code,
|
|
83
|
+
message: diagnostic.message,
|
|
84
|
+
range: mapper.rangeOf(diagnostic.range),
|
|
85
|
+
severity: warningCodes.has(diagnostic.code)
|
|
86
|
+
? exports.DiagnosticSeverity.warning
|
|
87
|
+
: exports.DiagnosticSeverity.error,
|
|
88
|
+
})),
|
|
89
|
+
inactiveRanges: analysis.inactiveRanges.map((range) => mapper.rangeOf(range)),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Compute foldable regions for inactive code in the selected Profile.
|
|
94
|
+
*
|
|
95
|
+
* Folds are inclusive zero-based line ranges. A range that begins and ends on
|
|
96
|
+
* the same editor line spans nothing foldable and is omitted, matching how
|
|
97
|
+
* VSCode treats single-line folds.
|
|
98
|
+
*/
|
|
99
|
+
function computeFoldingRanges(source, definitions) {
|
|
100
|
+
const folds = [];
|
|
101
|
+
for (const range of analyzeDocument(source, definitions).inactiveRanges) {
|
|
102
|
+
// An inactive range that ends at the start of a later line folds up to the
|
|
103
|
+
// last line it actually covers, never pulling in the following active line.
|
|
104
|
+
const end = range.end.character === 0 ? range.end.line - 1 : range.end.line;
|
|
105
|
+
if (end > range.start.line) {
|
|
106
|
+
folds.push({ start: range.start.line, end });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return folds;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=document-analysis.js.map
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { Disposable, ExtensionHost, StatusBarItem } from "./host.js";
|
|
2
|
+
/**
|
|
3
|
+
* The slice of the real `vscode` module the activation adapter consumes. It is
|
|
4
|
+
* declared locally so the package builds without `@types/vscode`; the module is
|
|
5
|
+
* resolved at runtime inside the extension host, never during CI or tests.
|
|
6
|
+
*/
|
|
7
|
+
interface VsRange {
|
|
8
|
+
new (startLine: number, startCharacter: number, endLine: number, endCharacter: number): unknown;
|
|
9
|
+
}
|
|
10
|
+
interface VsFoldingRange {
|
|
11
|
+
new (start: number, end: number): unknown;
|
|
12
|
+
}
|
|
13
|
+
interface VsUri {
|
|
14
|
+
parse(value: string): unknown;
|
|
15
|
+
}
|
|
16
|
+
interface VsTextDocument {
|
|
17
|
+
readonly uri: {
|
|
18
|
+
toString(): string;
|
|
19
|
+
};
|
|
20
|
+
readonly languageId: string;
|
|
21
|
+
readonly fileName: string;
|
|
22
|
+
getText(): string;
|
|
23
|
+
}
|
|
24
|
+
interface VsFoldingProvider {
|
|
25
|
+
provideFoldingRanges(document: VsTextDocument): readonly unknown[];
|
|
26
|
+
}
|
|
27
|
+
interface VsTextEditor {
|
|
28
|
+
readonly document: VsTextDocument;
|
|
29
|
+
setDecorations(decorationType: unknown, ranges: readonly unknown[]): void;
|
|
30
|
+
}
|
|
31
|
+
interface VsEvent<T> {
|
|
32
|
+
(listener: (event: T) => unknown): Disposable;
|
|
33
|
+
}
|
|
34
|
+
interface VsCodeApi {
|
|
35
|
+
readonly StatusBarAlignment: {
|
|
36
|
+
readonly Left: number;
|
|
37
|
+
};
|
|
38
|
+
readonly DiagnosticSeverity: {
|
|
39
|
+
readonly Error: number;
|
|
40
|
+
readonly Warning: number;
|
|
41
|
+
};
|
|
42
|
+
readonly Range: VsRange;
|
|
43
|
+
readonly FoldingRange: VsFoldingRange;
|
|
44
|
+
readonly Uri: VsUri;
|
|
45
|
+
Diagnostic: new (range: unknown, message: string, severity?: number) => {
|
|
46
|
+
code?: unknown;
|
|
47
|
+
};
|
|
48
|
+
readonly window: {
|
|
49
|
+
createStatusBarItem(alignment: number, priority?: number): StatusBarItem;
|
|
50
|
+
createTextEditorDecorationType(options: unknown): {
|
|
51
|
+
dispose(): void;
|
|
52
|
+
};
|
|
53
|
+
showInformationMessage(message: string): PromiseLike<unknown>;
|
|
54
|
+
showErrorMessage(message: string): PromiseLike<unknown>;
|
|
55
|
+
readonly visibleTextEditors: readonly VsTextEditor[];
|
|
56
|
+
readonly onDidChangeVisibleTextEditors: VsEvent<readonly VsTextEditor[]>;
|
|
57
|
+
};
|
|
58
|
+
readonly workspace: {
|
|
59
|
+
readonly workspaceFolders?: ReadonlyArray<{
|
|
60
|
+
readonly uri: {
|
|
61
|
+
readonly fsPath: string;
|
|
62
|
+
};
|
|
63
|
+
}>;
|
|
64
|
+
readonly textDocuments: readonly VsTextDocument[];
|
|
65
|
+
readonly onDidChangeConfiguration: VsEvent<{
|
|
66
|
+
affectsConfiguration(section: string): boolean;
|
|
67
|
+
}>;
|
|
68
|
+
readonly onDidChangeTextDocument: VsEvent<{
|
|
69
|
+
readonly document: VsTextDocument;
|
|
70
|
+
}>;
|
|
71
|
+
readonly onDidOpenTextDocument: VsEvent<VsTextDocument>;
|
|
72
|
+
readonly onDidCloseTextDocument: VsEvent<VsTextDocument>;
|
|
73
|
+
createFileSystemWatcher(glob: string): {
|
|
74
|
+
onDidChange: VsEvent<unknown>;
|
|
75
|
+
onDidCreate: VsEvent<unknown>;
|
|
76
|
+
onDidDelete: VsEvent<unknown>;
|
|
77
|
+
dispose(): void;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
readonly languages: {
|
|
81
|
+
createDiagnosticCollection(name: string): {
|
|
82
|
+
set(uri: unknown, diagnostics: readonly unknown[]): void;
|
|
83
|
+
delete(uri: unknown): void;
|
|
84
|
+
clear(): void;
|
|
85
|
+
dispose(): void;
|
|
86
|
+
};
|
|
87
|
+
registerFoldingRangeProvider(selector: unknown, provider: VsFoldingProvider): Disposable;
|
|
88
|
+
};
|
|
89
|
+
readonly commands: {
|
|
90
|
+
registerCommand(command: string, handler: () => unknown | Promise<unknown>): Disposable;
|
|
91
|
+
executeCommand(command: string, ...args: readonly unknown[]): PromiseLike<unknown>;
|
|
92
|
+
};
|
|
93
|
+
readonly extensions: {
|
|
94
|
+
getExtension(id: string): {
|
|
95
|
+
readonly exports: unknown;
|
|
96
|
+
activate(): PromiseLike<unknown>;
|
|
97
|
+
} | undefined;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/** The shape VSCode passes to `activate`; only `subscriptions` is required here. */
|
|
101
|
+
interface ExtensionContext {
|
|
102
|
+
readonly subscriptions: Disposable[];
|
|
103
|
+
}
|
|
104
|
+
/** Optional bridge to VSCode's built-in TypeScript language service. */
|
|
105
|
+
export declare function createTypeScriptIntegration(vscode: {
|
|
106
|
+
readonly commands: {
|
|
107
|
+
executeCommand(command: string, ...args: readonly unknown[]): PromiseLike<unknown>;
|
|
108
|
+
};
|
|
109
|
+
readonly extensions: VsCodeApi["extensions"];
|
|
110
|
+
}): {
|
|
111
|
+
configurePlugin(name: string, configuration: Readonly<Record<string, unknown>>): Promise<void>;
|
|
112
|
+
restartServer(): Promise<void>;
|
|
113
|
+
reloadProjects(): Promise<void>;
|
|
114
|
+
};
|
|
115
|
+
/** Adapt the real `vscode` API to the host interface the shell depends on. */
|
|
116
|
+
export declare function createHost(vscode: VsCodeApi): ExtensionHost;
|
|
117
|
+
/**
|
|
118
|
+
* Ensure the tsserver plugin is resolvable as `node_modules/tsifdef-tsserver`.
|
|
119
|
+
*
|
|
120
|
+
* tsserver loads a workspace plugin by resolving the module named in
|
|
121
|
+
* `typescriptServerPlugins` from the extension directory. vsce does not ship a
|
|
122
|
+
* `node_modules/` tree, so the compiled plugin would otherwise be unreachable
|
|
123
|
+
* and every `#if` would reach the parser as a syntax error. Recreate the tiny
|
|
124
|
+
* forwarder on activation so a fresh install (or a `--force` reinstall that
|
|
125
|
+
* wiped a previous shim) self-heals without manual steps.
|
|
126
|
+
*/
|
|
127
|
+
export declare function ensureTsserverPluginModule(): void;
|
|
128
|
+
/** VSCode entry point. Binds the live API to the host and starts the shell. */
|
|
129
|
+
export declare function activate(context: ExtensionContext): void;
|
|
130
|
+
/** VSCode shutdown hook. */
|
|
131
|
+
export declare function deactivate(): void;
|
|
132
|
+
export {};
|
|
133
|
+
//# sourceMappingURL=extension.d.ts.map
|