All Downloads are FREE. Search and download functionalities are using the official Maven repository.

META-INF.dirigible.dev-tools.formatter.FormatterWorkerPool.js Maven / Gradle / Ivy

There is a newer version: 10.6.27
Show newest version
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import * as Common from '../common/common.js';

const MaxWorkers = 2;

/**
 * @unrestricted
 */
export class FormatterWorkerPool {
  constructor() {
    this._taskQueue = [];
    /** @type {!Map} */
    this._workerTasks = new Map();
  }

  /**
   * @return {!Common.Worker.WorkerWrapper}
   */
  _createWorker() {
    const worker = new Common.Worker.WorkerWrapper('formatter_worker_entrypoint');
    worker.onmessage = this._onWorkerMessage.bind(this, worker);
    worker.onerror = this._onWorkerError.bind(this, worker);
    return worker;
  }

  _processNextTask() {
    if (!this._taskQueue.length) {
      return;
    }

    let freeWorker = [...this._workerTasks.keys()].find(worker => !this._workerTasks.get(worker));
    if (!freeWorker && this._workerTasks.size < MaxWorkers) {
      freeWorker = this._createWorker();
    }
    if (!freeWorker) {
      return;
    }

    const task = this._taskQueue.shift();
    this._workerTasks.set(freeWorker, task);
    freeWorker.postMessage({method: task.method, params: task.params});
  }

  /**
   * @param {!Common.Worker.WorkerWrapper} worker
   * @param {!MessageEvent} event
   */
  _onWorkerMessage(worker, event) {
    const task = this._workerTasks.get(worker);
    if (task.isChunked && event.data && !event.data['isLastChunk']) {
      task.callback(event.data);
      return;
    }

    this._workerTasks.set(worker, null);
    this._processNextTask();
    task.callback(event.data ? event.data : null);
  }

  /**
   * @param {!Common.Worker.WorkerWrapper} worker
   * @param {!Event} event
   */
  _onWorkerError(worker, event) {
    console.error(event);
    const task = this._workerTasks.get(worker);
    worker.terminate();
    this._workerTasks.delete(worker);

    const newWorker = this._createWorker();
    this._workerTasks.set(newWorker, null);
    this._processNextTask();
    task.callback(null);
  }

  /**
   * @param {string} methodName
   * @param {!Object} params
   * @param {function(boolean, *)} callback
   */
  _runChunkedTask(methodName, params, callback) {
    const task = new Task(methodName, params, onData, true);
    this._taskQueue.push(task);
    this._processNextTask();

    /**
     * @param {?Object} data
     */
    function onData(data) {
      if (!data) {
        callback(true, null);
        return;
      }
      const isLastChunk = !!data['isLastChunk'];
      const chunk = data['chunk'];
      callback(isLastChunk, chunk);
    }
  }

  /**
   * @param {string} methodName
   * @param {!Object} params
   * @return {!Promise<*>}
   */
  _runTask(methodName, params) {
    let callback;
    const promise = new Promise(fulfill => callback = fulfill);
    const task = new Task(methodName, params, callback, false);
    this._taskQueue.push(task);
    this._processNextTask();
    return promise;
  }

  /**
   * @param {string} content
   * @return {!Promise<*>}
   */
  parseJSONRelaxed(content) {
    return this._runTask('parseJSONRelaxed', {content: content});
  }

  /**
   * @param {string} content
   * @return {!Promise>}
   */
  parseSCSS(content) {
    return this._runTask('parseSCSS', {content: content}).then(rules => rules || []);
  }

  /**
   * @param {string} mimeType
   * @param {string} content
   * @param {string} indentString
   * @return {!Promise}
   */
  format(mimeType, content, indentString) {
    const parameters = {mimeType: mimeType, content: content, indentString: indentString};
    return /** @type {!Promise} */ (this._runTask('format', parameters));
  }

  /**
   * @param {string} content
   * @return {!Promise>}
   */
  javaScriptIdentifiers(content) {
    return this._runTask('javaScriptIdentifiers', {content: content}).then(ids => ids || []);
  }

  /**
   * @param {string} content
   * @return {!Promise}
   */
  evaluatableJavaScriptSubstring(content) {
    return this._runTask('evaluatableJavaScriptSubstring', {content: content}).then(text => text || '');
  }

  /**
   * @param {string} content
   * @param {function(boolean, !Array)} callback
   */
  parseCSS(content, callback) {
    this._runChunkedTask('parseCSS', {content: content}, onDataChunk);

    /**
     * @param {boolean} isLastChunk
     * @param {*} data
     */
    function onDataChunk(isLastChunk, data) {
      const rules = /** @type {!Array} */ (data || []);
      callback(isLastChunk, rules);
    }
  }

