Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/common/services/getIconClasses.ts
3294 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 { Schemas } from '../../../base/common/network.js';
7
import { DataUri } from '../../../base/common/resources.js';
8
import { URI, URI as uri } from '../../../base/common/uri.js';
9
import { PLAINTEXT_LANGUAGE_ID } from '../languages/modesRegistry.js';
10
import { ILanguageService } from '../languages/language.js';
11
import { IModelService } from './model.js';
12
import { FileKind } from '../../../platform/files/common/files.js';
13
import { ThemeIcon } from '../../../base/common/themables.js';
14
15
const fileIconDirectoryRegex = /(?:\/|^)(?:([^\/]+)\/)?([^\/]+)$/;
16
17
export function getIconClasses(modelService: IModelService, languageService: ILanguageService, resource: uri | undefined, fileKind?: FileKind, icon?: ThemeIcon | URI): string[] {
18
if (ThemeIcon.isThemeIcon(icon)) {
19
return [`codicon-${icon.id}`, 'predefined-file-icon'];
20
}
21
22
if (URI.isUri(icon)) {
23
return [];
24
}
25
26
// we always set these base classes even if we do not have a path
27
const classes = fileKind === FileKind.ROOT_FOLDER ? ['rootfolder-icon'] : fileKind === FileKind.FOLDER ? ['folder-icon'] : ['file-icon'];
28
if (resource) {
29
30
// Get the path and name of the resource. For data-URIs, we need to parse specially
31
let name: string | undefined;
32
if (resource.scheme === Schemas.data) {
33
const metadata = DataUri.parseMetaData(resource);
34
name = metadata.get(DataUri.META_DATA_LABEL);
35
} else {
36
const match = resource.path.match(fileIconDirectoryRegex);
37
if (match) {
38
name = fileIconSelectorEscape(match[2].toLowerCase());
39
if (match[1]) {
40
classes.push(`${fileIconSelectorEscape(match[1].toLowerCase())}-name-dir-icon`); // parent directory
41
}
42
43
} else {
44
name = fileIconSelectorEscape(resource.authority.toLowerCase());
45
}
46
}
47
48
// Root Folders
49
if (fileKind === FileKind.ROOT_FOLDER) {
50
classes.push(`${name}-root-name-folder-icon`);
51
}
52
53
// Folders
54
else if (fileKind === FileKind.FOLDER) {
55
classes.push(`${name}-name-folder-icon`);
56
}
57
58
// Files
59
else {
60
61
// Name & Extension(s)
62
if (name) {
63
classes.push(`${name}-name-file-icon`);
64
classes.push(`name-file-icon`); // extra segment to increase file-name score
65
// Avoid doing an explosive combination of extensions for very long filenames
66
// (most file systems do not allow files > 255 length) with lots of `.` characters
67
// https://github.com/microsoft/vscode/issues/116199
68
if (name.length <= 255) {
69
const dotSegments = name.split('.');
70
for (let i = 1; i < dotSegments.length; i++) {
71
classes.push(`${dotSegments.slice(i).join('.')}-ext-file-icon`); // add each combination of all found extensions if more than one
72
}
73
}
74
classes.push(`ext-file-icon`); // extra segment to increase file-ext score
75
}
76
77
// Detected Mode
78
const detectedLanguageId = detectLanguageId(modelService, languageService, resource);
79
if (detectedLanguageId) {
80
classes.push(`${fileIconSelectorEscape(detectedLanguageId)}-lang-file-icon`);
81
}
82
}
83
}
84
return classes;
85
}
86
87
export function getIconClassesForLanguageId(languageId: string): string[] {
88
return ['file-icon', `${fileIconSelectorEscape(languageId)}-lang-file-icon`];
89
}
90
91
function detectLanguageId(modelService: IModelService, languageService: ILanguageService, resource: uri): string | null {
92
if (!resource) {
93
return null; // we need a resource at least
94
}
95
96
let languageId: string | null = null;
97
98
// Data URI: check for encoded metadata
99
if (resource.scheme === Schemas.data) {
100
const metadata = DataUri.parseMetaData(resource);
101
const mime = metadata.get(DataUri.META_DATA_MIME);
102
103
if (mime) {
104
languageId = languageService.getLanguageIdByMimeType(mime);
105
}
106
}
107
108
// Any other URI: check for model if existing
109
else {
110
const model = modelService.getModel(resource);
111
if (model) {
112
languageId = model.getLanguageId();
113
}
114
}
115
116
// only take if the language id is specific (aka no just plain text)
117
if (languageId && languageId !== PLAINTEXT_LANGUAGE_ID) {
118
return languageId;
119
}
120
121
// otherwise fallback to path based detection
122
return languageService.guessLanguageIdByFilepathOrFirstLine(resource);
123
}
124
125
export function fileIconSelectorEscape(str: string): string {
126
return str.replace(/[\s]/g, '/'); // HTML class names can not contain certain whitespace characters (https://dom.spec.whatwg.org/#interface-domtokenlist), use / instead, which doesn't exist in file names.
127
}
128
129