diff --git a/menus/darwin.cson b/menus/darwin.cson index b65f67708b..faf75d141f 100644 --- a/menus/darwin.cson +++ b/menus/darwin.cson @@ -66,6 +66,7 @@ { label: 'Copy Path', command: 'editor:copy-path' } { label: 'Paste', command: 'core:paste' } { label: 'Paste Without Reformatting', command: 'editor:paste-without-reformatting' } + { label: 'Paste Without Reindenting', command: 'editor:paste-without-reindenting' } { label: 'Select All', command: 'core:select-all' } { type: 'separator' } { label: 'Toggle Comments', command: 'editor:toggle-line-comments' } diff --git a/menus/linux.cson b/menus/linux.cson index e4f6a0532c..69127c849f 100644 --- a/menus/linux.cson +++ b/menus/linux.cson @@ -39,6 +39,7 @@ { label: 'Copy Pat&h', command: 'editor:copy-path' } { label: '&Paste', command: 'core:paste' } { label: 'Paste Without Reformatting', command: 'editor:paste-without-reformatting' } + { label: 'Paste Without Reindenting', command: 'editor:paste-without-reindenting' } { label: 'Select &All', command: 'core:select-all' } { type: 'separator' } { label: '&Toggle Comments', command: 'editor:toggle-line-comments' } diff --git a/menus/win32.cson b/menus/win32.cson index 17ae6df142..79e8deacc9 100644 --- a/menus/win32.cson +++ b/menus/win32.cson @@ -47,6 +47,7 @@ { label: 'Copy Pat&h', command: 'editor:copy-path' } { label: '&Paste', command: 'core:paste' } { label: 'Paste Without Reformatting', command: 'editor:paste-without-reformatting' } + { label: 'Paste Without Reindenting', command: 'editor:paste-without-reindenting' } { label: 'Select &All', command: 'core:select-all' } { type: 'separator' } { label: '&Toggle Comments', command: 'editor:toggle-line-comments' } diff --git a/spec/clipboard-spec.js b/spec/clipboard-spec.js index 7e30f10d79..d500cc60ab 100644 --- a/spec/clipboard-spec.js +++ b/spec/clipboard-spec.js @@ -37,5 +37,26 @@ describe('Clipboard', () => { Object.defineProperty(process, 'platform', { value: originalPlatform }); }); } + + it('does not convert line endings when the setting is off', () => { + atom.config.set('editor.convertLineEndingOnCopy', 'off'); + atom.clipboard.write('next\ndone\r\n\n'); + expect(atom.clipboard.read()).toEqual('next\ndone\r\n\n'); + }); + + it('converts line endings to LF when the setting is LF', () => { + atom.config.set('editor.convertLineEndingOnCopy', 'LF'); + atom.clipboard.write('next\ndone\r\n\n'); + expect(atom.clipboard.read()).toEqual('next\ndone\n\n'); + }); + + it('converts line endings to CRLF when the setting is CRLF', () => { + atom.config.set('editor.convertLineEndingOnCopy', 'CRLF'); + atom.clipboard.write('next\ndone\r\n\n'); + expect(atom.clipboard.read()).toEqual('next\r\ndone\r\n\r\n'); + + // Cleanup: Back to the default setting + atom.config.set('editor.convertLineEndingOnCopy', 'system'); + }); }); }); diff --git a/src/clipboard.js b/src/clipboard.js index 2dd58ff8b6..5dadfd7be3 100644 --- a/src/clipboard.js +++ b/src/clipboard.js @@ -53,7 +53,16 @@ module.exports = class Clipboard { // * `text` The {String} to store. // * `metadata` (optional) The additional info to associate with the text. write(text, metadata) { - text = text.replace(/\r?\n/g, process.platform === 'win32' ? '\r\n' : '\n'); + const lineEndingOption = atom.config.get('editor.convertLineEndingOnCopy'); + if (lineEndingOption !== "off") { + text = text.replace( + /\r?\n/g, + lineEndingOption === "CRLF" || + (lineEndingOption === "system" && process.platform === 'win32') + ? '\r\n' + : '\n' + ); + } this.signatureForMetadata = this.md5(text); this.metadata = metadata; diff --git a/src/config-schema.js b/src/config-schema.js index a46ba18b50..d16225e139 100644 --- a/src/config-schema.js +++ b/src/config-schema.js @@ -1,6 +1,6 @@ -// This is loaded by atom-environment.coffee. See -// https://atom.io/docs/api/latest/Config for more information about config TODO: Link to Pulsar API site when documented -// schemas. +// This is loaded by atom-environment.js. +// See https://atom.io/docs/api/latest/Config for more information about config schemas. +// TODO: Link to Pulsar API site when documented const configSchema = { core: { type: 'object', @@ -496,6 +496,13 @@ const configSchema = { description: 'Automatically indent pasted text based on the indentation of the previous line.' }, + convertLineEndingOnCopy: { + type: 'string', + default: 'system', + enum: ['system', 'off', 'LF', 'CRLF'], + description: + 'Changes how newlines are converted when text is copied. When set to "system", newlines are changed to CRLF on Windows and LF on Unix. When set to "off", newlines are copied as-is.' + }, nonWordCharacters: { type: 'string', default: '/\\()"\':,.;<>~!@#$%^&*|+=[]{}`?-…', diff --git a/src/pane-element.js b/src/pane-element.js index 60b8cf5d00..0de590280d 100644 --- a/src/pane-element.js +++ b/src/pane-element.js @@ -1,6 +1,7 @@ const path = require('path'); const { CompositeDisposable } = require('event-kit'); +// The HTMLElement corresponding to a {Pane}. class PaneElement extends HTMLElement { constructor() { super(); @@ -72,6 +73,16 @@ class PaneElement extends HTMLElement { this.addEventListener('drop', handleDrop); } + // Sets up callbacks when PaneElement initializes + // + // Only called in {Pane::getElement} as of February 2025. + // + // * `model` The container {Pane}. + // * An {Object} with the following keys: + // * `views` A {ViewRegistry} used to hide and show pane items. + // * `applicationDelegate` An {ApplicationDelegate} used to open file paths. + // + // Returns this {PaneElement}. initialize(model, { views, applicationDelegate }) { this.model = model; this.views = views; diff --git a/src/register-default-commands.js b/src/register-default-commands.js index fedfc64517..18bd4cde2a 100644 --- a/src/register-default-commands.js +++ b/src/register-default-commands.js @@ -449,6 +449,11 @@ module.exports = function({commandRegistry, commandInstaller, config, notificati 'core:paste': function() { return this.pasteText(); }, + 'editor:paste-without-reindenting': function() { + return this.pasteText({ + autoIndent: false + }); + }, 'editor:paste-without-reformatting': function() { return this.pasteText({ normalizeLineEndings: false, diff --git a/src/view-registry.js b/src/view-registry.js index eaf00e18c1..2c1b4a3697 100644 --- a/src/view-registry.js +++ b/src/view-registry.js @@ -5,8 +5,8 @@ const AnyConstructor = Symbol('any-constructor'); // Essential: `ViewRegistry` handles the association between model and view // types in Pulsar. We call this association a View Provider. As in, for a given -// model, this class can provide a view via {::getView}, as long as the -// model/view association was registered via {::addViewProvider} +// model, this class can provide a view (a DOMElement) via {::getView}, as long +// as the model/view association was registered via {::addViewProvider} // // If you're adding your own kind of pane item, a good strategy for all but the // simplest items is to separate the model and the view. The model handles