From 7d512cf3d7d7568b32a6e314a277bc6d98fa3721 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Wed, 16 May 2012 14:04:18 -0700 Subject: [PATCH 01/61] shared schemes, all others can be deleted --- .../xcschemes/atom-debug.xcscheme | 86 +++++++++++++++++++ .../xcschemes/atom-release.xcscheme | 86 +++++++++++++++++++ .../xcschemes/libcef_dll_wrapper.xcscheme | 59 +++++++++++++ Atom/ResourceConfig.xcconfig | 7 ++ 4 files changed, 238 insertions(+) create mode 100644 Atom.xcodeproj/xcshareddata/xcschemes/atom-debug.xcscheme create mode 100644 Atom.xcodeproj/xcshareddata/xcschemes/atom-release.xcscheme create mode 100644 Atom.xcodeproj/xcshareddata/xcschemes/libcef_dll_wrapper.xcscheme create mode 100644 Atom/ResourceConfig.xcconfig diff --git a/Atom.xcodeproj/xcshareddata/xcschemes/atom-debug.xcscheme b/Atom.xcodeproj/xcshareddata/xcschemes/atom-debug.xcscheme new file mode 100644 index 000000000..b7620e646 --- /dev/null +++ b/Atom.xcodeproj/xcshareddata/xcschemes/atom-debug.xcscheme @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Atom.xcodeproj/xcshareddata/xcschemes/atom-release.xcscheme b/Atom.xcodeproj/xcshareddata/xcschemes/atom-release.xcscheme new file mode 100644 index 000000000..9740ad880 --- /dev/null +++ b/Atom.xcodeproj/xcshareddata/xcschemes/atom-release.xcscheme @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Atom.xcodeproj/xcshareddata/xcschemes/libcef_dll_wrapper.xcscheme b/Atom.xcodeproj/xcshareddata/xcschemes/libcef_dll_wrapper.xcscheme new file mode 100644 index 000000000..a6ed5e475 --- /dev/null +++ b/Atom.xcodeproj/xcshareddata/xcschemes/libcef_dll_wrapper.xcscheme @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Atom/ResourceConfig.xcconfig b/Atom/ResourceConfig.xcconfig new file mode 100644 index 000000000..9322e2e9e --- /dev/null +++ b/Atom/ResourceConfig.xcconfig @@ -0,0 +1,7 @@ +// +// Config.xcconfig +// Atom +// +// Created by Corey Johnson on 5/16/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// From 9c8722768a3be4697791fe54c82f34378a17bf32 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Wed, 16 May 2012 14:04:53 -0700 Subject: [PATCH 02/61] LOAD_RESOURCES_FROM_DIR is a build env set in ResourceConfig.xcconfig --- Atom.xcodeproj/project.pbxproj | 10 +++++++--- Atom/ResourceConfig.xcconfig | 8 +------- Rakefile | 2 +- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Atom.xcodeproj/project.pbxproj b/Atom.xcodeproj/project.pbxproj index b95de6be8..513c06e8c 100644 --- a/Atom.xcodeproj/project.pbxproj +++ b/Atom.xcodeproj/project.pbxproj @@ -396,6 +396,7 @@ 048A2FC5154870DC0051715C /* PathWatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PathWatcher.h; sourceTree = ""; }; 048A2FC6154870DC0051715C /* PathWatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PathWatcher.m; sourceTree = ""; }; 04E1DDDC152A0941001A9D07 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = frameworks/Sparkle.framework; sourceTree = ""; }; + 04F21A2615644AC10083F6D4 /* ResourceConfig.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = ResourceConfig.xcconfig; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -791,9 +792,10 @@ children = ( 0487D15E14FEE7880045E5E3 /* Resources */, 0487C93414FED5FB0045E5E3 /* ClientWindow.xib */, - 0487C93514FED5FB0045E5E3 /* Info.plist */, 0487C93614FED5FB0045E5E3 /* MainMenu.xib */, + 0487C93514FED5FB0045E5E3 /* Info.plist */, 0415A1FD1524EC8A0075C91C /* atom.icns */, + 04F21A2615644AC10083F6D4 /* ResourceConfig.xcconfig */, ); name = "Supporting Files"; sourceTree = ""; @@ -946,7 +948,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "rake compile-coffeescripts\n"; + shellScript = "if [ -z $LOAD_RESOURCES_FROM_DIR ]; then\n say 'compiling'\n rake compile-coffeescripts\nfi;"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -1054,7 +1056,7 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", - "LOAD_RESOURCES_FROM_DIR=\"\\\"$PROJECT_DIR\\\"\"", + "LOAD_RESOURCES_FROM_DIR=\"\\\"$LOAD_RESOURCES_FROM_DIR\\\"\"", "\"ENABLE_REMOTING=1\"", "\"ENABLE_P2P_APIS=1\"", "\"ENABLE_CONFIGURATION_POLICY\"", @@ -1090,6 +1092,7 @@ GCC_INLINES_ARE_PRIVATE_EXTERN = YES; GCC_OPTIMIZATION_LEVEL = 3; GCC_PREPROCESSOR_DEFINITIONS = ( + "LOAD_RESOURCES_FROM_DIR=\"\\\"$LOAD_RESOURCES_FROM_DIR\\\"\"", "\"CHROMIUM_BUILD\"", "\"ENABLE_REMOTING=1\"", "\"ENABLE_P2P_APIS=1\"", @@ -1117,6 +1120,7 @@ }; 0487C93114FED5370045E5E3 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 04F21A2615644AC10083F6D4 /* ResourceConfig.xcconfig */; buildSettings = { FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", diff --git a/Atom/ResourceConfig.xcconfig b/Atom/ResourceConfig.xcconfig index 9322e2e9e..e9cc63cdd 100644 --- a/Atom/ResourceConfig.xcconfig +++ b/Atom/ResourceConfig.xcconfig @@ -1,7 +1 @@ -// -// Config.xcconfig -// Atom -// -// Created by Corey Johnson on 5/16/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// +LOAD_RESOURCES_FROM_DIR=$PROJECT_DIR diff --git a/Rakefile b/Rakefile index c6ddeeb17..252c8f32f 100644 --- a/Rakefile +++ b/Rakefile @@ -7,7 +7,7 @@ BUILD_DIR = '/tmp/atom-build' desc "Build Atom via `xcodebuild`" task :build => :"verify-prerequisites" do - output = `xcodebuild -configuration Release SYMROOT=#{BUILD_DIR}` + output = `xcodebuild -scheme atom-release SYMROOT=#{BUILD_DIR}` if $?.exitstatus != 0 $stderr.puts "Error #{$?.exitstatus}:\n#{output}" exit($?.exitstatus) From 295f2ac19a3972c6824741c02f8d0384e0b98886 Mon Sep 17 00:00:00 2001 From: atom-bot Date: Wed, 16 May 2012 14:15:02 -0700 Subject: [PATCH 03/61] remove "say" call from build phase --- Atom.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Atom.xcodeproj/project.pbxproj b/Atom.xcodeproj/project.pbxproj index 513c06e8c..338f3cebb 100644 --- a/Atom.xcodeproj/project.pbxproj +++ b/Atom.xcodeproj/project.pbxproj @@ -948,7 +948,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if [ -z $LOAD_RESOURCES_FROM_DIR ]; then\n say 'compiling'\n rake compile-coffeescripts\nfi;"; + shellScript = "if [ -z $LOAD_RESOURCES_FROM_DIR ]; then\n rake compile-coffeescripts\nfi;"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ From e20d2dd9cb58307b6fb2d9ed6af05fd24e3dd0db Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Wed, 16 May 2012 14:31:51 -0700 Subject: [PATCH 04/61] better LOAD_RESOURCES_FROM_DIR macro detection in atom.mm --- Atom.xcodeproj/project.pbxproj | 4 ++-- Atom/src/Atom.mm | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Atom.xcodeproj/project.pbxproj b/Atom.xcodeproj/project.pbxproj index 338f3cebb..f149117c9 100644 --- a/Atom.xcodeproj/project.pbxproj +++ b/Atom.xcodeproj/project.pbxproj @@ -1056,7 +1056,7 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", - "LOAD_RESOURCES_FROM_DIR=\"\\\"$LOAD_RESOURCES_FROM_DIR\\\"\"", + "\"LOAD_RESOURCES_FROM_DIR=$LOAD_RESOURCES_FROM_DIR\"", "\"ENABLE_REMOTING=1\"", "\"ENABLE_P2P_APIS=1\"", "\"ENABLE_CONFIGURATION_POLICY\"", @@ -1092,7 +1092,7 @@ GCC_INLINES_ARE_PRIVATE_EXTERN = YES; GCC_OPTIMIZATION_LEVEL = 3; GCC_PREPROCESSOR_DEFINITIONS = ( - "LOAD_RESOURCES_FROM_DIR=\"\\\"$LOAD_RESOURCES_FROM_DIR\\\"\"", + "\"LOAD_RESOURCES_FROM_DIR=$LOAD_RESOURCES_FROM_DIR\"", "\"CHROMIUM_BUILD\"", "\"ENABLE_REMOTING=1\"", "\"ENABLE_P2P_APIS=1\"", diff --git a/Atom/src/Atom.mm b/Atom/src/Atom.mm index 1733e242c..ad3a0eaea 100755 --- a/Atom/src/Atom.mm +++ b/Atom/src/Atom.mm @@ -112,9 +112,10 @@ CefRefPtr atom = CefV8Value::CreateObject(NULL, NULL); global->SetValue("atom", atom, V8_PROPERTY_ATTRIBUTE_NONE); - -#ifdef LOAD_RESOURCES_FROM_DIR - char path[] = LOAD_RESOURCES_FROM_DIR; + +#define STR_VALUE(arg) #arg +#if defined(LOAD_RESOURCES_FROM_DIR) + char path[] = STR_VALUE(LOAD_RESOURCES_FROM_DIR); #else const char *path = [[[NSBundle mainBundle] resourcePath] UTF8String]; #endif From 1d615c357614d339c0d81fd787bf2aa271d5ea0d Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Wed, 16 May 2012 15:08:16 -0700 Subject: [PATCH 05/61] Preprocessor can't distinguish between a macro that is defined with a value, and one that is simply defined. So I removed the LOAD_RESOURCES_FROM_DIR from the release build --- Atom.xcodeproj/project.pbxproj | 3 +-- Atom/src/Atom.mm | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Atom.xcodeproj/project.pbxproj b/Atom.xcodeproj/project.pbxproj index f149117c9..bc3f9d849 100644 --- a/Atom.xcodeproj/project.pbxproj +++ b/Atom.xcodeproj/project.pbxproj @@ -1056,7 +1056,7 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", - "\"LOAD_RESOURCES_FROM_DIR=$LOAD_RESOURCES_FROM_DIR\"", + "LOAD_RESOURCES_FROM_DIR=\"\\\"$LOAD_RESOURCES_FROM_DIR\\\"\"", "\"ENABLE_REMOTING=1\"", "\"ENABLE_P2P_APIS=1\"", "\"ENABLE_CONFIGURATION_POLICY\"", @@ -1092,7 +1092,6 @@ GCC_INLINES_ARE_PRIVATE_EXTERN = YES; GCC_OPTIMIZATION_LEVEL = 3; GCC_PREPROCESSOR_DEFINITIONS = ( - "\"LOAD_RESOURCES_FROM_DIR=$LOAD_RESOURCES_FROM_DIR\"", "\"CHROMIUM_BUILD\"", "\"ENABLE_REMOTING=1\"", "\"ENABLE_P2P_APIS=1\"", diff --git a/Atom/src/Atom.mm b/Atom/src/Atom.mm index ad3a0eaea..7b563e796 100755 --- a/Atom/src/Atom.mm +++ b/Atom/src/Atom.mm @@ -113,9 +113,9 @@ CefRefPtr atom = CefV8Value::CreateObject(NULL, NULL); global->SetValue("atom", atom, V8_PROPERTY_ATTRIBUTE_NONE); -#define STR_VALUE(arg) #arg -#if defined(LOAD_RESOURCES_FROM_DIR) - char path[] = STR_VALUE(LOAD_RESOURCES_FROM_DIR); + +#ifdef LOAD_RESOURCES_FROM_DIR + char path[] = LOAD_RESOURCES_FROM_DIR; #else const char *path = [[[NSBundle mainBundle] resourcePath] UTF8String]; #endif From 734c461110eb59f17df886d8282ecde15c63ebce Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Wed, 16 May 2012 16:32:06 -0700 Subject: [PATCH 06/61] make sure require.coffee is always compiled --- Atom.xcodeproj/project.pbxproj | 2 +- Rakefile | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Atom.xcodeproj/project.pbxproj b/Atom.xcodeproj/project.pbxproj index bc3f9d849..afc739ad0 100644 --- a/Atom.xcodeproj/project.pbxproj +++ b/Atom.xcodeproj/project.pbxproj @@ -948,7 +948,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if [ -z $LOAD_RESOURCES_FROM_DIR ]; then\n rake compile-coffeescripts\nfi;"; + shellScript = "rake copy-files-to-bundle\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ diff --git a/Rakefile b/Rakefile index 252c8f32f..a6abfc1eb 100644 --- a/Rakefile +++ b/Rakefile @@ -48,8 +48,8 @@ task :benchmark do Rake::Task["run"].invoke end -desc "Compile CoffeeScripts" -task :"compile-coffeescripts" => :"verify-prerequisites" do +desc "Copy files to bundle and compile CoffeeScripts" +task :"copy-files-to-bundle" => :"verify-prerequisites" do project_dir = ENV['PROJECT_DIR'] || '.' built_dir = ENV['BUILT_PRODUCTS_DIR'] || '.' contents_dir = ENV['CONTENTS_FOLDER_PATH'].to_s @@ -61,8 +61,11 @@ task :"compile-coffeescripts" => :"verify-prerequisites" do cp_r dir, File.join(dest, dir) end - puts contents_dir - sh "coffee -c #{dest}/src #{dest}/vendor #{dest}/spec" + if ENV['LOAD_RESOURCES_FROM_DIR'] + sh "coffee -c #{dest}/src/stdlib/require.coffee" + else + sh "coffee -c #{dest}/src #{dest}/vendor #{dest}/spec" + end end desc "Change webkit frameworks to use @rpath as install name" From f05b64b4835ddfbed488ac9a359db97e51affa03 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Wed, 16 May 2012 17:25:07 -0700 Subject: [PATCH 07/61] Better error messages for failed benchmarks --- vendor/jasmine-console-reporter.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/vendor/jasmine-console-reporter.js b/vendor/jasmine-console-reporter.js index afad4657b..24ec50cc5 100644 --- a/vendor/jasmine-console-reporter.js +++ b/vendor/jasmine-console-reporter.js @@ -27,16 +27,17 @@ jasmine.ConsoleReporter.prototype.reportSpecResults = function(spec) { if (results.skipped) { status = 'skipped'; } - var resultItems = results.getItems(); for (var i = 0; i < resultItems.length; i++) { var result = resultItems[i]; - if (this.logErrors && result.type == 'expect' && result.passed && !result.passed()) { - console.log(spec.getFullName()) + console.log("ERROR: %s", spec.getFullName()) if (result.trace.stack) { console.log(result.trace.stack) } + else { + console.log(result.message) + } } } }; From 00f11d86d66b6c89eaa3c0a718a83d0804545eec Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Wed, 16 May 2012 17:25:33 -0700 Subject: [PATCH 08/61] Fix failing benchmark --- benchmark/benchmark-suite.coffee | 2 -- 1 file changed, 2 deletions(-) diff --git a/benchmark/benchmark-suite.coffee b/benchmark/benchmark-suite.coffee index 06078e855..276c330ac 100644 --- a/benchmark/benchmark-suite.coffee +++ b/benchmark/benchmark-suite.coffee @@ -53,8 +53,6 @@ describe "editor.", -> benchmark "moving-to-eof.", 1, -> editor.moveCursorToBottom() - waitsFor (scrollComplete) -> - editor.scrollView.on 'scroll', scrollComplete describe "on-first-line.", -> benchmark "inserting-newline", 5, -> From 7a82bf267966ec54dd286fd38bbca4f7af3649b9 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Thu, 17 May 2012 08:49:03 -0700 Subject: [PATCH 09/61] :lipstick: --- spec/app/editor-spec.coffee | 95 ++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 48 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index c79ed713d..246720f50 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -278,6 +278,53 @@ describe "Editor", -> editor.loadPreviousEditSession() expect(editor.buffer.path).toBe "2" + describe ".scrollTop(n)", -> + beforeEach -> + editor.attachToDom(heightInLines: 5) + expect(editor.verticalScrollbar.scrollTop()).toBe 0 + expect(editor.visibleLines.css('-webkit-tranform')).toBeNull() + expect(editor.gutter.lineNumbers.css('-webkit-tranform')).toBeNull() + + describe "when called with a scroll top argument", -> + it "sets the scrollTop of the vertical scrollbar and sets a transform on the line numbers and lines", -> + editor.scrollTop(100) + expect(editor.verticalScrollbar.scrollTop()).toBe 100 + expect(editor.visibleLines.css('-webkit-transform')).toBe 'matrix(1, 0, 0, 1, 0, -100)' + expect(editor.gutter.lineNumbers.css('-webkit-transform')).toBe 'matrix(1, 0, 0, 1, 0, -100)' + + editor.scrollTop(150) + expect(editor.verticalScrollbar.scrollTop()).toBe 150 + expect(editor.visibleLines.css('-webkit-transform')).toBe 'matrix(1, 0, 0, 1, 0, -150)' + expect(editor.gutter.lineNumbers.css('-webkit-transform')).toBe 'matrix(1, 0, 0, 1, 0, -150)' + + it "does not allow negative scrollTops to be assigned", -> + editor.scrollTop(-100) + expect(editor.scrollTop()).toBe 0 + + it "doesn't do anything if the scrollTop hasn't changed", -> + editor.scrollTop(100) + spyOn(editor.verticalScrollbar, 'scrollTop') + spyOn(editor.visibleLines, 'css') + spyOn(editor.gutter.lineNumbers, 'css') + + editor.scrollTop(100) + expect(editor.verticalScrollbar.scrollTop).not.toHaveBeenCalled() + expect(editor.visibleLines.css).not.toHaveBeenCalled() + expect(editor.gutter.lineNumbers.css).not.toHaveBeenCalled() + + describe "when the 'adjustVerticalScrollbar' option is false (defaults to true)", -> + it "doesn't adjust the scrollTop of the vertical scrollbar", -> + editor.scrollTop(100, adjustVerticalScrollbar: false) + expect(editor.verticalScrollbar.scrollTop()).toBe 0 + expect(editor.visibleLines.css('-webkit-transform')).toBe 'matrix(1, 0, 0, 1, 0, -100)' + expect(editor.gutter.lineNumbers.css('-webkit-transform')).toBe 'matrix(1, 0, 0, 1, 0, -100)' + + describe "when called with no argument", -> + it "returns the last assigned value or 0 if none has been assigned", -> + expect(editor.scrollTop()).toBe 0 + editor.scrollTop(50) + expect(editor.scrollTop()).toBe 50 + describe "editor-open event", -> it 'only triggers an editor-open event when it is first added to the DOM', -> openHandler = jasmine.createSpy('openHandler') @@ -559,53 +606,6 @@ describe "Editor", -> expect(editor.gutter.find('.line-number:first').text()).toBe "4" expect(editor.gutter.find('.line-number:last').text()).toBe "9" - describe ".scrollTop(n)", -> - beforeEach -> - editor.attachToDom(heightInLines: 5) - expect(editor.verticalScrollbar.scrollTop()).toBe 0 - expect(editor.visibleLines.css('-webkit-tranform')).toBeNull() - expect(editor.gutter.lineNumbers.css('-webkit-tranform')).toBeNull() - - describe "when called with a scroll top argument", -> - it "sets the scrollTop of the vertical scrollbar and sets a transform on the line numbers and lines", -> - editor.scrollTop(100) - expect(editor.verticalScrollbar.scrollTop()).toBe 100 - expect(editor.visibleLines.css('-webkit-transform')).toBe 'matrix(1, 0, 0, 1, 0, -100)' - expect(editor.gutter.lineNumbers.css('-webkit-transform')).toBe 'matrix(1, 0, 0, 1, 0, -100)' - - editor.scrollTop(150) - expect(editor.verticalScrollbar.scrollTop()).toBe 150 - expect(editor.visibleLines.css('-webkit-transform')).toBe 'matrix(1, 0, 0, 1, 0, -150)' - expect(editor.gutter.lineNumbers.css('-webkit-transform')).toBe 'matrix(1, 0, 0, 1, 0, -150)' - - it "does not allow negative scrollTops to be assigned", -> - editor.scrollTop(-100) - expect(editor.scrollTop()).toBe 0 - - it "doesn't do anything if the scrollTop hasn't changed", -> - editor.scrollTop(100) - spyOn(editor.verticalScrollbar, 'scrollTop') - spyOn(editor.visibleLines, 'css') - spyOn(editor.gutter.lineNumbers, 'css') - - editor.scrollTop(100) - expect(editor.verticalScrollbar.scrollTop).not.toHaveBeenCalled() - expect(editor.visibleLines.css).not.toHaveBeenCalled() - expect(editor.gutter.lineNumbers.css).not.toHaveBeenCalled() - - describe "when the 'adjustVerticalScrollbar' option is false (defaults to true)", -> - it "doesn't adjust the scrollTop of the vertical scrollbar", -> - editor.scrollTop(100, adjustVerticalScrollbar: false) - expect(editor.verticalScrollbar.scrollTop()).toBe 0 - expect(editor.visibleLines.css('-webkit-transform')).toBe 'matrix(1, 0, 0, 1, 0, -100)' - expect(editor.gutter.lineNumbers.css('-webkit-transform')).toBe 'matrix(1, 0, 0, 1, 0, -100)' - - describe "when called with no argument", -> - it "returns the last assigned value or 0 if none has been assigned", -> - expect(editor.scrollTop()).toBe 0 - editor.scrollTop(50) - expect(editor.scrollTop()).toBe 50 - describe "font size", -> it "sets the initial font size based on the value assigned to the root view", -> rootView.setFontSize(20) @@ -641,7 +641,6 @@ describe "Editor", -> editor.setFontSize(10) expect(editor.visibleLines.find(".line").length).toBeGreaterThan originalLineCount - describe "cursor movement", -> describe "when the arrow keys are pressed", -> it "moves the cursor by a single row/column", -> From 0e09659d79ed5d82bbf8f1325f6b4cc395b68416 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Thu, 17 May 2012 10:16:20 -0700 Subject: [PATCH 10/61] Gutter adjusts its width to account for the width of the last line number --- spec/app/editor-spec.coffee | 9 +++++++++ src/app/gutter.coffee | 4 +++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 246720f50..f0fe1a591 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -558,6 +558,15 @@ describe "Editor", -> expect(editor.gutter.find('.line-number:first').text()).toBe "2" expect(editor.gutter.find('.line-number:last').text()).toBe "7" + describe "width", -> + it "sets the width based on last line number", -> + expect(editor.gutter.lineNumbers.outerWidth()).toBe editor.charWidth * 2 + + it "updates the width when total number of lines gains a digit", -> + oneHundredLines = [0..100].join("\n") + editor.insertText(oneHundredLines) + expect(editor.gutter.lineNumbers.outerWidth()).toBe editor.charWidth * 3 + describe "when wrapping is on", -> it "renders a • instead of line number for wrapped portions of lines", -> editor.setMaxLineLength(50) diff --git a/src/app/gutter.coffee b/src/app/gutter.coffee index fbefad648..04d0e9deb 100644 --- a/src/app/gutter.coffee +++ b/src/app/gutter.coffee @@ -17,4 +17,6 @@ class Gutter extends View @lineNumbers[0].innerHTML = $$$ -> for row in rows @div {class: 'line-number'}, if row == lastScreenRow then '•' else row + 1 - lastScreenRow = row \ No newline at end of file + lastScreenRow = row + + @lineNumbers.width(editor.getLastScreenRow().toString().length * editor.charWidth) From 52fe580cf60a53e1dd06d48bf99aa6b774be4605 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Thu, 17 May 2012 10:17:05 -0700 Subject: [PATCH 11/61] Test that inserting lines re-renders the gutter --- spec/app/editor-spec.coffee | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index f0fe1a591..63327acfd 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -567,6 +567,12 @@ describe "Editor", -> editor.insertText(oneHundredLines) expect(editor.gutter.lineNumbers.outerWidth()).toBe editor.charWidth * 3 + describe "when the insertion of lines causes the editor to scroll", -> + it "renders line numbers correctly", -> + oneHundredLines = [0..100].join("\n") + editor.insertText(oneHundredLines) + expect(editor.gutter.lineNumbers.find('.line-number').length).toBe 6 + describe "when wrapping is on", -> it "renders a • instead of line number for wrapped portions of lines", -> editor.setMaxLineLength(50) From 857486bcf13893172555b8b198c3e9a64d8aef5a Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Thu, 17 May 2012 10:34:54 -0700 Subject: [PATCH 12/61] After buffer changes, remove any lines that are beyond the current last rendered screen row. --- spec/app/editor-spec.coffee | 1 + src/app/editor.coffee | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 63327acfd..1c111fbb7 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -381,6 +381,7 @@ describe "Editor", -> describe "when soft-wrap is enabled", -> beforeEach -> + setEditorHeightInLines(editor, 20) setEditorWidthInChars(editor, 50) editor.setSoftWrap(true) expect(editor.renderer.maxLineLength).toBe 50 diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 28e58e2bb..389997a7e 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -430,8 +430,11 @@ class Editor extends View @verticalScrollbarContent.height(@lineHeight * @screenLineCount()) rowDelta = newScreenRange.end.row - oldScreenRange.end.row - @lastRenderedScreenRow += rowDelta - @updateVisibleLines() if rowDelta < 0 + if rowDelta > 0 + @removeLineElements(@lastRenderedScreenRow + 1, @lastRenderedScreenRow + rowDelta) + else + @lastRenderedScreenRow += rowDelta + @updateVisibleLines() buildLineElements: (startRow, endRow) -> charWidth = @charWidth From cfb49aeba92ddd7e3e9ffb0f1785d0d076c7c3e2 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Thu, 17 May 2012 16:04:06 -0700 Subject: [PATCH 13/61] two-hundred --- spec/fixtures/two-hundred.txt | 201 ++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 spec/fixtures/two-hundred.txt diff --git a/spec/fixtures/two-hundred.txt b/spec/fixtures/two-hundred.txt new file mode 100644 index 000000000..49c20cb9e --- /dev/null +++ b/spec/fixtures/two-hundred.txt @@ -0,0 +1,201 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 ] \ No newline at end of file From fdf9676ea1d34737b8b2b16b931469d5a79a87bf Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Thu, 17 May 2012 16:04:57 -0700 Subject: [PATCH 14/61] Add scrollBottom to editor --- src/app/editor.coffee | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 389997a7e..4173abd8f 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -275,8 +275,11 @@ class Editor extends View if options?.adjustVerticalScrollbar ? true @verticalScrollbar.scrollTop(scrollTop) - scrollBottom: -> - @scrollTop() + @scrollView.height() + scrollBottom: (scrollBottom) -> + if scrollBottom? + @scrollTop(scrollBottom - @scrollView.height()) + else + @scrollTop() + @scrollView.height() renderVisibleLines: -> @clearLines() From 608ccc38e814efd7d6bafb574956ee0b987185f3 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Thu, 17 May 2012 16:06:39 -0700 Subject: [PATCH 15/61] Update rendered lines when buffer changes occur outside/straddling rendered region --- spec/app/editor-spec.coffee | 82 +++++++++++++++++++++++++++++++++++++ src/app/editor.coffee | 26 ++++++++++-- 2 files changed, 104 insertions(+), 4 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 1c111fbb7..5c2e66ff5 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -2236,6 +2236,88 @@ describe "Editor", -> expect(selections[0].getBufferRange()).toEqual [[1, 12], [1, 12]] expect(selections[1].getBufferRange()).toEqual [[1, 30], [1, 30]] + describe "when lines are added", -> + beforeEach -> + setEditorHeightInLines(editor, 5) + spyOn(editor, "scrollTo") + + describe "when the change the precedes the first rendered row", -> + it "inserts and removes rendered lines to account for upstream change", -> + editor.scrollBottom(editor.scrollView.prop('scrollHeight')) + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12) + + buffer.change([[1,0], [3,0]], "1\n2\n3\n") + expect(editor.visibleLines.find(".line").length).toBe 5 + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12) + + describe "when the change straddles the first rendered row", -> + it "doesn't render rows that were not previously rendered", -> + editor.scrollBottom(editor.scrollView.prop('scrollHeight')) + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12) + + buffer.change([[2,0], [7,0]], "2\n3\n4\n5\n6\n7\n8\n9\n") + expect(editor.visibleLines.find(".line").length).toBe 5 + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12) + + describe "when the change the straddles the last rendered row", -> + it "doesn't render rows that were not previously rendered", -> + buffer.change([[2,0], [7,0]], "2\n3\n4\n5\n6\n7\n8\n") + expect(editor.visibleLines.find(".line").length).toBe 5 + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(0) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(4) + + describe "when the change the follows the last rendered row", -> + it "does not change the rendered lines", -> + buffer.change([[12,0], [12,0]], "12\n13\n14\n") + expect(editor.visibleLines.find(".line").length).toBe 5 + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(0) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(4) + + describe "when lines are removed", -> + beforeEach -> + setEditorHeightInLines(editor, 5) + spyOn(editor, "scrollTo") + + describe "when the change the precedes the first rendered row", -> + it "removes rendered lines to account for upstream change", -> + editor.scrollBottom(editor.scrollView.prop('scrollHeight')) + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12) + + buffer.change([[1,0], [2,0]], "") + expect(editor.visibleLines.find(".line").length).toBe 4 + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(11) + + describe "when the change straddles the first rendered row", -> + it "renders the correct rows", -> + editor.scrollBottom(editor.scrollView.prop('scrollHeight')) + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12) + + buffer.change([[7,0], [11,0]], "1\n2\n") + expect(editor.visibleLines.find(".line").length).toBe 3 + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(10) + + describe "when the change the straddles the last rendered row", -> + it "renders the correct rows", -> + buffer.change([[2,0], [7,0]], "") + expect(editor.visibleLines.find(".line").length).toBe 5 + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(0) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(4) + + describe "when the change the follows the last rendered row", -> + it "does not change the rendered lines", -> + buffer.change([[12,0], [12,0]], "") + expect(editor.visibleLines.find(".line").length).toBe 5 + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(0) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(4) + describe "when the editor is attached to the dom", -> it "calculates line height and char width and updates the pixel position of the cursor", -> expect(editor.lineHeight).toBeNull() diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 4173abd8f..19bde6340 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -296,8 +296,6 @@ class Editor extends View firstVisibleScreenRow = @getFirstVisibleScreenRow() lastVisibleScreenRow = @getLastVisibleScreenRow() - return if @firstRenderedScreenRow <= firstVisibleScreenRow and @lastRenderedScreenRow >= lastVisibleScreenRow - @gutter.renderLineNumbers(firstVisibleScreenRow, lastVisibleScreenRow) if firstVisibleScreenRow > @firstRenderedScreenRow @@ -428,11 +426,31 @@ class Editor extends View unless newScreenRange.isSingleLine() and newScreenRange.coversSameRows(oldScreenRange) @gutter.renderLineNumbers(@getFirstVisibleScreenRow(), @getLastVisibleScreenRow()) - lineElements = @buildLineElements(newScreenRange.start.row, newScreenRange.end.row) - @replaceLineElements(oldScreenRange.start.row, oldScreenRange.end.row, lineElements) @verticalScrollbarContent.height(@lineHeight * @screenLineCount()) + return if oldScreenRange.start.row > @lastRenderedScreenRow + + newScreenRange = newScreenRange.copy() + oldScreenRange = oldScreenRange.copy() + endOfShortestRange = Math.min(oldScreenRange.end.row, newScreenRange.end.row) + + delta = @firstRenderedScreenRow - endOfShortestRange + if delta > 0 + newScreenRange.start.row += delta + newScreenRange.end.row += delta + oldScreenRange.start.row += delta + oldScreenRange.end.row += delta + + newScreenRange.start.row = Math.max(newScreenRange.start.row, @firstRenderedScreenRow) + oldScreenRange.start.row = Math.max(oldScreenRange.start.row, @firstRenderedScreenRow) + newScreenRange.end.row = Math.min(newScreenRange.end.row, @lastRenderedScreenRow) + oldScreenRange.end.row = Math.min(oldScreenRange.end.row, @lastRenderedScreenRow) + + lineElements = @buildLineElements(newScreenRange.start.row, newScreenRange.end.row) + @replaceLineElements(oldScreenRange.start.row, oldScreenRange.end.row, lineElements) + rowDelta = newScreenRange.end.row - oldScreenRange.end.row + if rowDelta > 0 @removeLineElements(@lastRenderedScreenRow + 1, @lastRenderedScreenRow + rowDelta) else From 06d1f8a7185e4bd41f2bea5f41a8ae0a7dabb580 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Thu, 17 May 2012 17:04:11 -0700 Subject: [PATCH 16/61] Create a failing fold spec --- spec/app/editor-spec.coffee | 12 ++++++ spec/fixtures/two-hundred.txt | 81 +++++++++++++++++------------------ 2 files changed, 52 insertions(+), 41 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 5c2e66ff5..562969a74 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -2464,6 +2464,18 @@ describe "Editor", -> editor.createFold([[1, 0], [1, 30]]) expect(editor.visibleLines.find('.line:eq(1)').html()).toMatch / $/ + describe "???", -> + fit "should render lines correctly", -> + setEditorHeightInLines(editor, 20) + setEditorWidthInChars(editor, 50) + editor.setBuffer(new Buffer(require.resolve('fixtures/two-hundred.txt'))) + editor.toggleSoftWrap() + editor.createFold([[4, 90], [6, 1]]) + editor.createFold([[6, 1], [8, 1]]) + + expect(editor.renderer.lineForRow(6).text).toBe "9" + expect(editor.renderer.lineForRow(7).text).toMatch /^10\.+/ + describe "editor-path-change event", -> it "emits event when buffer's path is changed", -> editor = new Editor diff --git a/spec/fixtures/two-hundred.txt b/spec/fixtures/two-hundred.txt index 49c20cb9e..fe9a3eb8a 100644 --- a/spec/fixtures/two-hundred.txt +++ b/spec/fixtures/two-hundred.txt @@ -1,201 +1,200 @@ -0 1 2 3 4 -5 +5.................................................................................................... 6 7 8 9 -10 +10.................................................................................................... 11 12 13 14 -15 +15.................................................................................................... 16 17 18 19 -20 +20.................................................................................................... 21 22 23 24 -25 +25.................................................................................................... 26 27 28 29 -30 +30.................................................................................................... 31 32 33 34 -35 +35.................................................................................................... 36 37 38 39 -40 +40.................................................................................................... 41 42 43 44 -45 +45.................................................................................................... 46 47 48 49 -50 +50.................................................................................................... 51 52 53 54 -55 +55.................................................................................................... 56 57 58 59 -60 +60.................................................................................................... 61 62 63 64 -65 +65.................................................................................................... 66 67 68 69 -70 +70.................................................................................................... 71 72 73 74 -75 +75.................................................................................................... 76 77 78 79 -80 +80.................................................................................................... 81 82 83 84 -85 +85.................................................................................................... 86 87 88 89 -90 +90.................................................................................................... 91 92 93 94 -95 +95.................................................................................................... 96 97 98 99 -100 +100.................................................................................................... 101 102 103 104 -105 +105.................................................................................................... 106 107 108 109 -110 +110.................................................................................................... 111 112 113 114 -115 +115.................................................................................................... 116 117 118 119 -120 +120.................................................................................................... 121 122 123 124 -125 +125.................................................................................................... 126 127 128 129 -130 +130.................................................................................................... 131 132 133 134 -135 +135.................................................................................................... 136 137 138 139 -140 +140.................................................................................................... 141 142 143 144 -145 +145.................................................................................................... 146 147 148 149 -150 +150.................................................................................................... 151 152 153 154 -155 +155.................................................................................................... 156 157 158 159 -160 +160.................................................................................................... 161 162 163 164 -165 +165.................................................................................................... 166 167 168 169 -170 +170.................................................................................................... 171 172 173 174 -175 +175.................................................................................................... 176 177 178 179 -180 +180.................................................................................................... 181 182 183 184 -185 +185.................................................................................................... 186 187 188 189 -190 +190.................................................................................................... 191 192 193 194 -195 +195.................................................................................................... 196 197 198 199 -200 ] \ No newline at end of file +200.................................................................................................... \ No newline at end of file From 87e6b0c5171510b1c11a4ba3fb3ea088f52c23fc Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Fri, 18 May 2012 09:43:44 -0700 Subject: [PATCH 17/61] a zero-index fixture file --- spec/fixtures/two-hundred-zero-indexed.txt | 200 +++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 spec/fixtures/two-hundred-zero-indexed.txt diff --git a/spec/fixtures/two-hundred-zero-indexed.txt b/spec/fixtures/two-hundred-zero-indexed.txt new file mode 100644 index 000000000..95ea4c30f --- /dev/null +++ b/spec/fixtures/two-hundred-zero-indexed.txt @@ -0,0 +1,200 @@ +0 +1 +2 +3 +4.................................................................................................... +5 +6 +7 +8 +9.................................................................................................... +10 +11 +12 +13 +14.................................................................................................... +15 +16 +17 +18 +19.................................................................................................... +20 +21 +22 +23 +24.................................................................................................... +25 +26 +27 +28 +29.................................................................................................... +30 +31 +32 +33 +34.................................................................................................... +35 +36 +37 +38 +39.................................................................................................... +40 +41 +42 +43 +44.................................................................................................... +45 +46 +47 +48 +49.................................................................................................... +50 +51 +52 +53 +54.................................................................................................... +55 +56 +57 +58 +59.................................................................................................... +60 +61 +62 +63 +64.................................................................................................... +65 +66 +67 +68 +69.................................................................................................... +70 +71 +72 +73 +74.................................................................................................... +75 +76 +77 +78 +79.................................................................................................... +80 +81 +82 +83 +84.................................................................................................... +85 +86 +87 +88 +89.................................................................................................... +90 +91 +92 +93 +94.................................................................................................... +95 +96 +97 +98 +99.................................................................................................... +100 +101 +102 +103 +104.................................................................................................... +105 +106 +107 +108 +109.................................................................................................... +110 +111 +112 +113 +114.................................................................................................... +115 +116 +117 +118 +119.................................................................................................... +120 +121 +122 +123 +124.................................................................................................... +125 +126 +127 +128 +129.................................................................................................... +130 +131 +132 +133 +134.................................................................................................... +135 +136 +137 +138 +139.................................................................................................... +140 +141 +142 +143 +144.................................................................................................... +145 +146 +147 +148 +149.................................................................................................... +150 +151 +152 +153 +154.................................................................................................... +155 +156 +157 +158 +159.................................................................................................... +160 +161 +162 +163 +164.................................................................................................... +165 +166 +167 +168 +169.................................................................................................... +170 +171 +172 +173 +174.................................................................................................... +175 +176 +177 +178 +179.................................................................................................... +180 +181 +182 +183 +184.................................................................................................... +185 +186 +187 +188 +189.................................................................................................... +190 +191 +192 +193 +194.................................................................................................... +195 +196 +197 +198 +199.................................................................................................... \ No newline at end of file From 20c9b61b91bf03d1e1b6322dcc2282c77ddf05dc Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Fri, 18 May 2012 12:22:40 -0600 Subject: [PATCH 18/61] Fix issue where creating a second fold on a wrapped line fragment caused a duplicate line to be rendered --- spec/app/editor-spec.coffee | 12 ---- spec/app/renderer-spec.coffee | 16 ++--- spec/fixtures/two-hundred-zero-indexed.txt | 80 +++++++++++----------- src/app/editor.coffee | 4 +- src/app/renderer.coffee | 32 +++++---- 5 files changed, 68 insertions(+), 76 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 562969a74..5c2e66ff5 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -2464,18 +2464,6 @@ describe "Editor", -> editor.createFold([[1, 0], [1, 30]]) expect(editor.visibleLines.find('.line:eq(1)').html()).toMatch / $/ - describe "???", -> - fit "should render lines correctly", -> - setEditorHeightInLines(editor, 20) - setEditorWidthInChars(editor, 50) - editor.setBuffer(new Buffer(require.resolve('fixtures/two-hundred.txt'))) - editor.toggleSoftWrap() - editor.createFold([[4, 90], [6, 1]]) - editor.createFold([[6, 1], [8, 1]]) - - expect(editor.renderer.lineForRow(6).text).toBe "9" - expect(editor.renderer.lineForRow(7).text).toMatch /^10\.+/ - describe "editor-path-change event", -> it "emits event when buffer's path is changed", -> editor = new Editor diff --git a/spec/app/renderer-spec.coffee b/spec/app/renderer-spec.coffee index 5feb36849..7fcbd4960 100644 --- a/spec/app/renderer-spec.coffee +++ b/spec/app/renderer-spec.coffee @@ -125,9 +125,9 @@ describe "Renderer", -> expect(renderer.lineForRow(6).text).toBe ' }' renderer.createFold([[6, 56], [8, 15]]) - expect(renderer.lineForRow(6).text).toBe 'right.push(...(left).concat(pivot).concat(sort(rig' - expect(renderer.lineForRow(7).text).toBe 'ht));' - expect(renderer.lineForRow(8).text).toBe ' };' + expect(renderer.lineForRow(5).text).toBe 'right.push(...(left).concat(pivot).concat(sort(rig' + expect(renderer.lineForRow(6).text).toBe 'ht));' + expect(renderer.lineForRow(7).text).toBe ' };' describe "when there is a fold placeholder ending at the max length boundary", -> it "wraps the line after the fold placeholder", -> @@ -460,7 +460,6 @@ describe "Renderer", -> expect(event.newRange).toEqual [[4, 0], [4, 59]] fold2.destroy() - expect(renderer.lineForRow(4).text).toBe ' while(items.length > 0) {...abc}' describe "when the old range is inside a fold", -> @@ -495,11 +494,10 @@ describe "Renderer", -> buffer.change([[4, 25], [7, 5]], '4)') expect(renderer.lineForRow(4).text).toBe ' while(items.length > 4)...concat(sort(right));' - # expect(changeHandler).toHaveBeenCalled() - # [[event]] = changeHandler.argsForCall - # expect(event.oldRange).toEqual [[4, 0], [4, 56]] - # expect(event.newRange).toEqual [[4, 0], [4, 60]] - + expect(changeHandler).toHaveBeenCalled() + [[event]] = changeHandler.argsForCall + expect(event.oldRange).toEqual [[4, 0], [4, 56]] + expect(event.newRange).toEqual [[4, 0], [4, 51]] describe "position translation", -> describe "when there is single fold spanning multiple lines", -> diff --git a/spec/fixtures/two-hundred-zero-indexed.txt b/spec/fixtures/two-hundred-zero-indexed.txt index 95ea4c30f..9001290d1 100644 --- a/spec/fixtures/two-hundred-zero-indexed.txt +++ b/spec/fixtures/two-hundred-zero-indexed.txt @@ -2,199 +2,199 @@ 1 2 3 -4.................................................................................................... +4---------------------------------------------------------------------------------------------------- 5 6 7 8 -9.................................................................................................... +9---------------------------------------------------------------------------------------------------- 10 11 12 13 -14.................................................................................................... +14---------------------------------------------------------------------------------------------------- 15 16 17 18 -19.................................................................................................... +19---------------------------------------------------------------------------------------------------- 20 21 22 23 -24.................................................................................................... +24---------------------------------------------------------------------------------------------------- 25 26 27 28 -29.................................................................................................... +29---------------------------------------------------------------------------------------------------- 30 31 32 33 -34.................................................................................................... +34---------------------------------------------------------------------------------------------------- 35 36 37 38 -39.................................................................................................... +39---------------------------------------------------------------------------------------------------- 40 41 42 43 -44.................................................................................................... +44---------------------------------------------------------------------------------------------------- 45 46 47 48 -49.................................................................................................... +49---------------------------------------------------------------------------------------------------- 50 51 52 53 -54.................................................................................................... +54---------------------------------------------------------------------------------------------------- 55 56 57 58 -59.................................................................................................... +59---------------------------------------------------------------------------------------------------- 60 61 62 63 -64.................................................................................................... +64---------------------------------------------------------------------------------------------------- 65 66 67 68 -69.................................................................................................... +69---------------------------------------------------------------------------------------------------- 70 71 72 73 -74.................................................................................................... +74---------------------------------------------------------------------------------------------------- 75 76 77 78 -79.................................................................................................... +79---------------------------------------------------------------------------------------------------- 80 81 82 83 -84.................................................................................................... +84---------------------------------------------------------------------------------------------------- 85 86 87 88 -89.................................................................................................... +89---------------------------------------------------------------------------------------------------- 90 91 92 93 -94.................................................................................................... +94---------------------------------------------------------------------------------------------------- 95 96 97 98 -99.................................................................................................... +99---------------------------------------------------------------------------------------------------- 100 101 102 103 -104.................................................................................................... +104---------------------------------------------------------------------------------------------------- 105 106 107 108 -109.................................................................................................... +109---------------------------------------------------------------------------------------------------- 110 111 112 113 -114.................................................................................................... +114---------------------------------------------------------------------------------------------------- 115 116 117 118 -119.................................................................................................... +119---------------------------------------------------------------------------------------------------- 120 121 122 123 -124.................................................................................................... +124---------------------------------------------------------------------------------------------------- 125 126 127 128 -129.................................................................................................... +129---------------------------------------------------------------------------------------------------- 130 131 132 133 -134.................................................................................................... +134---------------------------------------------------------------------------------------------------- 135 136 137 138 -139.................................................................................................... +139---------------------------------------------------------------------------------------------------- 140 141 142 143 -144.................................................................................................... +144---------------------------------------------------------------------------------------------------- 145 146 147 148 -149.................................................................................................... +149---------------------------------------------------------------------------------------------------- 150 151 152 153 -154.................................................................................................... +154---------------------------------------------------------------------------------------------------- 155 156 157 158 -159.................................................................................................... +159---------------------------------------------------------------------------------------------------- 160 161 162 163 -164.................................................................................................... +164---------------------------------------------------------------------------------------------------- 165 166 167 168 -169.................................................................................................... +169---------------------------------------------------------------------------------------------------- 170 171 172 173 -174.................................................................................................... +174---------------------------------------------------------------------------------------------------- 175 176 177 178 -179.................................................................................................... +179---------------------------------------------------------------------------------------------------- 180 181 182 183 -184.................................................................................................... +184---------------------------------------------------------------------------------------------------- 185 186 187 188 -189.................................................................................................... +189---------------------------------------------------------------------------------------------------- 190 191 192 193 -194.................................................................................................... +194---------------------------------------------------------------------------------------------------- 195 196 197 198 -199.................................................................................................... \ No newline at end of file +199---------------------------------------------------------------------------------------------------- diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 19bde6340..74188b4fc 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -769,5 +769,5 @@ class Editor extends View for cursor in @getCursors() do (cursor) -> cursor.resetCursorAnimation() - logLines: -> - @renderer.logLines() + logLines: (start, end) -> + @renderer.logLines(start, end) diff --git a/src/app/renderer.coffee b/src/app/renderer.coffee index af48284ea..17b75ef11 100644 --- a/src/app/renderer.coffee +++ b/src/app/renderer.coffee @@ -58,12 +58,17 @@ class Renderer fold = new Fold(this, bufferRange) @registerFold(bufferRange.start.row, fold) + # If this fold starts on a screen line that contains other folds, we'll + # need to the re-render the screen lines corresponding to these preceding + # folds as well. So we alter the start row of the buffer range we + # are going to change to the buffer row of the first fold in a potential + # series of folds that ends on the current fold's starting buffer row. + bufferRange = bufferRange.copy() + bufferRange.start.row = @bufferRowForScreenRow(@screenRowForBufferRow(bufferRange.start.row)) + oldScreenRange = @screenLineRangeForBufferRange(bufferRange) lines = @buildLineForBufferRow(bufferRange.start.row) - @lineMap.replaceScreenRows( - oldScreenRange.start.row, - oldScreenRange.end.row, - lines) + @lineMap.replaceScreenRows(oldScreenRange.start.row, oldScreenRange.end.row, lines) newScreenRange = @screenLineRangeForBufferRange(bufferRange) @trigger 'change', oldRange: oldScreenRange, newRange: newScreenRange @@ -71,8 +76,10 @@ class Renderer fold destroyFold: (fold) -> - bufferRange = fold.getRange() + bufferRange = fold.getRange().copy() @unregisterFold(bufferRange.start.row, fold) + + bufferRange.start.row = @bufferRowForScreenRow(@screenRowForBufferRow(bufferRange.start.row)) startScreenRow = @screenRowForBufferRow(bufferRange.start.row) oldScreenRange = @screenLineRangeForBufferRange(bufferRange) @@ -119,8 +126,11 @@ class Renderer @handleHighlighterChange(@lastHighlighterChangeEvent) handleHighlighterChange: (e) -> - oldBufferRange = e.oldRange - newBufferRange = e.newRange + oldBufferRange = e.oldRange.copy() + newBufferRange = e.newRange.copy() + + oldBufferRange.start.row = @bufferRowForScreenRow(@screenRowForBufferRow(oldBufferRange.start.row)) + newBufferRange.start.row = @bufferRowForScreenRow(@screenRowForBufferRow(newBufferRange.start.row)) oldScreenRange = @screenLineRangeForBufferRange(oldBufferRange) newScreenLines = @buildLinesForBufferRows(newBufferRange.start.row, newBufferRange.end.row) @@ -136,7 +146,6 @@ class Renderer lineFragments = [] startBufferColumn = null currentScreenLineLength = 0 - startBufferRow = @foldStartRowForBufferRow(startBufferRow) loop break if startBufferRow > endBufferRow and not startBufferColumn? @@ -175,9 +184,6 @@ class Renderer lineFragments - foldStartRowForBufferRow: (bufferRow) -> - @bufferRowForScreenRow(@screenRowForBufferRow(bufferRow)) - findWrapColumn: (line, maxLineLength) -> return unless line.length > maxLineLength @@ -232,7 +238,7 @@ class Renderer @highlighter.destroy() @buffer.off ".renderer#{@id}" - logLines: -> - @lineMap.logLines() + logLines: (start, end) -> + @lineMap.logLines(start, end) _.extend Renderer.prototype, EventEmitter From 8e9db80b58428d6005a235d94832421dd5e12b58 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Fri, 18 May 2012 11:59:54 -0700 Subject: [PATCH 19/61] wip: another failing fold test --- spec/app/editor-spec.coffee | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 5c2e66ff5..4d5711546 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -2424,6 +2424,18 @@ describe "Editor", -> beforeEach -> editor.attachToDom() + describe "???", -> + fit "won't fuck up", -> + setEditorHeightInLines(editor, 50) + editor.setBuffer(new Buffer(require.resolve('fixtures/two-hundred.txt'))) + editor.toggleSoftWrap() + fold8 = editor.createFold([[163, 3], [169, 33]]) + fold9 = editor.createFold([[169, 34], [184, 10]]) + editor.destroyFold(fold8.id) + + editor.scrollBottom(editor.scrollView.prop('scrollHeight')) + + describe "when a fold-selection event is triggered", -> it "folds the selected text and moves the cursor to just after the placeholder, then treats the placeholder as a single character", -> editor.getSelection().setBufferRange(new Range([4, 29], [7, 4])) From 4c92be669e1299a9442314a78eaae2ac6f2bef4c Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Fri, 18 May 2012 14:12:41 -0700 Subject: [PATCH 20/61] Remove non-passing unneeded spec --- spec/app/editor-spec.coffee | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 4d5711546..5c2e66ff5 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -2424,18 +2424,6 @@ describe "Editor", -> beforeEach -> editor.attachToDom() - describe "???", -> - fit "won't fuck up", -> - setEditorHeightInLines(editor, 50) - editor.setBuffer(new Buffer(require.resolve('fixtures/two-hundred.txt'))) - editor.toggleSoftWrap() - fold8 = editor.createFold([[163, 3], [169, 33]]) - fold9 = editor.createFold([[169, 34], [184, 10]]) - editor.destroyFold(fold8.id) - - editor.scrollBottom(editor.scrollView.prop('scrollHeight')) - - describe "when a fold-selection event is triggered", -> it "folds the selected text and moves the cursor to just after the placeholder, then treats the placeholder as a single character", -> editor.getSelection().setBufferRange(new Range([4, 29], [7, 4])) From 39e02bbf9f015661780451480695e6761931dc8d Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Fri, 18 May 2012 15:15:44 -0700 Subject: [PATCH 21/61] wip: what nathan and corey did --- spec/app/editor-spec.coffee | 47 ++--- spec/app/renderer-spec.coffee | 29 +-- spec/fixtures/two-hundred-zero-indexed.txt | 200 --------------------- spec/fixtures/two-hundred.txt | 160 ++++++++--------- src/app/editor.coffee | 4 +- src/app/fold.coffee | 60 ++++--- src/app/renderer.coffee | 47 ++--- src/app/selection.coffee | 4 +- 8 files changed, 164 insertions(+), 387 deletions(-) delete mode 100644 spec/fixtures/two-hundred-zero-indexed.txt diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 5c2e66ff5..6c75ff020 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -1,4 +1,4 @@ -RootView = require 'root-view' + RootView = require 'root-view' Buffer = require 'buffer' Editor = require 'editor' Range = require 'range' @@ -2422,47 +2422,36 @@ describe "Editor", -> describe "folding", -> beforeEach -> + editor.setBuffer(new Buffer(require.resolve('fixtures/two-hundred.txt'))) editor.attachToDom() describe "when a fold-selection event is triggered", -> - it "folds the selected text and moves the cursor to just after the placeholder, then treats the placeholder as a single character", -> + fit "folds the lines covered by the selection into a single line with a fold class", -> editor.getSelection().setBufferRange(new Range([4, 29], [7, 4])) editor.trigger 'fold-selection' - expect(editor.visibleLines.find('.line:eq(4)').find('.fold-placeholder')).toExist() - expect(editor.visibleLines.find('.line:eq(5)').text()).toBe ' return sort(left).concat(pivot).concat(sort(right));' + expect(editor.visibleLines.find('.line:eq(4)').toHaveClass('fold') + expect(editor.visibleLines.find('.line:eq(5)').text()).toBe '8' expect(editor.getSelection().isEmpty()).toBeTruthy() - expect(editor.getCursorScreenPosition()).toEqual [4, 32] + expect(editor.getCursorScreenPosition()).toEqual [5, 0] - editor.setCursorBufferPosition([9, 4]) - expect(editor.getCursorScreenPosition()).toEqual [6, 4] + # describe "when a fold placeholder is clicked", -> + # it "removes the associated fold and places the cursor at its beginning", -> + # editor.getSelection().setBufferRange(new Range([4, 29], [7, 4])) + # editor.trigger 'fold-selection' - editor.insertText('x') - expect(editor.getCursorScreenPosition()).toEqual [6, 5] - expect(editor.getCursorBufferPosition()).toEqual [9, 5] + # editor.find('.fold-placeholder .ellipsis').mousedown() - editor.setCursorScreenPosition([4, 30]) - expect(editor.getCursorScreenPosition()).toEqual [4, 29] - editor.moveCursorRight() - expect(editor.getCursorScreenPosition()).toEqual [4, 32] + # expect(editor.find('.fold-placeholder')).not.toExist() + # expect(editor.visibleLines.find('.line:eq(5)').text()).toBe ' current = items.shift();' - describe "when a fold placeholder is clicked", -> - it "removes the associated fold and places the cursor at its beginning", -> - editor.getSelection().setBufferRange(new Range([4, 29], [7, 4])) - editor.trigger 'fold-selection' + # expect(editor.getCursorBufferPosition()).toEqual [4, 29] - editor.find('.fold-placeholder .ellipsis').mousedown() - - expect(editor.find('.fold-placeholder')).not.toExist() - expect(editor.visibleLines.find('.line:eq(5)').text()).toBe ' current = items.shift();' - - expect(editor.getCursorBufferPosition()).toEqual [4, 29] - - describe "when there is nothing on a line except a fold placeholder", -> - it "follows the placeholder with a non-breaking space to ensure the line has the proper height", -> - editor.createFold([[1, 0], [1, 30]]) - expect(editor.visibleLines.find('.line:eq(1)').html()).toMatch / $/ + # describe "when there is nothing on a line except a fold placeholder", -> + # it "follows the placeholder with a non-breaking space to ensure the line has the proper height", -> + # editor.createFold([[1, 0], [1, 30]]) + # expect(editor.visibleLines.find('.line:eq(1)').html()).toMatch / $/ describe "editor-path-change event", -> it "emits event when buffer's path is changed", -> diff --git a/spec/app/renderer-spec.coffee b/spec/app/renderer-spec.coffee index 7fcbd4960..4d245b9d8 100644 --- a/spec/app/renderer-spec.coffee +++ b/spec/app/renderer-spec.coffee @@ -222,31 +222,34 @@ describe "Renderer", -> expect(event.newRange).toEqual([[0, 0], [18, 2]]) describe "folding", -> + beforeEach -> + buffer = new Buffer(require.resolve 'fixtures/two-hundred.js') + renderer = new Renderer(buffer, {tabText}) + describe "when folds are created and destroyed", -> describe "when a fold spans multiple lines", -> - it "replaces the lines spanned by the fold with a single line containing a placeholder", -> - previousLine4Text = renderer.lineForRow(4).text - previousLine5Text = renderer.lineForRow(5).text + fit "replaces the lines spanned by the fold with a single line with a html class of 'fold'", -> + fold = renderer.createFold(4, 7) - fold = renderer.createFold([[4, 29], [7, 4]]) - - expect(renderer.lineForRow(4).text).toBe ' while(items.length > 0) {...}' - expect(renderer.lineForRow(5).text).toBe ' return sort(left).concat(pivot).concat(sort(right));' + expect(renderer.lineForRow(4).text).toHaveClass('fold') + expect(renderer.lineForRow(4).text).toMatch /^4-+/ + expect(renderer.lineForRow(5).text).toBe '8' expect(changeHandler).toHaveBeenCalled() [event] = changeHandler.argsForCall[0] - expect(event.oldRange).toEqual [[4, 0], [7, 5]] - expect(event.newRange).toEqual [[4, 0], [4, 33]] + expect(event.oldRange).toEqual [[4, 0], [7, 1]] + expect(event.newRange).toEqual [[4, 0], [4, 101]] changeHandler.reset() fold.destroy() - expect(renderer.lineForRow(4).text).toBe previousLine4Text - expect(renderer.lineForRow(5).text).toBe previousLine5Text + expect(renderer.lineForRow(4).text).not.toHaveClass('fold') + expect(renderer.lineForRow(4).text).toMatch /^4-+/ + expect(renderer.lineForRow(5).text).toBe '5' expect(changeHandler).toHaveBeenCalled() [[event]] = changeHandler.argsForCall - expect(event.oldRange).toEqual [[4, 0], [4, 33]] - expect(event.newRange).toEqual [[4, 0], [7, 5]] + expect(event.oldRange).toEqual [[4, 0], [4, 101]] + expect(event.newRange).toEqual [[4, 0], [7, 1]] describe "when a fold spans a single line", -> it "renders a placeholder for the folded region, but does not skip any lines", -> diff --git a/spec/fixtures/two-hundred-zero-indexed.txt b/spec/fixtures/two-hundred-zero-indexed.txt deleted file mode 100644 index 9001290d1..000000000 --- a/spec/fixtures/two-hundred-zero-indexed.txt +++ /dev/null @@ -1,200 +0,0 @@ -0 -1 -2 -3 -4---------------------------------------------------------------------------------------------------- -5 -6 -7 -8 -9---------------------------------------------------------------------------------------------------- -10 -11 -12 -13 -14---------------------------------------------------------------------------------------------------- -15 -16 -17 -18 -19---------------------------------------------------------------------------------------------------- -20 -21 -22 -23 -24---------------------------------------------------------------------------------------------------- -25 -26 -27 -28 -29---------------------------------------------------------------------------------------------------- -30 -31 -32 -33 -34---------------------------------------------------------------------------------------------------- -35 -36 -37 -38 -39---------------------------------------------------------------------------------------------------- -40 -41 -42 -43 -44---------------------------------------------------------------------------------------------------- -45 -46 -47 -48 -49---------------------------------------------------------------------------------------------------- -50 -51 -52 -53 -54---------------------------------------------------------------------------------------------------- -55 -56 -57 -58 -59---------------------------------------------------------------------------------------------------- -60 -61 -62 -63 -64---------------------------------------------------------------------------------------------------- -65 -66 -67 -68 -69---------------------------------------------------------------------------------------------------- -70 -71 -72 -73 -74---------------------------------------------------------------------------------------------------- -75 -76 -77 -78 -79---------------------------------------------------------------------------------------------------- -80 -81 -82 -83 -84---------------------------------------------------------------------------------------------------- -85 -86 -87 -88 -89---------------------------------------------------------------------------------------------------- -90 -91 -92 -93 -94---------------------------------------------------------------------------------------------------- -95 -96 -97 -98 -99---------------------------------------------------------------------------------------------------- -100 -101 -102 -103 -104---------------------------------------------------------------------------------------------------- -105 -106 -107 -108 -109---------------------------------------------------------------------------------------------------- -110 -111 -112 -113 -114---------------------------------------------------------------------------------------------------- -115 -116 -117 -118 -119---------------------------------------------------------------------------------------------------- -120 -121 -122 -123 -124---------------------------------------------------------------------------------------------------- -125 -126 -127 -128 -129---------------------------------------------------------------------------------------------------- -130 -131 -132 -133 -134---------------------------------------------------------------------------------------------------- -135 -136 -137 -138 -139---------------------------------------------------------------------------------------------------- -140 -141 -142 -143 -144---------------------------------------------------------------------------------------------------- -145 -146 -147 -148 -149---------------------------------------------------------------------------------------------------- -150 -151 -152 -153 -154---------------------------------------------------------------------------------------------------- -155 -156 -157 -158 -159---------------------------------------------------------------------------------------------------- -160 -161 -162 -163 -164---------------------------------------------------------------------------------------------------- -165 -166 -167 -168 -169---------------------------------------------------------------------------------------------------- -170 -171 -172 -173 -174---------------------------------------------------------------------------------------------------- -175 -176 -177 -178 -179---------------------------------------------------------------------------------------------------- -180 -181 -182 -183 -184---------------------------------------------------------------------------------------------------- -185 -186 -187 -188 -189---------------------------------------------------------------------------------------------------- -190 -191 -192 -193 -194---------------------------------------------------------------------------------------------------- -195 -196 -197 -198 -199---------------------------------------------------------------------------------------------------- diff --git a/spec/fixtures/two-hundred.txt b/spec/fixtures/two-hundred.txt index fe9a3eb8a..9001290d1 100644 --- a/spec/fixtures/two-hundred.txt +++ b/spec/fixtures/two-hundred.txt @@ -1,200 +1,200 @@ +0 1 2 3 -4 -5.................................................................................................... +4---------------------------------------------------------------------------------------------------- +5 6 7 8 -9 -10.................................................................................................... +9---------------------------------------------------------------------------------------------------- +10 11 12 13 -14 -15.................................................................................................... +14---------------------------------------------------------------------------------------------------- +15 16 17 18 -19 -20.................................................................................................... +19---------------------------------------------------------------------------------------------------- +20 21 22 23 -24 -25.................................................................................................... +24---------------------------------------------------------------------------------------------------- +25 26 27 28 -29 -30.................................................................................................... +29---------------------------------------------------------------------------------------------------- +30 31 32 33 -34 -35.................................................................................................... +34---------------------------------------------------------------------------------------------------- +35 36 37 38 -39 -40.................................................................................................... +39---------------------------------------------------------------------------------------------------- +40 41 42 43 -44 -45.................................................................................................... +44---------------------------------------------------------------------------------------------------- +45 46 47 48 -49 -50.................................................................................................... +49---------------------------------------------------------------------------------------------------- +50 51 52 53 -54 -55.................................................................................................... +54---------------------------------------------------------------------------------------------------- +55 56 57 58 -59 -60.................................................................................................... +59---------------------------------------------------------------------------------------------------- +60 61 62 63 -64 -65.................................................................................................... +64---------------------------------------------------------------------------------------------------- +65 66 67 68 -69 -70.................................................................................................... +69---------------------------------------------------------------------------------------------------- +70 71 72 73 -74 -75.................................................................................................... +74---------------------------------------------------------------------------------------------------- +75 76 77 78 -79 -80.................................................................................................... +79---------------------------------------------------------------------------------------------------- +80 81 82 83 -84 -85.................................................................................................... +84---------------------------------------------------------------------------------------------------- +85 86 87 88 -89 -90.................................................................................................... +89---------------------------------------------------------------------------------------------------- +90 91 92 93 -94 -95.................................................................................................... +94---------------------------------------------------------------------------------------------------- +95 96 97 98 -99 -100.................................................................................................... +99---------------------------------------------------------------------------------------------------- +100 101 102 103 -104 -105.................................................................................................... +104---------------------------------------------------------------------------------------------------- +105 106 107 108 -109 -110.................................................................................................... +109---------------------------------------------------------------------------------------------------- +110 111 112 113 -114 -115.................................................................................................... +114---------------------------------------------------------------------------------------------------- +115 116 117 118 -119 -120.................................................................................................... +119---------------------------------------------------------------------------------------------------- +120 121 122 123 -124 -125.................................................................................................... +124---------------------------------------------------------------------------------------------------- +125 126 127 128 -129 -130.................................................................................................... +129---------------------------------------------------------------------------------------------------- +130 131 132 133 -134 -135.................................................................................................... +134---------------------------------------------------------------------------------------------------- +135 136 137 138 -139 -140.................................................................................................... +139---------------------------------------------------------------------------------------------------- +140 141 142 143 -144 -145.................................................................................................... +144---------------------------------------------------------------------------------------------------- +145 146 147 148 -149 -150.................................................................................................... +149---------------------------------------------------------------------------------------------------- +150 151 152 153 -154 -155.................................................................................................... +154---------------------------------------------------------------------------------------------------- +155 156 157 158 -159 -160.................................................................................................... +159---------------------------------------------------------------------------------------------------- +160 161 162 163 -164 -165.................................................................................................... +164---------------------------------------------------------------------------------------------------- +165 166 167 168 -169 -170.................................................................................................... +169---------------------------------------------------------------------------------------------------- +170 171 172 173 -174 -175.................................................................................................... +174---------------------------------------------------------------------------------------------------- +175 176 177 178 -179 -180.................................................................................................... +179---------------------------------------------------------------------------------------------------- +180 181 182 183 -184 -185.................................................................................................... +184---------------------------------------------------------------------------------------------------- +185 186 187 188 -189 -190.................................................................................................... +189---------------------------------------------------------------------------------------------------- +190 191 192 193 -194 -195.................................................................................................... +194---------------------------------------------------------------------------------------------------- +195 196 197 198 -199 -200.................................................................................................... \ No newline at end of file +199---------------------------------------------------------------------------------------------------- diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 74188b4fc..e52e296f9 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -522,8 +522,8 @@ class Editor extends View maxLineLength ?= @calcMaxLineLength() @renderer.setMaxLineLength(maxLineLength) if maxLineLength - createFold: (range) -> - @renderer.createFold(range) + createFold: (startRow, endRow) -> + @renderer.createFold(startRow, endRow) setSoftWrap: (@softWrap, maxLineLength=undefined) -> @setMaxLineLength(maxLineLength) if @attached diff --git a/src/app/fold.coffee b/src/app/fold.coffee index 2e6bab84d..535ad762b 100644 --- a/src/app/fold.coffee +++ b/src/app/fold.coffee @@ -3,49 +3,53 @@ Range = require 'range' module.exports = class Fold @idCounter: 1 - start: null - end: null - constructor: (@lineFolder, {@start, @end}) -> + renderer: null + startRow: null + endRow: null + + constructor: (@renderer, @startRow, @endRow) -> @id = @constructor.idCounter++ destroy: -> - @lineFolder.destroyFold(this) + @renderer.destroyFold(this) getRange: -> - new Range(@start, @end) + # new Range([@startRow, 0], @endRow) + throw "Don't worry about this yet -- sobo" handleBufferChange: (event) -> - oldStartRow = @start.row + # oldStartRow = @start.row - { oldRange } = event - if oldRange.start.isLessThanOrEqual(@start) and oldRange.end.isGreaterThanOrEqual(@end) - @lineFolder.unregisterFold(oldStartRow, this) - return + # { oldRange } = event + # if oldRange.start.isLessThanOrEqual(@start) and oldRange.end.isGreaterThanOrEqual(@end) + # @renderer.unregisterFold(oldStartRow, this) + # return - changeInsideFold = @start.isLessThanOrEqual(oldRange.start) and @end.isGreaterThan(oldRange.end) + # changeInsideFold = @start.isLessThanOrEqual(oldRange.start) and @end.isGreaterThan(oldRange.end) - @start = @updateAnchorPoint(@start, event) - @end = @updateAnchorPoint(@end, event, false) + # @start = @updateAnchorPoint(@start, event) + # @end = @updateAnchorPoint(@end, event, false) - if @start.row != oldStartRow - @lineFolder.unregisterFold(oldStartRow, this) - @lineFolder.registerFold(@start.row, this) + # if @start.row != oldStartRow + # @renderer.unregisterFold(oldStartRow, this) + # @lineFolder.registerFold(@start.row, this) - changeInsideFold + # changeInsideFold updateAnchorPoint: (point, event, inclusive=true) -> - { newRange, oldRange } = event - if inclusive - return point if oldRange.end.isGreaterThan(point) - else - return point if oldRange.end.isGreaterThanOrEqual(point) + # { newRange, oldRange } = event + # if inclusive + # return point if oldRange.end.isGreaterThan(point) + # else + # return point if oldRange.end.isGreaterThanOrEqual(point) - newRange.end.add(point.subtract(oldRange.end)) + # newRange.end.add(point.subtract(oldRange.end)) compare: (other) -> - startComparison = @start.compare(other.start) - if startComparison == 0 - other.end.compare(@end) - else - startComparison + other + # startComparison = @start.compare(other.start) + # if startComparison == 0 + # other.end.compare(@end) + # else + # startComparison diff --git a/src/app/renderer.coffee b/src/app/renderer.coffee index 17b75ef11..957566386 100644 --- a/src/app/renderer.coffee +++ b/src/app/renderer.coffee @@ -51,23 +51,13 @@ class Renderer bufferRowsForScreenRows: (startRow, endRow) -> @lineMap.bufferRowsForScreenRows(startRow, endRow) - createFold: (bufferRange) -> - bufferRange = Range.fromObject(bufferRange) - return if bufferRange.isEmpty() - - fold = new Fold(this, bufferRange) - @registerFold(bufferRange.start.row, fold) - - # If this fold starts on a screen line that contains other folds, we'll - # need to the re-render the screen lines corresponding to these preceding - # folds as well. So we alter the start row of the buffer range we - # are going to change to the buffer row of the first fold in a potential - # series of folds that ends on the current fold's starting buffer row. - bufferRange = bufferRange.copy() - bufferRange.start.row = @bufferRowForScreenRow(@screenRowForBufferRow(bufferRange.start.row)) + createFold: (startRow, endRow) -> + fold = new Fold(this, startRow, endRow) + @registerFold(startRow, fold) + bufferRange = new Range([[startRow, 0], [endRow, @buffer.lineLengthForRow(endRow)]]) oldScreenRange = @screenLineRangeForBufferRange(bufferRange) - lines = @buildLineForBufferRow(bufferRange.start.row) + lines = @buildLineForBufferRow(startRow) @lineMap.replaceScreenRows(oldScreenRange.start.row, oldScreenRange.end.row, lines) newScreenRange = @screenLineRangeForBufferRange(bufferRange) @@ -147,29 +137,20 @@ class Renderer startBufferColumn = null currentScreenLineLength = 0 - loop - break if startBufferRow > endBufferRow and not startBufferColumn? + while startBufferRow <= endBufferRow + break if startBufferColumn ?= 0 line = @highlighter.lineForRow(startBufferRow) line = line.splitAt(startBufferColumn)[1] wrapScreenColumn = @findWrapColumn(line.text, @maxLineLength - currentScreenLineLength) - continueMainLoop = false - for fold in @foldsForBufferRow(startBufferRow) - if fold.start.column >= startBufferColumn - foldStartSceenColumn = fold.start.column - startBufferColumn - if (foldStartSceenColumn) > wrapScreenColumn - foldPlaceholderLength - wrapScreenColumn = Math.min(wrapScreenColumn, foldStartSceenColumn) - break - prefix = line.splitAt(foldStartSceenColumn)[0] - placeholder = @buildFoldPlaceholder(fold) - lineFragments.push(prefix, placeholder) - startBufferRow = fold.end.row - startBufferColumn = fold.end.column - currentScreenLineLength = currentScreenLineLength + (prefix?.text.length ? 0) + foldPlaceholderLength - continueMainLoop = true - break - continue if continueMainLoop + if fold = @foldForBufferRow(startBufferRow) + placeholder = @buildFoldPlaceholder(fold) + lineFragments.push(placeholder) + startBufferRow = fold.end.row + 1 + startBufferColumn = 0 + currentScreenLineLength = 0 + continue if wrapScreenColumn? line = line.splitAt(wrapScreenColumn)[0] diff --git a/src/app/selection.coffee b/src/app/selection.coffee index f734ef680..c24d23bae 100644 --- a/src/app/selection.coffee +++ b/src/app/selection.coffee @@ -237,5 +237,5 @@ class Selection extends View fold: -> range = @getBufferRange() - @editor.createFold(range) - @cursor.setBufferPosition(range.end) + @editor.createFold(range.start.row, range.end.row) + @cursor.setBufferPosition([range.end.row + 1, 0]) From 03ae0bbbb6c99da919d5b43cbf466b85bacc4ecf Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 18 May 2012 19:22:56 -0600 Subject: [PATCH 22/61] Guts on the floor, but 2 line-wise fold specs are passing and the renderer is getting there --- spec/app/editor-spec.coffee | 4 +- spec/app/renderer-spec.coffee | 43 ++++++++++++-------- src/app/fold.coffee | 4 ++ src/app/renderer.coffee | 62 ++++++++++++++--------------- src/app/screen-line-fragment.coffee | 3 ++ 5 files changed, 65 insertions(+), 51 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 6c75ff020..662ad4cd3 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -2426,11 +2426,11 @@ describe "Editor", -> editor.attachToDom() describe "when a fold-selection event is triggered", -> - fit "folds the lines covered by the selection into a single line with a fold class", -> + it "folds the lines covered by the selection into a single line with a fold class", -> editor.getSelection().setBufferRange(new Range([4, 29], [7, 4])) editor.trigger 'fold-selection' - expect(editor.visibleLines.find('.line:eq(4)').toHaveClass('fold') + expect(editor.visibleLines.find('.line:eq(4)')).toHaveClass('fold') expect(editor.visibleLines.find('.line:eq(5)').text()).toBe '8' expect(editor.getSelection().isEmpty()).toBeTruthy() diff --git a/spec/app/renderer-spec.coffee b/spec/app/renderer-spec.coffee index 4d245b9d8..b943dbe45 100644 --- a/spec/app/renderer-spec.coffee +++ b/spec/app/renderer-spec.coffee @@ -223,16 +223,20 @@ describe "Renderer", -> describe "folding", -> beforeEach -> - buffer = new Buffer(require.resolve 'fixtures/two-hundred.js') + buffer = new Buffer(require.resolve 'fixtures/two-hundred.txt') renderer = new Renderer(buffer, {tabText}) + renderer.on 'change', changeHandler describe "when folds are created and destroyed", -> describe "when a fold spans multiple lines", -> fit "replaces the lines spanned by the fold with a single line with a html class of 'fold'", -> fold = renderer.createFold(4, 7) - expect(renderer.lineForRow(4).text).toHaveClass('fold') - expect(renderer.lineForRow(4).text).toMatch /^4-+/ + foldPlaceholder = renderer.lineForRow(4) + expect(foldPlaceholder.fold).toBe fold + expect(foldPlaceholder.text).toMatch /^4-+/ + expect(foldPlaceholder.bufferDelta).toEqual [4, 0] + expect(foldPlaceholder.screenDelta).toEqual [1, 0] expect(renderer.lineForRow(5).text).toBe '8' expect(changeHandler).toHaveBeenCalled() @@ -242,8 +246,10 @@ describe "Renderer", -> changeHandler.reset() fold.destroy() - expect(renderer.lineForRow(4).text).not.toHaveClass('fold') + expect(renderer.lineForRow(4).fold).toBeUndefined() expect(renderer.lineForRow(4).text).toMatch /^4-+/ + expect(renderer.lineForRow(4).bufferDelta).toEqual [1, 0] + expect(renderer.lineForRow(4).screenDelta).toEqual [1, 0] expect(renderer.lineForRow(5).text).toBe '5' expect(changeHandler).toHaveBeenCalled() @@ -252,29 +258,34 @@ describe "Renderer", -> expect(event.newRange).toEqual [[4, 0], [7, 1]] describe "when a fold spans a single line", -> - it "renders a placeholder for the folded region, but does not skip any lines", -> - fold = renderer.createFold([[2, 8], [2, 25]]) + fit "renders a fold placeholder for the folded line but does not skip any lines", -> + fold = renderer.createFold(4, 4) - [line2, line3] = renderer.linesForRows(2, 3) - expect(line2.text).toBe ' if (...) return items;' - expect(line3.text).toBe ' var pivot = items.shift(), current, left = [], right = [];' + foldPlaceholder = renderer.lineForRow(4) + expect(foldPlaceholder.fold).toBe fold + expect(foldPlaceholder.text).toMatch /^4-+/ + expect(foldPlaceholder.bufferDelta).toEqual [1, 0] + expect(foldPlaceholder.screenDelta).toEqual [1, 0] + expect(renderer.lineForRow(5).text).toBe '5' expect(changeHandler).toHaveBeenCalled() [[event]] = changeHandler.argsForCall - expect(event.oldRange).toEqual [[2, 0], [2, 40]] - expect(event.newRange).toEqual [[2, 0], [2, 26]] + expect(event.oldRange).toEqual [[4, 0], [4, 101]] + expect(event.newRange).toEqual [[4, 0], [4, 101]] changeHandler.reset() fold.destroy() - [line2, line3] = renderer.linesForRows(2, 3) - expect(line2.text).toBe ' if (items.length <= 1) return items;' - expect(line3.text).toBe ' var pivot = items.shift(), current, left = [], right = [];' + expect(renderer.lineForRow(4).fold).toBeUndefined() + expect(renderer.lineForRow(4).text).toMatch /^4-+/ + expect(renderer.lineForRow(4).bufferDelta).toEqual [1, 0] + expect(renderer.lineForRow(4).screenDelta).toEqual [1, 0] + expect(renderer.lineForRow(5).text).toBe '5' expect(changeHandler).toHaveBeenCalled() [[event]] = changeHandler.argsForCall - expect(event.newRange).toEqual [[2, 0], [2, 40]] - expect(event.oldRange).toEqual [[2, 0], [2, 26]] + expect(event.oldRange).toEqual [[4, 0], [4, 101]] + expect(event.newRange).toEqual [[4, 0], [4, 101]] changeHandler.reset() describe "when a fold is nested within another fold", -> diff --git a/src/app/fold.coffee b/src/app/fold.coffee index 535ad762b..d094cb26a 100644 --- a/src/app/fold.coffee +++ b/src/app/fold.coffee @@ -1,4 +1,5 @@ Range = require 'range' +Point = require 'point' module.exports = class Fold @@ -18,6 +19,9 @@ class Fold # new Range([@startRow, 0], @endRow) throw "Don't worry about this yet -- sobo" + getBufferDelta: -> + new Point(@endRow - @startRow + 1, 0) + handleBufferChange: (event) -> # oldStartRow = @start.row diff --git a/src/app/renderer.coffee b/src/app/renderer.coffee index 957566386..c1a942165 100644 --- a/src/app/renderer.coffee +++ b/src/app/renderer.coffee @@ -55,8 +55,9 @@ class Renderer fold = new Fold(this, startRow, endRow) @registerFold(startRow, fold) - bufferRange = new Range([[startRow, 0], [endRow, @buffer.lineLengthForRow(endRow)]]) + bufferRange = new Range([startRow, 0], [endRow, @buffer.lineLengthForRow(endRow)]) oldScreenRange = @screenLineRangeForBufferRange(bufferRange) + lines = @buildLineForBufferRow(startRow) @lineMap.replaceScreenRows(oldScreenRange.start.row, oldScreenRange.end.row, lines) newScreenRange = @screenLineRangeForBufferRange(bufferRange) @@ -66,22 +67,17 @@ class Renderer fold destroyFold: (fold) -> - bufferRange = fold.getRange().copy() - @unregisterFold(bufferRange.start.row, fold) - - bufferRange.start.row = @bufferRowForScreenRow(@screenRowForBufferRow(bufferRange.start.row)) - startScreenRow = @screenRowForBufferRow(bufferRange.start.row) + @unregisterFold(fold.startRow, fold) + { startRow, endRow } = fold + bufferRange = new Range([startRow, 0], [endRow, @buffer.lineLengthForRow(endRow)]) oldScreenRange = @screenLineRangeForBufferRange(bufferRange) - lines = @buildLinesForBufferRows(bufferRange.start.row, bufferRange.end.row) - @lineMap.replaceScreenRows( - oldScreenRange.start.row, - oldScreenRange.end.row - lines) + lines = @buildLinesForBufferRows(startRow, endRow) + @lineMap.replaceScreenRows(oldScreenRange.start.row, oldScreenRange.end.row, lines) newScreenRange = @screenLineRangeForBufferRange(bufferRange) @trigger 'change', oldRange: oldScreenRange, newRange: newScreenRange - @trigger 'unfold', fold.getRange() + @trigger 'unfold', bufferRange screenRowForBufferRow: (bufferRow) -> @lineMap.screenPositionForBufferPosition([bufferRow, 0]).row @@ -137,31 +133,30 @@ class Renderer startBufferColumn = null currentScreenLineLength = 0 + startBufferColumn = 0 while startBufferRow <= endBufferRow - break if - startBufferColumn ?= 0 - line = @highlighter.lineForRow(startBufferRow) - line = line.splitAt(startBufferColumn)[1] - wrapScreenColumn = @findWrapColumn(line.text, @maxLineLength - currentScreenLineLength) + screenLine = @highlighter.lineForRow(startBufferRow) if fold = @foldForBufferRow(startBufferRow) - placeholder = @buildFoldPlaceholder(fold) - lineFragments.push(placeholder) - startBufferRow = fold.end.row + 1 - startBufferColumn = 0 - currentScreenLineLength = 0 + screenLine = screenLine.copy() + screenLine.fold = fold + screenLine.bufferDelta = fold.getBufferDelta() + lineFragments.push(screenLine) + startBufferRow = fold.endRow + 1 continue + startBufferColumn ?= 0 + screenLine = screenLine.splitAt(startBufferColumn)[1] if startBufferColumn > 0 + wrapScreenColumn = @findWrapColumn(screenLine.text, @maxLineLength) if wrapScreenColumn? - line = line.splitAt(wrapScreenColumn)[0] - line.screenDelta = new Point(1, 0) + screenLine = screenLine.splitAt(wrapScreenColumn)[0] + screenLine.screenDelta = new Point(1, 0) startBufferColumn += wrapScreenColumn else startBufferRow++ - startBufferColumn = null + startBufferColumn = 0 - lineFragments.push(line) - currentScreenLineLength = 0 + lineFragments.push(screenLine) lineFragments @@ -186,16 +181,17 @@ class Renderer unregisterFold: (bufferRow, fold) -> folds = @activeFolds[bufferRow] - folds.splice(folds.indexOf(fold), 1) + _.remove(folds, fold) delete @foldsById[fold.id] - foldsForBufferRow: (bufferRow) -> - folds = @activeFolds[bufferRow] or [] - folds.sort (a, b) -> a.compare(b) + foldForBufferRow: (bufferRow) -> + @activeFolds[bufferRow]?[0] buildFoldPlaceholder: (fold) -> - token = new Token(value: '...', type: 'fold-placeholder', fold: fold, isAtomic: true) - new ScreenLineFragment([token], token.value, [0, token.value.length], fold.getRange().toDelta()) + + # token = new Token(value: '...', type: 'fold-placeholder', fold: fold, isAtomic: true) + # delta = new Point(fold.endRow - fold.startRow + 1, 0) + # new ScreenLineFragment([token], token.value, [0, token.value.length], delta) screenLineRangeForBufferRange: (bufferRange) -> @expandScreenRangeToLineEnds( diff --git a/src/app/screen-line-fragment.coffee b/src/app/screen-line-fragment.coffee index e16391d80..3b9a1d0df 100644 --- a/src/app/screen-line-fragment.coffee +++ b/src/app/screen-line-fragment.coffee @@ -14,6 +14,9 @@ class ScreenLineFragment @bufferDelta = Point.fromObject(bufferDelta) _.extend(this, extraFields) + copy: -> + new ScreenLineFragment(@tokens, @text, @screenDelta, @bufferDelta, { @state }) + splitAt: (column) -> return [new ScreenLineFragment([], '', [0, 0], [0, 0]), this] if column == 0 From 975778625769c651d7dda4c3b3c4657e398045f3 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 18 May 2012 19:39:23 -0600 Subject: [PATCH 23/61] Add passing spec for nested folds (that start on different rows) --- spec/app/renderer-spec.coffee | 78 ++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/spec/app/renderer-spec.coffee b/spec/app/renderer-spec.coffee index b943dbe45..2a92a3a1a 100644 --- a/spec/app/renderer-spec.coffee +++ b/spec/app/renderer-spec.coffee @@ -229,15 +229,15 @@ describe "Renderer", -> describe "when folds are created and destroyed", -> describe "when a fold spans multiple lines", -> - fit "replaces the lines spanned by the fold with a single line with a html class of 'fold'", -> + it "replaces the lines spanned by the fold with a placeholder that references the fold object", -> fold = renderer.createFold(4, 7) - foldPlaceholder = renderer.lineForRow(4) - expect(foldPlaceholder.fold).toBe fold - expect(foldPlaceholder.text).toMatch /^4-+/ - expect(foldPlaceholder.bufferDelta).toEqual [4, 0] - expect(foldPlaceholder.screenDelta).toEqual [1, 0] - expect(renderer.lineForRow(5).text).toBe '8' + [line4, line5] = renderer.linesForRows(4, 5) + expect(line4.fold).toBe fold + expect(line4.text).toMatch /^4-+/ + expect(line4.bufferDelta).toEqual [4, 0] + expect(line4.screenDelta).toEqual [1, 0] + expect(line5.text).toBe '8' expect(changeHandler).toHaveBeenCalled() [event] = changeHandler.argsForCall[0] @@ -246,11 +246,12 @@ describe "Renderer", -> changeHandler.reset() fold.destroy() - expect(renderer.lineForRow(4).fold).toBeUndefined() - expect(renderer.lineForRow(4).text).toMatch /^4-+/ - expect(renderer.lineForRow(4).bufferDelta).toEqual [1, 0] - expect(renderer.lineForRow(4).screenDelta).toEqual [1, 0] - expect(renderer.lineForRow(5).text).toBe '5' + [line4, line5] = renderer.linesForRows(4, 5) + expect(line4.fold).toBeUndefined() + expect(line4.text).toMatch /^4-+/ + expect(line4.bufferDelta).toEqual [1, 0] + expect(line4.screenDelta).toEqual [1, 0] + expect(line5.text).toBe '5' expect(changeHandler).toHaveBeenCalled() [[event]] = changeHandler.argsForCall @@ -258,15 +259,15 @@ describe "Renderer", -> expect(event.newRange).toEqual [[4, 0], [7, 1]] describe "when a fold spans a single line", -> - fit "renders a fold placeholder for the folded line but does not skip any lines", -> + it "renders a fold placeholder for the folded line but does not skip any lines", -> fold = renderer.createFold(4, 4) - foldPlaceholder = renderer.lineForRow(4) - expect(foldPlaceholder.fold).toBe fold - expect(foldPlaceholder.text).toMatch /^4-+/ - expect(foldPlaceholder.bufferDelta).toEqual [1, 0] - expect(foldPlaceholder.screenDelta).toEqual [1, 0] - expect(renderer.lineForRow(5).text).toBe '5' + [line4, line5] = renderer.linesForRows(4, 5) + expect(line4.fold).toBe fold + expect(line4.text).toMatch /^4-+/ + expect(line4.bufferDelta).toEqual [1, 0] + expect(line4.screenDelta).toEqual [1, 0] + expect(line5.text).toBe '5' expect(changeHandler).toHaveBeenCalled() [[event]] = changeHandler.argsForCall @@ -276,11 +277,12 @@ describe "Renderer", -> fold.destroy() - expect(renderer.lineForRow(4).fold).toBeUndefined() - expect(renderer.lineForRow(4).text).toMatch /^4-+/ - expect(renderer.lineForRow(4).bufferDelta).toEqual [1, 0] - expect(renderer.lineForRow(4).screenDelta).toEqual [1, 0] - expect(renderer.lineForRow(5).text).toBe '5' + [line4, line5] = renderer.linesForRows(4, 5) + expect(line4.fold).toBeUndefined() + expect(line4.text).toMatch /^4-+/ + expect(line4.bufferDelta).toEqual [1, 0] + expect(line4.screenDelta).toEqual [1, 0] + expect(line5.text).toBe '5' expect(changeHandler).toHaveBeenCalled() [[event]] = changeHandler.argsForCall @@ -289,20 +291,30 @@ describe "Renderer", -> changeHandler.reset() describe "when a fold is nested within another fold", -> - it "only renders the placeholder for the inner fold when the outer fold is destroyed", -> - outerFold = renderer.createFold([[4, 29], [8, 36]]) - innerFold = renderer.createFold([[8, 5], [8, 10]]) + fit "does not render the placeholder for the inner fold until the outer fold is destroyed", -> + innerFold = renderer.createFold(6, 7) + outerFold = renderer.createFold(4, 8) [line4, line5] = renderer.linesForRows(4, 5) - expect(line4.text).toBe ' while(items.length > 0) {...concat(sort(right));' - expect(line5.text).toBe ' };' + expect(line4.fold).toBe outerFold + expect(line4.text).toMatch /4-+/ + expect(line4.bufferDelta).toEqual [5, 0] + expect(line4.screenDelta).toEqual [1, 0] + expect(line5.text).toMatch /9-+/ outerFold.destroy() - [line4, line5] = renderer.linesForRows(4, 5) - expect(line4.text).toBe ' while(items.length > 0) {' - expect(line5.text).toBe ' current = items.shift();' - expect(renderer.lineForRow(8).text).toBe ' r... sort(left).concat(pivot).concat(sort(right));' + [line4, line5, line6, line7] = renderer.linesForRows(4, 7) + expect(line4.fold).toBeUndefined() + expect(line4.text).toMatch /^4-+/ + expect(line4.bufferDelta).toEqual [1, 0] + expect(line4.screenDelta).toEqual [1, 0] + expect(line5.text).toBe '5' + expect(line6.fold).toBe innerFold + expect(line6.text).toBe '6' + expect(line6.bufferDelta).toEqual [2, 0] + expect(line6.screenDelta).toEqual [1, 0] + expect(line7.text).toBe '8' it "allows the outer fold to start at the same location as the inner fold", -> renderer.createFold([[4, 29], [7, 4]]) From c491d9243738a9af23aae78881bbd9cd2eafab05 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 18 May 2012 20:14:05 -0600 Subject: [PATCH 24/61] Nested folds can start at the same row as the fold that contains them ...still need to test destroying the outer fold though. --- spec/app/renderer-spec.coffee | 14 ++++++++++---- src/app/renderer.coffee | 7 ++++--- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/spec/app/renderer-spec.coffee b/spec/app/renderer-spec.coffee index 2a92a3a1a..b5e072541 100644 --- a/spec/app/renderer-spec.coffee +++ b/spec/app/renderer-spec.coffee @@ -316,10 +316,16 @@ describe "Renderer", -> expect(line6.screenDelta).toEqual [1, 0] expect(line7.text).toBe '8' - it "allows the outer fold to start at the same location as the inner fold", -> - renderer.createFold([[4, 29], [7, 4]]) - renderer.createFold([[4, 29], [9, 2]]) - expect(renderer.lineForRow(4).text).toBe " while(items.length > 0) {...};" + fit "allows the outer fold to start at the same location as the inner fold", -> + innerFold = renderer.createFold(4, 6) + outerFold = renderer.createFold(4, 8) + + [line4, line5] = renderer.linesForRows(4, 5) + expect(line4.fold).toBe outerFold + expect(line4.text).toMatch /4-+/ + expect(line4.bufferDelta).toEqual [5, 0] + expect(line4.screenDelta).toEqual [1, 0] + expect(line5.text).toMatch /9-+/ describe "when a fold begins on the line on which another fold ends", -> describe "when the second fold is created before the first fold", -> diff --git a/src/app/renderer.coffee b/src/app/renderer.coffee index c1a942165..c49fe5d23 100644 --- a/src/app/renderer.coffee +++ b/src/app/renderer.coffee @@ -137,7 +137,7 @@ class Renderer while startBufferRow <= endBufferRow screenLine = @highlighter.lineForRow(startBufferRow) - if fold = @foldForBufferRow(startBufferRow) + if fold = @largestFoldForBufferRow(startBufferRow) screenLine = screenLine.copy() screenLine.fold = fold screenLine.bufferDelta = fold.getBufferDelta() @@ -184,8 +184,9 @@ class Renderer _.remove(folds, fold) delete @foldsById[fold.id] - foldForBufferRow: (bufferRow) -> - @activeFolds[bufferRow]?[0] + largestFoldForBufferRow: (bufferRow) -> + return unless folds = @activeFolds[bufferRow] + (folds.sort (a, b) -> b.endRow - a.endRow)[0] buildFoldPlaceholder: (fold) -> From a57ccc1b7fdea15e6b94cf7ce17da757f7978101 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 18 May 2012 20:59:58 -0600 Subject: [PATCH 25/61] Remove specs that don't matter anymore w/ linewise folding --- spec/app/renderer-spec.coffee | 101 ---------------------------------- 1 file changed, 101 deletions(-) diff --git a/spec/app/renderer-spec.coffee b/spec/app/renderer-spec.coffee index b5e072541..d7d553c3c 100644 --- a/spec/app/renderer-spec.coffee +++ b/spec/app/renderer-spec.coffee @@ -327,107 +327,6 @@ describe "Renderer", -> expect(line4.screenDelta).toEqual [1, 0] expect(line5.text).toMatch /9-+/ - describe "when a fold begins on the line on which another fold ends", -> - describe "when the second fold is created before the first fold", -> - it "renders a placeholder for both folds on the first line of the first fold", -> - fold1 = renderer.createFold([[7, 5], [8, 36]]) - fold2 = renderer.createFold([[4, 29], [7, 4]]) - - [line4, line5] = renderer.linesForRows(4, 5) - expect(line4.text).toBe ' while(items.length > 0) {...}...concat(sort(right));' - expect(line5.text).toBe ' };' - - expect(changeHandler.callCount).toBe 2 - [[event1], [event2]] = changeHandler.argsForCall - expect(event1.oldRange).toEqual [[7, 0], [8, 56]] - expect(event1.newRange).toEqual [[7, 0], [7, 28]] - expect(event2.oldRange).toEqual [[4, 0], [7, 28]] - expect(event2.newRange).toEqual [[4, 0], [4, 56]] - changeHandler.reset() - - fold1.destroy() - [line4, line5] = renderer.linesForRows(4, 5) - expect(line4.text).toBe ' while(items.length > 0) {...}' - expect(line5.text).toBe ' return sort(left).concat(pivot).concat(sort(right));' - - expect(changeHandler).toHaveBeenCalled() - [event] = changeHandler.argsForCall[0] - expect(event.oldRange).toEqual [[4, 0], [4, 56]] - expect(event.newRange).toEqual [[4, 0], [5, 56]] - changeHandler.reset() - - fold2.destroy() - [line4, line5] = renderer.linesForRows(4, 5) - expect(line4.text).toBe ' while(items.length > 0) {' - expect(line5.text).toBe ' current = items.shift();' - - expect(changeHandler).toHaveBeenCalled() - [event] = changeHandler.argsForCall[0] - expect(event.oldRange).toEqual [[4, 0], [4, 33]] - expect(event.newRange).toEqual [[4, 0], [7, 5]] - - describe "when the second fold is created after the first fold", -> - it "renders a placeholder for both folds on the first line of the first fold", -> - fold1 = renderer.createFold([[4, 29], [7, 4]]) - fold2 = renderer.createFold([[7, 5], [8, 36]]) - [line4, line5] = renderer.linesForRows(4, 5) - expect(line4.text).toBe ' while(items.length > 0) {...}...concat(sort(right));' - expect(line5.text).toBe ' };' - - expect(changeHandler.callCount).toBe 2 - [[event1], [event2]] = changeHandler.argsForCall - expect(event1.oldRange).toEqual [[4, 0], [7, 5]] - expect(event1.newRange).toEqual [[4, 0], [4, 33]] - expect(event2.oldRange).toEqual [[4, 0], [5, 56]] - expect(event2.newRange).toEqual [[4, 0], [4, 56]] - changeHandler.reset() - - fold1.destroy() - [line4, line5] = renderer.linesForRows(4, 5) - [line7] = renderer.linesForRows(7, 7) - expect(line4.text).toBe ' while(items.length > 0) {' - expect(line5.text).toBe ' current = items.shift();' - expect(line7.text).toBe ' }...concat(sort(right));' - - expect(changeHandler).toHaveBeenCalled() - [event] = changeHandler.argsForCall[0] - expect(event.oldRange).toEqual [[4, 0], [4, 56]] - expect(event.newRange).toEqual [[4, 0], [7, 28]] - changeHandler.reset() - - fold2.destroy() - [line4, line5] = renderer.linesForRows(4, 5) - expect(line4.text).toBe ' while(items.length > 0) {' - expect(line5.text).toBe ' current = items.shift();' - - expect(changeHandler).toHaveBeenCalled() - [event] = changeHandler.argsForCall[0] - expect(event.oldRange).toEqual [[7, 0], [7, 28]] - expect(event.newRange).toEqual [[7, 0], [8, 56]] - - describe "when a fold starts at the beginning of a line", -> - it "renders a placeholder at the beginning of the line", -> - renderer.createFold([[4, 0], [7, 4]]) - expect(renderer.lineForRow(4).text).toBe '...}' - - describe "when a fold ends at the beginning of a line", -> - it "renders a placeholder at the beginning of the line", -> - renderer.createFold([[4, 29], [7, 0]]) - expect(renderer.lineForRow(4).text).toBe ' while(items.length > 0) {... }' - - describe "when a fold starts on the first line of the buffer", -> - it "renders the first line correctly when the fold is destroyed", -> - fold = renderer.createFold([[0, 14], [0, 27]]) - fold.destroy() - expect(renderer.lineForRow(0).text).toBe 'var quicksort = function () {' - - describe "when a fold causes a wrapped line to become shorter than the max line length", -> - it "unwraps the line", -> - renderer.setMaxLineLength(50) - renderer.createFold([[3, 0], [3, 15]]) - expect(renderer.lineForRow(3).text).toBe '... items.shift(), current, left = [], right = [];' - expect(renderer.lineForRow(4).text).toBe ' while(items.length > 0) {' - describe "when the buffer changes", -> [fold1, fold2] = [] beforeEach -> From 080a511d0dc68a2a46bcd68de47538cc8a891c5c Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Mon, 21 May 2012 10:11:41 -0700 Subject: [PATCH 26/61] :lipstick: --- src/app/renderer.coffee | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/app/renderer.coffee b/src/app/renderer.coffee index c49fe5d23..9feb295b1 100644 --- a/src/app/renderer.coffee +++ b/src/app/renderer.coffee @@ -131,18 +131,19 @@ class Renderer buildLinesForBufferRows: (startBufferRow, endBufferRow) -> lineFragments = [] startBufferColumn = null + currentBufferRow = startBufferRow currentScreenLineLength = 0 startBufferColumn = 0 - while startBufferRow <= endBufferRow - screenLine = @highlighter.lineForRow(startBufferRow) + while currentBufferRow <= endBufferRow + screenLine = @highlighter.lineForRow(currentBufferRow) - if fold = @largestFoldForBufferRow(startBufferRow) + if fold = @largestFoldForBufferRow(currentBufferRow) screenLine = screenLine.copy() screenLine.fold = fold screenLine.bufferDelta = fold.getBufferDelta() lineFragments.push(screenLine) - startBufferRow = fold.endRow + 1 + currentBufferRow = fold.endRow + 1 continue startBufferColumn ?= 0 @@ -153,7 +154,7 @@ class Renderer screenLine.screenDelta = new Point(1, 0) startBufferColumn += wrapScreenColumn else - startBufferRow++ + currentBufferRow++ startBufferColumn = 0 lineFragments.push(screenLine) From d7e1ffcdf2a0fd8c47998b5da98c514625353dd6 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Mon, 21 May 2012 10:29:48 -0700 Subject: [PATCH 27/61] Update render spec to new version of Renderer.createFold --- spec/app/renderer-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/renderer-spec.coffee b/spec/app/renderer-spec.coffee index d7d553c3c..3f64b0ffa 100644 --- a/spec/app/renderer-spec.coffee +++ b/spec/app/renderer-spec.coffee @@ -556,5 +556,5 @@ describe "Renderer", -> describe ".bufferRowsForScreenRows()", -> it "returns the buffer rows corresponding to each screen row in the given range", -> renderer.setMaxLineLength(50) - renderer.createFold([[4, 29], [7, 4]]) + renderer.createFold(4, 7) expect(renderer.bufferRowsForScreenRows()).toEqual [0, 1, 2, 3, 3, 4, 8, 8, 9, 10, 11, 12] From 4f60135d81fea0d21be5faa350e7954de462866a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 21 May 2012 13:35:54 -0700 Subject: [PATCH 28/61] Remove random indent that was causing editor spec not to run --- spec/app/editor-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 662ad4cd3..ccb8c4b6e 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -1,4 +1,4 @@ - RootView = require 'root-view' +RootView = require 'root-view' Buffer = require 'buffer' Editor = require 'editor' Range = require 'range' From 9beff8edbb3457eba71d0f61a2bd581c8bb50969 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 21 May 2012 13:36:19 -0700 Subject: [PATCH 29/61] Un-F --- spec/app/renderer-spec.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/app/renderer-spec.coffee b/spec/app/renderer-spec.coffee index d7d553c3c..fda693db2 100644 --- a/spec/app/renderer-spec.coffee +++ b/spec/app/renderer-spec.coffee @@ -291,7 +291,7 @@ describe "Renderer", -> changeHandler.reset() describe "when a fold is nested within another fold", -> - fit "does not render the placeholder for the inner fold until the outer fold is destroyed", -> + it "does not render the placeholder for the inner fold until the outer fold is destroyed", -> innerFold = renderer.createFold(6, 7) outerFold = renderer.createFold(4, 8) @@ -316,7 +316,7 @@ describe "Renderer", -> expect(line6.screenDelta).toEqual [1, 0] expect(line7.text).toBe '8' - fit "allows the outer fold to start at the same location as the inner fold", -> + it "allows the outer fold to start at the same location as the inner fold", -> innerFold = renderer.createFold(4, 6) outerFold = renderer.createFold(4, 8) From ce1c1ff08174023edd9bb867ca01b0381b6f90bf Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 21 May 2012 13:38:39 -0700 Subject: [PATCH 30/61] Render fold lines with the 'fold' class and remove old fold placeholder support --- src/app/editor.coffee | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/app/editor.coffee b/src/app/editor.coffee index e52e296f9..b1b345d46 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -461,18 +461,17 @@ class Editor extends View charWidth = @charWidth charHeight = @charHeight lines = @renderer.linesForRows(startRow, endRow) + $$ -> for line in lines - @div class: 'line', => - appendNbsp = true - for token in line.tokens - if token.type is 'fold-placeholder' - @span ' ', class: 'fold-placeholder', style: "width: #{3 * charWidth}px; height: #{charHeight}px;", 'foldId': token.fold.id, => - @div class: "ellipsis", => @raw "…" - else - appendNbsp = false + lineClass = 'line' + lineClass += ' fold' if line.fold? + @div class: lineClass, => + if line.text == '' + @raw ' ' if line.text == '' + else + for token in line.tokens @span { class: token.type.replace('.', ' ') }, token.value - @raw ' ' if appendNbsp insertLineElements: (row, lineElements) -> @spliceLineElements(row, 0, lineElements) From 7ad583052194527e405bb998cae7e8e36685ed67 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 21 May 2012 14:18:00 -0700 Subject: [PATCH 31/61] Convert failing specs to linewise code folding --- spec/app/editor-spec.coffee | 51 +++++++++++++++---------------------- 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index ccb8c4b6e..f4214a30a 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -584,18 +584,10 @@ describe "Editor", -> describe "when there are folds", -> it "skips line numbers", -> - editor.createFold([[3, 10], [5, 1]]) + editor.createFold(3, 5) expect(editor.gutter.find('.line-number:eq(3)').text()).toBe '4' expect(editor.gutter.find('.line-number:eq(4)').text()).toBe '7' - describe "when there is a fold on the last screen line of a wrapped line", -> - it "renders line numbers correctly when the fold is destroyed (regression)", -> - setEditorHeightInLines(editor, 20) - editor.setMaxLineLength(50) - fold = editor.createFold([[3, 52], [3, 56]]) - fold.destroy() - expect(editor.gutter.find('.line-number:last').text()).toBe '13' - describe "when the scrollView is scrolled to the right", -> it "adds a drop shadow to the gutter", -> editor.attachToDom() @@ -905,46 +897,50 @@ describe "Editor", -> beforeEach -> setEditorWidthInChars(editor, 50) editor.setSoftWrap(true) - editor.createFold(new Range([3, 3], [3, 7])) + editor.createFold(2, 3) describe "when it is a single click", -> it "re-positions the cursor from the clicked screen position to the corresponding buffer position", -> expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0) - editor.visibleLines.trigger mousedownEvent(editor: editor, point: [4, 7]) - expect(editor.getCursorBufferPosition()).toEqual(row: 3, column: 58) + editor.visibleLines.trigger mousedownEvent(editor: editor, point: [9, 0]) + expect(editor.getCursorBufferPosition()).toEqual(row: 8, column: 11) describe "when it is a double click", -> it "selects the word under the cursor", -> expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0) - editor.visibleLines.trigger mousedownEvent(editor: editor, point: [4, 3], originalEvent: {detail: 1}) + editor.visibleLines.trigger mousedownEvent(editor: editor, point: [9, 0], originalEvent: {detail: 1}) editor.visibleLines.trigger 'mouseup' - editor.visibleLines.trigger mousedownEvent(editor: editor, point: [4, 3], originalEvent: {detail: 2}) - expect(editor.getSelectedText()).toBe "right" + editor.visibleLines.trigger mousedownEvent(editor: editor, point: [9, 0], originalEvent: {detail: 2}) + expect(editor.getSelectedText()).toBe "sort" describe "when it is clicked more then twice (triple, quadruple, etc...)", -> it "selects the line under the cursor", -> expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0) # Triple click - editor.visibleLines.trigger mousedownEvent(editor: editor, point: [4, 3], originalEvent: {detail: 1}) + point = [9, 3] + editor.visibleLines.trigger mousedownEvent(editor: editor, point: point, originalEvent: {detail: 1}) editor.visibleLines.trigger 'mouseup' - editor.visibleLines.trigger mousedownEvent(editor: editor, point: [4, 3], originalEvent: {detail: 2}) + editor.visibleLines.trigger mousedownEvent(editor: editor, point: point, originalEvent: {detail: 2}) editor.visibleLines.trigger 'mouseup' - editor.visibleLines.trigger mousedownEvent(editor: editor, point: [4, 3], originalEvent: {detail: 3}) + editor.visibleLines.trigger mousedownEvent(editor: editor, point: point, originalEvent: {detail: 3}) editor.visibleLines.trigger 'mouseup' - expect(editor.getSelectedText()).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.getSelectedText()).toBe " return sort(left).concat(pivot).concat(sort(right));" + + editor.logLines() # Quad click - editor.visibleLines.trigger mousedownEvent(editor: editor, point: [8, 3], originalEvent: {detail: 1}) + point = [12, 3] + editor.visibleLines.trigger mousedownEvent(editor: editor, point: point, originalEvent: {detail: 1}) editor.visibleLines.trigger 'mouseup' - editor.visibleLines.trigger mousedownEvent(editor: editor, point: [8, 3], originalEvent: {detail: 2}) + editor.visibleLines.trigger mousedownEvent(editor: editor, point: point, originalEvent: {detail: 2}) editor.visibleLines.trigger 'mouseup' - editor.visibleLines.trigger mousedownEvent(editor: editor, point: [8, 3], originalEvent: {detail: 3}) + editor.visibleLines.trigger mousedownEvent(editor: editor, point: point, originalEvent: {detail: 3}) editor.visibleLines.trigger 'mouseup' - editor.visibleLines.trigger mousedownEvent(editor: editor, point: [8, 3], originalEvent: {detail: 4}) + editor.visibleLines.trigger mousedownEvent(editor: editor, point: point, originalEvent: {detail: 4}) editor.visibleLines.trigger 'mouseup' - expect(editor.getSelectedText()).toBe " current < pivot ? left.push(current) : right.push(current);" + expect(editor.getSelectedText()).toBe " return sort(Array.apply(this, arguments));" describe "when soft-wrap is disabled", -> describe "when it is a single click", -> @@ -1184,13 +1180,6 @@ describe "Editor", -> editor.insertText("\n") expect(editor.buffer.lineForRow(2)).toEqual(" ") - describe "when a newline is inserted following a fold placeholder", -> - it "indents cursor based on the indentation of previous buffer line", -> - editor.createFold([[1, 10], [1, 30]]) - editor.setCursorBufferPosition([1, 30]) - editor.insertText("\n") - expect(editor.buffer.lineForRow(2)).toEqual(" ") - describe "when text beginning with a newline is inserted", -> it "indents cursor based on the indentation of previous buffer line", -> editor.setCursorBufferPosition([4, 29]) From 527e243d1a8f07770496d8b088d4e2a5169845b3 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 21 May 2012 14:30:35 -0700 Subject: [PATCH 32/61] Style fold lines a bit --- static/theme/twilight.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/theme/twilight.css b/static/theme/twilight.css index 8384a69bf..f83b9e705 100644 --- a/static/theme/twilight.css +++ b/static/theme/twilight.css @@ -70,8 +70,8 @@ color:#D2A8A1; } .fold { - background-color: #AC885B; - border-color: #F8F8F8; + background-color: #52412C; + color: #969696; } .support.function { From d2899fa58a8f54ec356069984b57b09808f9b32c Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Mon, 21 May 2012 15:01:14 -0700 Subject: [PATCH 33/61] Folds handle change events --- spec/app/renderer-spec.coffee | 120 ++++++++++++++++++---------------- src/app/fold.coffee | 38 +++++------ src/app/renderer.coffee | 2 +- 3 files changed, 81 insertions(+), 79 deletions(-) diff --git a/spec/app/renderer-spec.coffee b/spec/app/renderer-spec.coffee index ffb2ec7a6..99575012e 100644 --- a/spec/app/renderer-spec.coffee +++ b/spec/app/renderer-spec.coffee @@ -330,105 +330,111 @@ describe "Renderer", -> describe "when the buffer changes", -> [fold1, fold2] = [] beforeEach -> - fold1 = renderer.createFold([[4, 29], [7, 4]]) - fold2 = renderer.createFold([[7, 5], [8, 36]]) + fold1 = renderer.createFold(2, 4) + fold2 = renderer.createFold(6, 8) changeHandler.reset() describe "when the old range precedes lines with a fold", -> it "updates the buffer and re-positions subsequent folds", -> - buffer.change([[1, 5], [2, 10]], 'abc') + buffer.change([[0, 0], [1, 1]], 'abc') - expect(renderer.lineForRow(1).text).toBe ' varabcems.length <= 1) return items;' - expect(renderer.lineForRow(3).text).toBe ' while(items.length > 0) {...}...concat(sort(right));' + expect(renderer.lineForRow(0).text).toBe "abc" + expect(renderer.lineForRow(1).fold).toBe fold1 + expect(renderer.lineForRow(2).text).toBe "5" + expect(renderer.lineForRow(3).fold).toBe fold2 + expect(renderer.lineForRow(4).text).toMatch /^9-+/ expect(changeHandler).toHaveBeenCalled() [[event]] = changeHandler.argsForCall - expect(event.oldRange).toEqual [[1, 0], [2, 40]] - expect(event.newRange).toEqual [[1, 0], [1, 38]] + expect(event.oldRange).toEqual [[0, 0], [1, 1]] + expect(event.newRange).toEqual [[0, 0], [0, 3]] changeHandler.reset() fold1.destroy() - expect(renderer.lineForRow(3).text).toBe ' while(items.length > 0) {' - expect(renderer.lineForRow(6).text).toBe ' }...concat(sort(right));' + expect(renderer.lineForRow(0).text).toBe "abc" + expect(renderer.lineForRow(1).text).toBe "2" + expect(renderer.lineForRow(3).text).toMatch /^4-+/ + expect(renderer.lineForRow(4).text).toBe "5" + expect(renderer.lineForRow(5).fold).toBe fold2 + expect(renderer.lineForRow(6).text).toMatch /^9-+/ expect(changeHandler).toHaveBeenCalled() [[event]] = changeHandler.argsForCall - expect(event.oldRange).toEqual [[3, 0], [3, 56]] - expect(event.newRange).toEqual [[3, 0], [6, 28]] + expect(event.oldRange).toEqual [[1, 0], [1, 1]] + expect(event.newRange).toEqual [[1, 0], [3, 101]] describe "when the old range follows lines with a fold", -> it "re-positions the screen ranges for the change event based on the preceding fold", -> - buffer.change([[9, 3], [10, 0]], 'abc') + buffer.change([[10, 0], [11, 0]], 'abc') - expect(renderer.lineForRow(5).text).toBe ' }abc' - expect(renderer.lineForRow(6).text).toBe ' return sort(Array.apply(this, arguments));' + expect(renderer.lineForRow(1).text).toBe "1" + expect(renderer.lineForRow(2).fold).toBe fold1 + expect(renderer.lineForRow(3).text).toBe "5" + expect(renderer.lineForRow(4).fold).toBe fold2 + expect(renderer.lineForRow(5).text).toMatch /^9-+/ expect(changeHandler).toHaveBeenCalled() [[event]] = changeHandler.argsForCall - expect(event.oldRange).toEqual [[5, 0], [6, 0]] - expect(event.newRange).toEqual [[5, 0], [5, 6]] + expect(event.oldRange).toEqual [[6, 0], [7, 2]] # Expands ranges to encompes entire line + expect(event.newRange).toEqual [[6, 0], [6, 5]] - describe "when the old range contains unfolded text on the first line of a fold, preceding the fold placeholder", -> - it "re-renders the line with the placeholder and re-positions the fold", -> - buffer.change([[4, 4], [4, 9]], 'slongaz') - - expect(renderer.lineForRow(4).text).toBe ' slongaz(items.length > 0) {...}...concat(sort(right));' - expect(changeHandler).toHaveBeenCalled() - [[event]] = changeHandler.argsForCall - expect(event.oldRange).toEqual [[4, 0], [4, 56]] - expect(event.newRange).toEqual [[4, 0], [4, 58]] - - fold1.destroy() - expect(renderer.lineForRow(4).text).toBe ' slongaz(items.length > 0) {' - - describe "when the old range is contained to a single line in-between two fold placeholders", -> + describe "when the old range is contained to a single line in-between two folds", -> it "re-renders the line with the placeholder and re-positions the second fold", -> - buffer.insert([7, 4], 'abc') - expect(renderer.lineForRow(4).text).toBe ' while(items.length > 0) {...abc}...concat(sort(right));' + buffer.insert([5, 0], 'abc\n') + + expect(renderer.lineForRow(1).text).toBe "1" + expect(renderer.lineForRow(2).fold).toBe fold1 + expect(renderer.lineForRow(3).text).toMatch "abc" + expect(renderer.lineForRow(4).text).toBe "5" + expect(renderer.lineForRow(5).fold).toBe fold2 + expect(renderer.lineForRow(6).text).toMatch /^9-+/ + expect(changeHandler).toHaveBeenCalled() [[event]] = changeHandler.argsForCall - expect(event.oldRange).toEqual [[4, 0], [4, 56]] - expect(event.newRange).toEqual [[4, 0], [4, 59]] - - fold2.destroy() - expect(renderer.lineForRow(4).text).toBe ' while(items.length > 0) {...abc}' + expect(event.oldRange).toEqual [[3, 0], [3, 1]] + expect(event.newRange).toEqual [[3, 0], [4, 1]] describe "when the old range is inside a fold", -> it "does not trigger a change event, but updates the fold and ensures the change is present when the fold is destroyed", -> - buffer.change([[4, 29], [6, 0]], 'abc') + buffer.change([[2, 0], [2, 0]], 'abc') + + expect(renderer.lineForRow(1).text).toBe "1" + expect(renderer.lineForRow(2).fold).toBe fold1 + expect(renderer.lineForRow(3).text).toMatch "5" + expect(renderer.lineForRow(4).fold).toBe fold2 + expect(renderer.lineForRow(5).text).toMatch /^9-+/ - expect(renderer.lineForRow(4).text).toBe ' while(items.length > 0) {...}...concat(sort(right));' expect(changeHandler).not.toHaveBeenCalled() - fold1.destroy() - expect(renderer.lineForRow(4).text).toBe ' while(items.length > 0) {abc current < pivot ? left.push(current) : right.push(current);' - expect(renderer.lineForRow(5).text).toBe ' }...concat(sort(right));' - - expect(changeHandler).toHaveBeenCalled() - [[event]] = changeHandler.argsForCall - expect(event.oldRange).toEqual [[4, 0], [4, 56]] - expect(event.newRange).toEqual [[4, 0], [5, 28]] - describe "when the old range surrounds a fold", -> it "removes the fold and replaces the fold placeholder with the new text", -> - buffer.change([[4, 29], [7, 4]], 'party()') + buffer.change([[1, 0], [5, 1]], 'party!') + + expect(renderer.lineForRow(0).text).toBe "0" + expect(renderer.lineForRow(1).text).toBe "party!" + expect(renderer.lineForRow(2).fold).toBe fold2 + expect(renderer.lineForRow(3).text).toMatch /^9-+/ - expect(renderer.lineForRow(4).text).toBe ' while(items.length > 0) {party()}...concat(sort(right));' expect(changeHandler).toHaveBeenCalled() [[event]] = changeHandler.argsForCall - expect(event.oldRange).toEqual [[4, 0], [4, 56]] - expect(event.newRange).toEqual [[4, 0], [4, 60]] + expect(event.oldRange).toEqual [[1, 0], [3, 1]] + expect(event.newRange).toEqual [[1, 0], [1, 6]] describe "when the old range surrounds two nested folds", -> it "removes both folds and replaces the fold placeholder with the new text", -> - renderer.createFold([[4, 25], [7, 5]]) - buffer.change([[4, 25], [7, 5]], '4)') + renderer.createFold(2, 9) + changeHandler.reset() + + buffer.change([[1, 0], [10, 0]], 'goodbye') + + expect(renderer.lineForRow(0).text).toBe "0" + expect(renderer.lineForRow(1).text).toBe "goodbye10" + expect(renderer.lineForRow(2).text).toBe "11" - expect(renderer.lineForRow(4).text).toBe ' while(items.length > 4)...concat(sort(right));' expect(changeHandler).toHaveBeenCalled() [[event]] = changeHandler.argsForCall - expect(event.oldRange).toEqual [[4, 0], [4, 56]] - expect(event.newRange).toEqual [[4, 0], [4, 51]] + expect(event.oldRange).toEqual [[1, 0], [3, 2]] + expect(event.newRange).toEqual [[1, 0], [1, 9]] describe "position translation", -> describe "when there is single fold spanning multiple lines", -> diff --git a/src/app/fold.coffee b/src/app/fold.coffee index d094cb26a..5e0f2433f 100644 --- a/src/app/fold.coffee +++ b/src/app/fold.coffee @@ -16,39 +16,35 @@ class Fold @renderer.destroyFold(this) getRange: -> - # new Range([@startRow, 0], @endRow) throw "Don't worry about this yet -- sobo" getBufferDelta: -> new Point(@endRow - @startRow + 1, 0) handleBufferChange: (event) -> - # oldStartRow = @start.row + oldStartRow = @startRow - # { oldRange } = event - # if oldRange.start.isLessThanOrEqual(@start) and oldRange.end.isGreaterThanOrEqual(@end) - # @renderer.unregisterFold(oldStartRow, this) - # return + { oldRange } = event + if oldRange.start.row <= @startRow and oldRange.end.row >= @endRow + @renderer.unregisterFold(oldStartRow, this) + return - # changeInsideFold = @start.isLessThanOrEqual(oldRange.start) and @end.isGreaterThan(oldRange.end) + changeInsideFold = @startRow <= oldRange.start.row and @endRow >= oldRange.end.row + @startRow = @updateAnchorRow(@startRow, event) + @endRow = @updateAnchorRow(@endRow, event) - # @start = @updateAnchorPoint(@start, event) - # @end = @updateAnchorPoint(@end, event, false) + if @startRow != oldStartRow + @renderer.unregisterFold(oldStartRow, this) + @renderer.registerFold(@startRow, this) - # if @start.row != oldStartRow - # @renderer.unregisterFold(oldStartRow, this) - # @lineFolder.registerFold(@start.row, this) + changeInsideFold - # changeInsideFold + updateAnchorRow: (row, event) -> + { newRange, oldRange } = event + return row if row < oldRange.start.row - updateAnchorPoint: (point, event, inclusive=true) -> - # { newRange, oldRange } = event - # if inclusive - # return point if oldRange.end.isGreaterThan(point) - # else - # return point if oldRange.end.isGreaterThanOrEqual(point) - - # newRange.end.add(point.subtract(oldRange.end)) + deltaFromOldRangeEndRow = row - oldRange.end.row + newRange.end.row + deltaFromOldRangeEndRow compare: (other) -> other diff --git a/src/app/renderer.coffee b/src/app/renderer.coffee index 9feb295b1..30c560ec2 100644 --- a/src/app/renderer.coffee +++ b/src/app/renderer.coffee @@ -120,6 +120,7 @@ class Renderer oldScreenRange = @screenLineRangeForBufferRange(oldBufferRange) newScreenLines = @buildLinesForBufferRows(newBufferRange.start.row, newBufferRange.end.row) + @lineMap.replaceScreenRows oldScreenRange.start.row, oldScreenRange.end.row, newScreenLines newScreenRange = @screenLineRangeForBufferRange(newBufferRange) @@ -190,7 +191,6 @@ class Renderer (folds.sort (a, b) -> b.endRow - a.endRow)[0] buildFoldPlaceholder: (fold) -> - # token = new Token(value: '...', type: 'fold-placeholder', fold: fold, isAtomic: true) # delta = new Point(fold.endRow - fold.startRow + 1, 0) # new ScreenLineFragment([token], token.value, [0, token.value.length], delta) From b4e81571725691590d532ff42ed8846cd17d6d5b Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Mon, 21 May 2012 15:27:36 -0700 Subject: [PATCH 34/61] :lipstick: --- spec/app/line-map-spec.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/app/line-map-spec.coffee b/spec/app/line-map-spec.coffee index 52e8bf3ad..9307e8144 100644 --- a/spec/app/line-map-spec.coffee +++ b/spec/app/line-map-spec.coffee @@ -34,7 +34,6 @@ describe "LineMap", -> expect(map.lineForScreenRow(0)).toEqual line1 expect(map.lineForScreenRow(1)).toEqual line0a.concat(line0b) - describe ".spliceAtBufferRow(bufferRow, rowCount, lineFragments)", -> describe "when called with a row count of 0", -> it "inserts the given line fragments before the specified buffer row", -> From f39891b9128d5319d47e73a87cf252a0e3d21955 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 21 May 2012 17:55:58 -0700 Subject: [PATCH 35/61] Folds are destroyed when their placeholder line is clicked --- spec/app/editor-spec.coffee | 22 +++++++++------------- src/app/editor.coffee | 14 ++++++++------ 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index f4214a30a..b5ca972fa 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -2425,22 +2425,18 @@ describe "Editor", -> expect(editor.getSelection().isEmpty()).toBeTruthy() expect(editor.getCursorScreenPosition()).toEqual [5, 0] - # describe "when a fold placeholder is clicked", -> - # it "removes the associated fold and places the cursor at its beginning", -> - # editor.getSelection().setBufferRange(new Range([4, 29], [7, 4])) - # editor.trigger 'fold-selection' + describe "when a fold placeholder line is clicked", -> + fit "removes the associated fold and places the cursor at its beginning", -> + editor.getSelection().setBufferRange(new Range([3, 0], [9, 0])) + editor.trigger 'fold-selection' - # editor.find('.fold-placeholder .ellipsis').mousedown() + editor.find('.fold.line').mousedown() - # expect(editor.find('.fold-placeholder')).not.toExist() - # expect(editor.visibleLines.find('.line:eq(5)').text()).toBe ' current = items.shift();' + expect(editor.find('.fold')).not.toExist() + expect(editor.visibleLines.find('.line:eq(4)').text()).toMatch /4-+/ + expect(editor.visibleLines.find('.line:eq(5)').text()).toMatch /5/ - # expect(editor.getCursorBufferPosition()).toEqual [4, 29] - - # describe "when there is nothing on a line except a fold placeholder", -> - # it "follows the placeholder with a non-breaking space to ensure the line has the proper height", -> - # editor.createFold([[1, 0], [1, 30]]) - # expect(editor.visibleLines.find('.line:eq(1)').html()).toMatch / $/ + expect(editor.getCursorBufferPosition()).toEqual [3, 0] describe "editor-path-change event", -> it "emits event when buffer's path is changed", -> diff --git a/src/app/editor.coffee b/src/app/editor.coffee index b1b345d46..fbcb0cab9 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -173,8 +173,8 @@ class Editor extends View @isFocused = false @removeClass 'focused' - @visibleLines.on 'mousedown', '.fold-placeholder', (e) => - @destroyFold($(e.currentTarget).attr('foldId')) + @visibleLines.on 'mousedown', '.fold.line', (e) => + @destroyFold($(e.currentTarget).attr('fold-id')) false @visibleLines.on 'mousedown', (e) => @@ -464,9 +464,11 @@ class Editor extends View $$ -> for line in lines - lineClass = 'line' - lineClass += ' fold' if line.fold? - @div class: lineClass, => + if fold = line.fold + lineAttributes = { class: 'fold line', 'fold-id': fold.id } + else + lineAttributes = { class: 'line' } + @div lineAttributes, => if line.text == '' @raw ' ' if line.text == '' else @@ -690,7 +692,7 @@ class Editor extends View destroyFold: (foldId) -> fold = @renderer.foldsById[foldId] fold.destroy() - @setCursorBufferPosition(fold.start) + @setCursorBufferPosition([fold.startRow, 0]) splitLeft: -> @pane()?.splitLeft(@copy()).wrappedView From 87d0c1d18934305310275596f2ff523e84c6478a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 21 May 2012 18:35:03 -0700 Subject: [PATCH 36/61] Can use ffdescribe, ffit, fffdescribe, fffit to raise the focus priority. --- Rakefile | 2 +- vendor/jasmine-focused.js | 43 +++++++++++++++++++++++++++++++-------- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/Rakefile b/Rakefile index a6abfc1eb..2cbb0d9a0 100644 --- a/Rakefile +++ b/Rakefile @@ -86,7 +86,7 @@ end desc "Remove any 'fit' or 'fdescribe' focus directives from the specs" task :nof do - system %{find . -name *spec.coffee | xargs sed -E -i "" "s/f(it|describe) +(['\\"])/\\1 \\2/g"} + system %{find . -name *spec.coffee | xargs sed -E -i "" "s/f+(it|describe) +(['\\"])/\\1 \\2/g"} end task :"verify-prerequisites" do diff --git a/vendor/jasmine-focused.js b/vendor/jasmine-focused.js index b59bbac85..c10b1b976 100644 --- a/vendor/jasmine-focused.js +++ b/vendor/jasmine-focused.js @@ -1,20 +1,45 @@ -var fdescribe = function(description, specDefinitions) { - jasmine.getEnv().focus = true +var setGlobalFocusPriority = function(priority) { + env = jasmine.getEnv(); + if (!env.focusPriority) env.focusPriority = 1; + if (priority > env.focusPriority) env.focusPriority = priority; +}; + +var fdescribe = function(description, specDefinitions, priority) { + if (!priority) priority = 1; + setGlobalFocusPriority(priority) var suite = describe(description, specDefinitions); - suite.focus = true; + suite.focusPriority = priority; return suite; }; -var fit = function(description, definition) { - jasmine.getEnv().focus = true +var ffdescribe = function(description, specDefinitions) { + fdescribe(description, specDefinitions, 2); +}; + +var fffdescribe = function(description, specDefinitions) { + fdescribe(description, specDefinitions, 3); +}; + +var fit = function(description, definition, priority) { + if (!priority) priority = 1; + setGlobalFocusPriority(priority); var spec = it(description, definition); - spec.focus = true; + spec.focusPriority = priority; return spec; }; +var ffit = function(description, specDefinitions) { + fit(description, specDefinitions, 2); +}; + +var fffit = function(description, specDefinitions) { + fit(description, specDefinitions, 3); +}; + var fSpecFilter = function(specOrSuite) { - if (!jasmine.getEnv().focus) return true; - if (specOrSuite.focus) return true; + globalFocusPriority = jasmine.getEnv().focusPriority; + if (!globalFocusPriority) return true; + if (specOrSuite.focusPriority >= globalFocusPriority) return true; var parent = specOrSuite.parentSuite || specOrSuite.suite; if (!parent) return false; @@ -29,7 +54,7 @@ jasmine.AtomReporter.prototype.specFilter = function(spec) { paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); } - if (!paramMap.spec && !jasmine.getEnv().focus) { + if (!paramMap.spec && !jasmine.getEnv().focusPriority) { return true; } From 581998f94b525e58ecfe08cb9d60969f4039964f Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 21 May 2012 18:35:51 -0700 Subject: [PATCH 37/61] Oh, un-F --- spec/app/editor-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index b5ca972fa..cbca11e80 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -2426,7 +2426,7 @@ describe "Editor", -> expect(editor.getCursorScreenPosition()).toEqual [5, 0] describe "when a fold placeholder line is clicked", -> - fit "removes the associated fold and places the cursor at its beginning", -> + it "removes the associated fold and places the cursor at its beginning", -> editor.getSelection().setBufferRange(new Range([3, 0], [9, 0])) editor.trigger 'fold-selection' From f3cada9e201e5c357f703203f56dee527e7137d0 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 21 May 2012 18:46:16 -0700 Subject: [PATCH 38/61] Nuke some fold tests that don't matter anymore --- spec/app/renderer-spec.coffee | 112 ---------------------------------- 1 file changed, 112 deletions(-) diff --git a/spec/app/renderer-spec.coffee b/spec/app/renderer-spec.coffee index 99575012e..42fe6cd24 100644 --- a/spec/app/renderer-spec.coffee +++ b/spec/app/renderer-spec.coffee @@ -43,98 +43,6 @@ describe "Renderer", -> expect(renderer.lineForRow(3).text).toBe ' var pivot = items.shift(), current, left = [], ' expect(renderer.lineForRow(4).text).toBe 'right = [];' - describe "when a fold is created on the last screen line of a wrapped buffer line", -> - it "inserts the placeholder in the correct location and fires a change event", -> - fold = renderer.createFold([[3, 52], [3, 56]]) - expect(renderer.lineForRow(3).text).toBe ' var pivot = items.shift(), current, left = [], ' - expect(renderer.lineForRow(4).text).toBe 'r... = [];' - expect(renderer.lineForRow(5).text).toBe ' while(items.length > 0) {' - - expect(changeHandler).toHaveBeenCalled() - [[event]]= changeHandler.argsForCall - expect(event.oldRange).toEqual([[3, 0], [4, 11]]) - expect(event.newRange).toEqual([[3, 0], [4, 10]]) - - changeHandler.reset() - fold.destroy() - - expect(renderer.lineForRow(3).text).toBe ' var pivot = items.shift(), current, left = [], ' - expect(renderer.lineForRow(4).text).toBe 'right = [];' - expect(renderer.lineForRow(5).text).toBe ' while(items.length > 0) {' - - expect(changeHandler).toHaveBeenCalled() - [[event]]= changeHandler.argsForCall - expect(event.oldRange).toEqual([[3, 0], [4, 10]]) - expect(event.newRange).toEqual([[3, 0], [4, 11]]) - - describe "when a fold is created on the penultimate screen line of a wrapped buffer line", -> - beforeEach -> - renderer.setMaxLineLength(36) - changeHandler.reset() - - it "inserts the placeholder in the correct location and fires a change event", -> - fold = renderer.createFold([[6, 29], [6, 33]]) - expect(renderer.lineForRow(8).text).toBe " current < pivot ? " - expect(renderer.lineForRow(9).text).toBe "left....(current) : " - expect(renderer.lineForRow(10).text).toBe "right.push(current);" - - expect(changeHandler).toHaveBeenCalled() - [[event]]= changeHandler.argsForCall - expect(event.oldRange).toEqual([[8, 0], [10, 20]]) - expect(event.newRange).toEqual([[8, 0], [10, 20]]) - - changeHandler.reset() - fold.destroy() - - expect(changeHandler).toHaveBeenCalled() - [[event]]= changeHandler.argsForCall - expect(event.oldRange).toEqual([[8, 0], [10, 20]]) - expect(event.newRange).toEqual([[8, 0], [10, 20]]) - - describe "when a fold ends on the penultimate screen line of a wrapped buffer line", -> - beforeEach -> - renderer.setMaxLineLength(36) - changeHandler.reset() - - it "inserts the placeholder in the correct location and fires a change event", -> - fold = renderer.createFold([[5, 0], [6, 29]]) - expect(renderer.lineForRow(6).text).toBe " while(items.length > 0) {" - expect(renderer.lineForRow(7).text).toBe "...push(current) : " - expect(renderer.lineForRow(8).text).toBe "right.push(current);" - - expect(changeHandler).toHaveBeenCalled() - [[event]]= changeHandler.argsForCall - expect(event.oldRange).toEqual([[7, 0], [10, 20]]) - expect(event.newRange).toEqual([[7, 0], [8, 20]]) - - changeHandler.reset() - fold.destroy() - - expect(changeHandler).toHaveBeenCalled() - [[event]]= changeHandler.argsForCall - expect(event.oldRange).toEqual([[7, 0], [8, 20]]) - expect(event.newRange).toEqual([[7, 0], [10, 20]]) - - describe "when there is a fold placeholder straddling the max length boundary", -> - it "wraps the line before the fold placeholder", -> - renderer.createFold([[3, 49], [6, 1]]) - - expect(renderer.lineForRow(3).text).toBe ' var pivot = items.shift(), current, left = []' - expect(renderer.lineForRow(4).text).toBe '... current < pivot ? left.push(current) : ' - expect(renderer.lineForRow(5).text).toBe 'right.push(current);' - expect(renderer.lineForRow(6).text).toBe ' }' - - renderer.createFold([[6, 56], [8, 15]]) - expect(renderer.lineForRow(5).text).toBe 'right.push(...(left).concat(pivot).concat(sort(rig' - expect(renderer.lineForRow(6).text).toBe 'ht));' - expect(renderer.lineForRow(7).text).toBe ' };' - - describe "when there is a fold placeholder ending at the max length boundary", -> - it "wraps the line after the fold placeholder", -> - renderer.createFold([[3, 47], [3, 51]]) - expect(renderer.lineForRow(3).text).toBe ' var pivot = items.shift(), current, left = ...' - expect(renderer.lineForRow(4).text).toBe 'right = [];' - describe "when the buffer changes", -> describe "when buffer lines are updated", -> describe "when the update makes a soft-wrapped line shorter than the max line length", -> @@ -465,26 +373,6 @@ describe "Renderer", -> expect(renderer.bufferPositionForScreenPosition([5, 0])).toEqual [8, 0] expect(renderer.bufferPositionForScreenPosition([9, 2])).toEqual [12, 2] - describe "when there is a single fold spanning a single line", -> - it "translates positions to account for folded characters and the placeholder", -> - renderer.createFold([[4, 10], [4, 15]]) - - expect(renderer.screenPositionForBufferPosition([4, 5])).toEqual [4, 5] - expect(renderer.bufferPositionForScreenPosition([4, 5])).toEqual [4, 5] - - expect(renderer.screenPositionForBufferPosition([4, 15])).toEqual [4, 13] - expect(renderer.bufferPositionForScreenPosition([4, 13])).toEqual [4, 15] - - expect(renderer.screenPositionForBufferPosition([4, 20])).toEqual [4, 18] - expect(renderer.bufferPositionForScreenPosition([4, 18])).toEqual [4, 20] - - describe "when there is a fold on a wrapped line", -> - it "translates positions accounting for both the fold and the wrapped line", -> - renderer.setMaxLineLength(50) - renderer.createFold([[3, 51], [3, 58]]) - expect(renderer.screenPositionForBufferPosition([3, 58])).toEqual [4, 3] - expect(renderer.bufferPositionForScreenPosition([4, 3])).toEqual [3, 58] - describe ".clipScreenPosition(screenPosition, wrapBeyondNewlines: false, wrapAtSoftNewlines: false, skipAtomicTokens: false)", -> beforeEach -> renderer.setMaxLineLength(50) From 031c04b888fe9b24107e36da95e51d12b044d496 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 21 May 2012 18:48:28 -0700 Subject: [PATCH 39/61] Clean up remaining position translation spec for folds. It still has one legit failure though. --- spec/app/renderer-spec.coffee | 37 ++++++++++++++--------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/spec/app/renderer-spec.coffee b/spec/app/renderer-spec.coffee index 42fe6cd24..12292c108 100644 --- a/spec/app/renderer-spec.coffee +++ b/spec/app/renderer-spec.coffee @@ -345,33 +345,26 @@ describe "Renderer", -> expect(event.newRange).toEqual [[1, 0], [1, 9]] describe "position translation", -> - describe "when there is single fold spanning multiple lines", -> - it "translates positions to account for folded lines and characters and the placeholder", -> - renderer.createFold([[4, 29], [7, 4]]) + it "translates positions to account for folded lines and characters and the placeholder", -> + renderer.createFold(4, 7) - # preceding fold: identity - expect(renderer.screenPositionForBufferPosition([3, 0])).toEqual [3, 0] - expect(renderer.screenPositionForBufferPosition([4, 0])).toEqual [4, 0] - expect(renderer.screenPositionForBufferPosition([4, 29])).toEqual [4, 29] + # preceding fold: identity + expect(renderer.screenPositionForBufferPosition([3, 0])).toEqual [3, 0] + expect(renderer.screenPositionForBufferPosition([4, 0])).toEqual [4, 0] - expect(renderer.bufferPositionForScreenPosition([3, 0])).toEqual [3, 0] - expect(renderer.bufferPositionForScreenPosition([4, 0])).toEqual [4, 0] - expect(renderer.bufferPositionForScreenPosition([4, 29])).toEqual [4, 29] + expect(renderer.bufferPositionForScreenPosition([3, 0])).toEqual [3, 0] + expect(renderer.bufferPositionForScreenPosition([4, 0])).toEqual [4, 0] - # inside of fold: translate to the start of the fold - expect(renderer.screenPositionForBufferPosition([4, 35])).toEqual [4, 29] - expect(renderer.screenPositionForBufferPosition([5, 5])).toEqual [4, 29] + # inside of fold: translate to the start of the fold + expect(renderer.screenPositionForBufferPosition([4, 35])).toEqual [4, 0] + expect(renderer.screenPositionForBufferPosition([5, 5])).toEqual [4, 0] - # following fold, on last line of fold - expect(renderer.screenPositionForBufferPosition([7, 4])).toEqual [4, 32] - expect(renderer.bufferPositionForScreenPosition([4, 32])).toEqual [7, 4] + # following fold + expect(renderer.screenPositionForBufferPosition([8, 0])).toEqual [5, 0] + expect(renderer.screenPositionForBufferPosition([11, 2])).toEqual [8, 2] - # # following fold, subsequent line - expect(renderer.screenPositionForBufferPosition([8, 0])).toEqual [5, 0] - expect(renderer.screenPositionForBufferPosition([11, 13])).toEqual [8, 13] - - expect(renderer.bufferPositionForScreenPosition([5, 0])).toEqual [8, 0] - expect(renderer.bufferPositionForScreenPosition([9, 2])).toEqual [12, 2] + expect(renderer.bufferPositionForScreenPosition([5, 0])).toEqual [8, 0] + expect(renderer.bufferPositionForScreenPosition([9, 2])).toEqual [12, 2] describe ".clipScreenPosition(screenPosition, wrapBeyondNewlines: false, wrapAtSoftNewlines: false, skipAtomicTokens: false)", -> beforeEach -> From 72f1874c5b2d462bbbe24167a9a1b1190a00a0f7 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 21 May 2012 18:55:05 -0700 Subject: [PATCH 40/61] Nuke more old fold specs --- spec/app/renderer-spec.coffee | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/spec/app/renderer-spec.coffee b/spec/app/renderer-spec.coffee index 12292c108..b7246e26c 100644 --- a/spec/app/renderer-spec.coffee +++ b/spec/app/renderer-spec.coffee @@ -402,11 +402,6 @@ describe "Renderer", -> expect(renderer.clipScreenPosition([3, 58])).toEqual [3, 50] expect(renderer.clipScreenPosition([3, 1000])).toEqual [3, 50] - describe "if there is a fold placeholder at the very end of the screen line", -> - it "clips positions at the end of the screen line to the position preceding the placeholder", -> - renderer.createFold([[3, 47], [3, 51]]) - expect(renderer.clipScreenPosition([3, 50])).toEqual [3, 47] - describe "when wrapAtSoftNewlines is true", -> it "wraps positions at the end of soft-wrapped lines to the next screen line", -> expect(renderer.clipScreenPosition([3, 50], wrapAtSoftNewlines: true)).toEqual [3, 50] @@ -415,12 +410,6 @@ describe "Renderer", -> expect(renderer.clipScreenPosition([3, 1000], wrapAtSoftNewlines: true)).toEqual [4, 0] describe "when skipAtomicTokens is false (the default)", -> - it "clips screen positions in the middle of fold placeholders to the to the beginning of fold placeholders", -> - renderer.createFold([[3, 55], [3, 59]]) - expect(renderer.clipScreenPosition([4, 5])).toEqual [4, 4] - expect(renderer.clipScreenPosition([4, 6])).toEqual [4, 4] - expect(renderer.clipScreenPosition([4, 7])).toEqual [4, 7] - it "clips screen positions in the middle of atomic tab characters to the beginning of the character", -> buffer.insert([0, 0], '\t') expect(renderer.clipScreenPosition([0, 0])).toEqual [0, 0] @@ -428,13 +417,7 @@ describe "Renderer", -> expect(renderer.clipScreenPosition([0, tabText.length])).toEqual [0, tabText.length] describe "when skipAtomicTokens is true", -> - it "wraps the screen positions in the middle of fold placeholders to the end of the placeholder", -> - renderer.createFold([[3, 55], [3, 59]]) - expect(renderer.clipScreenPosition([4, 4], skipAtomicTokens: true)).toEqual [4, 4] - expect(renderer.clipScreenPosition([4, 5], skipAtomicTokens: true)).toEqual [4, 7] - expect(renderer.clipScreenPosition([4, 6], skipAtomicTokens: true)).toEqual [4, 7] - - it "clips screen positions in the middle of atomic tab characters to the beginning of the character", -> + it "clips screen positions in the middle of atomic tab characters to the end of the character", -> buffer.insert([0, 0], '\t') expect(renderer.clipScreenPosition([0, 0], skipAtomicTokens: true)).toEqual [0, 0] expect(renderer.clipScreenPosition([0, 1], skipAtomicTokens: true)).toEqual [0, tabText.length] From 3f4c7deb303497f13413638379ca94b2ad63ab7b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 21 May 2012 19:00:02 -0700 Subject: [PATCH 41/61] Nuke stray call to Editor.logLines --- spec/app/editor-spec.coffee | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index cbca11e80..797d0b214 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -927,8 +927,6 @@ describe "Editor", -> editor.visibleLines.trigger 'mouseup' expect(editor.getSelectedText()).toBe " return sort(left).concat(pivot).concat(sort(right));" - editor.logLines() - # Quad click point = [12, 3] editor.visibleLines.trigger mousedownEvent(editor: editor, point: point, originalEvent: {detail: 1}) From 6387f512c76b457d5ee04a972d12077aa43aae2a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 21 May 2012 19:02:05 -0700 Subject: [PATCH 42/61] Translate buffer positions inside of folds to column 0 of the first line of the fold --- src/app/screen-line-fragment.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/screen-line-fragment.coffee b/src/app/screen-line-fragment.coffee index 3b9a1d0df..6515e6ac2 100644 --- a/src/app/screen-line-fragment.coffee +++ b/src/app/screen-line-fragment.coffee @@ -49,7 +49,10 @@ class ScreenLineFragment translateColumn: (sourceDeltaType, targetDeltaType, sourceColumn, options={}) -> { skipAtomicTokens } = options - textLength = @text.length + if @fold + textLength = 0 + else + textLength = @text.length sourceColumn = Math.min(sourceColumn, textLength) currentSourceColumn = 0 From 74008ab32921ac28030cf81bace74a9a27bbf6f1 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 21 May 2012 19:20:18 -0700 Subject: [PATCH 43/61] Fold placeholder lines are treated as if they're empty --- spec/app/renderer-spec.coffee | 4 ++++ src/app/line-map.coffee | 4 ++-- src/app/screen-line-fragment.coffee | 12 +++++++----- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/spec/app/renderer-spec.coffee b/spec/app/renderer-spec.coffee index b7246e26c..901e6aee8 100644 --- a/spec/app/renderer-spec.coffee +++ b/spec/app/renderer-spec.coffee @@ -395,6 +395,10 @@ describe "Renderer", -> expect(renderer.clipScreenPosition([0, 30], wrapBeyondNewlines: true)).toEqual [1, 0] expect(renderer.clipScreenPosition([0, 1000], wrapBeyondNewlines: true)).toEqual [1, 0] + it "wraps positions in the middle of fold lines to the next screen line", -> + renderer.createFold(3, 5) + expect(renderer.clipScreenPosition([3, 5], wrapBeyondNewlines: true)).toEqual [4, 0] + describe "when wrapAtSoftNewlines is false (the default)", -> it "clips positions at the end of soft-wrapped lines to the character preceding the end of the line", -> expect(renderer.clipScreenPosition([3, 50])).toEqual [3, 50] diff --git a/src/app/line-map.coffee b/src/app/line-map.coffee index 2b95436df..3e12a71f2 100644 --- a/src/app/line-map.coffee +++ b/src/app/line-map.coffee @@ -121,8 +121,8 @@ class LineMap targetDelta = traversalResult[targetDeltaType] return targetDelta unless lastLineFragment - maxSourceColumn = sourceDelta.column + lastLineFragment.text.length - maxTargetColumn = targetDelta.column + lastLineFragment.text.length + maxSourceColumn = sourceDelta.column + lastLineFragment.textLength() + maxTargetColumn = targetDelta.column + lastLineFragment.textLength() if lastLineFragment.isSoftWrapped() and sourcePosition.column >= maxSourceColumn if wrapAtSoftNewlines diff --git a/src/app/screen-line-fragment.coffee b/src/app/screen-line-fragment.coffee index 6515e6ac2..8dfdde1e2 100644 --- a/src/app/screen-line-fragment.coffee +++ b/src/app/screen-line-fragment.coffee @@ -49,11 +49,7 @@ class ScreenLineFragment translateColumn: (sourceDeltaType, targetDeltaType, sourceColumn, options={}) -> { skipAtomicTokens } = options - if @fold - textLength = 0 - else - textLength = @text.length - sourceColumn = Math.min(sourceColumn, textLength) + sourceColumn = Math.min(sourceColumn, @textLength()) currentSourceColumn = 0 currentTargetColumn = 0 @@ -75,6 +71,12 @@ class ScreenLineFragment remainingColumns = sourceColumn - currentSourceColumn currentTargetColumn + remainingColumns + textLength: -> + if @fold + textLength = 0 + else + textLength = @text.length + isSoftWrapped: -> @screenDelta.row == 1 and @bufferDelta.row == 0 From ed271fee0fd829c08fc92a171d303adac62e570a Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 22 May 2012 09:11:09 -0700 Subject: [PATCH 44/61] Remove unused methods from Fold --- src/app/fold.coffee | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/app/fold.coffee b/src/app/fold.coffee index 5e0f2433f..fef7c4db0 100644 --- a/src/app/fold.coffee +++ b/src/app/fold.coffee @@ -15,9 +15,6 @@ class Fold destroy: -> @renderer.destroyFold(this) - getRange: -> - throw "Don't worry about this yet -- sobo" - getBufferDelta: -> new Point(@endRow - @startRow + 1, 0) @@ -44,12 +41,4 @@ class Fold return row if row < oldRange.start.row deltaFromOldRangeEndRow = row - oldRange.end.row - newRange.end.row + deltaFromOldRangeEndRow - - compare: (other) -> - other - # startComparison = @start.compare(other.start) - # if startComparison == 0 - # other.end.compare(@end) - # else - # startComparison + newRange.end.row + deltaFromOldRangeEndRow \ No newline at end of file From a7d43c2cca789d0c7d57375156dda45decc1e685 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 22 May 2012 09:38:14 -0700 Subject: [PATCH 45/61] meta-alt-u unfolds lines at cursor --- spec/app/editor-spec.coffee | 14 ++++++++++++++ src/app/editor.coffee | 4 ++++ src/app/keymaps/editor.coffee | 1 + 3 files changed, 19 insertions(+) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 797d0b214..01b7885d6 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -2436,6 +2436,20 @@ describe "Editor", -> expect(editor.getCursorBufferPosition()).toEqual [3, 0] + describe "when a cursor is on a fold placeholder line", -> + it "removes the associated fold and places the cursor at its beginning", -> + editor.getSelection().setBufferRange(new Range([3, 0], [9, 0])) + editor.trigger 'fold-selection' + + editor.setCursorBufferPosition([3,0]) + editor.trigger 'unfold' + + expect(editor.find('.fold')).not.toExist() + expect(editor.visibleLines.find('.line:eq(4)').text()).toMatch /4-+/ + expect(editor.visibleLines.find('.line:eq(5)').text()).toMatch /5/ + + expect(editor.getCursorBufferPosition()).toEqual [3, 0] + describe "editor-path-change event", -> it "emits event when buffer's path is changed", -> editor = new Editor diff --git a/src/app/editor.coffee b/src/app/editor.coffee index fbcb0cab9..8031abdc2 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -119,6 +119,7 @@ class Editor extends View 'redo': @redo 'toggle-soft-wrap': @toggleSoftWrap 'fold-selection': @foldSelection + 'unfold': => @unfoldRow(@getCursorBufferPosition().row) 'split-left': @splitLeft 'split-right': @splitRight 'split-up': @splitUp @@ -681,6 +682,9 @@ class Editor extends View foldSelection: -> @getSelection().fold() + unfoldRow: (row) -> + @renderer.largestFoldForBufferRow(row)?.destroy() + undo: -> if ranges = @buffer.undo() @setSelectedBufferRanges(ranges) diff --git a/src/app/keymaps/editor.coffee b/src/app/keymaps/editor.coffee index 37d2e9a8f..fe392b3dd 100644 --- a/src/app/keymaps/editor.coffee +++ b/src/app/keymaps/editor.coffee @@ -28,6 +28,7 @@ window.keymap.bindKeys '.editor', 'meta-Z': 'redo' 'alt-meta-w': 'toggle-soft-wrap' 'alt-meta-f': 'fold-selection' + 'alt-meta-u': 'unfold' 'alt-meta-left': 'split-left' 'alt-meta-right': 'split-right' 'alt-meta-up': 'split-up' From 7b4ebcdd4277dd4195c7c322f20a491ae26baee4 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 22 May 2012 12:09:37 -0700 Subject: [PATCH 46/61] Changes inside a fold correctly change the start/end row --- spec/app/renderer-spec.coffee | 11 +++++++++-- src/app/fold.coffee | 19 +++++++++++++------ src/app/renderer.coffee | 5 ++--- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/spec/app/renderer-spec.coffee b/spec/app/renderer-spec.coffee index 901e6aee8..2c890ddcc 100644 --- a/spec/app/renderer-spec.coffee +++ b/spec/app/renderer-spec.coffee @@ -304,15 +304,22 @@ describe "Renderer", -> describe "when the old range is inside a fold", -> it "does not trigger a change event, but updates the fold and ensures the change is present when the fold is destroyed", -> - buffer.change([[2, 0], [2, 0]], 'abc') + buffer.insert([2, 0], '\n') + expect(fold1.startRow).toBe 2 + expect(fold1.endRow).toBe 5 expect(renderer.lineForRow(1).text).toBe "1" + expect(renderer.lineForRow(2).text).toBe "" expect(renderer.lineForRow(2).fold).toBe fold1 + expect(renderer.lineForRow(2).bufferDelta).toEqual [4, 0] expect(renderer.lineForRow(3).text).toMatch "5" expect(renderer.lineForRow(4).fold).toBe fold2 expect(renderer.lineForRow(5).text).toMatch /^9-+/ - expect(changeHandler).not.toHaveBeenCalled() + expect(changeHandler).toHaveBeenCalled() + [[event]] = changeHandler.argsForCall + expect(event.oldRange).toEqual [[2, 0], [2, 1]] + expect(event.newRange).toEqual [[2, 0], [2, 0]] describe "when the old range surrounds a fold", -> it "removes the fold and replaces the fold placeholder with the new text", -> diff --git a/src/app/fold.coffee b/src/app/fold.coffee index fef7c4db0..c8058d92e 100644 --- a/src/app/fold.coffee +++ b/src/app/fold.coffee @@ -27,8 +27,8 @@ class Fold return changeInsideFold = @startRow <= oldRange.start.row and @endRow >= oldRange.end.row - @startRow = @updateAnchorRow(@startRow, event) - @endRow = @updateAnchorRow(@endRow, event) + @updateStartRow(event) + @updateEndRow(event) if @startRow != oldStartRow @renderer.unregisterFold(oldStartRow, this) @@ -36,9 +36,16 @@ class Fold changeInsideFold - updateAnchorRow: (row, event) -> + updateStartRow: (event) -> { newRange, oldRange } = event - return row if row < oldRange.start.row + return if oldRange.start.row >= @startRow - deltaFromOldRangeEndRow = row - oldRange.end.row - newRange.end.row + deltaFromOldRangeEndRow \ No newline at end of file + deltaFromOldRangeEndRow = @startRow - oldRange.end.row + @startRow = newRange.end.row + deltaFromOldRangeEndRow + + updateEndRow: (event) -> + { newRange, oldRange } = event + return if oldRange.start.row > @endRow + + deltaFromOldRangeEndRow = @endRow - oldRange.end.row + @endRow = newRange.end.row + deltaFromOldRangeEndRow diff --git a/src/app/renderer.coffee b/src/app/renderer.coffee index 30c560ec2..e04ea9735 100644 --- a/src/app/renderer.coffee +++ b/src/app/renderer.coffee @@ -106,10 +106,9 @@ class Renderer handleBufferChange: (e) -> for row, folds of @activeFolds for fold in new Array(folds...) - changeInsideFold = true if fold.handleBufferChange(e) + fold.handleBufferChange(e) - unless changeInsideFold - @handleHighlighterChange(@lastHighlighterChangeEvent) + @handleHighlighterChange(@lastHighlighterChangeEvent) handleHighlighterChange: (e) -> oldBufferRange = e.oldRange.copy() From 9afc12b05cce1a4be465f128b70c3a7e954d0c82 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Tue, 22 May 2012 13:24:31 -0700 Subject: [PATCH 47/61] Now that folds are linewise, we don't need to adjust the start row of buffer change events --- src/app/renderer.coffee | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/app/renderer.coffee b/src/app/renderer.coffee index e04ea9735..ccd0b054c 100644 --- a/src/app/renderer.coffee +++ b/src/app/renderer.coffee @@ -111,17 +111,12 @@ class Renderer @handleHighlighterChange(@lastHighlighterChangeEvent) handleHighlighterChange: (e) -> - oldBufferRange = e.oldRange.copy() - newBufferRange = e.newRange.copy() - - oldBufferRange.start.row = @bufferRowForScreenRow(@screenRowForBufferRow(oldBufferRange.start.row)) - newBufferRange.start.row = @bufferRowForScreenRow(@screenRowForBufferRow(newBufferRange.start.row)) - - oldScreenRange = @screenLineRangeForBufferRange(oldBufferRange) - newScreenLines = @buildLinesForBufferRows(newBufferRange.start.row, newBufferRange.end.row) + { oldRange, newRange } = e + oldScreenRange = @screenLineRangeForBufferRange(oldRange) + newScreenLines = @buildLinesForBufferRows(newRange.start.row, newRange.end.row) @lineMap.replaceScreenRows oldScreenRange.start.row, oldScreenRange.end.row, newScreenLines - newScreenRange = @screenLineRangeForBufferRange(newBufferRange) + newScreenRange = @screenLineRangeForBufferRange(newRange) @trigger 'change', { oldRange: oldScreenRange, newRange: newScreenRange, bufferChanged: true } From 13416bd6f2390fc4ed45bac05835d64a76bb4da8 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Tue, 22 May 2012 18:58:11 -0700 Subject: [PATCH 48/61] Add Buffer.logLines() --- src/app/buffer.coffee | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/app/buffer.coffee b/src/app/buffer.coffee index ee349f219..0c1f9de6c 100644 --- a/src/app/buffer.coffee +++ b/src/app/buffer.coffee @@ -242,4 +242,9 @@ class Buffer backwardsScanInRange: (regex, range, iterator) -> @scanInRange regex, range, iterator, true + logLines: (start=0, end=@getLastRow())-> + for row in [start..end] + line = @lineForRow(row) + console.log row, line, line.length + _.extend(Buffer.prototype, EventEmitter) From 04f4a9a7659b52932dbf6da575aa537e361e894f Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Tue, 22 May 2012 19:02:13 -0700 Subject: [PATCH 49/61] WIP: Handle buffer change events correctly with respect to folded lines We're handling changes that straddle the start row and are in the middle of the fold, but we're still screwing up when the old range straddles the end row. --- spec/app/renderer-spec.coffee | 81 ++++++++++++++++++++++++++++------- src/app/fold.coffee | 39 ++++++++++++----- src/app/renderer.coffee | 6 ++- 3 files changed, 98 insertions(+), 28 deletions(-) diff --git a/spec/app/renderer-spec.coffee b/spec/app/renderer-spec.coffee index 2c890ddcc..a79fa8512 100644 --- a/spec/app/renderer-spec.coffee +++ b/spec/app/renderer-spec.coffee @@ -1,7 +1,7 @@ Renderer = require 'renderer' Buffer = require 'buffer' -describe "Renderer", -> +fdescribe "Renderer", -> [renderer, buffer, changeHandler, tabText] = [] beforeEach -> tabText = ' ' @@ -303,23 +303,52 @@ describe "Renderer", -> expect(event.newRange).toEqual [[3, 0], [4, 1]] describe "when the old range is inside a fold", -> - it "does not trigger a change event, but updates the fold and ensures the change is present when the fold is destroyed", -> - buffer.insert([2, 0], '\n') - expect(fold1.startRow).toBe 2 - expect(fold1.endRow).toBe 5 + describe "when the end of the new range precedes the end of the fold", -> + it "updates the fold and ensures the change is present when the fold is destroyed", -> + buffer.insert([3, 0], '\n') + expect(fold1.startRow).toBe 2 + expect(fold1.endRow).toBe 5 + + buffer.logLines(0, 10) + renderer.logLines(0, 10) + + expect(renderer.lineForRow(1).text).toBe "1" + expect(renderer.lineForRow(2).text).toBe "2" + expect(renderer.lineForRow(2).fold).toBe fold1 + expect(renderer.lineForRow(2).bufferDelta).toEqual [4, 0] + expect(renderer.lineForRow(3).text).toMatch "5" + expect(renderer.lineForRow(4).fold).toBe fold2 + expect(renderer.lineForRow(5).text).toMatch /^9-+/ + + expect(changeHandler).toHaveBeenCalled() + [[event]] = changeHandler.argsForCall + expect(event.oldRange).toEqual [[2, 0], [2, 1]] + expect(event.newRange).toEqual [[2, 0], [2, 1]] + + describe "when the end of the new range exceeds the end of the fold", -> + ffit "expands the fold to contain all the inserted lines", -> + buffer.change([[3, 0], [4, 0]], 'a\nb\nc\nd\n') + expect(fold1.startRow).toBe 2 + expect(fold1.endRow).toBe 7 + + # buffer.logLines(0, 10) + # renderer.logLines(0, 10) + + # expect(renderer.lineForRow(1).text).toBe "1" + # expect(renderer.lineForRow(2).text).toBe "2" + # expect(renderer.lineForRow(2).fold).toBe fold1 + # expect(renderer.lineForRow(2).bufferDelta).toEqual [4, 0] + # expect(renderer.lineForRow(3).text).toMatch "5" + # expect(renderer.lineForRow(4).fold).toBe fold2 + # expect(renderer.lineForRow(5).text).toMatch /^9-+/ + + # expect(changeHandler).toHaveBeenCalled() + # [[event]] = changeHandler.argsForCall + # expect(event.oldRange).toEqual [[2, 0], [2, 1]] + # expect(event.newRange).toEqual [[2, 0], [2, 1]] + - expect(renderer.lineForRow(1).text).toBe "1" - expect(renderer.lineForRow(2).text).toBe "" - expect(renderer.lineForRow(2).fold).toBe fold1 - expect(renderer.lineForRow(2).bufferDelta).toEqual [4, 0] - expect(renderer.lineForRow(3).text).toMatch "5" - expect(renderer.lineForRow(4).fold).toBe fold2 - expect(renderer.lineForRow(5).text).toMatch /^9-+/ - expect(changeHandler).toHaveBeenCalled() - [[event]] = changeHandler.argsForCall - expect(event.oldRange).toEqual [[2, 0], [2, 1]] - expect(event.newRange).toEqual [[2, 0], [2, 0]] describe "when the old range surrounds a fold", -> it "removes the fold and replaces the fold placeholder with the new text", -> @@ -351,6 +380,26 @@ describe "Renderer", -> expect(event.oldRange).toEqual [[1, 0], [3, 2]] expect(event.newRange).toEqual [[1, 0], [1, 9]] + describe "when the old range straddles the beginning of a fold", -> + describe "when lines are added to the buffer", -> + it "replaces lines in the portion of the range that precedes the fold and adjusts the end of the fold to encompass additional lines", -> + buffer.change([[1, 1], [3, 0]], "a\nb\nc\nd\n") + + expect(fold1.startRow).toBe 2 + expect(fold1.endRow).toBe 6 + buffer.logLines(0, 10) + console.log "================================================" + renderer.logLines(0, 10) + + expect(renderer.lineForRow(1).text).toBe '1a' + expect(renderer.lineForRow(2).text).toBe 'b' + expect(renderer.lineForRow(2).fold).toBe fold1 + + + describe "when lines are removed from the buffer", -> + + + describe "position translation", -> it "translates positions to account for folded lines and characters and the placeholder", -> renderer.createFold(4, 7) diff --git a/src/app/fold.coffee b/src/app/fold.coffee index c8058d92e..6d71004a2 100644 --- a/src/app/fold.coffee +++ b/src/app/fold.coffee @@ -15,18 +15,19 @@ class Fold destroy: -> @renderer.destroyFold(this) + inspect: -> + "Fold(#{@startRow}, #{@endRow})" + getBufferDelta: -> new Point(@endRow - @startRow + 1, 0) handleBufferChange: (event) -> oldStartRow = @startRow - { oldRange } = event - if oldRange.start.row <= @startRow and oldRange.end.row >= @endRow - @renderer.unregisterFold(oldStartRow, this) + if @isContainedByRange(event.oldRange) + @renderer.unregisterFold(@startRow, this) return - changeInsideFold = @startRow <= oldRange.start.row and @endRow >= oldRange.end.row @updateStartRow(event) @updateEndRow(event) @@ -34,18 +35,34 @@ class Fold @renderer.unregisterFold(oldStartRow, this) @renderer.registerFold(@startRow, this) - changeInsideFold + isContainedByRange: (range) -> + range.start.row <= @startRow and @endRow <= range.end.row updateStartRow: (event) -> { newRange, oldRange } = event - return if oldRange.start.row >= @startRow - deltaFromOldRangeEndRow = @startRow - oldRange.end.row - @startRow = newRange.end.row + deltaFromOldRangeEndRow + if oldRange.end.row < @startRow + delta = newRange.end.row - oldRange.end.row + else if newRange.end.row < @startRow + delta = newRange.end.row - @startRow + else + delta = 0 + + console.log "start row delta", delta + + @startRow += delta updateEndRow: (event) -> { newRange, oldRange } = event - return if oldRange.start.row > @endRow - deltaFromOldRangeEndRow = @endRow - oldRange.end.row - @endRow = newRange.end.row + deltaFromOldRangeEndRow + if oldRange.end.row <= @endRow + delta = newRange.end.row - oldRange.end.row + else if newRange.end.row <= @endRow + console.log "newRange.end.row", newRange.end.row, " - @endRow", @endRow + delta = newRange.end.row - @endRow + else + delta = 0 + + console.log "end row delta", delta + + @endRow += delta diff --git a/src/app/renderer.coffee b/src/app/renderer.coffee index ccd0b054c..ac1f7c1d1 100644 --- a/src/app/renderer.coffee +++ b/src/app/renderer.coffee @@ -111,12 +111,15 @@ class Renderer @handleHighlighterChange(@lastHighlighterChangeEvent) handleHighlighterChange: (e) -> - { oldRange, newRange } = e + oldRange = @bufferRangeForScreenRange(@screenRangeForBufferRange(e.oldRange.copy())) + newRange = @bufferRangeForScreenRange(@screenRangeForBufferRange(e.newRange.copy())) oldScreenRange = @screenLineRangeForBufferRange(oldRange) + newScreenLines = @buildLinesForBufferRows(newRange.start.row, newRange.end.row) @lineMap.replaceScreenRows oldScreenRange.start.row, oldScreenRange.end.row, newScreenLines newScreenRange = @screenLineRangeForBufferRange(newRange) + console.log "New Screen Range", newScreenRange.inspect() @trigger 'change', { oldRange: oldScreenRange, newRange: newScreenRange, bufferChanged: true } @@ -176,6 +179,7 @@ class Renderer @foldsById[fold.id] = fold unregisterFold: (bufferRow, fold) -> + console.log "unregistering fold", fold.id folds = @activeFolds[bufferRow] _.remove(folds, fold) delete @foldsById[fold.id] From dd7fff4522739f30e209556b2dfda525d3f0a608 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Wed, 23 May 2012 11:03:33 -0700 Subject: [PATCH 50/61] =?UTF-8?q?WIP:=20Adding=20specs=20for=20various=20f?= =?UTF-8?q?old=20corner=20cases=E2=80=A6=20think=20we=20still=20have=20som?= =?UTF-8?q?e=20issues=20and=20gutter=20updates=20are=20failing.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/app/renderer-spec.coffee | 43 ++++++++++++++++------------------- src/app/fold.coffee | 5 ---- src/app/renderer.coffee | 2 -- 3 files changed, 20 insertions(+), 30 deletions(-) diff --git a/spec/app/renderer-spec.coffee b/spec/app/renderer-spec.coffee index a79fa8512..f35115aed 100644 --- a/spec/app/renderer-spec.coffee +++ b/spec/app/renderer-spec.coffee @@ -1,7 +1,7 @@ Renderer = require 'renderer' Buffer = require 'buffer' -fdescribe "Renderer", -> +describe "Renderer", -> [renderer, buffer, changeHandler, tabText] = [] beforeEach -> tabText = ' ' @@ -309,9 +309,6 @@ fdescribe "Renderer", -> expect(fold1.startRow).toBe 2 expect(fold1.endRow).toBe 5 - buffer.logLines(0, 10) - renderer.logLines(0, 10) - expect(renderer.lineForRow(1).text).toBe "1" expect(renderer.lineForRow(2).text).toBe "2" expect(renderer.lineForRow(2).fold).toBe fold1 @@ -326,29 +323,32 @@ fdescribe "Renderer", -> expect(event.newRange).toEqual [[2, 0], [2, 1]] describe "when the end of the new range exceeds the end of the fold", -> - ffit "expands the fold to contain all the inserted lines", -> + it "expands the fold to contain all the inserted lines", -> buffer.change([[3, 0], [4, 0]], 'a\nb\nc\nd\n') expect(fold1.startRow).toBe 2 expect(fold1.endRow).toBe 7 - # buffer.logLines(0, 10) - # renderer.logLines(0, 10) - - # expect(renderer.lineForRow(1).text).toBe "1" - # expect(renderer.lineForRow(2).text).toBe "2" - # expect(renderer.lineForRow(2).fold).toBe fold1 - # expect(renderer.lineForRow(2).bufferDelta).toEqual [4, 0] - # expect(renderer.lineForRow(3).text).toMatch "5" - # expect(renderer.lineForRow(4).fold).toBe fold2 - # expect(renderer.lineForRow(5).text).toMatch /^9-+/ - - # expect(changeHandler).toHaveBeenCalled() - # [[event]] = changeHandler.argsForCall - # expect(event.oldRange).toEqual [[2, 0], [2, 1]] - # expect(event.newRange).toEqual [[2, 0], [2, 1]] + expect(renderer.lineForRow(1).text).toBe "1" + expect(renderer.lineForRow(2).text).toBe "2" + expect(renderer.lineForRow(2).fold).toBe fold1 + expect(renderer.lineForRow(2).bufferDelta).toEqual [6, 0] + expect(renderer.lineForRow(3).text).toMatch "5" + expect(renderer.lineForRow(4).fold).toBe fold2 + expect(renderer.lineForRow(5).text).toMatch /^9-+/ + expect(changeHandler).toHaveBeenCalled() + [[event]] = changeHandler.argsForCall + expect(event.oldRange).toEqual [[2, 0], [2, 1]] + expect(event.newRange).toEqual [[2, 0], [2, 1]] + describe "when the old range straddles the end of the fold", -> + describe "when the end of the new range precedes the end of the fold", -> + it "shortens the fold so its end matches the end of the new range", -> + fold2.destroy() + buffer.change([[3, 0], [6, 1]], 'a') + expect(fold1.startRow).toBe 2 + expect(fold1.endRow).toBe 3 describe "when the old range surrounds a fold", -> it "removes the fold and replaces the fold placeholder with the new text", -> @@ -387,9 +387,6 @@ fdescribe "Renderer", -> expect(fold1.startRow).toBe 2 expect(fold1.endRow).toBe 6 - buffer.logLines(0, 10) - console.log "================================================" - renderer.logLines(0, 10) expect(renderer.lineForRow(1).text).toBe '1a' expect(renderer.lineForRow(2).text).toBe 'b' diff --git a/src/app/fold.coffee b/src/app/fold.coffee index 6d71004a2..244a36bd0 100644 --- a/src/app/fold.coffee +++ b/src/app/fold.coffee @@ -48,8 +48,6 @@ class Fold else delta = 0 - console.log "start row delta", delta - @startRow += delta updateEndRow: (event) -> @@ -58,11 +56,8 @@ class Fold if oldRange.end.row <= @endRow delta = newRange.end.row - oldRange.end.row else if newRange.end.row <= @endRow - console.log "newRange.end.row", newRange.end.row, " - @endRow", @endRow delta = newRange.end.row - @endRow else delta = 0 - console.log "end row delta", delta - @endRow += delta diff --git a/src/app/renderer.coffee b/src/app/renderer.coffee index ac1f7c1d1..d4861ee0e 100644 --- a/src/app/renderer.coffee +++ b/src/app/renderer.coffee @@ -119,7 +119,6 @@ class Renderer newScreenLines = @buildLinesForBufferRows(newRange.start.row, newRange.end.row) @lineMap.replaceScreenRows oldScreenRange.start.row, oldScreenRange.end.row, newScreenLines newScreenRange = @screenLineRangeForBufferRange(newRange) - console.log "New Screen Range", newScreenRange.inspect() @trigger 'change', { oldRange: oldScreenRange, newRange: newScreenRange, bufferChanged: true } @@ -179,7 +178,6 @@ class Renderer @foldsById[fold.id] = fold unregisterFold: (bufferRow, fold) -> - console.log "unregistering fold", fold.id folds = @activeFolds[bufferRow] _.remove(folds, fold) delete @foldsById[fold.id] From c1197c1d0e4c963c8f6953f07cb07d7fcf3de420 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Wed, 23 May 2012 14:56:22 -0700 Subject: [PATCH 51/61] spec :lipstick: --- spec/app/renderer-spec.coffee | 116 ++++++++++++++++------------------ 1 file changed, 55 insertions(+), 61 deletions(-) diff --git a/spec/app/renderer-spec.coffee b/spec/app/renderer-spec.coffee index f35115aed..da184e3c2 100644 --- a/spec/app/renderer-spec.coffee +++ b/spec/app/renderer-spec.coffee @@ -242,6 +242,36 @@ describe "Renderer", -> fold2 = renderer.createFold(6, 8) changeHandler.reset() + describe "when the old range surrounds a fold", -> + it "removes the fold and replaces the selection with the new text", -> + buffer.change([[1, 0], [5, 1]], 'party!') + + expect(renderer.lineForRow(0).text).toBe "0" + expect(renderer.lineForRow(1).text).toBe "party!" + expect(renderer.lineForRow(2).fold).toBe fold2 + expect(renderer.lineForRow(3).text).toMatch /^9-+/ + + expect(changeHandler).toHaveBeenCalled() + [[event]] = changeHandler.argsForCall + expect(event.oldRange).toEqual [[1, 0], [3, 1]] + expect(event.newRange).toEqual [[1, 0], [1, 6]] + + describe "when the old range surrounds two nested folds", -> + it "removes both folds and replaces the selection with the new text", -> + renderer.createFold(2, 9) + changeHandler.reset() + + buffer.change([[1, 0], [10, 0]], 'goodbye') + + expect(renderer.lineForRow(0).text).toBe "0" + expect(renderer.lineForRow(1).text).toBe "goodbye10" + expect(renderer.lineForRow(2).text).toBe "11" + + expect(changeHandler).toHaveBeenCalled() + [[event]] = changeHandler.argsForCall + expect(event.oldRange).toEqual [[1, 0], [3, 2]] + expect(event.newRange).toEqual [[1, 0], [1, 9]] + describe "when the old range precedes lines with a fold", -> it "updates the buffer and re-positions subsequent folds", -> buffer.change([[0, 0], [1, 1]], 'abc') @@ -271,7 +301,18 @@ describe "Renderer", -> expect(event.oldRange).toEqual [[1, 0], [1, 1]] expect(event.newRange).toEqual [[1, 0], [3, 101]] - describe "when the old range follows lines with a fold", -> + describe "when the old range straddles the beginning of a fold", -> + it "replaces lines in the portion of the range that precedes the fold and adjusts the end of the fold to encompass additional lines", -> + buffer.change([[1, 1], [3, 0]], "a\nb\nc\nd\n") + + expect(fold1.startRow).toBe 2 + expect(fold1.endRow).toBe 6 + + expect(renderer.lineForRow(1).text).toBe '1a' + expect(renderer.lineForRow(2).text).toBe 'b' + expect(renderer.lineForRow(2).fold).toBe fold1 + + describe "when the old range follows a fold", -> it "re-positions the screen ranges for the change event based on the preceding fold", -> buffer.change([[10, 0], [11, 0]], 'abc') @@ -286,22 +327,6 @@ describe "Renderer", -> expect(event.oldRange).toEqual [[6, 0], [7, 2]] # Expands ranges to encompes entire line expect(event.newRange).toEqual [[6, 0], [6, 5]] - describe "when the old range is contained to a single line in-between two folds", -> - it "re-renders the line with the placeholder and re-positions the second fold", -> - buffer.insert([5, 0], 'abc\n') - - expect(renderer.lineForRow(1).text).toBe "1" - expect(renderer.lineForRow(2).fold).toBe fold1 - expect(renderer.lineForRow(3).text).toMatch "abc" - expect(renderer.lineForRow(4).text).toBe "5" - expect(renderer.lineForRow(5).fold).toBe fold2 - expect(renderer.lineForRow(6).text).toMatch /^9-+/ - - expect(changeHandler).toHaveBeenCalled() - [[event]] = changeHandler.argsForCall - expect(event.oldRange).toEqual [[3, 0], [3, 1]] - expect(event.newRange).toEqual [[3, 0], [4, 1]] - describe "when the old range is inside a fold", -> describe "when the end of the new range precedes the end of the fold", -> it "updates the fold and ensures the change is present when the fold is destroyed", -> @@ -345,57 +370,26 @@ describe "Renderer", -> describe "when the end of the new range precedes the end of the fold", -> it "shortens the fold so its end matches the end of the new range", -> fold2.destroy() - buffer.change([[3, 0], [6, 1]], 'a') + buffer.change([[3, 0], [6, 0]], 'a\n') expect(fold1.startRow).toBe 2 - expect(fold1.endRow).toBe 3 + expect(fold1.endRow).toBe 4 - describe "when the old range surrounds a fold", -> - it "removes the fold and replaces the fold placeholder with the new text", -> - buffer.change([[1, 0], [5, 1]], 'party!') + describe "when the old range is contained to a single line in-between two folds", -> + it "re-renders the line with the placeholder and re-positions the second fold", -> + buffer.insert([5, 0], 'abc\n') - expect(renderer.lineForRow(0).text).toBe "0" - expect(renderer.lineForRow(1).text).toBe "party!" - expect(renderer.lineForRow(2).fold).toBe fold2 - expect(renderer.lineForRow(3).text).toMatch /^9-+/ + expect(renderer.lineForRow(1).text).toBe "1" + expect(renderer.lineForRow(2).fold).toBe fold1 + expect(renderer.lineForRow(3).text).toMatch "abc" + expect(renderer.lineForRow(4).text).toBe "5" + expect(renderer.lineForRow(5).fold).toBe fold2 + expect(renderer.lineForRow(6).text).toMatch /^9-+/ expect(changeHandler).toHaveBeenCalled() [[event]] = changeHandler.argsForCall - expect(event.oldRange).toEqual [[1, 0], [3, 1]] - expect(event.newRange).toEqual [[1, 0], [1, 6]] - - describe "when the old range surrounds two nested folds", -> - it "removes both folds and replaces the fold placeholder with the new text", -> - renderer.createFold(2, 9) - changeHandler.reset() - - buffer.change([[1, 0], [10, 0]], 'goodbye') - - expect(renderer.lineForRow(0).text).toBe "0" - expect(renderer.lineForRow(1).text).toBe "goodbye10" - expect(renderer.lineForRow(2).text).toBe "11" - - expect(changeHandler).toHaveBeenCalled() - [[event]] = changeHandler.argsForCall - expect(event.oldRange).toEqual [[1, 0], [3, 2]] - expect(event.newRange).toEqual [[1, 0], [1, 9]] - - describe "when the old range straddles the beginning of a fold", -> - describe "when lines are added to the buffer", -> - it "replaces lines in the portion of the range that precedes the fold and adjusts the end of the fold to encompass additional lines", -> - buffer.change([[1, 1], [3, 0]], "a\nb\nc\nd\n") - - expect(fold1.startRow).toBe 2 - expect(fold1.endRow).toBe 6 - - expect(renderer.lineForRow(1).text).toBe '1a' - expect(renderer.lineForRow(2).text).toBe 'b' - expect(renderer.lineForRow(2).fold).toBe fold1 - - - describe "when lines are removed from the buffer", -> - - + expect(event.oldRange).toEqual [[3, 0], [3, 1]] + expect(event.newRange).toEqual [[3, 0], [4, 1]] describe "position translation", -> it "translates positions to account for folded lines and characters and the placeholder", -> From 9b83ce6545bb04d8028e2a3fe76167052d765820 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Thu, 24 May 2012 11:17:37 -0700 Subject: [PATCH 52/61] renderer tells editor when the gutter needs to be updated --- benchmark/benchmark-suite.coffee | 2 +- spec/app/editor-spec.coffee | 10 +++++++++- spec/app/renderer-spec.coffee | 27 +++++++++++++++++++++++++++ src/app/editor.coffee | 4 ++-- src/app/renderer.coffee | 18 +++++++++++------- 5 files changed, 50 insertions(+), 11 deletions(-) diff --git a/benchmark/benchmark-suite.coffee b/benchmark/benchmark-suite.coffee index 276c330ac..9c1fc04ba 100644 --- a/benchmark/benchmark-suite.coffee +++ b/benchmark/benchmark-suite.coffee @@ -27,7 +27,7 @@ describe "editor.", -> editor.setBuffer new Buffer(require.resolve('fixtures/medium.coffee')) describe "at-begining.", -> - benchmark "insert-delete", -> + fbenchmark "insert-delete", -> editor.insertText('x') editor.backspace() diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 01b7885d6..49ea60881 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -583,11 +583,19 @@ describe "Editor", -> expect(editor.gutter.find('.line-number:eq(5)').text()).toBe '5' describe "when there are folds", -> - it "skips line numbers", -> + it "skips line numbers covered by the fold and updates them when the fold changes", -> editor.createFold(3, 5) expect(editor.gutter.find('.line-number:eq(3)').text()).toBe '4' expect(editor.gutter.find('.line-number:eq(4)').text()).toBe '7' + buffer.insert([4,0], "\n\n") + expect(editor.gutter.find('.line-number:eq(3)').text()).toBe '4' + expect(editor.gutter.find('.line-number:eq(4)').text()).toBe '9' + + buffer.delete([[3,0], [6,0]]) + expect(editor.gutter.find('.line-number:eq(3)').text()).toBe '4' + expect(editor.gutter.find('.line-number:eq(4)').text()).toBe '6' + describe "when the scrollView is scrolled to the right", -> it "adds a drop shadow to the gutter", -> editor.attachToDom() diff --git a/spec/app/renderer-spec.coffee b/spec/app/renderer-spec.coffee index da184e3c2..5f292e75f 100644 --- a/spec/app/renderer-spec.coffee +++ b/spec/app/renderer-spec.coffee @@ -10,6 +10,13 @@ describe "Renderer", -> changeHandler = jasmine.createSpy 'changeHandler' renderer.on 'change', changeHandler + describe "when the buffer changes", -> + it "renders line numbers correctly", -> + originalLineCount = renderer.lineCount() + oneHundredLines = [0..100].join("\n") + buffer.insert([0,0], oneHundredLines) + expect(renderer.lineCount()).toBe 100 + originalLineCount + describe "soft wrapping", -> beforeEach -> renderer.setMaxLineLength(50) @@ -55,6 +62,7 @@ describe "Renderer", -> [[event]]= changeHandler.argsForCall expect(event.oldRange).toEqual([[7, 0], [8, 20]]) expect(event.newRange).toEqual([[7, 0], [7, 47]]) + expect(event.lineNumbersChanged).toBeTruthy() describe "when the update causes a line to softwrap an additional time", -> it "rewraps the line and emits a change event", -> @@ -68,6 +76,7 @@ describe "Renderer", -> [[event]] = changeHandler.argsForCall expect(event.oldRange).toEqual([[7, 0], [8, 20]]) expect(event.newRange).toEqual([[7, 0], [9, 20]]) + expect(event.lineNumbersChanged).toBeTruthy() describe "when buffer lines are inserted", -> it "inserts / updates wrapped lines and emits a change event", -> @@ -81,6 +90,7 @@ describe "Renderer", -> [event] = changeHandler.argsForCall[0] expect(event.oldRange).toEqual([[7, 0], [8, 20]]) expect(event.newRange).toEqual([[7, 0], [10, 20]]) + expect(event.lineNumbersChanged).toBeTruthy() describe "when buffer lines are removed", -> it "removes lines and emits a change event", -> @@ -94,6 +104,7 @@ describe "Renderer", -> [event] = changeHandler.argsForCall[0] expect(event.oldRange).toEqual([[3, 0], [11, 45]]) expect(event.newRange).toEqual([[3, 0], [5, 45]]) + expect(event.lineNumbersChanged).toBeTruthy() describe "position translation", -> it "translates positions accounting for wrapped lines", -> @@ -128,6 +139,7 @@ describe "Renderer", -> [event] = changeHandler.argsForCall[0] expect(event.oldRange).toEqual([[0, 0], [15, 2]]) expect(event.newRange).toEqual([[0, 0], [18, 2]]) + expect(event.lineNumbersChanged).toBeTruthy() describe "folding", -> beforeEach -> @@ -151,6 +163,7 @@ describe "Renderer", -> [event] = changeHandler.argsForCall[0] expect(event.oldRange).toEqual [[4, 0], [7, 1]] expect(event.newRange).toEqual [[4, 0], [4, 101]] + expect(event.lineNumbersChanged).toBeTruthy() changeHandler.reset() fold.destroy() @@ -165,6 +178,7 @@ describe "Renderer", -> [[event]] = changeHandler.argsForCall expect(event.oldRange).toEqual [[4, 0], [4, 101]] expect(event.newRange).toEqual [[4, 0], [7, 1]] + expect(event.lineNumbersChanged).toBeTruthy() describe "when a fold spans a single line", -> it "renders a fold placeholder for the folded line but does not skip any lines", -> @@ -181,6 +195,10 @@ describe "Renderer", -> [[event]] = changeHandler.argsForCall expect(event.oldRange).toEqual [[4, 0], [4, 101]] expect(event.newRange).toEqual [[4, 0], [4, 101]] + + # Line numbers don't actually change, but it's not worth the complexity to have this + # be false for single line folds since they are so rare + expect(event.lineNumbersChanged).toBeTruthy() changeHandler.reset() fold.destroy() @@ -196,6 +214,7 @@ describe "Renderer", -> [[event]] = changeHandler.argsForCall expect(event.oldRange).toEqual [[4, 0], [4, 101]] expect(event.newRange).toEqual [[4, 0], [4, 101]] + expect(event.lineNumbersChanged).toBeTruthy() changeHandler.reset() describe "when a fold is nested within another fold", -> @@ -255,6 +274,7 @@ describe "Renderer", -> [[event]] = changeHandler.argsForCall expect(event.oldRange).toEqual [[1, 0], [3, 1]] expect(event.newRange).toEqual [[1, 0], [1, 6]] + expect(event.lineNumbersChanged).toBeTruthy() describe "when the old range surrounds two nested folds", -> it "removes both folds and replaces the selection with the new text", -> @@ -271,6 +291,7 @@ describe "Renderer", -> [[event]] = changeHandler.argsForCall expect(event.oldRange).toEqual [[1, 0], [3, 2]] expect(event.newRange).toEqual [[1, 0], [1, 9]] + expect(event.lineNumbersChanged).toBeTruthy() describe "when the old range precedes lines with a fold", -> it "updates the buffer and re-positions subsequent folds", -> @@ -286,6 +307,7 @@ describe "Renderer", -> [[event]] = changeHandler.argsForCall expect(event.oldRange).toEqual [[0, 0], [1, 1]] expect(event.newRange).toEqual [[0, 0], [0, 3]] + expect(event.lineNumbersChanged).toBeTruthy() changeHandler.reset() fold1.destroy() @@ -300,6 +322,7 @@ describe "Renderer", -> [[event]] = changeHandler.argsForCall expect(event.oldRange).toEqual [[1, 0], [1, 1]] expect(event.newRange).toEqual [[1, 0], [3, 101]] + expect(event.lineNumbersChanged).toBeTruthy() describe "when the old range straddles the beginning of a fold", -> it "replaces lines in the portion of the range that precedes the fold and adjusts the end of the fold to encompass additional lines", -> @@ -326,6 +349,7 @@ describe "Renderer", -> [[event]] = changeHandler.argsForCall expect(event.oldRange).toEqual [[6, 0], [7, 2]] # Expands ranges to encompes entire line expect(event.newRange).toEqual [[6, 0], [6, 5]] + expect(event.lineNumbersChanged).toBeTruthy() describe "when the old range is inside a fold", -> describe "when the end of the new range precedes the end of the fold", -> @@ -346,6 +370,7 @@ describe "Renderer", -> [[event]] = changeHandler.argsForCall expect(event.oldRange).toEqual [[2, 0], [2, 1]] expect(event.newRange).toEqual [[2, 0], [2, 1]] + expect(event.lineNumbersChanged).toBeTruthy() describe "when the end of the new range exceeds the end of the fold", -> it "expands the fold to contain all the inserted lines", -> @@ -365,6 +390,7 @@ describe "Renderer", -> [[event]] = changeHandler.argsForCall expect(event.oldRange).toEqual [[2, 0], [2, 1]] expect(event.newRange).toEqual [[2, 0], [2, 1]] + expect(event.lineNumbersChanged).toBeTruthy() describe "when the old range straddles the end of the fold", -> describe "when the end of the new range precedes the end of the fold", -> @@ -390,6 +416,7 @@ describe "Renderer", -> [[event]] = changeHandler.argsForCall expect(event.oldRange).toEqual [[3, 0], [3, 1]] expect(event.newRange).toEqual [[3, 0], [4, 1]] + expect(event.lineNumbersChanged).toBeTruthy() describe "position translation", -> it "translates positions to account for folded lines and characters and the placeholder", -> diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 8031abdc2..ca4f59db0 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -424,7 +424,7 @@ class Editor extends View @compositeCursor.updateBufferPosition() unless e.bufferChanged if @attached - unless newScreenRange.isSingleLine() and newScreenRange.coversSameRows(oldScreenRange) + if e.lineNumbersChanged @gutter.renderLineNumbers(@getFirstVisibleScreenRow(), @getLastVisibleScreenRow()) @verticalScrollbarContent.height(@lineHeight * @screenLineCount()) @@ -454,7 +454,7 @@ class Editor extends View if rowDelta > 0 @removeLineElements(@lastRenderedScreenRow + 1, @lastRenderedScreenRow + rowDelta) - else + else if rowDelta < 0 @lastRenderedScreenRow += rowDelta @updateVisibleLines() diff --git a/src/app/renderer.coffee b/src/app/renderer.coffee index d4861ee0e..633d5c255 100644 --- a/src/app/renderer.coffee +++ b/src/app/renderer.coffee @@ -37,7 +37,7 @@ class Renderer oldRange = @rangeForAllLines() @buildLineMap() newRange = @rangeForAllLines() - @trigger 'change', { oldRange, newRange } + @trigger 'change', { oldRange, newRange, lineNumbersChanged: true } lineForRow: (row) -> @lineMap.lineForScreenRow(row) @@ -62,7 +62,7 @@ class Renderer @lineMap.replaceScreenRows(oldScreenRange.start.row, oldScreenRange.end.row, lines) newScreenRange = @screenLineRangeForBufferRange(bufferRange) - @trigger 'change', oldRange: oldScreenRange, newRange: newScreenRange + @trigger 'change', oldRange: oldScreenRange, newRange: newScreenRange, lineNumbersChanged: true @trigger 'fold', bufferRange fold @@ -76,7 +76,7 @@ class Renderer @lineMap.replaceScreenRows(oldScreenRange.start.row, oldScreenRange.end.row, lines) newScreenRange = @screenLineRangeForBufferRange(bufferRange) - @trigger 'change', oldRange: oldScreenRange, newRange: newScreenRange + @trigger 'change', oldRange: oldScreenRange, newRange: newScreenRange, lineNumbersChanged: true @trigger 'unfold', bufferRange screenRowForBufferRow: (bufferRow) -> @@ -111,16 +111,20 @@ class Renderer @handleHighlighterChange(@lastHighlighterChangeEvent) handleHighlighterChange: (e) -> - oldRange = @bufferRangeForScreenRange(@screenRangeForBufferRange(e.oldRange.copy())) - newRange = @bufferRangeForScreenRange(@screenRangeForBufferRange(e.newRange.copy())) + newRange = e.newRange.copy() + newRange.start.row = @bufferRowForScreenRow(@screenRowForBufferRow(newRange.start.row)) - oldScreenRange = @screenLineRangeForBufferRange(oldRange) + oldScreenRange = @screenLineRangeForBufferRange(e.oldRange) newScreenLines = @buildLinesForBufferRows(newRange.start.row, newRange.end.row) @lineMap.replaceScreenRows oldScreenRange.start.row, oldScreenRange.end.row, newScreenLines newScreenRange = @screenLineRangeForBufferRange(newRange) - @trigger 'change', { oldRange: oldScreenRange, newRange: newScreenRange, bufferChanged: true } + @trigger 'change', + oldRange: oldScreenRange + newRange: newScreenRange + bufferChanged: true + lineNumbersChanged: !e.oldRange.coversSameRows(newRange) or !oldScreenRange.coversSameRows(newScreenRange) buildLineForBufferRow: (bufferRow) -> @buildLinesForBufferRows(bufferRow, bufferRow) From f97912c1608e0a14f4aa1548a25e122959304893 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Thu, 24 May 2012 11:43:09 -0700 Subject: [PATCH 53/61] :lipstick: --- spec/app/editor-spec.coffee | 166 ++++++++++++++++++------------------ 1 file changed, 84 insertions(+), 82 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 49ea60881..645f4c54f 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -544,6 +544,90 @@ describe "Editor", -> expect(editor.visibleLines.find('.line:eq(0)').text()).toBe editor.buffer.lineForRow(1) expect(editor.visibleLines.find('.line:eq(5)').text()).toBe editor.buffer.lineForRow(6) + describe "when lines are added", -> + beforeEach -> + editor.attachToDom() + setEditorHeightInLines(editor, 5) + spyOn(editor, "scrollTo") + + describe "when the change the precedes the first rendered row", -> + it "inserts and removes rendered lines to account for upstream change", -> + editor.scrollBottom(editor.scrollView.prop('scrollHeight')) + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12) + + buffer.change([[1,0], [3,0]], "1\n2\n3\n") + expect(editor.visibleLines.find(".line").length).toBe 5 + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12) + + describe "when the change straddles the first rendered row", -> + it "doesn't render rows that were not previously rendered", -> + editor.scrollBottom(editor.scrollView.prop('scrollHeight')) + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12) + + buffer.change([[2,0], [7,0]], "2\n3\n4\n5\n6\n7\n8\n9\n") + expect(editor.visibleLines.find(".line").length).toBe 5 + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12) + + describe "when the change the straddles the last rendered row", -> + it "doesn't render rows that were not previously rendered", -> + buffer.change([[2,0], [7,0]], "2\n3\n4\n5\n6\n7\n8\n") + expect(editor.visibleLines.find(".line").length).toBe 5 + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(0) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(4) + + describe "when the change the follows the last rendered row", -> + it "does not change the rendered lines", -> + buffer.change([[12,0], [12,0]], "12\n13\n14\n") + expect(editor.visibleLines.find(".line").length).toBe 5 + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(0) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(4) + + describe "when lines are removed", -> + beforeEach -> + editor.attachToDom() + setEditorHeightInLines(editor, 5) + spyOn(editor, "scrollTo") + + describe "when the change the precedes the first rendered row", -> + it "removes rendered lines to account for upstream change", -> + editor.scrollBottom(editor.scrollView.prop('scrollHeight')) + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12) + + buffer.change([[1,0], [2,0]], "") + expect(editor.visibleLines.find(".line").length).toBe 4 + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(11) + + describe "when the change straddles the first rendered row", -> + it "renders the correct rows", -> + editor.scrollBottom(editor.scrollView.prop('scrollHeight')) + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12) + + buffer.change([[7,0], [11,0]], "1\n2\n") + expect(editor.visibleLines.find(".line").length).toBe 3 + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(10) + + describe "when the change the straddles the last rendered row", -> + it "renders the correct rows", -> + buffer.change([[2,0], [7,0]], "") + expect(editor.visibleLines.find(".line").length).toBe 5 + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(0) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(4) + + describe "when the change the follows the last rendered row", -> + it "does not change the rendered lines", -> + buffer.change([[12,0], [12,0]], "") + expect(editor.visibleLines.find(".line").length).toBe 5 + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(0) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(4) + describe "gutter rendering", -> beforeEach -> editor.attachToDom(heightInLines: 5.5) @@ -2231,88 +2315,6 @@ describe "Editor", -> expect(selections[0].getBufferRange()).toEqual [[1, 12], [1, 12]] expect(selections[1].getBufferRange()).toEqual [[1, 30], [1, 30]] - describe "when lines are added", -> - beforeEach -> - setEditorHeightInLines(editor, 5) - spyOn(editor, "scrollTo") - - describe "when the change the precedes the first rendered row", -> - it "inserts and removes rendered lines to account for upstream change", -> - editor.scrollBottom(editor.scrollView.prop('scrollHeight')) - expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) - expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12) - - buffer.change([[1,0], [3,0]], "1\n2\n3\n") - expect(editor.visibleLines.find(".line").length).toBe 5 - expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) - expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12) - - describe "when the change straddles the first rendered row", -> - it "doesn't render rows that were not previously rendered", -> - editor.scrollBottom(editor.scrollView.prop('scrollHeight')) - expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) - expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12) - - buffer.change([[2,0], [7,0]], "2\n3\n4\n5\n6\n7\n8\n9\n") - expect(editor.visibleLines.find(".line").length).toBe 5 - expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) - expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12) - - describe "when the change the straddles the last rendered row", -> - it "doesn't render rows that were not previously rendered", -> - buffer.change([[2,0], [7,0]], "2\n3\n4\n5\n6\n7\n8\n") - expect(editor.visibleLines.find(".line").length).toBe 5 - expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(0) - expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(4) - - describe "when the change the follows the last rendered row", -> - it "does not change the rendered lines", -> - buffer.change([[12,0], [12,0]], "12\n13\n14\n") - expect(editor.visibleLines.find(".line").length).toBe 5 - expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(0) - expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(4) - - describe "when lines are removed", -> - beforeEach -> - setEditorHeightInLines(editor, 5) - spyOn(editor, "scrollTo") - - describe "when the change the precedes the first rendered row", -> - it "removes rendered lines to account for upstream change", -> - editor.scrollBottom(editor.scrollView.prop('scrollHeight')) - expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) - expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12) - - buffer.change([[1,0], [2,0]], "") - expect(editor.visibleLines.find(".line").length).toBe 4 - expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) - expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(11) - - describe "when the change straddles the first rendered row", -> - it "renders the correct rows", -> - editor.scrollBottom(editor.scrollView.prop('scrollHeight')) - expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) - expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12) - - buffer.change([[7,0], [11,0]], "1\n2\n") - expect(editor.visibleLines.find(".line").length).toBe 3 - expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) - expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(10) - - describe "when the change the straddles the last rendered row", -> - it "renders the correct rows", -> - buffer.change([[2,0], [7,0]], "") - expect(editor.visibleLines.find(".line").length).toBe 5 - expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(0) - expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(4) - - describe "when the change the follows the last rendered row", -> - it "does not change the rendered lines", -> - buffer.change([[12,0], [12,0]], "") - expect(editor.visibleLines.find(".line").length).toBe 5 - expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(0) - expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(4) - describe "when the editor is attached to the dom", -> it "calculates line height and char width and updates the pixel position of the cursor", -> expect(editor.lineHeight).toBeNull() From 74594ed2bec6b26030f19a0bf5e1194be53391fd Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Thu, 24 May 2012 13:53:48 -0700 Subject: [PATCH 54/61] Destroy folds before inserting text on a fold line. Spec for deleting a fold line. --- spec/app/renderer-spec.coffee | 10 ++++++++ spec/app/selection-spec.coffee | 22 ++++++++++++++++ src/app/editor.coffee | 6 +++++ src/app/renderer.coffee | 47 +++++++++++++++++----------------- src/app/selection.coffee | 1 + 5 files changed, 62 insertions(+), 24 deletions(-) diff --git a/spec/app/renderer-spec.coffee b/spec/app/renderer-spec.coffee index 5f292e75f..285c378e3 100644 --- a/spec/app/renderer-spec.coffee +++ b/spec/app/renderer-spec.coffee @@ -440,6 +440,16 @@ describe "Renderer", -> expect(renderer.bufferPositionForScreenPosition([5, 0])).toEqual [8, 0] expect(renderer.bufferPositionForScreenPosition([9, 2])).toEqual [12, 2] + describe ".destroyFoldsContainingBufferRow(row)", -> + describe "when two folds start on the given buffer row", -> + it "destroys both folds", -> + renderer.createFold(2, 4) + renderer.createFold(2, 6) + + expect(renderer.lineForRow(3).text).toBe '7' + renderer.destroyFoldsContainingBufferRow(2) + expect(renderer.lineForRow(3).text).toBe '3' + describe ".clipScreenPosition(screenPosition, wrapBeyondNewlines: false, wrapAtSoftNewlines: false, skipAtomicTokens: false)", -> beforeEach -> renderer.setMaxLineLength(50) diff --git a/spec/app/selection-spec.coffee b/spec/app/selection-spec.coffee index 2526e71c1..cf24a5392 100644 --- a/spec/app/selection-spec.coffee +++ b/spec/app/selection-spec.coffee @@ -273,3 +273,25 @@ describe "Selection", -> expect(editor.buffer.lineForRow(1)).toBe "var sort = function(items) {" expect(editor.buffer.lineForRow(2)).toBe " if (items.length <= 1) return items;" expect(selection.getBufferRange()).toEqual [[0, 1], [3, 15 - tabLength]] + + describe "when the selection ends on the begining of a fold line", -> + beforeEach -> + editor.createFold(2,4) + editor.createFold(2,6) + + describe "inserting text", -> + it "destroys the fold", -> + selection.setBufferRange([[1,0], [2,0]]) + selection.insertText('holy cow') + expect(editor.screenLineForRow(3).text).toBe buffer.lineForRow(3) + + describe "when the selection is empty", -> + describe "delete, when the selection is empty", -> + it "removes the lines contained by the fold", -> + oldLine7 = buffer.lineForRow(7) + oldLine8 = buffer.lineForRow(8) + + selection.setBufferRange([[2, 0], [2, 0]]) + selection.delete() + expect(editor.screenLineForRow(2).text).toBe oldLine7 + expect(editor.screenLineForRow(3).text).toBe oldLine8 diff --git a/src/app/editor.coffee b/src/app/editor.coffee index ca4f59db0..e544cbcca 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -338,6 +338,9 @@ class Editor extends View getScreenLines: -> @renderer.getLines() + screenLineForRow: (start) -> + @renderer.lineForRow(start) + linesForRows: (start, end) -> @renderer.linesForRows(start, end) @@ -347,6 +350,9 @@ class Editor extends View getLastScreenRow: -> @screenLineCount() - 1 + destroyFoldsContainingBufferRow: (bufferRow) -> + @renderer.destroyFoldsContainingBufferRow(bufferRow) + setBuffer: (buffer) -> if @buffer @saveCurrentEditSession() diff --git a/src/app/renderer.coffee b/src/app/renderer.coffee index 633d5c255..416f29652 100644 --- a/src/app/renderer.coffee +++ b/src/app/renderer.coffee @@ -79,6 +79,29 @@ class Renderer @trigger 'change', oldRange: oldScreenRange, newRange: newScreenRange, lineNumbersChanged: true @trigger 'unfold', bufferRange + destroyFoldsContainingBufferRow: (bufferRow) -> + folds = @activeFolds[bufferRow] ? [] + fold.destroy() for fold in new Array(folds...) + + registerFold: (bufferRow, fold) -> + @activeFolds[bufferRow] ?= [] + @activeFolds[bufferRow].push(fold) + @foldsById[fold.id] = fold + + unregisterFold: (bufferRow, fold) -> + folds = @activeFolds[bufferRow] + _.remove(folds, fold) + delete @foldsById[fold.id] + + largestFoldForBufferRow: (bufferRow) -> + return unless folds = @activeFolds[bufferRow] + (folds.sort (a, b) -> b.endRow - a.endRow)[0] + + screenLineRangeForBufferRange: (bufferRange) -> + @expandScreenRangeToLineEnds( + @lineMap.screenRangeForBufferRange( + @expandBufferRangeToLineEnds(bufferRange))) + screenRowForBufferRow: (bufferRow) -> @lineMap.screenPositionForBufferPosition([bufferRow, 0]).row @@ -176,30 +199,6 @@ class Renderer return column + 1 if /\s/.test(line[column]) return maxLineLength - registerFold: (bufferRow, fold) -> - @activeFolds[bufferRow] ?= [] - @activeFolds[bufferRow].push(fold) - @foldsById[fold.id] = fold - - unregisterFold: (bufferRow, fold) -> - folds = @activeFolds[bufferRow] - _.remove(folds, fold) - delete @foldsById[fold.id] - - largestFoldForBufferRow: (bufferRow) -> - return unless folds = @activeFolds[bufferRow] - (folds.sort (a, b) -> b.endRow - a.endRow)[0] - - buildFoldPlaceholder: (fold) -> - # token = new Token(value: '...', type: 'fold-placeholder', fold: fold, isAtomic: true) - # delta = new Point(fold.endRow - fold.startRow + 1, 0) - # new ScreenLineFragment([token], token.value, [0, token.value.length], delta) - - screenLineRangeForBufferRange: (bufferRange) -> - @expandScreenRangeToLineEnds( - @lineMap.screenRangeForBufferRange( - @expandBufferRangeToLineEnds(bufferRange))) - expandScreenRangeToLineEnds: (screenRange) -> screenRange = Range.fromObject(screenRange) { start, end } = screenRange diff --git a/src/app/selection.coffee b/src/app/selection.coffee index c24d23bae..882940f69 100644 --- a/src/app/selection.coffee +++ b/src/app/selection.coffee @@ -100,6 +100,7 @@ class Selection extends View insertText: (text) -> { text, shouldOutdent } = @autoIndentText(text) oldBufferRange = @getBufferRange() + @editor.destroyFoldsContainingBufferRow(oldBufferRange.end.row) isReversed = @isReversed() @clearSelection() newBufferRange = @editor.buffer.change(oldBufferRange, text) From c11ee74405c940de730ebbb9cffd22f74462554b Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Thu, 24 May 2012 14:08:32 -0700 Subject: [PATCH 55/61] Remove unused css --- static/editor.css | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/static/editor.css b/static/editor.css index 89096923b..0a154f554 100644 --- a/static/editor.css +++ b/static/editor.css @@ -109,27 +109,3 @@ background: white; opacity: .25; } - -.fold-placeholder { - -webkit-box-sizing: border-box; - position: relative; - display: inline-block; - vertical-align: text-bottom; - -webkit-border-radius: 3px; - background: rgba(255, 255, 255, .07); - border: 1px solid rgba(255, 255, 255, .25); - color: rgba(255, 255, 255, .95); -} - -.fold-placeholder:hover { - background: rgba(255, 255, 255, .2); - border: 1px solid rgba(255, 255, 255, .3); - color: white; -} - -.fold-placeholder .ellipsis { - position: relative; - width: 100%; - bottom: 40%; - text-align: center; -} From f8c3d89e60321877479fad8d4eea5d2bcffe4b71 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Thu, 24 May 2012 16:39:14 -0700 Subject: [PATCH 56/61] When folds are selected they are highlighted --- spec/app/editor-spec.coffee | 33 ++++++++++++++++++++++++++++++ src/app/composite-selection.coffee | 4 ++++ src/app/editor.coffee | 26 ++++++++++++++++++++--- src/app/fold.coffee | 3 +++ src/app/selection.coffee | 9 ++++++-- static/theme/twilight.css | 5 +++++ 6 files changed, 75 insertions(+), 5 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 645f4c54f..dcb5a17ca 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -2460,6 +2460,39 @@ describe "Editor", -> expect(editor.getCursorBufferPosition()).toEqual [3, 0] + describe "when a selection starts/stops intersecting a fold", -> + it "adds/removes the 'selected' class to the fold's line element", -> + editor.createFold(2, 4) + + editor.setSelectionBufferRange([[1, 0], [2, 0]], reverse: true) + expect(editor.lineElementForScreenRow(2)).toMatchSelector('.fold.selected') + + editor.setSelectionBufferRange([[1, 0], [1, 1]]) + expect(editor.lineElementForScreenRow(2)).not.toMatchSelector('.fold.selected') + + editor.setSelectionBufferRange([[1, 0], [5, 0]]) + expect(editor.lineElementForScreenRow(2)).toMatchSelector('.fold.selected') + + editor.setCursorScreenPosition([3,0]) + expect(editor.lineElementForScreenRow(2)).not.toMatchSelector('.fold.selected') + + editor.setCursorScreenPosition([2,0]) + expect(editor.lineElementForScreenRow(2)).toMatchSelector('.fold.selected') + + describe "when a selected fold is scrolled into view (and the fold line was not previously rendered)", -> + it "renders the fold's line element with the 'selected' class", -> + setEditorHeightInLines(editor, 5) + + editor.createFold(2, 4) + editor.setSelectionBufferRange([[1, 0], [5, 0]]) + expect(editor.visibleLines.find('.fold.selected')).toExist() + + editor.scrollToBottom() + expect(editor.visibleLines.find('.fold.selected')).not.toExist() + + editor.scrollTop(0) + expect(editor.lineElementForScreenRow(2)).toMatchSelector('.fold.selected') + describe "editor-path-change event", -> it "emits event when buffer's path is changed", -> editor = new Editor diff --git a/src/app/composite-selection.coffee b/src/app/composite-selection.coffee index 2d470b8f2..5a6887b45 100644 --- a/src/app/composite-selection.coffee +++ b/src/app/composite-selection.coffee @@ -74,6 +74,10 @@ class CompositeSeleciton getText: -> @getLastSelection().getText() + intersectsBufferRange: (bufferRange) -> + _.any @getSelections(), (selection) -> + selection.intersectsBufferRange(bufferRange) + expandSelectionsForward: (fn) -> fn(selection) for selection in @getSelections() @mergeIntersectingSelections() diff --git a/src/app/editor.coffee b/src/app/editor.coffee index e544cbcca..2cecb9487 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -62,6 +62,7 @@ class Editor extends View requireStylesheet 'theme/twilight.css' @id = Editor.idCounter++ + @lineCache = [] @bindKeys() @autoIndent = true @buildCursorAndSelection() @@ -335,13 +336,25 @@ class Editor extends View getLastVisibleScreenRow: -> Math.ceil((@scrollTop() + @scrollView.height()) / @lineHeight) - 1 + highlightSelectedFolds: -> + screenLines = @screenLinesForRows(@firstRenderedScreenRow, @lastRenderedScreenRow) + for screenLine, i in screenLines + if fold = screenLine.fold + screenRow = @firstRenderedScreenRow + i + element = @lineElementForScreenRow(screenRow) + if @compositeSelection.intersectsBufferRange(fold.getBufferRange()) + + element.addClass('selected') + else + element.removeClass('selected') + getScreenLines: -> @renderer.getLines() screenLineForRow: (start) -> @renderer.lineForRow(start) - linesForRows: (start, end) -> + screenLinesForRows: (start, end) -> @renderer.linesForRows(start, end) screenLineCount: -> @@ -468,11 +481,14 @@ class Editor extends View charWidth = @charWidth charHeight = @charHeight lines = @renderer.linesForRows(startRow, endRow) + compositeSelection = @compositeSelection $$ -> for line in lines if fold = line.fold lineAttributes = { class: 'fold line', 'fold-id': fold.id } + if compositeSelection.intersectsBufferRange(fold.getBufferRange()) + lineAttributes.class += ' selected' else lineAttributes = { class: 'line' } @div lineAttributes, => @@ -514,8 +530,9 @@ class Editor extends View elementsToReplace.forEach (element) => lines.removeChild(element) - getLineElement: (row) -> - @lineCache[row] + lineElementForScreenRow: (screenRow) -> + element = @lineCache[screenRow - @firstRenderedScreenRow] + $(element) toggleSoftWrap: -> @setSoftWrap(not @softWrap) @@ -747,6 +764,9 @@ class Editor extends View @scrollVertically(pixelPosition) @scrollHorizontally(pixelPosition) + scrollToBottom: -> + @scrollBottom(@scrollView.prop('scrollHeight')) + scrollVertically: (pixelPosition) -> linesInView = @scrollView.height() / @lineHeight maxScrollMargin = Math.floor((linesInView - 1) / 2) diff --git a/src/app/fold.coffee b/src/app/fold.coffee index 244a36bd0..280b94f78 100644 --- a/src/app/fold.coffee +++ b/src/app/fold.coffee @@ -18,6 +18,9 @@ class Fold inspect: -> "Fold(#{@startRow}, #{@endRow})" + getBufferRange: -> + new Range([@startRow, 0], [@endRow, Infinity]) + getBufferDelta: -> new Point(@endRow - @startRow + 1, 0) diff --git a/src/app/selection.coffee b/src/app/selection.coffee index 882940f69..4e83b23df 100644 --- a/src/app/selection.coffee +++ b/src/app/selection.coffee @@ -45,6 +45,8 @@ class Selection extends View @clearRegions() range = @getScreenRange() + + @editor.highlightSelectedFolds() return if range.isEmpty() rowSpan = range.end.row - range.start.row @@ -57,6 +59,7 @@ class Selection extends View @appendRegion(rowSpan - 1, { row: range.start.row + 1, column: 0}, null) @appendRegion(1, { row: range.end.row, column: 0 }, range.end) + appendRegion: (rows, start, end) -> { lineHeight, charWidth } = @editor css = @editor.pixelPositionForScreenPosition(start) @@ -80,8 +83,7 @@ class Selection extends View else new Range(@cursor.getScreenPosition(), @cursor.getScreenPosition()) - setScreenRange: (range, options={}) -> - { reverse } = options + setScreenRange: (range, {reverse}={}) -> { start, end } = range [start, end] = [end, start] if reverse @@ -97,6 +99,9 @@ class Selection extends View getText: -> @editor.buffer.getTextInRange @getBufferRange() + intersectsBufferRange: (bufferRange) -> + @getBufferRange().intersectsWith(bufferRange) + insertText: (text) -> { text, shouldOutdent } = @autoIndentText(text) oldBufferRange = @getBufferRange() diff --git a/static/theme/twilight.css b/static/theme/twilight.css index f83b9e705..97ceea195 100644 --- a/static/theme/twilight.css +++ b/static/theme/twilight.css @@ -74,6 +74,11 @@ color:#D2A8A1; color: #969696; } +.fold.selected { + background-color: #82715C; + color: #969696; +} + .support.function { color:#DAD085; } From bbcb59fe93b08d3f2fb04038614988e890ac2fbe Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Fri, 25 May 2012 10:58:30 -0700 Subject: [PATCH 57/61] Buffer changes updates a stable list of folds --- spec/app/renderer-spec.coffee | 59 ++++++++++++++++++++--------------- src/app/renderer.coffee | 6 ++-- 2 files changed, 37 insertions(+), 28 deletions(-) diff --git a/spec/app/renderer-spec.coffee b/spec/app/renderer-spec.coffee index 285c378e3..ef887fb6d 100644 --- a/spec/app/renderer-spec.coffee +++ b/spec/app/renderer-spec.coffee @@ -293,36 +293,45 @@ describe "Renderer", -> expect(event.newRange).toEqual [[1, 0], [1, 9]] expect(event.lineNumbersChanged).toBeTruthy() + describe "when multiple changes happen above the fold", -> + it "repositions folds correctly", -> + buffer.delete([[1, 1], [2, 0]]) + buffer.insert([0, 1], "\nnew") + + expect(fold1.startRow).toBe 2 + expect(fold1.endRow).toBe 4 + describe "when the old range precedes lines with a fold", -> - it "updates the buffer and re-positions subsequent folds", -> - buffer.change([[0, 0], [1, 1]], 'abc') + describe "when the new range precedes lines with a fold", -> + it "updates the buffer and re-positions subsequent folds", -> + buffer.change([[0, 0], [1, 1]], 'abc') - expect(renderer.lineForRow(0).text).toBe "abc" - expect(renderer.lineForRow(1).fold).toBe fold1 - expect(renderer.lineForRow(2).text).toBe "5" - expect(renderer.lineForRow(3).fold).toBe fold2 - expect(renderer.lineForRow(4).text).toMatch /^9-+/ + expect(renderer.lineForRow(0).text).toBe "abc" + expect(renderer.lineForRow(1).fold).toBe fold1 + expect(renderer.lineForRow(2).text).toBe "5" + expect(renderer.lineForRow(3).fold).toBe fold2 + expect(renderer.lineForRow(4).text).toMatch /^9-+/ - expect(changeHandler).toHaveBeenCalled() - [[event]] = changeHandler.argsForCall - expect(event.oldRange).toEqual [[0, 0], [1, 1]] - expect(event.newRange).toEqual [[0, 0], [0, 3]] - expect(event.lineNumbersChanged).toBeTruthy() - changeHandler.reset() + expect(changeHandler).toHaveBeenCalled() + [[event]] = changeHandler.argsForCall + expect(event.oldRange).toEqual [[0, 0], [1, 1]] + expect(event.newRange).toEqual [[0, 0], [0, 3]] + expect(event.lineNumbersChanged).toBeTruthy() + changeHandler.reset() - fold1.destroy() - expect(renderer.lineForRow(0).text).toBe "abc" - expect(renderer.lineForRow(1).text).toBe "2" - expect(renderer.lineForRow(3).text).toMatch /^4-+/ - expect(renderer.lineForRow(4).text).toBe "5" - expect(renderer.lineForRow(5).fold).toBe fold2 - expect(renderer.lineForRow(6).text).toMatch /^9-+/ + fold1.destroy() + expect(renderer.lineForRow(0).text).toBe "abc" + expect(renderer.lineForRow(1).text).toBe "2" + expect(renderer.lineForRow(3).text).toMatch /^4-+/ + expect(renderer.lineForRow(4).text).toBe "5" + expect(renderer.lineForRow(5).fold).toBe fold2 + expect(renderer.lineForRow(6).text).toMatch /^9-+/ - expect(changeHandler).toHaveBeenCalled() - [[event]] = changeHandler.argsForCall - expect(event.oldRange).toEqual [[1, 0], [1, 1]] - expect(event.newRange).toEqual [[1, 0], [3, 101]] - expect(event.lineNumbersChanged).toBeTruthy() + expect(changeHandler).toHaveBeenCalled() + [[event]] = changeHandler.argsForCall + expect(event.oldRange).toEqual [[1, 0], [1, 1]] + expect(event.newRange).toEqual [[1, 0], [3, 101]] + expect(event.lineNumbersChanged).toBeTruthy() describe "when the old range straddles the beginning of a fold", -> it "replaces lines in the portion of the range that precedes the fold and adjusts the end of the fold to encompass additional lines", -> diff --git a/src/app/renderer.coffee b/src/app/renderer.coffee index 416f29652..29e698405 100644 --- a/src/app/renderer.coffee +++ b/src/app/renderer.coffee @@ -127,9 +127,9 @@ class Renderer @lineMap.clipScreenPosition(position, options) handleBufferChange: (e) -> - for row, folds of @activeFolds - for fold in new Array(folds...) - fold.handleBufferChange(e) + allFolds = [] # Folds can modify @activeFolds, so first make sure we have a stable array of folds + allFolds.push(folds...) for row, folds of @activeFolds + fold.handleBufferChange(e) for fold in allFolds @handleHighlighterChange(@lastHighlighterChangeEvent) From 35b2f0e8c7118829a11b61002b0f47368e775224 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Fri, 25 May 2012 10:58:30 -0700 Subject: [PATCH 58/61] Buffer changes updates a stable list of folds --- spec/app/renderer-spec.coffee | 59 ++++++++++++++++++++-------------- spec/app/selection-spec.coffee | 6 ++++ src/app/renderer.coffee | 6 ++-- src/app/selection.coffee | 1 + 4 files changed, 44 insertions(+), 28 deletions(-) diff --git a/spec/app/renderer-spec.coffee b/spec/app/renderer-spec.coffee index 285c378e3..ef887fb6d 100644 --- a/spec/app/renderer-spec.coffee +++ b/spec/app/renderer-spec.coffee @@ -293,36 +293,45 @@ describe "Renderer", -> expect(event.newRange).toEqual [[1, 0], [1, 9]] expect(event.lineNumbersChanged).toBeTruthy() + describe "when multiple changes happen above the fold", -> + it "repositions folds correctly", -> + buffer.delete([[1, 1], [2, 0]]) + buffer.insert([0, 1], "\nnew") + + expect(fold1.startRow).toBe 2 + expect(fold1.endRow).toBe 4 + describe "when the old range precedes lines with a fold", -> - it "updates the buffer and re-positions subsequent folds", -> - buffer.change([[0, 0], [1, 1]], 'abc') + describe "when the new range precedes lines with a fold", -> + it "updates the buffer and re-positions subsequent folds", -> + buffer.change([[0, 0], [1, 1]], 'abc') - expect(renderer.lineForRow(0).text).toBe "abc" - expect(renderer.lineForRow(1).fold).toBe fold1 - expect(renderer.lineForRow(2).text).toBe "5" - expect(renderer.lineForRow(3).fold).toBe fold2 - expect(renderer.lineForRow(4).text).toMatch /^9-+/ + expect(renderer.lineForRow(0).text).toBe "abc" + expect(renderer.lineForRow(1).fold).toBe fold1 + expect(renderer.lineForRow(2).text).toBe "5" + expect(renderer.lineForRow(3).fold).toBe fold2 + expect(renderer.lineForRow(4).text).toMatch /^9-+/ - expect(changeHandler).toHaveBeenCalled() - [[event]] = changeHandler.argsForCall - expect(event.oldRange).toEqual [[0, 0], [1, 1]] - expect(event.newRange).toEqual [[0, 0], [0, 3]] - expect(event.lineNumbersChanged).toBeTruthy() - changeHandler.reset() + expect(changeHandler).toHaveBeenCalled() + [[event]] = changeHandler.argsForCall + expect(event.oldRange).toEqual [[0, 0], [1, 1]] + expect(event.newRange).toEqual [[0, 0], [0, 3]] + expect(event.lineNumbersChanged).toBeTruthy() + changeHandler.reset() - fold1.destroy() - expect(renderer.lineForRow(0).text).toBe "abc" - expect(renderer.lineForRow(1).text).toBe "2" - expect(renderer.lineForRow(3).text).toMatch /^4-+/ - expect(renderer.lineForRow(4).text).toBe "5" - expect(renderer.lineForRow(5).fold).toBe fold2 - expect(renderer.lineForRow(6).text).toMatch /^9-+/ + fold1.destroy() + expect(renderer.lineForRow(0).text).toBe "abc" + expect(renderer.lineForRow(1).text).toBe "2" + expect(renderer.lineForRow(3).text).toMatch /^4-+/ + expect(renderer.lineForRow(4).text).toBe "5" + expect(renderer.lineForRow(5).fold).toBe fold2 + expect(renderer.lineForRow(6).text).toMatch /^9-+/ - expect(changeHandler).toHaveBeenCalled() - [[event]] = changeHandler.argsForCall - expect(event.oldRange).toEqual [[1, 0], [1, 1]] - expect(event.newRange).toEqual [[1, 0], [3, 101]] - expect(event.lineNumbersChanged).toBeTruthy() + expect(changeHandler).toHaveBeenCalled() + [[event]] = changeHandler.argsForCall + expect(event.oldRange).toEqual [[1, 0], [1, 1]] + expect(event.newRange).toEqual [[1, 0], [3, 101]] + expect(event.lineNumbersChanged).toBeTruthy() describe "when the old range straddles the beginning of a fold", -> it "replaces lines in the portion of the range that precedes the fold and adjusts the end of the fold to encompass additional lines", -> diff --git a/spec/app/selection-spec.coffee b/spec/app/selection-spec.coffee index cf24a5392..e3d699f8d 100644 --- a/spec/app/selection-spec.coffee +++ b/spec/app/selection-spec.coffee @@ -285,6 +285,12 @@ describe "Selection", -> selection.insertText('holy cow') expect(editor.screenLineForRow(3).text).toBe buffer.lineForRow(3) + describe "backspace", -> + it "destroys the fold", -> + selection.setBufferRange([[1,0], [2,0]]) + selection.backspace() + expect(editor.screenLineForRow(3).text).toBe buffer.lineForRow(3) + describe "when the selection is empty", -> describe "delete, when the selection is empty", -> it "removes the lines contained by the fold", -> diff --git a/src/app/renderer.coffee b/src/app/renderer.coffee index 416f29652..29e698405 100644 --- a/src/app/renderer.coffee +++ b/src/app/renderer.coffee @@ -127,9 +127,9 @@ class Renderer @lineMap.clipScreenPosition(position, options) handleBufferChange: (e) -> - for row, folds of @activeFolds - for fold in new Array(folds...) - fold.handleBufferChange(e) + allFolds = [] # Folds can modify @activeFolds, so first make sure we have a stable array of folds + allFolds.push(folds...) for row, folds of @activeFolds + fold.handleBufferChange(e) for fold in allFolds @handleHighlighterChange(@lastHighlighterChangeEvent) diff --git a/src/app/selection.coffee b/src/app/selection.coffee index 4e83b23df..4ca43e49b 100644 --- a/src/app/selection.coffee +++ b/src/app/selection.coffee @@ -145,6 +145,7 @@ class Selection extends View @editor.getCurrentMode().autoOutdent(state, new AceOutdentAdaptor(@editor.buffer, @editor), bufferRow) backspace: -> + @editor.destroyFoldsContainingBufferRow(@getBufferRange().end.row) @selectLeft() if @isEmpty() @deleteSelectedText() From f6db1a2051ac3ce8b08d8e6b00812973adc2d7d3 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Fri, 25 May 2012 11:17:50 -0700 Subject: [PATCH 59/61] remove bufferRow arg from Renderer.registerFold() --- src/app/fold.coffee | 2 +- src/app/renderer.coffee | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/fold.coffee b/src/app/fold.coffee index 280b94f78..74838ba7a 100644 --- a/src/app/fold.coffee +++ b/src/app/fold.coffee @@ -36,7 +36,7 @@ class Fold if @startRow != oldStartRow @renderer.unregisterFold(oldStartRow, this) - @renderer.registerFold(@startRow, this) + @renderer.registerFold(this) isContainedByRange: (range) -> range.start.row <= @startRow and @endRow <= range.end.row diff --git a/src/app/renderer.coffee b/src/app/renderer.coffee index 29e698405..a34dc0dd2 100644 --- a/src/app/renderer.coffee +++ b/src/app/renderer.coffee @@ -53,7 +53,7 @@ class Renderer createFold: (startRow, endRow) -> fold = new Fold(this, startRow, endRow) - @registerFold(startRow, fold) + @registerFold(fold) bufferRange = new Range([startRow, 0], [endRow, @buffer.lineLengthForRow(endRow)]) oldScreenRange = @screenLineRangeForBufferRange(bufferRange) @@ -83,9 +83,9 @@ class Renderer folds = @activeFolds[bufferRow] ? [] fold.destroy() for fold in new Array(folds...) - registerFold: (bufferRow, fold) -> - @activeFolds[bufferRow] ?= [] - @activeFolds[bufferRow].push(fold) + registerFold: (fold) -> + @activeFolds[fold.startRow] ?= [] + @activeFolds[fold.startRow].push(fold) @foldsById[fold.id] = fold unregisterFold: (bufferRow, fold) -> From e2ca3814a96be4bbd3e794d563719ffd2933ff3e Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 25 May 2012 12:35:32 -0700 Subject: [PATCH 60/61] Hide the cursor when it is on a fold line --- spec/app/editor-spec.coffee | 8 ++++++-- src/app/cursor.coffee | 5 +++++ src/app/editor.coffee | 4 +++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index dcb5a17ca..9c512ae52 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -2446,7 +2446,7 @@ describe "Editor", -> expect(editor.getCursorBufferPosition()).toEqual [3, 0] - describe "when a cursor is on a fold placeholder line", -> + describe "when the unfold event is triggered when the cursor is on a fold placeholder line", -> it "removes the associated fold and places the cursor at its beginning", -> editor.getSelection().setBufferRange(new Range([3, 0], [9, 0])) editor.trigger 'fold-selection' @@ -2461,7 +2461,7 @@ describe "Editor", -> expect(editor.getCursorBufferPosition()).toEqual [3, 0] describe "when a selection starts/stops intersecting a fold", -> - it "adds/removes the 'selected' class to the fold's line element", -> + it "adds/removes the 'selected' class to the fold's line element and hides the cursor if it is on the fold line", -> editor.createFold(2, 4) editor.setSelectionBufferRange([[1, 0], [2, 0]], reverse: true) @@ -2478,6 +2478,10 @@ describe "Editor", -> editor.setCursorScreenPosition([2,0]) expect(editor.lineElementForScreenRow(2)).toMatchSelector('.fold.selected') + expect(editor.find('.cursor').css('display')).toBe 'none' + + editor.setCursorScreenPosition([3,0]) + expect(editor.find('.cursor').css('display')).toBe 'block' describe "when a selected fold is scrolled into view (and the fold line was not previously rendered)", -> it "renders the fold's line element with the 'selected' class", -> diff --git a/src/app/cursor.coffee b/src/app/cursor.coffee index 6bfcef620..a3fc37abd 100644 --- a/src/app/cursor.coffee +++ b/src/app/cursor.coffee @@ -177,4 +177,9 @@ class Cursor extends View if this == _.last(@editor.getCursors()) @editor.scrollTo(pixelPosition) + if @editor.isFoldedAtScreenRow(screenPosition.row) + @hide() + else + @show() + @selection.updateAppearance() diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 2cecb9487..044478a2b 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -343,7 +343,6 @@ class Editor extends View screenRow = @firstRenderedScreenRow + i element = @lineElementForScreenRow(screenRow) if @compositeSelection.intersectsBufferRange(fold.getBufferRange()) - element.addClass('selected') else element.removeClass('selected') @@ -363,6 +362,9 @@ class Editor extends View getLastScreenRow: -> @screenLineCount() - 1 + isFoldedAtScreenRow: (screenRow) -> + @screenLineForRow(screenRow).fold? + destroyFoldsContainingBufferRow: (bufferRow) -> @renderer.destroyFoldsContainingBufferRow(bufferRow) From 7c105a0446eb924d0f591bca2f52f5fb06ecddce Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 25 May 2012 12:46:27 -0700 Subject: [PATCH 61/61] Tweak fold line colors The deselected orange is now a darkened version of the orange used for coffeescript arrows in the twilight theme. The selected color is now a green based on the color of the cursor, to lend a feeling of similarity between the fold line and the cursor. --- static/theme/twilight.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/theme/twilight.css b/static/theme/twilight.css index 97ceea195..ddd2d0e5e 100644 --- a/static/theme/twilight.css +++ b/static/theme/twilight.css @@ -70,12 +70,12 @@ color:#D2A8A1; } .fold { - background-color: #52412C; + background-color: #524228; color: #969696; } .fold.selected { - background-color: #82715C; + background-color: #2A3B2A; color: #969696; }