Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/API-Reference/language/CodeInspection.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ Each error object in the results should have the following structure:
type:?Type ,
fix: { // an optional fix, if present will show the fix button
replaceText: "text to replace the offset given below",
title: "optional tooltip describing what the fix does",
rangeOffset: {
start: number,
end: number
Expand Down Expand Up @@ -195,6 +196,7 @@ Each error object in the results should have the following structure:
| type | [<code>Type</code>](#Type) | The type of the error. Defaults to `Type.WARNING` if unspecified. |
| fix | <code>Object</code> | An optional fix object. |
| fix.replaceText | <code>string</code> | The text to replace the error with. |
| fix.title | <code>string</code> | Optional tooltip on the Fix button describing what the fix does. |
| fix.rangeOffset | <code>Object</code> | The range within the text to replace. |
| fix.rangeOffset.start | <code>number</code> | The start offset of the range. |
| fix.rangeOffset.end | <code>number</code> | The end offset of the range. If no errors are found, return either `null`(treated as file is problem free) or an object with a zero-length `errors` array. Always use `message` to safely display the error as text. If you want to display HTML error message, then explicitly use `htmlMessage` to display it. Both `message` and `htmlMessage` can be used simultaneously. After scanning the file, if you need to omit the lint result, return or resolve with `{isIgnored: true}`. This prevents the file from being marked with a no errors tick mark in the status bar and excludes the linter from the problems panel. |
Expand Down
66 changes: 66 additions & 0 deletions docs/API-Reference/utils/NodeUtils.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,72 @@ This is only available in the native app.
| url | <code>string</code> |
| browserName | <code>string</code> |

<a name="downloadFile"></a>

## downloadFile(url, destFile, [options]) ⇒ <code>Promise.&lt;void&gt;</code>
Downloads a URL to a file on disk, fully node-side (native fetch, streamed to disk).
When an expected sha256 is given, a mismatch deletes the file and rejects - a resolved
promise means the file holds exactly the pinned bytes.
This is only available in the native app.

**Kind**: global function

| Param | Type | Description |
| --- | --- | --- |
| url | <code>string</code> | download URL (redirects followed) |
| destFile | <code>string</code> | platform path to write (parent directories created) |
| [options] | <code>Object</code> | |
| [options.sha256] | <code>string</code> | expected hex digest of the downloaded bytes |
| [options.progress] | <code>function</code> | called with (transferredBytes, totalBytes) as the download advances; totalBytes is 0 if the server sent no length |

<a name="extractZipFile"></a>

## extractZipFile(zipPath, destDir) ⇒ <code>Promise.&lt;void&gt;</code>
Extracts a zip file into a directory node-side (stdlib only, no browser JSZip; creates the
directory if missing, restores unix executable bits recorded in the archive). Python wheels
are plain zips, so this installs those too.
This is only available in the native app.

**Kind**: global function

| Param | Type | Description |
| --- | --- | --- |
| zipPath | <code>string</code> | platform path of the zip file |
| destDir | <code>string</code> | platform path of the directory to extract into |

<a name="setExecutableBits"></a>

## setExecutableBits(filePath) ⇒ <code>Promise.&lt;void&gt;</code>
Marks a file as executable (chmod 755); no-op on Windows. For binaries whose archives did
not carry unix mode bits.
This is only available in the native app.

**Kind**: global function

| Param | Type | Description |
| --- | --- | --- |
| filePath | <code>string</code> | platform path of the file |

<a name="execFileWithInput"></a>

## execFileWithInput(command, [args], [options]) ⇒ <code>Promise.&lt;{code: number, stdout: string, stderr: string}&gt;</code>
Runs an executable with the given args, feeding it text on stdin and capturing its output -
a one-shot filter-style invocation (e.g. `ruff format -` for the Python beautifier). No
shell is involved. Resolves with the exit code rather than rejecting on non-zero, so
callers can read stderr for the reason.
This is only available in the native app.

**Kind**: global function

| Param | Type | Description |
| --- | --- | --- |
| command | <code>string</code> | platform path of the executable (or a PATH command name) |
| [args] | <code>Array.&lt;string&gt;</code> | |
| [options] | <code>Object</code> | |
| [options.stdinText] | <code>string</code> | written to the process's stdin, then closed |
| [options.cwd] | <code>string</code> | working directory |
| [options.timeoutMs] | <code>number</code> | kill the process and reject after this long |

<a name="getEnvironmentVariable"></a>

## getEnvironmentVariable(varName) ⇒ <code>Promise.&lt;string&gt;</code>
Expand Down
4 changes: 2 additions & 2 deletions gulpfile.js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -909,8 +909,8 @@ function _renameExtensionConcatAsExtensionJSInDist(extensionName) {
}

const minifyableExtensions = ["CloseOthers", "CodeFolding", "DebugCommands", "Git",
"HealthData", "JavaScriptCodeHints", "JavaScriptRefactoring", "PHPSupport", "QuickView",
"TypeScriptSupport"];
"HealthData", "JavaScriptCodeHints", "JavaScriptRefactoring", "PHPSupport", "PythonSupport",
"QuickView", "TypeScriptSupport"];
// extensions that nned not be minified either coz they are single file extensions or some other reason.
const nonMinifyExtensions = ["CSSAtRuleCodeHints", "CSSCodeHints",
"CSSPseudoSelectorHints", "DarkTheme", "DocCommentHints", "HandlebarsSupport", "HTMLCodeHints",
Expand Down
40 changes: 37 additions & 3 deletions src-node/lsp-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,37 @@ function handleMessage(serverId, msg) {
}
}

/**
* Resolve a workspace/configuration item's dotted section (e.g. "python" or "python.pyrefly")
* inside a server's registered workspaceConfiguration object. Returns null when any path
* segment is missing - the spec's "no config" answer.
* @param {?Object} config - the server's workspaceConfiguration (may be undefined)
* @param {?string} section - the requested section; no section means the whole object
* @return {*}
*/
function _lookupConfigSection(config, section) {
if (!config) {
return null;
}
if (!section) {
return config;
}
let value = config;
for (const part of section.split('.')) {
if (value === null || typeof value !== 'object' || !(part in value)) {
return null;
}
value = value[part];
}
return value;
}

/**
* Answer a server-initiated request with a benign, spec-shaped reply. We advertise minimal client
* capabilities (no dynamic registration, workspace.configuration=false), so servers should rarely
* send these - this is the safety net that guarantees no server hangs awaiting a reply.
* Servers that pull configuration regardless (e.g. pyrefly) get the workspaceConfiguration
* object registered at startServer; anything else gets the spec's null "no config".
* @param {string} serverId - The server identifier (for logging)
* @param {Object} server - The server state object
* @param {Object} msg - The incoming JSON-RPC request (method + id)
Expand All @@ -168,7 +195,8 @@ function _respondToServerRequest(serverId, server, msg) {
// Result must be an array matching params.items length; null entries mean "no config".
response = {
jsonrpc: '2.0', id: msg.id,
result: ((msg.params && msg.params.items) || []).map(() => null)
result: ((msg.params && msg.params.items) || []).map(
item => _lookupConfigSection(server.workspaceConfiguration, item && item.section))
};
break;
case 'client/registerCapability':
Expand Down Expand Up @@ -205,10 +233,13 @@ exports.ping = async function ping() {
* @param {string} params.command - Command used to spawn the language server
* @param {string[]} [params.args=['--stdio']] - Arguments for the command
* @param {string} params.rootUri - Root URI of the workspace
* @param {Object} [params.workspaceConfiguration] - settings tree served to the server's
* workspace/configuration pulls (sections resolved by dotted path); pulls answer null
* without it
* @returns {Promise<Object>} Result with success status and server info
*/
exports.startServer = async function startServer(params) {
const { serverId, command, args = ['--stdio'], rootUri } = params;
const { serverId, command, args = ['--stdio'], rootUri, workspaceConfiguration } = params;

if (!serverId || !command) {
throw new Error('serverId and command are required');
Expand All @@ -224,7 +255,9 @@ exports.startServer = async function startServer(params) {
// same spawn-self pattern _npmInstallInFolder and the ESLint service use. This sidesteps
// node_modules/.bin shims entirely (they are sh scripts / .cmd on Windows).
// 2. A server bundled in src-node/node_modules/.bin.
// 3. Fall back to PATH.
// 3. Fall back to spawning the command as given - which also covers an absolute path to a
// native binary (e.g. a user-installed server like pyrefly), spawned as-is, or a PATH
// lookup for a bare command name.
let commandPath = command;
let spawnArgs = args;
if (path.isAbsolute(command) && command.endsWith('.js')) {
Expand All @@ -245,6 +278,7 @@ exports.startServer = async function startServer(params) {
process: serverProcess,
pending: new Map(),
rootUri,
workspaceConfiguration,
stderrTail: [] // keep the last few stderr lines to attach to crash reports
};

Expand Down
Loading
Loading