<commit_before>'use babel';
import 'object-assign-shim';

import {
  calculate as calculateSpecificity,
} from 'specificity';

import {
  CompositeDisposable,
} from 'atom';

import {
  showDetailView,
} from './main.js';

import {
  addEventListener,
  isCSSTextEditor,
} from './utils.js';

var tagName = 'css-specificity-status';

var prototype = Object.create(HTMLElement.prototype);

Object.assign(prototype, {
  createdCallback() {
    this.classList.add('inline-block');

    this.link = this.appendChild(document.createElement('a'));
    this.link.classList.add('inline-block');

    this.disposables = new CompositeDisposable();

    this.disposables.add(
      atom.workspace.observeActivePaneItem(paneItem =>
        this.update(paneItem)
      ),

      atom.workspace.observeTextEditors(textEditor => {
        if(!isCSSTextEditor(textEditor)) return;

        var textEditorDisposable = new CompositeDisposable(
          textEditor.onDidChangeCursorPosition(() =>
            this.update(textEditor)
          ),

          textEditor.onDidAddCursor(() =>
            this.update(textEditor)
          ),

          textEditor.onDidRemoveCursor(() =>
            this.update(textEditor)
          ),

          textEditor.onDidStopChanging(() =>
            this.update(textEditor)
          ),

          textEditor.onDidDestroy(() => {
            textEditorDisposable.dispose();
            this.disposables.remove(textEditorDisposable);
          }),
        );

        this.disposables.add(textEditorDisposable);
      }),

      addEventListener(this.link, 'click', () =>
        showDetailView()
      ),
    );
  },

  destroy() {
    this.disposables.dispose();
  },

  show() {
    this.style.display = '';
  },

  hide() {
    this.style.display = 'none';
  },

  update(textEditor) {
    if(!isCSSTextEditor(textEditor)) return this.hide();

    this.currentSelector = this.getSelectors(textEditor);
    var { specificity } = calculateSpecificity(this.currentSelector)[0] || {};

    if(!specificity) return this.hide();

    this.link.innerHTML = `Specificity: ${specificity}`;
    this.show();
  },

  getSelectors(textEditor) {
    var selectors = textEditor.getCursorBufferPositions().map(cursorPosition =>
      this.getSelectorForCursorPosition(textEditor, cursorPosition)
    );
    return selectors.filter((selector, i) =>
      selectors.indexOf(selector) == i
    ).join(',');
  },

  getSelectorForCursorPosition(textEditor, cursorPosition) {
    var previousRange;
    var targetRange;

    textEditor.scan(/}/g, ({range, stop}) => {
      if(range.start.isGreaterThan(cursorPosition)) {
        stop();
        targetRange = [
          previousRange ? previousRange.start.translate([0, 1]) : [0, 0],
          range.start.translate([0, 1]),
        ];
      }
      previousRange = range;
    });

    if(!targetRange) return '';

    var selector = textEditor.getTextInBufferRange(targetRange).match(/^\s*([^{]*)/)[1];

    // strip of comments and trim()
    selector = selector.replace(/\/\*(.|\s)+?\*\//g, '').trim();

    return selector;
  },
});

document.registerElement(tagName, {
  prototype,
});

export default function cssSpecificityStatus() {
  return document.createElement(tagName);
}<commit_msg>minor improvements<commit_after>'use babel';
import 'object-assign-shim';

import {
  calculate as calculateSpecificity,
} from 'specificity';

import {
  CompositeDisposable,
} from 'atom';

import {
  showDetailView,
} from './main.js';

import {
  addEventListener,
  isCSSTextEditor,
} from './utils.js';

var tagName = 'css-specificity-status';

var prototype = Object.create(HTMLElement.prototype);

Object.assign(prototype, {
  createdCallback() {
    this.classList.add('inline-block');

    this.link = this.appendChild(document.createElement('a'));
    this.link.classList.add('inline-block');

    this.disposables = new CompositeDisposable(
      atom.workspace.observeActivePaneItem(paneItem =>
        this.update(paneItem)
      ),

      atom.workspace.observeTextEditors(textEditor => {
        if(!isCSSTextEditor(textEditor)) return;

        var textEditorDisposable = new CompositeDisposable(
          textEditor.onDidChangeCursorPosition(() =>
            this.update(textEditor)
          ),

          textEditor.onDidAddCursor(() =>
            this.update(textEditor)
          ),

          textEditor.onDidRemoveCursor(() =>
            this.update(textEditor)
          ),

          textEditor.onDidStopChanging(() =>
            this.update(textEditor)
          ),

          textEditor.onDidDestroy(() => {
            textEditorDisposable.dispose();
            this.disposables.remove(textEditorDisposable);
          }),
        );

        this.disposables.add(textEditorDisposable);
      }),

      addEventListener(this.link, 'click', () =>
        showDetailView()
      ),
    );
  },

  destroy() {
    this.disposables.dispose();
  },

  show() {
    this.style.display = '';
  },

  hide() {
    this.style.display = 'none';
  },

  update(textEditor) {
    if(!isCSSTextEditor(textEditor)) return this.hide();

    this.currentSelector = this.getSelectors(textEditor);
    var { specificity } = calculateSpecificity(this.currentSelector)[0] || {};

    if(!specificity) return this.hide();

    this.link.innerHTML = `Specificity: ${specificity}`;
    this.show();
  },

  getSelectors(textEditor) {
    // selectors is an array of arrays
    var selectors = textEditor.getCursorBufferPositions().map(cursorPosition =>
      this.getSelectorForCursorPosition(textEditor, cursorPosition)
    );
    // merge the arrays in selectors
    selectors = Array.prototype.concat.apply([], selectors);
    // filter duplicate selectors
    return selectors.filter((selector, i) =>
      selectors.indexOf(selector) == i
    ).join(',');
  },

  getSelectorForCursorPosition(textEditor, cursorPosition) {
    var CSSBlock = textEditor.getTextInBufferRange([
      this.findNextClosingBracketBufferPosition(textEditor, cursorPosition, true) || [0, 0],
      this.findNextClosingBracketBufferPosition(textEditor, cursorPosition) || [Infinity, 0],
    ]);

    // strip comments
    CSSBlock = CSSBlock.replace(/\/\*(.|\s)+?\*\//g, '');

    // strip everything after opening bracket
    var selector = CSSBlock.match(/^\s*([^{]*)/)[1];

    // split into comma separated parts
    return selector.split(',').map(part => part.trim());
  },

  findNextClosingBracketBufferPosition(textEditor, cursorPosition, reverse = false) {
    var scanRange = reverse ? [[0, 0], cursorPosition] : [cursorPosition, [Infinity, 0]];
    var result;

    textEditor[reverse ? 'backwardsScanInBufferRange' : 'scanInBufferRange'](/}/g, scanRange, ({range, stop}) => {
      var scopeDescriptor = textEditor.scopeDescriptorForBufferPosition(range.start);
      if(scopeDescriptor.getScopesArray().join(' ').search('comment') > -1) return;

      stop();
      result = range.start.translate([0, 1]);
    });

    return result;
  },
});

document.registerElement(tagName, {
  prototype,
});

export default function cssSpecificityStatus() {
  return document.createElement(tagName);
}
{'fraction_non_alphanumeric': 0.14445532093413507, 'fraction_numerical': 0.0026569710529995806, 'mean_word_length': 3.052124645892351, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4713028', 'n_tokens_mistral': 2159, 'n_tokens_neox': 2075, 'n_words': 333}