Path: blob/main/src/vs/editor/common/services/getIconClasses.ts
3294 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Schemas } from '../../../base/common/network.js';6import { DataUri } from '../../../base/common/resources.js';7import { URI, URI as uri } from '../../../base/common/uri.js';8import { PLAINTEXT_LANGUAGE_ID } from '../languages/modesRegistry.js';9import { ILanguageService } from '../languages/language.js';10import { IModelService } from './model.js';11import { FileKind } from '../../../platform/files/common/files.js';12import { ThemeIcon } from '../../../base/common/themables.js';1314const fileIconDirectoryRegex = /(?:\/|^)(?:([^\/]+)\/)?([^\/]+)$/;1516export function getIconClasses(modelService: IModelService, languageService: ILanguageService, resource: uri | undefined, fileKind?: FileKind, icon?: ThemeIcon | URI): string[] {17if (ThemeIcon.isThemeIcon(icon)) {18return [`codicon-${icon.id}`, 'predefined-file-icon'];19}2021if (URI.isUri(icon)) {22return [];23}2425// we always set these base classes even if we do not have a path26const classes = fileKind === FileKind.ROOT_FOLDER ? ['rootfolder-icon'] : fileKind === FileKind.FOLDER ? ['folder-icon'] : ['file-icon'];27if (resource) {2829// Get the path and name of the resource. For data-URIs, we need to parse specially30let name: string | undefined;31if (resource.scheme === Schemas.data) {32const metadata = DataUri.parseMetaData(resource);33name = metadata.get(DataUri.META_DATA_LABEL);34} else {35const match = resource.path.match(fileIconDirectoryRegex);36if (match) {37name = fileIconSelectorEscape(match[2].toLowerCase());38if (match[1]) {39classes.push(`${fileIconSelectorEscape(match[1].toLowerCase())}-name-dir-icon`); // parent directory40}4142} else {43name = fileIconSelectorEscape(resource.authority.toLowerCase());44}45}4647// Root Folders48if (fileKind === FileKind.ROOT_FOLDER) {49classes.push(`${name}-root-name-folder-icon`);50}5152// Folders53else if (fileKind === FileKind.FOLDER) {54classes.push(`${name}-name-folder-icon`);55}5657// Files58else {5960// Name & Extension(s)61if (name) {62classes.push(`${name}-name-file-icon`);63classes.push(`name-file-icon`); // extra segment to increase file-name score64// Avoid doing an explosive combination of extensions for very long filenames65// (most file systems do not allow files > 255 length) with lots of `.` characters66// https://github.com/microsoft/vscode/issues/11619967if (name.length <= 255) {68const dotSegments = name.split('.');69for (let i = 1; i < dotSegments.length; i++) {70classes.push(`${dotSegments.slice(i).join('.')}-ext-file-icon`); // add each combination of all found extensions if more than one71}72}73classes.push(`ext-file-icon`); // extra segment to increase file-ext score74}7576// Detected Mode77const detectedLanguageId = detectLanguageId(modelService, languageService, resource);78if (detectedLanguageId) {79classes.push(`${fileIconSelectorEscape(detectedLanguageId)}-lang-file-icon`);80}81}82}83return classes;84}8586export function getIconClassesForLanguageId(languageId: string): string[] {87return ['file-icon', `${fileIconSelectorEscape(languageId)}-lang-file-icon`];88}8990function detectLanguageId(modelService: IModelService, languageService: ILanguageService, resource: uri): string | null {91if (!resource) {92return null; // we need a resource at least93}9495let languageId: string | null = null;9697// Data URI: check for encoded metadata98if (resource.scheme === Schemas.data) {99const metadata = DataUri.parseMetaData(resource);100const mime = metadata.get(DataUri.META_DATA_MIME);101102if (mime) {103languageId = languageService.getLanguageIdByMimeType(mime);104}105}106107// Any other URI: check for model if existing108else {109const model = modelService.getModel(resource);110if (model) {111languageId = model.getLanguageId();112}113}114115// only take if the language id is specific (aka no just plain text)116if (languageId && languageId !== PLAINTEXT_LANGUAGE_ID) {117return languageId;118}119120// otherwise fallback to path based detection121return languageService.guessLanguageIdByFilepathOrFirstLine(resource);122}123124export function fileIconSelectorEscape(str: string): string {125return 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.126}127128129