diff --git a/docs/getting-started.md b/docs/getting-started.md index b5abb2d63..d869e9506 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -173,8 +173,8 @@ its own namespace. - showInvisibles: Whether to render placeholders for invisible characters (defaults to false) - fuzzyFinder - ignoredNames: Files to ignore *only* in the fuzzy-finder -- stripTrailingWhitespace - - singleTrailingNewline: Whether to reduce multiple newlines to one at the end of files +- whitespace + - ensureSingleTrailingNewline: Whether to reduce multiple newlines to one at the end of files - wrapGuide - columns: Array of hashes with a `pattern` and `column` key to match the the path of the current editor to a column position. diff --git a/package.json b/package.json index adb5638b0..bed01f2da 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version" : "0.0.0", "dependencies": { - "coffee-script": "1.5", + "coffee-script": "1.6.2", "ctags": "0.3.0", "oniguruma": "0.8.0", "mkdirp": "0.3.5", diff --git a/script/compile-coffee b/script/compile-coffee index f3d19392c..99bc3cc31 100755 --- a/script/compile-coffee +++ b/script/compile-coffee @@ -16,6 +16,6 @@ node --version > /dev/null 2>&1 || { } INPUT_FILE="${1}" -OUTPUT_FILE="${2}" +OUTPUT_DIR=`dirname "$2"` -node_modules/.bin/coffee -c -p "${INPUT_FILE}" > "${OUTPUT_FILE}" +node_modules/.bin/coffee -c -m -o "$OUTPUT_DIR" "$INPUT_FILE" diff --git a/spec/app/select-list-spec.coffee b/spec/app/select-list-spec.coffee index a39c4a128..5702fe546 100644 --- a/spec/app/select-list-spec.coffee +++ b/spec/app/select-list-spec.coffee @@ -124,6 +124,9 @@ describe "SelectList", -> expect(selectList.confirmed).toHaveBeenCalledWith(array[2]) describe "when there is no item selected (because the list is empty)", -> + beforeEach -> + selectList.attachToDom() + it "does not trigger the confirmed hook", -> miniEditor.insertText("i will never match anything") window.advanceClock(selectList.inputThrottle) diff --git a/spec/stdlib/cson-spec.coffee b/spec/stdlib/cson-spec.coffee index 0171cebfb..bee9e2032 100644 --- a/spec/stdlib/cson-spec.coffee +++ b/spec/stdlib/cson-spec.coffee @@ -81,8 +81,8 @@ describe "CSON", -> fontSize: 20 core: themes: ['a', 'b'] - stripTrailingWhitespace: - singleTrailingNewline: true + whitespace: + ensureSingleTrailingNewline: true cson = CSON.stringify(object) CoffeeScript = require 'coffee-script' diff --git a/src/app/project.coffee b/src/app/project.coffee index e2ad125d3..6b29bab09 100644 --- a/src/app/project.coffee +++ b/src/app/project.coffee @@ -75,6 +75,7 @@ class Project fs.absolute filePath relativize: (fullPath) -> + return fullPath unless fullPath.lastIndexOf(@getPath()) is 0 fullPath.replace(@getPath(), "").replace(/^\//, '') getSoftTabs: -> @softTabs diff --git a/src/app/select-list.coffee b/src/app/select-list.coffee index 0b5ff5283..9da84cc09 100644 --- a/src/app/select-list.coffee +++ b/src/app/select-list.coffee @@ -45,7 +45,9 @@ class SelectList extends View schedulePopulateList: -> clearTimeout(@scheduleTimeout) - @scheduleTimeout = setTimeout((=> @populateList()), @inputThrottle) + populateCallback = => + @populateList() if @isOnDom() + @scheduleTimeout = setTimeout(populateCallback, @inputThrottle) setArray: (@array) -> @populateList() diff --git a/src/app/sortable-list.coffee b/src/app/sortable-list.coffee deleted file mode 100644 index ebdc675a1..000000000 --- a/src/app/sortable-list.coffee +++ /dev/null @@ -1,54 +0,0 @@ -{View} = require 'space-pen' -$ = require 'jquery' - -module.exports = -class SortableList extends View - @viewClass: -> 'sortable-list' - - initialize: -> - @on 'dragstart', '.sortable', @onDragStart - @on 'dragend', '.sortable', @onDragEnd - @on 'dragover', '.sortable', @onDragOver - @on 'dragenter', '.sortable', @onDragEnter - @on 'dragleave', '.sortable', @onDragLeave - @on 'drop', '.sortable', @onDrop - - onDragStart: (event) => - unless @shouldAllowDrag(event) - event.preventDefault() - return - - el = @getSortableElement(event) - el.addClass 'is-dragging' - event.originalEvent.dataTransfer.setData 'sortable-index', el.index() - - onDragEnd: (event) => - @getSortableElement(event).removeClass 'is-dragging' - - onDragEnter: (event) => - event.preventDefault() - - onDragOver: (event) => - event.preventDefault() - @getSortableElement(event).addClass 'is-drop-target' - - onDragLeave: (event) => - @getSortableElement(event).removeClass 'is-drop-target' - - onDrop: (event) => - return false if !@shouldAllowDrop(event) - event.stopPropagation() - @find('.is-drop-target').removeClass 'is-drop-target' - - shouldAllowDrag: (event) -> - true - - shouldAllowDrop: (event) -> - true - - getDroppedElement: (event) -> - index = event.originalEvent.dataTransfer.getData('sortable-index') - @find(".sortable:eq(#{index})") - - getSortableElement: (event) -> - $(event.target).closest('.sortable') diff --git a/src/packages/autocomplete/spec/autocomplete-spec.coffee b/src/packages/autocomplete/spec/autocomplete-spec.coffee index b525a8fe7..0bc25a3f0 100644 --- a/src/packages/autocomplete/spec/autocomplete-spec.coffee +++ b/src/packages/autocomplete/spec/autocomplete-spec.coffee @@ -288,6 +288,9 @@ describe "AutocompleteView", -> expect(editor.lineForBufferRow(10)).toBe matchToSelect.text() describe "when the mini-editor receives keyboard input", -> + beforeEach -> + editor.attachToDom() + describe "when text is removed from the mini-editor", -> it "reloads the match list based on the mini-editor's text", -> editor.getBuffer().insert([10,0] ,"t") diff --git a/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee b/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee index 5ad9be5e2..1c11229db 100644 --- a/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee +++ b/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee @@ -39,7 +39,7 @@ class FuzzyFinderView extends SelectList $$ -> @li => if git? - status = git.statuses[project.resolve(path)] + status = git.statuses[path] if git.isStatusNew(status) @div class: 'status new' else if git.isStatusModified(status) @@ -60,7 +60,7 @@ class FuzzyFinderView extends SelectList typeClass = 'text-name' @span fs.base(path), class: "file label #{typeClass}" - if folder = fs.directory(project.relativize(path)) + if folder = project.relativize(fs.directory(path)) @span " - #{folder}/", class: 'directory' openPath: (path) -> @@ -76,7 +76,7 @@ class FuzzyFinderView extends SelectList confirmed : (path) -> return unless path.length - if fs.isFile(project.resolve(path)) + if fs.isFile(path) @cancel() @openPath(path) else @@ -133,11 +133,10 @@ class FuzzyFinderView extends SelectList @miniEditor.setText(currentWord) populateGitStatusPaths: -> - projectRelativePaths = [] - for path, status of git.statuses - continue unless fs.isFile(path) - projectRelativePaths.push(project.relativize(path)) - @setArray(projectRelativePaths) + paths = [] + paths.push(path) for path, status of git.statuses when fs.isFile(path) + + @setArray(paths) populateProjectPaths: (options = {}) -> if @projectPaths? @@ -147,7 +146,6 @@ class FuzzyFinderView extends SelectList path.indexOf(options.filter) >= 0 else @projectPaths - @setArray(listedItems) options.done(listedItems) if options.done? else @@ -165,8 +163,6 @@ class FuzzyFinderView extends SelectList populateOpenBufferPaths: -> editSessions = project.getEditSessions().filter (editSession) -> editSession.getPath()? - editSessions = _.uniq editSessions, (editSession) -> - editSession.getPath() editSessions = _.sortBy editSessions, (editSession) => if editSession is rootView.getActivePaneItem() @@ -174,17 +170,10 @@ class FuzzyFinderView extends SelectList else -(editSession.lastOpened or 1) - @paths = _.map editSessions, (editSession) -> - project.relativize editSession.getPath() + @paths = [] + @paths.push(editSession.getPath()) for editSession in editSessions - @setArray(@paths) - - getOpenedPaths: -> - paths = {} - for editSession in project.getEditSessions() - path = editSession.getPath() - paths[path] = editSession.lastOpened if path? - paths + @setArray(_.uniq(@paths)) detach: -> super diff --git a/src/packages/fuzzy-finder/lib/fuzzy-finder.coffee b/src/packages/fuzzy-finder/lib/fuzzy-finder.coffee index d90ff497d..0a7db9dc8 100644 --- a/src/packages/fuzzy-finder/lib/fuzzy-finder.coffee +++ b/src/packages/fuzzy-finder/lib/fuzzy-finder.coffee @@ -31,7 +31,12 @@ module.exports = @projectPaths = null serialize: -> - @fuzzyFinderView?.getOpenedPaths() + if @fuzzyFinderView? + paths = {} + for editSession in project.getEditSessions() + path = editSession.getPath() + paths[path] = editSession.lastOpened if path? + paths createView: -> unless @fuzzyFinderView diff --git a/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee b/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee index fb6c8ceb3..c71a090c5 100644 --- a/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee +++ b/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee @@ -77,8 +77,8 @@ describe 'FuzzyFinder', -> expect(rootView.getActiveView()).toBe editor2 rootView.trigger 'fuzzy-finder:toggle-file-finder' - finderView.confirmed('dir/a') expectedPath = project.resolve('dir/a') + finderView.confirmed(expectedPath) expect(finderView.hasParent()).toBeFalsy() expect(editor1.getPath()).not.toBe expectedPath @@ -144,6 +144,7 @@ describe 'FuzzyFinder', -> atom.deactivatePackage('fuzzy-finder') states = _.map atom.getPackageState('fuzzy-finder'), (path, time) -> [ path, time ] + expect(states.length).toBe 3 states = _.sortBy states, (path, time) -> -time paths = [ 'sample-with-tabs.coffee', 'sample.txt', 'sample.js' ] @@ -187,7 +188,7 @@ describe 'FuzzyFinder', -> describe "when the active pane has an item for the selected path", -> it "switches to the item for the selected path", -> expectedPath = project.resolve('sample.txt') - finderView.confirmed('sample.txt') + finderView.confirmed(expectedPath) expect(finderView.hasParent()).toBeFalsy() expect(editor1.getPath()).not.toBe expectedPath @@ -203,7 +204,7 @@ describe 'FuzzyFinder', -> expect(rootView.getActiveView()).toBe editor1 expectedPath = project.resolve('sample.txt') - finderView.confirmed('sample.txt') + finderView.confirmed(expectedPath) expect(finderView.hasParent()).toBeFalsy() expect(editor1.getPath()).toBe expectedPath @@ -372,7 +373,7 @@ describe 'FuzzyFinder', -> runs -> expect(finderView).not.toBeVisible() - expect(openedPath).toBe "#{project.getPath()}/sample.txt" + expect(openedPath).toBe project.resolve("sample.txt") it "displays an error when the word under the cursor doesn't match any files", -> editor.setText("moogoogaipan") diff --git a/src/packages/gfm/scoped-properties/gfm.cson b/src/packages/gfm/scoped-properties/gfm.cson deleted file mode 100644 index a9be1a2a4..000000000 --- a/src/packages/gfm/scoped-properties/gfm.cson +++ /dev/null @@ -1,3 +0,0 @@ -'.source.gfm': - 'editor': - 'increaseIndentPattern': '^\\s*([\\*\\+-])[ \\t]+' diff --git a/src/packages/gfm/spec/gfm-spec.coffee b/src/packages/gfm/spec/gfm-spec.coffee index ea579187e..3ca1b3a79 100644 --- a/src/packages/gfm/spec/gfm-spec.coffee +++ b/src/packages/gfm/spec/gfm-spec.coffee @@ -132,10 +132,3 @@ describe "GitHub Flavored Markdown grammar", -> {tokens} = grammar.tokenizeLine("> Quotation") expect(tokens[0]).toEqual value: ">", scopes: ["source.gfm", "support.quote.gfm"] expect(tokens[1]).toEqual value: " Quotation", scopes: ["source.gfm", "comment.quote.gfm"] - - describe "auto indent", -> - it "indents newlines entered after list lines", -> - config.set('editor.autoIndent', true) - editSession = project.buildEditSession('gfm.md') - editSession.insertNewlineBelow() - expect(editSession.buffer.lineForRow(1)).toBe ' ' diff --git a/src/packages/strip-trailing-whitespace/package.cson b/src/packages/strip-trailing-whitespace/package.cson deleted file mode 100644 index 0daeeb412..000000000 --- a/src/packages/strip-trailing-whitespace/package.cson +++ /dev/null @@ -1 +0,0 @@ -'main': './lib/strip-trailing-whitespace' diff --git a/src/packages/tabs/lib/tab-bar-view.coffee b/src/packages/tabs/lib/tab-bar-view.coffee index 68f41df86..926e5c3ae 100644 --- a/src/packages/tabs/lib/tab-bar-view.coffee +++ b/src/packages/tabs/lib/tab-bar-view.coffee @@ -1,15 +1,18 @@ $ = require 'jquery' +{View} = require 'space-pen' _ = require 'underscore' -SortableList = require 'sortable-list' TabView = require './tab-view' module.exports = -class TabBarView extends SortableList +class TabBarView extends View @content: -> - @ul class: "tabs #{@viewClass()}" + @ul class: "tabs sortable-list" initialize: (@pane) -> - super + @on 'dragstart', '.sortable', @onDragStart + @on 'dragend', '.sortable', @onDragEnd + @on 'dragover', @onDragOver + @on 'drop', @onDrop @paneContainer = @pane.getContainer() @addTabForItem(item) for item in @pane.getItems() @@ -74,27 +77,66 @@ class TabBarView extends SortableList (@paneContainer.getPanes().length > 1) or (@pane.getItems().length > 1) onDragStart: (event) => - super + unless @shouldAllowDrag(event) + event.preventDefault() + return + + el = $(event.target).closest('.sortable') + el.addClass 'is-dragging' + event.originalEvent.dataTransfer.setData 'sortable-index', el.index() + pane = $(event.target).closest('.pane') paneIndex = @paneContainer.indexOfPane(pane) event.originalEvent.dataTransfer.setData 'from-pane-index', paneIndex + onDragEnd: (event) => + @find(".is-dragging").removeClass 'is-dragging' + + onDragOver: (event) => + event.preventDefault() + currentDropTargetIndex = @find(".is-drop-target").index() + newDropTargetIndex = @getDropTargetIndex(event) + + if newDropTargetIndex != currentDropTargetIndex + @children().removeClass 'is-drop-target drop-target-is-after' + sortableObjects = @find(".sortable") + if newDropTargetIndex < sortableObjects.length + sortableObjects.eq(newDropTargetIndex).addClass 'is-drop-target' + else + sortableObjects.eq(newDropTargetIndex - 1).addClass 'drop-target-is-after' + + onDrop: (event) => - super + event.stopPropagation() + @children('.is-drop-target').removeClass 'is-drop-target' + @children('.drop-target-is-after').removeClass 'drop-target-is-after' dataTransfer = event.originalEvent.dataTransfer fromIndex = parseInt(dataTransfer.getData('sortable-index')) fromPaneIndex = parseInt(dataTransfer.getData('from-pane-index')) fromPane = @paneContainer.paneAtIndex(fromPaneIndex) - toIndex = @getSortableElement(event).index() + toIndex = @getDropTargetIndex(event) toPane = $(event.target).closest('.pane').view() draggedTab = fromPane.find(".tabs .sortable:eq(#{fromIndex})").view() item = draggedTab.item if toPane is fromPane - toIndex++ if fromIndex > toIndex + toIndex-- if fromIndex < toIndex toPane.moveItem(item, toIndex) else - fromPane.moveItemToPane(item, toPane, toIndex) + fromPane.moveItemToPane(item, toPane, toIndex--) toPane.showItem(item) toPane.focus() + + getDropTargetIndex: (event) -> + el = $(event.target).closest('.sortable') + el = $(event.target).find('.sortable').last() if el.length == 0 + + elementCenter = el.offset().left + el.width() / 2 + + if event.originalEvent.pageX < elementCenter + el.index() + else if el.next().length > 0 + el.next().index() + else + el.index() + 1 diff --git a/src/packages/tabs/spec/tabs-spec.coffee b/src/packages/tabs/spec/tabs-spec.coffee index 820f3f9eb..a366126d9 100644 --- a/src/packages/tabs/spec/tabs-spec.coffee +++ b/src/packages/tabs/spec/tabs-spec.coffee @@ -235,6 +235,20 @@ describe "TabBarView", -> expect(pane.activeItem).toBe item1 expect(pane.focus).toHaveBeenCalled() + describe "when it is dropped on the tab bar", -> + it "moves the tab and its item to the end", -> + expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["Item 1", "sample.js", "Item 2"] + expect(pane.getItems()).toEqual [item1, editSession1, item2] + expect(pane.activeItem).toBe item2 + spyOn(pane, 'focus') + + [dragStartEvent, dropEvent] = buildDragEvents(tabBar.tabAtIndex(0), tabBar) + tabBar.onDragStart(dragStartEvent) + tabBar.onDrop(dropEvent) + + expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["sample.js", "Item 2", "Item 1"] + expect(pane.getItems()).toEqual [editSession1, item2, item1] + describe "when a tab is dragged to a different pane", -> [pane2, tabBar2, item2b] = [] diff --git a/src/packages/tabs/stylesheets/tabs.less b/src/packages/tabs/stylesheets/tabs.less index bb262bba1..120503c5d 100644 --- a/src/packages/tabs/stylesheets/tabs.less +++ b/src/packages/tabs/stylesheets/tabs.less @@ -83,11 +83,10 @@ } -.tab.is-drop-target:after { +.tab.is-drop-target:before, .tab.drop-target-is-after:after { + content: ""; position: absolute; top: 0; - right: -2px; - content: ""; z-index: 999; display: inline-block; width: 2px; @@ -96,15 +95,30 @@ background: #0098ff; } -.tab.is-drop-target:before { +.tab.is-drop-target:after, .tab.drop-target-is-after:before { content: ""; position: absolute; + top: 30px; + z-index: 9999; width: 4px; height: 4px; background: #0098ff; - right: -4px; - top: 30px; border-radius: 4px; - z-index: 9999; border: 1px solid transparent; -} \ No newline at end of file +} + +.tab.is-drop-target:before { + left: -2px; +} + +.tab.is-drop-target:after { + left: -4px; +} + +.tab.drop-target-is-after:before { + right: -4px; +} + +.tab.drop-target-is-after:after { + right: -2px; +} diff --git a/src/packages/strip-trailing-whitespace/lib/strip-trailing-whitespace.coffee b/src/packages/whitespace/lib/whitespace.coffee similarity index 51% rename from src/packages/strip-trailing-whitespace/lib/strip-trailing-whitespace.coffee rename to src/packages/whitespace/lib/whitespace.coffee index 1f93f4a68..087bfabd3 100644 --- a/src/packages/strip-trailing-whitespace/lib/strip-trailing-whitespace.coffee +++ b/src/packages/whitespace/lib/whitespace.coffee @@ -1,13 +1,16 @@ module.exports = activate: -> - rootView.eachBuffer (buffer) => @stripTrailingWhitespaceBeforeSave(buffer) + rootView.eachBuffer (buffer) => @whitespaceBeforeSave(buffer) - stripTrailingWhitespaceBeforeSave: (buffer) -> + configDefaults: + ensureSingleTrailingNewline: true + + whitespaceBeforeSave: (buffer) -> buffer.on 'will-be-saved', -> buffer.transact -> - buffer.scan /[ \t]+$/g, (match, range, { replace }) -> - replace('') - if config.get('stripTrailingWhitespace.singleTrailingNewline') + buffer.scan /[ \t]+$/g, (match, range, { replace }) -> replace('') + + if config.get('whitespace.ensureSingleTrailingNewline') if buffer.getLastLine() is '' row = buffer.getLastRow() - 1 while row and buffer.lineForRow(row) is '' diff --git a/src/packages/whitespace/package.cson b/src/packages/whitespace/package.cson new file mode 100644 index 000000000..fb9bfc916 --- /dev/null +++ b/src/packages/whitespace/package.cson @@ -0,0 +1 @@ +'main': './lib/whitespace' diff --git a/src/packages/strip-trailing-whitespace/spec/strip-trailing-whitespace-spec.coffee b/src/packages/whitespace/spec/whitespace-spec.coffee similarity index 72% rename from src/packages/strip-trailing-whitespace/spec/strip-trailing-whitespace-spec.coffee rename to src/packages/whitespace/spec/whitespace-spec.coffee index e41d2d3ab..854cf3295 100644 --- a/src/packages/strip-trailing-whitespace/spec/strip-trailing-whitespace-spec.coffee +++ b/src/packages/whitespace/spec/whitespace-spec.coffee @@ -1,7 +1,7 @@ RootView = require 'root-view' fs = require 'fs-utils' -describe "StripTrailingWhitespace", -> +describe "Whitespace", -> [editor, path] = [] beforeEach -> @@ -10,17 +10,20 @@ describe "StripTrailingWhitespace", -> window.rootView = new RootView rootView.open(path) - atom.activatePackage('strip-trailing-whitespace') + atom.activatePackage('whitespace') + rootView.focus() editor = rootView.getActiveView() afterEach -> fs.remove(path) if fs.exists(path) - rootView.remove() it "strips trailing whitespace before an editor saves a buffer", -> spyOn(fs, 'write') + config.set("whitespace.ensureSingleTrailingNewline", false) + config.update() + # works for buffers that are already open when extension is initialized editor.insertText("foo \nbar\t \n\nbaz") editor.getBuffer().save() @@ -34,15 +37,14 @@ describe "StripTrailingWhitespace", -> editor.getBuffer().save() expect(editor.getText()).toBe 'Some text.\n' - describe "stripTrailingWhitespace.singleTrailingNewline config", -> + describe "whitespace.ensureSingleTrailingNewline config", -> [originalConfigValue] = [] beforeEach -> - originalConfigValue = config.get("stripTrailingWhitespace.singleTrailingNewline") - config.set("stripTrailingWhitespace.singleTrailingNewline", true) - config.update() + originalConfigValue = config.get("whitespace.ensureSingleTrailingNewline") + expect(originalConfigValue).toBe true afterEach -> - config.set("stripTrailingWhitespace.singleTrailingNewline", originalConfigValue) + config.set("whitespace.ensureSingleTrailingNewline", originalConfigValue) config.update() it "adds a trailing newline when there is no trailing newline", -> @@ -69,3 +71,12 @@ describe "StripTrailingWhitespace", -> editor.insertText "\n" editor.getBuffer().save() expect(editor.getText()).toBe "\n" + + it "does not add trailing newline if ensureSingleTrailingNewline is false", -> + config.set("whitespace.ensureSingleTrailingNewline", false) + config.update() + + editor.insertText "no trailing newline" + editor.getBuffer().save() + expect(editor.getText()).toBe "no trailing newline" +