Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Add option to ignore current line when trimming whitespace #12

Merged
merged 19 commits into from

4 participants

@watsonian

It's entirely possible that there's a better way to do this, but I came up with this solution. This adds a new ignoreWhitespaceOnCurrentLine option. When enabled, it prevents saves from trimming whitespace on the current line. This is to catch situations where you've just started a new method definition, have the leading indentation in place, and then save. You obviously don't want that whitespace to disappear. Or situations where you're typing a Markdown list, start a new bullet point with a space, and then save. It works with multiple cursors too.

Depends on atom/text-buffer#4 being merged to get TextBuffer#backwardsScan.

watsonian added some commits
@watsonian watsonian Don't touch whitespace on the current line.
It's fairly common to start a method definition and then save
just before starting in on that method. The result is that the
method indentation is removed. This happens in a number of
situations. With this change, whitespace on the current line
is ignored if there's ONLY whitespace on that line.
f362fe6
@watsonian watsonian Add some tests! ac916ac
@watsonian

For a visual example of what this actually does:

before save

xx
def somethingxxx
xxC
end

after save


def something
xxC
end

where x is a space and C is the cursor. Prior to this change you would have ended up with:


def something
C
end

lib/whitespace.coffee
((12 lines not shown))
if grammarScopeName is 'source.gfm'
# GitHub Flavored Markdown permits two spaces at the end of a line
[whitespace] = match
replace('') unless whitespace is ' ' and whitespace isnt lineText
+ else if ignoreCurLine and onlyWhitespace and whitespaceRow is cursorRow
@mutle
mutle added a note

How about this instead to get rid of the empty statement?

else if not (ignoreCurLine and onlyWhitespace and whitespaceRow is cursorRow)
  replace('')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@watsonian

This works fairly well now. I'm going to investigate getting this to work with multiple cursors and also see if it's possible to trim all whitespace after the current cursor position.

lib/whitespace.coffee
@@ -24,8 +24,16 @@ class Whitespace
@subscribe buffer, 'destroyed', =>
@unsubscribe(buffer)
- removeTrailingWhitespace: (buffer, grammarScopeName) ->
+ removeTrailingWhitespace: (editor, grammarScopeName) ->
+ buffer = editor.getBuffer()
+ ignoreCurLine = atom.config.get('whitespace.ignoreWhitespaceOnCurrentLine')
@nathansobo Owner

Can you spell out this variable name? ignoreCurrentLine

Yep, will do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@watsonian

Okay, sorted out the weird seemingly off-by-one issue. The problem has to do with the indexes changing as the preceding whitespace is removed on other lines. I'm going to try changing this so the buffer scan goes in reverse to see if that solves it.

spec/whitespace-spec.coffee
@@ -52,6 +52,41 @@ describe "Whitespace", ->
editor.save()
expect(editor.getText()).toBe "don't trim me \n"
+ describe "whitespace.ignoreWhitespaceOnCurrentLine config", ->
@nathansobo Owner

I'd like to see these specs structured as follows:

describe "when 'whitespace.ignoreWhitespaceOnCurrentLine' is true", ->
  it "removes the whitespace from all lines, excluding the current line", ->

describe "when 'whitespace.ignoreWhitespaceOnCurrentLine' is false", ->
  it "removes the whitespace from all lines, including the current line", ->

I'm not convinced we need a separate test for lines that are only whitespace vs lines that have trailing whitespace. Thoughts?

You're right. It mattered initially because I was only going to have it work against lines that only had whitespace, but since then I changed course and they're not needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
watsonian added some commits
@watsonian watsonian s/ignoreCurLine/ignoreCurrentLine/ ee65af4
@watsonian watsonian Switch to using backwardsScan.
This fixes a bug where the cursor index would change out from under
you as the replacements in the scan were happening. This was causing
the cursor row to be mis-identified, which was resulting in inconsistent
row identification (which looked sort of like an off-by-one error).
By scanning from the end of the buffer, we prevent the index from getting
mangled like that and end up with the correct cursor row.
9ceaa58
@watsonian watsonian referenced this pull request in atom/text-buffer
Merged

