diff --git a/CHANGELOG.md b/CHANGELOG.md index 8823bd9cc..d4e2254d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,6 @@ See https://atom.io/releases + +## 1.3.0 + +* The tree-view now sorts directory entries more naturally, in a locale-sensitive way. +* Lines can now be moved up and down with multiple cursors. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c4dee3c17..8d215e1fe 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,7 +12,7 @@ These are just guidelines, not rules, use your best judgment and feel free to pr * [Atom and Packages](#atom-and-packages) [How Can I Contribute?](#how-can-i-contribute) - * [Submitting Issues](#submitting-issues) + * [Reporting Bugs](#reporting-bugs) * [Your First Code Contribution](#your-first-code-contribution) * [Pull Requests](#pull-requests) @@ -71,30 +71,91 @@ For more information on how to work with Atom's official packages, see [Contribu Also, because Atom is so extensible, it's possible that a feature you've become accustomed to in Atom or an issue you're encountering aren't coming from a bundled package at all, but rather a [community package](https://atom.io/packages) you've installed. Each community package has its own repository too, and you should be able to find it in Settings > Packages for the packages you installed and contribute there. -## How can I contribute? +## How Can I Contribute? -### Submitting Issues +### Reporting Bugs -* You can create an issue [here](https://github.com/atom/atom/issues/new), but - before doing that please read the notes below on debugging and submitting issues, - and include as many details as possible with your report. -* Check the [debugging guide](https://atom.io/docs/latest/hacking-atom-debugging) for tips - on debugging. You might be able to find the cause of the problem and fix - things yourself. -* Include the version of Atom you are using and the OS. -* Include screenshots and animated GIFs whenever possible; they are immensely - helpful. -* Include the behavior you expected and other places you've seen that behavior - such as Emacs, vi, Xcode, etc. -* Check the dev tools (`alt-cmd-i`) for errors to include. If the dev tools - are open _before_ the error is triggered, a full stack trace for the error - will be logged. If you can reproduce the error, use this approach to get the - full stack trace and include it in the issue. -* On Mac, check Console.app for stack traces to include if reporting a crash. -* Perform a [cursory search](https://github.com/issues?q=+is%3Aissue+user%3Aatom) - to see if a similar issue has already been submitted. -* Please setup a [profile picture](https://help.github.com/articles/how-do-i-set-up-my-profile-picture) - to make yourself recognizable and so we can all get to know each other better. +This section guides you through submitting a bug report for Atom. Following these guidelines helps maintainers and the community understand your report :pencil:, reproduce the behavior :computer: :computer:, and find related reports :mag_right:. + +Before creating bug reports, please check [this list](#before-submitting-a-bug-report) as you might find out that you don't need to create one. When you are creating a bug report, please [include as many details as possible](#how-do-i-submit-a-good-bug-report). If you'd like, you can use [this template](#template-for-submitting-bug-reports) to structure the information. + +#### Before Submitting A Bug Report + +* **Check the [debugging guide](https://atom.io/docs/latest/hacking-atom-debugging).** You might be able to find the cause of the problem and fix things yourself. Most importantly, check if you can reproduce the problem [in the latest version of Atom](https://atom.io/docs/latest/hacking-atom-debugging#update-to-the-latest-version), if the problem happens when you run Atom in [safe mode](https://atom.io/docs/latest/hacking-atom-debugging#check-if-the-problem-shows-up-in-safe-mode), and if you can get the desired behavior by changing [Atom's or packages' config settings](https://atom.io/docs/latest/hacking-atom-debugging#check-atom-and-package-settings). +* **Check the [FAQs on the forum](https://discuss.atom.io/c/faq)** for a list of common questions and problems. +* **Determine [which repository the problem should be reported in](#atom-and-packages)**. +* **Perform a [cursory search](https://github.com/issues?q=+is%3Aissue+user%3Aatom)** to see if the problem has already been reported. If it has, add a comment to the existing issue instead of opening a new one. + +#### How Do I Submit A (Good) Bug Report? + +Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/). After you've determined [which repository](#atom-and-packages) your bug is related to, create an issue on that repository and provide the following information. + +Explain the problem and include additional details to help maintainers reproduce the problem: + +* **Use a clear and descriptive title** for the issue to identify the problem. +* **Describe the exact steps which reproduce the problem** in as many details as possible. For example, start by explaining how you started Atom, e.g. which command exactly you used in the terminal, or how you started Atom otherwise. When listing steps, **don't just say what you did, but explain how you did it**. For example, if you moved the cursor to the end of a line, explain if you used the mouse, or a keyboard shortcut or an Atom command, and if so which one? +* **Provide specific examples to demonstrate the steps**. Include links to files or GitHub projects, or copy/pasteable snippets, which you use in those examples. If you're providing snippets in the issue, use [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines). +* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior. +* **Explain which behavior you expected to see instead and why.** +* **Include screenshots and animated GIFs** which show you following the described steps and clearly demonstrate the problem. If you use the keyboard while following the steps, **record the GIF with the [Keybinding Resolver](https://github.com/atom/keybinding-resolver) shown**. You can use [this tool](http://www.cockos.com/licecap/) to record GIFs on OSX and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux. +* **If you're reporting that Atom crashed**, include a crash report with a stack trace from the operating system. On OSX, the crash report will be available in `Console.app` under "Diagnostic and usage information" > "User diagnostic reports". Include the crash report in the issue in a [code block](https://help.github.com/articles/markdown-basics/#multiple-lines) or put it in a [gist](https://gist.github.com/) and provide link to that gist. +* **If the problem is related to performance**, include a [CPU profile capture and a screenshot](https://atom.io/docs/latest/hacking-atom-debugging#diagnose-performance-problems-with-the-dev-tools-cpu-profiler) with your report. +* **If the Chrome's developer tools pane is shown without you triggering it**, that normally means that an exception was thrown. The Console tab will include an entry for the exception. Expand the exception so that the stack trace is visible, and provide the full exception and stack trace in a [code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines) and as a screenshot. +* **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened and share more information using the guidelines below. + +Provide more context by answering these questions: + +* **Can you reproduce the problem in [safe mode](https://atom.io/docs/latest/hacking-atom-debugging#check-if-the-problem-shows-up-in-safe-mode)?** +* **Did the problem start happening recently** (e.g. after updating to a new version of Atom) or was this always a problem? +* If the problem started happening recently, **can you reproduce the problem in an older version of Atom?** What's the most recent version in which the problem doesn't happen? You can download older versions of Atom from [the releases page](https://github.com/atom/atom/releases). +* **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens. +* If the problem is related to working with files (e.g. opening and editing files), **does the problem happen for all files and projects or only some?** Does the problem happen only when working with local or remote files (e.g. on network drives), with files of a specific type (e.g. only JavaScript or Python files), with large files or files with very long lines, or with files in a specific encoding? Is there anything else special about the files you are using? + +Include details about your configuration and environment: + +* **Which version of Atom are you using?** You can get the exact version by running `atom -v` in your terminal, or by starting Atom and running the `Application: About` command from the [Command Palette](https://github.com/atom/command-palette). +* **What's the name and version of the OS you're using**? +* **Are you running Atom in a virtual machine?** If so, which VM software are you using and which operating systems and versions are used for the host and the guest? +* **Which [packages](#atom-and-packages) do you have installed?** You can get that list by running `apm list --installed`. +* **Are you using [local configuration files](https://atom.io/docs/latest/using-atom-basic-customization)** `config.cson`, `keymap.cson`, `snippets.cson`, `styles.less` and `init.coffee` to customize Atom? If so, provide the contents of those files, preferably in a [code block](https://help.github.com/articles/markdown-basics/#multiple-lines) or with a link to a [gist](https://gist.github.com/). +* **Are you using Atom with multiple monitors?** If so, can you reproduce the problem when you use a single monitor? +* **Which keyboard layout are you using?** Are you using a US layout or some other layout? + +#### Template For Submitting Bug Reports + + [Short description of problem here] + + **Reproduction Steps:** + + 1. [First Step] + 2. [Second Step] + 3. [Other Steps...] + + **Expected behavior:** + + [Describe expected behavior here] + + **Observed behavior:** + + [Describe observed behavior here] + + **Screenshots and GIFs** + + ![Screenshots and GIFs which follow reproduction steps to demonstrate the problem](url) + + **Atom version:** [Enter Atom version here] + **OS and version:** [Enter OS name and version here] + + **Installed packages:** + + [List of installed packages here] + + **Additional information:** + + * Problem can be reproduced in safe mode: [Yes/No] + * Problem started happening recently, didn't happen in an older version of Atom: [Yes/No] + * Problem can be reliably reproduced, doesn't happen randomly: [Yes/No] + * Problem happens with all files and projects, not only some files or projects: [Yes/No] ### Your First Code Contribution diff --git a/package.json b/package.json index 4cc3964e6..b55d24502 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "url": "https://github.com/atom/atom/issues" }, "license": "MIT", - "electronVersion": "0.34.0", + "electronVersion": "0.34.3", "dependencies": { "async": "0.2.6", "atom-keymap": "^6.1.0", @@ -116,7 +116,7 @@ "symbols-view": "0.110.0", "tabs": "0.88.0", "timecop": "0.33.0", - "tree-view": "0.196.0", + "tree-view": "0.197.0", "update-package-dependencies": "0.10.0", "welcome": "0.32.0", "whitespace": "0.32.0", @@ -139,7 +139,7 @@ "language-mustache": "0.13.0", "language-objective-c": "0.15.0", "language-perl": "0.30.0", - "language-php": "0.32.0", + "language-php": "0.33.0", "language-property-list": "0.8.0", "language-python": "0.41.0", "language-ruby": "0.60.0", diff --git a/spec/fixtures/sample-with-many-folds.js b/spec/fixtures/sample-with-many-folds.js new file mode 100644 index 000000000..a3c5b7acc --- /dev/null +++ b/spec/fixtures/sample-with-many-folds.js @@ -0,0 +1,12 @@ +1; +2; +function f3() { + return 4; +}; +6; +7; +function f8() { + return 9; +}; +11; +12; diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index ed299ab54..552a0ee7c 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -2123,6 +2123,692 @@ describe "TextEditor", -> expect(editor2.getSelectedBufferRanges()).not.toEqual editor.getSelectedBufferRanges() describe "buffer manipulation", -> + describe ".moveLineUp", -> + it "moves the line under the cursor up", -> + editor.setCursorBufferPosition([1, 0]) + editor.moveLineUp() + expect(editor.getTextInBufferRange([[0, 0], [0, 30]])).toBe " var sort = function(items) {" + expect(editor.indentationForBufferRow(0)).toBe 1 + expect(editor.indentationForBufferRow(1)).toBe 0 + + it "updates the line's indentation when the editor.autoIndent setting is true", -> + atom.config.set('editor.autoIndent', true) + editor.setCursorBufferPosition([1, 0]) + editor.moveLineUp() + expect(editor.indentationForBufferRow(0)).toBe 0 + expect(editor.indentationForBufferRow(1)).toBe 0 + + describe "when there is a single selection", -> + describe "when the selection spans a single line", -> + describe "when there is no fold in the preceeding row", -> + it "moves the line to the preceding row", -> + expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];" + + editor.setSelectedBufferRange([[3, 2], [3, 9]]) + editor.moveLineUp() + + expect(editor.getSelectedBufferRange()).toEqual [[2, 2], [2, 9]] + expect(editor.lineTextForBufferRow(2)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" + + describe "when the cursor is at the beginning of a fold", -> + it "moves the line to the previous row without breaking the fold", -> + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.createFold(4, 7) + editor.setSelectedBufferRange([[4, 2], [4, 9]], preserveFolds: true) + expect(editor.getSelectedBufferRange()).toEqual [[4, 2], [4, 9]] + + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + + editor.moveLineUp() + + expect(editor.getSelectedBufferRange()).toEqual [[3, 2], [3, 9]] + expect(editor.lineTextForBufferRow(3)).toBe " while(items.length > 0) {" + expect(editor.lineTextForBufferRow(7)).toBe " var pivot = items.shift(), current, left = [], right = [];" + + expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeFalsy() + + + describe "when the preceding row consists of folded code", -> + it "moves the line above the folded row and preseveres the correct folds", -> + expect(editor.lineTextForBufferRow(8)).toBe " return sort(left).concat(pivot).concat(sort(right));" + expect(editor.lineTextForBufferRow(9)).toBe " };" + + editor.createFold(4, 7) + + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + + editor.setSelectedBufferRange([[8, 0], [8, 4]]) + editor.moveLineUp() + + expect(editor.getSelectedBufferRange()).toEqual [[4, 0], [4, 4]] + expect(editor.lineTextForBufferRow(4)).toBe " return sort(left).concat(pivot).concat(sort(right));" + expect(editor.lineTextForBufferRow(5)).toBe " while(items.length > 0) {" + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() + + describe "when the selection spans multiple lines", -> + it "moves the lines spanned by the selection to the preceding row", -> + expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.setSelectedBufferRange([[3, 2], [4, 9]]) + editor.moveLineUp() + + expect(editor.getSelectedBufferRange()).toEqual [[2, 2], [3, 9]] + expect(editor.lineTextForBufferRow(2)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(3)).toBe " while(items.length > 0) {" + expect(editor.lineTextForBufferRow(4)).toBe " if (items.length <= 1) return items;" + + describe "when the selection's end intersects a fold", -> + it "moves the lines to the previous row without breaking the fold", -> + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.createFold(4, 7) + editor.setSelectedBufferRange([[3, 2], [4, 9]], preserveFolds: true) + + expect(editor.isFoldedAtBufferRow(3)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + + editor.moveLineUp() + + expect(editor.getSelectedBufferRange()).toEqual [[2, 2], [3, 9]] + expect(editor.lineTextForBufferRow(2)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(3)).toBe " while(items.length > 0) {" + expect(editor.lineTextForBufferRow(7)).toBe " if (items.length <= 1) return items;" + + expect(editor.isFoldedAtBufferRow(2)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeFalsy() + + describe "when the selection's start intersects a fold", -> + it "moves the lines to the previous row without breaking the fold", -> + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.createFold(4, 7) + editor.setSelectedBufferRange([[4, 2], [8, 9]], preserveFolds: true) + + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() + + editor.moveLineUp() + + expect(editor.getSelectedBufferRange()).toEqual [[3, 2], [7, 9]] + expect(editor.lineTextForBufferRow(3)).toBe " while(items.length > 0) {" + expect(editor.lineTextForBufferRow(7)).toBe " return sort(left).concat(pivot).concat(sort(right));" + expect(editor.lineTextForBufferRow(8)).toBe " var pivot = items.shift(), current, left = [], right = [];" + + expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + + + describe "when the selection spans multiple lines, but ends at column 0", -> + it "does not move the last line of the selection", -> + expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.setSelectedBufferRange([[3, 2], [4, 0]]) + editor.moveLineUp() + + expect(editor.getSelectedBufferRange()).toEqual [[2, 2], [3, 0]] + expect(editor.lineTextForBufferRow(2)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + describe "when the preceeding row is a folded row", -> + it "moves the lines spanned by the selection to the preceeding row, but preserves the folded code", -> + expect(editor.lineTextForBufferRow(8)).toBe " return sort(left).concat(pivot).concat(sort(right));" + expect(editor.lineTextForBufferRow(9)).toBe " };" + + editor.createFold(4, 7) + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + + editor.setSelectedBufferRange([[8, 0], [9, 2]]) + editor.moveLineUp() + + expect(editor.getSelectedBufferRange()).toEqual [[4, 0], [5, 2]] + expect(editor.lineTextForBufferRow(4)).toBe " return sort(left).concat(pivot).concat(sort(right));" + expect(editor.lineTextForBufferRow(5)).toBe " };" + expect(editor.lineTextForBufferRow(6)).toBe " while(items.length > 0) {" + expect(editor.isFoldedAtBufferRow(5)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(9)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(10)).toBeFalsy() + + describe "when there are multiple selections", -> + describe "when all the selections span different lines", -> + describe "when there is no folds", -> + it "moves all lines that are spanned by a selection to the preceding row", -> + editor.setSelectedBufferRanges([[[1, 2], [1, 9]], [[3, 2], [3, 9]], [[5, 2], [5, 9]]]) + editor.moveLineUp() + + expect(editor.getSelectedBufferRanges()).toEqual [[[0, 2], [0, 9]], [[2, 2], [2, 9]], [[4, 2], [4, 9]]] + expect(editor.lineTextForBufferRow(0)).toBe " var sort = function(items) {" + expect(editor.lineTextForBufferRow(1)).toBe "var quicksort = function () {" + expect(editor.lineTextForBufferRow(2)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(4)).toBe " current = items.shift();" + expect(editor.lineTextForBufferRow(5)).toBe " while(items.length > 0) {" + + describe "when one selection intersects a fold", -> + it "moves the lines to the previous row without breaking the fold", -> + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.createFold(4, 7) + editor.setSelectedBufferRanges([ + [[2, 2], [2, 9]], + [[4, 2], [4, 9]] + ], preserveFolds: true) + + expect(editor.isFoldedAtBufferRow(2)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(3)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() + + editor.moveLineUp() + + expect(editor.getSelectedBufferRanges()).toEqual([ + [[1, 2], [1, 9]], + [[3, 2], [3, 9]] + ]) + + expect(editor.lineTextForBufferRow(1)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(2)).toBe " var sort = function(items) {" + expect(editor.lineTextForBufferRow(3)).toBe " while(items.length > 0) {" + expect(editor.lineTextForBufferRow(7)).toBe " var pivot = items.shift(), current, left = [], right = [];" + + expect(editor.isFoldedAtBufferRow(1)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(2)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + + describe "when there is a fold", -> + it "moves all lines that spanned by a selection to preceding row, preserving all folds", -> + editor.createFold(4, 7) + + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + + editor.setSelectedBufferRanges([[[8, 0], [8, 3]], [[11, 0], [11, 5]]]) + editor.moveLineUp() + + expect(editor.getSelectedBufferRanges()).toEqual [[[4, 0], [4, 3]], [[10, 0], [10, 5]]] + expect(editor.lineTextForBufferRow(4)).toBe " return sort(left).concat(pivot).concat(sort(right));" + expect(editor.lineTextForBufferRow(10)).toBe " return sort(Array.apply(this, arguments));" + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() + + describe 'when there are many folds', -> + beforeEach -> + waitsForPromise -> + atom.workspace.open('sample-with-many-folds.js', autoIndent: false).then (o) -> editor = o + + describe 'and many selections intersects folded rows', -> + it 'moves and preserves all the folds', -> + editor.createFold(2, 4) + editor.createFold(7, 9) + + editor.setSelectedBufferRanges([ + [[1, 0], [5, 4]], + [[7, 0], [7, 4]] + ], preserveFolds: true) + + editor.moveLineUp() + + expect(editor.lineTextForBufferRow(1)).toEqual "function f3() {" + expect(editor.lineTextForBufferRow(4)).toEqual "6;" + expect(editor.lineTextForBufferRow(5)).toEqual "1;" + expect(editor.lineTextForBufferRow(6)).toEqual "function f8() {" + expect(editor.lineTextForBufferRow(9)).toEqual "7;" + + expect(editor.isFoldedAtBufferRow(1)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(2)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(4)).toBeFalsy() + + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() + + describe "when some of the selections span the same lines", -> + it "moves lines that contain multiple selections correctly", -> + editor.setSelectedBufferRanges([[[3, 2], [3, 9]], [[3, 12], [3, 13]]]) + editor.moveLineUp() + + expect(editor.getSelectedBufferRanges()).toEqual [[[2, 2], [2, 9]], [[2, 12], [2, 13]]] + expect(editor.lineTextForBufferRow(2)).toBe " var pivot = items.shift(), current, left = [], right = [];" + + describe "when one of the selections spans line 0", -> + it "doesn't move any lines, since line 0 can't move", -> + editor.setSelectedBufferRanges([[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[4, 2], [4, 9]]]) + + editor.moveLineUp() + + expect(editor.getSelectedBufferRanges()).toEqual [[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[4, 2], [4, 9]]] + expect(buffer.isModified()).toBe false + + describe "when one of the selections spans the last line, and it is empty", -> + it "doesn't move any lines, since the last line can't move", -> + buffer.append('\n') + editor.setSelectedBufferRanges([[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[13, 0], [13, 0]]]) + + editor.moveLineUp() + + expect(editor.getSelectedBufferRanges()).toEqual [[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[13, 0], [13, 0]]] + + describe ".moveLineDown", -> + it "moves the line under the cursor down", -> + editor.setCursorBufferPosition([0, 0]) + editor.moveLineDown() + expect(editor.getTextInBufferRange([[1, 0], [1, 31]])).toBe "var quicksort = function () {" + expect(editor.indentationForBufferRow(0)).toBe 1 + expect(editor.indentationForBufferRow(1)).toBe 0 + + it "updates the line's indentation when the editor.autoIndent setting is true", -> + atom.config.set('editor.autoIndent', true) + editor.setCursorBufferPosition([0, 0]) + editor.moveLineDown() + expect(editor.indentationForBufferRow(0)).toBe 1 + expect(editor.indentationForBufferRow(1)).toBe 2 + + describe "when there is a single selection", -> + describe "when the selection spans a single line", -> + describe "when there is no fold in the following row", -> + it "moves the line to the following row", -> + expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];" + + editor.setSelectedBufferRange([[2, 2], [2, 9]]) + editor.moveLineDown() + + expect(editor.getSelectedBufferRange()).toEqual [[3, 2], [3, 9]] + expect(editor.lineTextForBufferRow(2)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" + + describe "when the cursor is at the beginning of a fold", -> + it "moves the line to the following row without breaking the fold", -> + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.createFold(4, 7) + editor.setSelectedBufferRange([[4, 2], [4, 9]], preserveFolds: true) + + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + + editor.moveLineDown() + + expect(editor.getSelectedBufferRange()).toEqual [[5, 2], [5, 9]] + expect(editor.lineTextForBufferRow(4)).toBe " return sort(left).concat(pivot).concat(sort(right));" + expect(editor.lineTextForBufferRow(5)).toBe " while(items.length > 0) {" + + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() + + describe "when the following row is a folded row", -> + it "moves the line below the folded row and preserves the fold", -> + expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.createFold(4, 7) + + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + + editor.setSelectedBufferRange([[3, 0], [3, 4]]) + editor.moveLineDown() + + expect(editor.getSelectedBufferRange()).toEqual [[7, 0], [7, 4]] + expect(editor.lineTextForBufferRow(3)).toBe " while(items.length > 0) {" + expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeFalsy() + + + expect(editor.lineTextForBufferRow(7)).toBe " var pivot = items.shift(), current, left = [], right = [];" + + describe "when the selection spans multiple lines", -> + it "moves the lines spanned by the selection to the following row", -> + expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.setSelectedBufferRange([[2, 2], [3, 9]]) + editor.moveLineDown() + + expect(editor.getSelectedBufferRange()).toEqual [[3, 2], [4, 9]] + expect(editor.lineTextForBufferRow(2)).toBe " while(items.length > 0) {" + expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(4)).toBe " var pivot = items.shift(), current, left = [], right = [];" + + describe "when the selection spans multiple lines, but ends at column 0", -> + it "does not move the last line of the selection", -> + expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.setSelectedBufferRange([[2, 2], [3, 0]]) + editor.moveLineDown() + + expect(editor.getSelectedBufferRange()).toEqual [[3, 2], [4, 0]] + expect(editor.lineTextForBufferRow(2)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + describe "when the selection's end intersects a fold", -> + it "moves the lines to the following row without breaking the fold", -> + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.createFold(4, 7) + editor.setSelectedBufferRange([[3, 2], [4, 9]], preserveFolds: true) + + expect(editor.isFoldedAtBufferRow(3)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + + editor.moveLineDown() + + expect(editor.getSelectedBufferRange()).toEqual [[4, 2], [5, 9]] + expect(editor.lineTextForBufferRow(3)).toBe " return sort(left).concat(pivot).concat(sort(right));" + expect(editor.lineTextForBufferRow(4)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(5)).toBe " while(items.length > 0) {" + + expect(editor.isFoldedAtBufferRow(4)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() + + describe "when the selection's start intersects a fold", -> + it "moves the lines to the following row without breaking the fold", -> + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.createFold(4, 7) + editor.setSelectedBufferRange([[4, 2], [8, 9]], preserveFolds: true) + + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() + + editor.moveLineDown() + + expect(editor.getSelectedBufferRange()).toEqual [[5, 2], [9, 9]] + expect(editor.lineTextForBufferRow(4)).toBe " };" + expect(editor.lineTextForBufferRow(5)).toBe " while(items.length > 0) {" + expect(editor.lineTextForBufferRow(9)).toBe " return sort(left).concat(pivot).concat(sort(right));" + + expect(editor.isFoldedAtBufferRow(4)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(10)).toBeFalsy() + + describe "when the following row is a folded row", -> + it "moves the lines spanned by the selection to the following row, but preserves the folded code", -> + expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];" + + editor.createFold(4, 7) + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + + editor.setSelectedBufferRange([[2, 0], [3, 2]]) + editor.moveLineDown() + + expect(editor.getSelectedBufferRange()).toEqual [[6, 0], [7, 2]] + expect(editor.lineTextForBufferRow(2)).toBe " while(items.length > 0) {" + expect(editor.isFoldedAtBufferRow(1)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(2)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeFalsy() + expect(editor.lineTextForBufferRow(6)).toBe " if (items.length <= 1) return items;" + + describe "when there are multiple selections", -> + describe "when all the selections span different lines", -> + describe "when there is no folds", -> + it "moves all lines that are spanned by a selection to the following row", -> + editor.setSelectedBufferRanges([[[1, 2], [1, 9]], [[3, 2], [3, 9]], [[5, 2], [5, 9]]]) + editor.moveLineDown() + + expect(editor.getSelectedBufferRanges()).toEqual [[[6, 2], [6, 9]], [[4, 2], [4, 9]], [[2, 2], [2, 9]]] + expect(editor.lineTextForBufferRow(1)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(2)).toBe " var sort = function(items) {" + expect(editor.lineTextForBufferRow(3)).toBe " while(items.length > 0) {" + expect(editor.lineTextForBufferRow(4)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(5)).toBe " current < pivot ? left.push(current) : right.push(current);" + expect(editor.lineTextForBufferRow(6)).toBe " current = items.shift();" + + describe 'when there are many folds', -> + beforeEach -> + waitsForPromise -> + atom.workspace.open('sample-with-many-folds.js', autoIndent: false).then (o) -> editor = o + + describe 'and many selections intersects folded rows', -> + it 'moves and preserves all the folds', -> + editor.createFold(2, 4) + editor.createFold(7, 9) + + editor.setSelectedBufferRanges([ + [[2, 0], [2, 4]], + [[6, 0], [10, 4]] + ], preserveFolds: true) + + editor.moveLineDown() + + expect(editor.lineTextForBufferRow(2)).toEqual "6;" + expect(editor.lineTextForBufferRow(3)).toEqual "function f3() {" + expect(editor.lineTextForBufferRow(6)).toEqual "12;" + expect(editor.lineTextForBufferRow(7)).toEqual "7;" + expect(editor.lineTextForBufferRow(8)).toEqual "function f8() {" + expect(editor.lineTextForBufferRow(11)).toEqual "11;" + + expect(editor.isFoldedAtBufferRow(2)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(7)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(9)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(10)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(11)).toBeFalsy() + + describe "when there is a fold below one of the selected row", -> + it "moves all lines spanned by a selection to the following row, preserving the fold", -> + editor.createFold(4, 7) + + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + + editor.setSelectedBufferRanges([[[1, 2], [1, 6]], [[3, 0], [3, 4]], [[8, 0], [8, 3]]]) + editor.moveLineDown() + + expect(editor.getSelectedBufferRanges()).toEqual [[[9, 0], [9, 3]], [[7, 0], [7, 4]], [[2, 2], [2, 6]]] + expect(editor.lineTextForBufferRow(2)).toBe " var sort = function(items) {" + expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeFalsy() + expect(editor.lineTextForBufferRow(7)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(9)).toBe " return sort(left).concat(pivot).concat(sort(right));" + + describe "when there is a fold below a group of multiple selections without any lines with no selection in-between", -> + it "moves all the lines below the fold, preserving the fold", -> + editor.createFold(4, 7) + + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + + editor.setSelectedBufferRanges([[[2, 2], [2, 6]], [[3, 0], [3, 4]]]) + editor.moveLineDown() + + expect(editor.getSelectedBufferRanges()).toEqual [[[7, 0], [7, 4]], [[6, 2], [6, 6]]] + expect(editor.lineTextForBufferRow(2)).toBe " while(items.length > 0) {" + expect(editor.isFoldedAtBufferRow(2)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeFalsy() + expect(editor.lineTextForBufferRow(6)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(7)).toBe " var pivot = items.shift(), current, left = [], right = [];" + + describe "when one selection intersects a fold", -> + it "moves the lines to the previous row without breaking the fold", -> + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.createFold(4, 7) + editor.setSelectedBufferRanges([ + [[2, 2], [2, 9]], + [[4, 2], [4, 9]] + ], preserveFolds: true) + + expect(editor.isFoldedAtBufferRow(2)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(3)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() + + editor.moveLineDown() + + expect(editor.getSelectedBufferRanges()).toEqual([ + [[5, 2], [5, 9]] + [[3, 2], [3, 9]], + ]) + + expect(editor.lineTextForBufferRow(2)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(4)).toBe " return sort(left).concat(pivot).concat(sort(right));" + + expect(editor.lineTextForBufferRow(5)).toBe " while(items.length > 0) {" + expect(editor.lineTextForBufferRow(9)).toBe " };" + + expect(editor.isFoldedAtBufferRow(2)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(3)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(4)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() + + describe "when some of the selections span the same lines", -> + it "moves lines that contain multiple selections correctly", -> + editor.setSelectedBufferRanges([[[3, 2], [3, 9]], [[3, 12], [3, 13]]]) + editor.moveLineDown() + + expect(editor.getSelectedBufferRanges()).toEqual [[[4, 12], [4, 13]], [[4, 2], [4, 9]]] + expect(editor.lineTextForBufferRow(3)).toBe " while(items.length > 0) {" + + describe "when the selections are above a wrapped line", -> + beforeEach -> + editor.setSoftWrapped(true) + editor.setEditorWidthInChars(80) + editor.setText(""" + 1 + 2 + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + 3 + 4 + """) + + it 'moves the lines past the soft wrapped line', -> + editor.setSelectedBufferRanges([[[0, 0], [0, 0]], [[1, 0], [1, 0]]]) + + editor.moveLineDown() + + expect(editor.lineTextForBufferRow(0)).not.toBe "2" + expect(editor.lineTextForBufferRow(1)).toBe "1" + expect(editor.lineTextForBufferRow(2)).toBe "2" + describe ".insertText(text)", -> describe "when there is a single selection", -> beforeEach -> @@ -4492,36 +5178,6 @@ describe "TextEditor", -> """ expect(editor.getSelectedBufferRange()).toEqual [[13, 0], [14, 2]] - describe ".moveLineUp()", -> - it "moves the line under the cursor up", -> - editor.setCursorBufferPosition([1, 0]) - editor.moveLineUp() - expect(editor.getTextInBufferRange([[0, 0], [0, 30]])).toBe " var sort = function(items) {" - expect(editor.indentationForBufferRow(0)).toBe 1 - expect(editor.indentationForBufferRow(1)).toBe 0 - - it "updates the line's indentation when the editor.autoIndent setting is true", -> - atom.config.set('editor.autoIndent', true) - editor.setCursorBufferPosition([1, 0]) - editor.moveLineUp() - expect(editor.indentationForBufferRow(0)).toBe 0 - expect(editor.indentationForBufferRow(1)).toBe 0 - - describe ".moveLineDown()", -> - it "moves the line under the cursor down", -> - editor.setCursorBufferPosition([0, 0]) - editor.moveLineDown() - expect(editor.getTextInBufferRange([[1, 0], [1, 31]])).toBe "var quicksort = function () {" - expect(editor.indentationForBufferRow(0)).toBe 1 - expect(editor.indentationForBufferRow(1)).toBe 0 - - it "updates the line's indentation when the editor.autoIndent setting is true", -> - atom.config.set('editor.autoIndent', true) - editor.setCursorBufferPosition([0, 0]) - editor.moveLineDown() - expect(editor.indentationForBufferRow(0)).toBe 1 - expect(editor.indentationForBufferRow(1)).toBe 2 - describe ".shouldPromptToSave()", -> it "returns false when an edit session's buffer is in use by more than one session", -> jasmine.unspy(editor, 'shouldPromptToSave') diff --git a/src/browser/atom-window.coffee b/src/browser/atom-window.coffee index a346f5c77..c507b634c 100644 --- a/src/browser/atom-window.coffee +++ b/src/browser/atom-window.coffee @@ -28,7 +28,6 @@ class AtomWindow title: 'Atom' 'web-preferences': 'direct-write': true - 'subpixel-font-scaling': true if @isSpec options['web-preferences']['page-visibility'] = true diff --git a/src/initialize-test-window.coffee b/src/initialize-test-window.coffee index f33cca09d..72a071fb6 100644 --- a/src/initialize-test-window.coffee +++ b/src/initialize-test-window.coffee @@ -57,6 +57,12 @@ module.exports = ({blobStore}) -> document.title = "Spec Suite" + # Avoid throttling of test window by playing silence + context = new AudioContext() + source = context.createBufferSource() + source.connect(context.destination) + source.start(0) + testRunner = require(testRunnerPath) legacyTestRunner = require(legacyTestRunnerPath) buildDefaultApplicationDelegate = -> new ApplicationDelegate() diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 04052751d..3120a1600 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -866,116 +866,177 @@ class TextEditor extends Model @transact groupingInterval, => fn(selection, index) for selection, index in @getSelectionsOrderedByBufferPosition() - # Move lines intersection the most recent selection up by one row in screen - # coordinates. + # Move lines intersecting the most recent selection or multiple selections + # up by one row in screen coordinates. moveLineUp: -> - selection = @getSelectedBufferRange() - return if selection.start.row is 0 - lastRow = @buffer.getLastRow() - return if selection.isEmpty() and selection.start.row is lastRow and @buffer.getLastLine() is '' + selections = @getSelectedBufferRanges() + selections.sort (a, b) -> a.compare(b) + + if selections[0].start.row is 0 + return + + if selections[selections.length - 1].start.row is @getLastBufferRow() and @buffer.getLastLine() is '' + return @transact => - foldedRows = [] - rows = [selection.start.row..selection.end.row] - if selection.start.row isnt selection.end.row and selection.end.column is 0 - rows.pop() unless @isFoldedAtBufferRow(selection.end.row) + newSelectionRanges = [] - # Move line around the fold that is directly above the selection - precedingScreenRow = @screenPositionForBufferPosition([selection.start.row]).translate([-1]) - precedingBufferRow = @bufferPositionForScreenPosition(precedingScreenRow).row - if fold = @largestFoldContainingBufferRow(precedingBufferRow) - insertDelta = fold.getBufferRange().getRowCount() - else - insertDelta = 1 + while selections.length > 0 + # Find selections spanning a contiguous set of lines + selection = selections.shift() + selectionsToMove = [selection] - for row in rows - if fold = @displayBuffer.largestFoldStartingAtBufferRow(row) - bufferRange = fold.getBufferRange() - startRow = bufferRange.start.row - endRow = bufferRange.end.row - foldedRows.push(startRow - insertDelta) + while selection.end.row is selections[0]?.start.row + selectionsToMove.push(selections[0]) + selection.end.row = selections[0].end.row + selections.shift() + + # Compute the range spanned by all these selections... + linesRangeStart = [selection.start.row, 0] + if selection.end.row > selection.start.row and selection.end.column is 0 + # Don't move the last line of a multi-line selection if the selection ends at column 0 + linesRange = new Range(linesRangeStart, selection.end) else - startRow = row - endRow = row + linesRange = new Range(linesRangeStart, [selection.end.row + 1, 0]) - insertPosition = Point.fromObject([startRow - insertDelta]) - endPosition = Point.min([endRow + 1], @buffer.getEndPosition()) - lines = @buffer.getTextInRange([[startRow], endPosition]) - if endPosition.row is lastRow and endPosition.column > 0 and not @buffer.lineEndingForRow(endPosition.row) - lines = "#{lines}\n" + # If there's a fold containing either the starting row or the end row + # of the selection then the whole fold needs to be moved and restored. + # The initial fold range is stored and will be translated once the + # insert delta is know. + selectionFoldRanges = [] + foldAtSelectionStart = + @displayBuffer.largestFoldContainingBufferRow(selection.start.row) + foldAtSelectionEnd = + @displayBuffer.largestFoldContainingBufferRow(selection.end.row) + if fold = foldAtSelectionStart ? foldAtSelectionEnd + selectionFoldRanges.push range = fold.getBufferRange() + newEndRow = range.end.row + 1 + linesRange.end.row = newEndRow if newEndRow > linesRange.end.row + fold.destroy() - @buffer.deleteRows(startRow, endRow) + # If selected line range is preceded by a fold, one line above on screen + # could be multiple lines in the buffer. + precedingScreenRow = @screenRowForBufferRow(linesRange.start.row) - 1 + precedingBufferRow = @bufferRowForScreenRow(precedingScreenRow) + insertDelta = linesRange.start.row - precedingBufferRow + + # Any folds in the text that is moved will need to be re-created. + # It includes the folds that were intersecting with the selection. + rangesToRefold = selectionFoldRanges.concat( + @outermostFoldsInBufferRowRange(linesRange.start.row, linesRange.end.row).map (fold) -> + range = fold.getBufferRange() + fold.destroy() + range + ).map (range) -> range.translate([-insertDelta, 0]) # Make sure the inserted text doesn't go into an existing fold - if fold = @displayBuffer.largestFoldStartingAtBufferRow(insertPosition.row) - @unfoldBufferRow(insertPosition.row) - foldedRows.push(insertPosition.row + endRow - startRow + fold.getBufferRange().getRowCount()) + if fold = @displayBuffer.largestFoldStartingAtBufferRow(precedingBufferRow) + rangesToRefold.push(fold.getBufferRange().translate([linesRange.getRowCount() - 1, 0])) + fold.destroy() - @buffer.insert(insertPosition, lines) + # Delete lines spanned by selection and insert them on the preceding buffer row + lines = @buffer.getTextInRange(linesRange) + lines += @buffer.lineEndingForRow(linesRange.end.row - 1) unless lines[lines.length - 1] is '\n' + @buffer.delete(linesRange) + @buffer.insert([precedingBufferRow, 0], lines) - # Restore folds that existed before the lines were moved - for foldedRow in foldedRows when 0 <= foldedRow <= @getLastBufferRow() - @foldBufferRow(foldedRow) + # Restore folds that existed before the lines were moved + for rangeToRefold in rangesToRefold + @displayBuffer.createFold(rangeToRefold.start.row, rangeToRefold.end.row) - @setSelectedBufferRange(selection.translate([-insertDelta]), preserveFolds: true, autoscroll: true) + for selection in selectionsToMove + newSelectionRanges.push(selection.translate([-insertDelta, 0])) + + @setSelectedBufferRanges(newSelectionRanges, {autoscroll: false, preserveFolds: true}) @autoIndentSelectedRows() if @shouldAutoIndent() + @scrollToBufferPosition([newSelectionRanges[0].start.row, 0]) - # Move lines intersecting the most recent selection down by one row in screen - # coordinates. + # Move lines intersecting the most recent selection or muiltiple selections + # down by one row in screen coordinates. moveLineDown: -> - selection = @getSelectedBufferRange() - lastRow = @buffer.getLastRow() - return if selection.end.row is lastRow - return if selection.end.row is lastRow - 1 and @buffer.getLastLine() is '' + selections = @getSelectedBufferRanges() + selections.sort (a, b) -> a.compare(b) + selections = selections.reverse() @transact => - foldedRows = [] - rows = [selection.end.row..selection.start.row] - if selection.start.row isnt selection.end.row and selection.end.column is 0 - rows.shift() unless @isFoldedAtBufferRow(selection.end.row) + @consolidateSelections() + newSelectionRanges = [] - # Move line around the fold that is directly below the selection - followingScreenRow = @screenPositionForBufferPosition([selection.end.row]).translate([1]) - followingBufferRow = @bufferPositionForScreenPosition(followingScreenRow).row - if fold = @largestFoldContainingBufferRow(followingBufferRow) - insertDelta = fold.getBufferRange().getRowCount() - else - insertDelta = 1 + while selections.length > 0 + # Find selections spanning a contiguous set of lines + selection = selections.shift() + selectionsToMove = [selection] - for row in rows - if fold = @displayBuffer.largestFoldStartingAtBufferRow(row) - bufferRange = fold.getBufferRange() - startRow = bufferRange.start.row - endRow = bufferRange.end.row - foldedRows.push(endRow + insertDelta) + # if the current selection start row matches the next selections' end row - make them one selection + while selection.start.row is selections[0]?.end.row + selectionsToMove.push(selections[0]) + selection.start.row = selections[0].start.row + selections.shift() + + # Compute the range spanned by all these selections... + linesRangeStart = [selection.start.row, 0] + if selection.end.row > selection.start.row and selection.end.column is 0 + # Don't move the last line of a multi-line selection if the selection ends at column 0 + linesRange = new Range(linesRangeStart, selection.end) else - startRow = row - endRow = row + linesRange = new Range(linesRangeStart, [selection.end.row + 1, 0]) - if endRow + 1 is lastRow - endPosition = [endRow, @buffer.lineLengthForRow(endRow)] - else - endPosition = [endRow + 1] - lines = @buffer.getTextInRange([[startRow], endPosition]) - @buffer.deleteRows(startRow, endRow) + # If there's a fold containing either the starting row or the end row + # of the selection then the whole fold needs to be moved and restored. + # The initial fold range is stored and will be translated once the + # insert delta is know. + selectionFoldRanges = [] + foldAtSelectionStart = + @displayBuffer.largestFoldContainingBufferRow(selection.start.row) + foldAtSelectionEnd = + @displayBuffer.largestFoldContainingBufferRow(selection.end.row) + if fold = foldAtSelectionStart ? foldAtSelectionEnd + selectionFoldRanges.push range = fold.getBufferRange() + newEndRow = range.end.row + 1 + linesRange.end.row = newEndRow if newEndRow > linesRange.end.row + fold.destroy() - insertPosition = Point.min([startRow + insertDelta], @buffer.getEndPosition()) - if insertPosition.row is @buffer.getLastRow() and insertPosition.column > 0 + # If selected line range is followed by a fold, one line below on screen + # could be multiple lines in the buffer. But at the same time, if the + # next buffer row is wrapped, one line in the buffer can represent many + # screen rows. + followingScreenRow = @displayBuffer.lastScreenRowForBufferRow(linesRange.end.row) + 1 + followingBufferRow = @bufferRowForScreenRow(followingScreenRow) + insertDelta = followingBufferRow - linesRange.end.row + + # Any folds in the text that is moved will need to be re-created. + # It includes the folds that were intersecting with the selection. + rangesToRefold = selectionFoldRanges.concat( + @outermostFoldsInBufferRowRange(linesRange.start.row, linesRange.end.row).map (fold) -> + range = fold.getBufferRange() + fold.destroy() + range + ).map (range) -> range.translate([insertDelta, 0]) + + # Make sure the inserted text doesn't go into an existing fold + if fold = @displayBuffer.largestFoldStartingAtBufferRow(followingBufferRow) + rangesToRefold.push(fold.getBufferRange().translate([insertDelta - 1, 0])) + fold.destroy() + + # Delete lines spanned by selection and insert them on the following correct buffer row + insertPosition = new Point(selection.translate([insertDelta, 0]).start.row, 0) + lines = @buffer.getTextInRange(linesRange) + if linesRange.end.row is @buffer.getLastRow() lines = "\n#{lines}" - # Make sure the inserted text doesn't go into an existing fold - if fold = @displayBuffer.largestFoldStartingAtBufferRow(insertPosition.row) - @unfoldBufferRow(insertPosition.row) - foldedRows.push(insertPosition.row + fold.getBufferRange().getRowCount()) - + @buffer.delete(linesRange) @buffer.insert(insertPosition, lines) - # Restore folds that existed before the lines were moved - for foldedRow in foldedRows when 0 <= foldedRow <= @getLastBufferRow() - @foldBufferRow(foldedRow) + # Restore folds that existed before the lines were moved + for rangeToRefold in rangesToRefold + @displayBuffer.createFold(rangeToRefold.start.row, rangeToRefold.end.row) - @setSelectedBufferRange(selection.translate([insertDelta]), preserveFolds: true, autoscroll: true) + for selection in selectionsToMove + newSelectionRanges.push(selection.translate([insertDelta, 0])) + + @setSelectedBufferRanges(newSelectionRanges, {autoscroll: false, preserveFolds: true}) @autoIndentSelectedRows() if @shouldAutoIndent() + @scrollToBufferPosition([newSelectionRanges[0].start.row - 1, 0]) # Duplicate the most recent cursor's current line. duplicateLines: ->