  /**
   * @param {string} content
   * @param {function(boolean, !Array)} callback
   */
  javaScriptOutline(content, callback) {
    this._runChunkedTask('javaScriptOutline', {content: content}, onDataChunk);

    /**
     * @param {boolean} isLastChunk
     * @param {*} data
     */
    function onDataChunk(isLastChunk, data) {
      const items = /** @type {!Array.} */ (data || []);
      callback(isLastChunk, items);
    }
  }

  /**
   * @param {string} content
   * @param {string} mimeType
   * @param {function(boolean, !Array)} callback
   * @return {boolean}
   */
  outlineForMimetype(content, mimeType, callback) {
    switch (mimeType) {
      case 'text/html':
      case 'text/javascript':
        this.javaScriptOutline(content, javaScriptCallback);
        return true;
      case 'text/css':
        this.parseCSS(content, cssCallback);
        return true;
    }
    return false;

    /**
     * @param {boolean} isLastChunk
     * @param {!Array} items
     */
    function javaScriptCallback(isLastChunk, items) {
      callback(
          isLastChunk,
          items.map(item => ({line: item.line, column: item.column, title: item.name, subtitle: item.arguments})));
    }

    /**
     * @param {boolean} isLastChunk
     * @param {!Array} rules
     */
    function cssCallback(isLastChunk, rules) {
      callback(
          isLastChunk,
          rules.map(
              rule => ({line: rule.lineNumber, column: rule.columnNumber, title: rule.selectorText || rule.atRule})));
    }
  }

  /**
   * @param {string} content
   * @return {!Promise}
   */
  findLastExpression(content) {
    return /** @type {!Promise} */ (this._runTask('findLastExpression', {content}));
  }

  /**
   * @param {string} content
   * @return {!Promise}
   */
  findLastFunctionCall(content) {
    return /** @type {!Promise} */ (
        this._runTask('findLastFunctionCall', {content}));
  }

  /**
   * @param {string} content
   * @return {!Promise>}
   */
  argumentsList(content) {
    return /** @type {!Promise>} */ (this._runTask('argumentsList', {content}));
  }
}

/**
 * @unrestricted
 */
class Task {
  /**
   * @param {string} method
   * @param {!Object} params
   * @param {function(?MessageEvent)} callback
   * @param {boolean=} isChunked
   */
  constructor(method, params, callback, isChunked) {
    this.method = method;
    this.params = params;
    this.callback = callback;
    this.isChunked = isChunked;
  }
}

export class FormatResult {
  constructor() {
    /** @type {string} */
    this.content;
    /** @type {!FormatMapping} */
    this.mapping;
  }
}

// eslint-disable-next-line no-unused-vars
class JSOutlineItem {
  constructor() {
    /** @type {string} */
    this.name;
    /** @type {(string|undefined)} */
    this.arguments;
    /** @type {number} */
    this.line;
    /** @type {number} */
    this.column;
  }
}

// eslint-disable-next-line no-unused-vars
class CSSProperty {
  constructor() {
    /** @type {string} */
    this.name;
    /** @type {!TextRange} */
    this.nameRange;
    /** @type {string} */
    this.value;
    /** @type {!TextRange} */
    this.valueRange;
    /** @type {!TextRange} */
    this.range;
    /** @type {(boolean|undefined)} */
    this.disabled;
  }
}

// eslint-disable-next-line no-unused-vars
class CSSStyleRule {
  constructor() {
    /** @type {string} */
    this.selectorText;
    /** @type {!TextRange} */
    this.styleRange;
    /** @type {number} */
    this.lineNumber;
    /** @type {number} */
    this.columnNumber;
    /** @type {!Array.} */
    this.properties;
  }
}

// eslint-disable-next-line no-unused-vars
class SCSSProperty {
  constructor() {
    /** @type {!TextRange} */
    this.range;
    /** @type {!TextRange} */
    this.name;
    /** @type {!TextRange} */
    this.value;
    /** @type {boolean} */
    this.disabled;
  }
}

// eslint-disable-next-line no-unused-vars
class SCSSRule {
  constructor() {
    /** @type {!Array} */
    this.selectors;
    /** @type {!Array} */
    this.properties;
    /** @type {!TextRange} */
    this.styleRange;
  }
}

/**
 * @return {!FormatterWorkerPool}
 */
export function formatterWorkerPool() {
  if (!Formatter._formatterWorkerPool) {
    Formatter._formatterWorkerPool = new FormatterWorkerPool();
  }
  return Formatter._formatterWorkerPool;
}

/** @typedef {{line: number, column: number, title: string, subtitle: (string|undefined) }} */
export let OutlineItem;

/** @typedef {{original: !Array, formatted: !Array}} */
export let FormatMapping;

/**
 * @typedef {{atRule: string, lineNumber: number, columnNumber: number}}
 */
export let CSSAtRule;

/**
 * @typedef {(CSSStyleRule|CSSAtRule)}
 */
export let CSSRule;

/**
 * @typedef {{startLine: number, startColumn: number, endLine: number, endColumn: number}}
 */
export let TextRange;




© 2015 - 2024 Weber Informatics LLC | Privacy Policy