Add backwardsScan function #4

@watsonian

@nathansobo Okay, I think this is ready for review now.

spec/whitespace-spec.coffee
((13 lines not shown))
+ it "removes the whitespace from all lines, excluding the current lines", ->
+ editor.insertText "1 \n2 \n3 \n"
+ editor.setCursorBufferPosition([1,3])
+ editor.addCursorAtBufferPosition([2,3])
+ editor.save()
+ expect(editor.getText()).toBe "1\n2 \n3 \n"
+
+ describe "when 'whitespace.ignoreWhitespaceOnCurrentLine' is false", ->
+ [originalConfigValue] = []
+ beforeEach ->
+ originalConfigValue = atom.config.get("whitespace.ignoreWhitespaceOnCurrentLine")
+ expect(originalConfigValue).toBe true
+ atom.config.set("whitespace.ignoreWhitespaceOnCurrentLine", false)
+
+ afterEach ->
+ atom.config.set("whitespace.ignoreWhitespaceOnCurrentLine", originalConfigValue)
@nathansobo Owner

You shouldn't need to reset this as the config is restored after each spec run. If that's not the case there's something wrong.

@nathansobo Owner

For that reason, I'd say ditch the beforeEach calls and just do everything in the it.

Ah, okay. I was following what was already in place with the whitespace.ensureSingleTrailingNewline tests. If that's not needed, I can pull it out for sure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@nathansobo
Owner

Okay, this is looking good, but since it required a new text-buffer API we're going to need to wait for the next Atom release to merge and publish it. Should be soon.

@watsonian

@nathansobo :metal: thanks for helping me out with this! :grin:

@Habbie

+1 for this feature! Came here to complain about annoying interaction between https://github.com/atom/whitespace and https://github.com/atom/autosave, and this would fix it!

@watsonian

The text-buffer change in atom/text-buffer#4 is in the latest Atom release now, so merging this! :metal:

@watsonian watsonian merged commit e354303 into master
@watsonian watsonian deleted the ignore-current-line branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 28, 2014
  1. @watsonian

    Don't touch whitespace on the current line.

    watsonian authored
    It's fairly common to start a method definition and then save
    just before starting in on that method. The result is that the
    method indentation is removed. This happens in a number of
    situations. With this change, whitespace on the current line
    is ignored if there's ONLY whitespace on that line.
  2. @watsonian

    Add some tests!

    watsonian authored
  3. @watsonian

    No need for the no-op line.

    watsonian authored
  4. @watsonian
  5. @watsonian

    Fix off-by-one error.

    watsonian authored
  6. @watsonian
  7. @watsonian
Commits on Mar 1, 2014
  1. @watsonian
  2. @watsonian

    Switch to using backwardsScan.

    watsonian authored
    This fixes a bug where the cursor index would change out from under
    you as the replacements in the scan were happening. This was causing
    the cursor row to be mis-identified, which was resulting in inconsistent
    row identification (which looked sort of like an off-by-one error).
    By scanning from the end of the buffer, we prevent the index from getting
    mangled like that and end up with the correct cursor row.
  3. @watsonian
  4. @watsonian
  5. @watsonian
  6. @watsonian
  7. @watsonian
  8. @watsonian
  9. @watsonian
  10. @watsonian
  11. @watsonian

    Fix GFM tests.

    watsonian authored
  12. @watsonian

    Cleanup and a broken spec.

    watsonian authored
