diff --git a/.travis.yml b/.travis.yml index bf1bd330e..3ce84c266 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,6 +32,10 @@ install: script: script/cibuild +cache: + directories: + - node_modules + notifications: email: on_success: never diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index 8dd1c573b..26d9c2f42 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -284,6 +284,7 @@ module.exports = (grunt) -> ciTasks.push('download-electron') ciTasks.push('download-electron-chromedriver') ciTasks.push('build') + ciTasks.push('fingerprint') ciTasks.push('dump-symbols') if process.platform isnt 'win32' ciTasks.push('set-version', 'check-licenses', 'lint', 'generate-asar') ciTasks.push('mkdeb') if process.platform is 'linux' diff --git a/build/package.json b/build/package.json index 2ce92de17..de9053006 100644 --- a/build/package.json +++ b/build/package.json @@ -27,7 +27,7 @@ "grunt-peg": "~1.1.0", "grunt-shell": "~0.3.1", "grunt-standard": "^1.0.2", - "legal-eagle": "~0.12.0", + "legal-eagle": "~0.13.0", "minidump": "~0.9", "npm": "2.13.3", "rcedit": "~0.3.0", diff --git a/build/tasks/fingerprint-task.js b/build/tasks/fingerprint-task.js new file mode 100644 index 000000000..9cfcdae76 --- /dev/null +++ b/build/tasks/fingerprint-task.js @@ -0,0 +1,7 @@ +var fingerprint = require('../../script/utils/fingerprint') + +module.exports = function (grunt) { + grunt.registerTask('fingerprint', 'Fingerpint the node_modules folder for caching on CI', function () { + fingerprint.writeFingerprint() + }) +} diff --git a/package.json b/package.json index e18337a3d..0d9c7b221 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "wrap-guide": "0.38.1", "language-c": "0.49.0", "language-clojure": "0.18.0", - "language-coffee-script": "0.43.0", + "language-coffee-script": "0.45.0", "language-csharp": "0.11.0", "language-css": "0.35.0", "language-gfm": "0.81.0", @@ -127,7 +127,7 @@ "language-html": "0.42.0", "language-hyperlink": "0.15.0", "language-java": "0.16.1", - "language-javascript": "0.101.1", + "language-javascript": "0.102.0", "language-json": "0.17.1", "language-less": "0.28.3", "language-make": "0.20.0", @@ -139,7 +139,7 @@ "language-python": "0.42.1", "language-ruby": "0.64.0", "language-ruby-on-rails": "0.24.0", - "language-sass": "0.43.0", + "language-sass": "0.44.0", "language-shellscript": "0.20.0", "language-source": "0.9.0", "language-sql": "0.19.0", diff --git a/script/cibuild b/script/cibuild index c1aedddc8..b3f0b3f83 100755 --- a/script/cibuild +++ b/script/cibuild @@ -1,5 +1,7 @@ #!/usr/bin/env node var cp = require('./utils/child-process-wrapper.js'); +var crypto = require('crypto') +var fingerprint = require('./utils/fingerprint') var fs = require('fs'); var path = require('path'); @@ -42,6 +44,11 @@ function setEnvironmentVariables() { } function removeNodeModules() { + if (fingerprint.fingerprintMatches()) { + console.log('node_modules matches current fingerprint ' + fingerprint.fingerprint() + ' - not removing') + return + } + var fsPlus; try { fsPlus = require('fs-plus'); @@ -98,8 +105,8 @@ cp.safeExec.bind(global, 'npm install npm --loglevel error', {cwd: path.resolve( var async = require('async'); var gruntPath = path.join('build', 'node_modules', '.bin', 'grunt') + (process.platform === 'win32' ? '.cmd' : ''); var tasks = [ - cp.safeExec.bind(global, 'git clean -dff'), - cp.safeExec.bind(global, gruntPath + ' ci --gruntfile build/Gruntfile.coffee --stack --no-color'), + cp.safeExec.bind(global, 'git clean -dff -e node_modules'), // If we left them behind in removeNodeModules() they are OK to use + cp.safeExec.bind(global, gruntPath + ' ci --gruntfile build/Gruntfile.coffee --stack --no-color') ] async.series(tasks, function(error) { process.exit(error ? 1 : 0); diff --git a/script/utils/fingerprint.js b/script/utils/fingerprint.js new file mode 100644 index 000000000..2da48039f --- /dev/null +++ b/script/utils/fingerprint.js @@ -0,0 +1,31 @@ +var crypto = require('crypto') +var fs = require('fs') +var path = require('path') + +var fingerprintPath = path.resolve(__dirname, '..', '..', 'node_modules', '.atom-ci-fingerprint') + +module.exports = { + fingerprint: function () { + var packageJson = fs.readFileSync(path.resolve(__dirname, '..', '..', 'package.json')) + var body = packageJson.toString() + process.platform + process.version + return crypto.createHash('sha1').update(body).digest('hex') + }, + + writeFingerprint: function () { + var fingerprint = this.fingerprint() + fs.writeFileSync(fingerprintPath, fingerprint) + console.log('Wrote ci fingerprint:', fingerprintPath, fingerprint) + }, + + readFingerprint: function() { + if (fs.existsSync(fingerprintPath)) { + return fs.readFileSync(fingerprintPath).toString() + } else { + return null + } + }, + + fingerprintMatches: function () { + return this.readFingerprint() && this.readFingerprint() === this.fingerprint() + } +} diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 54899a1d6..06a857a64 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -1704,6 +1704,18 @@ describe "TextEditorPresenter", -> expectUndefinedStateForHighlight(presenter, highlight) + it "does not include highlights that end before the first visible row", -> + editor.setText("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.") + editor.setSoftWrapped(true) + editor.setWidth(100, true) + editor.setDefaultCharWidth(10) + + marker = editor.markBufferRange([[0, 0], [0, 4]], invalidate: 'never') + highlight = editor.decorateMarker(marker, type: 'highlight', class: 'a') + presenter = buildPresenter(explicitHeight: 30, scrollTop: 10, tileSize: 2) + + expect(stateForHighlightInTile(presenter, highlight, 0)).toBeUndefined() + it "updates when ::scrollTop changes", -> editor.setSelectedBufferRanges([ [[6, 2], [6, 4]], diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 4001fba8e..63e3e3cf9 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -373,6 +373,8 @@ class AtomApplication # :windowDimensions - Object with height and width keys. # :window - {AtomWindow} to open file paths in. openPaths: ({pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions, profileStartup, window}={}) -> + devMode = Boolean(devMode) + safeMode = Boolean(safeMode) locationsToOpen = (@locationForPathToOpen(pathToOpen, executedFrom) for pathToOpen in pathsToOpen) pathsToOpen = (locationToOpen.pathToOpen for locationToOpen in locationsToOpen) diff --git a/src/config.coffee b/src/config.coffee index 888193059..489e16016 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -699,7 +699,7 @@ class Config @endTransaction() fn(args...) result = callback() - new Promise (resolve, reject) => + new Promise (resolve, reject) -> result.then(endTransaction(resolve)).catch(endTransaction(reject)) catch error @endTransaction() diff --git a/src/default-directory-searcher.coffee b/src/default-directory-searcher.coffee index 1c9b40312..6b8ffe3e3 100644 --- a/src/default-directory-searcher.coffee +++ b/src/default-directory-searcher.coffee @@ -1,8 +1,7 @@ Task = require './task' -# Public: Searches local files for lines matching a specified regex. -# -# Implements thenable so it can be used with `Promise.all()`. +# Searches local files for lines matching a specified regex. Implements `.then()` +# so that it can be used with `Promise.all()`. class DirectorySearch constructor: (rootPaths, regex, options) -> scanHandlerOptions = @@ -22,31 +21,25 @@ class DirectorySearch @task.terminate() resolve() - # Public: Implementation of `then()` to satisfy the *thenable* contract. - # This makes it possible to use a `DirectorySearch` with `Promise.all()`. - # - # Returns `Promise`. then: (args...) -> @promise.then.apply(@promise, args) - # Public: Cancels the search. cancel: -> # This will cause @promise to reject. @task.cancel() null - # Default provider for the `atom.directory-searcher` service. module.exports = class DefaultDirectorySearcher - # Public: Determines whether this object supports search for a `Directory`. + # Determines whether this object supports search for a `Directory`. # # * `directory` {Directory} whose search needs might be supported by this object. # # Returns a `boolean` indicating whether this object can search this `Directory`. canSearchDirectory: (directory) -> true - # Public: Performs a text search for files in the specified `Directory`, subject to the + # Performs a text search for files in the specified `Directory`, subject to the # specified parameters. # # Results are streamed back to the caller by invoking methods on the specified `options`, diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 7bd66b87f..b1f28af11 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -1244,14 +1244,7 @@ class TextEditorPresenter updateHighlightState: (decorationId, properties, screenRange) -> return unless @startRow? and @endRow? and @lineHeight? and @hasPixelPositionRequirements() - return if screenRange.isEmpty() - - if screenRange.start.row < @startRow - screenRange.start.row = @startRow - screenRange.start.column = 0 - if screenRange.end.row >= @endRow - screenRange.end.row = @endRow - screenRange.end.column = 0 + @constrainRangeToVisibleRowRange(screenRange) return if screenRange.isEmpty() @@ -1281,6 +1274,23 @@ class TextEditorPresenter true + constrainRangeToVisibleRowRange: (screenRange) -> + if screenRange.start.row < @startRow + screenRange.start.row = @startRow + screenRange.start.column = 0 + + if screenRange.end.row < @startRow + screenRange.end.row = @startRow + screenRange.end.column = 0 + + if screenRange.start.row >= @endRow + screenRange.start.row = @endRow + screenRange.start.column = 0 + + if screenRange.end.row >= @endRow + screenRange.end.row = @endRow + screenRange.end.column = 0 + repositionRegionWithinTile: (region, tileStartRow) -> region.top += @scrollTop - tileStartRow * @lineHeight region.left += @scrollLeft