Files
atom/packages/line-ending-selector/lib/main.js
2019-02-25 13:05:02 +01:00

197 lines
5.6 KiB
JavaScript

'use babel'
import _ from 'underscore-plus'
import { CompositeDisposable, Disposable } from 'atom'
import SelectListView from 'atom-select-list'
import StatusBarItem from './status-bar-item'
import helpers from './helpers'
const LineEndingRegExp = /\r\n|\n/g
// the following regular expression is executed natively via the `substring` package,
// where `\A` corresponds to the beginning of the string.
// More info: https://github.com/atom/line-ending-selector/pull/56
// eslint-disable-next-line no-useless-escape
const LFRegExp = /(\A|[^\r])\n/g
const CRLFRegExp = /\r\n/g
let disposables = null
let modalPanel = null
let lineEndingListView = null
export function activate () {
disposables = new CompositeDisposable()
disposables.add(
atom.commands.add('atom-text-editor', {
'line-ending-selector:show': event => {
if (!modalPanel) {
lineEndingListView = new SelectListView({
items: [
{ name: 'LF', value: '\n' },
{ name: 'CRLF', value: '\r\n' }
],
filterKeyForItem: lineEnding => lineEnding.name,
didConfirmSelection: lineEnding => {
setLineEnding(
atom.workspace.getActiveTextEditor(),
lineEnding.value
)
modalPanel.hide()
},
didCancelSelection: () => {
modalPanel.hide()
},
elementForItem: lineEnding => {
const element = document.createElement('li')
element.textContent = lineEnding.name
return element
}
})
modalPanel = atom.workspace.addModalPanel({
item: lineEndingListView
})
disposables.add(
new Disposable(() => {
lineEndingListView.destroy()
modalPanel.destroy()
modalPanel = null
})
)
}
lineEndingListView.reset()
modalPanel.show()
lineEndingListView.focus()
},
'line-ending-selector:convert-to-LF': event => {
const editorElement = event.target.closest('atom-text-editor')
setLineEnding(editorElement.getModel(), '\n')
},
'line-ending-selector:convert-to-CRLF': event => {
const editorElement = event.target.closest('atom-text-editor')
setLineEnding(editorElement.getModel(), '\r\n')
}
})
)
}
export function deactivate () {
disposables.dispose()
}
export function consumeStatusBar (statusBar) {
let statusBarItem = new StatusBarItem()
let currentBufferDisposable = null
let tooltipDisposable = null
const updateTile = _.debounce(buffer => {
getLineEndings(buffer).then(lineEndings => {
if (lineEndings.size === 0) {
let defaultLineEnding = getDefaultLineEnding()
buffer.setPreferredLineEnding(defaultLineEnding)
lineEndings = new Set().add(defaultLineEnding)
}
statusBarItem.setLineEndings(lineEndings)
})
}, 0)
disposables.add(
atom.workspace.observeActiveTextEditor(editor => {
if (currentBufferDisposable) currentBufferDisposable.dispose()
if (editor && editor.getBuffer) {
let buffer = editor.getBuffer()
updateTile(buffer)
currentBufferDisposable = buffer.onDidChange(({ oldText, newText }) => {
if (!statusBarItem.hasLineEnding('\n')) {
if (newText.indexOf('\n') >= 0) {
updateTile(buffer)
}
} else if (!statusBarItem.hasLineEnding('\r\n')) {
if (newText.indexOf('\r\n') >= 0) {
updateTile(buffer)
}
} else if (oldText.indexOf('\n')) {
updateTile(buffer)
}
})
} else {
statusBarItem.setLineEndings(new Set())
currentBufferDisposable = null
}
if (tooltipDisposable) {
disposables.remove(tooltipDisposable)
tooltipDisposable.dispose()
}
tooltipDisposable = atom.tooltips.add(statusBarItem.element, {
title () {
return `File uses ${statusBarItem.description()} line endings`
}
})
disposables.add(tooltipDisposable)
})
)
disposables.add(
new Disposable(() => {
if (currentBufferDisposable) currentBufferDisposable.dispose()
})
)
statusBarItem.onClick(() => {
const editor = atom.workspace.getActiveTextEditor()
atom.commands.dispatch(
atom.views.getView(editor),
'line-ending-selector:show'
)
})
let tile = statusBar.addRightTile({ item: statusBarItem, priority: 200 })
disposables.add(new Disposable(() => tile.destroy()))
}
function getDefaultLineEnding () {
switch (atom.config.get('line-ending-selector.defaultLineEnding')) {
case 'LF':
return '\n'
case 'CRLF':
return '\r\n'
case 'OS Default':
default:
return helpers.getProcessPlatform() === 'win32' ? '\r\n' : '\n'
}
}
function getLineEndings (buffer) {
if (typeof buffer.find === 'function') {
return Promise.all([buffer.find(LFRegExp), buffer.find(CRLFRegExp)]).then(
([hasLF, hasCRLF]) => {
const result = new Set()
if (hasLF) result.add('\n')
if (hasCRLF) result.add('\r\n')
return result
}
)
} else {
return new Promise(resolve => {
const result = new Set()
for (let i = 0; i < buffer.getLineCount() - 1; i++) {
result.add(buffer.lineEndingForRow(i))
}
resolve(result)
})
}
}
function setLineEnding (item, lineEnding) {
if (item && item.getBuffer) {
let buffer = item.getBuffer()
buffer.setPreferredLineEnding(lineEnding)
buffer.setText(buffer.getText().replace(LineEndingRegExp, lineEnding))
}
}