mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-09-11 11:15:24 +03:00
Machine decaf status-bar
source
This commit is contained in:
parent
cce8f048ec
commit
f37b1dd1a8
83
packages/status-bar/lib/cursor-position-view.js
Normal file
83
packages/status-bar/lib/cursor-position-view.js
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS104: Avoid inline assignments
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
|
||||
*/
|
||||
let CursorPositionView;
|
||||
const {Disposable} = require('atom');
|
||||
|
||||
module.exports =
|
||||
(CursorPositionView = class CursorPositionView {
|
||||
constructor() {
|
||||
let left;
|
||||
this.viewUpdatePending = false;
|
||||
|
||||
this.element = document.createElement('status-bar-cursor');
|
||||
this.element.classList.add('cursor-position', 'inline-block');
|
||||
this.goToLineLink = document.createElement('a');
|
||||
this.goToLineLink.classList.add('inline-block');
|
||||
this.element.appendChild(this.goToLineLink);
|
||||
|
||||
this.formatString = (left = atom.config.get('status-bar.cursorPositionFormat')) != null ? left : '%L:%C';
|
||||
|
||||
this.activeItemSubscription = atom.workspace.onDidChangeActiveTextEditor(activeEditor => this.subscribeToActiveTextEditor());
|
||||
|
||||
this.subscribeToConfig();
|
||||
this.subscribeToActiveTextEditor();
|
||||
|
||||
this.tooltip = atom.tooltips.add(this.element, {title: () => `Line ${this.row}, Column ${this.column}`});
|
||||
|
||||
this.handleClick();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.activeItemSubscription.dispose();
|
||||
this.cursorSubscription?.dispose();
|
||||
this.tooltip.dispose();
|
||||
this.configSubscription?.dispose();
|
||||
this.clickSubscription.dispose();
|
||||
return this.updateSubscription?.dispose();
|
||||
}
|
||||
|
||||
subscribeToActiveTextEditor() {
|
||||
this.cursorSubscription?.dispose();
|
||||
const selectionsMarkerLayer = atom.workspace.getActiveTextEditor()?.selectionsMarkerLayer;
|
||||
this.cursorSubscription = selectionsMarkerLayer?.onDidUpdate(this.scheduleUpdate.bind(this));
|
||||
return this.scheduleUpdate();
|
||||
}
|
||||
|
||||
subscribeToConfig() {
|
||||
this.configSubscription?.dispose();
|
||||
return this.configSubscription = atom.config.observe('status-bar.cursorPositionFormat', value => {
|
||||
this.formatString = value != null ? value : '%L:%C';
|
||||
return this.scheduleUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
handleClick() {
|
||||
const clickHandler = () => atom.commands.dispatch(atom.views.getView(atom.workspace.getActiveTextEditor()), 'go-to-line:toggle');
|
||||
this.element.addEventListener('click', clickHandler);
|
||||
return this.clickSubscription = new Disposable(() => this.element.removeEventListener('click', clickHandler));
|
||||
}
|
||||
|
||||
scheduleUpdate() {
|
||||
if (this.viewUpdatePending) { return; }
|
||||
|
||||
this.viewUpdatePending = true;
|
||||
return this.updateSubscription = atom.views.updateDocument(() => {
|
||||
let position;
|
||||
this.viewUpdatePending = false;
|
||||
if (position = atom.workspace.getActiveTextEditor()?.getCursorBufferPosition()) {
|
||||
this.row = position.row + 1;
|
||||
this.column = position.column + 1;
|
||||
this.goToLineLink.textContent = this.formatString.replace('%L', this.row).replace('%C', this.column);
|
||||
return this.element.classList.remove('hide');
|
||||
} else {
|
||||
this.goToLineLink.textContent = '';
|
||||
return this.element.classList.add('hide');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
158
packages/status-bar/lib/file-info-view.js
Normal file
158
packages/status-bar/lib/file-info-view.js
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
|
||||
*/
|
||||
let FileInfoView;
|
||||
const {Disposable} = require('atom');
|
||||
const url = require('url');
|
||||
const fs = require('fs-plus');
|
||||
|
||||
module.exports =
|
||||
(FileInfoView = class FileInfoView {
|
||||
constructor() {
|
||||
this.element = document.createElement('status-bar-file');
|
||||
this.element.classList.add('file-info', 'inline-block');
|
||||
|
||||
this.currentPath = document.createElement('a');
|
||||
this.currentPath.classList.add('current-path');
|
||||
this.element.appendChild(this.currentPath);
|
||||
this.element.currentPath = this.currentPath;
|
||||
|
||||
this.element.getActiveItem = this.getActiveItem.bind(this);
|
||||
|
||||
this.activeItemSubscription = atom.workspace.getCenter().onDidChangeActivePaneItem(() => {
|
||||
return this.subscribeToActiveItem();
|
||||
});
|
||||
this.subscribeToActiveItem();
|
||||
|
||||
this.registerTooltip();
|
||||
const clickHandler = event => {
|
||||
const isShiftClick = event.shiftKey;
|
||||
this.showCopiedTooltip(isShiftClick);
|
||||
const text = this.getActiveItemCopyText(isShiftClick);
|
||||
atom.clipboard.write(text);
|
||||
return setTimeout(() => {
|
||||
return this.clearCopiedTooltip();
|
||||
}
|
||||
, 2000);
|
||||
};
|
||||
|
||||
this.element.addEventListener('click', clickHandler);
|
||||
this.clickSubscription = new Disposable(() => this.element.removeEventListener('click', clickHandler));
|
||||
}
|
||||
|
||||
registerTooltip() {
|
||||
return this.tooltip = atom.tooltips.add(this.element, { title() {
|
||||
return "Click to copy absolute file path (Shift + Click to copy relative path)";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
clearCopiedTooltip() {
|
||||
this.copiedTooltip?.dispose();
|
||||
return this.registerTooltip();
|
||||
}
|
||||
|
||||
showCopiedTooltip(copyRelativePath) {
|
||||
this.tooltip?.dispose();
|
||||
this.copiedTooltip?.dispose();
|
||||
const text = this.getActiveItemCopyText(copyRelativePath);
|
||||
return this.copiedTooltip = atom.tooltips.add(this.element, {
|
||||
title: `Copied: ${text}`,
|
||||
trigger: 'manual',
|
||||
delay: {
|
||||
show: 0
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
getActiveItemCopyText(copyRelativePath) {
|
||||
const activeItem = this.getActiveItem();
|
||||
let path = activeItem?.getPath?.();
|
||||
if ((path == null)) { return activeItem?.getTitle?.() || ''; }
|
||||
|
||||
// Make sure we try to relativize before parsing URLs.
|
||||
if (copyRelativePath) {
|
||||
const relativized = atom.project.relativize(path);
|
||||
if (relativized !== path) {
|
||||
return relativized;
|
||||
}
|
||||
}
|
||||
|
||||
// An item path could be a url, we only want to copy the `path` part
|
||||
if (path?.indexOf('://') > 0) {
|
||||
({
|
||||
path
|
||||
} = url.parse(path));
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
subscribeToActiveItem() {
|
||||
let activeItem;
|
||||
this.modifiedSubscription?.dispose();
|
||||
this.titleSubscription?.dispose();
|
||||
|
||||
if (activeItem = this.getActiveItem()) {
|
||||
if (this.updateCallback == null) { this.updateCallback = () => this.update(); }
|
||||
|
||||
if (typeof activeItem.onDidChangeTitle === 'function') {
|
||||
this.titleSubscription = activeItem.onDidChangeTitle(this.updateCallback);
|
||||
} else if (typeof activeItem.on === 'function') {
|
||||
//TODO Remove once title-changed event support is removed
|
||||
activeItem.on('title-changed', this.updateCallback);
|
||||
this.titleSubscription = { dispose: () => {
|
||||
return activeItem.off?.('title-changed', this.updateCallback);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
this.modifiedSubscription = activeItem.onDidChangeModified?.(this.updateCallback);
|
||||
}
|
||||
|
||||
return this.update();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.activeItemSubscription.dispose();
|
||||
this.titleSubscription?.dispose();
|
||||
this.modifiedSubscription?.dispose();
|
||||
this.clickSubscription?.dispose();
|
||||
this.copiedTooltip?.dispose();
|
||||
return this.tooltip?.dispose();
|
||||
}
|
||||
|
||||
getActiveItem() {
|
||||
return atom.workspace.getCenter().getActivePaneItem();
|
||||
}
|
||||
|
||||
update() {
|
||||
this.updatePathText();
|
||||
return this.updateBufferHasModifiedText(this.getActiveItem()?.isModified?.());
|
||||
}
|
||||
|
||||
updateBufferHasModifiedText(isModified) {
|
||||
if (isModified) {
|
||||
this.element.classList.add('buffer-modified');
|
||||
return this.isModified = true;
|
||||
} else {
|
||||
this.element.classList.remove('buffer-modified');
|
||||
return this.isModified = false;
|
||||
}
|
||||
}
|
||||
|
||||
updatePathText() {
|
||||
let path, title;
|
||||
if (path = this.getActiveItem()?.getPath?.()) {
|
||||
const relativized = atom.project.relativize(path);
|
||||
return this.currentPath.textContent = (relativized != null) ? fs.tildify(relativized) : path;
|
||||
} else if ((title = this.getActiveItem()?.getTitle?.())) {
|
||||
return this.currentPath.textContent = title;
|
||||
} else {
|
||||
return this.currentPath.textContent = '';
|
||||
}
|
||||
}
|
||||
});
|
287
packages/status-bar/lib/git-view.js
Normal file
287
packages/status-bar/lib/git-view.js
Normal file
@ -0,0 +1,287 @@
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS104: Avoid inline assignments
|
||||
* DS205: Consider reworking code to avoid use of IIFEs
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
|
||||
*/
|
||||
let GitView;
|
||||
const _ = require("underscore-plus");
|
||||
const {CompositeDisposable, GitRepositoryAsync} = require("atom");
|
||||
|
||||
module.exports =
|
||||
(GitView = class GitView {
|
||||
constructor() {
|
||||
this.element = document.createElement('status-bar-git');
|
||||
this.element.classList.add('git-view');
|
||||
|
||||
this.createBranchArea();
|
||||
this.createCommitsArea();
|
||||
this.createStatusArea();
|
||||
|
||||
this.activeItemSubscription = atom.workspace.getCenter().onDidChangeActivePaneItem(() => {
|
||||
return this.subscribeToActiveItem();
|
||||
});
|
||||
this.projectPathSubscription = atom.project.onDidChangePaths(() => {
|
||||
return this.subscribeToRepositories();
|
||||
});
|
||||
this.subscribeToRepositories();
|
||||
this.subscribeToActiveItem();
|
||||
}
|
||||
|
||||
createBranchArea() {
|
||||
this.branchArea = document.createElement('div');
|
||||
this.branchArea.classList.add('git-branch', 'inline-block');
|
||||
this.element.appendChild(this.branchArea);
|
||||
this.element.branchArea = this.branchArea;
|
||||
|
||||
const branchIcon = document.createElement('span');
|
||||
branchIcon.classList.add('icon', 'icon-git-branch');
|
||||
this.branchArea.appendChild(branchIcon);
|
||||
|
||||
this.branchLabel = document.createElement('span');
|
||||
this.branchLabel.classList.add('branch-label');
|
||||
this.branchArea.appendChild(this.branchLabel);
|
||||
return this.element.branchLabel = this.branchLabel;
|
||||
}
|
||||
|
||||
createCommitsArea() {
|
||||
this.commitsArea = document.createElement('div');
|
||||
this.commitsArea.classList.add('git-commits', 'inline-block');
|
||||
this.element.appendChild(this.commitsArea);
|
||||
|
||||
this.commitsAhead = document.createElement('span');
|
||||
this.commitsAhead.classList.add('icon', 'icon-arrow-up', 'commits-ahead-label');
|
||||
this.commitsArea.appendChild(this.commitsAhead);
|
||||
|
||||
this.commitsBehind = document.createElement('span');
|
||||
this.commitsBehind.classList.add('icon', 'icon-arrow-down', 'commits-behind-label');
|
||||
return this.commitsArea.appendChild(this.commitsBehind);
|
||||
}
|
||||
|
||||
createStatusArea() {
|
||||
this.gitStatus = document.createElement('div');
|
||||
this.gitStatus.classList.add('git-status', 'inline-block');
|
||||
this.element.appendChild(this.gitStatus);
|
||||
|
||||
this.gitStatusIcon = document.createElement('span');
|
||||
this.gitStatusIcon.classList.add('icon');
|
||||
this.gitStatus.appendChild(this.gitStatusIcon);
|
||||
return this.element.gitStatusIcon = this.gitStatusIcon;
|
||||
}
|
||||
|
||||
subscribeToActiveItem() {
|
||||
const activeItem = this.getActiveItem();
|
||||
|
||||
this.savedSubscription?.dispose();
|
||||
this.savedSubscription = activeItem?.onDidSave?.(() => this.update());
|
||||
|
||||
return this.update();
|
||||
}
|
||||
|
||||
subscribeToRepositories() {
|
||||
this.repositorySubscriptions?.dispose();
|
||||
this.repositorySubscriptions = new CompositeDisposable;
|
||||
|
||||
return (() => {
|
||||
const result = [];
|
||||
for (let repo of Array.from(atom.project.getRepositories())) {
|
||||
if (repo != null) {
|
||||
this.repositorySubscriptions.add(repo.onDidChangeStatus(({path, status}) => {
|
||||
if (path === this.getActiveItemPath()) { return this.update(); }
|
||||
})
|
||||
);
|
||||
result.push(this.repositorySubscriptions.add(repo.onDidChangeStatuses(() => {
|
||||
return this.update();
|
||||
})
|
||||
));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
})();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.activeItemSubscription?.dispose();
|
||||
this.projectPathSubscription?.dispose();
|
||||
this.savedSubscription?.dispose();
|
||||
this.repositorySubscriptions?.dispose();
|
||||
this.branchTooltipDisposable?.dispose();
|
||||
this.commitsAheadTooltipDisposable?.dispose();
|
||||
this.commitsBehindTooltipDisposable?.dispose();
|
||||
return this.statusTooltipDisposable?.dispose();
|
||||
}
|
||||
|
||||
getActiveItemPath() {
|
||||
return this.getActiveItem()?.getPath?.();
|
||||
}
|
||||
|
||||
getRepositoryForActiveItem() {
|
||||
const [rootDir] = atom.project.relativizePath(this.getActiveItemPath());
|
||||
const rootDirIndex = atom.project.getPaths().indexOf(rootDir);
|
||||
if (rootDirIndex >= 0) {
|
||||
return atom.project.getRepositories()[rootDirIndex];
|
||||
} else {
|
||||
for (let repo of Array.from(atom.project.getRepositories())) {
|
||||
if (repo) {
|
||||
return repo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getActiveItem() {
|
||||
return atom.workspace.getCenter().getActivePaneItem();
|
||||
}
|
||||
|
||||
update() {
|
||||
const repo = this.getRepositoryForActiveItem();
|
||||
this.updateBranchText(repo);
|
||||
this.updateAheadBehindCount(repo);
|
||||
return this.updateStatusText(repo);
|
||||
}
|
||||
|
||||
updateBranchText(repo) {
|
||||
if (this.showGitInformation(repo)) {
|
||||
const head = repo.getShortHead(this.getActiveItemPath());
|
||||
this.branchLabel.textContent = head;
|
||||
if (head) { this.branchArea.style.display = ''; }
|
||||
this.branchTooltipDisposable?.dispose();
|
||||
return this.branchTooltipDisposable = atom.tooltips.add(this.branchArea, {title: `On branch ${head}`});
|
||||
} else {
|
||||
return this.branchArea.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
showGitInformation(repo) {
|
||||
let itemPath;
|
||||
if (repo == null) { return false; }
|
||||
|
||||
if ((itemPath = this.getActiveItemPath())) {
|
||||
return atom.project.contains(itemPath);
|
||||
} else {
|
||||
return (this.getActiveItem() == null);
|
||||
}
|
||||
}
|
||||
|
||||
updateAheadBehindCount(repo) {
|
||||
if (!this.showGitInformation(repo)) {
|
||||
this.commitsArea.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
const itemPath = this.getActiveItemPath();
|
||||
const {ahead, behind} = repo.getCachedUpstreamAheadBehindCount(itemPath);
|
||||
if (ahead > 0) {
|
||||
this.commitsAhead.textContent = ahead;
|
||||
this.commitsAhead.style.display = '';
|
||||
this.commitsAheadTooltipDisposable?.dispose();
|
||||
this.commitsAheadTooltipDisposable = atom.tooltips.add(this.commitsAhead, {title: `${_.pluralize(ahead, 'commit')} ahead of upstream`});
|
||||
} else {
|
||||
this.commitsAhead.style.display = 'none';
|
||||
}
|
||||
|
||||
if (behind > 0) {
|
||||
this.commitsBehind.textContent = behind;
|
||||
this.commitsBehind.style.display = '';
|
||||
this.commitsBehindTooltipDisposable?.dispose();
|
||||
this.commitsBehindTooltipDisposable = atom.tooltips.add(this.commitsBehind, {title: `${_.pluralize(behind, 'commit')} behind upstream`});
|
||||
} else {
|
||||
this.commitsBehind.style.display = 'none';
|
||||
}
|
||||
|
||||
if ((ahead > 0) || (behind > 0)) {
|
||||
return this.commitsArea.style.display = '';
|
||||
} else {
|
||||
return this.commitsArea.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
clearStatus() {
|
||||
return this.gitStatusIcon.classList.remove('icon-diff-modified', 'status-modified', 'icon-diff-added', 'status-added', 'icon-diff-ignored', 'status-ignored');
|
||||
}
|
||||
|
||||
updateAsNewFile() {
|
||||
let textEditor;
|
||||
this.clearStatus();
|
||||
|
||||
this.gitStatusIcon.classList.add('icon-diff-added', 'status-added');
|
||||
if (textEditor = atom.workspace.getActiveTextEditor()) {
|
||||
this.gitStatusIcon.textContent = `+${textEditor.getLineCount()}`;
|
||||
this.updateTooltipText(`${_.pluralize(textEditor.getLineCount(), 'line')} in this new file not yet committed`);
|
||||
} else {
|
||||
this.gitStatusIcon.textContent = '';
|
||||
this.updateTooltipText();
|
||||
}
|
||||
|
||||
return this.gitStatus.style.display = '';
|
||||
}
|
||||
|
||||
updateAsModifiedFile(repo, path) {
|
||||
const stats = repo.getDiffStats(path);
|
||||
this.clearStatus();
|
||||
|
||||
this.gitStatusIcon.classList.add('icon-diff-modified', 'status-modified');
|
||||
if (stats.added && stats.deleted) {
|
||||
this.gitStatusIcon.textContent = `+${stats.added}, -${stats.deleted}`;
|
||||
this.updateTooltipText(`${_.pluralize(stats.added, 'line')} added and ${_.pluralize(stats.deleted, 'line')} deleted in this file not yet committed`);
|
||||
} else if (stats.added) {
|
||||
this.gitStatusIcon.textContent = `+${stats.added}`;
|
||||
this.updateTooltipText(`${_.pluralize(stats.added, 'line')} added to this file not yet committed`);
|
||||
} else if (stats.deleted) {
|
||||
this.gitStatusIcon.textContent = `-${stats.deleted}`;
|
||||
this.updateTooltipText(`${_.pluralize(stats.deleted, 'line')} deleted from this file not yet committed`);
|
||||
} else {
|
||||
this.gitStatusIcon.textContent = '';
|
||||
this.updateTooltipText();
|
||||
}
|
||||
|
||||
return this.gitStatus.style.display = '';
|
||||
}
|
||||
|
||||
updateAsIgnoredFile() {
|
||||
this.clearStatus();
|
||||
|
||||
this.gitStatusIcon.classList.add('icon-diff-ignored', 'status-ignored');
|
||||
this.gitStatusIcon.textContent = '';
|
||||
this.gitStatus.style.display = '';
|
||||
return this.updateTooltipText("File is ignored by git");
|
||||
}
|
||||
|
||||
updateTooltipText(text) {
|
||||
this.statusTooltipDisposable?.dispose();
|
||||
if (text) {
|
||||
return this.statusTooltipDisposable = atom.tooltips.add(this.gitStatusIcon, {title: text});
|
||||
}
|
||||
}
|
||||
|
||||
updateStatusText(repo) {
|
||||
const hideStatus = () => {
|
||||
this.clearStatus();
|
||||
return this.gitStatus.style.display = 'none';
|
||||
};
|
||||
|
||||
const itemPath = this.getActiveItemPath();
|
||||
if (this.showGitInformation(repo) && (itemPath != null)) {
|
||||
let left;
|
||||
const status = (left = repo.getCachedPathStatus(itemPath)) != null ? left : 0;
|
||||
if (repo.isStatusNew(status)) {
|
||||
return this.updateAsNewFile();
|
||||
}
|
||||
|
||||
if (repo.isStatusModified(status)) {
|
||||
return this.updateAsModifiedFile(repo, itemPath);
|
||||
}
|
||||
|
||||
if (repo.isPathIgnored(itemPath)) {
|
||||
return this.updateAsIgnoredFile();
|
||||
} else {
|
||||
return hideStatus();
|
||||
}
|
||||
} else {
|
||||
return hideStatus();
|
||||
}
|
||||
}
|
||||
});
|
27
packages/status-bar/lib/launch-mode-view.js
Normal file
27
packages/status-bar/lib/launch-mode-view.js
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
|
||||
*/
|
||||
let LaunchModeView;
|
||||
module.exports =
|
||||
(LaunchModeView = class LaunchModeView {
|
||||
constructor(param) {
|
||||
if (param == null) { param = {}; }
|
||||
const {safeMode, devMode} = param;
|
||||
this.element = document.createElement('status-bar-launch-mode');
|
||||
this.element.classList.add('inline-block', 'icon', 'icon-color-mode');
|
||||
if (devMode) {
|
||||
this.element.classList.add('text-error');
|
||||
this.tooltipDisposable = atom.tooltips.add(this.element, {title: 'This window is in dev mode'});
|
||||
} else if (safeMode) {
|
||||
this.element.classList.add('text-success');
|
||||
this.tooltipDisposable = atom.tooltips.add(this.element, {title: 'This window is in safe mode'});
|
||||
}
|
||||
}
|
||||
|
||||
detachedCallback() {
|
||||
return this.tooltipDisposable?.dispose();
|
||||
}
|
||||
});
|
150
packages/status-bar/lib/main.js
Normal file
150
packages/status-bar/lib/main.js
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
|
||||
*/
|
||||
const {CompositeDisposable, Emitter} = require('atom');
|
||||
const Grim = require('grim');
|
||||
const StatusBarView = require('./status-bar-view');
|
||||
const FileInfoView = require('./file-info-view');
|
||||
const CursorPositionView = require('./cursor-position-view');
|
||||
const SelectionCountView = require('./selection-count-view');
|
||||
const GitView = require('./git-view');
|
||||
const LaunchModeView = require('./launch-mode-view');
|
||||
|
||||
module.exports = {
|
||||
activate() {
|
||||
this.emitters = new Emitter();
|
||||
this.subscriptions = new CompositeDisposable();
|
||||
|
||||
this.statusBar = new StatusBarView();
|
||||
this.attachStatusBar();
|
||||
|
||||
this.subscriptions.add(atom.config.onDidChange('status-bar.fullWidth', () => {
|
||||
return this.attachStatusBar();
|
||||
})
|
||||
);
|
||||
|
||||
this.updateStatusBarVisibility();
|
||||
|
||||
this.statusBarVisibilitySubscription =
|
||||
atom.config.observe('status-bar.isVisible', () => {
|
||||
return this.updateStatusBarVisibility();
|
||||
});
|
||||
|
||||
atom.commands.add('atom-workspace', 'status-bar:toggle', () => {
|
||||
if (this.statusBarPanel.isVisible()) {
|
||||
return atom.config.set('status-bar.isVisible', false);
|
||||
} else {
|
||||
return atom.config.set('status-bar.isVisible', true);
|
||||
}
|
||||
});
|
||||
|
||||
const {safeMode, devMode} = atom.getLoadSettings();
|
||||
if (safeMode || devMode) {
|
||||
const launchModeView = new LaunchModeView({safeMode, devMode});
|
||||
this.statusBar.addLeftTile({item: launchModeView.element, priority: -1});
|
||||
}
|
||||
|
||||
this.fileInfo = new FileInfoView();
|
||||
this.statusBar.addLeftTile({item: this.fileInfo.element, priority: 0});
|
||||
|
||||
this.cursorPosition = new CursorPositionView();
|
||||
this.statusBar.addLeftTile({item: this.cursorPosition.element, priority: 1});
|
||||
|
||||
this.selectionCount = new SelectionCountView();
|
||||
this.statusBar.addLeftTile({item: this.selectionCount.element, priority: 2});
|
||||
|
||||
this.gitInfo = new GitView();
|
||||
return this.gitInfoTile = this.statusBar.addRightTile({item: this.gitInfo.element, priority: 0});
|
||||
},
|
||||
|
||||
deactivate() {
|
||||
this.statusBarVisibilitySubscription?.dispose();
|
||||
this.statusBarVisibilitySubscription = null;
|
||||
|
||||
this.gitInfo?.destroy();
|
||||
this.gitInfo = null;
|
||||
|
||||
this.fileInfo?.destroy();
|
||||
this.fileInfo = null;
|
||||
|
||||
this.cursorPosition?.destroy();
|
||||
this.cursorPosition = null;
|
||||
|
||||
this.selectionCount?.destroy();
|
||||
this.selectionCount = null;
|
||||
|
||||
this.statusBarPanel?.destroy();
|
||||
this.statusBarPanel = null;
|
||||
|
||||
this.statusBar?.destroy();
|
||||
this.statusBar = null;
|
||||
|
||||
this.subscriptions?.dispose();
|
||||
this.subscriptions = null;
|
||||
|
||||
this.emitters?.dispose();
|
||||
this.emitters = null;
|
||||
|
||||
if (atom.__workspaceView != null) { return delete atom.__workspaceView.statusBar; }
|
||||
},
|
||||
|
||||
updateStatusBarVisibility() {
|
||||
if (atom.config.get('status-bar.isVisible')) {
|
||||
return this.statusBarPanel.show();
|
||||
} else {
|
||||
return this.statusBarPanel.hide();
|
||||
}
|
||||
},
|
||||
|
||||
provideStatusBar() {
|
||||
return {
|
||||
addLeftTile: this.statusBar.addLeftTile.bind(this.statusBar),
|
||||
addRightTile: this.statusBar.addRightTile.bind(this.statusBar),
|
||||
getLeftTiles: this.statusBar.getLeftTiles.bind(this.statusBar),
|
||||
getRightTiles: this.statusBar.getRightTiles.bind(this.statusBar),
|
||||
disableGitInfoTile: this.gitInfoTile.destroy.bind(this.gitInfoTile)
|
||||
};
|
||||
},
|
||||
|
||||
attachStatusBar() {
|
||||
if (this.statusBarPanel != null) { this.statusBarPanel.destroy(); }
|
||||
|
||||
const panelArgs = {item: this.statusBar, priority: 0};
|
||||
if (atom.config.get('status-bar.fullWidth')) {
|
||||
return this.statusBarPanel = atom.workspace.addFooterPanel(panelArgs);
|
||||
} else {
|
||||
return this.statusBarPanel = atom.workspace.addBottomPanel(panelArgs);
|
||||
}
|
||||
},
|
||||
|
||||
// Deprecated
|
||||
//
|
||||
// Wrap deprecation calls on the methods returned rather than
|
||||
// Services API method which would be registered and trigger
|
||||
// a deprecation call
|
||||
legacyProvideStatusBar() {
|
||||
const statusbar = this.provideStatusBar();
|
||||
|
||||
return {
|
||||
addLeftTile(...args) {
|
||||
Grim.deprecate("Use version ^1.0.0 of the status-bar Service API.");
|
||||
return statusbar.addLeftTile(...args);
|
||||
},
|
||||
addRightTile(...args) {
|
||||
Grim.deprecate("Use version ^1.0.0 of the status-bar Service API.");
|
||||
return statusbar.addRightTile(...args);
|
||||
},
|
||||
getLeftTiles() {
|
||||
Grim.deprecate("Use version ^1.0.0 of the status-bar Service API.");
|
||||
return statusbar.getLeftTiles();
|
||||
},
|
||||
getRightTiles() {
|
||||
Grim.deprecate("Use version ^1.0.0 of the status-bar Service API.");
|
||||
return statusbar.getRightTiles();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
79
packages/status-bar/lib/selection-count-view.js
Normal file
79
packages/status-bar/lib/selection-count-view.js
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS104: Avoid inline assignments
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
|
||||
*/
|
||||
let SelectionCountView;
|
||||
const _ = require('underscore-plus');
|
||||
|
||||
module.exports =
|
||||
(SelectionCountView = class SelectionCountView {
|
||||
constructor() {
|
||||
let left;
|
||||
this.element = document.createElement('status-bar-selection');
|
||||
this.element.classList.add('selection-count', 'inline-block');
|
||||
|
||||
this.tooltipElement = document.createElement('div');
|
||||
this.tooltipDisposable = atom.tooltips.add(this.element, {item: this.tooltipElement});
|
||||
|
||||
this.formatString = (left = atom.config.get('status-bar.selectionCountFormat')) != null ? left : '(%L, %C)';
|
||||
|
||||
this.activeItemSubscription = atom.workspace.onDidChangeActiveTextEditor(() => this.subscribeToActiveTextEditor());
|
||||
|
||||
this.subscribeToConfig();
|
||||
this.subscribeToActiveTextEditor();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.activeItemSubscription.dispose();
|
||||
this.selectionSubscription?.dispose();
|
||||
this.configSubscription?.dispose();
|
||||
return this.tooltipDisposable.dispose();
|
||||
}
|
||||
|
||||
subscribeToConfig() {
|
||||
this.configSubscription?.dispose();
|
||||
return this.configSubscription = atom.config.observe('status-bar.selectionCountFormat', value => {
|
||||
this.formatString = value != null ? value : '(%L, %C)';
|
||||
return this.scheduleUpdateCount();
|
||||
});
|
||||
}
|
||||
|
||||
subscribeToActiveTextEditor() {
|
||||
this.selectionSubscription?.dispose();
|
||||
const activeEditor = this.getActiveTextEditor();
|
||||
const selectionsMarkerLayer = activeEditor?.selectionsMarkerLayer;
|
||||
this.selectionSubscription = selectionsMarkerLayer?.onDidUpdate(this.scheduleUpdateCount.bind(this));
|
||||
return this.scheduleUpdateCount();
|
||||
}
|
||||
|
||||
getActiveTextEditor() {
|
||||
return atom.workspace.getActiveTextEditor();
|
||||
}
|
||||
|
||||
scheduleUpdateCount() {
|
||||
if (!this.scheduledUpdate) {
|
||||
this.scheduledUpdate = true;
|
||||
return atom.views.updateDocument(() => {
|
||||
this.updateCount();
|
||||
return this.scheduledUpdate = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
updateCount() {
|
||||
const count = this.getActiveTextEditor()?.getSelectedText().length;
|
||||
const range = this.getActiveTextEditor()?.getSelectedBufferRange();
|
||||
let lineCount = range?.getRowCount();
|
||||
if (range?.end.column === 0) { lineCount -= 1; }
|
||||
if (count > 0) {
|
||||
this.element.textContent = this.formatString.replace('%L', lineCount).replace('%C', count);
|
||||
return this.tooltipElement.textContent = `${_.pluralize(lineCount, 'line')}, ${_.pluralize(count, 'character')} selected`;
|
||||
} else {
|
||||
this.element.textContent = '';
|
||||
return this.tooltipElement.textContent = '';
|
||||
}
|
||||
}
|
||||
});
|
148
packages/status-bar/lib/status-bar-view.js
Normal file
148
packages/status-bar/lib/status-bar-view.js
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS205: Consider reworking code to avoid use of IIFEs
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
|
||||
*/
|
||||
let StatusBarView;
|
||||
const {Disposable} = require('atom');
|
||||
const Tile = require('./tile');
|
||||
|
||||
module.exports =
|
||||
(StatusBarView = class StatusBarView {
|
||||
constructor() {
|
||||
this.element = document.createElement('status-bar');
|
||||
this.element.classList.add('status-bar');
|
||||
|
||||
const flexboxHackElement = document.createElement('div');
|
||||
flexboxHackElement.classList.add('flexbox-repaint-hack');
|
||||
this.element.appendChild(flexboxHackElement);
|
||||
|
||||
this.leftPanel = document.createElement('div');
|
||||
this.leftPanel.classList.add('status-bar-left');
|
||||
flexboxHackElement.appendChild(this.leftPanel);
|
||||
this.element.leftPanel = this.leftPanel;
|
||||
|
||||
this.rightPanel = document.createElement('div');
|
||||
this.rightPanel.classList.add('status-bar-right');
|
||||
flexboxHackElement.appendChild(this.rightPanel);
|
||||
this.element.rightPanel = this.rightPanel;
|
||||
|
||||
this.leftTiles = [];
|
||||
this.rightTiles = [];
|
||||
|
||||
this.element.getLeftTiles = this.getLeftTiles.bind(this);
|
||||
this.element.getRightTiles = this.getRightTiles.bind(this);
|
||||
this.element.addLeftTile = this.addLeftTile.bind(this);
|
||||
this.element.addRightTile = this.addRightTile.bind(this);
|
||||
|
||||
this.bufferSubscriptions = [];
|
||||
|
||||
this.activeItemSubscription = atom.workspace.getCenter().onDidChangeActivePaneItem(() => {
|
||||
this.unsubscribeAllFromBuffer();
|
||||
this.storeActiveBuffer();
|
||||
this.subscribeAllToBuffer();
|
||||
|
||||
return this.element.dispatchEvent(new CustomEvent('active-buffer-changed', {bubbles: true}));
|
||||
});
|
||||
|
||||
this.storeActiveBuffer();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.activeItemSubscription.dispose();
|
||||
this.unsubscribeAllFromBuffer();
|
||||
return this.element.remove();
|
||||
}
|
||||
|
||||
addLeftTile(options) {
|
||||
let index;
|
||||
const newItem = options.item;
|
||||
const newPriority = options?.priority != null ? options?.priority : this.leftTiles[this.leftTiles.length - 1].priority + 1;
|
||||
let nextItem = null;
|
||||
for (index = 0; index < this.leftTiles.length; index++) {
|
||||
const {priority, item} = this.leftTiles[index];
|
||||
if (priority > newPriority) {
|
||||
nextItem = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const newTile = new Tile(newItem, newPriority, this.leftTiles);
|
||||
this.leftTiles.splice(index, 0, newTile);
|
||||
const newElement = atom.views.getView(newItem);
|
||||
const nextElement = atom.views.getView(nextItem);
|
||||
this.leftPanel.insertBefore(newElement, nextElement);
|
||||
return newTile;
|
||||
}
|
||||
|
||||
addRightTile(options) {
|
||||
let index;
|
||||
const newItem = options.item;
|
||||
const newPriority = options?.priority != null ? options?.priority : this.rightTiles[0].priority + 1;
|
||||
let nextItem = null;
|
||||
for (index = 0; index < this.rightTiles.length; index++) {
|
||||
const {priority, item} = this.rightTiles[index];
|
||||
if (priority < newPriority) {
|
||||
nextItem = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const newTile = new Tile(newItem, newPriority, this.rightTiles);
|
||||
this.rightTiles.splice(index, 0, newTile);
|
||||
const newElement = atom.views.getView(newItem);
|
||||
const nextElement = atom.views.getView(nextItem);
|
||||
this.rightPanel.insertBefore(newElement, nextElement);
|
||||
return newTile;
|
||||
}
|
||||
|
||||
getLeftTiles() {
|
||||
return this.leftTiles;
|
||||
}
|
||||
|
||||
getRightTiles() {
|
||||
return this.rightTiles;
|
||||
}
|
||||
|
||||
getActiveBuffer() {
|
||||
return this.buffer;
|
||||
}
|
||||
|
||||
getActiveItem() {
|
||||
return atom.workspace.getCenter().getActivePaneItem();
|
||||
}
|
||||
|
||||
storeActiveBuffer() {
|
||||
return this.buffer = this.getActiveItem()?.getBuffer?.();
|
||||
}
|
||||
|
||||
subscribeToBuffer(event, callback) {
|
||||
this.bufferSubscriptions.push([event, callback]);
|
||||
if (this.buffer) { return this.buffer.on(event, callback); }
|
||||
}
|
||||
|
||||
subscribeAllToBuffer() {
|
||||
if (!this.buffer) { return; }
|
||||
return (() => {
|
||||
const result = [];
|
||||
for (let [event, callback] of Array.from(this.bufferSubscriptions)) {
|
||||
result.push(this.buffer.on(event, callback));
|
||||
}
|
||||
return result;
|
||||
})();
|
||||
}
|
||||
|
||||
unsubscribeAllFromBuffer() {
|
||||
if (!this.buffer) { return; }
|
||||
return (() => {
|
||||
const result = [];
|
||||
for (let [event, callback] of Array.from(this.bufferSubscriptions)) {
|
||||
result.push(this.buffer.off(event, callback));
|
||||
}
|
||||
return result;
|
||||
})();
|
||||
}
|
||||
});
|
27
packages/status-bar/lib/tile.js
Normal file
27
packages/status-bar/lib/tile.js
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
|
||||
*/
|
||||
let Tile;
|
||||
module.exports =
|
||||
(Tile = class Tile {
|
||||
constructor(item, priority, collection) {
|
||||
this.item = item;
|
||||
this.priority = priority;
|
||||
this.collection = collection;
|
||||
}
|
||||
|
||||
getItem() {
|
||||
return this.item;
|
||||
}
|
||||
|
||||
getPriority() {
|
||||
return this.priority;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.collection.splice(this.collection.indexOf(this), 1);
|
||||
return atom.views.getView(this.item).remove();
|
||||
}
|
||||
});
|
Loading…
Reference in New Issue
Block a user