getIconClasses.js 3.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  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. import { Schemas } from '../../../base/common/network.js';
  6. import { DataUri, basenameOrAuthority } from '../../../base/common/resources.js';
  7. import { PLAINTEXT_MODE_ID } from '../modes/modesRegistry.js';
  8. import { FileKind } from '../../../platform/files/common/files.js';
  9. export function getIconClasses(modelService, modeService, resource, fileKind) {
  10. // we always set these base classes even if we do not have a path
  11. const classes = fileKind === FileKind.ROOT_FOLDER ? ['rootfolder-icon'] : fileKind === FileKind.FOLDER ? ['folder-icon'] : ['file-icon'];
  12. if (resource) {
  13. // Get the path and name of the resource. For data-URIs, we need to parse specially
  14. let name;
  15. if (resource.scheme === Schemas.data) {
  16. const metadata = DataUri.parseMetaData(resource);
  17. name = metadata.get(DataUri.META_DATA_LABEL);
  18. }
  19. else {
  20. name = cssEscape(basenameOrAuthority(resource).toLowerCase());
  21. }
  22. // Folders
  23. if (fileKind === FileKind.FOLDER) {
  24. classes.push(`${name}-name-folder-icon`);
  25. }
  26. // Files
  27. else {
  28. // Name & Extension(s)
  29. if (name) {
  30. classes.push(`${name}-name-file-icon`);
  31. // Avoid doing an explosive combination of extensions for very long filenames
  32. // (most file systems do not allow files > 255 length) with lots of `.` characters
  33. // https://github.com/microsoft/vscode/issues/116199
  34. if (name.length <= 255) {
  35. const dotSegments = name.split('.');
  36. for (let i = 1; i < dotSegments.length; i++) {
  37. classes.push(`${dotSegments.slice(i).join('.')}-ext-file-icon`); // add each combination of all found extensions if more than one
  38. }
  39. }
  40. classes.push(`ext-file-icon`); // extra segment to increase file-ext score
  41. }
  42. // Detected Mode
  43. const detectedModeId = detectModeId(modelService, modeService, resource);
  44. if (detectedModeId) {
  45. classes.push(`${cssEscape(detectedModeId)}-lang-file-icon`);
  46. }
  47. }
  48. }
  49. return classes;
  50. }
  51. function detectModeId(modelService, modeService, resource) {
  52. if (!resource) {
  53. return null; // we need a resource at least
  54. }
  55. let modeId = null;
  56. // Data URI: check for encoded metadata
  57. if (resource.scheme === Schemas.data) {
  58. const metadata = DataUri.parseMetaData(resource);
  59. const mime = metadata.get(DataUri.META_DATA_MIME);
  60. if (mime) {
  61. modeId = modeService.getModeId(mime);
  62. }
  63. }
  64. // Any other URI: check for model if existing
  65. else {
  66. const model = modelService.getModel(resource);
  67. if (model) {
  68. modeId = model.getLanguageId();
  69. }
  70. }
  71. // only take if the mode is specific (aka no just plain text)
  72. if (modeId && modeId !== PLAINTEXT_MODE_ID) {
  73. return modeId;
  74. }
  75. // otherwise fallback to path based detection
  76. return modeService.getModeIdByFilepathOrFirstLine(resource);
  77. }
  78. export function cssEscape(str) {
  79. return str.replace(/[\11\12\14\15\40]/g, '/'); // HTML class names can not contain certain whitespace characters, use / instead, which doesn't exist in file names.
  80. }