Skip to content

Commit 1a4e466

Browse files
alexr00bpaseroalexdima
authored
Set a read-only message from an extension (microsoft#185216)
* wip * Allow extensions to provide a readonly message Part of microsoft#166971 * Address feedback * Further address feedback * Fix some nits * Add test * Improve tests and respond to feedback * Don't render editor.readOnlyMessage in the settings UI * No need to validate the IMarkdownString --------- Co-authored-by: Benjamin Pasero <[email protected]> Co-authored-by: Alex Dima <[email protected]>
1 parent 76cc1fc commit 1a4e466

File tree

31 files changed

+349
-151
lines changed

31 files changed

+349
-151
lines changed

extensions/vscode-api-tests/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"notebookMime",
2727
"portsAttributes",
2828
"quickPickSortByLabel",
29+
"readonlyMessage",
2930
"resolvers",
3031
"saveEditor",
3132
"scmActionButton",
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import * as assert from 'assert';
7+
import * as vscode from 'vscode';
8+
import { TestFS } from '../memfs';
9+
import { assertNoRpc, closeAllEditors } from '../utils';
10+
11+
suite('vscode API - file system', () => {
12+
13+
teardown(async function () {
14+
assertNoRpc();
15+
await closeAllEditors();
16+
});
17+
18+
test('readonly file system - boolean', async function () {
19+
const fs = new TestFS('this-fs', false);
20+
const reg = vscode.workspace.registerFileSystemProvider(fs.scheme, fs, { isReadonly: true });
21+
let error: any | undefined;
22+
try {
23+
await vscode.workspace.fs.writeFile(vscode.Uri.parse('this-fs:/foo.txt'), Buffer.from('Hello World'));
24+
} catch (e) {
25+
error = e;
26+
}
27+
assert.strictEqual(vscode.workspace.fs.isWritableFileSystem('this-fs'), false);
28+
assert.strictEqual(error instanceof vscode.FileSystemError, true);
29+
const fileError: vscode.FileSystemError = error;
30+
assert.strictEqual(fileError.code, 'NoPermissions');
31+
reg.dispose();
32+
});
33+
34+
test('readonly file system - markdown', async function () {
35+
const fs = new TestFS('this-fs', false);
36+
const reg = vscode.workspace.registerFileSystemProvider(fs.scheme, fs, { isReadonly: new vscode.MarkdownString('This file is readonly.') });
37+
let error: any | undefined;
38+
try {
39+
await vscode.workspace.fs.writeFile(vscode.Uri.parse('this-fs:/foo.txt'), Buffer.from('Hello World'));
40+
} catch (e) {
41+
error = e;
42+
}
43+
assert.strictEqual(vscode.workspace.fs.isWritableFileSystem('this-fs'), false);
44+
assert.strictEqual(error instanceof vscode.FileSystemError, true);
45+
const fileError: vscode.FileSystemError = error;
46+
assert.strictEqual(fileError.code, 'NoPermissions');
47+
reg.dispose();
48+
});
49+
50+
test('writeable file system', async function () {
51+
const fs = new TestFS('this-fs', false);
52+
const reg = vscode.workspace.registerFileSystemProvider(fs.scheme, fs);
53+
let error: any | undefined;
54+
try {
55+
await vscode.workspace.fs.writeFile(vscode.Uri.parse('this-fs:/foo.txt'), Buffer.from('Hello World'));
56+
} catch (e) {
57+
error = e;
58+
}
59+
assert.strictEqual(vscode.workspace.fs.isWritableFileSystem('this-fs'), true);
60+
assert.strictEqual(error, undefined);
61+
reg.dispose();
62+
});
63+
});

src/vs/editor/common/config/editorOptions.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import * as arrays from 'vs/base/common/arrays';
1616
import * as objects from 'vs/base/common/objects';
1717
import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/core/textModelDefaults';
1818
import { IDocumentDiffProvider } from 'vs/editor/common/diff/documentDiffProvider';
19+
import { IMarkdownString } from 'vs/base/common/htmlContent';
1920

2021
//#region typed options
2122

@@ -151,6 +152,10 @@ export interface IEditorOptions {
151152
* Defaults to false.
152153
*/
153154
readOnly?: boolean;
155+
/**
156+
* The message to display when the editor is readonly.
157+
*/
158+
readOnlyMessage?: IMarkdownString;
154159
/**
155160
* Should the textarea used for input use the DOM `readonly` attribute.
156161
* Defaults to false.
@@ -3459,6 +3464,30 @@ class EditorRulers extends BaseEditorOption<EditorOption.rulers, (number | IRule
34593464

34603465
//#endregion
34613466

3467+
//#region readonly
3468+
3469+
/**
3470+
* Configuration options for readonly message
3471+
*/
3472+
class ReadonlyMessage extends BaseEditorOption<EditorOption.readOnlyMessage, IMarkdownString | undefined, IMarkdownString | undefined> {
3473+
constructor() {
3474+
const defaults = undefined;
3475+
3476+
super(
3477+
EditorOption.readOnlyMessage, 'readOnlyMessage', defaults
3478+
);
3479+
}
3480+
3481+
public validate(_input: any): IMarkdownString | undefined {
3482+
if (!_input || typeof _input !== 'object') {
3483+
return this.defaultValue;
3484+
}
3485+
return _input as IMarkdownString;
3486+
}
3487+
}
3488+
3489+
//#endregion
3490+
34623491
//#region scrollbar
34633492

34643493
/**
@@ -5024,6 +5053,7 @@ export const enum EditorOption {
50245053
quickSuggestions,
50255054
quickSuggestionsDelay,
50265055
readOnly,
5056+
readOnlyMessage,
50275057
renameOnType,
50285058
renderControlCharacters,
50295059
renderFinalNewline,
@@ -5530,6 +5560,7 @@ export const EditorOptions = {
55305560
readOnly: register(new EditorBooleanOption(
55315561
EditorOption.readOnly, 'readOnly', false,
55325562
)),
5563+
readOnlyMessage: register(new ReadonlyMessage()),
55335564
renameOnType: register(new EditorBooleanOption(
55345565
EditorOption.renameOnType, 'renameOnType', false,
55355566
{ description: nls.localize('renameOnType', "Controls whether the editor auto renames on type."), markdownDeprecationMessage: nls.localize('renameOnTypeDeprecate', "Deprecated, use `editor.linkedEditing` instead.") }

src/vs/editor/common/services/resolverService.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6+
import { IMarkdownString } from 'vs/base/common/htmlContent';
67
import { IDisposable, IReference } from 'vs/base/common/lifecycle';
78
import { URI } from 'vs/base/common/uri';
89
import { ITextModel, ITextSnapshot } from 'vs/editor/common/model';
@@ -55,7 +56,7 @@ export interface ITextEditorModel extends IEditorModel {
5556
/**
5657
* Signals if this model is readonly or not.
5758
*/
58-
isReadonly(): boolean;
59+
isReadonly(): boolean | IMarkdownString;
5960

6061
/**
6162
* The language id of the text model if known.

src/vs/editor/common/standalone/standaloneEnums.ts

Lines changed: 57 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -262,61 +262,62 @@ export enum EditorOption {
262262
quickSuggestions = 86,
263263
quickSuggestionsDelay = 87,
264264
readOnly = 88,
265-
renameOnType = 89,
266-
renderControlCharacters = 90,
267-
renderFinalNewline = 91,
268-
renderLineHighlight = 92,
269-
renderLineHighlightOnlyWhenFocus = 93,
270-
renderValidationDecorations = 94,
271-
renderWhitespace = 95,
272-
revealHorizontalRightPadding = 96,
273-
roundedSelection = 97,
274-
rulers = 98,
275-
scrollbar = 99,
276-
scrollBeyondLastColumn = 100,
277-
scrollBeyondLastLine = 101,
278-
scrollPredominantAxis = 102,
279-
selectionClipboard = 103,
280-
selectionHighlight = 104,
281-
selectOnLineNumbers = 105,
282-
showFoldingControls = 106,
283-
showUnused = 107,
284-
snippetSuggestions = 108,
285-
smartSelect = 109,
286-
smoothScrolling = 110,
287-
stickyScroll = 111,
288-
stickyTabStops = 112,
289-
stopRenderingLineAfter = 113,
290-
suggest = 114,
291-
suggestFontSize = 115,
292-
suggestLineHeight = 116,
293-
suggestOnTriggerCharacters = 117,
294-
suggestSelection = 118,
295-
tabCompletion = 119,
296-
tabIndex = 120,
297-
unicodeHighlighting = 121,
298-
unusualLineTerminators = 122,
299-
useShadowDOM = 123,
300-
useTabStops = 124,
301-
wordBreak = 125,
302-
wordSeparators = 126,
303-
wordWrap = 127,
304-
wordWrapBreakAfterCharacters = 128,
305-
wordWrapBreakBeforeCharacters = 129,
306-
wordWrapColumn = 130,
307-
wordWrapOverride1 = 131,
308-
wordWrapOverride2 = 132,
309-
wrappingIndent = 133,
310-
wrappingStrategy = 134,
311-
showDeprecated = 135,
312-
inlayHints = 136,
313-
editorClassName = 137,
314-
pixelRatio = 138,
315-
tabFocusMode = 139,
316-
layoutInfo = 140,
317-
wrappingInfo = 141,
318-
defaultColorDecorators = 142,
319-
colorDecoratorsActivatedOn = 143
265+
readOnlyMessage = 89,
266+
renameOnType = 90,
267+
renderControlCharacters = 91,
268+
renderFinalNewline = 92,
269+
renderLineHighlight = 93,
270+
renderLineHighlightOnlyWhenFocus = 94,
271+
renderValidationDecorations = 95,
272+
renderWhitespace = 96,
273+
revealHorizontalRightPadding = 97,
274+
roundedSelection = 98,
275+
rulers = 99,
276+
scrollbar = 100,
277+
scrollBeyondLastColumn = 101,
278+
scrollBeyondLastLine = 102,
279+
scrollPredominantAxis = 103,
280+
selectionClipboard = 104,
281+
selectionHighlight = 105,
282+
selectOnLineNumbers = 106,
283+
showFoldingControls = 107,
284+
showUnused = 108,
285+
snippetSuggestions = 109,
286+
smartSelect = 110,
287+
smoothScrolling = 111,
288+
stickyScroll = 112,
289+
stickyTabStops = 113,
290+
stopRenderingLineAfter = 114,
291+
suggest = 115,
292+
suggestFontSize = 116,
293+
suggestLineHeight = 117,
294+
suggestOnTriggerCharacters = 118,
295+
suggestSelection = 119,
296+
tabCompletion = 120,
297+
tabIndex = 121,
298+
unicodeHighlighting = 122,
299+
unusualLineTerminators = 123,
300+
useShadowDOM = 124,
301+
useTabStops = 125,
302+
wordBreak = 126,
303+
wordSeparators = 127,
304+
wordWrap = 128,
305+
wordWrapBreakAfterCharacters = 129,
306+
wordWrapBreakBeforeCharacters = 130,
307+
wordWrapColumn = 131,
308+
wordWrapOverride1 = 132,
309+
wordWrapOverride2 = 133,
310+
wrappingIndent = 134,
311+
wrappingStrategy = 135,
312+
showDeprecated = 136,
313+
inlayHints = 137,
314+
editorClassName = 138,
315+
pixelRatio = 139,
316+
tabFocusMode = 140,
317+
layoutInfo = 141,
318+
wrappingInfo = 142,
319+
defaultColorDecorators = 143,
320+
colorDecoratorsActivatedOn = 144
320321
}
321322

322323
/**
@@ -920,4 +921,4 @@ export enum WrappingIndent {
920921
* DeepIndent => wrapped lines get +2 indentation toward the parent.
921922
*/
922923
DeepIndent = 3
923-
}
924+
}

src/vs/editor/contrib/message/browser/messageController.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@
3737
border: 1px solid var(--vscode-inputValidation-infoBorder);
3838
}
3939

40+
.monaco-editor .monaco-editor-overlaymessage .message p {
41+
margin-block: 0px;
42+
}
43+
44+
.monaco-editor .monaco-editor-overlaymessage .message a {
45+
font-weight: bold;
46+
color: var(--vscode-inputValidation-infoForeground);
47+
}
48+
4049
.monaco-editor.hc-black .monaco-editor-overlaymessage .message,
4150
.monaco-editor.hc-light .monaco-editor-overlaymessage .message {
4251
border-width: 2px;

0 commit comments

Comments
 (0)