Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/files/common/files.ts
3296 views
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 { URI } from '../../../../base/common/uri.js';
7
import { IEditorOptions } from '../../../../editor/common/config/editorOptions.js';
8
import { IWorkbenchEditorConfiguration, IEditorIdentifier, EditorResourceAccessor, SideBySideEditor } from '../../../common/editor.js';
9
import { EditorInput } from '../../../common/editor/editorInput.js';
10
import { IFilesConfiguration as PlatformIFilesConfiguration, FileChangeType, IFileService } from '../../../../platform/files/common/files.js';
11
import { ContextKeyExpr, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js';
12
import { ITextModelContentProvider } from '../../../../editor/common/services/resolverService.js';
13
import { Disposable, DisposableStore, MutableDisposable } from '../../../../base/common/lifecycle.js';
14
import { ITextModel } from '../../../../editor/common/model.js';
15
import { IModelService } from '../../../../editor/common/services/model.js';
16
import { ILanguageService, ILanguageSelection } from '../../../../editor/common/languages/language.js';
17
import { ITextFileService } from '../../../services/textfile/common/textfiles.js';
18
import { InputFocusedContextKey } from '../../../../platform/contextkey/common/contextkeys.js';
19
import { IEditorGroup } from '../../../services/editor/common/editorGroupsService.js';
20
import { Event } from '../../../../base/common/event.js';
21
import { ITextEditorOptions } from '../../../../platform/editor/common/editor.js';
22
import { IEditorService } from '../../../services/editor/common/editorService.js';
23
import { localize } from '../../../../nls.js';
24
import { IExpression } from '../../../../base/common/glob.js';
25
26
/**
27
* Explorer viewlet id.
28
*/
29
export const VIEWLET_ID = 'workbench.view.explorer';
30
31
/**
32
* Explorer file view id.
33
*/
34
export const VIEW_ID = 'workbench.explorer.fileView';
35
36
/**
37
* Context Keys to use with keybindings for the Explorer and Open Editors view
38
*/
39
export const ExplorerViewletVisibleContext = new RawContextKey<boolean>('explorerViewletVisible', true, { type: 'boolean', description: localize('explorerViewletVisible', "True when the EXPLORER viewlet is visible.") });
40
export const FoldersViewVisibleContext = new RawContextKey<boolean>('foldersViewVisible', true, { type: 'boolean', description: localize('foldersViewVisible', "True when the FOLDERS view (the file tree within the explorer view container) is visible.") });
41
export const ExplorerFolderContext = new RawContextKey<boolean>('explorerResourceIsFolder', false, { type: 'boolean', description: localize('explorerResourceIsFolder', "True when the focused item in the EXPLORER is a folder.") });
42
export const ExplorerResourceReadonlyContext = new RawContextKey<boolean>('explorerResourceReadonly', false, { type: 'boolean', description: localize('explorerResourceReadonly', "True when the focused item in the EXPLORER is read-only.") });
43
export const ExplorerResourceWritableContext = ExplorerResourceReadonlyContext.toNegated();
44
export const ExplorerResourceParentReadOnlyContext = new RawContextKey<boolean>('explorerResourceParentReadonly', false, { type: 'boolean', description: localize('explorerResourceParentReadonly', "True when the focused item in the EXPLORER's parent is read-only.") });
45
46
/**
47
* Comma separated list of editor ids that can be used for the selected explorer resource.
48
*/
49
export const ExplorerResourceAvailableEditorIdsContext = new RawContextKey<string>('explorerResourceAvailableEditorIds', '');
50
export const ExplorerRootContext = new RawContextKey<boolean>('explorerResourceIsRoot', false, { type: 'boolean', description: localize('explorerResourceIsRoot', "True when the focused item in the EXPLORER is a root folder.") });
51
export const ExplorerResourceCut = new RawContextKey<boolean>('explorerResourceCut', false, { type: 'boolean', description: localize('explorerResourceCut', "True when an item in the EXPLORER has been cut for cut and paste.") });
52
export const ExplorerResourceMoveableToTrash = new RawContextKey<boolean>('explorerResourceMoveableToTrash', false, { type: 'boolean', description: localize('explorerResourceMoveableToTrash', "True when the focused item in the EXPLORER can be moved to trash.") });
53
export const FilesExplorerFocusedContext = new RawContextKey<boolean>('filesExplorerFocus', true, { type: 'boolean', description: localize('filesExplorerFocus', "True when the focus is inside the EXPLORER view.") });
54
export const OpenEditorsFocusedContext = new RawContextKey<boolean>('openEditorsFocus', true, { type: 'boolean', description: localize('openEditorsFocus', "True when the focus is inside the OPEN EDITORS view.") });
55
export const ExplorerFocusedContext = new RawContextKey<boolean>('explorerViewletFocus', true, { type: 'boolean', description: localize('explorerViewletFocus', "True when the focus is inside the EXPLORER viewlet.") });
56
export const ExplorerFindProviderActive = new RawContextKey<boolean>('explorerFindProviderActive', false, { type: 'boolean', description: localize('explorerFindProviderActive', "True when the explorer tree is using the explorer find provider.") });
57
58
// compressed nodes
59
export const ExplorerCompressedFocusContext = new RawContextKey<boolean>('explorerViewletCompressedFocus', true, { type: 'boolean', description: localize('explorerViewletCompressedFocus', "True when the focused item in the EXPLORER view is a compact item.") });
60
export const ExplorerCompressedFirstFocusContext = new RawContextKey<boolean>('explorerViewletCompressedFirstFocus', true, { type: 'boolean', description: localize('explorerViewletCompressedFirstFocus', "True when the focus is inside a compact item's first part in the EXPLORER view.") });
61
export const ExplorerCompressedLastFocusContext = new RawContextKey<boolean>('explorerViewletCompressedLastFocus', true, { type: 'boolean', description: localize('explorerViewletCompressedLastFocus', "True when the focus is inside a compact item's last part in the EXPLORER view.") });
62
63
export const ViewHasSomeCollapsibleRootItemContext = new RawContextKey<boolean>('viewHasSomeCollapsibleItem', false, { type: 'boolean', description: localize('viewHasSomeCollapsibleItem', "True when a workspace in the EXPLORER view has some collapsible root child.") });
64
65
export const FilesExplorerFocusCondition = ContextKeyExpr.and(FoldersViewVisibleContext, FilesExplorerFocusedContext, ContextKeyExpr.not(InputFocusedContextKey));
66
export const ExplorerFocusCondition = ContextKeyExpr.and(FoldersViewVisibleContext, ExplorerFocusedContext, ContextKeyExpr.not(InputFocusedContextKey));
67
68
/**
69
* Text file editor id.
70
*/
71
export const TEXT_FILE_EDITOR_ID = 'workbench.editors.files.textFileEditor';
72
73
/**
74
* File editor input id.
75
*/
76
export const FILE_EDITOR_INPUT_ID = 'workbench.editors.files.fileEditorInput';
77
78
/**
79
* Binary file editor id.
80
*/
81
export const BINARY_FILE_EDITOR_ID = 'workbench.editors.files.binaryFileEditor';
82
83
/**
84
* Language identifier for binary files opened as text.
85
*/
86
export const BINARY_TEXT_FILE_MODE = 'code-text-binary';
87
88
export interface IFilesConfiguration extends PlatformIFilesConfiguration, IWorkbenchEditorConfiguration {
89
explorer: {
90
openEditors: {
91
visible: number;
92
sortOrder: 'editorOrder' | 'alphabetical' | 'fullPath';
93
};
94
autoReveal: boolean | 'focusNoScroll';
95
autoRevealExclude: IExpression;
96
enableDragAndDrop: boolean;
97
confirmDelete: boolean;
98
enableUndo: boolean;
99
confirmUndo: UndoConfirmLevel;
100
expandSingleFolderWorkspaces: boolean;
101
sortOrder: SortOrder;
102
sortOrderLexicographicOptions: LexicographicOptions;
103
sortOrderReverse: boolean;
104
decorations: {
105
colors: boolean;
106
badges: boolean;
107
};
108
incrementalNaming: 'simple' | 'smart' | 'disabled';
109
excludeGitIgnore: boolean;
110
fileNesting: {
111
enabled: boolean;
112
expand: boolean;
113
patterns: { [parent: string]: string };
114
};
115
autoOpenDroppedFile: boolean;
116
};
117
editor: IEditorOptions;
118
}
119
120
export interface IFileResource {
121
resource: URI;
122
isDirectory?: boolean;
123
}
124
125
export const enum SortOrder {
126
Default = 'default',
127
Mixed = 'mixed',
128
FilesFirst = 'filesFirst',
129
Type = 'type',
130
Modified = 'modified',
131
FoldersNestsFiles = 'foldersNestsFiles',
132
}
133
134
export const enum UndoConfirmLevel {
135
Verbose = 'verbose',
136
Default = 'default',
137
Light = 'light',
138
}
139
140
export const enum LexicographicOptions {
141
Default = 'default',
142
Upper = 'upper',
143
Lower = 'lower',
144
Unicode = 'unicode',
145
}
146
147
export interface ISortOrderConfiguration {
148
sortOrder: SortOrder;
149
lexicographicOptions: LexicographicOptions;
150
reverse: boolean;
151
}
152
153
export class TextFileContentProvider extends Disposable implements ITextModelContentProvider {
154
private readonly fileWatcherDisposable = this._register(new MutableDisposable());
155
156
constructor(
157
@ITextFileService private readonly textFileService: ITextFileService,
158
@IFileService private readonly fileService: IFileService,
159
@ILanguageService private readonly languageService: ILanguageService,
160
@IModelService private readonly modelService: IModelService
161
) {
162
super();
163
}
164
165
static async open(resource: URI, scheme: string, label: string, editorService: IEditorService, options?: ITextEditorOptions): Promise<void> {
166
await editorService.openEditor({
167
original: { resource: TextFileContentProvider.resourceToTextFile(scheme, resource) },
168
modified: { resource },
169
label,
170
options
171
});
172
}
173
174
private static resourceToTextFile(scheme: string, resource: URI): URI {
175
return resource.with({ scheme, query: JSON.stringify({ scheme: resource.scheme, query: resource.query }) });
176
}
177
178
private static textFileToResource(resource: URI): URI {
179
const { scheme, query } = JSON.parse(resource.query);
180
181
return resource.with({ scheme, query });
182
}
183
184
async provideTextContent(resource: URI): Promise<ITextModel | null> {
185
if (!resource.query) {
186
// We require the URI to use the `query` to transport the original scheme and query
187
// as done by `resourceToTextFile`
188
return null;
189
}
190
191
const savedFileResource = TextFileContentProvider.textFileToResource(resource);
192
193
// Make sure our text file is resolved up to date
194
const codeEditorModel = await this.resolveEditorModel(resource);
195
196
// Make sure to keep contents up to date when it changes
197
if (!this.fileWatcherDisposable.value) {
198
const disposables = new DisposableStore();
199
this.fileWatcherDisposable.value = disposables;
200
disposables.add(this.fileService.onDidFilesChange(changes => {
201
if (changes.contains(savedFileResource, FileChangeType.UPDATED)) {
202
this.resolveEditorModel(resource, false /* do not create if missing */); // update model when resource changes
203
}
204
}));
205
206
if (codeEditorModel) {
207
disposables.add(Event.once(codeEditorModel.onWillDispose)(() => this.fileWatcherDisposable.clear()));
208
}
209
}
210
211
return codeEditorModel;
212
}
213
214
private resolveEditorModel(resource: URI, createAsNeeded?: true): Promise<ITextModel>;
215
private resolveEditorModel(resource: URI, createAsNeeded?: boolean): Promise<ITextModel | null>;
216
private async resolveEditorModel(resource: URI, createAsNeeded: boolean = true): Promise<ITextModel | null> {
217
const savedFileResource = TextFileContentProvider.textFileToResource(resource);
218
219
const content = await this.textFileService.readStream(savedFileResource);
220
221
let codeEditorModel = this.modelService.getModel(resource);
222
if (codeEditorModel) {
223
this.modelService.updateModel(codeEditorModel, content.value);
224
} else if (createAsNeeded) {
225
const textFileModel = this.modelService.getModel(savedFileResource);
226
227
let languageSelector: ILanguageSelection;
228
if (textFileModel) {
229
languageSelector = this.languageService.createById(textFileModel.getLanguageId());
230
} else {
231
languageSelector = this.languageService.createByFilepathOrFirstLine(savedFileResource);
232
}
233
234
codeEditorModel = this.modelService.createModel(content.value, languageSelector, resource);
235
}
236
237
return codeEditorModel;
238
}
239
}
240
241
export class OpenEditor implements IEditorIdentifier {
242
243
private id: number;
244
private static COUNTER = 0;
245
246
constructor(private _editor: EditorInput, private _group: IEditorGroup) {
247
this.id = OpenEditor.COUNTER++;
248
}
249
250
get editor() {
251
return this._editor;
252
}
253
254
get group() {
255
return this._group;
256
}
257
258
get groupId() {
259
return this._group.id;
260
}
261
262
getId(): string {
263
return `openeditor:${this.groupId}:${this.id}`;
264
}
265
266
isPreview(): boolean {
267
return !this._group.isPinned(this.editor);
268
}
269
270
isSticky(): boolean {
271
return this._group.isSticky(this.editor);
272
}
273
274
getResource(): URI | undefined {
275
return EditorResourceAccessor.getOriginalUri(this.editor, { supportSideBySide: SideBySideEditor.PRIMARY });
276
}
277
}
278
279