This page is out of date. Refresh to see the latest.
View
1  lib/main.coffee
@@ -3,6 +3,7 @@ Whitespace = require './whitespace'
module.exports =
configDefaults:
removeTrailingWhitespace: true
+ ignoreWhitespaceOnCurrentLine: true
ensureSingleTrailingNewline: true
activate: ->
View
14 lib/whitespace.coffee
@@ -16,7 +16,7 @@ class Whitespace
@subscribe buffer, 'will-be-saved', =>
buffer.transact =>
if atom.config.get('whitespace.removeTrailingWhitespace')
- @removeTrailingWhitespace(buffer, editor.getGrammar().scopeName)
+ @removeTrailingWhitespace(editor, editor.getGrammar().scopeName)
if atom.config.get('whitespace.ensureSingleTrailingNewline')
@ensureSingleTrailingNewline(buffer)
@@ -24,8 +24,16 @@ class Whitespace
@subscribe buffer, 'destroyed', =>
@unsubscribe(buffer)
- removeTrailingWhitespace: (buffer, grammarScopeName) ->
- buffer.scan /[ \t]+$/g, ({lineText, match, replace}) ->
+ removeTrailingWhitespace: (editor, grammarScopeName) ->
+ buffer = editor.getBuffer()
+ ignoreCurrentLine = atom.config.get('whitespace.ignoreWhitespaceOnCurrentLine')
+
+ buffer.backwardsScan /[ \t]+$/g, ({lineText, match, replace}) ->
+ whitespaceRow = buffer.positionForCharacterIndex(match.index).row
+ cursorRows = (cursor.getBufferRow() for cursor in editor.getCursors())
+
+ return if ignoreCurrentLine and whitespaceRow in cursorRows
+
if grammarScopeName is 'source.gfm'
# GitHub Flavored Markdown permits two spaces at the end of a line
[whitespace] = match
View
104 spec/whitespace-spec.coffee
@@ -20,22 +20,6 @@ describe "Whitespace", ->
waitsForPromise ->
atom.packages.activatePackage('whitespace')
- it "strips trailing whitespace before an editor saves a buffer", ->
- atom.config.set("whitespace.ensureSingleTrailingNewline", false)
-
- # works for buffers that are already open when package is initialized
- editor.insertText("foo \nbar\t \n\nbaz")
- editor.save()
- expect(editor.getText()).toBe "foo\nbar\n\nbaz"
-
- # works for buffers that are opened after package is initialized
- editor = atom.project.openSync('sample.txt')
- editor.moveCursorToEndOfLine()
- editor.insertText(" ")
-
- editor.save()
- expect(editor.getText()).toBe 'Some text.\n'
-
describe "when the editor is destroyed", ->
beforeEach ->
editor.destroy()
@@ -45,21 +29,61 @@ describe "Whitespace", ->
buffer.save()
expect(buffer.getText()).toBe "foo \nbar\t \n\nbaz"
- it "does not trim trailing whitespace if removeTrailingWhitespace is false", ->
- atom.config.set("whitespace.removeTrailingWhitespace", false)
+ describe "when 'whitespace.removeTrailingWhitespace' is true", ->
+ beforeEach ->
+ atom.config.set("whitespace.removeTrailingWhitespace", true)
- editor.insertText "don't trim me "
- editor.save()
- expect(editor.getText()).toBe "don't trim me \n"
+ it "strips trailing whitespace before an editor saves a buffer", ->
+ # works for buffers that are already open when package is initialized
+ editor.insertText("foo \nbar\t \n\nbaz\n")
+ editor.save()
+ expect(editor.getText()).toBe "foo\nbar\n\nbaz\n"
- describe "whitespace.ensureSingleTrailingNewline config", ->
- [originalConfigValue] = []
+ # works for buffers that are opened after package is initialized
+ editor = atom.project.openSync('sample.txt')
+ editor.moveCursorToEndOfLine()
+ editor.insertText(" ")
+
+ # move cursor to next line to avoid ignoreWhitespaceOnCurrentLine
+ editor.moveCursorToBottom()
+
+ editor.save()
+ expect(editor.getText()).toBe 'Some text.\n'
+
+ describe "when 'whitespace.removeTrailingWhitespace' is false", ->
beforeEach ->
- originalConfigValue = atom.config.get("whitespace.ensureSingleTrailingNewline")
- expect(originalConfigValue).toBe true
+ atom.config.set("whitespace.removeTrailingWhitespace", false)
- afterEach ->
- atom.config.set("whitespace.ensureSingleTrailingNewline", originalConfigValue)
+ it "does not trim trailing whitespace", ->
+ editor.insertText "don't trim me "
+ editor.save()
+ expect(editor.getText()).toBe "don't trim me \n"
+
+ describe "when 'whitespace.ignoreWhitespaceOnCurrentLine' is true", ->
+ beforeEach ->
+ atom.config.set("whitespace.ignoreWhitespaceOnCurrentLine", true)
+
+ it "removes the whitespace from all lines, excluding the current lines", ->
+ editor.insertText "1 \n2 \n3 \n"
+ editor.setCursorBufferPosition([1,3])
+ editor.addCursorAtBufferPosition([2,3])
+ editor.save()
+ expect(editor.getText()).toBe "1\n2 \n3 \n"
+
+ describe "when 'whitespace.ignoreWhitespaceOnCurrentLine' is false", ->
+ beforeEach ->
+ atom.config.set("whitespace.ignoreWhitespaceOnCurrentLine", false)
+
+ it "removes the whitespace from all lines, including the current lines", ->
+ editor.insertText "1 \n2 \n3 \n"
+ editor.setCursorBufferPosition([1,3])
+ editor.addCursorAtBufferPosition([2,3])
+ editor.save()
+ expect(editor.getText()).toBe "1\n2\n3\n"
+
+ describe "when 'whitespace.ensureSingleTrailingNewline' is true", ->
+ beforeEach ->
+ atom.config.set("whitespace.ensureSingleTrailingNewline", true)
it "adds a trailing newline when there is no trailing newline", ->
editor.insertText "foo"
@@ -86,13 +110,6 @@ describe "Whitespace", ->
editor.save()
expect(editor.getText()).toBe "\n"
- it "does not add trailing newline if ensureSingleTrailingNewline is false", ->
- atom.config.set("whitespace.ensureSingleTrailingNewline", false)
-
- editor.insertText "no trailing newline"
- editor.save()
- expect(editor.getText()).toBe "no trailing newline"
-
it "does not move the cursor when the new line is added", ->
editor.insertText "foo\nboo"
editor.setCursorBufferPosition([0,3])
@@ -100,8 +117,19 @@ describe "Whitespace", ->
expect(editor.getText()).toBe "foo\nboo\n"
expect(editor.getCursorBufferPosition()).toEqual([0,3])
+ describe "when 'whitespace.ensureSingleTrailingNewline' is false", ->
+ beforeEach ->
+ atom.config.set("whitespace.ensureSingleTrailingNewline", false)
+
+ it "does not add trailing newline if ensureSingleTrailingNewline is false", ->
+ editor.insertText "no trailing newline"
+ editor.save()
+ expect(editor.getText()).toBe "no trailing newline"
+
describe "GFM whitespace trimming", ->
beforeEach ->
+ atom.config.set("whitespace.ignoreWhitespaceOnCurrentLine", false)
+
waitsForPromise ->
atom.packages.activatePackage("language-gfm")
@@ -131,3 +159,11 @@ describe "Whitespace", ->
editor.setText "foo\n "
editor.save()
expect(editor.getText()).toBe "foo\n"
+
+ it "respects 'whitespace.ignoreWhitespaceOnCurrentLine' setting", ->
+ atom.config.set("whitespace.ignoreWhitespaceOnCurrentLine", true)
+
+ editor.insertText "foo \nline break!"
+ editor.setCursorBufferPosition([0,4])
+ editor.save()
+ expect(editor.getText()).toBe "foo \nline break!\n"
Something went wrong with that request. Please try again.