Skip to content
Merged
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 README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ VS Codeのフォーク。本家にはないUI/UX改善を追加した(してい
| `coderm.workbench.editor.disableGroupLock` | `boolean` | `true` | エディタグループのロック機能を完全に無効化。グループは自動・手動を問わずロックできず、常にロック解除状態で動作 |
| `coderm.languageHost.enabled` | `boolean` | `false` | (実験的)ネイティブ(Rust)Language Host を有効化。設定した言語で tree-sitter ベースの documentSymbol・foldingRange・hover・definition・references・documentHighlights を提供(Phase 5) |
| `coderm.languageHost.languages` | `array` | `[]` | (実験的)ネイティブ Host が扱う言語 ID(例: "typescript", "tsx")。空の場合は機能無効 |
| `coderm.languageHost.isolatedEnabled` | `boolean` | `false` | (実験的)`isolatedExtensions` に列挙した拡張を、メインのローカルプロセス拡張ホストから分離した専用拡張ホストプロセスで実行(Phase 6) |
| `coderm.languageHost.isolatedExtensions` | `array` | `[]` | (実験的)`isolatedEnabled` が有効な場合に、分離拡張ホストにルーティングする拡張 ID(Phase 6) |

---

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ Settings unique to Coderm that are not available in upstream VS Code.
| `coderm.workbench.editor.disableGroupLock` | `boolean` | `true` | Completely disable the editor group lock feature — groups can never be locked (automatically or manually) and always behave as unlocked |
| `coderm.languageHost.enabled` | `boolean` | `false` | _(experimental)_ Enable the native (Rust) Language Host. Provides tree-sitter-backed documentSymbol, foldingRange, hover, definition, references, and document highlights for the configured languages (Phase 5) |
| `coderm.languageHost.languages` | `array` | `[]` | _(experimental)_ Language IDs handled by the native host (e.g. "typescript", "tsx"). Empty keeps the feature inert |
| `coderm.languageHost.isolatedEnabled` | `boolean` | `false` | _(experimental)_ Run the extensions listed in `isolatedExtensions` inside a dedicated extension host process, isolated from the main local process extension host (Phase 6) |
| `coderm.languageHost.isolatedExtensions` | `array` | `[]` | _(experimental)_ Extension IDs to route into the isolated extension host when `isolatedEnabled` is true (Phase 6) |

---

Expand Down
7 changes: 7 additions & 0 deletions src/vs/platform/extensions/common/extensionHostStarter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ export interface IExtensionHostProcessOptions {
detached: boolean;
execArgv: string[] | undefined;
silent: boolean;
// --- Coderm start: isolated language EH kind ---
// When set to 'isolatedExtensionHost' the spawned utility process is named
// 'isolated-extension-host' (visible in Process Explorer). Optional: the main
// process falls back to the regular extension-host name when undefined, so the
// local process extension host is unaffected.
kind?: 'extensionHost' | 'isolatedExtensionHost';
// --- Coderm end ---
}

export interface IExtensionHostStarter {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,9 @@ export class ExtensionHostStarter extends Disposable implements IDisposable, IEx
extHost.start({
...opts,
type: 'extensionHost',
name: 'extension-host',
// --- Coderm start: isolated language EH kind ---
name: opts.kind === 'isolatedExtensionHost' ? 'isolated-extension-host' : 'extension-host',
// --- Coderm end ---
entryPoint: 'vs/workbench/api/node/extensionHostProcess',
args,
execArgv: opts.execArgv,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ import { registerLanguageFeatureProviders } from '../../../services/languageHost

export const CodermLanguageHostEnabledSetting = 'coderm.languageHost.enabled';

// --- Coderm start: Phase 6 isolated EH settings ---
export const CodermLanguageHostIsolatedEnabledSetting = 'coderm.languageHost.isolatedEnabled';
// --- Coderm end ---

Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).registerConfiguration({
id: 'coderm.languageHost',
order: 103,
Expand All @@ -47,6 +51,27 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).regis
"Language IDs handled by the native Language Host (e.g. \"typescript\", \"tsx\"). Empty (default) keeps the feature inert."),
items: { type: 'string' },
},
// --- Coderm start: Phase 6 isolated EH settings ---
[CodermLanguageHostIsolatedEnabledSetting]: {
// Note: default:false intentionally deviates from the project's "new settings
// default to enabled" convention (project CLAUDE.md, development rules). This
// spawns an additional extension host process for the listed extensions; keep
// it inert until Phase 6 is proven end-to-end.
type: 'boolean',
default: false,
scope: ConfigurationScope.APPLICATION,
description: localize('coderm.languageHost.isolatedEnabled',
"(experimental, Phase 6) Run the extensions listed in isolatedExtensions inside a dedicated extension host process, isolated from the main local process extension host."),
},
'coderm.languageHost.isolatedExtensions': {
type: 'array',
default: [],
scope: ConfigurationScope.APPLICATION,
description: localize('coderm.languageHost.isolatedExtensions',
"(experimental, Phase 6) Extension IDs to route into the isolated extension host when isolatedEnabled is true (e.g. [\"vscode.typescript-language-features\"])."),
items: { type: 'string' },
},
// --- Coderm end ---
},
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,13 @@ class BrowserExtensionHostFactory implements IExtensionHostFactory {
case ExtensionHostKind.LocalProcess: {
return null;
}
// --- Coderm start: isolated language EH kind ---
case ExtensionHostKind.LocalIsolatedProcess: {
// Isolated language EH is a desktop-only feature; the browser host
// never routes extensions to it.
return null;
}
// --- Coderm end ---
case ExtensionHostKind.LocalWebWorker: {
const startup = (
isInitialStart
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ import { ExtensionHostManager } from './extensionHostManager.js';
import { IExtensionHostManager } from './extensionHostManagers.js';
import { IResolveAuthorityErrorResult } from './extensionHostProxy.js';
import { IExtensionManifestPropertiesService } from './extensionManifestPropertiesService.js';
import { ExtensionRunningLocation, LocalProcessRunningLocation, LocalWebWorkerRunningLocation, RemoteRunningLocation } from './extensionRunningLocation.js';
// --- Coderm start: isolated language EH kind ---
import { ExtensionRunningLocation, LocalIsolatedProcessRunningLocation, LocalProcessRunningLocation, LocalWebWorkerRunningLocation, RemoteRunningLocation } from './extensionRunningLocation.js';
// --- Coderm end ---
import { ExtensionRunningLocationTracker, filterExtensionIdentifiers } from './extensionRunningLocationTracker.js';
import { ActivationKind, ActivationTimes, ExtensionActivationReason, ExtensionHostStartup, ExtensionPointContribution, IExtensionHost, IExtensionInspectInfo, IExtensionService, IExtensionsStatus, IInternalExtensionService, IMessage, IResponsiveStateChangeEvent, IWillActivateEvent, WillStopExtensionHostsEvent, toExtension, toExtensionDescription } from './extensions.js';
import { ExtensionsProposedApi } from './extensionsProposedApi.js';
Expand Down Expand Up @@ -825,6 +827,15 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
for (let affinity = 0; affinity <= this._runningLocations.maxLocalWebWorkerAffinity; affinity++) {
locations.push(new LocalWebWorkerRunningLocation(affinity));
}
// --- Coderm start: isolated language EH kind ---
// Only spawn an isolated EH when at least one extension is routed to it.
// Without this guard the default (feature off) would still launch a process.
if (this._runningLocations.hasLocalIsolatedProcessExtensions()) {
for (let affinity = 0; affinity <= this._runningLocations.maxLocalIsolatedProcessAffinity; affinity++) {
locations.push(new LocalIsolatedProcessRunningLocation(affinity));
}
}
// --- Coderm end ---
locations.push(new RemoteRunningLocation());
for (const location of locations) {
if (this._extensionHostManagers.getByRunningLocation(location)) {
Expand Down Expand Up @@ -891,6 +902,14 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
}
this._extensionHostManagers.stopOne(extensionHost);
}
// --- Coderm start: isolated language EH kind ---
else if (extensionHost.kind === ExtensionHostKind.LocalIsolatedProcess) {
// Isolate the blast radius: stop only the crashed host and leave the
// main/local process extension host untouched. Restart decisions are
// handled by the NativeExtensionService override below.
this._extensionHostManagers.stopOne(extensionHost);
}
// --- Coderm end ---
}

private _getExtensionHostExitInfoWithTimeout(reconnectionToken: string): Promise<IExtensionHostExitInfo | null> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import { ExtensionIdentifier, IExtensionDescription } from '../../../../platform
export const enum ExtensionHostKind {
LocalProcess = 1,
LocalWebWorker = 2,
Remote = 3
Remote = 3,
// --- Coderm start: isolated language EH kind ---
LocalIsolatedProcess = 4
// --- Coderm end ---
}

export function extensionHostKindToString(kind: ExtensionHostKind | null): string {
Expand All @@ -20,6 +23,9 @@ export function extensionHostKindToString(kind: ExtensionHostKind | null): strin
case ExtensionHostKind.LocalProcess: return 'LocalProcess';
case ExtensionHostKind.LocalWebWorker: return 'LocalWebWorker';
case ExtensionHostKind.Remote: return 'Remote';
// --- Coderm start: isolated language EH kind ---
case ExtensionHostKind.LocalIsolatedProcess: return 'LocalIsolatedProcess';
// --- Coderm end ---
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,24 @@ export class RemoteRunningLocation {
}
}

export type ExtensionRunningLocation = LocalProcessRunningLocation | LocalWebWorkerRunningLocation | RemoteRunningLocation;
// --- Coderm start: isolated language EH kind ---
export class LocalIsolatedProcessRunningLocation {
public readonly kind = ExtensionHostKind.LocalIsolatedProcess;
constructor(
public readonly affinity: number
) { }
public equals(other: ExtensionRunningLocation) {
return (this.kind === other.kind && this.affinity === other.affinity);
}
public asString(): string {
if (this.affinity === 0) {
return 'LocalIsolatedProcess';
}
return `LocalIsolatedProcess${this.affinity}`;
}
}
// --- Coderm end ---

// --- Coderm start: isolated language EH kind ---
export type ExtensionRunningLocation = LocalProcessRunningLocation | LocalWebWorkerRunningLocation | RemoteRunningLocation | LocalIsolatedProcessRunningLocation;
// --- Coderm end ---
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,19 @@ import { IReadOnlyExtensionDescriptionRegistry } from './extensionDescriptionReg
import { ExtensionHostKind, ExtensionRunningPreference, IExtensionHostKindPicker, determineExtensionHostKinds } from './extensionHostKind.js';
import { IExtensionHostManager } from './extensionHostManagers.js';
import { IExtensionManifestPropertiesService } from './extensionManifestPropertiesService.js';
import { ExtensionRunningLocation, LocalProcessRunningLocation, LocalWebWorkerRunningLocation, RemoteRunningLocation } from './extensionRunningLocation.js';
// --- Coderm start: isolated language EH kind ---
import { ExtensionRunningLocation, LocalIsolatedProcessRunningLocation, LocalProcessRunningLocation, LocalWebWorkerRunningLocation, RemoteRunningLocation } from './extensionRunningLocation.js';
// --- Coderm end ---
import { isProposedApiEnabled } from './extensions.js';

export class ExtensionRunningLocationTracker {

private _runningLocation = new ExtensionIdentifierMap<ExtensionRunningLocation | null>();
private _maxLocalProcessAffinity: number = 0;
private _maxLocalWebWorkerAffinity: number = 0;
// --- Coderm start: isolated language EH kind ---
private _maxLocalIsolatedProcessAffinity: number = 0;
// --- Coderm end ---

public get maxLocalProcessAffinity(): number {
return this._maxLocalProcessAffinity;
Expand All @@ -30,6 +35,25 @@ export class ExtensionRunningLocationTracker {
return this._maxLocalWebWorkerAffinity;
}

// --- Coderm start: isolated language EH kind ---
public get maxLocalIsolatedProcessAffinity(): number {
return this._maxLocalIsolatedProcessAffinity;
}

// Returns true when at least one extension has been routed to the isolated
// language extension host. AbstractExtensionService uses this to avoid
// spawning an isolated EH process when there is nothing to host, so the
// default (feature off) stays indistinguishable from upstream.
public hasLocalIsolatedProcessExtensions(): boolean {
for (const runningLocation of this._runningLocation.values()) {
if (runningLocation && runningLocation.kind === ExtensionHostKind.LocalIsolatedProcess) {
return true;
}
}
return false;
}
// --- Coderm end ---

constructor(
private readonly _registry: IReadOnlyExtensionDescriptionRegistry,
private readonly _extensionHostKindPicker: IExtensionHostKindPicker,
Expand Down Expand Up @@ -230,7 +254,7 @@ export class ExtensionRunningLocationTracker {
return this._doComputeRunningLocation(this._runningLocation, localExtensions, remoteExtensions, isInitialAllocation).runningLocation;
}

private _doComputeRunningLocation(existingRunningLocation: ExtensionIdentifierMap<ExtensionRunningLocation | null>, localExtensions: IExtensionDescription[], remoteExtensions: IExtensionDescription[], isInitialAllocation: boolean): { runningLocation: ExtensionIdentifierMap<ExtensionRunningLocation | null>; maxLocalProcessAffinity: number; maxLocalWebWorkerAffinity: number } {
private _doComputeRunningLocation(existingRunningLocation: ExtensionIdentifierMap<ExtensionRunningLocation | null>, localExtensions: IExtensionDescription[], remoteExtensions: IExtensionDescription[], isInitialAllocation: boolean): { runningLocation: ExtensionIdentifierMap<ExtensionRunningLocation | null>; maxLocalProcessAffinity: number; maxLocalWebWorkerAffinity: number; maxLocalIsolatedProcessAffinity: number } {
// Skip extensions that have an existing running location
localExtensions = localExtensions.filter(extension => !existingRunningLocation.has(extension.identifier));
remoteExtensions = remoteExtensions.filter(extension => !existingRunningLocation.has(extension.identifier));
Expand All @@ -253,6 +277,9 @@ export class ExtensionRunningLocationTracker {
const result = new ExtensionIdentifierMap<ExtensionRunningLocation | null>();
const localProcessExtensions: IExtensionDescription[] = [];
const localWebWorkerExtensions: IExtensionDescription[] = [];
// --- Coderm start: isolated language EH kind ---
const localIsolatedProcessExtensions: IExtensionDescription[] = [];
// --- Coderm end ---
for (const [extensionIdKey, extensionHostKind] of extensionHostKinds) {
let runningLocation: ExtensionRunningLocation | null = null;
if (extensionHostKind === ExtensionHostKind.LocalProcess) {
Expand All @@ -267,6 +294,13 @@ export class ExtensionRunningLocationTracker {
}
} else if (extensionHostKind === ExtensionHostKind.Remote) {
runningLocation = new RemoteRunningLocation();
} else if (extensionHostKind === ExtensionHostKind.LocalIsolatedProcess) {
// --- Coderm start: isolated language EH kind ---
const extensionDescription = extensions.get(extensionIdKey);
if (extensionDescription) {
localIsolatedProcessExtensions.push(extensionDescription);
}
// --- Coderm end ---
}
result.set(extensionIdKey, runningLocation);
}
Expand All @@ -281,6 +315,13 @@ export class ExtensionRunningLocationTracker {
const affinity = localWebWorkerAffinities.get(extension.identifier) || 0;
result.set(extension.identifier, new LocalWebWorkerRunningLocation(affinity));
}
// --- Coderm start: isolated language EH kind ---
const { affinities: localIsolatedProcessAffinities, maxAffinity: maxLocalIsolatedProcessAffinity } = this._computeAffinity(localIsolatedProcessExtensions, ExtensionHostKind.LocalIsolatedProcess, isInitialAllocation);
for (const extension of localIsolatedProcessExtensions) {
const affinity = localIsolatedProcessAffinities.get(extension.identifier) || 0;
result.set(extension.identifier, new LocalIsolatedProcessRunningLocation(affinity));
}
// --- Coderm end ---

// Add extensions that already have an existing running location
for (const [extensionIdKey, runningLocation] of existingRunningLocation) {
Expand All @@ -289,14 +330,17 @@ export class ExtensionRunningLocationTracker {
}
}

return { runningLocation: result, maxLocalProcessAffinity: maxAffinity, maxLocalWebWorkerAffinity: maxLocalWebWorkerAffinity };
return { runningLocation: result, maxLocalProcessAffinity: maxAffinity, maxLocalWebWorkerAffinity: maxLocalWebWorkerAffinity, maxLocalIsolatedProcessAffinity: maxLocalIsolatedProcessAffinity };
}

public initializeRunningLocation(localExtensions: IExtensionDescription[], remoteExtensions: IExtensionDescription[]): void {
const { runningLocation, maxLocalProcessAffinity, maxLocalWebWorkerAffinity } = this._doComputeRunningLocation(this._runningLocation, localExtensions, remoteExtensions, true);
const { runningLocation, maxLocalProcessAffinity, maxLocalWebWorkerAffinity, maxLocalIsolatedProcessAffinity } = this._doComputeRunningLocation(this._runningLocation, localExtensions, remoteExtensions, true);
this._runningLocation = runningLocation;
this._maxLocalProcessAffinity = maxLocalProcessAffinity;
this._maxLocalWebWorkerAffinity = maxLocalWebWorkerAffinity;
// --- Coderm start: isolated language EH kind ---
this._maxLocalIsolatedProcessAffinity = maxLocalIsolatedProcessAffinity;
// --- Coderm end ---
}

/**
Expand Down Expand Up @@ -324,6 +368,9 @@ export class ExtensionRunningLocationTracker {
// Determine new running location
const localProcessExtensions: IExtensionDescription[] = [];
const localWebWorkerExtensions: IExtensionDescription[] = [];
// --- Coderm start: isolated language EH kind ---
const localIsolatedProcessExtensions: IExtensionDescription[] = [];
// --- Coderm end ---
for (const extension of toAdd) {
const extensionKind = this.readExtensionKinds(extension);
const isRemote = extension.extensionLocation.scheme === Schemas.vscodeRemote;
Expand All @@ -335,6 +382,10 @@ export class ExtensionRunningLocationTracker {
localWebWorkerExtensions.push(extension);
} else if (extensionHostKind === ExtensionHostKind.Remote) {
runningLocation = new RemoteRunningLocation();
} else if (extensionHostKind === ExtensionHostKind.LocalIsolatedProcess) {
// --- Coderm start: isolated language EH kind ---
localIsolatedProcessExtensions.push(extension);
// --- Coderm end ---
}
this._runningLocation.set(extension.identifier, runningLocation);
}
Expand All @@ -350,6 +401,14 @@ export class ExtensionRunningLocationTracker {
const affinity = webWorkerExtensionsAffinities.get(extension.identifier) || 0;
this._runningLocation.set(extension.identifier, new LocalWebWorkerRunningLocation(affinity));
}

// --- Coderm start: isolated language EH kind ---
const { affinities: isolatedProcessExtensionsAffinities } = this._computeAffinity(localIsolatedProcessExtensions, ExtensionHostKind.LocalIsolatedProcess, false);
for (const extension of localIsolatedProcessExtensions) {
const affinity = isolatedProcessExtensionsAffinities.get(extension.identifier) || 0;
this._runningLocation.set(extension.identifier, new LocalIsolatedProcessRunningLocation(affinity));
}
// --- Coderm end ---
}
}

Expand Down
Loading
Loading