This repository has been archived by the owner on Dec 15, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 61
Fix spaces-to-tabs and tabs-to-spaces to use "tab stops" correctly #67
Open
giseburt
wants to merge
3
commits into
atom:master
Choose a base branch
from
giseburt:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -77,14 +77,42 @@ class Whitespace | |
|
||
convertTabsToSpaces: (editor) -> | ||
buffer = editor.getBuffer() | ||
spacesText = new Array(editor.getTabLength() + 1).join(' ') | ||
|
||
tabLength = editor.getTabLength() | ||
|
||
buffer.transact -> | ||
buffer.scan /\t/g, ({replace}) -> replace(spacesText) | ||
buffer.scan /^.*\t.*$/g, ({matchText, replace}) -> | ||
while match = /^([^\t]*)\t(.*)$/.exec(matchText) | ||
newTabLength = tabLength-(match[1].length %% tabLength) | ||
matchText = match[1] + (new Array(newTabLength+1).join(" ")) + match[2] | ||
|
||
replace(matchText) | ||
|
||
convertSpacesToTabs: (editor) -> | ||
buffer = editor.getBuffer() | ||
spacesText = new Array(editor.getTabLength() + 1).join(' ') | ||
|
||
tabLength = editor.getTabLength() | ||
|
||
buffer.transact -> | ||
buffer.scan new RegExp(spacesText, 'g'), ({replace}) -> replace('\t') | ||
buffer.scan /^.*[ ].*$/g, ({match, replace}) -> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar question here... why not search for chunks of contiguous leading whitespace and use the range to break them up in a tab-stop aware way? Then you don't have to process a bunch of unrelated characters. I don't think we should be inserting tabs for anything but leading whitespace. |
||
matchText = match[0] | ||
spaceCount = 0 | ||
charCount = 0 | ||
outText = "" | ||
for c in matchText | ||
charCount++ | ||
if c is " " | ||
spaceCount++ | ||
if charCount %% tabLength is 0 | ||
spaceCount = 0 | ||
outText += "\t" | ||
else | ||
if spaceCount > 0 | ||
outText += (new Array(spaceCount+1).join(" ")) | ||
spaceCount = 0 | ||
outText += c | ||
|
||
if spaceCount > 0 | ||
outText += (new Array(spaceCount+1).join(" ")) | ||
|
||
replace(outText) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,12 @@ describe "Whitespace", -> | |
waitsForPromise -> | ||
atom.packages.activatePackage('whitespace') | ||
|
||
atom.config.set("whitespace.ensureSingleTrailingNewline", true) | ||
|
||
describe "sanity checks", -> | ||
it 'should start with the package active', -> | ||
expect(atom.packages.isPackageActive('whitespace')).toBe true | ||
|
||
describe "when the editor is destroyed", -> | ||
beforeEach -> | ||
editor.destroy() | ||
|
@@ -59,6 +65,9 @@ describe "Whitespace", -> | |
beforeEach -> | ||
atom.config.set("whitespace.removeTrailingWhitespace", false) | ||
|
||
it "actually set whitespace.removeTrailingWhitespace to false", -> | ||
expect(atom.config.get("whitespace.removeTrailingWhitespace")).toBe false | ||
|
||
it "does not trim trailing whitespace", -> | ||
editor.insertText "don't trim me \n\n" | ||
editor.save() | ||
|
@@ -77,6 +86,13 @@ describe "Whitespace", -> | |
describe "when 'whitespace.ignoreWhitespaceOnCurrentLine' is true", -> | ||
beforeEach -> | ||
atom.config.set("whitespace.ignoreWhitespaceOnCurrentLine", true) | ||
atom.config.set("whitespace.removeTrailingWhitespace", true) | ||
|
||
it "actually set whitespace.removeTrailingWhitespace to true", -> | ||
expect(atom.config.get("whitespace.removeTrailingWhitespace")).toBe true | ||
|
||
it "actually set whitespace.ignoreWhitespaceOnCurrentLine to true", -> | ||
expect(atom.config.get("whitespace.ignoreWhitespaceOnCurrentLine")).toBe true | ||
|
||
it "removes the whitespace from all lines, excluding the current lines", -> | ||
editor.insertText "1 \n2 \n3 \n" | ||
|
@@ -88,6 +104,10 @@ describe "Whitespace", -> | |
describe "when 'whitespace.ignoreWhitespaceOnCurrentLine' is false", -> | ||
beforeEach -> | ||
atom.config.set("whitespace.ignoreWhitespaceOnCurrentLine", false) | ||
atom.config.set("whitespace.removeTrailingWhitespace", true) | ||
|
||
it "actually set whitespace.ignoreWhitespaceOnCurrentLine to false", -> | ||
expect(atom.config.get("whitespace.ignoreWhitespaceOnCurrentLine")).toBe false | ||
|
||
it "removes the whitespace from all lines, including the current lines", -> | ||
editor.insertText "1 \n2 \n3 \n" | ||
|
@@ -99,6 +119,17 @@ describe "Whitespace", -> | |
describe "when 'whitespace.ignoreWhitespaceOnlyLines' is false", -> | ||
beforeEach -> | ||
atom.config.set("whitespace.ignoreWhitespaceOnlyLines", false) | ||
atom.config.set("whitespace.ignoreWhitespaceOnCurrentLine", false) | ||
atom.config.set("whitespace.removeTrailingWhitespace", true) | ||
|
||
it "actually set whitespace.ignoreWhitespaceOnlyLines to false", -> | ||
expect(atom.config.get("whitespace.ignoreWhitespaceOnlyLines")).toBe false | ||
|
||
it "actually set whitespace.ignoreWhitespaceOnCurrentLine to false", -> | ||
expect(atom.config.get("whitespace.ignoreWhitespaceOnCurrentLine")).toBe false | ||
|
||
it "actually set whitespace.removeTrailingWhitespace to true", -> | ||
expect(atom.config.get("whitespace.removeTrailingWhitespace")).toBe true | ||
|
||
it "removes the whitespace from all lines, including the whitespace-only lines", -> | ||
editor.insertText "1 \n2\t \n\t \n3\n" | ||
|
@@ -111,6 +142,17 @@ describe "Whitespace", -> | |
describe "when 'whitespace.ignoreWhitespaceOnlyLines' is true", -> | ||
beforeEach -> | ||
atom.config.set("whitespace.ignoreWhitespaceOnlyLines", true) | ||
atom.config.set("whitespace.ignoreWhitespaceOnCurrentLine", false) | ||
atom.config.set("whitespace.removeTrailingWhitespace", true) | ||
|
||
it "actually set whitespace.ignoreWhitespaceOnlyLines to true", -> | ||
expect(atom.config.get("whitespace.ignoreWhitespaceOnlyLines")).toBe true | ||
|
||
it "actually set whitespace.ignoreWhitespaceOnCurrentLine to false", -> | ||
expect(atom.config.get("whitespace.ignoreWhitespaceOnCurrentLine")).toBe false | ||
|
||
it "actually set whitespace.removeTrailingWhitespace to true", -> | ||
expect(atom.config.get("whitespace.removeTrailingWhitespace")).toBe true | ||
|
||
it "removes the whitespace from all lines, excluding the whitespace-only lines", -> | ||
editor.insertText "1 \n2\t \n\t \n3\n" | ||
|
@@ -177,13 +219,32 @@ describe "Whitespace", -> | |
describe "GFM whitespace trimming", -> | ||
beforeEach -> | ||
atom.config.set("whitespace.ignoreWhitespaceOnCurrentLine", false) | ||
atom.config.set("whitespace.ignoreWhitespaceOnlyLines", false) | ||
atom.config.set("whitespace.ignoreWhitespaceOnCurrentLine", false) | ||
atom.config.set("whitespace.removeTrailingWhitespace", true) | ||
atom.config.set("whitespace.ensureSingleTrailingNewline", true) | ||
|
||
waitsForPromise -> | ||
atom.packages.activatePackage("language-gfm") | ||
|
||
runs -> | ||
editor.setGrammar(atom.grammars.grammarForScopeName("source.gfm")) | ||
|
||
it "actually set whitespace.ignoreWhitespaceOnCurrentLine to false", -> | ||
expect(atom.config.get("whitespace.ignoreWhitespaceOnCurrentLine")).toBe false | ||
|
||
it "actually set whitespace.ignoreWhitespaceOnlyLines to false", -> | ||
expect(atom.config.get("whitespace.ignoreWhitespaceOnlyLines")).toBe false | ||
|
||
it "actually set whitespace.ignoreWhitespaceOnCurrentLine to false", -> | ||
expect(atom.config.get("whitespace.ignoreWhitespaceOnCurrentLine")).toBe false | ||
|
||
it "actually set whitespace.removeTrailingWhitespace to true", -> | ||
expect(atom.config.get("whitespace.removeTrailingWhitespace")).toBe true | ||
|
||
it "actually set whitespace.ensureSingleTrailingNewline to true", -> | ||
expect(atom.config.get("whitespace.ensureSingleTrailingNewline")).toBe true | ||
|
||
it "trims GFM text with a single space", -> | ||
editor.insertText "foo \nline break!" | ||
editor.save() | ||
|
@@ -217,6 +278,7 @@ describe "Whitespace", -> | |
expect(editor.getText()).toBe "foo \nline break!\n" | ||
|
||
it "respects 'whitespace.ignoreWhitespaceOnlyLines' setting", -> | ||
atom.config.set("whitespace.ignoreWhitespaceOnCurrentLine", false) | ||
atom.config.set("whitespace.ignoreWhitespaceOnlyLines", true) | ||
|
||
editor.insertText "\t \nline break!" | ||
|
@@ -225,6 +287,10 @@ describe "Whitespace", -> | |
|
||
describe "when the editor is split", -> | ||
it "does not throw exceptions when the editor is saved after the split is closed (regression)", -> | ||
atom.config.set("whitespace.ignoreWhitespaceOnCurrentLine", false) | ||
atom.config.set("whitespace.ignoreWhitespaceOnlyLines", false) | ||
atom.config.set("whitespace.ensureSingleTrailingNewline", true) | ||
|
||
atom.workspace.getActivePane().splitRight(copyActiveItem: true) | ||
atom.workspace.getPanes()[0].destroyItems() | ||
|
||
|
@@ -234,6 +300,13 @@ describe "Whitespace", -> | |
expect(editor.getText()).toBe 'test\n' | ||
|
||
describe "when deactivated", -> | ||
beforeEach -> | ||
waitsForPromise -> | ||
atom.packages.activatePackage("whitespace") | ||
|
||
it 'should start with the package active', -> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure why you added this... |
||
expect(atom.packages.isPackageActive('whitespace')).toBe true | ||
|
||
it "does not remove trailing whitespace from editors opened after deactivation", -> | ||
atom.config.set("whitespace.removeTrailingWhitespace", true) | ||
atom.packages.deactivatePackage('whitespace') | ||
|
@@ -257,32 +330,40 @@ describe "Whitespace", -> | |
|
||
it "removes the trailing whitespace in the active editor", -> | ||
atom.commands.dispatch(workspaceElement, 'whitespace:remove-trailing-whitespace') | ||
expect(buffer.getText()).toBe "foo\nbar\n\nbaz" | ||
expect(buffer.getText().replace(/[ ]/g, '•')).toBe "foo\nbar\n\nbaz".replace(/[ ]/g, '•') | ||
|
||
it "does not attempt to remove whitespace when the package is deactivated", -> | ||
atom.packages.deactivatePackage 'whitespace' | ||
expect(buffer.getText()).toBe "foo \nbar\t \n\nbaz" | ||
expect(buffer.getText().replace(/[ ]/g, '•')).toBe "foo \nbar\t \n\nbaz".replace(/[ ]/g, '•') | ||
|
||
describe "when the 'whitespace:convert-tabs-to-spaces' command is run", -> | ||
it "removes all \t characters and replaces them with spaces using the configured tab length", -> | ||
it "removes all \t characters and replaces them with appropriate spaces using the configured tab length", -> | ||
editor.setTabLength(2) | ||
buffer.setText('\ta\n\t\nb\t\nc\t\td') | ||
atom.commands.dispatch(workspaceElement, 'whitespace:convert-tabs-to-spaces') | ||
expect(buffer.getText()).toBe " a\n \nb \nc d" | ||
expect(buffer.getText().replace(/[ ]/g, '•')).toBe " a\n \nb \nc d".replace(/[ ]/g, '•') | ||
|
||
buffer.setText(' \ta\n \nb\t\nc \t \td') | ||
atom.commands.dispatch(workspaceElement, 'whitespace:convert-tabs-to-spaces') | ||
expect(buffer.getText().replace(/[ ]/g, '•')).toBe " a\n \nb \nc d".replace(/[ ]/g, '•') | ||
|
||
editor.setTabLength(3) | ||
buffer.setText('\ta\n\t\nb\t\nc\t\td') | ||
atom.commands.dispatch(workspaceElement, 'whitespace:convert-tabs-to-spaces') | ||
expect(buffer.getText()).toBe " a\n \nb \nc d" | ||
expect(buffer.getText().replace(/[ ]/g, '•')).toBe " a\n \nb \nc d".replace(/[ ]/g, '•') | ||
|
||
buffer.setText(' \ta\n\t\nb\t\nc \t \td') | ||
atom.commands.dispatch(workspaceElement, 'whitespace:convert-tabs-to-spaces') | ||
expect(buffer.getText().replace(/[ ]/g, '•')).toBe " a\n \nb \nc d".replace(/[ ]/g, '•') | ||
|
||
describe "when the 'whitespace:convert-spaces-to-tabs' command is run", -> | ||
it "removes all space characters and replaces them with hard tabs", -> | ||
it "removes appropriate space characters and replaces them with hard tabs", -> | ||
editor.setTabLength(2) | ||
buffer.setText(" a\n \nb \nc d") | ||
buffer.setText(" a\n \nb \nc d\n e") | ||
atom.commands.dispatch(workspaceElement, 'whitespace:convert-spaces-to-tabs') | ||
expect(buffer.getText()).toBe '\ta\n\t\nb\t\nc\t\td' | ||
expect(buffer.getText().replace(/[ ]/g, '•').replace(/\t/g, '†')).toBe '\ta\n\t \nb\t \nc\t\td\n\t\t e'.replace(/[ ]/g, '•').replace(/\t/g, '†') | ||
|
||
editor.setTabLength(3) | ||
buffer.setText(" a\n \nb \nc d" | ||
buffer.setText(" a\n \nb \nc d\n e") | ||
atom.commands.dispatch(workspaceElement, 'whitespace:convert-spaces-to-tabs') | ||
expect(buffer.getText()).toBe '\ta\n\t\nb\t\nc\t\td') | ||
expect(buffer.getText().replace(/[ ]/g, '•').replace(/\t/g, '†')).toBe '\ta\n\t\nb\t \nc\t\t d\n\t\t e'.replace(/[ ]/g, '•').replace(/\t/g, '†') |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like you could continue to scan for single
\t
characters and use the associated matche'srange
to determine how many spaces to replace this with rather than matching again inside the scan iterator.