@theia/preferences 1.74.0-next.10 → 1.74.0-next.15
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/lib/browser/preference-frontend-contribution.d.ts +8 -1
- package/lib/browser/preference-frontend-contribution.d.ts.map +1 -1
- package/lib/browser/preference-frontend-contribution.js +45 -5
- package/lib/browser/preference-frontend-contribution.js.map +1 -1
- package/lib/browser/preference-frontend-module.d.ts.map +1 -1
- package/lib/browser/preference-frontend-module.js +3 -0
- package/lib/browser/preference-frontend-module.js.map +1 -1
- package/lib/browser/session-preference-status-bar-contribution.d.ts +33 -0
- package/lib/browser/session-preference-status-bar-contribution.d.ts.map +1 -0
- package/lib/browser/session-preference-status-bar-contribution.js +157 -0
- package/lib/browser/session-preference-status-bar-contribution.js.map +1 -0
- package/lib/browser/views/components/preference-node-renderer.d.ts +8 -0
- package/lib/browser/views/components/preference-node-renderer.d.ts.map +1 -1
- package/lib/browser/views/components/preference-node-renderer.js +37 -5
- package/lib/browser/views/components/preference-node-renderer.js.map +1 -1
- package/lib/common/cli-preferences.d.ts +1 -0
- package/lib/common/cli-preferences.d.ts.map +1 -1
- package/lib/node/preference-backend-module.d.ts.map +1 -1
- package/lib/node/preference-backend-module.js +2 -0
- package/lib/node/preference-backend-module.js.map +1 -1
- package/lib/node/preference-cli-contribution.d.ts +11 -1
- package/lib/node/preference-cli-contribution.d.ts.map +1 -1
- package/lib/node/preference-cli-contribution.js +44 -9
- package/lib/node/preference-cli-contribution.js.map +1 -1
- package/lib/node/preference-cli-contribution.spec.d.ts +2 -0
- package/lib/node/preference-cli-contribution.spec.d.ts.map +1 -0
- package/lib/node/preference-cli-contribution.spec.js +157 -0
- package/lib/node/preference-cli-contribution.spec.js.map +1 -0
- package/package.json +8 -8
- package/src/browser/preference-frontend-contribution.ts +50 -5
- package/src/browser/preference-frontend-module.ts +3 -0
- package/src/browser/session-preference-status-bar-contribution.ts +157 -0
- package/src/browser/views/components/preference-node-renderer.ts +38 -5
- package/src/common/cli-preferences.ts +1 -0
- package/src/node/preference-backend-module.ts +2 -0
- package/src/node/preference-cli-contribution.spec.ts +171 -0
- package/src/node/preference-cli-contribution.ts +48 -10
|
@@ -21,29 +21,64 @@ const inversify_1 = require("@theia/core/shared/inversify");
|
|
|
21
21
|
let PreferenceCliContribution = class PreferenceCliContribution {
|
|
22
22
|
constructor() {
|
|
23
23
|
this.preferences = [];
|
|
24
|
+
this.sessionPreferences = [];
|
|
24
25
|
}
|
|
25
26
|
configure(conf) {
|
|
26
27
|
conf.option('set-preference', {
|
|
27
28
|
nargs: 1,
|
|
28
|
-
desc: 'sets the specified preference'
|
|
29
|
+
desc: 'sets the specified preference (persisted to user settings)'
|
|
30
|
+
});
|
|
31
|
+
conf.option('session-preference', {
|
|
32
|
+
nargs: 1,
|
|
33
|
+
desc: 'sets the specified preference for this process only (in-memory, not persisted)'
|
|
29
34
|
});
|
|
30
35
|
}
|
|
31
36
|
setArguments(args) {
|
|
32
37
|
if (args.setPreference) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
this.parseInto(args.setPreference, this.preferences);
|
|
39
|
+
}
|
|
40
|
+
if (args.sessionPreference) {
|
|
41
|
+
this.parseInto(args.sessionPreference, this.sessionPreferences);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
parseInto(raw, target) {
|
|
45
|
+
const entries = raw instanceof Array ? raw : [raw];
|
|
46
|
+
for (const entry of entries) {
|
|
47
|
+
const firstEqualIndex = entry.indexOf('=');
|
|
48
|
+
if (firstEqualIndex <= 0) {
|
|
49
|
+
console.warn(`Ignoring preference CLI argument "${entry}": expected KEY=JSONVALUE.`);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
let rawValue = entry.substring(firstEqualIndex + 1);
|
|
53
|
+
if (rawValue.startsWith('base64:')) {
|
|
54
|
+
rawValue = Buffer.from(rawValue.substring('base64:'.length), 'base64').toString('utf-8');
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
target.push([entry.substring(0, firstEqualIndex), JSON.parse(rawValue)]);
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
const reason = e instanceof Error ? e.message : String(e);
|
|
61
|
+
console.warn(`Ignoring preference CLI argument "${entry}": value is not valid JSON (${reason}).`);
|
|
41
62
|
}
|
|
42
63
|
}
|
|
43
64
|
}
|
|
44
65
|
async getPreferences() {
|
|
45
66
|
return this.preferences;
|
|
46
67
|
}
|
|
68
|
+
async getSessionPreferences() {
|
|
69
|
+
return this.sessionPreferences;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Forward `--session-preference` values to the remote backend when attaching
|
|
73
|
+
* to a remote (e.g. dev container). Values are base64-encoded JSON to survive
|
|
74
|
+
* shell argument parsing intact.
|
|
75
|
+
*/
|
|
76
|
+
enhanceArgs(_context) {
|
|
77
|
+
return this.sessionPreferences.map(([key, value]) => {
|
|
78
|
+
const encoded = Buffer.from(JSON.stringify(value), 'utf-8').toString('base64');
|
|
79
|
+
return `--session-preference=${key}=base64:${encoded}`;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
47
82
|
};
|
|
48
83
|
exports.PreferenceCliContribution = PreferenceCliContribution;
|
|
49
84
|
exports.PreferenceCliContribution = PreferenceCliContribution = tslib_1.__decorate([
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preference-cli-contribution.js","sourceRoot":"","sources":["../../src/node/preference-cli-contribution.ts"],"names":[],"mappings":";AAAA,gFAAgF;AAChF,yCAAyC;AACzC,EAAE;AACF,2EAA2E;AAC3E,mEAAmE;AACnE,wCAAwC;AACxC,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,6EAA6E;AAC7E,yDAAyD;AACzD,uDAAuD;AACvD,EAAE;AACF,gFAAgF;AAChF,gFAAgF;;;;AAEhF,4DAA0D;
|
|
1
|
+
{"version":3,"file":"preference-cli-contribution.js","sourceRoot":"","sources":["../../src/node/preference-cli-contribution.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preference-cli-contribution.spec.d.ts","sourceRoot":"","sources":["../../src/node/preference-cli-contribution.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// *****************************************************************************
|
|
3
|
+
// Copyright (C) 2026 EclipseSource and others.
|
|
4
|
+
//
|
|
5
|
+
// This program and the accompanying materials are made available under the
|
|
6
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
7
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
8
|
+
//
|
|
9
|
+
// This Source Code may also be made available under the following Secondary
|
|
10
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
11
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
12
|
+
// with the GNU Classpath Exception which is available at
|
|
13
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
14
|
+
//
|
|
15
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
16
|
+
// *****************************************************************************
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
const chai_1 = require("chai");
|
|
19
|
+
const os_1 = require("@theia/core/lib/common/os");
|
|
20
|
+
const preference_cli_contribution_1 = require("./preference-cli-contribution");
|
|
21
|
+
const REMOTE_CLI_CONTEXT = {
|
|
22
|
+
platform: { os: os_1.OS.Type.Linux, arch: 'x64' },
|
|
23
|
+
directory: '/'
|
|
24
|
+
};
|
|
25
|
+
describe('PreferenceCliContribution', () => {
|
|
26
|
+
let contribution;
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
contribution = new preference_cli_contribution_1.PreferenceCliContribution();
|
|
29
|
+
});
|
|
30
|
+
describe('--set-preference', () => {
|
|
31
|
+
it('parses a single key=value entry as JSON', async () => {
|
|
32
|
+
contribution.setArguments({ setPreference: 'editor.fontSize=14' });
|
|
33
|
+
(0, chai_1.expect)(await contribution.getPreferences()).to.deep.equal([['editor.fontSize', 14]]);
|
|
34
|
+
(0, chai_1.expect)(await contribution.getSessionPreferences()).to.deep.equal([]);
|
|
35
|
+
});
|
|
36
|
+
it('parses string values quoted as JSON strings', async () => {
|
|
37
|
+
contribution.setArguments({ setPreference: 'workbench.colorTheme="Dark+"' });
|
|
38
|
+
(0, chai_1.expect)(await contribution.getPreferences()).to.deep.equal([['workbench.colorTheme', 'Dark+']]);
|
|
39
|
+
});
|
|
40
|
+
it('parses object values', async () => {
|
|
41
|
+
contribution.setArguments({ setPreference: 'ai-features.chat.toolConfirmation={"shellExecute":"always_allow"}' });
|
|
42
|
+
(0, chai_1.expect)(await contribution.getPreferences()).to.deep.equal([
|
|
43
|
+
['ai-features.chat.toolConfirmation', { shellExecute: 'always_allow' }]
|
|
44
|
+
]);
|
|
45
|
+
});
|
|
46
|
+
it('accepts an array of entries', async () => {
|
|
47
|
+
contribution.setArguments({ setPreference: ['editor.fontSize=12', 'editor.tabSize=2'] });
|
|
48
|
+
(0, chai_1.expect)(await contribution.getPreferences()).to.deep.equal([
|
|
49
|
+
['editor.fontSize', 12],
|
|
50
|
+
['editor.tabSize', 2]
|
|
51
|
+
]);
|
|
52
|
+
});
|
|
53
|
+
it('decodes base64-prefixed JSON values', async () => {
|
|
54
|
+
const json = JSON.stringify({ nested: true, n: 1 });
|
|
55
|
+
const b64 = Buffer.from(json, 'utf-8').toString('base64');
|
|
56
|
+
contribution.setArguments({ setPreference: `my.pref=base64:${b64}` });
|
|
57
|
+
(0, chai_1.expect)(await contribution.getPreferences()).to.deep.equal([
|
|
58
|
+
['my.pref', { nested: true, n: 1 }]
|
|
59
|
+
]);
|
|
60
|
+
});
|
|
61
|
+
it('preserves "=" characters inside the value', async () => {
|
|
62
|
+
contribution.setArguments({ setPreference: 'my.pref="a=b=c"' });
|
|
63
|
+
(0, chai_1.expect)(await contribution.getPreferences()).to.deep.equal([['my.pref', 'a=b=c']]);
|
|
64
|
+
});
|
|
65
|
+
it('skips entries that do not contain "="', async () => {
|
|
66
|
+
const originalWarn = console.warn;
|
|
67
|
+
console.warn = () => { };
|
|
68
|
+
try {
|
|
69
|
+
contribution.setArguments({ setPreference: ['justAKey', 'editor.fontSize=14'] });
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
console.warn = originalWarn;
|
|
73
|
+
}
|
|
74
|
+
(0, chai_1.expect)(await contribution.getPreferences()).to.deep.equal([['editor.fontSize', 14]]);
|
|
75
|
+
});
|
|
76
|
+
it('skips entries whose value is not valid JSON', async () => {
|
|
77
|
+
const originalWarn = console.warn;
|
|
78
|
+
console.warn = () => { };
|
|
79
|
+
try {
|
|
80
|
+
contribution.setArguments({ setPreference: ['my.pref=notJson', 'editor.fontSize=14'] });
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
console.warn = originalWarn;
|
|
84
|
+
}
|
|
85
|
+
(0, chai_1.expect)(await contribution.getPreferences()).to.deep.equal([['editor.fontSize', 14]]);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
describe('--session-preference', () => {
|
|
89
|
+
it('routes entries into the session preferences bucket', async () => {
|
|
90
|
+
contribution.setArguments({ sessionPreference: 'ai-features.chat.defaultToolConfirmation="always_allow"' });
|
|
91
|
+
(0, chai_1.expect)(await contribution.getSessionPreferences()).to.deep.equal([
|
|
92
|
+
['ai-features.chat.defaultToolConfirmation', 'always_allow']
|
|
93
|
+
]);
|
|
94
|
+
(0, chai_1.expect)(await contribution.getPreferences()).to.deep.equal([]);
|
|
95
|
+
});
|
|
96
|
+
it('supports multiple session entries and base64', async () => {
|
|
97
|
+
const b64 = Buffer.from('{"shellExecute":"always_allow"}', 'utf-8').toString('base64');
|
|
98
|
+
contribution.setArguments({
|
|
99
|
+
sessionPreference: [
|
|
100
|
+
'ai-features.chat.defaultToolConfirmation="always_allow"',
|
|
101
|
+
`ai-features.chat.toolConfirmation=base64:${b64}`
|
|
102
|
+
]
|
|
103
|
+
});
|
|
104
|
+
(0, chai_1.expect)(await contribution.getSessionPreferences()).to.deep.equal([
|
|
105
|
+
['ai-features.chat.defaultToolConfirmation', 'always_allow'],
|
|
106
|
+
['ai-features.chat.toolConfirmation', { shellExecute: 'always_allow' }]
|
|
107
|
+
]);
|
|
108
|
+
});
|
|
109
|
+
it('keeps --set-preference and --session-preference independent', async () => {
|
|
110
|
+
contribution.setArguments({
|
|
111
|
+
setPreference: 'editor.fontSize=14',
|
|
112
|
+
sessionPreference: 'editor.fontSize=20'
|
|
113
|
+
});
|
|
114
|
+
(0, chai_1.expect)(await contribution.getPreferences()).to.deep.equal([['editor.fontSize', 14]]);
|
|
115
|
+
(0, chai_1.expect)(await contribution.getSessionPreferences()).to.deep.equal([['editor.fontSize', 20]]);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
describe('enhanceArgs (remote forwarding)', () => {
|
|
119
|
+
it('returns an empty list when no session prefs are set', () => {
|
|
120
|
+
(0, chai_1.expect)(contribution.enhanceArgs(REMOTE_CLI_CONTEXT)).to.deep.equal([]);
|
|
121
|
+
});
|
|
122
|
+
it('emits one base64-encoded --session-preference flag per entry', () => {
|
|
123
|
+
contribution.setArguments({
|
|
124
|
+
sessionPreference: [
|
|
125
|
+
'ai-features.chat.defaultToolConfirmation="always_allow"',
|
|
126
|
+
'ai-features.chat.toolConfirmation={"shellExecute":"always_allow"}'
|
|
127
|
+
]
|
|
128
|
+
});
|
|
129
|
+
const args = contribution.enhanceArgs(REMOTE_CLI_CONTEXT);
|
|
130
|
+
(0, chai_1.expect)(args).to.have.lengthOf(2);
|
|
131
|
+
for (const arg of args) {
|
|
132
|
+
(0, chai_1.expect)(arg.startsWith('--session-preference=')).to.be.true;
|
|
133
|
+
const eq = arg.indexOf('=', '--session-preference='.length);
|
|
134
|
+
const key = arg.substring('--session-preference='.length, eq);
|
|
135
|
+
const rawValue = arg.substring(eq + 1);
|
|
136
|
+
(0, chai_1.expect)(rawValue.startsWith('base64:')).to.be.true;
|
|
137
|
+
const decoded = JSON.parse(Buffer.from(rawValue.substring('base64:'.length), 'base64').toString('utf-8'));
|
|
138
|
+
if (key === 'ai-features.chat.defaultToolConfirmation') {
|
|
139
|
+
(0, chai_1.expect)(decoded).to.equal('always_allow');
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
(0, chai_1.expect)(decoded).to.deep.equal({ shellExecute: 'always_allow' });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
it('does not forward --set-preference values', () => {
|
|
147
|
+
contribution.setArguments({
|
|
148
|
+
setPreference: 'editor.fontSize=14',
|
|
149
|
+
sessionPreference: 'foo="bar"'
|
|
150
|
+
});
|
|
151
|
+
const args = contribution.enhanceArgs(REMOTE_CLI_CONTEXT);
|
|
152
|
+
(0, chai_1.expect)(args).to.have.lengthOf(1);
|
|
153
|
+
(0, chai_1.expect)(args[0]).to.match(/^--session-preference=foo=base64:/);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
//# sourceMappingURL=preference-cli-contribution.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preference-cli-contribution.spec.js","sourceRoot":"","sources":["../../src/node/preference-cli-contribution.spec.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theia/preferences",
|
|
3
|
-
"version": "1.74.0-next.
|
|
3
|
+
"version": "1.74.0-next.15+28d45f428",
|
|
4
4
|
"description": "Theia - Preferences Extension",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@theia/core": "1.74.0-next.
|
|
7
|
-
"@theia/editor": "1.74.0-next.
|
|
8
|
-
"@theia/filesystem": "1.74.0-next.
|
|
9
|
-
"@theia/monaco": "1.74.0-next.
|
|
6
|
+
"@theia/core": "1.74.0-next.15+28d45f428",
|
|
7
|
+
"@theia/editor": "1.74.0-next.15+28d45f428",
|
|
8
|
+
"@theia/filesystem": "1.74.0-next.15+28d45f428",
|
|
9
|
+
"@theia/monaco": "1.74.0-next.15+28d45f428",
|
|
10
10
|
"@theia/monaco-editor-core": "1.108.201",
|
|
11
|
-
"@theia/userstorage": "1.74.0-next.
|
|
12
|
-
"@theia/workspace": "1.74.0-next.
|
|
11
|
+
"@theia/userstorage": "1.74.0-next.15+28d45f428",
|
|
12
|
+
"@theia/workspace": "1.74.0-next.15+28d45f428",
|
|
13
13
|
"async-mutex": "^0.3.2",
|
|
14
14
|
"fast-deep-equal": "^3.1.3",
|
|
15
15
|
"jsonc-parser": "^3.3.1",
|
|
@@ -55,5 +55,5 @@
|
|
|
55
55
|
"nyc": {
|
|
56
56
|
"extends": "../../configs/nyc.json"
|
|
57
57
|
},
|
|
58
|
-
"gitHead": "
|
|
58
|
+
"gitHead": "28d45f4284702e106aa784d69bc3d3418243a05a"
|
|
59
59
|
}
|
|
@@ -28,11 +28,56 @@ export class PreferenceFrontendContribution implements FrontendApplicationContri
|
|
|
28
28
|
protected readonly preferenceService: PreferenceService;
|
|
29
29
|
|
|
30
30
|
onStart(): void {
|
|
31
|
-
this.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
this.applyCliPreferences();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
protected async applyCliPreferences(): Promise<void> {
|
|
35
|
+
// Fetch both buckets in parallel; both are RPC hops to the same backend and
|
|
36
|
+
// can overlap with the preference service initialising its providers.
|
|
37
|
+
const [session, persistent] = await Promise.all([
|
|
38
|
+
this.CliPreferences.getSessionPreferences().catch(e => {
|
|
39
|
+
console.warn('Failed to fetch --session-preference values:', e);
|
|
40
|
+
return [] as [string, unknown][];
|
|
41
|
+
}),
|
|
42
|
+
this.CliPreferences.getPreferences().catch(e => {
|
|
43
|
+
console.warn('Failed to fetch --set-preference values:', e);
|
|
44
|
+
return [] as [string, unknown][];
|
|
45
|
+
})
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
// `preferenceService.set()` needs the target provider registered in the providers
|
|
49
|
+
// map, which only happens once `initializeProviders()` has walked every scope and
|
|
50
|
+
// resolved `ready`. Writing before that returns `undefined` from `getProvider()`
|
|
51
|
+
// and the call throws "Unable to write to ... Settings".
|
|
52
|
+
await this.preferenceService.ready;
|
|
53
|
+
|
|
54
|
+
// Session writes go first and are awaited sequentially so that when the same key
|
|
55
|
+
// appears in both `--session-preference` and `--set-preference`, the persistent
|
|
56
|
+
// write in the next block sees a settled session provider (and correctly triggers
|
|
57
|
+
// `evictSessionOverride`) rather than racing with the session write.
|
|
58
|
+
await this.applyAll(session, PreferenceScope.Session);
|
|
59
|
+
if (session.length > 0) {
|
|
60
|
+
// Log keys only. Values may carry overrides for security-sensitive prefs
|
|
61
|
+
// (e.g. AI tool auto-approval) and should not leak into screenshots or support bundles.
|
|
62
|
+
console.info(`Applied ${session.length} --session-preference value(s):`,
|
|
63
|
+
session.map(([k]) => k).join(', '));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
await this.applyAll(persistent, PreferenceScope.User);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Applies a batch of CLI-provided preferences sequentially. A rejection from an
|
|
71
|
+
* individual write (bad key, invalid scope, etc.) is logged and does not abort the
|
|
72
|
+
* remaining writes, and it is not left as an unhandled promise rejection.
|
|
73
|
+
*/
|
|
74
|
+
protected async applyAll(entries: ReadonlyArray<[string, unknown]>, scope: PreferenceScope): Promise<void> {
|
|
75
|
+
for (const [key, value] of entries) {
|
|
76
|
+
try {
|
|
77
|
+
await this.preferenceService.set(key, value, scope);
|
|
78
|
+
} catch (e) {
|
|
79
|
+
console.warn(`Failed to apply CLI preference "${key}" to ${PreferenceScope[scope]} scope:`, e);
|
|
35
80
|
}
|
|
36
|
-
}
|
|
81
|
+
}
|
|
37
82
|
}
|
|
38
83
|
}
|
|
@@ -32,6 +32,7 @@ import { PreferenceOpenHandler } from './preference-open-handler';
|
|
|
32
32
|
import { CliPreferences, CliPreferencesPath } from '../common/cli-preferences';
|
|
33
33
|
import { ServiceConnectionProvider } from '@theia/core/lib/browser/messaging/service-connection-provider';
|
|
34
34
|
import { PreferenceFrontendContribution } from './preference-frontend-contribution';
|
|
35
|
+
import { SessionPreferenceStatusBarContribution } from './session-preference-status-bar-contribution';
|
|
35
36
|
import { PreferenceLayoutProvider } from './util/preference-layout';
|
|
36
37
|
import { PreferencesWidget } from './views/preference-widget';
|
|
37
38
|
import { PreferenceStorageFactory } from '../common/abstract-resource-preference-provider';
|
|
@@ -70,6 +71,8 @@ export function bindPreferences(bind: interfaces.Bind, unbind: interfaces.Unbind
|
|
|
70
71
|
bind(CliPreferences).toDynamicValue(ctx => ServiceConnectionProvider.createProxy<CliPreferences>(ctx.container, CliPreferencesPath)).inSingletonScope();
|
|
71
72
|
bind(PreferenceFrontendContribution).toSelf().inSingletonScope();
|
|
72
73
|
bind(FrontendApplicationContribution).toService(PreferenceFrontendContribution);
|
|
74
|
+
bind(SessionPreferenceStatusBarContribution).toSelf().inSingletonScope();
|
|
75
|
+
bind(FrontendApplicationContribution).toService(SessionPreferenceStatusBarContribution);
|
|
73
76
|
|
|
74
77
|
bind(WidgetStatusBarContribution).toConstantValue(noopWidgetStatusBarContribution(PreferencesWidget));
|
|
75
78
|
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 EclipseSource and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
|
18
|
+
import { CommonCommands, FrontendApplicationContribution, StatusBar, StatusBarAlignment } from '@theia/core/lib/browser';
|
|
19
|
+
import { MarkdownStringImpl } from '@theia/core/lib/common/markdown-rendering/markdown-string';
|
|
20
|
+
import { PreferenceProviderProvider, PreferenceScope, PreferenceService } from '@theia/core/lib/common/preferences';
|
|
21
|
+
import { DisposableCollection, nls } from '@theia/core';
|
|
22
|
+
|
|
23
|
+
export const SESSION_PREFERENCE_STATUS_BAR_ID = 'session-preference-status';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Status bar item that reports preferences whose effective value comes from the
|
|
27
|
+
* in-memory session scope (typically set via `--session-preference`). Each active
|
|
28
|
+
* override is listed in the tooltip with a link that opens the Settings UI filtered
|
|
29
|
+
* to that preference, so the user can read its description there.
|
|
30
|
+
*/
|
|
31
|
+
@injectable()
|
|
32
|
+
export class SessionPreferenceStatusBarContribution implements FrontendApplicationContribution {
|
|
33
|
+
|
|
34
|
+
@inject(StatusBar)
|
|
35
|
+
protected readonly statusBar: StatusBar;
|
|
36
|
+
|
|
37
|
+
@inject(PreferenceService)
|
|
38
|
+
protected readonly preferenceService: PreferenceService;
|
|
39
|
+
|
|
40
|
+
@inject(PreferenceProviderProvider)
|
|
41
|
+
protected readonly providerProvider: PreferenceProviderProvider;
|
|
42
|
+
|
|
43
|
+
protected readonly toDispose = new DisposableCollection();
|
|
44
|
+
|
|
45
|
+
@postConstruct()
|
|
46
|
+
protected init(): void {
|
|
47
|
+
this.toDispose.push(
|
|
48
|
+
this.preferenceService.onPreferenceChanged(e => {
|
|
49
|
+
// React to both additions and removals of session overrides.
|
|
50
|
+
// Filtering by `isInSessionScope(key)` would miss removals because by the time
|
|
51
|
+
// the event fires, the session value is already gone.
|
|
52
|
+
if (e.scope === PreferenceScope.Session) {
|
|
53
|
+
this.refresh();
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
onStart(): void {
|
|
60
|
+
this.preferenceService.ready.then(() => this.refresh());
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
protected refresh(): void {
|
|
64
|
+
const entries = this.collectActive();
|
|
65
|
+
if (entries.length === 0) {
|
|
66
|
+
this.statusBar.removeElement(SESSION_PREFERENCE_STATUS_BAR_ID);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const label = nls.localize('theia/preferences/sessionPreferences/statusBar',
|
|
70
|
+
'Session Preferences ({0})', entries.length);
|
|
71
|
+
this.statusBar.setElement(SESSION_PREFERENCE_STATUS_BAR_ID, {
|
|
72
|
+
alignment: StatusBarAlignment.LEFT,
|
|
73
|
+
text: `$(warning) ${label}`,
|
|
74
|
+
tooltip: this.buildTooltip(entries),
|
|
75
|
+
// Lower priority sits further right on the left area, so this entry stays
|
|
76
|
+
// to the right of higher-priority entries (workspace, git, problems).
|
|
77
|
+
priority: 1,
|
|
78
|
+
backgroundColor: 'var(--theia-statusBarItem-prominentBackground)',
|
|
79
|
+
color: 'var(--theia-statusBarItem-prominentForeground)'
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
protected collectActive(): ReadonlyArray<[string, unknown]> {
|
|
84
|
+
// Read directly from the session provider rather than walking schema properties,
|
|
85
|
+
// so overrides for keys that aren't registered as schema properties (e.g. a stray
|
|
86
|
+
// `--session-preference foo.bar=1`) still show up here and in the tooltip.
|
|
87
|
+
const sessionProvider = this.providerProvider(PreferenceScope.Session);
|
|
88
|
+
const preferences = sessionProvider?.getPreferences() ?? {};
|
|
89
|
+
const result: [string, unknown][] = Object.entries(preferences);
|
|
90
|
+
// Stable order: alphabetical by key.
|
|
91
|
+
result.sort(([a], [b]) => a.localeCompare(b));
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
protected buildTooltip(entries: ReadonlyArray<[string, unknown]>): MarkdownStringImpl {
|
|
96
|
+
// `isTrusted` is restricted to the single command we actually emit links for, so
|
|
97
|
+
// a stray `command:` link in any preference key (extremely unlikely) can't execute.
|
|
98
|
+
const md = new MarkdownStringImpl('', {
|
|
99
|
+
supportThemeIcons: true,
|
|
100
|
+
isTrusted: { enabledCommands: [CommonCommands.OPEN_PREFERENCES.id] }
|
|
101
|
+
});
|
|
102
|
+
md.appendMarkdown(`**${nls.localize('theia/preferences/sessionPreferences/tooltipHeader',
|
|
103
|
+
'These settings are overridden for this session')}**\n\n`);
|
|
104
|
+
md.appendMarkdown(nls.localize('theia/preferences/sessionPreferences/tooltipBody',
|
|
105
|
+
'They were passed in via --session-preference and only live in memory. Click a setting name to jump to it in the Settings view.'));
|
|
106
|
+
md.appendMarkdown('\n\n');
|
|
107
|
+
for (const [key, value] of entries) {
|
|
108
|
+
// The setting name is a plain markdown link to the Settings view; the value
|
|
109
|
+
// follows as plain text after a colon. No code-block styling on either side.
|
|
110
|
+
md.appendMarkdown(`[${this.escapeLinkText(key)}](${this.buildOpenSettingsUri(key)}): ${this.formatDisplayValue(value)}\n\n`);
|
|
111
|
+
}
|
|
112
|
+
md.appendMarkdown(nls.localize('theia/preferences/sessionPreferences/tooltipChangeNote',
|
|
113
|
+
'Editing any of these clears its session override for this run. To use the saved values from the next launch on, restart without the --session-preference arguments.'));
|
|
114
|
+
return md;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
protected buildOpenSettingsUri(preferenceKey: string): string {
|
|
118
|
+
// The Settings UI uses its search term, so a plain key string filters the view
|
|
119
|
+
// to that single preference.
|
|
120
|
+
const args = encodeURIComponent(JSON.stringify([preferenceKey]));
|
|
121
|
+
return `command:${CommonCommands.OPEN_PREFERENCES.id}?${args}`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Produces a readable, no-code-formatting representation of a preference value:
|
|
126
|
+
* strings without surrounding JSON quotes, booleans as on/off, primitives as-is,
|
|
127
|
+
* objects/arrays as compact JSON. The result is shown as plain text in the tooltip.
|
|
128
|
+
*/
|
|
129
|
+
protected formatDisplayValue(value: unknown): string {
|
|
130
|
+
if (typeof value === 'string') {
|
|
131
|
+
return value.length === 0 ? '(empty)' : value;
|
|
132
|
+
}
|
|
133
|
+
if (typeof value === 'boolean') {
|
|
134
|
+
return value
|
|
135
|
+
? nls.localize('theia/preferences/sessionPreferences/valueOn', 'on')
|
|
136
|
+
: nls.localize('theia/preferences/sessionPreferences/valueOff', 'off');
|
|
137
|
+
}
|
|
138
|
+
if (value === undefined) {
|
|
139
|
+
return '(unset)';
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
return JSON.stringify(value);
|
|
143
|
+
} catch {
|
|
144
|
+
return String(value);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Escape `]` so it can't terminate the markdown link early. */
|
|
149
|
+
protected escapeLinkText(text: string): string {
|
|
150
|
+
return text.replace(/\]/g, '\\]');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
onStop(): void {
|
|
154
|
+
this.toDispose.dispose();
|
|
155
|
+
this.statusBar.removeElement(SESSION_PREFERENCE_STATUS_BAR_ID);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -349,12 +349,39 @@ export abstract class PreferenceLeafNodeRenderer<ValueType extends JSONValue, In
|
|
|
349
349
|
headlineWrapper.removeChild(currentFirstChild);
|
|
350
350
|
}
|
|
351
351
|
|
|
352
|
-
const
|
|
353
|
-
if (
|
|
354
|
-
this.compareOtherModifiedScopes(headlineWrapper,
|
|
352
|
+
const existingModifiedScopes = headlineWrapper.querySelector('.preference-modified-scopes-suffix') as HTMLElement | null;
|
|
353
|
+
if (existingModifiedScopes) {
|
|
354
|
+
this.compareOtherModifiedScopes(headlineWrapper, existingModifiedScopes);
|
|
355
355
|
} else {
|
|
356
356
|
this.createOtherModifiedScopes(headlineWrapper);
|
|
357
357
|
}
|
|
358
|
+
this.updateSessionOverrideState();
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Adds (or removes) a subtle "Overridden by session" suffix in the headline when a
|
|
363
|
+
* session override (set via `--session-preference`) is active for this preference.
|
|
364
|
+
* Styled like the existing "Modified in:" suffix so it reads as a hint, not a warning.
|
|
365
|
+
* Editing remains enabled; writing a new value clears the session override via
|
|
366
|
+
* `PreferenceServiceImpl.evictSessionOverride`.
|
|
367
|
+
*/
|
|
368
|
+
protected updateSessionOverrideState(): void {
|
|
369
|
+
const { headlineWrapper } = this;
|
|
370
|
+
// Defensive: remove any leftover marker from earlier prominent styling.
|
|
371
|
+
headlineWrapper.querySelector('.preference-session-override-marker')?.remove();
|
|
372
|
+
|
|
373
|
+
const hasSessionOverride = this.inspection?.sessionValue !== undefined;
|
|
374
|
+
const existingSuffix = headlineWrapper.querySelector('.preference-session-suffix') as HTMLElement | null;
|
|
375
|
+
if (hasSessionOverride && !existingSuffix) {
|
|
376
|
+
const suffix = document.createElement('i');
|
|
377
|
+
suffix.classList.add('preference-leaf-headline-suffix', 'preference-session-suffix');
|
|
378
|
+
suffix.textContent = nls.localize('theia/preferences/sessionOverride/suffix', 'Overridden by session');
|
|
379
|
+
suffix.title = nls.localize('theia/preferences/sessionOverride/suffixTooltip',
|
|
380
|
+
'This preference has a session value set via --session-preference. Editing it here clears the session override for the rest of this session.');
|
|
381
|
+
headlineWrapper.appendChild(suffix);
|
|
382
|
+
} else if (!hasSessionOverride && existingSuffix) {
|
|
383
|
+
existingSuffix.remove();
|
|
384
|
+
}
|
|
358
385
|
}
|
|
359
386
|
|
|
360
387
|
protected compareOtherModifiedScopes(headlineWrapper: HTMLDivElement, currentSuffix: HTMLElement): void {
|
|
@@ -389,8 +416,14 @@ export abstract class PreferenceLeafNodeRenderer<ValueType extends JSONValue, In
|
|
|
389
416
|
const modifiedScopes = this.getModifiedScopesAsStrings();
|
|
390
417
|
if (modifiedScopes.length !== 0) {
|
|
391
418
|
const wrapper = document.createElement('i');
|
|
392
|
-
wrapper.classList.add('preference-leaf-headline-suffix');
|
|
393
|
-
|
|
419
|
+
wrapper.classList.add('preference-leaf-headline-suffix', 'preference-modified-scopes-suffix');
|
|
420
|
+
// The session suffix, if present, is always inserted after the modified-scopes suffix.
|
|
421
|
+
const sessionSuffix = headlineWrapper.querySelector('.preference-session-suffix');
|
|
422
|
+
if (sessionSuffix) {
|
|
423
|
+
headlineWrapper.insertBefore(wrapper, sessionSuffix);
|
|
424
|
+
} else {
|
|
425
|
+
headlineWrapper.appendChild(wrapper);
|
|
426
|
+
}
|
|
394
427
|
|
|
395
428
|
const messagePrefix = this.getModifiedMessagePrefix();
|
|
396
429
|
const messageWrapper = document.createElement('span');
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import { ContainerModule } from '@theia/core/shared/inversify';
|
|
18
18
|
import { CliContribution } from '@theia/core/lib/node/cli';
|
|
19
|
+
import { RemoteCliContribution } from '@theia/core/lib/node/remote/remote-cli-contribution';
|
|
19
20
|
import { PreferenceCliContribution } from './preference-cli-contribution';
|
|
20
21
|
import { ConnectionContainerModule } from '@theia/core/lib/node/messaging/connection-container-module';
|
|
21
22
|
import { CliPreferences, CliPreferencesPath } from '../common/cli-preferences';
|
|
@@ -35,6 +36,7 @@ export default new ContainerModule(bind => {
|
|
|
35
36
|
bind(PreferenceCliContribution).toSelf().inSingletonScope();
|
|
36
37
|
bind(CliPreferences).toService(PreferenceCliContribution);
|
|
37
38
|
bind(CliContribution).toService(PreferenceCliContribution);
|
|
39
|
+
bind(RemoteCliContribution).toService(PreferenceCliContribution);
|
|
38
40
|
bind(JSONCEditor).toSelf().inSingletonScope();
|
|
39
41
|
|
|
40
42
|
bind(PreferenceStorageFactory).toFactory(({ container }) => (uri: URI, scope: PreferenceScope) => new BackendPreferenceStorage(
|