From 32329070c24f09183f325562caf2696ef1e60bbc Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 3 Oct 2013 17:40:38 -0700 Subject: [PATCH 1/6] Add a simple gutter api for adding/removing classes --- spec/editor-spec.coffee | 30 +++++++++++++++++++++++ src/gutter.coffee | 54 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/spec/editor-spec.coffee b/spec/editor-spec.coffee index e407048cb..d063926d8 100644 --- a/spec/editor-spec.coffee +++ b/spec/editor-spec.coffee @@ -1886,6 +1886,36 @@ describe "Editor", -> config.set("editor.showLineNumbers", false) expect(editor.gutter.lineNumbers).not.toBeVisible() + describe "using gutter's api", -> + it "can get all the line number elements", -> + elements = editor.gutter.getLineNumberElements() + len = editor.gutter.lastScreenRow - editor.gutter.firstScreenRow + 1 + expect(elements).toHaveLength(len) + + it "can get a single line number element", -> + element = editor.gutter.getLineNumberElement(3) + + expect(element).toBeTruthy() + expect($(element)).toHaveClass('line-number') + expect($(element)).toHaveClass('line-number-3') + + it "returns falsy when there is no line element", -> + expect(editor.gutter.getLineNumberElement(42)).toBeFalsy() + + it "can add and remove classes to all the line numbers", -> + elements = editor.gutter.addClassToAllLines('heyok') + expect($(elements)).toHaveClass('heyok') + + elements = editor.gutter.removeClassFromAllLines('heyok') + expect($(elements)).not.toHaveClass('heyok') + + it "can add and remove classes from a single line number", -> + element = editor.gutter.addClassToLine(3, 'heyok') + expect($(element)).toHaveClass('heyok') + + element = editor.gutter.getLineNumberElement(2) + expect($(element)).not.toHaveClass('heyok') + describe "gutter line highlighting", -> beforeEach -> editor.attachToDom(heightInLines: 5.5) diff --git a/src/gutter.coffee b/src/gutter.coffee index 0fb2ae9a3..b8526548d 100644 --- a/src/gutter.coffee +++ b/src/gutter.coffee @@ -62,6 +62,56 @@ class Gutter extends View setShowLineNumbers: (showLineNumbers) -> if showLineNumbers then @lineNumbers.show() else @lineNumbers.hide() + # Get all the line-number divs. + # + # Returns a list of {HTMLElement}s. + getLineNumberElements: -> + @lineNumbers[0].getElementsByClassName('line-number') + + # Get a single line-number div. + # + # * lineNumber: 0 based line number + # + # Returns a {HTMLElement} + getLineNumberElement: (lineNumber) -> + @lineNumbers[0].getElementsByClassName("line-number-#{lineNumber}")[0] + + # Add a class to all line-number divs. + # + # * clas: string class name + # + # Returns a list of {HTMLElement}s. + addClassToAllLines: (clas)-> + $.fn.addClass.call(@getLineNumberElements(), clas) + + # Remove a class from all line-number divs. + # + # * clas: string class name + # + # Returns a list of {HTMLElement}s. + removeClassFromAllLines: (clas)-> + $.fn.removeClass.call(@getLineNumberElements(), clas) + + # Add a class to a single line-number div + # + # * lineNumber: 0 based line number + # * clas: string class name + # + # Returns the {HTMLElement} on which the class was set. undefined if the line was not found + addClassToLine: (lineNumber, clas)-> + line = @lineNumbers[0].getElementsByClassName("line-number-#{lineNumber}") + $.fn.addClass.call(line, clas) if line + + # Remove a class from a single line-number div + # + # * lineNumber: 0 based line number + # * clas: string class name + # + # Returns the {HTMLElement} on which the class was set. undefined if the line was not found + removeClassFromLine: (lineNumber, clas)-> + line = @lineNumbers[0].getElementsByClassName("line-number-#{lineNumber}") + $.fn.removeClass.call(line, clas) if line + ### Internal ### updateLineNumbers: (changes, renderFrom, renderTo) -> @@ -96,12 +146,12 @@ class Gutter extends View else rowValue = (row + 1).toString() - classes = 'line-number' + classes = "line-number line-number-#{row}" classes += ' fold' if editor.isFoldedAtBufferRow(row) rowValuePadding = _.multiplyString(' ', maxDigits - rowValue.length) - html += """
#{rowValuePadding}#{rowValue}
""" + html += """
#{rowValuePadding}#{rowValue}
""" lastScreenRow = row From 964e88f131f879120e2286e7cea7f44bb220ec3f Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Mon, 7 Oct 2013 13:55:04 -0700 Subject: [PATCH 2/6] Add benchmark for the gutter api fns --- benchmark/benchmark-suite.coffee | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/benchmark/benchmark-suite.coffee b/benchmark/benchmark-suite.coffee index 2b8bec8ba..3653e3e89 100644 --- a/benchmark/benchmark-suite.coffee +++ b/benchmark/benchmark-suite.coffee @@ -123,6 +123,19 @@ describe "editor.", -> div = document.createElement('div') div.innerHTML = html + describe "gutter-api.", -> + describe "getLineNumberElementsForClass.", -> + beforeEach -> + editor.gutter.addClassToLine(20, 'omgwow') + editor.gutter.addClassToLine(40, 'omgwow') + + benchmark "DOM", 20000, -> + editor.gutter.getLineNumberElementsForClass('omgwow') + + describe "getLineNumberElement.", -> + benchmark "DOM", 20000, -> + editor.gutter.getLineNumberElementDOM(12) + describe "line-htmlification.", -> div = null html = null From 1ff97fc21a8770e37e35a3c8c0d075a72bb9f4d0 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Mon, 7 Oct 2013 13:57:00 -0700 Subject: [PATCH 3/6] Add getLineNumbersForClass() Also clean up and use klass --- spec/editor-spec.coffee | 15 ++++++++++++- src/gutter.coffee | 48 +++++++++++++++++++++++------------------ 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/spec/editor-spec.coffee b/spec/editor-spec.coffee index d063926d8..700a88e16 100644 --- a/spec/editor-spec.coffee +++ b/spec/editor-spec.coffee @@ -1900,7 +1900,7 @@ describe "Editor", -> expect($(element)).toHaveClass('line-number-3') it "returns falsy when there is no line element", -> - expect(editor.gutter.getLineNumberElement(42)).toBeFalsy() + expect(editor.gutter.getLineNumberElement(42).length).toBeFalsy() it "can add and remove classes to all the line numbers", -> elements = editor.gutter.addClassToAllLines('heyok') @@ -1916,6 +1916,19 @@ describe "Editor", -> element = editor.gutter.getLineNumberElement(2) expect($(element)).not.toHaveClass('heyok') + it "can fetch line numbers by their class", -> + editor.gutter.addClassToLine(1, 'heyok') + editor.gutter.addClassToLine(3, 'heyok') + + elements = editor.gutter.getLineNumberElementsForClass('heyok') + expect(elements.length).toBe 2 + + expect($(elements[0])).toHaveClass 'line-number-1' + expect($(elements[0])).toHaveClass 'heyok' + + expect($(elements[1])).toHaveClass 'line-number-3' + expect($(elements[1])).toHaveClass 'heyok' + describe "gutter line highlighting", -> beforeEach -> editor.attachToDom(heightInLines: 5.5) diff --git a/src/gutter.coffee b/src/gutter.coffee index b8526548d..bca1db848 100644 --- a/src/gutter.coffee +++ b/src/gutter.coffee @@ -66,51 +66,57 @@ class Gutter extends View # # Returns a list of {HTMLElement}s. getLineNumberElements: -> - @lineNumbers[0].getElementsByClassName('line-number') + @lineNumbers[0].childNodes + + # Get all the line-number divs. + # + # Returns a list of {HTMLElement}s. + getLineNumberElementsForClass: (klass) -> + @lineNumbers[0].getElementsByClassName(klass) # Get a single line-number div. # - # * lineNumber: 0 based line number + # * bufferRow: 0 based line number # - # Returns a {HTMLElement} - getLineNumberElement: (lineNumber) -> - @lineNumbers[0].getElementsByClassName("line-number-#{lineNumber}")[0] + # Returns a list of {HTMLElement}s that correspond to the bufferRow + getLineNumberElement: (bufferRow) -> + @getLineNumberElementsForClass("line-number-#{bufferRow}") # Add a class to all line-number divs. # - # * clas: string class name + # * klass: string class name # # Returns a list of {HTMLElement}s. - addClassToAllLines: (clas)-> - $.fn.addClass.call(@getLineNumberElements(), clas) + addClassToAllLines: (klass)-> + $.fn.addClass.call(@getLineNumberElements(), klass) # Remove a class from all line-number divs. # - # * clas: string class name + # * klass: string class name # # Returns a list of {HTMLElement}s. - removeClassFromAllLines: (clas)-> - $.fn.removeClass.call(@getLineNumberElements(), clas) + removeClassFromAllLines: (klass)-> + $.fn.removeClass.call(@getLineNumberElements(), klass) # Add a class to a single line-number div # - # * lineNumber: 0 based line number - # * clas: string class name + # * bufferRow: 0 based line number + # * klass: string class name # # Returns the {HTMLElement} on which the class was set. undefined if the line was not found - addClassToLine: (lineNumber, clas)-> - line = @lineNumbers[0].getElementsByClassName("line-number-#{lineNumber}") - $.fn.addClass.call(line, clas) if line + addClassToLine: (bufferRow, klass)-> + line = @getLineNumberElement(bufferRow) + $.fn.addClass.call(line, klass) if line and line.length # Remove a class from a single line-number div # - # * lineNumber: 0 based line number - # * clas: string class name + # * bufferRow: 0 based line number + # * klass: string class name # # Returns the {HTMLElement} on which the class was set. undefined if the line was not found - removeClassFromLine: (lineNumber, clas)-> - line = @lineNumbers[0].getElementsByClassName("line-number-#{lineNumber}") - $.fn.removeClass.call(line, clas) if line + removeClassFromLine: (bufferRow, klass)-> + line = @getLineNumberElement(bufferRow) + $.fn.removeClass.call(line, klass) if line and line.length ### Internal ### From 2cee400547b1588fc5035857eba559e4d156019e Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Mon, 7 Oct 2013 14:56:29 -0700 Subject: [PATCH 4/6] More benchmarks --- benchmark/benchmark-suite.coffee | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/benchmark/benchmark-suite.coffee b/benchmark/benchmark-suite.coffee index 3653e3e89..b5401d385 100644 --- a/benchmark/benchmark-suite.coffee +++ b/benchmark/benchmark-suite.coffee @@ -132,9 +132,31 @@ describe "editor.", -> benchmark "DOM", 20000, -> editor.gutter.getLineNumberElementsForClass('omgwow') - describe "getLineNumberElement.", -> - benchmark "DOM", 20000, -> - editor.gutter.getLineNumberElementDOM(12) + benchmark "getLineNumberElement.DOM", 20000, -> + editor.gutter.getLineNumberElement(12) + + benchmark "toggle-class", 2000, -> + editor.gutter.addClassToLine(40, 'omgwow') + editor.gutter.removeClassFromLine(40, 'omgwow') + + describe "find-then-unset.", -> + classes = ['one', 'two', 'three', 'four'] + + benchmark "single-class", 200, -> + editor.gutter.addClassToLine(30, 'omgwow') + editor.gutter.addClassToLine(40, 'omgwow') + editor.gutter.removeClassFromAllLines('omgwow') + + benchmark "multiple-class", 200, -> + editor.gutter.addClassToLine(30, 'one') + editor.gutter.addClassToLine(30, 'two') + + editor.gutter.addClassToLine(40, 'two') + editor.gutter.addClassToLine(40, 'three') + editor.gutter.addClassToLine(40, 'four') + + for klass in classes + editor.gutter.removeClassFromAllLines(klass) describe "line-htmlification.", -> div = null From 9c6353977fa64dcd972dbd4bb080fbbd7ec9ea46 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Mon, 7 Oct 2013 14:57:45 -0700 Subject: [PATCH 5/6] Rework the api using native methods. This is :racehorse: editor.300-line-file.gutter-api.getLineNumberElementsForClass.DOM: 3 / 20000 = 0.00015ms editor.300-line-file.gutter-api.getLineNumberElement.DOM: 8 / 20000 = 0.0004ms editor.300-line-file.gutter-api.toggle-class: 17 / 2000 = 0.0085ms editor.300-line-file.gutter-api.find-then-unset.single-class: 3 / 200 = 0.015ms editor.300-line-file.gutter-api.find-then-unset.multiple-class: 9 / 200 = 0.045ms --- spec/editor-spec.coffee | 18 +++++++++--------- src/gutter.coffee | 38 ++++++++++++++++++++++++++------------ 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/spec/editor-spec.coffee b/spec/editor-spec.coffee index 700a88e16..48de79f12 100644 --- a/spec/editor-spec.coffee +++ b/spec/editor-spec.coffee @@ -1894,24 +1894,24 @@ describe "Editor", -> it "can get a single line number element", -> element = editor.gutter.getLineNumberElement(3) - expect(element).toBeTruthy() - expect($(element)).toHaveClass('line-number') - expect($(element)).toHaveClass('line-number-3') it "returns falsy when there is no line element", -> - expect(editor.gutter.getLineNumberElement(42).length).toBeFalsy() + expect(editor.gutter.getLineNumberElement(42)).toHaveLength 0 it "can add and remove classes to all the line numbers", -> - elements = editor.gutter.addClassToAllLines('heyok') + wasAdded = editor.gutter.addClassToAllLines('heyok') + expect(wasAdded).toBe true + + elements = editor.gutter.getLineNumberElementsForClass('heyok') expect($(elements)).toHaveClass('heyok') - elements = editor.gutter.removeClassFromAllLines('heyok') - expect($(elements)).not.toHaveClass('heyok') + editor.gutter.removeClassFromAllLines('heyok') + expect($(editor.gutter.getLineNumberElements())).not.toHaveClass('heyok') it "can add and remove classes from a single line number", -> - element = editor.gutter.addClassToLine(3, 'heyok') - expect($(element)).toHaveClass('heyok') + wasAdded = editor.gutter.addClassToLine(3, 'heyok') + expect(wasAdded).toBe true element = editor.gutter.getLineNumberElement(2) expect($(element)).not.toHaveClass('heyok') diff --git a/src/gutter.coffee b/src/gutter.coffee index bca1db848..1e38daa7a 100644 --- a/src/gutter.coffee +++ b/src/gutter.coffee @@ -78,7 +78,8 @@ class Gutter extends View # # * bufferRow: 0 based line number # - # Returns a list of {HTMLElement}s that correspond to the bufferRow + # Returns a list of {HTMLElement}s that correspond to the bufferRow. More than + # one in the list indicates a wrapped line. getLineNumberElement: (bufferRow) -> @getLineNumberElementsForClass("line-number-#{bufferRow}") @@ -86,37 +87,50 @@ class Gutter extends View # # * klass: string class name # - # Returns a list of {HTMLElement}s. + # Returns true if the class was added to any lines addClassToAllLines: (klass)-> - $.fn.addClass.call(@getLineNumberElements(), klass) + elements = @getLineNumberElements() + el.classList.add(klass) for el in elements + !!elements.length # Remove a class from all line-number divs. # - # * klass: string class name + # * klass: string class name. Can only be one class name. i.e. 'my-class' # - # Returns a list of {HTMLElement}s. + # Returns true if the class was removed from any lines removeClassFromAllLines: (klass)-> - $.fn.removeClass.call(@getLineNumberElements(), klass) + # This is faster than calling $.removeClass on all lines, and faster than + # making a new array and iterating through it. + elements = @getLineNumberElementsForClass(klass) + willRemoveClasses = !!elements.length + elements[0].classList.remove(klass) while elements.length > 0 + willRemoveClasses # Add a class to a single line-number div # # * bufferRow: 0 based line number # * klass: string class name # - # Returns the {HTMLElement} on which the class was set. undefined if the line was not found + # Returns true if there were lines the class was added to addClassToLine: (bufferRow, klass)-> - line = @getLineNumberElement(bufferRow) - $.fn.addClass.call(line, klass) if line and line.length + elements = @getLineNumberElement(bufferRow) + el.classList.add(klass) for el in elements + !!elements.length # Remove a class from a single line-number div # # * bufferRow: 0 based line number # * klass: string class name # - # Returns the {HTMLElement} on which the class was set. undefined if the line was not found + # Returns true if there were lines the class was removed from removeClassFromLine: (bufferRow, klass)-> - line = @getLineNumberElement(bufferRow) - $.fn.removeClass.call(line, klass) if line and line.length + classesRemoved = false + elements = @getLineNumberElement(bufferRow) + for el in elements + hasClass = el.classList.contains(klass) + classesRemoved |= hasClass + el.classList.remove(klass) if hasClass + classesRemoved ### Internal ### From 0c54f6254d0fac574267057971eef788ccbe9e9a Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Mon, 7 Oct 2013 15:15:30 -0700 Subject: [PATCH 6/6] Upgrade to new versions of git-diff and bookmarks --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 3254a0d4a..c6a656627 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "archive-view": "0.8.0", "autocomplete": "0.6.0", "autoflow": "0.3.0", - "bookmarks": "0.4.0", + "bookmarks": "0.5.0", "bracket-matcher": "0.6.0", "collaboration": "0.20.0", "command-logger": "0.4.0", @@ -56,7 +56,7 @@ "find-and-replace": "0.24.0", "fuzzy-finder": "0.7.0", "gfm": "0.5.0", - "git-diff": "0.4.0", + "git-diff": "0.5.0", "gists": "0.3.0", "github-sign-in": "0.7.0", "go-to-line": "0.4.0",