From 722089be4531300e991b92bd384fc31d7ed44579 Mon Sep 17 00:00:00 2001 From: basarat Date: Sun, 8 Mar 2015 14:49:22 +1100 Subject: [PATCH 01/36] feat(typescript) initial commit of built in typescript support --- package.json | 1 + src/typescript-transpile.js | 152 ++++++++++++++++++++++++++++++++++++ src/typescript.coffee | 141 +++++++++++++++++++++++++++++++++ static/index.js | 7 ++ 4 files changed, 301 insertions(+) create mode 100644 src/typescript-transpile.js create mode 100644 src/typescript.coffee diff --git a/package.json b/package.json index 2ec0091a3..60f96cb34 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "temp": "0.8.1", "text-buffer": "^4.1.5", "theorist": "^1.0.2", + "typescript": "^1.4.1", "underscore-plus": "^1.6.6" }, "packageDependencies": { diff --git a/src/typescript-transpile.js b/src/typescript-transpile.js new file mode 100644 index 000000000..64d832d1c --- /dev/null +++ b/src/typescript-transpile.js @@ -0,0 +1,152 @@ +// From https://github.com/teppeis/typescript-simple/blob/master/index.js +// with https://github.com/teppeis/typescript-simple/pull/7 + +var fs = require('fs'); +var os = require('os'); +var path = require('path'); +var ts = require('typescript'); +var FILENAME_TS = 'file.ts'; +function tss(code, options) { + if (options) { + return new tss.TypeScriptSimple(options).compile(code); + } + else { + return defaultTss.compile(code); + } +} +var tss; +(function (tss) { + var TypeScriptSimple = (function () { + /** + * @param {ts.CompilerOptions=} options TypeScript compile options (some options are ignored) + */ + function TypeScriptSimple(options, doSemanticChecks) { + if (options === void 0) { options = {}; } + if (doSemanticChecks === void 0) { doSemanticChecks = true; } + this.doSemanticChecks = doSemanticChecks; + this.service = null; + this.outputs = {}; + this.files = {}; + if (options.target == null) { + options.target = ts.ScriptTarget.ES5; + } + if (options.module == null) { + options.module = ts.ModuleKind.None; + } + this.options = options; + } + /** + * @param {string} code TypeScript source code to compile + * @param {string} only needed if you plan to use sourceMaps. Provide the complete filePath relevant to you + * @return {string} The JavaScript with inline sourceMaps if sourceMaps were enabled + */ + TypeScriptSimple.prototype.compile = function (code, filename) { + if (filename === void 0) { filename = FILENAME_TS; } + if (!this.service) { + this.service = this.createService(); + } + var file = this.files[FILENAME_TS]; + file.text = code; + file.version++; + return this.toJavaScript(this.service, filename); + }; + TypeScriptSimple.prototype.createService = function () { + var _this = this; + var defaultLib = this.getDefaultLibFilename(this.options); + var defaultLibPath = path.join(this.getTypeScriptBinDir(), defaultLib); + this.files[defaultLib] = { version: 0, text: fs.readFileSync(defaultLibPath).toString() }; + this.files[FILENAME_TS] = { version: 0, text: '' }; + var servicesHost = { + getScriptFileNames: function () { return [_this.getDefaultLibFilename(_this.options), FILENAME_TS]; }, + getScriptVersion: function (filename) { return _this.files[filename] && _this.files[filename].version.toString(); }, + getScriptSnapshot: function (filename) { + var file = _this.files[filename]; + return { + getText: function (start, end) { return file.text.substring(start, end); }, + getLength: function () { return file.text.length; }, + getLineStartPositions: function () { return []; }, + getChangeRange: function (oldSnapshot) { return undefined; } + }; + }, + getCurrentDirectory: function () { return process.cwd(); }, + getScriptIsOpen: function () { return true; }, + getCompilationSettings: function () { return _this.options; }, + getDefaultLibFilename: function (options) { + return _this.getDefaultLibFilename(options); + }, + log: function (message) { return console.log(message); } + }; + return ts.createLanguageService(servicesHost, ts.createDocumentRegistry()); + }; + TypeScriptSimple.prototype.getTypeScriptBinDir = function () { + return path.dirname(require.resolve('typescript')); + }; + TypeScriptSimple.prototype.getDefaultLibFilename = function (options) { + if (options.target === ts.ScriptTarget.ES6) { + return 'lib.es6.d.ts'; + } + else { + return 'lib.d.ts'; + } + }; + /** + * converts {"version":3,"file":"file.js","sourceRoot":"","sources":["file.ts"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG,MAAM,CAAC"} + * to {"version":3,"sources":["foo/test.ts"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG,MAAM,CAAC","file":"foo/test.ts","sourcesContent":["var x = 'test';"]} + * derived from : https://github.com/thlorenz/convert-source-map + */ + TypeScriptSimple.prototype.getInlineSourceMap = function (mapText, filename) { + var sourceMap = JSON.parse(mapText); + sourceMap.file = filename; + sourceMap.sources = [filename]; + sourceMap.sourcesContent = [this.files[FILENAME_TS].text]; + delete sourceMap.sourceRoot; + return JSON.stringify(sourceMap); + }; + TypeScriptSimple.prototype.toJavaScript = function (service, filename) { + if (filename === void 0) { filename = FILENAME_TS; } + var output = service.getEmitOutput(FILENAME_TS); + // Meaning of succeeded is driven by whether we need to check for semantic errors or not + var succeeded = output.emitOutputStatus === ts.EmitReturnStatus.Succeeded; + if (!this.doSemanticChecks) { + // We have an output. It implies syntactic success + if (!succeeded) + succeeded = !!output.outputFiles.length; + } + if (succeeded) { + var outputFilename = FILENAME_TS.replace(/ts$/, 'js'); + var file = output.outputFiles.filter(function (file) { return file.name === outputFilename; })[0]; + // Fixed in v1.5 https://github.com/Microsoft/TypeScript/issues/1653 + var text = file.text.replace(/\r\n/g, os.EOL); + // If we have sourceMaps convert them to inline sourceMaps + if (this.options.sourceMap) { + var sourceMapFilename = FILENAME_TS.replace(/ts$/, 'js.map'); + var sourceMapFile = output.outputFiles.filter(function (file) { return file.name === sourceMapFilename; })[0]; + // Transform sourcemap + var sourceMapText = sourceMapFile.text; + sourceMapText = this.getInlineSourceMap(sourceMapText, filename); + var base64SourceMapText = new Buffer(sourceMapText).toString('base64'); + text = text.replace('//# sourceMappingURL=' + sourceMapFilename, '//# sourceMappingURL=data:application/json;base64,' + base64SourceMapText); + } + return text; + } + var allDiagnostics = service.getCompilerOptionsDiagnostics().concat(service.getSyntacticDiagnostics(FILENAME_TS)); + if (this.doSemanticChecks) + allDiagnostics = allDiagnostics.concat(service.getSemanticDiagnostics(FILENAME_TS)); + throw new Error(this.formatDiagnostics(allDiagnostics)); + }; + TypeScriptSimple.prototype.formatDiagnostics = function (diagnostics) { + return diagnostics.map(function (d) { + if (d.file) { + return 'L' + d.file.getLineAndCharacterFromPosition(d.start).line + ': ' + d.messageText; + } + else { + return d.messageText; + } + }).join(os.EOL); + }; + return TypeScriptSimple; + })(); + tss.TypeScriptSimple = TypeScriptSimple; +})(tss || (tss = {})); +var defaultTss = new tss.TypeScriptSimple(); +module.exports = tss; diff --git a/src/typescript.coffee b/src/typescript.coffee new file mode 100644 index 000000000..e47e7b2a1 --- /dev/null +++ b/src/typescript.coffee @@ -0,0 +1,141 @@ +### +Cache for source code transpiled by TypeScript. + +Inspired by https://github.com/atom/atom/blob/7a719d585db96ff7d2977db9067e1d9d4d0adf1a/src/babel.coffee +### + +crypto = require 'crypto' +fs = require 'fs-plus' +path = require 'path' +tss = null # Defer until used + +stats = + hits: 0 + misses: 0 + +defaultOptions = + target: 1 + module: 'commonjs' + sourceMap: true + +### +shasum - Hash with an update() method. +value - Must be a value that could be returned by JSON.parse(). +### +updateDigestForJsonValue = (shasum, value) -> + # Implmentation is similar to that of pretty-printing a JSON object, except: + # * Strings are not escaped. + # * No effort is made to avoid trailing commas. + # These shortcuts should not affect the correctness of this function. + type = typeof value + if type is 'string' + shasum.update('"', 'utf8') + shasum.update(value, 'utf8') + shasum.update('"', 'utf8') + else if type in ['boolean', 'number'] + shasum.update(value.toString(), 'utf8') + else if value is null + shasum.update('null', 'utf8') + else if Array.isArray value + shasum.update('[', 'utf8') + for item in value + updateDigestForJsonValue(shasum, item) + shasum.update(',', 'utf8') + shasum.update(']', 'utf8') + else + # value must be an object: be sure to sort the keys. + keys = Object.keys value + keys.sort() + + shasum.update('{', 'utf8') + for key in keys + updateDigestForJsonValue(shasum, key) + shasum.update(': ', 'utf8') + updateDigestForJsonValue(shasum, value[key]) + shasum.update(',', 'utf8') + shasum.update('}', 'utf8') + +createTypeScriptVersionAndOptionsDigest = (version, options) -> + shasum = crypto.createHash('sha1') + # Include the version of typescript in the hash. + shasum.update('typescript', 'utf8') + shasum.update('\0', 'utf8') + shasum.update(version, 'utf8') + shasum.update('\0', 'utf8') + updateDigestForJsonValue(shasum, options) + shasum.digest('hex') + +cacheDir = null +jsCacheDir = null + +getCachePath = (sourceCode) -> + digest = crypto.createHash('sha1').update(sourceCode, 'utf8').digest('hex') + + unless jsCacheDir? + tsVersion = require('typescript/package.json').version + jsCacheDir = path.join(cacheDir, createTypeScriptVersionAndOptionsDigest(tsVersion, defaultOptions)) + + path.join(jsCacheDir, "#{digest}.js") + +getCachedJavaScript = (cachePath) -> + if fs.isFileSync(cachePath) + try + cachedJavaScript = fs.readFileSync(cachePath, 'utf8') + stats.hits++ + return cachedJavaScript + null + +# Returns the TypeScript options that should be used to transpile filePath. +createOptions = (filePath) -> + options = filename: filePath + for key, value of defaultOptions + options[key] = value + options + +transpile = (sourceCode, filePath, cachePath) -> + options = createOptions(filePath) + tss ?= new (require './typescript-transpile').TypeScriptSimple(options, false) + js = tss.compile(sourceCode, filePath) + stats.misses++ + + try + fs.writeFileSync(cachePath, js) + + js + +# Function that obeys the contract of an entry in the require.extensions map. +# Returns the transpiled version of the JavaScript code at filePath, which is +# either generated on the fly or pulled from cache. +loadFile = (module, filePath) -> + sourceCode = fs.readFileSync(filePath, 'utf8') + cachePath = getCachePath(sourceCode) + js = getCachedJavaScript(cachePath) ? transpile(sourceCode, filePath, cachePath) + module._compile(js, filePath) + +register = -> + Object.defineProperty(require.extensions, '.ts', { + enumerable: true + writable: false + value: loadFile + }) + +setCacheDirectory = (newCacheDir) -> + if cacheDir isnt newCacheDir + cacheDir = newCacheDir + jsCacheDir = null + +module.exports = + register: register + setCacheDirectory: setCacheDirectory + getCacheMisses: -> stats.misses + getCacheHits: -> stats.hits + + # Visible for testing. + createTypeScriptVersionAndOptionsDigest: createTypeScriptVersionAndOptionsDigest + + addPathToCache: (filePath) -> + return if path.extname(filePath) isnt '.ts' + + sourceCode = fs.readFileSync(filePath, 'utf8') + cachePath = getCachePath(sourceCode) + transpile(sourceCode, filePath, cachePath) diff --git a/static/index.js b/static/index.js index e8142a698..d99a9e9e7 100644 --- a/static/index.js +++ b/static/index.js @@ -47,6 +47,7 @@ window.onload = function() { setupCsonCache(cacheDir); setupSourceMapCache(cacheDir); setupBabel(cacheDir); + setupTypeScript(cacheDir); require(loadSettings.bootstrapScript); require('ipc').sendChannel('window-command', 'window:loaded'); @@ -95,6 +96,12 @@ var setupBabel = function(cacheDir) { babel.register(); } +var setupTypeScript = function(cacheDir) { + var typescript = require('../src/typescript'); + typescript.setCacheDirectory(path.join(cacheDir, 'typescript')); + typescript.register(); +} + var setupCsonCache = function(cacheDir) { require('season').setCacheDir(path.join(cacheDir, 'cson')); } From 8793bebfab542915a7b8fde58228ff86f8d506fc Mon Sep 17 00:00:00 2001 From: Basarat Syed Date: Wed, 18 Mar 2015 17:53:15 +1100 Subject: [PATCH 02/36] :white_check_mark: tests for typescript transpiling --- spec/fixtures/typescript/invalid.ts | 1 + spec/fixtures/typescript/valid.ts | 2 ++ spec/typescript-spec.coffee | 36 +++++++++++++++++++++++++++++ src/typescript.coffee | 2 +- 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 spec/fixtures/typescript/invalid.ts create mode 100644 spec/fixtures/typescript/valid.ts create mode 100644 spec/typescript-spec.coffee diff --git a/spec/fixtures/typescript/invalid.ts b/spec/fixtures/typescript/invalid.ts new file mode 100644 index 000000000..7a8d0b6d0 --- /dev/null +++ b/spec/fixtures/typescript/invalid.ts @@ -0,0 +1 @@ +var foo = 123 123; // Syntax error diff --git a/spec/fixtures/typescript/valid.ts b/spec/fixtures/typescript/valid.ts new file mode 100644 index 000000000..46cf54693 --- /dev/null +++ b/spec/fixtures/typescript/valid.ts @@ -0,0 +1,2 @@ +var inc = v => v + 1 +export = inc diff --git a/spec/typescript-spec.coffee b/spec/typescript-spec.coffee new file mode 100644 index 000000000..408e9fb77 --- /dev/null +++ b/spec/typescript-spec.coffee @@ -0,0 +1,36 @@ +typescript = require '../src/typescript' +crypto = require 'crypto' + +describe "TypeScript transpiler support", -> + beforeEach -> + jasmine.snapshotDeprecations() + + afterEach -> + jasmine.restoreDeprecationsSnapshot() + + describe "::createTypeScriptVersionAndOptionsDigest", -> + it "returns a digest for the library version and specified options", -> + defaultOptions = + target: 1 # ES5 + module: 'commonjs' + sourceMap: true + version = '1.4.1' + shasum = crypto.createHash('sha1') + shasum.update('typescript', 'utf8') + shasum.update('\0', 'utf8') + shasum.update(version, 'utf8') + shasum.update('\0', 'utf8') + shasum.update('{"target": 1,"module": "commonjs","sourceMap": true,}') + expectedDigest = shasum.digest('hex') + + observedDigest = typescript.createTypeScriptVersionAndOptionsDigest(version, defaultOptions) + expect(observedDigest).toEqual expectedDigest + + describe "when there is a .ts file", -> + it "transpiles it using typescript", -> + transpiled = require('./fixtures/typescript/valid.ts') + expect(transpiled(3)).toBe 4 + + describe "when the .ts file is invalid", -> + it "does not transpile", -> + expect(-> require('./fixtures/typescript/invalid.ts')).toThrow() diff --git a/src/typescript.coffee b/src/typescript.coffee index e47e7b2a1..e336eceff 100644 --- a/src/typescript.coffee +++ b/src/typescript.coffee @@ -14,7 +14,7 @@ stats = misses: 0 defaultOptions = - target: 1 + target: 1 # ES5 module: 'commonjs' sourceMap: true From c2f258c679796520493d535ba84dc4b16b22bd3d Mon Sep 17 00:00:00 2001 From: Basarat Syed Date: Thu, 19 Mar 2015 11:45:13 +1100 Subject: [PATCH 03/36] Addressed code review --- package.json | 1 + spec/typescript-spec.coffee | 8 +- src/typescript-transpile.js | 152 ------------------------------------ src/typescript.coffee | 45 +---------- 4 files changed, 6 insertions(+), 200 deletions(-) delete mode 100644 src/typescript-transpile.js diff --git a/package.json b/package.json index 60f96cb34..9f12fe358 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "text-buffer": "^4.1.5", "theorist": "^1.0.2", "typescript": "^1.4.1", + "typescript-simple": "^1.0.0", "underscore-plus": "^1.6.6" }, "packageDependencies": { diff --git a/spec/typescript-spec.coffee b/spec/typescript-spec.coffee index 408e9fb77..493715d36 100644 --- a/spec/typescript-spec.coffee +++ b/spec/typescript-spec.coffee @@ -2,12 +2,6 @@ typescript = require '../src/typescript' crypto = require 'crypto' describe "TypeScript transpiler support", -> - beforeEach -> - jasmine.snapshotDeprecations() - - afterEach -> - jasmine.restoreDeprecationsSnapshot() - describe "::createTypeScriptVersionAndOptionsDigest", -> it "returns a digest for the library version and specified options", -> defaultOptions = @@ -20,7 +14,7 @@ describe "TypeScript transpiler support", -> shasum.update('\0', 'utf8') shasum.update(version, 'utf8') shasum.update('\0', 'utf8') - shasum.update('{"target": 1,"module": "commonjs","sourceMap": true,}') + shasum.update(JSON.stringify(defaultOptions)) expectedDigest = shasum.digest('hex') observedDigest = typescript.createTypeScriptVersionAndOptionsDigest(version, defaultOptions) diff --git a/src/typescript-transpile.js b/src/typescript-transpile.js deleted file mode 100644 index 64d832d1c..000000000 --- a/src/typescript-transpile.js +++ /dev/null @@ -1,152 +0,0 @@ -// From https://github.com/teppeis/typescript-simple/blob/master/index.js -// with https://github.com/teppeis/typescript-simple/pull/7 - -var fs = require('fs'); -var os = require('os'); -var path = require('path'); -var ts = require('typescript'); -var FILENAME_TS = 'file.ts'; -function tss(code, options) { - if (options) { - return new tss.TypeScriptSimple(options).compile(code); - } - else { - return defaultTss.compile(code); - } -} -var tss; -(function (tss) { - var TypeScriptSimple = (function () { - /** - * @param {ts.CompilerOptions=} options TypeScript compile options (some options are ignored) - */ - function TypeScriptSimple(options, doSemanticChecks) { - if (options === void 0) { options = {}; } - if (doSemanticChecks === void 0) { doSemanticChecks = true; } - this.doSemanticChecks = doSemanticChecks; - this.service = null; - this.outputs = {}; - this.files = {}; - if (options.target == null) { - options.target = ts.ScriptTarget.ES5; - } - if (options.module == null) { - options.module = ts.ModuleKind.None; - } - this.options = options; - } - /** - * @param {string} code TypeScript source code to compile - * @param {string} only needed if you plan to use sourceMaps. Provide the complete filePath relevant to you - * @return {string} The JavaScript with inline sourceMaps if sourceMaps were enabled - */ - TypeScriptSimple.prototype.compile = function (code, filename) { - if (filename === void 0) { filename = FILENAME_TS; } - if (!this.service) { - this.service = this.createService(); - } - var file = this.files[FILENAME_TS]; - file.text = code; - file.version++; - return this.toJavaScript(this.service, filename); - }; - TypeScriptSimple.prototype.createService = function () { - var _this = this; - var defaultLib = this.getDefaultLibFilename(this.options); - var defaultLibPath = path.join(this.getTypeScriptBinDir(), defaultLib); - this.files[defaultLib] = { version: 0, text: fs.readFileSync(defaultLibPath).toString() }; - this.files[FILENAME_TS] = { version: 0, text: '' }; - var servicesHost = { - getScriptFileNames: function () { return [_this.getDefaultLibFilename(_this.options), FILENAME_TS]; }, - getScriptVersion: function (filename) { return _this.files[filename] && _this.files[filename].version.toString(); }, - getScriptSnapshot: function (filename) { - var file = _this.files[filename]; - return { - getText: function (start, end) { return file.text.substring(start, end); }, - getLength: function () { return file.text.length; }, - getLineStartPositions: function () { return []; }, - getChangeRange: function (oldSnapshot) { return undefined; } - }; - }, - getCurrentDirectory: function () { return process.cwd(); }, - getScriptIsOpen: function () { return true; }, - getCompilationSettings: function () { return _this.options; }, - getDefaultLibFilename: function (options) { - return _this.getDefaultLibFilename(options); - }, - log: function (message) { return console.log(message); } - }; - return ts.createLanguageService(servicesHost, ts.createDocumentRegistry()); - }; - TypeScriptSimple.prototype.getTypeScriptBinDir = function () { - return path.dirname(require.resolve('typescript')); - }; - TypeScriptSimple.prototype.getDefaultLibFilename = function (options) { - if (options.target === ts.ScriptTarget.ES6) { - return 'lib.es6.d.ts'; - } - else { - return 'lib.d.ts'; - } - }; - /** - * converts {"version":3,"file":"file.js","sourceRoot":"","sources":["file.ts"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG,MAAM,CAAC"} - * to {"version":3,"sources":["foo/test.ts"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG,MAAM,CAAC","file":"foo/test.ts","sourcesContent":["var x = 'test';"]} - * derived from : https://github.com/thlorenz/convert-source-map - */ - TypeScriptSimple.prototype.getInlineSourceMap = function (mapText, filename) { - var sourceMap = JSON.parse(mapText); - sourceMap.file = filename; - sourceMap.sources = [filename]; - sourceMap.sourcesContent = [this.files[FILENAME_TS].text]; - delete sourceMap.sourceRoot; - return JSON.stringify(sourceMap); - }; - TypeScriptSimple.prototype.toJavaScript = function (service, filename) { - if (filename === void 0) { filename = FILENAME_TS; } - var output = service.getEmitOutput(FILENAME_TS); - // Meaning of succeeded is driven by whether we need to check for semantic errors or not - var succeeded = output.emitOutputStatus === ts.EmitReturnStatus.Succeeded; - if (!this.doSemanticChecks) { - // We have an output. It implies syntactic success - if (!succeeded) - succeeded = !!output.outputFiles.length; - } - if (succeeded) { - var outputFilename = FILENAME_TS.replace(/ts$/, 'js'); - var file = output.outputFiles.filter(function (file) { return file.name === outputFilename; })[0]; - // Fixed in v1.5 https://github.com/Microsoft/TypeScript/issues/1653 - var text = file.text.replace(/\r\n/g, os.EOL); - // If we have sourceMaps convert them to inline sourceMaps - if (this.options.sourceMap) { - var sourceMapFilename = FILENAME_TS.replace(/ts$/, 'js.map'); - var sourceMapFile = output.outputFiles.filter(function (file) { return file.name === sourceMapFilename; })[0]; - // Transform sourcemap - var sourceMapText = sourceMapFile.text; - sourceMapText = this.getInlineSourceMap(sourceMapText, filename); - var base64SourceMapText = new Buffer(sourceMapText).toString('base64'); - text = text.replace('//# sourceMappingURL=' + sourceMapFilename, '//# sourceMappingURL=data:application/json;base64,' + base64SourceMapText); - } - return text; - } - var allDiagnostics = service.getCompilerOptionsDiagnostics().concat(service.getSyntacticDiagnostics(FILENAME_TS)); - if (this.doSemanticChecks) - allDiagnostics = allDiagnostics.concat(service.getSemanticDiagnostics(FILENAME_TS)); - throw new Error(this.formatDiagnostics(allDiagnostics)); - }; - TypeScriptSimple.prototype.formatDiagnostics = function (diagnostics) { - return diagnostics.map(function (d) { - if (d.file) { - return 'L' + d.file.getLineAndCharacterFromPosition(d.start).line + ': ' + d.messageText; - } - else { - return d.messageText; - } - }).join(os.EOL); - }; - return TypeScriptSimple; - })(); - tss.TypeScriptSimple = TypeScriptSimple; -})(tss || (tss = {})); -var defaultTss = new tss.TypeScriptSimple(); -module.exports = tss; diff --git a/src/typescript.coffee b/src/typescript.coffee index e336eceff..3814a6c66 100644 --- a/src/typescript.coffee +++ b/src/typescript.coffee @@ -18,43 +18,6 @@ defaultOptions = module: 'commonjs' sourceMap: true -### -shasum - Hash with an update() method. -value - Must be a value that could be returned by JSON.parse(). -### -updateDigestForJsonValue = (shasum, value) -> - # Implmentation is similar to that of pretty-printing a JSON object, except: - # * Strings are not escaped. - # * No effort is made to avoid trailing commas. - # These shortcuts should not affect the correctness of this function. - type = typeof value - if type is 'string' - shasum.update('"', 'utf8') - shasum.update(value, 'utf8') - shasum.update('"', 'utf8') - else if type in ['boolean', 'number'] - shasum.update(value.toString(), 'utf8') - else if value is null - shasum.update('null', 'utf8') - else if Array.isArray value - shasum.update('[', 'utf8') - for item in value - updateDigestForJsonValue(shasum, item) - shasum.update(',', 'utf8') - shasum.update(']', 'utf8') - else - # value must be an object: be sure to sort the keys. - keys = Object.keys value - keys.sort() - - shasum.update('{', 'utf8') - for key in keys - updateDigestForJsonValue(shasum, key) - shasum.update(': ', 'utf8') - updateDigestForJsonValue(shasum, value[key]) - shasum.update(',', 'utf8') - shasum.update('}', 'utf8') - createTypeScriptVersionAndOptionsDigest = (version, options) -> shasum = crypto.createHash('sha1') # Include the version of typescript in the hash. @@ -62,7 +25,7 @@ createTypeScriptVersionAndOptionsDigest = (version, options) -> shasum.update('\0', 'utf8') shasum.update(version, 'utf8') shasum.update('\0', 'utf8') - updateDigestForJsonValue(shasum, options) + shasum.update(JSON.stringify(options)) shasum.digest('hex') cacheDir = null @@ -72,8 +35,8 @@ getCachePath = (sourceCode) -> digest = crypto.createHash('sha1').update(sourceCode, 'utf8').digest('hex') unless jsCacheDir? - tsVersion = require('typescript/package.json').version - jsCacheDir = path.join(cacheDir, createTypeScriptVersionAndOptionsDigest(tsVersion, defaultOptions)) + tssVersion = require('typescript-simple/package.json').version + jsCacheDir = path.join(cacheDir, createTypeScriptVersionAndOptionsDigest(tssVersion, defaultOptions)) path.join(jsCacheDir, "#{digest}.js") @@ -94,7 +57,7 @@ createOptions = (filePath) -> transpile = (sourceCode, filePath, cachePath) -> options = createOptions(filePath) - tss ?= new (require './typescript-transpile').TypeScriptSimple(options, false) + tss ?= new (require 'typescript-simple').TypeScriptSimple(options, false) js = tss.compile(sourceCode, filePath) stats.misses++ From 030dd4c690d339fc938f20b66bee459c2f7c2c2b Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Thu, 19 Mar 2015 09:17:35 +0800 Subject: [PATCH 04/36] :arrow_up: atom-shell@0.22.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fd906dd53..11bc09810 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "url": "http://github.com/atom/atom/raw/master/LICENSE.md" } ], - "atomShellVersion": "0.21.3", + "atomShellVersion": "0.22.1", "dependencies": { "async": "0.2.6", "atom-keymap": "^4", From bc00ad3f01b34fa910fb25189daa0d04e7c4737f Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Thu, 19 Mar 2015 09:17:54 +0800 Subject: [PATCH 05/36] :arrow_up: apm@0.146.0 --- apm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apm/package.json b/apm/package.json index a989c7a83..6b3001029 100644 --- a/apm/package.json +++ b/apm/package.json @@ -6,6 +6,6 @@ "url": "https://github.com/atom/atom.git" }, "dependencies": { - "atom-package-manager": "0.145.0" + "atom-package-manager": "0.146.0" } } From 679840cada23adec621b25a085c751ab719ef5d9 Mon Sep 17 00:00:00 2001 From: Basarat Syed Date: Fri, 20 Mar 2015 10:58:52 +1100 Subject: [PATCH 06/36] chore: remove unneeded dependency --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 9f12fe358..256df223a 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,6 @@ "temp": "0.8.1", "text-buffer": "^4.1.5", "theorist": "^1.0.2", - "typescript": "^1.4.1", "typescript-simple": "^1.0.0", "underscore-plus": "^1.6.6" }, From a4003f31da2bfae13c54ec7d2fedcc7995c061fe Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 23 Mar 2015 18:03:43 +0800 Subject: [PATCH 07/36] :arrow_up: atom-shell@0.22.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 11bc09810..a3078ddc0 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "url": "http://github.com/atom/atom/raw/master/LICENSE.md" } ], - "atomShellVersion": "0.22.1", + "atomShellVersion": "0.22.2", "dependencies": { "async": "0.2.6", "atom-keymap": "^4", From eb6e15790a26f8e68f3073d735510b76bd7e6a34 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 23 Mar 2015 18:24:42 +0800 Subject: [PATCH 08/36] :arrow_up: apm@0.148.0 --- apm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apm/package.json b/apm/package.json index 374a7d98a..ccfa1995e 100644 --- a/apm/package.json +++ b/apm/package.json @@ -6,6 +6,6 @@ "url": "https://github.com/atom/atom.git" }, "dependencies": { - "atom-package-manager": "0.147.0" + "atom-package-manager": "0.148.0" } } From 2620c95d86dc98aae2f5730a193f23c7ff46f803 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Tue, 24 Mar 2015 12:06:03 +0800 Subject: [PATCH 09/36] String.contains is not a standard method in Chrome 41 --- spec/default-directory-provider-spec.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/default-directory-provider-spec.coffee b/spec/default-directory-provider-spec.coffee index 780f2afd5..236283d27 100644 --- a/spec/default-directory-provider-spec.coffee +++ b/spec/default-directory-provider-spec.coffee @@ -15,8 +15,8 @@ describe "DefaultDirectoryProvider", -> provider = new DefaultDirectoryProvider() tmp = temp.mkdirSync() nonNormalizedPath = tmp + path.sep + ".." + path.sep + path.basename(tmp) - expect(tmp.contains("..")).toBe false - expect(nonNormalizedPath.contains("..")).toBe true + expect(tmp.includes("..")).toBe false + expect(nonNormalizedPath.includes("..")).toBe true directory = provider.directoryForURISync(nonNormalizedPath) expect(directory.getPath()).toEqual tmp From 6f1b061dac48210f46335d28769fa03dc103ffa0 Mon Sep 17 00:00:00 2001 From: Basarat Syed Date: Tue, 24 Mar 2015 18:05:30 +1100 Subject: [PATCH 10/36] Added TypeScript to the compile-cache --- spec/compile-cache-spec.coffee | 13 ++++++++++++- src/compile-cache.coffee | 4 ++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/spec/compile-cache-spec.coffee b/spec/compile-cache-spec.coffee index 534457157..6eb6556d0 100644 --- a/spec/compile-cache-spec.coffee +++ b/spec/compile-cache-spec.coffee @@ -3,26 +3,37 @@ CSON = require 'season' CoffeeCache = require 'coffee-cash' babel = require '../src/babel' +typescript = require '../src/typescript' CompileCache = require '../src/compile-cache' describe "Compile Cache", -> describe ".addPathToCache(filePath)", -> - it "adds the path to the correct CSON, CoffeeScript, or babel cache", -> + it "adds the path to the correct CSON, CoffeeScript, babel or typescript cache", -> spyOn(CSON, 'readFileSync').andCallThrough() spyOn(CoffeeCache, 'addPathToCache').andCallThrough() spyOn(babel, 'addPathToCache').andCallThrough() + spyOn(typescript, 'addPathToCache').andCallThrough() CompileCache.addPathToCache(path.join(__dirname, 'fixtures', 'cson.cson')) expect(CSON.readFileSync.callCount).toBe 1 expect(CoffeeCache.addPathToCache.callCount).toBe 0 expect(babel.addPathToCache.callCount).toBe 0 + expect(typescript.addPathToCache.callCount).toBe 0 CompileCache.addPathToCache(path.join(__dirname, 'fixtures', 'coffee.coffee')) expect(CSON.readFileSync.callCount).toBe 1 expect(CoffeeCache.addPathToCache.callCount).toBe 1 expect(babel.addPathToCache.callCount).toBe 0 + expect(typescript.addPathToCache.callCount).toBe 0 CompileCache.addPathToCache(path.join(__dirname, 'fixtures', 'babel', 'babel-double-quotes.js')) expect(CSON.readFileSync.callCount).toBe 1 expect(CoffeeCache.addPathToCache.callCount).toBe 1 expect(babel.addPathToCache.callCount).toBe 1 + expect(typescript.addPathToCache.callCount).toBe 0 + + CompileCache.addPathToCache(path.join(__dirname, 'fixtures', 'typescript', 'valid.ts')) + expect(CSON.readFileSync.callCount).toBe 1 + expect(CoffeeCache.addPathToCache.callCount).toBe 1 + expect(babel.addPathToCache.callCount).toBe 1 + expect(typescript.addPathToCache.callCount).toBe 1 diff --git a/src/compile-cache.coffee b/src/compile-cache.coffee index c31f5bdd1..8fe8d6711 100644 --- a/src/compile-cache.coffee +++ b/src/compile-cache.coffee @@ -2,6 +2,7 @@ path = require 'path' CSON = require 'season' CoffeeCache = require 'coffee-cash' babel = require './babel' +typescript = require './typescript' # This file is required directly by apm so that files can be cached during # package install so that the first package load in Atom doesn't have to @@ -16,6 +17,7 @@ exports.addPathToCache = (filePath, atomHome) -> CoffeeCache.setCacheDirectory(path.join(cacheDir, 'coffee')) CSON.setCacheDir(path.join(cacheDir, 'cson')) babel.setCacheDirectory(path.join(cacheDir, 'js', 'babel')) + typescript.setCacheDirectory(path.join(cacheDir, 'ts')) switch path.extname(filePath) when '.coffee' @@ -24,3 +26,5 @@ exports.addPathToCache = (filePath, atomHome) -> CSON.readFileSync(filePath) when '.js' babel.addPathToCache(filePath) + when '.ts' + typescript.addPathToCache(filePath) From 3e90553180508755037ab74f4a81c3ea7892f1a1 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 25 Mar 2015 09:49:54 +0800 Subject: [PATCH 11/36] :arrow_up: apm@0.150.0 --- apm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apm/package.json b/apm/package.json index 544292634..7e4037d74 100644 --- a/apm/package.json +++ b/apm/package.json @@ -6,6 +6,6 @@ "url": "https://github.com/atom/atom.git" }, "dependencies": { - "atom-package-manager": "0.149.0" + "atom-package-manager": "0.150.0" } } From 2421125479e9946e429cd66bf069a966bf9ffdcf Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 25 Mar 2015 16:48:38 -0700 Subject: [PATCH 12/36] :arrow_up: welcome@0.26 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ee66a9b42..719e337d1 100644 --- a/package.json +++ b/package.json @@ -119,7 +119,7 @@ "timecop": "0.31.0", "tree-view": "0.168.0", "update-package-dependencies": "0.9.0", - "welcome": "0.25.0", + "welcome": "0.26.0", "whitespace": "0.29.0", "wrap-guide": "0.31.0", "language-c": "0.41.0", From 13f2d9d48149656442e05e217191d98fd6c023cf Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Wed, 25 Mar 2015 17:54:42 -0700 Subject: [PATCH 13/36] :arrow_up: text-buffer@5.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 719e337d1..47a9a7c29 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "space-pen": "3.8.2", "stacktrace-parser": "0.1.1", "temp": "0.8.1", - "text-buffer": "^5.0.2", + "text-buffer": "^5.1.0", "theorist": "^1.0.2", "underscore-plus": "^1.6.6" }, From c0d4103fef936211236be01dbc1f2849ff39f46c Mon Sep 17 00:00:00 2001 From: "Machiste N. Quintana" Date: Thu, 26 Mar 2015 10:50:12 -0400 Subject: [PATCH 14/36] Update set-version-task.coffee Wooo 2015 --- build/tasks/set-version-task.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/tasks/set-version-task.coffee b/build/tasks/set-version-task.coffee index 6f7a68c61..48b6091c4 100644 --- a/build/tasks/set-version-task.coffee +++ b/build/tasks/set-version-task.coffee @@ -46,7 +46,7 @@ module.exports = (grunt) -> strings = CompanyName: 'GitHub, Inc.' FileDescription: 'Atom' - LegalCopyright: 'Copyright (C) 2014 GitHub, Inc. All rights reserved' + LegalCopyright: 'Copyright (C) 2015 GitHub, Inc. All rights reserved' ProductName: 'Atom' ProductVersion: version From 05c53f378e08a24832a6244a166760fb8da5f6a1 Mon Sep 17 00:00:00 2001 From: "Machiste N. Quintana" Date: Thu, 26 Mar 2015 10:52:57 -0400 Subject: [PATCH 15/36] Update LICENSE.md :memo: Update copyright --- LICENSE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.md b/LICENSE.md index 4d231b456..bbb875dc2 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright (c) 2014 GitHub Inc. +Copyright (c) 2015 GitHub Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the From 307e2ad5655139819cd88ac8db64f7ee00c28f3b Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 26 Mar 2015 09:04:20 -0700 Subject: [PATCH 16/36] :arrow_up: language-sql@0.15 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 47a9a7c29..c67459112 100644 --- a/package.json +++ b/package.json @@ -148,7 +148,7 @@ "language-sass": "0.36.0", "language-shellscript": "0.13.0", "language-source": "0.9.0", - "language-sql": "0.14.0", + "language-sql": "0.15.0", "language-text": "0.6.0", "language-todo": "0.17.0", "language-toml": "0.15.0", From 06da4153e8cb725061f5a9d757d269c9b423f92d Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 26 Mar 2015 10:09:15 -0700 Subject: [PATCH 17/36] Build on fedora 21 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 76fa18eae..be5624b21 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ # DESCRIPTION: Image to build Atom and create a .rpm file # Base docker image -FROM fedora:20 +FROM fedora:21 # Install dependencies RUN yum install -y \ From dafa40fd4dba56282f82920e7e240711c40fd444 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 26 Mar 2015 10:13:49 -0700 Subject: [PATCH 18/36] Try using canonical nodejs npm packages --- Dockerfile | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index be5624b21..c8eca8804 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,11 +12,9 @@ RUN yum install -y \ glibc-devel \ git-core \ libgnome-keyring-devel \ - rpmdevtools - -# Install node -RUN curl -sL https://rpm.nodesource.com/setup | bash - -RUN yum install -y nodejs + rpmdevtools \ + nodejs \ + npm ADD . /atom WORKDIR /atom From ad87ed5c40ef6f9a839575d66179db26a972e559 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 26 Mar 2015 10:23:56 -0700 Subject: [PATCH 19/36] Log detected version --- script/utils/verify-requirements.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/utils/verify-requirements.js b/script/utils/verify-requirements.js index 554c27dd0..693af4a2b 100644 --- a/script/utils/verify-requirements.js +++ b/script/utils/verify-requirements.js @@ -57,7 +57,7 @@ function verifyNpm(cb) { var npmMajorVersion = +versionArray[0] || 0; var npmMinorVersion = +versionArray[1] || 0; if (npmMajorVersion === 1 && npmMinorVersion < 4) - cb("npm v1.4+ is required to build Atom."); + cb("npm v1.4+ is required to build Atom. Version " + npmVersion + " was detected"); else cb(null, "npm: v" + npmVersion); }); From a08c939699246472d55c0c30fd13f6baef61f1d5 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 26 Mar 2015 10:25:05 -0700 Subject: [PATCH 20/36] Disable npm version check on CI --- script/utils/verify-requirements.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/utils/verify-requirements.js b/script/utils/verify-requirements.js index 693af4a2b..36bf85b21 100644 --- a/script/utils/verify-requirements.js +++ b/script/utils/verify-requirements.js @@ -56,7 +56,7 @@ function verifyNpm(cb) { var versionArray = npmVersion.split('.'); var npmMajorVersion = +versionArray[0] || 0; var npmMinorVersion = +versionArray[1] || 0; - if (npmMajorVersion === 1 && npmMinorVersion < 4) + if (npmMajorVersion === 1 && npmMinorVersion < 4 && !process.env.JANKY_SHA1) cb("npm v1.4+ is required to build Atom. Version " + npmVersion + " was detected"); else cb(null, "npm: v" + npmVersion); From 9bc723f4907dfb16527e9a792753e7fe8609f312 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 26 Mar 2015 10:27:34 -0700 Subject: [PATCH 21/36] Upgrade to npm 1.4 on Fedora --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index c8eca8804..31726e401 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,5 +16,7 @@ RUN yum install -y \ nodejs \ npm +RUN npm install -g npm@1.4.28 + ADD . /atom WORKDIR /atom From 67843c8ebd19f537266e44cfc6af4a923317ebbf Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 26 Mar 2015 10:33:21 -0700 Subject: [PATCH 22/36] Remove Janky bypass of npm version check --- script/utils/verify-requirements.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/utils/verify-requirements.js b/script/utils/verify-requirements.js index 36bf85b21..890172753 100644 --- a/script/utils/verify-requirements.js +++ b/script/utils/verify-requirements.js @@ -56,8 +56,8 @@ function verifyNpm(cb) { var versionArray = npmVersion.split('.'); var npmMajorVersion = +versionArray[0] || 0; var npmMinorVersion = +versionArray[1] || 0; - if (npmMajorVersion === 1 && npmMinorVersion < 4 && !process.env.JANKY_SHA1) - cb("npm v1.4+ is required to build Atom. Version " + npmVersion + " was detected"); + if (npmMajorVersion === 1 && npmMinorVersion < 4) + cb("npm v1.4+ is required to build Atom. Version " + npmVersion + " was detected."); else cb(null, "npm: v" + npmVersion); }); From 871c32b75dc5aa9cd10610db2e8d1e9995209af7 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 26 Mar 2015 10:38:58 -0700 Subject: [PATCH 23/36] Only log install errors --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 31726e401..d792c30c5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,7 @@ RUN yum install -y \ nodejs \ npm -RUN npm install -g npm@1.4.28 +RUN npm install -g npm@1.4.28 --loglevel error ADD . /atom WORKDIR /atom From 2f5d97533802ea5f348e2d684d46fe60bf319538 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 26 Mar 2015 11:12:37 -0700 Subject: [PATCH 24/36] Remove autoscroll-related legacy editor view support Signed-off-by: Nathan Sobo --- spec/display-buffer-spec.coffee | 10 ++-------- spec/text-editor-spec.coffee | 12 +----------- src/cursor.coffee | 13 ++----------- src/display-buffer.coffee | 14 +++----------- src/selection.coffee | 7 +------ src/text-editor-component.coffee | 1 - src/text-editor.coffee | 8 +++----- 7 files changed, 12 insertions(+), 53 deletions(-) diff --git a/spec/display-buffer-spec.coffee b/spec/display-buffer-spec.coffee index 95f22acca..386cbf062 100644 --- a/spec/display-buffer-spec.coffee +++ b/spec/display-buffer-spec.coffee @@ -48,7 +48,6 @@ describe "DisplayBuffer", -> expect(displayBuffer.getLineCount()).toBe 100 + originalLineCount it "reassigns the scrollTop if it exceeds the max possible value after lines are removed", -> - displayBuffer.manageScrollPosition = true displayBuffer.setHeight(50) displayBuffer.setLineHeightInPixels(10) displayBuffer.setScrollTop(80) @@ -287,7 +286,6 @@ describe "DisplayBuffer", -> it "sets ::scrollLeft to 0 and keeps it there when soft wrapping is enabled", -> displayBuffer.setDefaultCharWidth(10) displayBuffer.setWidth(85) - displayBuffer.manageScrollPosition = true displayBuffer.setSoftWrapped(false) displayBuffer.setScrollLeft(Infinity) @@ -1174,7 +1172,6 @@ describe "DisplayBuffer", -> describe "::setScrollTop", -> beforeEach -> - displayBuffer.manageScrollPosition = true displayBuffer.setLineHeightInPixels(10) it "disallows negative values", -> @@ -1196,7 +1193,6 @@ describe "DisplayBuffer", -> describe "when editor.scrollPastEnd is false", -> beforeEach -> atom.config.set("editor.scrollPastEnd", false) - displayBuffer.manageScrollPosition = true displayBuffer.setLineHeightInPixels(10) it "does not add the height of the view to the scroll height", -> @@ -1209,7 +1205,6 @@ describe "DisplayBuffer", -> describe "when editor.scrollPastEnd is true", -> beforeEach -> atom.config.set("editor.scrollPastEnd", true) - displayBuffer.manageScrollPosition = true displayBuffer.setLineHeightInPixels(10) it "adds the height of the view to the scroll height", -> @@ -1221,7 +1216,6 @@ describe "DisplayBuffer", -> describe "::setScrollLeft", -> beforeEach -> - displayBuffer.manageScrollPosition = true displayBuffer.setLineHeightInPixels(10) displayBuffer.setDefaultCharWidth(10) @@ -1242,7 +1236,6 @@ describe "DisplayBuffer", -> describe "::scrollToScreenPosition(position, [options])", -> beforeEach -> - displayBuffer.manageScrollPosition = true displayBuffer.setLineHeightInPixels(10) displayBuffer.setDefaultCharWidth(10) displayBuffer.setHorizontalScrollbarHeight(0) @@ -1320,6 +1313,7 @@ describe "DisplayBuffer", -> expect(displayBuffer.getVisibleRowRange()).toEqual [0, 0] it "ends at last buffer row even if there's more space available", -> + displayBuffer.setHeight(150) displayBuffer.setScrollTop(60) - expect(displayBuffer.getVisibleRowRange()).toEqual [6, 13] + expect(displayBuffer.getVisibleRowRange()).toEqual [0, 13] diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index cddb8bdf6..bd412ca98 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -921,7 +921,6 @@ describe "TextEditor", -> describe "autoscroll", -> beforeEach -> - editor.manageScrollPosition = true editor.setVerticalScrollMargin(2) editor.setHorizontalScrollMargin(2) editor.setLineHeightInPixels(10) @@ -1254,7 +1253,6 @@ describe "TextEditor", -> expect(editor.getSelectedBufferRange()).toEqual [[0,0], [2,0]] it "autoscrolls to the selection", -> - editor.manageScrollPosition = true editor.setLineHeightInPixels(10) editor.setDefaultCharWidth(10) editor.setHeight(50) @@ -1523,10 +1521,9 @@ describe "TextEditor", -> describe ".setSelectedBufferRange(range)", -> describe "when the 'autoscroll' option is true", -> it "autoscrolls to the selection", -> - editor.manageScrollPosition = true editor.setLineHeightInPixels(10) editor.setDefaultCharWidth(10) - editor.setHeight(50) + editor.setHeight(70) editor.setWidth(50) editor.setHorizontalScrollbarHeight(0) @@ -1560,8 +1557,6 @@ describe "TextEditor", -> expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 0]], [[3, 4], [5, 6]]] it "autoscrolls to the added selection if needed", -> - editor.manageScrollPosition = true - editor.setLineHeightInPixels(10) editor.setDefaultCharWidth(10) editor.setHeight(50) @@ -1922,7 +1917,6 @@ describe "TextEditor", -> expect(cursor2.getBufferPosition()).toEqual [2, 7] it "autoscrolls to the last cursor", -> - editor.manageScrollPosition = true editor.setCursorScreenPosition([1, 2]) editor.addCursorAtScreenPosition([10, 4]) editor.setLineHeightInPixels(10) @@ -4072,8 +4066,6 @@ describe "TextEditor", -> describe ".pageUp/Down()", -> it "scrolls one screen height up or down and moves the cursor one page length", -> - editor.manageScrollPosition = true - editor.setLineHeightInPixels(10) editor.setHeight(50) expect(editor.getScrollHeight()).toBe 130 @@ -4097,8 +4089,6 @@ describe "TextEditor", -> describe ".selectPageUp/Down()", -> it "selects one screen height of text up or down", -> - editor.manageScrollPosition = true - editor.setLineHeightInPixels(10) editor.setHeight(50) expect(editor.getScrollHeight()).toBe 130 diff --git a/src/cursor.coffee b/src/cursor.coffee index 33f50ca9c..2a0665485 100644 --- a/src/cursor.coffee +++ b/src/cursor.coffee @@ -15,7 +15,6 @@ class Cursor extends Model bufferPosition: null goalColumn: null visible: true - needsAutoscroll: null # Instantiated by a {TextEditor} constructor: ({@editor, @marker, id}) -> @@ -30,9 +29,7 @@ class Cursor extends Model {textChanged} = e return if oldHeadScreenPosition.isEqual(newHeadScreenPosition) - # Supports old editor view - @needsAutoscroll ?= @isLastCursor() and !textChanged - @autoscroll() if @editor.manageScrollPosition and @isLastCursor() and textChanged + @autoscroll() if @isLastCursor() and textChanged @goalColumn = null @@ -53,7 +50,6 @@ class Cursor extends Model @emit 'destroyed' @emitter.emit 'did-destroy' @emitter.dispose() - @needsAutoscroll = true destroy: -> @marker.destroy() @@ -600,7 +596,6 @@ class Cursor extends Model setVisible: (visible) -> if @visible != visible @visible = visible - @needsAutoscroll ?= true if @visible and @isLastCursor() @emit 'visibility-changed', @visible @emitter.emit 'did-change-visibility', @visible @@ -628,7 +623,6 @@ class Cursor extends Model # Public: Prevents this cursor from causing scrolling. clearAutoscroll: -> - @needsAutoscroll = null # Public: Deselects the current selection. clearSelection: -> @@ -656,11 +650,8 @@ class Cursor extends Model changePosition: (options, fn) -> @clearSelection() - @needsAutoscroll = options.autoscroll ? @isLastCursor() fn() - if @needsAutoscroll - @emit 'autoscrolled' # Support legacy editor - @autoscroll() if @needsAutoscroll and @editor.manageScrollPosition # Support react editor view + @autoscroll() if options.autoscroll ? @isLastCursor() getPixelRect: -> @editor.pixelRectForScreenRange(@getScreenRange()) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 4ab65d0f7..059d04909 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -22,7 +22,6 @@ class DisplayBuffer extends Model Serializable.includeInto(this) @properties - manageScrollPosition: false softWrapped: null editorWidthInChars: null lineHeightInPixels: null @@ -268,10 +267,7 @@ class DisplayBuffer extends Model getScrollTop: -> @scrollTop setScrollTop: (scrollTop) -> - if @manageScrollPosition - @scrollTop = Math.round(Math.max(0, Math.min(@getMaxScrollTop(), scrollTop))) - else - @scrollTop = Math.round(scrollTop) + @scrollTop = Math.round(Math.max(0, Math.min(@getMaxScrollTop(), scrollTop))) getMaxScrollTop: -> @getScrollHeight() - @getClientHeight() @@ -283,11 +279,7 @@ class DisplayBuffer extends Model getScrollLeft: -> @scrollLeft setScrollLeft: (scrollLeft) -> - if @manageScrollPosition - @scrollLeft = Math.round(Math.max(0, Math.min(@getScrollWidth() - @getClientWidth(), scrollLeft))) - @scrollLeft - else - @scrollLeft = Math.round(scrollLeft) + @scrollLeft = Math.round(Math.max(0, Math.min(@getScrollWidth() - @getClientWidth(), scrollLeft))) getMaxScrollLeft: -> @getScrollWidth() - @getClientWidth() @@ -1113,7 +1105,7 @@ class DisplayBuffer extends Model handleTokenizedBufferChange: (tokenizedBufferChange) => {start, end, delta, bufferChange} = tokenizedBufferChange @updateScreenLines(start, end + 1, delta, delayChangeEvent: bufferChange?) - @setScrollTop(Math.min(@getScrollTop(), @getMaxScrollTop())) if @manageScrollPosition and delta < 0 + @setScrollTop(Math.min(@getScrollTop(), @getMaxScrollTop())) if delta < 0 updateScreenLines: (startBufferRow, endBufferRow, bufferDelta=0, options={}) -> startBufferRow = @rowMap.bufferRowRangeForBufferRow(startBufferRow)[0] diff --git a/src/selection.coffee b/src/selection.coffee index 375498c41..524f1bd9b 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -14,7 +14,6 @@ class Selection extends Model editor: null initialScreenRange: null wordwise: false - needsAutoscroll: null constructor: ({@cursor, @marker, @editor, id}) -> @emitter = new Emitter @@ -100,15 +99,13 @@ class Selection extends Model # * `autoscroll` if `true`, the {TextEditor} scrolls to the new selection. setBufferRange: (bufferRange, options={}) -> bufferRange = Range.fromObject(bufferRange) - @needsAutoscroll = options.autoscroll options.reversed ?= @isReversed() @editor.destroyFoldsContainingBufferRange(bufferRange) unless options.preserveFolds @modifySelection => needsFlash = options.flash delete options.flash if options.flash? - @cursor.needsAutoscroll = false if @needsAutoscroll? @marker.setBufferRange(bufferRange, options) - @autoscroll() if @needsAutoscroll and @editor.manageScrollPosition + @autoscroll() if options.autoscroll @decoration.flash('flash', @editor.selectionFlashDuration) if needsFlash # Public: Returns the starting and ending buffer rows the selection is @@ -359,7 +356,6 @@ class Selection extends Model @editor.unfoldBufferRow(oldBufferRange.end.row) wasReversed = @isReversed() @clear() - @cursor.needsAutoscroll = @cursor.isLastCursor() autoIndentFirstLine = false precedingText = @editor.getTextInRange([[oldBufferRange.start.row, 0], oldBufferRange.start]) @@ -758,7 +754,6 @@ class Selection extends Model @editor.scrollToScreenRange(@getScreenRange()) clearAutoscroll: -> - @needsAutoscroll = null modifySelection: (fn) -> @retainSelection = true diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index 75a9a5136..63a7f9a1c 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -39,7 +39,6 @@ class TextEditorComponent @lineOverdrawMargin = lineOverdrawMargin if lineOverdrawMargin? @disposables = new CompositeDisposable - @editor.manageScrollPosition = true @observeConfig() @setScrollSensitivity(atom.config.get('editor.scrollSensitivity')) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 6ccb97392..114df486a 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -76,7 +76,7 @@ class TextEditor extends Model @delegatesProperties '$lineHeightInPixels', '$defaultCharWidth', '$height', '$width', '$verticalScrollbarWidth', '$horizontalScrollbarHeight', '$scrollTop', '$scrollLeft', - 'manageScrollPosition', toProperty: 'displayBuffer' + toProperty: 'displayBuffer' constructor: ({@softTabs, initialLine, initialColumn, tabLength, softWrapped, @displayBuffer, buffer, registerEditor, suppressCursorCreation, @mini, @placeholderText, @gutterVisible}) -> super @@ -1132,12 +1132,10 @@ class TextEditor extends Model # Essential: Undo the last change. undo: -> - @getLastCursor().needsAutoscroll = true @buffer.undo(this) # Essential: Redo the last change. redo: -> - @getLastCursor().needsAutoscroll = true @buffer.redo(this) # Extended: Batch multiple operations as a single undo/redo step. @@ -1950,7 +1948,7 @@ class TextEditor extends Model addSelectionForBufferRange: (bufferRange, options={}) -> @markBufferRange(bufferRange, _.defaults(@getSelectionMarkerAttributes(), options)) selection = @getLastSelection() - selection.autoscroll() if @manageScrollPosition + selection.autoscroll() selection # Essential: Add a selection for the given range in screen coordinates. @@ -1964,7 +1962,7 @@ class TextEditor extends Model addSelectionForScreenRange: (screenRange, options={}) -> @markScreenRange(screenRange, _.defaults(@getSelectionMarkerAttributes(), options)) selection = @getLastSelection() - selection.autoscroll() if @manageScrollPosition + selection.autoscroll() selection # Essential: Select from the current cursor position to the given position in From e2f3ecf156e5c28453c88dd51c4a19e5e4d3a0fd Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 26 Mar 2015 12:18:08 -0700 Subject: [PATCH 25/36] :arrow_up: snippets@0.86 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c67459112..64ee17f6e 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "package-generator": "0.38.0", "release-notes": "0.52.0", "settings-view": "0.185.0", - "snippets": "0.85.0", + "snippets": "0.86.0", "spell-check": "0.55.0", "status-bar": "0.64.0", "styleguide": "0.44.0", From 3deb16b522e63794435a43ba5851daad14524e58 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 26 Mar 2015 12:33:32 -0700 Subject: [PATCH 26/36] Set log level to error npm install --- script/cibuild | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/cibuild b/script/cibuild index af5f789e1..c63d5dbf6 100755 --- a/script/cibuild +++ b/script/cibuild @@ -48,7 +48,7 @@ function removeNodeModules() { readEnvironmentVariables(); removeNodeModules(); -cp.safeExec.bind(global, 'npm install npm', {cwd: path.resolve(__dirname, '..', 'build')}, function() { +cp.safeExec.bind(global, 'npm install npm --loglevel error', {cwd: path.resolve(__dirname, '..', 'build')}, function() { cp.safeExec.bind(global, 'node script/bootstrap', function(error) { if (error) process.exit(1); From d9527fd8add798aaf87364d0adee6d5d3123941b Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 26 Mar 2015 12:59:38 -0700 Subject: [PATCH 27/36] :arrow_up: apm@0.151 --- apm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apm/package.json b/apm/package.json index 7e4037d74..f43d9e024 100644 --- a/apm/package.json +++ b/apm/package.json @@ -6,6 +6,6 @@ "url": "https://github.com/atom/atom.git" }, "dependencies": { - "atom-package-manager": "0.150.0" + "atom-package-manager": "0.151.0" } } From 45afc9eb463613ab2cd70bd46afdfd8adcb44188 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 26 Mar 2015 13:29:30 -0700 Subject: [PATCH 28/36] Handle EISDIR save errors Closes #6100 --- src/pane.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pane.coffee b/src/pane.coffee index a1239acb5..a6e456d6a 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -686,7 +686,7 @@ class Pane extends Model true handleSaveError: (error) -> - if error.message.endsWith('is a directory') + if error.code is 'EISDIR' or error.message.endsWith('is a directory') atom.notifications.addWarning("Unable to save file: #{error.message}") else if error.code is 'EACCES' and error.path? atom.notifications.addWarning("Unable to save file: Permission denied '#{error.path}'") From b6d0099242a5f834abbe9fe0eb9c532b031027fd Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 26 Mar 2015 13:46:08 -0700 Subject: [PATCH 29/36] :arrow_up: symbols-view@0.93 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 64ee17f6e..ebb5997bd 100644 --- a/package.json +++ b/package.json @@ -114,7 +114,7 @@ "spell-check": "0.55.0", "status-bar": "0.64.0", "styleguide": "0.44.0", - "symbols-view": "0.92.0", + "symbols-view": "0.93.0", "tabs": "0.67.0", "timecop": "0.31.0", "tree-view": "0.168.0", From 6d827f31d0b0cdc227672e32ce3b677ded8b3f0f Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 26 Mar 2015 13:55:59 -0700 Subject: [PATCH 30/36] :arrow_up: language-ruby@0.50 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ebb5997bd..97b20430a 100644 --- a/package.json +++ b/package.json @@ -143,7 +143,7 @@ "language-php": "0.21.0", "language-property-list": "0.8.0", "language-python": "0.32.0", - "language-ruby": "0.49.0", + "language-ruby": "0.50.0", "language-ruby-on-rails": "0.21.0", "language-sass": "0.36.0", "language-shellscript": "0.13.0", From 10458a5b454b09a54ac9133434ece18beeb13739 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 26 Mar 2015 15:30:53 -0600 Subject: [PATCH 31/36] Always autoscroll when the range of the last selection changes Signed-off-by: Max Brunsfeld --- spec/display-buffer-spec.coffee | 6 ++- spec/text-editor-presenter-spec.coffee | 4 +- spec/text-editor-spec.coffee | 46 ++++++++++++--------- src/cursor.coffee | 4 +- src/display-buffer.coffee | 56 ++++++++++++++++++-------- src/selection.coffee | 8 +++- src/text-editor.coffee | 11 +++-- 7 files changed, 85 insertions(+), 50 deletions(-) diff --git a/spec/display-buffer-spec.coffee b/spec/display-buffer-spec.coffee index 386cbf062..2893fd5a4 100644 --- a/spec/display-buffer-spec.coffee +++ b/spec/display-buffer-spec.coffee @@ -1240,13 +1240,17 @@ describe "DisplayBuffer", -> displayBuffer.setDefaultCharWidth(10) displayBuffer.setHorizontalScrollbarHeight(0) displayBuffer.setHeight(50) - displayBuffer.setWidth(50) + displayBuffer.setWidth(150) it "sets the scroll top and scroll left so the given screen position is in view", -> displayBuffer.scrollToScreenPosition([8, 20]) expect(displayBuffer.getScrollBottom()).toBe (9 + displayBuffer.getVerticalScrollMargin()) * 10 expect(displayBuffer.getScrollRight()).toBe (20 + displayBuffer.getHorizontalScrollMargin()) * 10 + displayBuffer.scrollToScreenPosition([8, 20]) + expect(displayBuffer.getScrollBottom()).toBe (9 + displayBuffer.getVerticalScrollMargin()) * 10 + expect(displayBuffer.getScrollRight()).toBe (20 + displayBuffer.getHorizontalScrollMargin()) * 10 + describe "when the 'center' option is true", -> it "vertically scrolls to center the given position vertically", -> displayBuffer.scrollToScreenPosition([8, 20], center: true) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 4ad319cc1..0cf0c7e53 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -351,11 +351,11 @@ describe "TextEditorPresenter", -> expectValues presenter.getState().hiddenInput, {top: 0, left: 0} expectStateUpdate presenter, -> editor.setCursorBufferPosition([11, 43]) - expectValues presenter.getState().hiddenInput, {top: 50 - 10, left: 300 - 10} + expectValues presenter.getState().hiddenInput, {top: 11 * 10 - editor.getScrollTop(), left: 43 * 10 - editor.getScrollLeft()} newCursor = null expectStateUpdate presenter, -> newCursor = editor.addCursorAtBufferPosition([6, 10]) - expectValues presenter.getState().hiddenInput, {top: (6 * 10) - 40, left: (10 * 10) - 70} + expectValues presenter.getState().hiddenInput, {top: (6 * 10) - editor.getScrollTop(), left: (10 * 10) - editor.getScrollLeft()} expectStateUpdate presenter, -> newCursor.destroy() expectValues presenter.getState().hiddenInput, {top: 50 - 10, left: 300 - 10} diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index bd412ca98..afebf591d 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -1519,23 +1519,28 @@ describe "TextEditor", -> expect(selection1.getScreenRange()).toEqual [[2, 2], [3, 4]] describe ".setSelectedBufferRange(range)", -> - describe "when the 'autoscroll' option is true", -> - it "autoscrolls to the selection", -> - editor.setLineHeightInPixels(10) - editor.setDefaultCharWidth(10) - editor.setHeight(70) - editor.setWidth(50) - editor.setHorizontalScrollbarHeight(0) + it "autoscrolls the selection if it is last unless the 'autoscroll' option is false", -> + editor.setVerticalScrollMargin(2) + editor.setHorizontalScrollMargin(2) + editor.setLineHeightInPixels(10) + editor.setDefaultCharWidth(10) + editor.setHeight(70) + editor.setWidth(100) + editor.setHorizontalScrollbarHeight(0) - expect(editor.getScrollTop()).toBe 0 + expect(editor.getScrollTop()).toBe 0 - editor.setSelectedBufferRange([[5, 6], [6, 8]], autoscroll: true) - expect(editor.getScrollBottom()).toBe (7 + editor.getVerticalScrollMargin()) * 10 - expect(editor.getScrollRight()).toBe 50 + editor.setSelectedBufferRange([[5, 6], [6, 8]]) + expect(editor.getScrollBottom()).toBe (7 + editor.getVerticalScrollMargin()) * 10 + expect(editor.getScrollRight()).toBe (8 + editor.getHorizontalScrollMargin()) * 10 - editor.setSelectedBufferRange([[6, 6], [6, 8]], autoscroll: true) - expect(editor.getScrollBottom()).toBe (7 + editor.getVerticalScrollMargin()) * 10 - expect(editor.getScrollRight()).toBe (8 + editor.getHorizontalScrollMargin()) * 10 + editor.setSelectedBufferRange([[0, 0], [0, 0]]) + expect(editor.getScrollTop()).toBe 0 + expect(editor.getScrollLeft()).toBe 0 + + editor.setSelectedBufferRange([[6, 6], [6, 8]]) + expect(editor.getScrollBottom()).toBe (7 + editor.getVerticalScrollMargin()) * 10 + expect(editor.getScrollRight()).toBe (8 + editor.getHorizontalScrollMargin()) * 10 describe ".selectMarker(marker)", -> describe "if the marker is valid", -> @@ -1557,14 +1562,15 @@ describe "TextEditor", -> expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 0]], [[3, 4], [5, 6]]] it "autoscrolls to the added selection if needed", -> + editor.setVerticalScrollMargin(2) + editor.setHorizontalScrollMargin(2) editor.setLineHeightInPixels(10) editor.setDefaultCharWidth(10) - editor.setHeight(50) - editor.setWidth(50) - + editor.setHeight(80) + editor.setWidth(100) editor.addSelectionForBufferRange([[8, 10], [8, 15]]) - expect(editor.getScrollTop()).toBe 75 - expect(editor.getScrollLeft()).toBe 160 + expect(editor.getScrollBottom()).toBe (9 * 10) + (2 * 10) + expect(editor.getScrollRight()).toBe (15 * 10) + (2 * 10) describe ".addSelectionBelow()", -> describe "when the selection is non-empty", -> @@ -4050,7 +4056,7 @@ describe "TextEditor", -> editor.setLineHeightInPixels(10) editor.setDefaultCharWidth(10) editor.setHeight(60) - editor.setWidth(50) + editor.setWidth(130) editor.setHorizontalScrollbarHeight(0) expect(editor.getScrollTop()).toBe 0 expect(editor.getScrollLeft()).toBe 0 diff --git a/src/cursor.coffee b/src/cursor.coffee index 2a0665485..d0602a2f4 100644 --- a/src/cursor.coffee +++ b/src/cursor.coffee @@ -29,8 +29,6 @@ class Cursor extends Model {textChanged} = e return if oldHeadScreenPosition.isEqual(newHeadScreenPosition) - @autoscroll() if @isLastCursor() and textChanged - @goalColumn = null movedEvent = @@ -651,7 +649,7 @@ class Cursor extends Model changePosition: (options, fn) -> @clearSelection() fn() - @autoscroll() if options.autoscroll ? @isLastCursor() + @autoscroll() if options.autoscroll getPixelRect: -> @editor.pixelRectForScreenRange(@getScreenRange()) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 059d04909..b60113661 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -199,12 +199,22 @@ class DisplayBuffer extends Model # visible - A {Boolean} indicating of the tokenized buffer is shown setVisible: (visible) -> @tokenizedBuffer.setVisible(visible) - getVerticalScrollMargin: -> @verticalScrollMargin + getVerticalScrollMargin: -> Math.min(@verticalScrollMargin, (@getHeight() - @getLineHeightInPixels()) / 2) setVerticalScrollMargin: (@verticalScrollMargin) -> @verticalScrollMargin - getHorizontalScrollMargin: -> @horizontalScrollMargin + getVerticalScrollMarginInPixels: -> + scrollMarginInPixels = @getVerticalScrollMargin() * @getLineHeightInPixels() + maxScrollMarginInPixels = (@getHeight() - @getLineHeightInPixels()) / 2 + Math.min(scrollMarginInPixels, maxScrollMarginInPixels) + + getHorizontalScrollMargin: -> Math.min(@horizontalScrollMargin, (@getWidth() - @getDefaultCharWidth()) / 2) setHorizontalScrollMargin: (@horizontalScrollMargin) -> @horizontalScrollMargin + getHorizontalScrollMarginInPixels: -> + scrollMarginInPixels = @getHorizontalScrollMargin() * @getDefaultCharWidth() + maxScrollMarginInPixels = (@getWidth() - @getDefaultCharWidth()) / 2 + Math.min(scrollMarginInPixels, maxScrollMarginInPixels) + getHorizontalScrollbarHeight: -> @horizontalScrollbarHeight setHorizontalScrollbarHeight: (@horizontalScrollbarHeight) -> @horizontalScrollbarHeight @@ -272,7 +282,7 @@ class DisplayBuffer extends Model getMaxScrollTop: -> @getScrollHeight() - @getClientHeight() - getScrollBottom: -> @scrollTop + @height + getScrollBottom: -> @scrollTop + @getClientHeight() setScrollBottom: (scrollBottom) -> @setScrollTop(scrollBottom - @getClientHeight()) @getScrollBottom() @@ -364,15 +374,16 @@ class DisplayBuffer extends Model @intersectsVisibleRowRange(start.row, end.row + 1) scrollToScreenRange: (screenRange, options) -> - verticalScrollMarginInPixels = @getVerticalScrollMargin() * @getLineHeightInPixels() - horizontalScrollMarginInPixels = @getHorizontalScrollMargin() * @getDefaultCharWidth() + verticalScrollMarginInPixels = @getVerticalScrollMarginInPixels() + horizontalScrollMarginInPixels = @getHorizontalScrollMarginInPixels() - {top, left, height, width} = @pixelRectForScreenRange(screenRange) - bottom = top + height - right = left + width + {top, left} = @pixelRectForScreenRange(new Range(screenRange.start, screenRange.start)) + {top: endTop, left: endLeft, height: endHeight} = @pixelRectForScreenRange(new Range(screenRange.end, screenRange.end)) + bottom = endTop + endHeight + right = endLeft if options?.center - desiredScrollCenter = top + height / 2 + desiredScrollCenter = (top + bottom) / 2 unless @getScrollTop() < desiredScrollCenter < @getScrollBottom() desiredScrollTop = desiredScrollCenter - @getHeight() / 2 desiredScrollBottom = desiredScrollCenter + @getHeight() / 2 @@ -383,15 +394,26 @@ class DisplayBuffer extends Model desiredScrollLeft = left - horizontalScrollMarginInPixels desiredScrollRight = right + horizontalScrollMarginInPixels - if desiredScrollTop < @getScrollTop() - @setScrollTop(desiredScrollTop) - else if desiredScrollBottom > @getScrollBottom() - @setScrollBottom(desiredScrollBottom) + if options?.reversed ? true + if desiredScrollBottom > @getScrollBottom() + @setScrollBottom(desiredScrollBottom) + if desiredScrollTop < @getScrollTop() + @setScrollTop(desiredScrollTop) - if desiredScrollLeft < @getScrollLeft() - @setScrollLeft(desiredScrollLeft) - else if desiredScrollRight > @getScrollRight() - @setScrollRight(desiredScrollRight) + if desiredScrollRight > @getScrollRight() + @setScrollRight(desiredScrollRight) + if desiredScrollLeft < @getScrollLeft() + @setScrollLeft(desiredScrollLeft) + else + if desiredScrollTop < @getScrollTop() + @setScrollTop(desiredScrollTop) + if desiredScrollBottom > @getScrollBottom() + @setScrollBottom(desiredScrollBottom) + + if desiredScrollLeft < @getScrollLeft() + @setScrollLeft(desiredScrollLeft) + if desiredScrollRight > @getScrollRight() + @setScrollRight(desiredScrollRight) scrollToScreenPosition: (screenPosition, options) -> @scrollToScreenRange(new Range(screenPosition, screenPosition), options) diff --git a/src/selection.coffee b/src/selection.coffee index 524f1bd9b..df0e4a5d1 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -733,6 +733,12 @@ class Selection extends Model {oldHeadBufferPosition, oldTailBufferPosition} = e {oldHeadScreenPosition, oldTailScreenPosition} = e + if this is @editor.getLastSelection() + if @marker.hasTail() + @autoscroll() + else + @cursor.autoscroll() + eventObject = oldBufferRange: new Range(oldHeadBufferPosition, oldTailBufferPosition) oldScreenRange: new Range(oldHeadScreenPosition, oldTailScreenPosition) @@ -751,7 +757,7 @@ class Selection extends Model @linewise = false autoscroll: -> - @editor.scrollToScreenRange(@getScreenRange()) + @editor.scrollToScreenRange(@getScreenRange(), reversed: @isReversed()) clearAutoscroll: -> diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 114df486a..820737834 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -1947,9 +1947,7 @@ class TextEditor extends Model # Returns the added {Selection}. addSelectionForBufferRange: (bufferRange, options={}) -> @markBufferRange(bufferRange, _.defaults(@getSelectionMarkerAttributes(), options)) - selection = @getLastSelection() - selection.autoscroll() - selection + @getLastSelection() # Essential: Add a selection for the given range in screen coordinates. # @@ -1961,9 +1959,7 @@ class TextEditor extends Model # Returns the added {Selection}. addSelectionForScreenRange: (screenRange, options={}) -> @markScreenRange(screenRange, _.defaults(@getSelectionMarkerAttributes(), options)) - selection = @getLastSelection() - selection.autoscroll() - selection + @getLastSelection() # Essential: Select from the current cursor position to the given position in # buffer coordinates. @@ -2269,11 +2265,14 @@ class TextEditor extends Model @selections.push(selection) selectionBufferRange = selection.getBufferRange() @mergeIntersectingSelections(preserveFolds: marker.getProperties().preserveFolds) + if selection.destroyed for selection in @getSelections() if selection.intersectsBufferRange(selectionBufferRange) + selection.autoscroll() return selection else + selection.autoscroll() @emit 'selection-added', selection @emitter.emit 'did-add-selection', selection selection From a70be30ee7017b86114592800d9d4bdccff88aa8 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 26 Mar 2015 14:37:30 -0700 Subject: [PATCH 32/36] :art: --- src/typescript.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/typescript.coffee b/src/typescript.coffee index 3814a6c66..3a54941f3 100644 --- a/src/typescript.coffee +++ b/src/typescript.coffee @@ -57,7 +57,9 @@ createOptions = (filePath) -> transpile = (sourceCode, filePath, cachePath) -> options = createOptions(filePath) - tss ?= new (require 'typescript-simple').TypeScriptSimple(options, false) + unless tss? + {TypeScriptSimple} = require 'typescript-simple' + tss = new TypeScriptSimple(options, false) js = tss.compile(sourceCode, filePath) stats.misses++ From 9cdbda5c148c96dc18064b03898960ffdd3b1938 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 26 Mar 2015 14:42:33 -0700 Subject: [PATCH 33/36] :arrow_up: language-c@0.42 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ac4c5569b..96db70912 100644 --- a/package.json +++ b/package.json @@ -123,7 +123,7 @@ "welcome": "0.26.0", "whitespace": "0.29.0", "wrap-guide": "0.31.0", - "language-c": "0.41.0", + "language-c": "0.42.0", "language-clojure": "0.13.0", "language-coffee-script": "0.39.0", "language-csharp": "0.5.0", From 99c437ccecff469d2fc4ba70ff3ff9a0f694f9e6 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 26 Mar 2015 16:48:21 -0600 Subject: [PATCH 34/36] Allow autoscroll override option in cursor/selection methods Signed-off-by: Max Brunsfeld --- spec/text-editor-component-spec.coffee | 6 +++--- spec/text-editor-presenter-spec.coffee | 12 ++++++------ spec/text-editor-spec.coffee | 24 ++++++++++++++++++++++++ src/cursor.coffee | 16 +++++++++++----- src/selection.coffee | 24 +++++++++++++++++++----- src/text-editor.coffee | 19 ++++++++++++++----- 6 files changed, 77 insertions(+), 24 deletions(-) diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index 20ddcdcab..135e551a3 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -693,7 +693,7 @@ describe "TextEditorComponent", -> describe "cursor rendering", -> it "renders the currently visible cursors", -> cursor1 = editor.getLastCursor() - cursor1.setScreenPosition([0, 5]) + cursor1.setScreenPosition([0, 5], autoscroll: false) wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' wrapperNode.style.width = 20 * lineHeightInPixels + 'px' @@ -706,8 +706,8 @@ describe "TextEditorComponent", -> expect(cursorNodes[0].offsetWidth).toBe charWidth expect(cursorNodes[0].style['-webkit-transform']).toBe "translate(#{5 * charWidth}px, #{0 * lineHeightInPixels}px)" - cursor2 = editor.addCursorAtScreenPosition([8, 11]) - cursor3 = editor.addCursorAtScreenPosition([4, 10]) + cursor2 = editor.addCursorAtScreenPosition([8, 11], autoscroll: false) + cursor3 = editor.addCursorAtScreenPosition([4, 10], autoscroll: false) nextAnimationFrame() cursorNodes = componentNode.querySelectorAll('.cursor') diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 0cf0c7e53..b62391db8 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -1417,33 +1417,33 @@ describe "TextEditorPresenter", -> expect(stateForSelection(presenter, 1)).toBeUndefined() # moving into view - expectStateUpdate presenter, -> editor.getSelections()[1].setBufferRange([[2, 4], [2, 6]]) + expectStateUpdate presenter, -> editor.getSelections()[1].setBufferRange([[2, 4], [2, 6]], autoscroll: false) expectValues stateForSelection(presenter, 1), { regions: [{top: 2 * 10, left: 4 * 10, width: 2 * 10, height: 10}] } # becoming empty - expectStateUpdate presenter, -> editor.getSelections()[1].clear() + expectStateUpdate presenter, -> editor.getSelections()[1].clear(autoscroll: false) expect(stateForSelection(presenter, 1)).toBeUndefined() # becoming non-empty - expectStateUpdate presenter, -> editor.getSelections()[1].setBufferRange([[2, 4], [2, 6]]) + expectStateUpdate presenter, -> editor.getSelections()[1].setBufferRange([[2, 4], [2, 6]], autoscroll: false) expectValues stateForSelection(presenter, 1), { regions: [{top: 2 * 10, left: 4 * 10, width: 2 * 10, height: 10}] } # moving out of view - expectStateUpdate presenter, -> editor.getSelections()[1].setBufferRange([[3, 4], [3, 6]]) + expectStateUpdate presenter, -> editor.getSelections()[1].setBufferRange([[3, 4], [3, 6]], autoscroll: false) expect(stateForSelection(presenter, 1)).toBeUndefined() # adding - expectStateUpdate presenter, -> editor.addSelectionForBufferRange([[1, 4], [1, 6]]) + expectStateUpdate presenter, -> editor.addSelectionForBufferRange([[1, 4], [1, 6]], autoscroll: false) expectValues stateForSelection(presenter, 2), { regions: [{top: 1 * 10, left: 4 * 10, width: 2 * 10, height: 10}] } # moving added selection - expectStateUpdate presenter, -> editor.getSelections()[2].setBufferRange([[1, 4], [1, 8]]) + expectStateUpdate presenter, -> editor.getSelections()[2].setBufferRange([[1, 4], [1, 8]], autoscroll: false) expectValues stateForSelection(presenter, 2), { regions: [{top: 1 * 10, left: 4 * 10, width: 4 * 10, height: 10}] } diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index afebf591d..49b75bafd 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -993,6 +993,30 @@ describe "TextEditor", -> editor.undo() expect(editor.getScrollTop()).toBe 0 + it "honors the autoscroll option on cursor and selection manipulation methods", -> + expect(editor.getScrollTop()).toBe 0 + editor.addCursorAtScreenPosition([11, 11], autoscroll: false) + editor.addCursorAtBufferPosition([11, 11], autoscroll: false) + editor.setCursorScreenPosition([11, 11], autoscroll: false) + editor.setCursorBufferPosition([11, 11], autoscroll: false) + editor.addSelectionForBufferRange([[11, 11], [11, 11]], autoscroll: false) + editor.addSelectionForScreenRange([[11, 11], [11, 12]], autoscroll: false) + editor.setSelectedBufferRange([[11, 0], [11, 1]], autoscroll: false) + editor.setSelectedScreenRange([[11, 0], [11, 6]], autoscroll: false) + editor.clearSelections(autoscroll: false) + expect(editor.getScrollTop()).toBe 0 + + editor.addSelectionForScreenRange([[0, 0], [0, 4]]) + + editor.getCursors()[0].setScreenPosition([11, 11], autoscroll: true) + expect(editor.getScrollTop()).toBeGreaterThan 0 + editor.getCursors()[0].setBufferPosition([0, 0], autoscroll: true) + expect(editor.getScrollTop()).toBe 0 + editor.getSelections()[0].setScreenRange([[11, 0], [11, 4]], autoscroll: true) + expect(editor.getScrollTop()).toBeGreaterThan 0 + editor.getSelections()[0].setBufferRange([[0, 0], [0, 4]], autoscroll: true) + expect(editor.getScrollTop()).toBe 0 + describe '.logCursorScope()', -> beforeEach -> spyOn(atom.notifications, 'addInfo') diff --git a/src/cursor.coffee b/src/cursor.coffee index d0602a2f4..2e67ab43b 100644 --- a/src/cursor.coffee +++ b/src/cursor.coffee @@ -122,8 +122,9 @@ class Cursor extends Model # # * `bufferPosition` {Array} of two numbers: the buffer row, and the buffer column. # * `options` (optional) {Object} with the following keys: - # * `autoscroll` A Boolean which, if `true`, scrolls the {TextEditor} to wherever - # the cursor moves to. + # * `autoscroll` {Boolean} indicating whether to autoscroll to the new + # position. Defaults to `true` if this is the most recently added cursor, + # `false` otherwise. setBufferPosition: (bufferPosition, options={}) -> @changePosition options, => @marker.setHeadBufferPosition(bufferPosition, options) @@ -648,8 +649,12 @@ class Cursor extends Model changePosition: (options, fn) -> @clearSelection() - fn() - @autoscroll() if options.autoscroll + @editor.suppressAutoscroll = true if options.autoscroll is false + try + fn() + finally + @editor.suppressAutoscroll = false if options?.autoscroll is false + @autoscroll() if options.autoscroll is true getPixelRect: -> @editor.pixelRectForScreenRange(@getScreenRange()) @@ -659,7 +664,8 @@ class Cursor extends Model new Range(new Point(row, column), new Point(row, column + 1)) autoscroll: (options) -> - @editor.scrollToScreenRange(@getScreenRange(), options) + unless @editor.suppressAutoscroll + @editor.scrollToScreenRange(@getScreenRange(), options) getBeginningOfNextParagraphBufferPosition: -> start = @getBufferPosition() diff --git a/src/selection.coffee b/src/selection.coffee index df0e4a5d1..4987708eb 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -95,8 +95,11 @@ class Selection extends Model # # * `screenRange` The new {Range} to select. # * `options` (optional) {Object} with the keys: - # * `preserveFolds` if `true`, the fold settings are preserved after the selection moves. - # * `autoscroll` if `true`, the {TextEditor} scrolls to the new selection. + # * `preserveFolds` if `true`, the fold settings are preserved after the + # selection moves. + # * `autoscroll` {Boolean} indicating whether to autoscroll to the new + # range. Defaults to `true` if this is the most recently added selection, + # `false` otherwise. setBufferRange: (bufferRange, options={}) -> bufferRange = Range.fromObject(bufferRange) options.reversed ?= @isReversed() @@ -104,8 +107,10 @@ class Selection extends Model @modifySelection => needsFlash = options.flash delete options.flash if options.flash? + @editor.suppressAutoscroll = true if options.autoscroll is false @marker.setBufferRange(bufferRange, options) - @autoscroll() if options.autoscroll + @editor.suppressAutoscroll = false if options.autoscroll is false + @autoscroll() if options?.autoscroll is true @decoration.flash('flash', @editor.selectionFlashDuration) if needsFlash # Public: Returns the starting and ending buffer rows the selection is @@ -181,9 +186,17 @@ class Selection extends Model ### # Public: Clears the selection, moving the marker to the head. - clear: -> + # + # * `options` (optional) {Object} with the following keys: + # * `autoscroll` {Boolean} indicating whether to autoscroll to the new + # range. Defaults to `true` if this is the most recently added selection, + # `false` otherwise. + clear: (options) -> + @editor.suppressAutoscroll = true if options?.autoscroll is false @marker.setProperties(goalScreenRange: null) @marker.clearTail() unless @retainSelection + @editor.suppressAutoscroll = false if options?.autoscroll is false + @autoscroll() if options?.autoscroll is true @finalize() # Public: Selects the text from the current cursor position to a given screen @@ -757,7 +770,8 @@ class Selection extends Model @linewise = false autoscroll: -> - @editor.scrollToScreenRange(@getScreenRange(), reversed: @isReversed()) + unless @editor.suppressAutoscroll + @editor.scrollToScreenRange(@getScreenRange(), reversed: @isReversed()) clearAutoscroll: -> diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 820737834..beaf4aaac 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -69,6 +69,7 @@ class TextEditor extends Model suppressSelectionMerging: false updateBatchDepth: 0 selectionFlashDuration: 500 + suppressAutoscroll: false @delegatesMethods 'suggestedIndentForBufferRow', 'autoIndentBufferRow', 'autoIndentBufferRows', 'autoDecreaseIndentForBufferRow', 'toggleLineCommentForBufferRow', 'toggleLineCommentsForBufferRows', @@ -1606,8 +1607,10 @@ class TextEditor extends Model # * `bufferPosition` A {Point} or {Array} of `[row, column]` # # Returns a {Cursor}. - addCursorAtBufferPosition: (bufferPosition) -> + addCursorAtBufferPosition: (bufferPosition, options) -> + @suppressAutoscroll = true if options?.autoscroll is false @markBufferPosition(bufferPosition, @getSelectionMarkerAttributes()) + @suppressAutoscroll = false if options?.autoscroll is false @getLastSelection().cursor # Essential: Add a cursor at the position in screen coordinates. @@ -1615,8 +1618,10 @@ class TextEditor extends Model # * `screenPosition` A {Point} or {Array} of `[row, column]` # # Returns a {Cursor}. - addCursorAtScreenPosition: (screenPosition) -> + addCursorAtScreenPosition: (screenPosition, options) -> + @suppressAutoscroll = true if options?.autoscroll is false @markScreenPosition(screenPosition, @getSelectionMarkerAttributes()) + @suppressAutoscroll = false if options?.autoscroll is false @getLastSelection().cursor # Essential: Returns {Boolean} indicating whether or not there are multiple cursors. @@ -1946,7 +1951,9 @@ class TextEditor extends Model # # Returns the added {Selection}. addSelectionForBufferRange: (bufferRange, options={}) -> + @suppressAutoscroll = true if options.autoscroll is false @markBufferRange(bufferRange, _.defaults(@getSelectionMarkerAttributes(), options)) + @suppressAutoscroll = false if options.autoscroll is false @getLastSelection() # Essential: Add a selection for the given range in screen coordinates. @@ -1958,7 +1965,9 @@ class TextEditor extends Model # # Returns the added {Selection}. addSelectionForScreenRange: (screenRange, options={}) -> + @suppressAutoscroll = true if options.autoscroll is false @markScreenRange(screenRange, _.defaults(@getSelectionMarkerAttributes(), options)) + @suppressAutoscroll = false if options.autoscroll is false @getLastSelection() # Essential: Select from the current cursor position to the given position in @@ -2272,7 +2281,7 @@ class TextEditor extends Model selection.autoscroll() return selection else - selection.autoscroll() + selection.autoscroll() unless options.autoscroll is false @emit 'selection-added', selection @emitter.emit 'did-add-selection', selection selection @@ -2285,9 +2294,9 @@ class TextEditor extends Model # Reduce one or more selections to a single empty selection based on the most # recently added cursor. - clearSelections: -> + clearSelections: (options) -> @consolidateSelections() - @getLastSelection().clear() + @getLastSelection().clear(options) # Reduce multiple selections to the most recently added selection. consolidateSelections: -> From ae4f7f6170da83c786ba06235f0997e52e806b10 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 26 Mar 2015 16:48:24 -0600 Subject: [PATCH 35/36] Explicitly autoscroll when needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than when the selection’s marker changes. This is simpler than suppressing autoscroll via state when we don’t want it. It also captures the intent to autoscroll when attempting to move the cursor at the beginning or end of the document. Signed-off-by: Max Brunsfeld --- spec/text-editor-component-spec.coffee | 4 ++-- spec/text-editor-spec.coffee | 11 +++++++++-- src/cursor.coffee | 17 ++++++---------- src/selection.coffee | 27 ++++++++++++-------------- src/text-editor.coffee | 19 +++++++----------- 5 files changed, 36 insertions(+), 42 deletions(-) diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index 135e551a3..656dca0f0 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -1524,7 +1524,7 @@ describe "TextEditorComponent", -> expect(inputNode.offsetLeft).toBe 0 # In bounds, not focused - editor.setCursorBufferPosition([5, 4]) + editor.setCursorBufferPosition([5, 4], autoscroll: false) nextAnimationFrame() expect(inputNode.offsetTop).toBe 0 expect(inputNode.offsetLeft).toBe 0 @@ -1542,7 +1542,7 @@ describe "TextEditorComponent", -> expect(inputNode.offsetLeft).toBe 0 # Out of bounds, not focused - editor.setCursorBufferPosition([1, 2]) + editor.setCursorBufferPosition([1, 2], autoscroll: false) nextAnimationFrame() expect(inputNode.offsetTop).toBe 0 expect(inputNode.offsetLeft).toBe 0 diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 49b75bafd..d87952e85 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -970,9 +970,8 @@ describe "TextEditor", -> it "scrolls left when the last cursor gets closer than ::horizontalScrollMargin to the left of the editor", -> editor.setScrollRight(editor.getScrollWidth()) - editor.setCursorScreenPosition([6, 62]) - expect(editor.getScrollRight()).toBe editor.getScrollWidth() + editor.setCursorScreenPosition([6, 62], autoscroll: false) editor.moveLeft() expect(editor.getScrollLeft()).toBe 59 * 10 @@ -996,13 +995,21 @@ describe "TextEditor", -> it "honors the autoscroll option on cursor and selection manipulation methods", -> expect(editor.getScrollTop()).toBe 0 editor.addCursorAtScreenPosition([11, 11], autoscroll: false) + expect(editor.getScrollTop()).toBe 0 editor.addCursorAtBufferPosition([11, 11], autoscroll: false) + expect(editor.getScrollTop()).toBe 0 editor.setCursorScreenPosition([11, 11], autoscroll: false) + expect(editor.getScrollTop()).toBe 0 editor.setCursorBufferPosition([11, 11], autoscroll: false) + expect(editor.getScrollTop()).toBe 0 editor.addSelectionForBufferRange([[11, 11], [11, 11]], autoscroll: false) + expect(editor.getScrollTop()).toBe 0 editor.addSelectionForScreenRange([[11, 11], [11, 12]], autoscroll: false) + expect(editor.getScrollTop()).toBe 0 editor.setSelectedBufferRange([[11, 0], [11, 1]], autoscroll: false) + expect(editor.getScrollTop()).toBe 0 editor.setSelectedScreenRange([[11, 0], [11, 6]], autoscroll: false) + expect(editor.getScrollTop()).toBe 0 editor.clearSelections(autoscroll: false) expect(editor.getScrollTop()).toBe 0 diff --git a/src/cursor.coffee b/src/cursor.coffee index 2e67ab43b..c96f2eff5 100644 --- a/src/cursor.coffee +++ b/src/cursor.coffee @@ -624,8 +624,8 @@ class Cursor extends Model clearAutoscroll: -> # Public: Deselects the current selection. - clearSelection: -> - @selection?.clear() + clearSelection: (options) -> + @selection?.clear(options) # Public: Get the RegExp used by the cursor to determine what a "word" is. # @@ -648,13 +648,9 @@ class Cursor extends Model ### changePosition: (options, fn) -> - @clearSelection() - @editor.suppressAutoscroll = true if options.autoscroll is false - try - fn() - finally - @editor.suppressAutoscroll = false if options?.autoscroll is false - @autoscroll() if options.autoscroll is true + @clearSelection(options) + fn() + @autoscroll() if options.autoscroll ? @isLastCursor() getPixelRect: -> @editor.pixelRectForScreenRange(@getScreenRange()) @@ -664,8 +660,7 @@ class Cursor extends Model new Range(new Point(row, column), new Point(row, column + 1)) autoscroll: (options) -> - unless @editor.suppressAutoscroll - @editor.scrollToScreenRange(@getScreenRange(), options) + @editor.scrollToScreenRange(@getScreenRange(), options) getBeginningOfNextParagraphBufferPosition: -> start = @getBufferPosition() diff --git a/src/selection.coffee b/src/selection.coffee index 4987708eb..0bee6f7de 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -1,6 +1,6 @@ {Point, Range} = require 'text-buffer' {Model} = require 'theorist' -{pick} = require 'underscore-plus' +{pick} = _ = require 'underscore-plus' {Emitter} = require 'event-kit' Grim = require 'grim' @@ -34,6 +34,9 @@ class Selection extends Model destroy: -> @marker.destroy() + isLastSelection: -> + this is @editor.getLastSelection() + ### Section: Event Subscription ### @@ -107,10 +110,8 @@ class Selection extends Model @modifySelection => needsFlash = options.flash delete options.flash if options.flash? - @editor.suppressAutoscroll = true if options.autoscroll is false @marker.setBufferRange(bufferRange, options) - @editor.suppressAutoscroll = false if options.autoscroll is false - @autoscroll() if options?.autoscroll is true + @autoscroll() if options?.autoscroll ? @isLastSelection() @decoration.flash('flash', @editor.selectionFlashDuration) if needsFlash # Public: Returns the starting and ending buffer rows the selection is @@ -192,11 +193,9 @@ class Selection extends Model # range. Defaults to `true` if this is the most recently added selection, # `false` otherwise. clear: (options) -> - @editor.suppressAutoscroll = true if options?.autoscroll is false @marker.setProperties(goalScreenRange: null) @marker.clearTail() unless @retainSelection - @editor.suppressAutoscroll = false if options?.autoscroll is false - @autoscroll() if options?.autoscroll is true + @autoscroll() if options?.autoscroll ? @isLastSelection() @finalize() # Public: Selects the text from the current cursor position to a given screen @@ -407,6 +406,8 @@ class Selection extends Model else if options.autoDecreaseIndent and NonWhitespaceRegExp.test(text) @editor.autoDecreaseIndentForBufferRow(newBufferRange.start.row) + @autoscroll() if @isLastSelection() + newBufferRange # Public: Removes the first character before the selection if the selection @@ -722,7 +723,7 @@ class Selection extends Model else options.goalScreenRange = myGoalScreenRange ? otherGoalScreenRange - @setBufferRange(@getBufferRange().union(otherSelection.getBufferRange()), options) + @setBufferRange(@getBufferRange().union(otherSelection.getBufferRange()), _.extend(autoscroll: false, options)) otherSelection.destroy() ### @@ -746,12 +747,6 @@ class Selection extends Model {oldHeadBufferPosition, oldTailBufferPosition} = e {oldHeadScreenPosition, oldTailScreenPosition} = e - if this is @editor.getLastSelection() - if @marker.hasTail() - @autoscroll() - else - @cursor.autoscroll() - eventObject = oldBufferRange: new Range(oldHeadBufferPosition, oldTailBufferPosition) oldScreenRange: new Range(oldHeadScreenPosition, oldTailScreenPosition) @@ -770,8 +765,10 @@ class Selection extends Model @linewise = false autoscroll: -> - unless @editor.suppressAutoscroll + if @marker.hasTail() @editor.scrollToScreenRange(@getScreenRange(), reversed: @isReversed()) + else + @cursor.autoscroll() clearAutoscroll: -> diff --git a/src/text-editor.coffee b/src/text-editor.coffee index beaf4aaac..355a644b8 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -69,7 +69,6 @@ class TextEditor extends Model suppressSelectionMerging: false updateBatchDepth: 0 selectionFlashDuration: 500 - suppressAutoscroll: false @delegatesMethods 'suggestedIndentForBufferRow', 'autoIndentBufferRow', 'autoIndentBufferRows', 'autoDecreaseIndentForBufferRow', 'toggleLineCommentForBufferRow', 'toggleLineCommentsForBufferRows', @@ -1133,11 +1132,13 @@ class TextEditor extends Model # Essential: Undo the last change. undo: -> - @buffer.undo(this) + @buffer.undo() + @getLastSelection().autoscroll() # Essential: Redo the last change. redo: -> @buffer.redo(this) + @getLastSelection().autoscroll() # Extended: Batch multiple operations as a single undo/redo step. # @@ -1608,9 +1609,8 @@ class TextEditor extends Model # # Returns a {Cursor}. addCursorAtBufferPosition: (bufferPosition, options) -> - @suppressAutoscroll = true if options?.autoscroll is false @markBufferPosition(bufferPosition, @getSelectionMarkerAttributes()) - @suppressAutoscroll = false if options?.autoscroll is false + @getLastSelection().cursor.autoscroll() unless options?.autoscroll is false @getLastSelection().cursor # Essential: Add a cursor at the position in screen coordinates. @@ -1619,9 +1619,8 @@ class TextEditor extends Model # # Returns a {Cursor}. addCursorAtScreenPosition: (screenPosition, options) -> - @suppressAutoscroll = true if options?.autoscroll is false @markScreenPosition(screenPosition, @getSelectionMarkerAttributes()) - @suppressAutoscroll = false if options?.autoscroll is false + @getLastSelection().cursor.autoscroll() unless options?.autoscroll is false @getLastSelection().cursor # Essential: Returns {Boolean} indicating whether or not there are multiple cursors. @@ -1951,9 +1950,8 @@ class TextEditor extends Model # # Returns the added {Selection}. addSelectionForBufferRange: (bufferRange, options={}) -> - @suppressAutoscroll = true if options.autoscroll is false @markBufferRange(bufferRange, _.defaults(@getSelectionMarkerAttributes(), options)) - @suppressAutoscroll = false if options.autoscroll is false + @getLastSelection().autoscroll() unless options.autoscroll is false @getLastSelection() # Essential: Add a selection for the given range in screen coordinates. @@ -1965,9 +1963,8 @@ class TextEditor extends Model # # Returns the added {Selection}. addSelectionForScreenRange: (screenRange, options={}) -> - @suppressAutoscroll = true if options.autoscroll is false @markScreenRange(screenRange, _.defaults(@getSelectionMarkerAttributes(), options)) - @suppressAutoscroll = false if options.autoscroll is false + @getLastSelection().autoscroll() unless options.autoscroll is false @getLastSelection() # Essential: Select from the current cursor position to the given position in @@ -2278,10 +2275,8 @@ class TextEditor extends Model if selection.destroyed for selection in @getSelections() if selection.intersectsBufferRange(selectionBufferRange) - selection.autoscroll() return selection else - selection.autoscroll() unless options.autoscroll is false @emit 'selection-added', selection @emitter.emit 'did-add-selection', selection selection From 477a9d7002603ca297efd3e07e3af0300b5d7597 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 26 Mar 2015 16:25:12 -0700 Subject: [PATCH 36/36] :arrow_up: apm@0.152 --- apm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apm/package.json b/apm/package.json index f43d9e024..e10632417 100644 --- a/apm/package.json +++ b/apm/package.json @@ -6,6 +6,6 @@ "url": "https://github.com/atom/atom.git" }, "dependencies": { - "atom-package-manager": "0.151.0" + "atom-package-manager": "0.152.0" } }