All files / app/assets/javascripts/ide utils.js

93.33% Statements 56/60
74.46% Branches 35/47
91.3% Functions 21/23
94.23% Lines 49/52

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170            23634x   101x 101x 8080x   101x 8080x   101x 8080x     101x       106x           104x           102x   102x                     108x   312x 106x     27x   27x     27x     101x           101x 27x   45x       885x   885x   885x 885x 885x       73x               73x 73x     101x     15x       85x 85x   85x 85x 124x   124x 124x 124x   124x     85x       8x       6x                                       31x 31x 31x       101x                                  
import { flatten, isString } from 'lodash';
import { languages } from 'monaco-editor';
import { setDiagnosticsOptions as yamlDiagnosticsOptions } from 'monaco-yaml';
import { performanceMarkAndMeasure } from '~/performance/utils';
import { SIDE_LEFT, SIDE_RIGHT } from './constants';
 
const toLowerCase = (x) => x.toLowerCase();
 
const monacoLanguages = languages.getLanguages();
const monacoExtensions = new Set(
  flatten(monacoLanguages.map((lang) => lang.extensions?.map(toLowerCase) || [])),
);
const monacoMimetypes = new Set(
  flatten(monacoLanguages.map((lang) => lang.mimetypes?.map(toLowerCase) || [])),
);
const monacoFilenames = new Set(
  flatten(monacoLanguages.map((lang) => lang.filenames?.map(toLowerCase) || [])),
);
 
const KNOWN_TYPES = [
  {
    isText: false,
    isMatch(mimeType) {
      return mimeType.toLowerCase().includes('image/');
    },
  },
  {
    isText: true,
    isMatch(mimeType) {
      return mimeType.toLowerCase().includes('text/');
    },
  },
  {
    isText: true,
    isMatch(mimeType, fileName) {
      const fileExtension = fileName.includes('.') ? `.${fileName.split('.').pop()}` : '';
 
      return (
        monacoExtensions.has(fileExtension.toLowerCase()) ||
        monacoMimetypes.has(mimeType.toLowerCase()) ||
        monacoFilenames.has(fileName.toLowerCase())
      );
    },
  },
];
 
export function isTextFile({ name, raw, binary, content, mimeType = '' }) {
  // some file objects already have a `binary` property set on them. If so, use it first
  if (typeof binary === 'boolean') return !binary;
 
  const knownType = KNOWN_TYPES.find((type) => type.isMatch(mimeType, name));
  if (knownType) return knownType.isText;
 
  // does the string contain ascii characters only (ranges from space to tilde, tabs and new lines)
  const asciiRegex = /^[ -~\t\n\r]+$/;
 
  const fileContents = raw || content;
 
  // for unknown types, determine the type by evaluating the file contents
  return isString(fileContents) && (fileContents === '' || asciiRegex.test(fileContents));
}
 
export const createPathWithExt = (p) => {
  const ext = p.lastIndexOf('.') >= 0 ? p.substring(p.lastIndexOf('.') + 1) : '';
 
  return `${p.substring(1, p.lastIndexOf('.') + 1 || p.length)}${ext || '.js'}`;
};
 
export const trimPathComponents = (path) =>
  path
    .split('/')
    .map((s) => s.trim())
    .join('/');
 
export function registerLanguages(def, ...defs) {
  defs.forEach((lang) => registerLanguages(lang));
 
  const languageId = def.id;
 
  languages.register(def);
  languages.setMonarchTokensProvider(languageId, def.language);
  languages.setLanguageConfiguration(languageId, def.conf);
}
 
export function registerSchema(schema, options = {}) {
  const defaultOptions = {
    validate: true,
    enableSchemaRequest: true,
    hover: true,
    completion: true,
    schemas: [schema],
    ...options,
  };
  languages.json.jsonDefaults.setDiagnosticsOptions(defaultOptions);
  yamlDiagnosticsOptions(defaultOptions);
}
 
export const otherSide = (side) => (side === SIDE_RIGHT ? SIDE_LEFT : SIDE_RIGHT);
 
export function trimTrailingWhitespace(content) {
  return content.replace(/[^\S\r\n]+$/gm, '');
}
 
export function getPathParents(path, maxDepth = Infinity) {
  const pathComponents = path.split('/');
  const paths = [];
 
  let depth = 0;
  while (pathComponents.length && depth < maxDepth) {
    pathComponents.pop();
 
    let parentPath = pathComponents.join('/');
    Iif (parentPath.startsWith('/')) parentPath = parentPath.slice(1);
    if (parentPath) paths.push(parentPath);
 
    depth += 1;
  }
 
  return paths;
}
 
export function getPathParent(path) {
  return getPathParents(path, 1)[0];
}
 
export function getFileEOL(content = '') {
  return content.includes('\r\n') ? 'CRLF' : 'LF';
}
 
/**
 * Adds or increments the numeric suffix to a filename/branch name.
 * Retains underscore or dash before the numeric suffix if it already exists.
 *
 * Examples:
 *  hello -> hello-1
 *  hello-2425 -> hello-2425
 *  hello.md -> hello-1.md
 *  hello_2.md -> hello_3.md
 *  hello_ -> hello_1
 *  main-patch-22432 -> main-patch-22433
 *  patch_332 -> patch_333
 *
 * @param {string} filename File name or branch name
 * @param {number} [randomize] Should randomize the numeric suffix instead of auto-incrementing?
 */
export function addNumericSuffix(filename, randomize = false) {
  return filename.replace(/([ _-]?)(\d*)(\..+?$|$)/, (_, before, number, after) => {
    const n = randomize ? Math.random().toString().substring(2, 7).slice(-5) : Number(number) + 1;
    return `${before || '-'}${n}${after}`;
  });
}
 
export const measurePerformance = (
  mark,
  measureName,
  measureStart = undefined,
  measureEnd = mark,
) => {
  performanceMarkAndMeasure({
    mark,
    measures: [
      {
        name: measureName,
        start: measureStart,
        end: measureEnd,
      },
    ],
  });
};