diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..444ce0b4c --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,24 @@ +# Contributor Code of Conduct + +As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. + +We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery +- Personal attacks +- Trolling or insulting/derogatory comments +- Public or private harassment +- Publishing other's private information, such as physical or electronic addresses, without explicit permission +- Other unethical or unprofessional conduct + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team. + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting a project maintainer at [atom@github.com](mailto:atom@github.com). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. Maintainers are obligated to maintain confidentiality with regard to the reporter of an incident. + +This Code of Conduct is adapted from the Contributor Covenant, version 1.3.0, available from http://contributor-covenant.org/version/1/3/0/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 36de7b46c..ada420a40 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,7 +30,7 @@ These are just guidelines, not rules, use your best judgment and feel free to pr ### Code of Conduct -This project adheres to the [Contributor Covenant 1.2](http://contributor-covenant.org/version/1/2/0). +This project adheres to the Contributor Covenant [code of conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [atom@github.com](mailto:atom@github.com). diff --git a/README.md b/README.md index 168124ac5..7f7acb5fe 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Visit [atom.io](https://atom.io) to learn more or visit the [Atom forum](https:/ Follow [@AtomEditor](https://twitter.com/atomeditor) on Twitter for important announcements. -This project adheres to the [Contributor Covenant 1.2](http://contributor-covenant.org/version/1/2/0). +This project adheres to the Contributor Covenant [code of conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to atom@github.com. ## Documentation diff --git a/build/tasks/install-task.coffee b/build/tasks/install-task.coffee index 54fd06022..2d9054385 100644 --- a/build/tasks/install-task.coffee +++ b/build/tasks/install-task.coffee @@ -20,9 +20,6 @@ module.exports = (grunt) -> copyFolder = path.resolve 'script', 'copy-folder.cmd' if runas('cmd', ['/c', copyFolder, shellAppDir, installDir], admin: true) isnt 0 grunt.log.error("Failed to copy #{shellAppDir} to #{installDir}") - - createShortcut = path.resolve 'script', 'create-shortcut.cmd' - runas('cmd', ['/c', createShortcut, path.join(installDir, 'atom.exe'), appName]) else if process.platform is 'darwin' rm installDir mkdir path.dirname(installDir) diff --git a/package.json b/package.json index c87f4723d..77661a010 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "autocomplete-atom-api": "0.9.2", "autocomplete-css": "0.11.0", "autocomplete-html": "0.7.2", - "autocomplete-plus": "2.23.1", + "autocomplete-plus": "2.24.0", "autocomplete-snippets": "1.9.0", "autoflow": "0.26.0", "autosave": "0.23.0", @@ -88,7 +88,7 @@ "dev-live-reload": "0.47.0", "encoding-selector": "0.21.0", "exception-reporting": "0.37.0", - "find-and-replace": "0.191.0", + "find-and-replace": "0.192.0", "fuzzy-finder": "0.93.0", "git-diff": "0.57.0", "go-to-line": "0.30.0", @@ -117,38 +117,38 @@ "welcome": "0.33.0", "whitespace": "0.32.1", "wrap-guide": "0.38.1", - "language-c": "0.50.1", - "language-clojure": "0.18.0", + "language-c": "0.51.0", + "language-clojure": "0.19.0", "language-coffee-script": "0.46.0", "language-csharp": "0.11.0", - "language-css": "0.35.1", - "language-gfm": "0.81.0", - "language-git": "0.10.0", + "language-css": "0.36.0", + "language-gfm": "0.82.0", + "language-git": "0.11.0", "language-go": "0.40.0", - "language-html": "0.43.0", - "language-hyperlink": "0.15.0", + "language-html": "0.43.1", + "language-hyperlink": "0.16.0", "language-java": "0.17.0", - "language-javascript": "0.103.0", - "language-json": "0.17.1", + "language-javascript": "0.104.0", + "language-json": "0.17.2", "language-less": "0.29.0", "language-make": "0.21.0", "language-mustache": "0.13.0", - "language-objective-c": "0.15.0", - "language-perl": "0.31.0", + "language-objective-c": "0.15.1", + "language-perl": "0.32.0", "language-php": "0.34.0", "language-property-list": "0.8.0", "language-python": "0.42.1", - "language-ruby": "0.64.1", + "language-ruby": "0.65.0", "language-ruby-on-rails": "0.24.0", - "language-sass": "0.44.1", + "language-sass": "0.45.0", "language-shellscript": "0.21.0", "language-source": "0.9.0", - "language-sql": "0.19.0", + "language-sql": "0.20.0", "language-text": "0.7.0", "language-todo": "0.27.0", - "language-toml": "0.17.0", + "language-toml": "0.18.0", "language-xml": "0.34.1", - "language-yaml": "0.24.0" + "language-yaml": "0.25.0" }, "private": true, "scripts": { diff --git a/script/create-shortcut.cmd b/script/create-shortcut.cmd deleted file mode 100644 index ca295d434..000000000 --- a/script/create-shortcut.cmd +++ /dev/null @@ -1,23 +0,0 @@ -@echo off - -set USAGE=Usage: %0 source name-on-desktop - -if [%1] == [] ( - echo %USAGE% - exit 1 -) -if [%2] == [] ( - echo %USAGE% - exit 2 -) - -set SCRIPT="%TEMP%\%RANDOM%-%RANDOM%-%RANDOM%-%RANDOM%.vbs" - -echo Set oWS = WScript.CreateObject("WScript.Shell") >> %SCRIPT% -echo sLinkFile = "%USERPROFILE%\Desktop\%2.lnk" >> %SCRIPT% -echo Set oLink = oWS.CreateShortcut(sLinkFile) >> %SCRIPT% -echo oLink.TargetPath = %1 >> %SCRIPT% -echo oLink.Save >> %SCRIPT% - -cscript /nologo %SCRIPT% -del %SCRIPT% diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index 609d20291..d5e9f5425 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -484,7 +484,7 @@ describe('TextEditorComponent', function () { it('displays newlines as their own token outside of the other tokens\' scopeDescriptor', async function () { editor.setText('let\n') await nextViewUpdatePromise() - expect(component.lineNodeForScreenRow(0).innerHTML).toBe('let' + invisibles.eol + '') + expect(component.lineNodeForScreenRow(0).innerHTML).toBe('let' + invisibles.eol + '') }) it('displays trailing carriage returns using a visible, non-empty value', async function () { diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 8dd34fde8..2aeb8822e 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -611,7 +611,7 @@ describe "TextEditorPresenter", -> expect(presenter.getState().hiddenInput.width).toBe 15 expectStateUpdate presenter, -> - presenter.getLinesYardstick().setScopedCharacterWidth(['source.js', 'storage.modifier.js'], 'r', 20) + presenter.getLinesYardstick().setScopedCharacterWidth(['source.js', 'storage.type.var.js'], 'r', 20) presenter.characterWidthsChanged() expect(presenter.getState().hiddenInput.width).toBe 20 @@ -1449,12 +1449,12 @@ describe "TextEditorPresenter", -> presenter = buildPresenter(explicitHeight: 20) expectStateUpdate presenter, -> - presenter.getLinesYardstick().setScopedCharacterWidth(['source.js', 'storage.modifier.js'], 'v', 20) + presenter.getLinesYardstick().setScopedCharacterWidth(['source.js', 'storage.type.var.js'], 'v', 20) presenter.characterWidthsChanged() expect(stateForCursor(presenter, 0)).toEqual {top: 1 * 10, left: (3 * 10) + 20, width: 10, height: 10} expectStateUpdate presenter, -> - presenter.getLinesYardstick().setScopedCharacterWidth(['source.js', 'storage.modifier.js'], 'r', 20) + presenter.getLinesYardstick().setScopedCharacterWidth(['source.js', 'storage.type.var.js'], 'r', 20) presenter.characterWidthsChanged() expect(stateForCursor(presenter, 0)).toEqual {top: 1 * 10, left: (3 * 10) + 20, width: 20, height: 10} diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 0cee8215a..43265c58d 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -762,11 +762,24 @@ describe "TextEditor", -> editor.moveToBeginningOfWord() expect(editor.getCursorBufferPosition()).toEqual [10, 0] + it "treats lines with only whitespace as a word (CRLF line ending)", -> + editor.buffer.setText(buffer.getText().replace(/\n/g, "\r\n")) + editor.setCursorBufferPosition([11, 0]) + editor.moveToBeginningOfWord() + expect(editor.getCursorBufferPosition()).toEqual [10, 0] + it "works when the current line is blank", -> editor.setCursorBufferPosition([10, 0]) editor.moveToBeginningOfWord() expect(editor.getCursorBufferPosition()).toEqual [9, 2] + it "works when the current line is blank (CRLF line ending)", -> + editor.buffer.setText(buffer.getText().replace(/\n/g, "\r\n")) + editor.setCursorBufferPosition([10, 0]) + editor.moveToBeginningOfWord() + expect(editor.getCursorBufferPosition()).toEqual [9, 2] + editor.buffer.setText(buffer.getText().replace(/\r\n/g, "\n")) + describe ".moveToPreviousWordBoundary()", -> it "moves the cursor to the previous word boundary", -> editor.setCursorBufferPosition [0, 8] @@ -821,11 +834,23 @@ describe "TextEditor", -> editor.moveToEndOfWord() expect(editor.getCursorBufferPosition()).toEqual [10, 0] + it "treats lines with only whitespace as a word (CRLF line ending)", -> + editor.buffer.setText(buffer.getText().replace(/\n/g, "\r\n")) + editor.setCursorBufferPosition([9, 4]) + editor.moveToEndOfWord() + expect(editor.getCursorBufferPosition()).toEqual [10, 0] + it "works when the current line is blank", -> editor.setCursorBufferPosition([10, 0]) editor.moveToEndOfWord() expect(editor.getCursorBufferPosition()).toEqual [11, 8] + it "works when the current line is blank (CRLF line ending)", -> + editor.buffer.setText(buffer.getText().replace(/\n/g, "\r\n")) + editor.setCursorBufferPosition([10, 0]) + editor.moveToEndOfWord() + expect(editor.getCursorBufferPosition()).toEqual [11, 8] + describe ".moveToBeginningOfNextWord()", -> it "moves the cursor before the first character of the next word", -> editor.setCursorBufferPosition [0, 6] @@ -1055,8 +1080,36 @@ describe "TextEditor", -> editor.moveToBeginningOfNextParagraph() expect(editor.getCursorBufferPosition()).toEqual [0, 0] + it "moves the cursor before the first line of the next paragraph (CRLF line endings)", -> + editor.setText(editor.getText().replace(/\n/g, '\r\n')) + + editor.setCursorBufferPosition [0, 6] + editor.foldBufferRow(4) + + editor.moveToBeginningOfNextParagraph() + expect(editor.getCursorBufferPosition()).toEqual [10, 0] + + editor.setText("") + editor.setCursorBufferPosition [0, 0] + editor.moveToBeginningOfNextParagraph() + expect(editor.getCursorBufferPosition()).toEqual [0, 0] + describe ".moveToBeginningOfPreviousParagraph()", -> - it "moves the cursor before the first line of the pevious paragraph", -> + it "moves the cursor before the first line of the previous paragraph", -> + editor.setCursorBufferPosition [10, 0] + editor.foldBufferRow(4) + + editor.moveToBeginningOfPreviousParagraph() + expect(editor.getCursorBufferPosition()).toEqual [0, 0] + + editor.setText("") + editor.setCursorBufferPosition [0, 0] + editor.moveToBeginningOfPreviousParagraph() + expect(editor.getCursorBufferPosition()).toEqual [0, 0] + + it "moves the cursor before the first line of the previous paragraph (CRLF line endings)", -> + editor.setText(editor.getText().replace(/\n/g, '\r\n')) + editor.setCursorBufferPosition [10, 0] editor.foldBufferRow(4) @@ -5337,7 +5390,7 @@ describe "TextEditor", -> tokens = atom.grammars.decodeTokens(line, tags) expect(tokens[0].value).toBe "var" - expect(tokens[0].scopes).toEqual ["source.js", "storage.modifier.js"] + expect(tokens[0].scopes).toEqual ["source.js", "storage.type.var.js"] expect(tokens[6].value).toBe "http://github.com" expect(tokens[6].scopes).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"] diff --git a/spec/tokenized-buffer-spec.coffee b/spec/tokenized-buffer-spec.coffee index 5d6d3cfdc..692e758a9 100644 --- a/spec/tokenized-buffer-spec.coffee +++ b/spec/tokenized-buffer-spec.coffee @@ -198,7 +198,7 @@ describe "TokenizedBuffer", -> buffer.setTextInRange([[1, 0], [3, 0]], "foo()") # previous line 0 remains - expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0]).toEqual(value: 'var', scopes: ['source.js', 'storage.modifier.js']) + expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0]).toEqual(value: 'var', scopes: ['source.js', 'storage.type.var.js']) # previous line 3 should be combined with input to form line 1 expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: 'foo', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js']) @@ -242,7 +242,7 @@ describe "TokenizedBuffer", -> buffer.setTextInRange([[1, 0], [2, 0]], "foo()\nbar()\nbaz()\nquux()") # previous line 0 remains - expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0]).toEqual( value: 'var', scopes: ['source.js', 'storage.modifier.js']) + expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0]).toEqual( value: 'var', scopes: ['source.js', 'storage.type.var.js']) # 3 new lines inserted expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: 'foo', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js']) @@ -582,7 +582,7 @@ describe "TokenizedBuffer", -> fullyTokenize(tokenizedBuffer) expect(tokenizedBuffer.tokenForPosition([1, 0]).scopes).toEqual ["source.js"] expect(tokenizedBuffer.tokenForPosition([1, 1]).scopes).toEqual ["source.js"] - expect(tokenizedBuffer.tokenForPosition([1, 2]).scopes).toEqual ["source.js", "storage.modifier.js"] + expect(tokenizedBuffer.tokenForPosition([1, 2]).scopes).toEqual ["source.js", "storage.type.var.js"] describe ".bufferRangeForScopeAtPosition(selector, position)", -> beforeEach -> @@ -599,8 +599,8 @@ describe "TokenizedBuffer", -> describe "when the selector matches a single token at the position", -> it "returns the range covered by the token", -> - expect(tokenizedBuffer.bufferRangeForScopeAtPosition('.storage.modifier.js', [0, 1])).toEqual [[0, 0], [0, 3]] - expect(tokenizedBuffer.bufferRangeForScopeAtPosition('.storage.modifier.js', [0, 3])).toEqual [[0, 0], [0, 3]] + expect(tokenizedBuffer.bufferRangeForScopeAtPosition('.storage.type.var.js', [0, 1])).toEqual [[0, 0], [0, 3]] + expect(tokenizedBuffer.bufferRangeForScopeAtPosition('.storage.type.var.js', [0, 3])).toEqual [[0, 0], [0, 3]] describe "when the selector matches a run of multiple tokens at the position", -> it "returns the range covered by all contigous tokens (within a single line)", -> diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 0587a2629..e06a3e598 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -76,7 +76,7 @@ describe "Workspace", -> expect(editor4.getCursorScreenPosition()).toEqual [2, 4] expect(atom.workspace.getActiveTextEditor().getPath()).toBe editor3.getPath() - expect(document.title).toBe "#{path.basename(editor3.getLongTitle())} - #{atom.project.getPaths()[0]} - Atom" + expect(document.title).toMatch ///^#{path.basename(editor3.getLongTitle())}\ \u2014\ #{atom.project.getPaths()[0]}/// describe "where there are no open panes or editors", -> it "constructs the view with no open editors", -> @@ -732,7 +732,7 @@ describe "Workspace", -> describe "when the project has no path", -> it "sets the title to 'untitled'", -> atom.project.setPaths([]) - expect(document.title).toBe 'untitled - Atom' + expect(document.title).toMatch ///^untitled/// describe "when the project has a path", -> beforeEach -> @@ -742,25 +742,25 @@ describe "Workspace", -> describe "when there is an active pane item", -> it "sets the title to the pane item's title plus the project path", -> item = atom.workspace.getActivePaneItem() - expect(document.title).toBe "#{item.getTitle()} - #{atom.project.getPaths()[0]} - Atom" + expect(document.title).toMatch ///^#{item.getTitle()}\ \u2014\ #{atom.project.getPaths()[0]}/// describe "when the title of the active pane item changes", -> it "updates the window title based on the item's new title", -> editor = atom.workspace.getActivePaneItem() editor.buffer.setPath(path.join(temp.dir, 'hi')) - expect(document.title).toBe "#{editor.getTitle()} - #{atom.project.getPaths()[0]} - Atom" + expect(document.title).toMatch ///^#{editor.getTitle()}\ \u2014\ #{atom.project.getPaths()[0]}/// describe "when the active pane's item changes", -> it "updates the title to the new item's title plus the project path", -> atom.workspace.getActivePane().activateNextItem() item = atom.workspace.getActivePaneItem() - expect(document.title).toBe "#{item.getTitle()} - #{atom.project.getPaths()[0]} - Atom" + expect(document.title).toMatch ///^#{item.getTitle()}\ \u2014\ #{atom.project.getPaths()[0]}/// describe "when the last pane item is removed", -> it "updates the title to contain the project's path", -> atom.workspace.getActivePane().destroy() expect(atom.workspace.getActivePaneItem()).toBeUndefined() - expect(document.title).toBe "#{atom.project.getPaths()[0]} - Atom" + expect(document.title).toMatch ///^#{atom.project.getPaths()[0]}/// describe "when an inactive pane's item changes", -> it "does not update the title", -> @@ -784,7 +784,7 @@ describe "Workspace", -> }) workspace2.deserialize(atom.workspace.serialize(), atom.deserializers) item = workspace2.getActivePaneItem() - expect(document.title).toBe "#{item.getLongTitle()} - #{atom.project.getPaths()[0]} - Atom" + expect(document.title).toMatch ///^#{item.getLongTitle()}\ \u2014\ #{atom.project.getPaths()[0]}/// workspace2.destroy() describe "document edited status", -> diff --git a/src/cursor.coffee b/src/cursor.coffee index 0f87c2760..5b3b23b73 100644 --- a/src/cursor.coffee +++ b/src/cursor.coffee @@ -3,6 +3,8 @@ _ = require 'underscore-plus' Model = require './model' +EmptyLineRegExp = /(\r\n[\t ]*\r\n)|(\n[\t ]*\n)/g + # Extended: The `Cursor` class represents the little blinking line identifying # where text can be inserted. # @@ -467,10 +469,13 @@ class Cursor extends Model scanRange = [[previousNonBlankRow, 0], currentBufferPosition] beginningOfWordPosition = null - @editor.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, stop}) -> - if range.end.isGreaterThanOrEqual(currentBufferPosition) or allowPrevious - beginningOfWordPosition = range.start - if not beginningOfWordPosition?.isEqual(currentBufferPosition) + @editor.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, matchText, stop}) -> + # Ignore 'empty line' matches between '\r' and '\n' + return if matchText is '' and range.start.column isnt 0 + + if range.start.isLessThan(currentBufferPosition) + if range.end.isGreaterThanOrEqual(currentBufferPosition) or allowPrevious + beginningOfWordPosition = range.start stop() if beginningOfWordPosition? @@ -496,13 +501,12 @@ class Cursor extends Model scanRange = [currentBufferPosition, @editor.getEofBufferPosition()] endOfWordPosition = null - @editor.scanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, stop}) -> - if allowNext - if range.end.isGreaterThan(currentBufferPosition) - endOfWordPosition = range.end - stop() - else - if range.start.isLessThanOrEqual(currentBufferPosition) + @editor.scanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, matchText, stop}) -> + # Ignore 'empty line' matches between '\r' and '\n' + return if matchText is '' and range.start.column isnt 0 + + if range.end.isGreaterThan(currentBufferPosition) + if allowNext or range.start.isLessThanOrEqual(currentBufferPosition) endOfWordPosition = range.end stop() @@ -603,14 +607,14 @@ class Cursor extends Model # non-word characters in the regex. (default: true) # # Returns a {RegExp}. - wordRegExp: ({includeNonWordCharacters}={}) -> - includeNonWordCharacters ?= true - nonWordCharacters = @config.get('editor.nonWordCharacters', scope: @getScopeDescriptor()) - segments = ["^[\t ]*$"] - segments.push("[^\\s#{_.escapeRegExp(nonWordCharacters)}]+") - if includeNonWordCharacters - segments.push("[#{_.escapeRegExp(nonWordCharacters)}]+") - new RegExp(segments.join("|"), "g") + wordRegExp: (options) -> + scope = @getScopeDescriptor() + nonWordCharacters = _.escapeRegExp(@config.get('editor.nonWordCharacters', {scope})) + + source = "^[\t ]*$|[^\\s#{nonWordCharacters}]+" + if options?.includeNonWordCharacters ? true + source += "|" + "[#{nonWordCharacters}]+" + new RegExp(source, "g") # Public: Get the RegExp used by the cursor to determine what a "subword" is. # @@ -666,10 +670,9 @@ class Cursor extends Model {row, column} = eof position = new Point(row, column - 1) - @editor.scanInBufferRange /^\n*$/g, scanRange, ({range, stop}) -> - unless range.start.isEqual(start) - position = range.start - stop() + @editor.scanInBufferRange EmptyLineRegExp, scanRange, ({range, stop}) -> + position = range.start.traverse(Point(1, 0)) + stop() unless position.isEqual(start) position getBeginningOfPreviousParagraphBufferPosition: -> @@ -679,8 +682,7 @@ class Cursor extends Model scanRange = [[row-1, column], [0, 0]] position = new Point(0, 0) zero = new Point(0, 0) - @editor.backwardsScanInBufferRange /^\n*$/g, scanRange, ({range, stop}) -> - unless range.start.isEqual(zero) - position = range.start - stop() + @editor.backwardsScanInBufferRange EmptyLineRegExp, scanRange, ({range, stop}) -> + position = range.start.traverse(Point(1, 0)) + stop() unless position.isEqual(start) position diff --git a/src/workspace.coffee b/src/workspace.coffee index 04feef61e..f64f58ee0 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -161,15 +161,22 @@ class Workspace extends Model itemTitle ?= "untitled" projectPath ?= projectPaths[0] + titleParts = [] if item? and projectPath? - document.title = "#{itemTitle} - #{projectPath} - #{appName}" - @applicationDelegate.setRepresentedFilename(itemPath ? projectPath) + titleParts.push itemTitle, projectPath + representedPath = itemPath ? projectPath else if projectPath? - document.title = "#{projectPath} - #{appName}" - @applicationDelegate.setRepresentedFilename(projectPath) + titleParts.push projectPath + representedPath = projectPath else - document.title = "#{itemTitle} - #{appName}" - @applicationDelegate.setRepresentedFilename("") + titleParts.push itemTitle + representedPath = "" + + unless process.platform is 'darwin' + titleParts.push appName + + document.title = titleParts.join(" \u2014 ") + @applicationDelegate.setRepresentedFilename(representedPath) # On OS X, fades the application window's proxy icon when the current file # has been modified.