From bbb7dfdd07ef301d9c12c3f4ffaca56a92d8da18 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 30 Apr 2013 11:32:09 -0700 Subject: [PATCH 01/91] Only the underlayer needs height: 100% --- static/editor.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/editor.less b/static/editor.less index 7e81c9cab..e36496814 100644 --- a/static/editor.less +++ b/static/editor.less @@ -120,12 +120,12 @@ .editor .underlayer, .editor .lines, .editor .overlayer { width: 100%; - height: 100%; } .editor .underlayer { z-index: 0; position: absolute; + height: 100%; } .editor .lines { From 35b0e062609150401c28daea0ee7cb2830d8934e Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 30 Apr 2013 11:36:07 -0700 Subject: [PATCH 02/91] :lipstick: --- src/app/editor.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/editor.coffee b/src/app/editor.coffee index a718bcb85..9defac0f1 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -895,6 +895,7 @@ class Editor extends View @activeEditSession.finalizeSelections() @syncCursorAnimations() + afterAttach: (onDom) -> return unless onDom @redraw() if @redrawOnReattach @@ -1274,8 +1275,8 @@ class Editor extends View updateLayerDimensions: -> height = @lineHeight * @getScreenLineCount() unless @layerHeight == height - @renderedLines.height(height) @underlayer.css('min-height', height) + @renderedLines.height(height) @overlayer.height(height) @layerHeight = height From 16c892caa6ca45c3f47c87c19720544ccad9479c Mon Sep 17 00:00:00 2001 From: probablycorey Date: Tue, 30 Apr 2013 11:47:29 -0700 Subject: [PATCH 03/91] Update click and drag spec Stop relying on setInterval to trigger the scroll events in the spec. Instead simulate the mouse moving at the top of the screen. I found testing the setInterval approach required so much mocking that it wasn't worth it. --- spec/app/editor-spec.coffee | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 8e9c97cf7..dec1c2a3f 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -541,20 +541,23 @@ describe "Editor", -> expect(editor.getCursorScreenPosition()).toEqual(row: 5, column: 27) it "selects and scrolls if the mouse is dragged outside of the editor itself", -> - intervalFns = [] + editor.vScrollMargin = 0 editor.attachToDom(heightInLines: 5) editor.scrollToBottom() - spyOn(window, 'setInterval').andCallFake (fn) -> intervalFns.push(fn) + spyOn(window, 'setInterval').andCallFake -> + # start editor.renderedLines.trigger mousedownEvent(editor: editor, point: [12, 0]) + originalScrollTop = editor.scrollTop() # moving changes selection - $(document).trigger mousemoveEvent(editor: editor, pageX: 0, pageY: -15) - expect(editor.scrollTop()).toBe 4 * editor.lineHeight + $(document).trigger mousemoveEvent(editor: editor, pageX: 0, pageY: -1) + expect(editor.scrollTop()).toBe originalScrollTop - editor.lineHeight - # if cursor stays off screen, we keep moving / scrolling up - fn() for fn in intervalFns + # every mouse move selects more text + for x in [0..10] + $(document).trigger mousemoveEvent(editor: editor, pageX: 0, pageY: -1) expect(editor.scrollTop()).toBe 0 From 471ac4976fcee3d6310ea5902da1a76c81e0b02e Mon Sep 17 00:00:00 2001 From: probablycorey Date: Tue, 30 Apr 2013 11:48:09 -0700 Subject: [PATCH 04/91] Test correct editor in font size spec --- spec/app/editor-spec.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index dec1c2a3f..b1666b857 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -320,8 +320,9 @@ describe "Editor", -> expect(editor.verticalScrollbarContent.height()).toBe buffer.getLineCount() * editor.lineHeight newEditor = new Editor(editor.activeEditSession.copy()) + editor.remove() newEditor.attachToDom() - expect(editor.css('font-size')).toBe '30px' + expect(newEditor.css('font-size')).toBe '30px' it "updates the position and size of selection regions", -> config.set("editor.fontSize", 10) From af5392b8ac4d992cdbf8a47acc1e2ed37d3ecb91 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Tue, 30 Apr 2013 11:52:02 -0700 Subject: [PATCH 05/91] Add extra padding to the bottom of an editor's vertical scrollbar This gives us some space when we scroll to the bottom of a file. Similar to how MacVim works when using the mouse. Closes #464 --- spec/app/editor-spec.coffee | 4 +++- src/app/editor.coffee | 9 +++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index b1666b857..0c0f76a58 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -317,7 +317,7 @@ describe "Editor", -> expect(editor.charWidth).toBeGreaterThan charWidthBefore expect(editor.getCursorView().position()).toEqual { top: 5 * editor.lineHeight, left: 6 * editor.charWidth } expect(editor.renderedLines.outerHeight()).toBe buffer.getLineCount() * editor.lineHeight - expect(editor.verticalScrollbarContent.height()).toBe buffer.getLineCount() * editor.lineHeight + expect(editor.verticalScrollbarContent.height()).toBe (buffer.getLineCount() + editor.bottomPaddingInLines) * editor.lineHeight newEditor = new Editor(editor.activeEditSession.copy()) editor.remove() @@ -1297,6 +1297,7 @@ describe "Editor", -> describe "when lines are removed", -> beforeEach -> + editor.bottomPaddingInLines = 0 editor.attachToDom(heightInLines: 5) it "sets the rendered screen line's width to either the max line length or the scollView's width (whichever is greater)", -> @@ -1391,6 +1392,7 @@ describe "Editor", -> describe "when autoscrolling at the end of the document", -> it "renders lines properly", -> editor.edit(project.buildEditSession('two-hundred.txt')) + editor.bottomPaddingInLines = 0 editor.attachToDom(heightInLines: 5.5) expect(editor.renderedLines.find('.line').length).toBe 8 diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 9defac0f1..f8f47ba2f 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -64,6 +64,7 @@ class Editor extends View newCursors: null newSelections: null redrawOnReattach: false + bottomPaddingInLines: 10 # Public: The constructor for setting up an `Editor` instance. # @@ -1280,7 +1281,10 @@ class Editor extends View @overlayer.height(height) @layerHeight = height - @verticalScrollbarContent.height(height) + bottomPaddingInLines = if @mini then 0 else @bottomPaddingInLines + heightWithPadding = height + (@lineHeight * bottomPaddingInLines) + @verticalScrollbarContent.height(heightWithPadding) + @scrollBottom(height) if @scrollBottom() > height minWidth = @charWidth * @maxScreenLineLength() + 20 @@ -1516,7 +1520,8 @@ class Editor extends View # # Returns a {Number}. getLastVisibleScreenRow: -> - Math.max(0, Math.ceil((@scrollTop() + @scrollView.height()) / @lineHeight) - 1) + calculatedRow = Math.ceil((@scrollTop() + @scrollView.height()) / @lineHeight) - 1 + Math.max(0, Math.min(@getScreenLineCount() - 1, calculatedRow)) # Public: Given a row number, identifies if it is currently visible. # From a788006d7c96503fcdd598b2f1c2c3cf24fb1c6a Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 30 Apr 2013 15:03:46 -0700 Subject: [PATCH 06/91] Default editor.preferredLineLength config to 80 --- src/app/editor.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/editor.coffee b/src/app/editor.coffee index f8f47ba2f..f1f37df8f 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -23,6 +23,7 @@ class Editor extends View autoIndent: true autoIndentOnPaste: false nonWordCharacters: "./\\()\"':,.;<>~!@#$%^&*|+=[]{}`~?-" + preferredLineLength: 80 @nextEditorId: 1 From ca7da8a0daaa28752c2dca78dc4383cffd7c9c35 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 30 Apr 2013 21:32:19 -0700 Subject: [PATCH 07/91] Don't set un-parseable numbers to 0 in the config Previously if an integer or float field was empty it would default to zero instead of undefined which made it inconsistent with string value fields. Now the config value is only set as a Number when it can be parsed as one. --- spec/app/config-panel-spec.coffee | 24 ++++++++++++++++++++++-- src/app/config-panel.coffee | 31 +++++++++++++++---------------- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/spec/app/config-panel-spec.coffee b/spec/app/config-panel-spec.coffee index a1f9dabed..be8717246 100644 --- a/spec/app/config-panel-spec.coffee +++ b/spec/app/config-panel-spec.coffee @@ -42,6 +42,19 @@ describe "ConfigPanel", -> panel.stringInput.val('moo').change() expect(config.get('foo.string')).toBe 'moo' + panel.intInput.val('abcd').change() + expect(config.get('foo.int')).toBe 'abcd' + + panel.floatInput.val('defg').change() + expect(config.get('foo.float')).toBe 'defg' + + panel.intInput.val('').change() + expect(config.get('foo.int')).toBe undefined + panel.floatInput.val('').change() + expect(config.get('foo.float')).toBe undefined + panel.stringInput.val('').change() + expect(config.get('foo.string')).toBe undefined + it "automatically binds named editors to their corresponding config keys", -> class TestPanel extends ConfigPanel @content: -> @@ -73,10 +86,17 @@ describe "ConfigPanel", -> expect(config.get('foo.float')).toBe 3.3 expect(config.get('foo.string')).toBe 'All limitations are self imposed.' + panel.intEditor.setText('not an int') panel.floatEditor.setText('not a float') + window.advanceClock(10000) # wait for contents-modified to be triggered + expect(config.get('foo.int')).toBe 'not an int' + expect(config.get('foo.float')).toBe 'not a float' + + panel.intEditor.setText('') + panel.floatEditor.setText('') panel.stringEditor.setText('') window.advanceClock(10000) # wait for contents-modified to be triggered - expect(config.get('foo.int')).toBe 0 - expect(config.get('foo.float')).toBe 0 + expect(config.get('foo.int')).toBe undefined + expect(config.get('foo.float')).toBe undefined expect(config.get('foo.string')).toBe undefined diff --git a/src/app/config-panel.coffee b/src/app/config-panel.coffee index 16aa17ab9..178248a90 100644 --- a/src/app/config-panel.coffee +++ b/src/app/config-panel.coffee @@ -22,17 +22,20 @@ class ConfigPanel extends View input.attr('checked', value) else input.val(value) if value - input.on 'change', -> + input.on 'change', => value = input.val() - config.set name, switch type - when 'int' - parseInt(value) - when 'float' - parseFloat(value) - when 'checkbox' - !!input.attr('checked') - else - value + if type == 'checkbox' + value = !!input.attr('checked') + else + value = @parseValue(type, value) + config.set(name, value) + + parseValue: (type, value) -> + switch type + when 'int' then value = parseInt(value) or value + when 'float' then value = parseFloat(value) or value + value = undefined if value == '' + value bindEditors: -> for editor in @find('.editor[id]').views() @@ -45,9 +48,5 @@ class ConfigPanel extends View value ?= "" editor.setText(value.toString()) - editor.getBuffer().on 'contents-modified', -> - value = editor.getText() - if type == 'int' then value = parseInt(value) or 0 - if type == 'float' then value = parseFloat(value) or 0 - if value == "" then value = undefined - config.set name, value + editor.getBuffer().on 'contents-modified', => + config.set(name, @parseValue(type, editor.getText())) From 047d9525e78aa6c779bb2f1b4059ba91639ade1b Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 30 Apr 2013 21:55:43 -0700 Subject: [PATCH 08/91] Ignore first modified event in config editor This event comes from initially setting the text for the current config value on the editor and was causing the config to be immediately saved multiple times when opened. --- spec/app/config-panel-spec.coffee | 13 +++++++++++++ src/app/config-panel.coffee | 5 +++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/spec/app/config-panel-spec.coffee b/spec/app/config-panel-spec.coffee index be8717246..0b56292b7 100644 --- a/spec/app/config-panel-spec.coffee +++ b/spec/app/config-panel-spec.coffee @@ -67,6 +67,7 @@ describe "ConfigPanel", -> config.set('foo.float', 1.1) config.set('foo.string', 'I think therefore I am.') panel = new TestPanel + window.advanceClock(10000) # wait for contents-modified to be triggered expect(panel.intEditor.getText()).toBe '1' expect(panel.floatEditor.getText()).toBe '1.1' expect(panel.stringEditor.getText()).toBe 'I think therefore I am.' @@ -100,3 +101,15 @@ describe "ConfigPanel", -> expect(config.get('foo.int')).toBe undefined expect(config.get('foo.float')).toBe undefined expect(config.get('foo.string')).toBe undefined + + it "does not save the config value until it has been changed to a new value", -> + class TestPanel extends ConfigPanel + @content: -> + @div => + @subview "foo.int", new Editor(mini: true, attributes: {id: 'foo.int', type: 'int'}) + + config.set('foo.int', 1) + spyOn(config, 'set') + new TestPanel + window.advanceClock(10000) # wait for contents-modified to be triggered + expect(config.set).not.toHaveBeenCalled() diff --git a/src/app/config-panel.coffee b/src/app/config-panel.coffee index 178248a90..663aa5697 100644 --- a/src/app/config-panel.coffee +++ b/src/app/config-panel.coffee @@ -48,5 +48,6 @@ class ConfigPanel extends View value ?= "" editor.setText(value.toString()) - editor.getBuffer().on 'contents-modified', => - config.set(name, @parseValue(type, editor.getText())) + editor.getBuffer().one 'contents-modified', => + editor.getBuffer().on 'contents-modified', => + config.set(name, @parseValue(type, editor.getText())) From f417e898f6821d45357059ba4b187698c8e1dd25 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 30 Apr 2013 22:02:46 -0700 Subject: [PATCH 09/91] Only call save on active item when it exists Closes #529 --- spec/app/pane-spec.coffee | 1 + src/app/pane.coffee | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/app/pane-spec.coffee b/spec/app/pane-spec.coffee index a55650699..ca5ded3dd 100644 --- a/spec/app/pane-spec.coffee +++ b/spec/app/pane-spec.coffee @@ -303,6 +303,7 @@ describe "Pane", -> describe "when the current item has no save method", -> it "does nothing", -> + pane.activeItem.getUri = -> 'you are eye' expect(pane.activeItem.save).toBeUndefined() pane.trigger 'core:save' diff --git a/src/app/pane.coffee b/src/app/pane.coffee index b042a66fb..b224ab624 100644 --- a/src/app/pane.coffee +++ b/src/app/pane.coffee @@ -186,7 +186,7 @@ class Pane extends View saveItem: (item, nextAction) -> if item.getUri?() - item.save() + item.save?() nextAction?() else @saveItemAs(item, nextAction) From 6077cf23898248400e620a3883761ca24cd5d824 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 30 Apr 2013 22:06:05 -0700 Subject: [PATCH 10/91] Mention save error with markdow preview --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b06c16ca..6a1c5cda2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +* Fixed: Error when saving with the markdown preview focused + * Fixed: Atom always running in dev mode * Fixed: Crash when running in dev mode without a path to the Atom source From 986e5f9c7a76346808e8ef21d785c2b5a1f563e5 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 30 Apr 2013 22:29:48 -0700 Subject: [PATCH 11/91] Default to 80 when editor.preferredLineLength <= 0 Previously any non-null value would be used as the target column in the wrap guide and autoflow packages when really 80 should have been used if the value was non-postive. Now config.getPositiveInt() is called with a default value of 80 if the current value isn't already positive. --- spec/app/config-spec.coffee | 11 +++++++++++ src/app/config.coffee | 18 ++++++++++++++++++ src/packages/autoflow/autoflow.coffee | 2 +- .../wrap-guide/lib/wrap-guide-view.coffee | 2 +- 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/spec/app/config-spec.coffee b/spec/app/config-spec.coffee index 7bfe22e2f..f09fa913b 100644 --- a/spec/app/config-spec.coffee +++ b/spec/app/config-spec.coffee @@ -17,6 +17,17 @@ describe "Config", -> expect(config.save).toHaveBeenCalled() expect(observeHandler).toHaveBeenCalledWith 42 + describe ".getPositiveInt(keyPath, defaultValue)", -> + it "returns the proper current or default value", -> + config.set('editor.preferredLineLength', 0) + expect(config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80 + config.set('editor.preferredLineLength', -1234) + expect(config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80 + config.set('editor.preferredLineLength', 'abcd') + expect(config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80 + config.set('editor.preferredLineLength', null) + expect(config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80 + describe ".save()", -> beforeEach -> spyOn(fsUtils, 'write') diff --git a/src/app/config.coffee b/src/app/config.coffee index 067f3d0cb..8209307cf 100644 --- a/src/app/config.coffee +++ b/src/app/config.coffee @@ -100,6 +100,24 @@ class Config _.valueForKeyPath(@settings, keyPath) ? _.valueForKeyPath(@defaultSettings, keyPath) + # Public: Retrieves the setting for the given key as an integer number. + # + # keyPath - The {String} name of the key to retrieve + # Returns the value from Atom's default settings, the user's configuration file, + # or `NaN` if the key doesn't exist in either. + getInt: (keyPath, defaultValueWhenFalsy) -> + parseInt(@get(keyPath)) + + # Public: Retrieves the setting for the given key as a positive integer number. + # + # keyPath - The {String} name of the key to retrieve + # defaultValue - The integer {Number} to fall back to if the value isn't + # positive + # Returns the value from Atom's default settings, the user's configuration file, + # or `defaultValue` if the key value isn't greater than zero. + getPositiveInt: (keyPath, defaultValue) -> + Math.max(@getInt(keyPath), 0) or defaultValue + # Public: Sets the value for a configuration setting. # # This value is stored in Atom's internal configuration file. diff --git a/src/packages/autoflow/autoflow.coffee b/src/packages/autoflow/autoflow.coffee index 058b9c3b5..83bf56d02 100644 --- a/src/packages/autoflow/autoflow.coffee +++ b/src/packages/autoflow/autoflow.coffee @@ -8,7 +8,7 @@ module.exports = editor.getBuffer().change(range, @reflow(editor.getTextInRange(range))) reflow: (text) -> - wrapColumn = config.get('editor.preferredLineLength') ? 80 + wrapColumn = config.getPositiveInt('editor.preferredLineLength', 80) lines = [] currentLine = [] diff --git a/src/packages/wrap-guide/lib/wrap-guide-view.coffee b/src/packages/wrap-guide/lib/wrap-guide-view.coffee index dcce9c44e..dbfbc6333 100644 --- a/src/packages/wrap-guide/lib/wrap-guide-view.coffee +++ b/src/packages/wrap-guide/lib/wrap-guide-view.coffee @@ -19,7 +19,7 @@ class WrapGuideView extends View @subscribe $(window), 'resize', => @updateGuide() getDefaultColumn: -> - config.get('editor.preferredLineLength') ? 80 + config.getPositiveInt('editor.preferredLineLength', 80) getGuideColumn: (path) -> customColumns = config.get('wrapGuide.columns') From 334c4095bbb4f87fdb22a1c90e6fa247f2a6e526 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 30 Apr 2013 22:43:51 -0700 Subject: [PATCH 12/91] Write empty object values on same line as keys --- spec/stdlib/cson-spec.coffee | 2 ++ src/stdlib/cson.coffee | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/spec/stdlib/cson-spec.coffee b/spec/stdlib/cson-spec.coffee index bee9e2032..6ec3ec12a 100644 --- a/spec/stdlib/cson-spec.coffee +++ b/spec/stdlib/cson-spec.coffee @@ -73,6 +73,8 @@ describe "CSON", -> it "returns formatted CSON", -> expect(CSON.stringify(a: {b: 'c'})).toBe "'a':\n 'b': 'c'" + expect(CSON.stringify(a:{})).toBe "'a': {}" + expect(CSON.stringify(a:[])).toBe "'a': []" describe "when converting back to an object", -> it "produces the original object", -> diff --git a/src/stdlib/cson.coffee b/src/stdlib/cson.coffee index 5b8527d75..6d703d578 100644 --- a/src/stdlib/cson.coffee +++ b/src/stdlib/cson.coffee @@ -94,7 +94,10 @@ module.exports = else if _.isArray(value) cson += " #{@stringifyArray(value, indentLevel)}" else if _.isObject(value) - cson += "\n#{@stringifyObject(value, indentLevel + 2)}" + if _.isEmpty(value) + cson += ' {}' + else + cson += "\n#{@stringifyObject(value, indentLevel + 2)}" else throw new Error("Unrecognized value type for key: #{key} with value: #{value}") prefix = '\n' From 1afe4f0cd9d8e4a8d6d13095ded60d626364ca2a Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 30 Apr 2013 23:00:23 -0700 Subject: [PATCH 13/91] Mention missing wrap guide fix --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a1c5cda2..f0e355262 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +* Fixed: Wrap guide not displaying * Fixed: Error when saving with the markdown preview focused * Fixed: Atom always running in dev mode From 536beb0d40243c76026cbd74828fcdb09e49652b Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 30 Apr 2013 23:10:22 -0700 Subject: [PATCH 14/91] Support setting number fields to 0 Previously entering 0 would end up as '0' in the config value instead of as a numeric value. --- spec/app/config-panel-spec.coffee | 12 ++++++++++++ src/app/config-panel.coffee | 8 ++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/spec/app/config-panel-spec.coffee b/spec/app/config-panel-spec.coffee index 0b56292b7..e79571c7d 100644 --- a/spec/app/config-panel-spec.coffee +++ b/spec/app/config-panel-spec.coffee @@ -39,6 +39,12 @@ describe "ConfigPanel", -> panel.floatInput.val('90.2').change() expect(config.get('foo.float')).toBe 90.2 + panel.intInput.val('0').change() + expect(config.get('foo.int')).toBe 0 + + panel.floatInput.val('0').change() + expect(config.get('foo.float')).toBe 0 + panel.stringInput.val('moo').change() expect(config.get('foo.string')).toBe 'moo' @@ -102,6 +108,12 @@ describe "ConfigPanel", -> expect(config.get('foo.float')).toBe undefined expect(config.get('foo.string')).toBe undefined + panel.intEditor.setText('0') + panel.floatEditor.setText('0') + window.advanceClock(10000) # wait for contents-modified to be triggered + expect(config.get('foo.int')).toBe 0 + expect(config.get('foo.float')).toBe 0 + it "does not save the config value until it has been changed to a new value", -> class TestPanel extends ConfigPanel @content: -> diff --git a/src/app/config-panel.coffee b/src/app/config-panel.coffee index 663aa5697..e0dcb811b 100644 --- a/src/app/config-panel.coffee +++ b/src/app/config-panel.coffee @@ -32,8 +32,12 @@ class ConfigPanel extends View parseValue: (type, value) -> switch type - when 'int' then value = parseInt(value) or value - when 'float' then value = parseFloat(value) or value + when 'int' + intValue = parseInt(value) + value = intValue unless isNaN(intValue) + when 'float' + floatValue = parseFloat(value) + value = floatValue unless isNaN(floatValue) value = undefined if value == '' value From c3232a463c6cbf360c247af913a3891e64893184 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 1 May 2013 07:58:12 -0700 Subject: [PATCH 15/91] Use ems instead of pixels for config editor widths This allows the editor to be big enough to still show multiple characters in large font sizes. --- src/app/editor-config-panel.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/editor-config-panel.coffee b/src/app/editor-config-panel.coffee index a350d41b5..ddebeac36 100644 --- a/src/app/editor-config-panel.coffee +++ b/src/app/editor-config-panel.coffee @@ -15,7 +15,7 @@ class EditorConfigPanel extends ConfigPanel @div class: 'control-group', => @label class: 'control-label', "Font Size:" @div class: 'controls', => - @subview "fontSizeEditor", new Editor(mini: true, attributes: {id: 'editor.fontSize', type: 'int', style: 'width: 40px'}) + @subview "fontSizeEditor", new Editor(mini: true, attributes: {id: 'editor.fontSize', type: 'int', style: 'width: 4em'}) @div class: 'control-group', => @label class: 'control-label', "Font Family:" @@ -56,7 +56,7 @@ class EditorConfigPanel extends ConfigPanel @div class: 'control-group', => @label class: 'control-label', for: 'editor.preferredLineLength', "Preferred Line Length:" @div class: 'controls', => - @subview "preferredLineLengthEditor", new Editor(mini: true, attributes: {id: 'editor.preferredLineLength', type: 'int', style: 'width: 40px'}) + @subview "preferredLineLengthEditor", new Editor(mini: true, attributes: {id: 'editor.preferredLineLength', type: 'int', style: 'width: 4em'}) @div class: 'control-group', => @label class: 'control-label', for: 'editor.nonWordCharacters', "Non-Word Characters:" From 78d8485243bec3bd8e0d87cd30b5f0e4a078df1a Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 1 May 2013 08:02:17 -0700 Subject: [PATCH 16/91] Allow config window to be closed with meta-W window.confirmClose() should close the window immediately when there is no rootView which is the case in the config window. --- src/app/window.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/window.coffee b/src/app/window.coffee index fdad3efec..a70829762 100644 --- a/src/app/window.coffee +++ b/src/app/window.coffee @@ -263,4 +263,7 @@ window.profile = (description, fn) -> # Public: Shows a dialog asking if the window was _really_ meant to be closed. confirmClose = -> - rootView.confirmClose().done -> window.close() + if rootView? + rootView.confirmClose().done -> window.close() + else + window.close() From 932d552ae749d3f16c047b76f928d4ef6cb9d47d Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 1 May 2013 08:22:03 -0700 Subject: [PATCH 17/91] Only show scrolls when needed in tree view --- src/packages/tree-view/stylesheets/tree-view.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/tree-view/stylesheets/tree-view.less b/src/packages/tree-view/stylesheets/tree-view.less index 4e02304b3..fcc209fca 100644 --- a/src/packages/tree-view/stylesheets/tree-view.less +++ b/src/packages/tree-view/stylesheets/tree-view.less @@ -24,7 +24,7 @@ .tree-view-scroller { height: 100%; width: 100%; - overflow: scroll; + overflow: auto; } .tree-view { From aeffa0e150fab00ff8df0177111aed63c1b18ebd Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 1 May 2013 13:10:14 -0700 Subject: [PATCH 18/91] Remove duplicate property --- static/octicon-mixins.less | 1 - 1 file changed, 1 deletion(-) diff --git a/static/octicon-mixins.less b/static/octicon-mixins.less index fa660a6e2..ea2b93892 100644 --- a/static/octicon-mixins.less +++ b/static/octicon-mixins.less @@ -5,7 +5,6 @@ display: inline-block; line-height: 1; -webkit-font-smoothing: antialiased; - line-height: 1; text-decoration: none; } From 7609b08e86f2f298b7ddb54907dfba57997bdf71 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Tue, 30 Apr 2013 14:35:45 -0700 Subject: [PATCH 19/91] :lipstick: --- src/app/editor.coffee | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/app/editor.coffee b/src/app/editor.coffee index f1f37df8f..f52312ac8 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -841,7 +841,7 @@ class Editor extends View @destroyFold($(e.currentTarget).attr('fold-id')) false - onMouseDown = (e) => + @renderedLines.on 'mousedown', (e) => clickCount = e.originalEvent.detail screenPosition = @screenPositionFromMouseEvent(e) @@ -859,8 +859,6 @@ class Editor extends View @selectOnMousemoveUntilMouseup() unless e.ctrlKey or e.originalEvent.which > 1 - @renderedLines.on 'mousedown', onMouseDown - @on "textInput", (e) => @insertText(e.originalEvent.data) false From cb2d24baca067641f1615244d883d95840c203e4 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Tue, 30 Apr 2013 15:03:44 -0700 Subject: [PATCH 20/91] Listen for clicks outside of the render lines on the underlayer --- spec/app/editor-spec.coffee | 9 +++------ src/app/editor.coffee | 18 +++++++----------- static/editor.less | 2 +- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 0c0f76a58..690ea5c26 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -1214,7 +1214,7 @@ describe "Editor", -> describe "when scrolling more than the editors height", -> it "removes lines that are offscreen and not in range of the overdraw and builds lines that become visible", -> - editor.scrollTop(editor.scrollView.prop('scrollHeight') - editor.scrollView.height()) + editor.scrollTop(editor.layerHeight - editor.scrollView.height()) expect(editor.renderedLines.find('.line').length).toBe 8 expect(editor.renderedLines.find('.line:first').text()).toBe buffer.lineForRow(5) expect(editor.renderedLines.find('.line:last').text()).toBe buffer.lineForRow(12) @@ -2131,16 +2131,13 @@ describe "Editor", -> it "move the cursor to the end of the file", -> expect(editor.getCursorScreenPosition()).toEqual [0,0] - event = $.Event("click") - event.offsetY = Infinity + event = mousedownEvent(editor: editor, point: [Infinity, 10]) editor.underlayer.trigger event expect(editor.getCursorScreenPosition()).toEqual [12,2] it "selects to the end of the files when shift is pressed", -> expect(editor.getSelection().getScreenRange()).toEqual [[0,0], [0,0]] - event = $.Event("click") - event.offsetY = Infinity - event.shiftKey = true + event = mousedownEvent(editor: editor, point: [Infinity, 10], shiftKey: true) editor.underlayer.trigger event expect(editor.getSelection().getScreenRange()).toEqual [[0,0], [12,2]] diff --git a/src/app/editor.coffee b/src/app/editor.coffee index f52312ac8..905d14034 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -821,13 +821,9 @@ class Editor extends View @isFocused = false @removeClass 'is-focused' - @underlayer.on 'click', (e) => - return unless e.target is @underlayer[0] - return unless e.offsetY > @overlayer.height() - if e.shiftKey - @selectToBottom() - else - @moveCursorToBottom() + @underlayer.on 'mousedown', (e) => + @renderedLines.trigger(e) + false @overlayer.on 'mousedown', (e) => @overlayer.hide() @@ -1275,14 +1271,14 @@ class Editor extends View updateLayerDimensions: -> height = @lineHeight * @getScreenLineCount() unless @layerHeight == height - @underlayer.css('min-height', height) - @renderedLines.height(height) - @overlayer.height(height) @layerHeight = height + @renderedLines.height(@layerHeight) + @overlayer.height(@layerHeight) bottomPaddingInLines = if @mini then 0 else @bottomPaddingInLines - heightWithPadding = height + (@lineHeight * bottomPaddingInLines) + heightWithPadding = @layerHeight + (@lineHeight * bottomPaddingInLines) @verticalScrollbarContent.height(heightWithPadding) + @underlayer.height(heightWithPadding) @scrollBottom(height) if @scrollBottom() > height diff --git a/static/editor.less b/static/editor.less index e36496814..699ffe3bc 100644 --- a/static/editor.less +++ b/static/editor.less @@ -125,7 +125,7 @@ .editor .underlayer { z-index: 0; position: absolute; - height: 100%; + min-height: 100%; } .editor .lines { From 0be166bb59b0d7feb63a51793ba3ef02228f1b43 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Tue, 30 Apr 2013 15:13:43 -0700 Subject: [PATCH 21/91] Remove the bottomPaddingInLines from editor --- spec/app/editor-spec.coffee | 4 +--- src/app/editor.coffee | 8 ++------ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 690ea5c26..7419ef4ee 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -317,7 +317,7 @@ describe "Editor", -> expect(editor.charWidth).toBeGreaterThan charWidthBefore expect(editor.getCursorView().position()).toEqual { top: 5 * editor.lineHeight, left: 6 * editor.charWidth } expect(editor.renderedLines.outerHeight()).toBe buffer.getLineCount() * editor.lineHeight - expect(editor.verticalScrollbarContent.height()).toBe (buffer.getLineCount() + editor.bottomPaddingInLines) * editor.lineHeight + expect(editor.verticalScrollbarContent.height()).toBe buffer.getLineCount() * editor.lineHeight newEditor = new Editor(editor.activeEditSession.copy()) editor.remove() @@ -1297,7 +1297,6 @@ describe "Editor", -> describe "when lines are removed", -> beforeEach -> - editor.bottomPaddingInLines = 0 editor.attachToDom(heightInLines: 5) it "sets the rendered screen line's width to either the max line length or the scollView's width (whichever is greater)", -> @@ -1392,7 +1391,6 @@ describe "Editor", -> describe "when autoscrolling at the end of the document", -> it "renders lines properly", -> editor.edit(project.buildEditSession('two-hundred.txt')) - editor.bottomPaddingInLines = 0 editor.attachToDom(heightInLines: 5.5) expect(editor.renderedLines.find('.line').length).toBe 8 diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 905d14034..ed720127f 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -1272,14 +1272,10 @@ class Editor extends View height = @lineHeight * @getScreenLineCount() unless @layerHeight == height @layerHeight = height + @underlayer.height(@layerHeight) @renderedLines.height(@layerHeight) @overlayer.height(@layerHeight) - - bottomPaddingInLines = if @mini then 0 else @bottomPaddingInLines - heightWithPadding = @layerHeight + (@lineHeight * bottomPaddingInLines) - @verticalScrollbarContent.height(heightWithPadding) - @underlayer.height(heightWithPadding) - + @verticalScrollbarContent.height(@layerHeight) @scrollBottom(height) if @scrollBottom() > height minWidth = @charWidth * @maxScreenLineLength() + 20 From 8f03ff29524a63d373d495d922a1a80eacee06a4 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Wed, 1 May 2013 12:04:33 -0700 Subject: [PATCH 22/91] :lipstick: --- native/atom_application.mm | 3 +-- native/atom_cef_client.cpp | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/native/atom_application.mm b/native/atom_application.mm index f3117f94f..6a4092665 100644 --- a/native/atom_application.mm +++ b/native/atom_application.mm @@ -278,8 +278,7 @@ } } -- (NSApplicationTerminateReply)applicationShouldTerminate: - (NSApplication *)sender { +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { for (NSWindow *window in [self windows]) { [window performClose:self]; } diff --git a/native/atom_cef_client.cpp b/native/atom_cef_client.cpp index 4b39da42d..025a7921d 100644 --- a/native/atom_cef_client.cpp +++ b/native/atom_cef_client.cpp @@ -171,7 +171,6 @@ bool AtomCefClient::OnKeyEvent(CefRefPtr browser, } void AtomCefClient::OnBeforeClose(CefRefPtr browser) { -// REQUIRE_UI_THREAD(); // When uncommented this fails when app is terminated m_Browser = NULL; numberOfOpenBrowsers--; if (numberOfOpenBrowsers == 0) { From 850ecb23dfff0dc06d1fa339bf74865f224da4b7 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Wed, 1 May 2013 13:52:56 -0700 Subject: [PATCH 23/91] Atom will auto-update on close if a newer version is on Speakeasy The first call to terminate is canceled so that every window can be closed. On AtomCefClient the OnBeforeClose method is called when a browser is finished closing. Once all windows have finished closing, AtomCefClient calls terminate again, which will terminate the application. Closes #527 --- native/atom_application.mm | 17 +++++++++++++---- native/atom_cef_client.cpp | 2 +- native/atom_cef_client.h | 1 + native/atom_cef_client_mac.mm | 4 ++++ 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/native/atom_application.mm b/native/atom_application.mm index 6a4092665..c831239c3 100644 --- a/native/atom_application.mm +++ b/native/atom_application.mm @@ -278,12 +278,21 @@ } } +// The first call to terminate is canceled so that every window can be closed. +// On AtomCefClient the OnBeforeClose method is called when a browser is +// finished closing. Once all windows have finished closing, AtomCefClient calls +// terminate again, which will terminate the application. - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { - for (NSWindow *window in [self windows]) { - [window performClose:self]; + if (self.windows.count > 0) { + for (NSWindow *window in self.windows) { + [window performClose:self]; + } + return NSTerminateCancel; + } + else { + CefQuitMessageLoop(); + return NSTerminateNow; } - - return NSTerminateCancel; } # pragma mark CefAppProtocol diff --git a/native/atom_cef_client.cpp b/native/atom_cef_client.cpp index 025a7921d..b25cee48e 100644 --- a/native/atom_cef_client.cpp +++ b/native/atom_cef_client.cpp @@ -174,7 +174,7 @@ void AtomCefClient::OnBeforeClose(CefRefPtr browser) { m_Browser = NULL; numberOfOpenBrowsers--; if (numberOfOpenBrowsers == 0) { - CefQuitMessageLoop(); + Terminate(); } } diff --git a/native/atom_cef_client.h b/native/atom_cef_client.h index 83c07ec12..8f93c384c 100644 --- a/native/atom_cef_client.h +++ b/native/atom_cef_client.h @@ -127,6 +127,7 @@ class AtomCefClient : public CefClient, void ShowSaveDialog(int replyId, CefRefPtr browser); CefRefPtr CreateReplyDescriptor(int replyId, int callbackIndex); void Exit(int status); + void Terminate(); void Log(const char *message); void Show(CefRefPtr browser); void ToggleFullScreen(CefRefPtr browser); diff --git a/native/atom_cef_client_mac.mm b/native/atom_cef_client_mac.mm index 72573ebb2..bb7acb242 100644 --- a/native/atom_cef_client_mac.mm +++ b/native/atom_cef_client_mac.mm @@ -159,6 +159,10 @@ void AtomCefClient::Exit(int status) { exit(status); } +void AtomCefClient::Terminate() { + [NSApp terminate:NSApp]; +} + void AtomCefClient::Log(const char *message) { NSLog(@"%s", message); } From 01ad97e7b011684cc9fefe0af24b648cfe6ded73 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 1 May 2013 16:46:22 -0700 Subject: [PATCH 24/91] Show ellipsis for long paths in fuzzy finder Previously the directory was shown after the file name and it would wrap if too long causing inconsistent row heights. Now the full path is shown below the name in a smaller font and both the path and file are set to overflow with an ellipsis. --- .../fuzzy-finder/lib/fuzzy-finder-view.coffee | 5 ++--- .../spec/fuzzy-finder-spec.coffee | 6 +++--- .../stylesheets/fuzzy-finder.less | 21 +++++++++++++++---- static/overlay.less | 2 +- themes/atom-dark-ui/select-list.less | 4 ++-- 5 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee b/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee index 0080f7bbb..56d6074d8 100644 --- a/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee +++ b/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee @@ -59,9 +59,8 @@ class FuzzyFinderView extends SelectList else typeClass = 'text-name' - @span fsUtils.base(path), class: "file #{typeClass}" - if folder = project.relativize(fsUtils.directory(path)) - @span " - #{folder}/", class: 'directory' + @div fsUtils.base(path), class: "file #{typeClass}" + @div project.relativize(path), class: 'path' openPath: (path) -> rootView.open(path, {@allowActiveEditorChange}) if path diff --git a/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee b/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee index 15035a371..f9efdf36b 100644 --- a/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee +++ b/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee @@ -137,13 +137,13 @@ describe 'FuzzyFinder', -> rootView.attachToDom() rootView.open 'sample-with-tabs.coffee' rootView.trigger 'fuzzy-finder:toggle-buffer-finder' - expect(_.pluck(finderView.list.children('li'), 'outerText')).toEqual ['sample.txt', 'sample.js', 'sample-with-tabs.coffee'] + expect(_.pluck(finderView.list.find('li > div.file'), 'outerText')).toEqual ['sample.txt', 'sample.js', 'sample-with-tabs.coffee'] rootView.trigger 'fuzzy-finder:toggle-buffer-finder' rootView.open 'sample.txt' rootView.trigger 'fuzzy-finder:toggle-buffer-finder' - expect(_.pluck(finderView.list.children('li'), 'outerText')).toEqual ['sample-with-tabs.coffee', 'sample.js', 'sample.txt'] + expect(_.pluck(finderView.list.find('li > div.file'), 'outerText')).toEqual ['sample-with-tabs.coffee', 'sample.js', 'sample.txt'] expect(finderView.list.children().first()).toHaveClass 'selected' it "serializes the list of paths and their last opened time", -> @@ -182,7 +182,7 @@ describe 'FuzzyFinder', -> rootView.open 'sample.js' rootView.getActivePane().splitRight() rootView.trigger 'fuzzy-finder:toggle-buffer-finder' - expect(_.pluck(finderView.list.children('li'), 'outerText')).toEqual ['sample.js'] + expect(_.pluck(finderView.list.find('li > div.file'), 'outerText')).toEqual ['sample.js'] describe "when a path selection is confirmed", -> [editor1, editor2] = [] diff --git a/src/packages/fuzzy-finder/stylesheets/fuzzy-finder.less b/src/packages/fuzzy-finder/stylesheets/fuzzy-finder.less index 3c8a4f3f1..8ab705937 100644 --- a/src/packages/fuzzy-finder/stylesheets/fuzzy-finder.less +++ b/src/packages/fuzzy-finder/stylesheets/fuzzy-finder.less @@ -1,8 +1,21 @@ -.fuzzy-finder .directory { +.fuzzy-finder { + + &.select-list li { + padding: 5px 10px 5px 10px; + } + + .path, .file { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + } +} + +.fuzzy-finder .path { color: rgba(0, 0, 0, 0.5); - word-break: break-word; - margin-left: 5px; - line-height: 150%; + margin-top: -2px; + margin-left: 26px; + font-size: .9em; } .fuzzy-finder .file:before { diff --git a/static/overlay.less b/static/overlay.less index 60bd0a88f..60e57628d 100644 --- a/static/overlay.less +++ b/static/overlay.less @@ -40,4 +40,4 @@ border-bottom: none; border-bottom-left-radius: 0; border-bottom-right-radius: 0; -} \ No newline at end of file +} diff --git a/themes/atom-dark-ui/select-list.less b/themes/atom-dark-ui/select-list.less index 0413d192a..4af644466 100644 --- a/themes/atom-dark-ui/select-list.less +++ b/themes/atom-dark-ui/select-list.less @@ -36,7 +36,7 @@ } .right, - .directory { + .path { color: #777; } @@ -45,7 +45,7 @@ } .selected .right, - .selected .directory { + .selected .path { color: #ccc; } From 37b298fcba1c05b07b34eeb916870aad2fda17eb Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 1 May 2013 16:51:39 -0700 Subject: [PATCH 25/91] Lessify the base fuzzy finder stylehsheet --- .../stylesheets/fuzzy-finder.less | 135 +++++++++--------- 1 file changed, 69 insertions(+), 66 deletions(-) diff --git a/src/packages/fuzzy-finder/stylesheets/fuzzy-finder.less b/src/packages/fuzzy-finder/stylesheets/fuzzy-finder.less index 8ab705937..4f55a629d 100644 --- a/src/packages/fuzzy-finder/stylesheets/fuzzy-finder.less +++ b/src/packages/fuzzy-finder/stylesheets/fuzzy-finder.less @@ -4,74 +4,77 @@ padding: 5px 10px 5px 10px; } - .path, .file { + .path { text-overflow: ellipsis; white-space: nowrap; overflow: hidden; + color: rgba(0, 0, 0, 0.5); + margin-top: -2px; + margin-left: 26px; + font-size: .9em; + } + + .status { + font-family: 'Octicons Regular'; + font-size: 16px; + width: 16px; + height: 16px; + margin-left: 5px; + -webkit-font-smoothing: antialiased; + color: #9d9d9d; + float: right; + + &new:before { + position: relative; + top: 3px; + content: "\f06b"; + } + + &:before { + position: relative; + top: 3px; + content: "\f06d"; + } + } + + .file { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + + &:before { + font-family: 'Octicons Regular'; + font-size: 16px; + width: 16px; + height: 16px; + margin-right: 5px; + margin-left: 5px; + -webkit-font-smoothing: antialiased; + color: #9d9d9d; + } + + &.text-name:before { + content: "\f011"; + } + + &.image-name:before { + content: "\f012"; + } + + &.compressed-name:before { + content: "\f013"; + } + + &.pdf-name:before { + content: "\f014"; + } + + &.readme-name:before { + content: "\f007"; + } + + &.binary-name:before { + content: "\f094"; + } } } - -.fuzzy-finder .path { - color: rgba(0, 0, 0, 0.5); - margin-top: -2px; - margin-left: 26px; - font-size: .9em; -} - -.fuzzy-finder .file:before { - font-family: 'Octicons Regular'; - font-size: 16px; - width: 16px; - height: 16px; - margin-right: 5px; - margin-left: 5px; - -webkit-font-smoothing: antialiased; - color: #9d9d9d; -} - -.fuzzy-finder .status { - font-family: 'Octicons Regular'; - font-size: 16px; - width: 16px; - height: 16px; - margin-left: 5px; - -webkit-font-smoothing: antialiased; - color: #9d9d9d; - float: right; -} - -.fuzzy-finder .status.new:before { - position: relative; - top: 3px; - content: "\f06b"; -} - -.fuzzy-finder .status.modified:before { - position: relative; - top: 3px; - content: "\f06d"; -} - -.fuzzy-finder .file.text-name:before { - content: "\f011"; -} - -.fuzzy-finder .file.image-name:before { - content: "\f012"; -} - -.fuzzy-finder .file.compressed-name:before { - content: "\f013"; -} - -.fuzzy-finder .file.pdf-name:before { - content: "\f014"; -} - -.fuzzy-finder .file.readme-name:before { - content: "\f007"; -} - -.fuzzy-finder .file.binary-name:before { - content: "\f094"; -} From 71e73750e21c9d5e41b82a90a9a5cffe98394d31 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 1 May 2013 16:52:13 -0700 Subject: [PATCH 26/91] Kill negative top margin on path --- src/packages/fuzzy-finder/stylesheets/fuzzy-finder.less | 1 - 1 file changed, 1 deletion(-) diff --git a/src/packages/fuzzy-finder/stylesheets/fuzzy-finder.less b/src/packages/fuzzy-finder/stylesheets/fuzzy-finder.less index 4f55a629d..c72afa89a 100644 --- a/src/packages/fuzzy-finder/stylesheets/fuzzy-finder.less +++ b/src/packages/fuzzy-finder/stylesheets/fuzzy-finder.less @@ -9,7 +9,6 @@ white-space: nowrap; overflow: hidden; color: rgba(0, 0, 0, 0.5); - margin-top: -2px; margin-left: 26px; font-size: .9em; } From 64fea21a3897061b104704b3183da6c4dadfba6d Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Tue, 23 Apr 2013 11:08:24 -0700 Subject: [PATCH 27/91] Bump biscotto --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 379f1eb11..807e0ef60 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ }, "devDependencies" : { - "biscotto" : "0.0.9" + "biscotto" : "0.0.10" }, "private": true, From 2357f46761a0a8300311d58c88457326f958f957 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Tue, 23 Apr 2013 11:16:56 -0700 Subject: [PATCH 28/91] Fix block comments in src --- src/app/atom-package.coffee | 4 +-- src/app/binding-set.coffee | 4 +-- src/app/buffer-change-operation.coffee | 4 +-- src/app/buffer-marker.coffee | 12 ++----- src/app/config.coffee | 4 +-- src/app/cursor-view.coffee | 2 +- src/app/cursor.coffee | 10 ++---- src/app/directory.coffee | 4 +-- src/app/display-buffer-marker.coffee | 12 ++----- src/app/display-buffer.coffee | 20 +++-------- src/app/edit-session.coffee | 24 ++++--------- src/app/editor.coffee | 36 +++++-------------- src/app/file.coffee | 4 +-- src/app/fold.coffee | 4 +-- src/app/git.coffee | 12 ++----- src/app/gutter.coffee | 12 ++----- src/app/image-edit-session.coffee | 4 +-- src/app/image-view.coffee | 4 +-- src/app/language-mode.coffee | 4 +-- src/app/null-grammar.coffee | 4 +-- src/app/package.coffee | 4 +-- src/app/pane-container.coffee | 8 ++--- src/app/pane-row.coffee | 4 +-- src/app/pane.coffee | 8 ++--- src/app/point.coffee | 4 +-- src/app/project.coffee | 16 +++------ src/app/root-view.coffee | 8 ++--- src/app/screen-line.coffee | 4 +-- src/app/select-list.coffee | 4 +-- src/app/selection.coffee | 8 ++--- src/app/syntax.coffee | 4 +-- src/app/text-buffer.coffee | 12 ++----- src/app/text-mate-grammar.coffee | 8 ++--- src/app/text-mate-package.coffee | 4 +-- .../text-mate-scope-selector-matchers.coffee | 4 +-- src/app/text-mate-theme.coffee | 4 +-- src/app/theme.coffee | 4 +-- src/app/tokenized-buffer.coffee | 4 +-- src/app/window.coffee | 4 +-- 39 files changed, 76 insertions(+), 224 deletions(-) diff --git a/src/app/atom-package.coffee b/src/app/atom-package.coffee index 10fa6b874..698a7c988 100644 --- a/src/app/atom-package.coffee +++ b/src/app/atom-package.coffee @@ -6,9 +6,7 @@ $ = require 'jquery' CSON = require 'cson' -### -# Internal: Loads and resolves packages. # -### +### Internal: Loads and resolves packages. ### module.exports = class AtomPackage extends Package diff --git a/src/app/binding-set.coffee b/src/app/binding-set.coffee index a13c564c1..547de99c7 100644 --- a/src/app/binding-set.coffee +++ b/src/app/binding-set.coffee @@ -5,9 +5,7 @@ fsUtils = require 'fs-utils' Specificity = require 'specificity' PEG = require 'pegjs' -### -# Internal # -### +### Internal ### module.exports = class BindingSet diff --git a/src/app/buffer-change-operation.coffee b/src/app/buffer-change-operation.coffee index b39bd3d4d..8c146174b 100644 --- a/src/app/buffer-change-operation.coffee +++ b/src/app/buffer-change-operation.coffee @@ -1,9 +1,7 @@ Range = require 'range' _ = require 'underscore' -### -# Internal # -### +### Internal ### module.exports = class BufferChangeOperation diff --git a/src/app/buffer-marker.coffee b/src/app/buffer-marker.coffee index 4a6e35af1..a922910af 100644 --- a/src/app/buffer-marker.coffee +++ b/src/app/buffer-marker.coffee @@ -10,16 +10,12 @@ class BufferMarker suppressObserverNotification: false invalidationStrategy: null - ### - # Internal # - ### + ### Internal ### constructor: ({@id, @buffer, range, @invalidationStrategy, noTail, reverse}) -> @invalidationStrategy ?= 'contains' @setRange(range, {noTail, reverse}) - ### - # Public # - ### + ### Public ### # Public: Sets the marker's range, potentialy modifying both its head and tail. # @@ -148,9 +144,7 @@ class BufferMarker unobserve: (callback) -> @off 'changed', callback - ### - # Internal # - ### + ### Internal ### tryToInvalidate: (changedRange) -> betweenStartAndEnd = @getRange().containsRange(changedRange, exclusive: false) diff --git a/src/app/config.coffee b/src/app/config.coffee index 8209307cf..02d719a9a 100644 --- a/src/app/config.coffee +++ b/src/app/config.coffee @@ -29,9 +29,7 @@ class Config settings: null configFileHasErrors: null - ### - # Internal # - ### + ### Internal ### constructor: -> @defaultSettings = diff --git a/src/app/cursor-view.coffee b/src/app/cursor-view.coffee index 6e4c96686..64b9851dd 100644 --- a/src/app/cursor-view.coffee +++ b/src/app/cursor-view.coffee @@ -3,7 +3,7 @@ Point = require 'point' Range = require 'range' _ = require 'underscore' -# Internal: +### Internal ### module.exports = class CursorView extends View @content: -> diff --git a/src/app/cursor.coffee b/src/app/cursor.coffee index c06b85b5b..a657b6bcb 100644 --- a/src/app/cursor.coffee +++ b/src/app/cursor.coffee @@ -14,10 +14,8 @@ class Cursor visible: true needsAutoscroll: null - ### - # Internal # - ### - + ### Internal ### + constructor: ({@editSession, @marker}) -> @updateVisibility() @editSession.observeMarker @marker, (e) => @@ -53,9 +51,7 @@ class Cursor unless fn() @trigger 'autoscrolled' if @needsAutoscroll - ### - # Public # - ### + ### Public ### # Public: Moves a cursor to a given screen position. # diff --git a/src/app/directory.coffee b/src/app/directory.coffee index 8ec45eba3..d274ae6a9 100644 --- a/src/app/directory.coffee +++ b/src/app/directory.coffee @@ -51,9 +51,7 @@ class Directory directories.concat(files) - ### - # Internal # - ### + ### Internal ### afterSubscribe: -> @subscribeToNativeChangeEvents() if @subscriptionCount() == 1 diff --git a/src/app/display-buffer-marker.coffee b/src/app/display-buffer-marker.coffee index d88f67bdc..8a6e02057 100644 --- a/src/app/display-buffer-marker.coffee +++ b/src/app/display-buffer-marker.coffee @@ -9,16 +9,12 @@ class DisplayBufferMarker tailScreenPosition: null valid: true - ### - # Internal # - ### + ### Internal ### constructor: ({@id, @displayBuffer}) -> @buffer = @displayBuffer.buffer - ### - # Public # - ### + ### Public ### # Public: Gets the screen range of the display marker. # @@ -127,9 +123,7 @@ class DisplayBufferMarker @off 'changed', callback @unobserveBufferMarkerIfNeeded() - ### - # Internal # - ### + ### Internal ### observeBufferMarkerIfNeeded: -> return if @subscriptionCount() diff --git a/src/app/display-buffer.coffee b/src/app/display-buffer.coffee index ea00368e5..983ba1b57 100644 --- a/src/app/display-buffer.coffee +++ b/src/app/display-buffer.coffee @@ -18,9 +18,7 @@ class DisplayBuffer foldsById: null markers: null - ### - # Internal # - ### + ### Internal ### constructor: (@buffer, options={}) -> @id = @constructor.idCounter++ @@ -45,9 +43,7 @@ class DisplayBuffer @trigger 'changed', eventProperties @resumeMarkerObservers() - ### - # Public # - ### + ### Public ### setVisible: (visible) -> @tokenizedBuffer.setVisible(visible) @@ -332,9 +328,7 @@ class DisplayBuffer clipScreenPosition: (position, options) -> @lineMap.clipScreenPosition(position, options) - ### - # Internal # - ### + ### Internal ### registerFold: (fold) -> @activeFolds[fold.startRow] ?= [] @@ -433,9 +427,7 @@ class DisplayBuffer lineFragments - ### - # Public # - ### + ### Public ### # Public: Given a line, finds the point where it would wrap. # @@ -695,9 +687,7 @@ class DisplayBuffer observeMarker: (id, callback) -> @getMarker(id).observe(callback) - ### - # Internal # - ### + ### Internal ### pauseMarkerObservers: -> marker.pauseEvents() for marker in @getMarkers() diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index 68fb53a8d..98bf361f7 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -15,9 +15,7 @@ module.exports = class EditSession registerDeserializer(this) - ### - # Internal # - ### + ### Internal ### @version: 1 @@ -93,9 +91,7 @@ class EditSession copy: -> EditSession.deserialize(@serialize(), @project) - ### - # Public # - ### + ### Public ### # Public: Retrieves the filename of the open file. # @@ -614,9 +610,7 @@ class EditSession redo: -> @buffer.redo(this) - ### - # Internal # - ### + ### Internal ### transact: (fn) -> isNewTransaction = @buffer.transact() @@ -639,9 +633,7 @@ class EditSession abort: -> @buffer.abort() - ### - # Public # - ### + ### Public ### # Public: Folds all the rows. foldAll: -> @@ -897,9 +889,7 @@ class EditSession @foldCurrentRow() if cursorRowFolded - ### - # Internal # - ### + ### Internal ### mutateSelectedText: (fn) -> @transact => fn(selection) for selection in @getSelections() @@ -918,9 +908,7 @@ class EditSession pushOperation: (operation) -> @buffer.pushOperation(operation, this) - ### - # Public # - ### + ### Public ### # Public: Constructs a new marker at the given screen range. # diff --git a/src/app/editor.coffee b/src/app/editor.coffee index ed720127f..ff4621e24 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -27,9 +27,7 @@ class Editor extends View @nextEditorId: 1 - ### - # Internal # - ### + ### Internal ### @content: (params) -> attributes = { class: @classes(params), tabindex: -1 } @@ -192,9 +190,7 @@ class Editor extends View do (name, method) => @command name, (e) => method.call(this, e); false - ### - # Public # - ### + ### Public ### # Public: Retrieves a single cursor # @@ -796,9 +792,7 @@ class Editor extends View # iterator - A {Function} that's called on each match backwardsScanInBufferRange: (args...) -> @getBuffer().backwardsScanInRange(args...) - ### - # Internal # - ### + ### Internal ### configure: -> @observeConfig 'editor.showLineNumbers', (showLineNumbers) => @gutter.setShowLineNumbers(showLineNumbers) @@ -986,9 +980,7 @@ class Editor extends View else @scrollTop() + @scrollView.height() - ### - # Public # - ### + ### Public ### # Public: Retrieves the {EditSession}'s buffer. # @@ -1253,9 +1245,7 @@ class Editor extends View appendToLinesView: (view) -> @overlayer.append(view) - ### - # Internal # - ### + ### Internal ### calculateDimensions: -> fragment = $('') @@ -1497,9 +1487,7 @@ class Editor extends View @renderedLines.css('padding-bottom', paddingBottom) @gutter.lineNumbers.css('padding-bottom', paddingBottom) - ### - # Public # - ### + ### Public ### # Public: Retrieves the number of the row that is visible and currently at the top of the editor. # @@ -1522,9 +1510,7 @@ class Editor extends View isScreenRowVisible: (row) -> @getFirstVisibleScreenRow() <= row <= @getLastVisibleScreenRow() - ### - # Internal # - ### + ### Internal ### handleScreenLinesChange: (change) -> @pendingChanges.push(change) @@ -1632,9 +1618,7 @@ class Editor extends View toggleLineCommentsInSelection: -> @activeEditSession.toggleLineCommentsInSelection() - ### - # Public # - ### + ### Public ### # Public: Converts a buffer position to a pixel position. # @@ -1767,9 +1751,7 @@ class Editor extends View path = @getPath() pasteboard.write(path) if path? - ### - # Internal # - ### + ### Internal ### transact: (fn) -> @activeEditSession.transact(fn) commit: -> @activeEditSession.commit() diff --git a/src/app/file.coffee b/src/app/file.coffee index 4f1782ff2..9476df2b1 100644 --- a/src/app/file.coffee +++ b/src/app/file.coffee @@ -68,9 +68,7 @@ class File exists: -> fsUtils.exists(@getPath()) - ### - # Internal # - ### + ### Internal ### afterSubscribe: -> @subscribeToNativeChangeEvents() if @exists() and @subscriptionCount() == 1 diff --git a/src/app/fold.coffee b/src/app/fold.coffee index e53824924..ea6b2d5d9 100644 --- a/src/app/fold.coffee +++ b/src/app/fold.coffee @@ -13,9 +13,7 @@ class Fold startRow: null endRow: null - ### - # Internal # - ### + ### Internal ### constructor: (@displayBuffer, @startRow, @endRow) -> @id = @constructor.idCounter++ diff --git a/src/app/git.coffee b/src/app/git.coffee index b83aa802f..b0fd57336 100644 --- a/src/app/git.coffee +++ b/src/app/git.coffee @@ -29,9 +29,7 @@ class Git upstream: null statusTask: null - ### - # Internal # - ### + ### Internal ### # Internal: Creates a new `Git` object. # @@ -72,9 +70,7 @@ class Git @unsubscribe() - ### - # Public # - ### + ### Public ### # Public: Retrieves the git repository. # @@ -245,9 +241,7 @@ class Git getLineDiffs: (path, text) -> @getRepo().getLineDiffs(@relativize(path), text) - ### - # Internal # - ### + ### Internal ### refreshStatus: -> if @statusTask? diff --git a/src/app/gutter.coffee b/src/app/gutter.coffee index ff937d720..484c28c43 100644 --- a/src/app/gutter.coffee +++ b/src/app/gutter.coffee @@ -9,9 +9,7 @@ _ = require 'underscore' module.exports = class Gutter extends View - ### - # Internal # - ### + ### Internal ### @content: -> @div class: 'gutter', => @@ -50,9 +48,7 @@ class Gutter extends View $(document).on "mousemove.gutter-#{@getEditor().id}", moveHandler $(document).one "mouseup.gutter-#{@getEditor().id}", => $(document).off 'mousemove', moveHandler - ### - # Public # - ### + ### Public ### # Public: Retrieves the containing {Editor}. # @@ -66,9 +62,7 @@ class Gutter extends View setShowLineNumbers: (showLineNumbers) -> if showLineNumbers then @lineNumbers.show() else @lineNumbers.hide() - ### - # Internal # - ### + ### Internal ### updateLineNumbers: (changes, renderFrom, renderTo) -> if renderFrom < @firstScreenRow or renderTo > @lastScreenRow diff --git a/src/app/image-edit-session.coffee b/src/app/image-edit-session.coffee index 6a8ecb856..84ab5470c 100644 --- a/src/app/image-edit-session.coffee +++ b/src/app/image-edit-session.coffee @@ -21,9 +21,7 @@ class ImageEditSession '.png' ], fsUtils.extension(path), true) >= 0 - ### - # Internal # - ### + ### Internal ### @deserialize: (state) -> if fsUtils.exists(state.path) diff --git a/src/app/image-view.coffee b/src/app/image-view.coffee index 2a339576b..2cb79d846 100644 --- a/src/app/image-view.coffee +++ b/src/app/image-view.coffee @@ -86,9 +86,7 @@ class ImageView extends ScrollView @image.height(@originalHeight) @centerImage() - ### - # Internal # - ### + ### Internal ### adjustSize: (factor) -> return unless @loaded and @isVisible() diff --git a/src/app/language-mode.coffee b/src/app/language-mode.coffee index 36afb5905..3e0287706 100644 --- a/src/app/language-mode.coffee +++ b/src/app/language-mode.coffee @@ -5,9 +5,7 @@ require 'underscore-extensions' EventEmitter = require 'event-emitter' Subscriber = require 'subscriber' -### -# Internal # -### +### Internal ### module.exports = class LanguageMode diff --git a/src/app/null-grammar.coffee b/src/app/null-grammar.coffee index 8d7079d55..54bdfe95f 100644 --- a/src/app/null-grammar.coffee +++ b/src/app/null-grammar.coffee @@ -2,9 +2,7 @@ Token = require 'token' EventEmitter = require 'event-emitter' _ = require 'underscore' -### -# Internal # -### +### Internal ### module.exports = class NullGrammar name: 'Null Grammar' diff --git a/src/app/package.coffee b/src/app/package.coffee index d4b72a446..c5e0777f8 100644 --- a/src/app/package.coffee +++ b/src/app/package.coffee @@ -1,8 +1,6 @@ fsUtils = require 'fs-utils' -### -# Internal # -### +### Internal ### module.exports = class Package @build: (path) -> diff --git a/src/app/pane-container.coffee b/src/app/pane-container.coffee index 48e506877..461d76664 100644 --- a/src/app/pane-container.coffee +++ b/src/app/pane-container.coffee @@ -6,9 +6,7 @@ module.exports = class PaneContainer extends View registerDeserializer(this) - ### - # Internal # - ### + ### Internal ### @deserialize: ({root}) -> container = new PaneContainer @@ -26,9 +24,7 @@ class PaneContainer extends View deserializer: 'PaneContainer' root: @getRoot()?.serialize() - ### - # Public # - ### + ### Public ### focusNextPane: -> panes = @getPanes() diff --git a/src/app/pane-row.coffee b/src/app/pane-row.coffee index d1030cd68..f13e40570 100644 --- a/src/app/pane-row.coffee +++ b/src/app/pane-row.coffee @@ -2,9 +2,7 @@ $ = require 'jquery' _ = require 'underscore' PaneAxis = require 'pane-axis' -### -# Internal # -### +### Internal ### module.exports = class PaneRow extends PaneAxis diff --git a/src/app/pane.coffee b/src/app/pane.coffee index b224ab624..e85cadd17 100644 --- a/src/app/pane.coffee +++ b/src/app/pane.coffee @@ -7,9 +7,7 @@ PaneColumn = require 'pane-column' module.exports = class Pane extends View - ### - # Internal # - ### + ### Internal ### @content: (wrappedView) -> @div class: 'pane', => @@ -65,9 +63,7 @@ class Pane extends View @attached = true @trigger 'pane:attached', [this] - ### - # Public # - ### + ### Public ### makeActive: -> for pane in @getContainer().getPanes() when pane isnt this diff --git a/src/app/point.coffee b/src/app/point.coffee index b49a3520c..09d8b5f2c 100644 --- a/src/app/point.coffee +++ b/src/app/point.coffee @@ -172,9 +172,7 @@ class Point toArray: -> [@row, @column] - ### - # Internal # - ### + ### Internal ### inspect: -> "(#{@row}, #{@column})" diff --git a/src/app/project.coffee b/src/app/project.coffee index f47a8894e..38a96efcf 100644 --- a/src/app/project.coffee +++ b/src/app/project.coffee @@ -32,9 +32,7 @@ class Project @editSessions = [] @buffers = [] - ### - # Internal # - ### + ### Internal ### serialize: -> deserializer: 'Project' @@ -46,9 +44,7 @@ class Project destroy: -> editSession.destroy() for editSession in @getEditSessions() - ### - # Public # - ### + ### Public ### # Public: Retrieves the project path. # @@ -164,9 +160,7 @@ class Project getEditSessions: -> new Array(@editSessions...) - ### - # Internal # - ### + ### Internal ### buildEditSessionForBuffer: (buffer, editSessionOptions) -> options = _.extend(@defaultEditSessionOptions(), editSessionOptions) @@ -196,9 +190,7 @@ class Project else @on 'buffer-created', (buffer) -> callback(buffer) - ### - # Public # - ### + ### Public ### # Public: Removes an {EditSession} association from the project. # diff --git a/src/app/root-view.coffee b/src/app/root-view.coffee index 90407e2e2..cc00344aa 100644 --- a/src/app/root-view.coffee +++ b/src/app/root-view.coffee @@ -25,9 +25,7 @@ class RootView extends View disabledPackages: [] themes: ['atom-dark-ui', 'atom-dark-syntax'] - ### - # Internal: - ### + ### Internal ### @content: ({panes}={}) -> @div id: 'root-view', => @@ -98,9 +96,7 @@ class RootView extends View afterAttach: (onDom) -> @focus() if onDom - ### - # Public # - ### + ### Public ### # Public: Shows a dialog asking if the pane was _really_ meant to be closed. confirmClose: -> diff --git a/src/app/screen-line.coffee b/src/app/screen-line.coffee index 99a0424fb..75b58f36a 100644 --- a/src/app/screen-line.coffee +++ b/src/app/screen-line.coffee @@ -1,8 +1,6 @@ _ = require 'underscore' -### -# Internal # -### +### Internal ### module.exports = class ScreenLine diff --git a/src/app/select-list.coffee b/src/app/select-list.coffee index e82c07598..1813cb6c3 100644 --- a/src/app/select-list.coffee +++ b/src/app/select-list.coffee @@ -6,9 +6,7 @@ fuzzyFilter = require 'fuzzy-filter' module.exports = class SelectList extends View - ### - # Internal # - ### + ### Internal ### @content: -> @div class: @viewClass(), => diff --git a/src/app/selection.coffee b/src/app/selection.coffee index e7b69a2a3..1da79a049 100644 --- a/src/app/selection.coffee +++ b/src/app/selection.coffee @@ -13,9 +13,7 @@ class Selection wordwise: false needsAutoscroll: null - ### - # Internal # - ### + ### Internal ### constructor: ({@cursor, @marker, @editSession, @goalBufferRange}) -> @cursor.selection = this @@ -40,9 +38,7 @@ class Selection clearAutoscroll: -> @needsAutoscroll = null - ### - # Public # - ### + ### Public ### # Public: Identifies if the selection is highlighting anything. # diff --git a/src/app/syntax.coffee b/src/app/syntax.coffee index 4115c2797..a8859154e 100644 --- a/src/app/syntax.coffee +++ b/src/app/syntax.coffee @@ -6,9 +6,7 @@ fsUtils = require 'fs-utils' EventEmitter = require 'event-emitter' NullGrammar = require 'null-grammar' -### -# Internal # -### +### Internal ### module.exports = class Syntax diff --git a/src/app/text-buffer.coffee b/src/app/text-buffer.coffee index 2a20ea1cc..f50df8805 100644 --- a/src/app/text-buffer.coffee +++ b/src/app/text-buffer.coffee @@ -55,9 +55,7 @@ class Buffer @undoManager = new UndoManager(this) - ### - # Internal # - ### + ### Internal ### destroy: -> throw new Error("Destroying buffer twice with path '#{@getPath()}'") if @destroyed @@ -98,9 +96,7 @@ class Buffer @file.on "moved", => @trigger "path-changed", this - ### - # Public # - ### + ### Public ### # Public: Identifies if the buffer belongs to multiple editors. # @@ -726,9 +722,7 @@ class Buffer @file? && @file.exists() - ### - # Internal # - ### + ### Internal ### scheduleModifiedEvents: -> clearTimeout(@stoppedChangingTimeout) if @stoppedChangingTimeout diff --git a/src/app/text-mate-grammar.coffee b/src/app/text-mate-grammar.coffee index cf48323c0..d4a9016bd 100644 --- a/src/app/text-mate-grammar.coffee +++ b/src/app/text-mate-grammar.coffee @@ -8,9 +8,7 @@ EventEmitter = require 'event-emitter' pathSplitRegex = new RegExp("[#{nodePath.sep}.]") TextMateScopeSelector = require 'text-mate-scope-selector' -### -# Internal # -### +### Internal ### module.exports = class TextMateGrammar @@ -502,9 +500,7 @@ class Pattern tokens -### -# Internal # -### +### Internal ### shiftCapture = (captureIndices) -> [captureIndices.shift(), captureIndices.shift(), captureIndices.shift()] diff --git a/src/app/text-mate-package.coffee b/src/app/text-mate-package.coffee index 8700cbcc2..2f0efdda0 100644 --- a/src/app/text-mate-package.coffee +++ b/src/app/text-mate-package.coffee @@ -5,9 +5,7 @@ _ = require 'underscore' TextMateGrammar = require 'text-mate-grammar' async = require 'async' -### -# Internal # -### +### Internal ### module.exports = class TextMatePackage extends Package diff --git a/src/app/text-mate-scope-selector-matchers.coffee b/src/app/text-mate-scope-selector-matchers.coffee index 9cb852134..67b5310ab 100644 --- a/src/app/text-mate-scope-selector-matchers.coffee +++ b/src/app/text-mate-scope-selector-matchers.coffee @@ -1,6 +1,4 @@ -### -# Internal # -### +### Internal ### class SegmentMatcher constructor: (segment) -> diff --git a/src/app/text-mate-theme.coffee b/src/app/text-mate-theme.coffee index c032ec620..f8e72512d 100644 --- a/src/app/text-mate-theme.coffee +++ b/src/app/text-mate-theme.coffee @@ -3,9 +3,7 @@ fsUtils = require 'fs-utils' plist = require 'plist' Theme = require 'theme' -### -# Internal # -### +### Internal ### module.exports = class TextMateTheme extends Theme diff --git a/src/app/theme.coffee b/src/app/theme.coffee index d6ebdff6b..b7aea0119 100644 --- a/src/app/theme.coffee +++ b/src/app/theme.coffee @@ -1,8 +1,6 @@ fsUtils = require 'fs-utils' -### -# Internal # -### +### Internal ### module.exports = class Theme diff --git a/src/app/tokenized-buffer.coffee b/src/app/tokenized-buffer.coffee index 876e65a51..1d3656169 100644 --- a/src/app/tokenized-buffer.coffee +++ b/src/app/tokenized-buffer.coffee @@ -6,9 +6,7 @@ Token = require 'token' Range = require 'range' Point = require 'point' -### -# Internal # -### +### Internal ### module.exports = class TokenizedBuffer diff --git a/src/app/window.coffee b/src/app/window.coffee index a70829762..daf1cc6e1 100644 --- a/src/app/window.coffee +++ b/src/app/window.coffee @@ -10,9 +10,7 @@ require 'space-pen-extensions' deserializers = {} deferredDeserializers = {} -### -# Internal # -### +### Internal ### # This method is called in any window needing a general environment, including specs window.setUpEnvironment = -> From 52139dc24edad8b3fca12dab764b06b8fc206bcc Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Tue, 30 Apr 2013 12:52:29 -0700 Subject: [PATCH 29/91] Bump biscotto --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 807e0ef60..85dec3096 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ }, "devDependencies" : { - "biscotto" : "0.0.10" + "biscotto" : "0.0.11" }, "private": true, From 685ec5d8d7021f15857d5181582149b16fb032c2 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Tue, 30 Apr 2013 12:53:18 -0700 Subject: [PATCH 30/91] Update buffer-marker docs --- src/app/buffer-marker.coffee | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/app/buffer-marker.coffee b/src/app/buffer-marker.coffee index a922910af..818f62fa3 100644 --- a/src/app/buffer-marker.coffee +++ b/src/app/buffer-marker.coffee @@ -17,7 +17,7 @@ class BufferMarker ### Public ### - # Public: Sets the marker's range, potentialy modifying both its head and tail. + # Sets the marker's range, potentialy modifying both its head and tail. # # range - The new {Range} the marker should cover # options - A hash of options with the following keys: @@ -33,7 +33,7 @@ class BufferMarker @setTailPosition(range.start) unless options.noTail @setHeadPosition(range.end) - # Public: Identifies if the ending position of a marker is greater than the starting position. + # Identifies if the ending position of a marker is greater than the starting position. # # This can happen when, for example, you highlight text "up" in a {Buffer}. # @@ -41,13 +41,13 @@ class BufferMarker isReversed: -> @tailPosition? and @headPosition.isLessThan(@tailPosition) - # Public: Identifies if the marker's head position is equal to its tail. + # Identifies if the marker's head position is equal to its tail. # # Returns a {Boolean}. isRangeEmpty: -> @getHeadPosition().isEqual(@getTailPosition()) - # Public: Retrieves the {Range} between a marker's head and its tail. + # Retrieves the {Range} between a marker's head and its tail. # # Returns a {Range}. getRange: -> @@ -56,17 +56,17 @@ class BufferMarker else new Range(@headPosition, @headPosition) - # Public: Retrieves the position of the marker's head. + # Retrieves the position of the marker's head. # # Returns a {Point}. getHeadPosition: -> @headPosition - # Public: Retrieves the position of the marker's tail. + # Retrieves the position of the marker's tail. # # Returns a {Point}. getTailPosition: -> @tailPosition ? @getHeadPosition() - # Public: Sets the position of the marker's head. + # Sets the position of the marker's head. # # newHeadPosition - The new {Point} to place the head # options - A hash with the following keys: @@ -84,7 +84,7 @@ class BufferMarker @notifyObservers({oldHeadPosition, newHeadPosition, bufferChanged}) @headPosition - # Public: Sets the position of the marker's tail. + # Sets the position of the marker's tail. # # newHeadPosition - The new {Point} to place the tail # options - A hash with the following keys: @@ -102,19 +102,19 @@ class BufferMarker @notifyObservers({oldTailPosition, newTailPosition, bufferChanged}) @tailPosition - # Public: Retrieves the starting position of the marker. + # Retrieves the starting position of the marker. # # Returns a {Point}. getStartPosition: -> @getRange().start - # Public: Retrieves the ending position of the marker. + # Retrieves the ending position of the marker. # # Returns a {Point}. getEndPosition: -> @getRange().end - # Public: Sets the marker's tail to the same position as the marker's head. + # Sets the marker's tail to the same position as the marker's head. # # This only works if there isn't already a tail position. # @@ -122,25 +122,25 @@ class BufferMarker placeTail: -> @setTailPosition(@getHeadPosition()) unless @tailPosition - # Public: Removes the tail from the marker. + # Removes the tail from the marker. clearTail: -> oldTailPosition = @getTailPosition() @tailPosition = null newTailPosition = @getTailPosition() @notifyObservers({oldTailPosition, newTailPosition, bufferChanged: false}) - # Public: Identifies if a {Point} is within the marker. + # Identifies if a {Point} is within the marker. # # Returns a {Boolean}. containsPoint: (point) -> @getRange().containsPoint(point) - # Public: Sets a callback to be fired whenever a marker is changed. + # Sets a callback to be fired whenever a marker is changed. observe: (callback) -> @on 'changed', callback cancel: => @unobserve(callback) - # Public: Removes the fired callback whenever a marker changes. + # Removes the fired callback whenever a marker changes. unobserve: (callback) -> @off 'changed', callback From 66a80ade30b1f33c398c7b7df6e5f3e85b7c1511 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Tue, 30 Apr 2013 13:08:58 -0700 Subject: [PATCH 31/91] Update config --- src/app/config.coffee | 49 +++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/src/app/config.coffee b/src/app/config.coffee index 02d719a9a..8d4d38afc 100644 --- a/src/app/config.coffee +++ b/src/app/config.coffee @@ -88,7 +88,19 @@ class Config @watchSubscription?.close() @watchSubscription = null - # Public: Retrieves the setting for the given key. + setDefaults: (keyPath, defaults) -> + keys = keyPath.split('.') + hash = @defaultSettings + for key in keys + hash[key] ?= {} + hash = hash[key] + + _.extend hash, defaults + @update() + + ### Public ### + + # Retrieves the setting for the given key. # # keyPath - The {String} name of the key to retrieve # @@ -98,25 +110,28 @@ class Config _.valueForKeyPath(@settings, keyPath) ? _.valueForKeyPath(@defaultSettings, keyPath) - # Public: Retrieves the setting for the given key as an integer number. + + # Retrieves the setting for the given key as an integer. # # keyPath - The {String} name of the key to retrieve + # # Returns the value from Atom's default settings, the user's configuration file, # or `NaN` if the key doesn't exist in either. getInt: (keyPath, defaultValueWhenFalsy) -> parseInt(@get(keyPath)) - # Public: Retrieves the setting for the given key as a positive integer number. + # Retrieves the setting for the given key as a positive integer. # # keyPath - The {String} name of the key to retrieve # defaultValue - The integer {Number} to fall back to if the value isn't # positive + # # Returns the value from Atom's default settings, the user's configuration file, # or `defaultValue` if the key value isn't greater than zero. getPositiveInt: (keyPath, defaultValue) -> Math.max(@getInt(keyPath), 0) or defaultValue - - # Public: Sets the value for a configuration setting. + + # Sets the value for a configuration setting. # # This value is stored in Atom's internal configuration file. # @@ -129,17 +144,7 @@ class Config @update() value - setDefaults: (keyPath, defaults) -> - keys = keyPath.split('.') - hash = @defaultSettings - for key in keys - hash[key] ?= {} - hash = hash[key] - - _.extend hash, defaults - @update() - - # Public: Establishes an event listener for a given key. + # Establishes an event listener for a given key. # # Whenever the value of the key is changed, a callback is fired. # @@ -160,6 +165,18 @@ class Config callback(value) subscription + ### Internal ### + + setDefaults: (keyPath, defaults) -> + keys = keyPath.split('.') + hash = @defaultSettings + for key in keys + hash[key] ?= {} + hash = hash[key] + + _.extend hash, defaults + @update() + update: -> return if @configFileHasErrors @save() From 26564ee020a2092c8e70cf37576b219915246030 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Tue, 30 Apr 2013 13:21:57 -0700 Subject: [PATCH 32/91] Update cursor --- src/app/cursor.coffee | 84 ++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/src/app/cursor.coffee b/src/app/cursor.coffee index a657b6bcb..c79ca05c2 100644 --- a/src/app/cursor.coffee +++ b/src/app/cursor.coffee @@ -53,7 +53,7 @@ class Cursor ### Public ### - # Public: Moves a cursor to a given screen position. + # Moves a cursor to a given screen position. # # screenPosition - An {Array} of two numbers: the screen row, and the screen column. # options - An object with the following keys: @@ -63,13 +63,13 @@ class Cursor @changePosition options, => @editSession.setMarkerHeadScreenPosition(@marker, screenPosition, options) - # Public: Gets the screen position of the cursor. + # Gets the screen position of the cursor. # # Returns an {Array} of two numbers: the screen row, and the screen column. getScreenPosition: -> @editSession.getMarkerHeadScreenPosition(@marker) - # Public: Moves a cursor to a given buffer position. + # Moves a cursor to a given buffer position. # # bufferPosition - An {Array} of two numbers: the buffer row, and the buffer column. # options - An object with the following keys: @@ -79,17 +79,17 @@ class Cursor @changePosition options, => @editSession.setMarkerHeadBufferPosition(@marker, bufferPosition, options) - # Public: Gets the current buffer position. + # Gets the current buffer position. # # Returns an {Array} of two numbers: the buffer row, and the buffer column. getBufferPosition: -> @editSession.getMarkerHeadBufferPosition(@marker) - # Public: If the marker range is empty, the cursor is marked as being visible. + # If the marker range is empty, the cursor is marked as being visible. updateVisibility: -> @setVisible(@editSession.isMarkerRangeEmpty(@marker)) - # Public: Sets the visibility of the cursor. + # Sets the visibility of the cursor. # # visible - A {Boolean} indicating whether the cursor should be visible setVisible: (visible) -> @@ -98,25 +98,27 @@ class Cursor @needsAutoscroll ?= true if @visible and @isLastCursor() @trigger 'visibility-changed', @visible - # Public: Retrieves the visibility of the cursor. + # Retrieves the visibility of the cursor. # # Returns a {Boolean}. isVisible: -> @visible - # Public: Identifies what the cursor considers a "word" RegExp. + # Identifies what the cursor considers a "word" RegExp. # # Returns a {RegExp}. wordRegExp: -> nonWordCharacters = config.get("editor.nonWordCharacters") new RegExp("^[\t ]*$|[^\\s#{_.escapeRegExp(nonWordCharacters)}]+|[#{_.escapeRegExp(nonWordCharacters)}]+", "g") - # Public: Identifies if this cursor is the last in the {EditSession}. + # Identifies if this cursor is the last in the {EditSession}. + # + # "Last" is defined as the most recently added cursor. # # Returns a {Boolean}. isLastCursor: -> this == @editSession.getCursor() - # Public: Identifies if the cursor is surrounded by whitespace. + # Identifies if the cursor is surrounded by whitespace. # # "Surrounded" here means that all characters before and after the cursor is whitespace. # @@ -126,84 +128,84 @@ class Cursor range = [[row, Math.min(0, column - 1)], [row, Math.max(0, column + 1)]] /^\s+$/.test @editSession.getTextInBufferRange(range) - # Public: Removes the setting for auto-scroll. + # Removes the setting for auto-scroll. clearAutoscroll: -> @needsAutoscroll = null - # Public: Deselects whatever the cursor is selecting. + # Deselects whatever the cursor is selecting. clearSelection: -> if @selection @selection.goalBufferRange = null @selection.clear() unless @selection.retainSelection - # Public: Retrieves the cursor's screen row. + # Retrieves the cursor's screen row. # # Returns a {Number}. getScreenRow: -> @getScreenPosition().row - # Public: Retrieves the cursor's screen column. + # Retrieves the cursor's screen column. # # Returns a {Number}. getScreenColumn: -> @getScreenPosition().column - # Public: Retrieves the cursor's buffer row. + # Retrieves the cursor's buffer row. # # Returns a {Number}. getBufferRow: -> @getBufferPosition().row - # Public: Retrieves the cursor's buffer column. + # Retrieves the cursor's buffer column. # # Returns a {Number}. getBufferColumn: -> @getBufferPosition().column - # Public: Retrieves the cursor's buffer row text. + # Retrieves the cursor's buffer row text. # # Returns a {String}. getCurrentBufferLine: -> @editSession.lineForBufferRow(@getBufferRow()) - # Public: Moves the cursor up one screen row. + # Moves the cursor up one screen row. moveUp: (rowCount = 1) -> { row, column } = @getScreenPosition() column = @goalColumn if @goalColumn? @setScreenPosition({row: row - rowCount, column: column}) @goalColumn = column - # Public: Moves the cursor down one screen row. + # Moves the cursor down one screen row. moveDown: (rowCount = 1) -> { row, column } = @getScreenPosition() column = @goalColumn if @goalColumn? @setScreenPosition({row: row + rowCount, column: column}) @goalColumn = column - # Public: Moves the cursor left one screen column. + # Moves the cursor left one screen column. moveLeft: -> { row, column } = @getScreenPosition() [row, column] = if column > 0 then [row, column - 1] else [row - 1, Infinity] @setScreenPosition({row, column}) - # Public: Moves the cursor right one screen column. + # Moves the cursor right one screen column. moveRight: -> { row, column } = @getScreenPosition() @setScreenPosition([row, column + 1], skipAtomicTokens: true, wrapBeyondNewlines: true, wrapAtSoftNewlines: true) - # Public: Moves the cursor to the top of the buffer. + # Moves the cursor to the top of the buffer. moveToTop: -> @setBufferPosition([0,0]) - # Public: Moves the cursor to the bottom of the buffer. + # Moves the cursor to the bottom of the buffer. moveToBottom: -> @setBufferPosition(@editSession.getEofBufferPosition()) - # Public: Moves the cursor to the beginning of the buffer line. + # Moves the cursor to the beginning of the buffer line. moveToBeginningOfLine: -> @setBufferPosition([@getBufferRow(), 0]) - # Public: Moves the cursor to the beginning of the first character in the line. + # Moves the cursor to the beginning of the first character in the line. moveToFirstCharacterOfLine: -> position = @getBufferPosition() scanRange = @getCurrentLineBufferRange() @@ -214,7 +216,7 @@ class Cursor newPosition = [position.row, 0] if newPosition.isEqual(position) @setBufferPosition(newPosition) - # Public: Moves the cursor to the beginning of the buffer line, skipping all whitespace. + # Moves the cursor to the beginning of the buffer line, skipping all whitespace. skipLeadingWhitespace: -> position = @getBufferPosition() scanRange = @getCurrentLineBufferRange() @@ -224,25 +226,25 @@ class Cursor @setBufferPosition(endOfLeadingWhitespace) if endOfLeadingWhitespace.isGreaterThan(position) - # Public: Moves the cursor to the end of the buffer line. + # Moves the cursor to the end of the buffer line. moveToEndOfLine: -> @setBufferPosition([@getBufferRow(), Infinity]) - # Public: Moves the cursor to the beginning of the word. + # Moves the cursor to the beginning of the word. moveToBeginningOfWord: -> @setBufferPosition(@getBeginningOfCurrentWordBufferPosition()) - # Public: Moves the cursor to the end of the word. + # Moves the cursor to the end of the word. moveToEndOfWord: -> if position = @getEndOfCurrentWordBufferPosition() @setBufferPosition(position) - # Public: Moves the cursor to the beginning of the next word. + # Moves the cursor to the beginning of the next word. moveToBeginningOfNextWord: -> if position = @getBeginningOfNextWordBufferPosition() @setBufferPosition(position) - # Public: Retrieves the buffer position of where the current word starts. + # Retrieves the buffer position of where the current word starts. # # options - A hash with one option: # :wordRegex - A {RegExp} indicating what constitutes a "word" (default: {wordRegExp}) @@ -263,7 +265,7 @@ class Cursor beginningOfWordPosition or currentBufferPosition - # Public: Retrieves the buffer position of where the current word ends. + # Retrieves the buffer position of where the current word ends. # # options - A hash with one option: # :wordRegex - A {RegExp} indicating what constitutes a "word" (default: {wordRegExp}) @@ -283,7 +285,7 @@ class Cursor endOfWordPosition ? currentBufferPosition - # Public: Retrieves the buffer position of where the next word starts. + # Retrieves the buffer position of where the next word starts. # # options - A hash with one option: # :wordRegex - A {RegExp} indicating what constitutes a "word" (default: {wordRegExp}) @@ -301,7 +303,7 @@ class Cursor beginningOfNextWordPosition or currentBufferPosition - # Public: Gets the word located under the cursor. + # Gets the word located under the cursor. # # options - An object with properties based on {.getBeginningOfCurrentWordBufferPosition}. # @@ -311,7 +313,7 @@ class Cursor endOptions = _.extend(_.clone(options), allowNext: false) new Range(@getBeginningOfCurrentWordBufferPosition(startOptions), @getEndOfCurrentWordBufferPosition(endOptions)) - # Public: Retrieves the range for the current line. + # Retrieves the range for the current line. # # options - A hash with the same keys as {EditSession.bufferRangeForBufferRow} # @@ -319,7 +321,7 @@ class Cursor getCurrentLineBufferRange: (options) -> @editSession.bufferRangeForBufferRow(@getBufferRow(), options) - # Public: Retrieves the range for the current paragraph. + # Retrieves the range for the current paragraph. # # A paragraph is defined as a block of text surrounded by empty lines. # @@ -341,19 +343,19 @@ class Cursor new Range([startRow, 0], [endRow, @editSession.lineLengthForBufferRow(endRow)]) - # Public: Retrieves the characters that constitute a word preceeding the current cursor position. + # Retrieves the characters that constitute a word preceeding the current cursor position. # # Returns a {String}. getCurrentWordPrefix: -> @editSession.getTextInBufferRange([@getBeginningOfCurrentWordBufferPosition(), @getBufferPosition()]) - # Public: Identifies if the cursor is at the start of a line. + # Identifies if the cursor is at the start of a line. # # Returns a {Boolean}. isAtBeginningOfLine: -> @getBufferPosition().column == 0 - # Public: Retrieves the indentation level of the current line. + # Retrieves the indentation level of the current line. # # Returns a {Number}. getIndentLevel: -> @@ -362,13 +364,13 @@ class Cursor else @getBufferColumn() - # Public: Identifies if the cursor is at the end of a line. + # Identifies if the cursor is at the end of a line. # # Returns a {Boolean}. isAtEndOfLine: -> @getBufferPosition().isEqual(@getCurrentLineBufferRange().end) - # Public: Retrieves the grammar's token scopes for the line. + # Retrieves the grammar's token scopes for the line. # # Returns an {Array} of {String}s. getScopes: -> From 16cef29925d02c110a939baea6cd16152e672435 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Tue, 30 Apr 2013 13:25:14 -0700 Subject: [PATCH 33/91] Update directory --- src/app/directory.coffee | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/app/directory.coffee b/src/app/directory.coffee index d274ae6a9..2cce2db80 100644 --- a/src/app/directory.coffee +++ b/src/app/directory.coffee @@ -12,24 +12,26 @@ module.exports = class Directory path: null - # Public: Creates a new directory. + ### Public ### + + # Creates a new directory. # # path - A {String} representing the file directory # symlink - A {Boolean} indicating if the path is a symlink (default: false) constructor: (@path, @symlink=false) -> - # Public: Retrieves the basename of the directory. + # Retrieves the basename of the directory. # # Returns a {String}. getBaseName: -> fsUtils.base(@path) - # Public: Retrieves the directory's path. + # Retrieves the directory's path. # # Returns a {String}. getPath: -> @path - # Public: Retrieves the file entries in the directory. + # Retrieves the file entries in the directory. # # This does follow symlinks. # From a8a28fea1890aad0a12ff269aa1fb581a96bcbca Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Tue, 30 Apr 2013 13:26:18 -0700 Subject: [PATCH 34/91] Update DisplayBufferMarker --- src/app/display-buffer-marker.coffee | 32 ++++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/app/display-buffer-marker.coffee b/src/app/display-buffer-marker.coffee index 8a6e02057..c81388b9d 100644 --- a/src/app/display-buffer-marker.coffee +++ b/src/app/display-buffer-marker.coffee @@ -16,39 +16,39 @@ class DisplayBufferMarker ### Public ### - # Public: Gets the screen range of the display marker. + # Gets the screen range of the display marker. # # Returns a {Range}. getScreenRange: -> @displayBuffer.screenRangeForBufferRange(@getBufferRange(), wrapAtSoftNewlines: true) - # Public: Modifies the screen range of the display marker. + # Modifies the screen range of the display marker. # # screenRange - The new {Range} to use # options - A hash of options matching those found in {BufferMarker.setRange} setScreenRange: (screenRange, options) -> @setBufferRange(@displayBuffer.bufferRangeForScreenRange(screenRange), options) - # Public: Gets the buffer range of the display marker. + # Gets the buffer range of the display marker. # # Returns a {Range}. getBufferRange: -> @buffer.getMarkerRange(@id) - # Public: Modifies the buffer range of the display marker. + # Modifies the buffer range of the display marker. # # screenRange - The new {Range} to use # options - A hash of options matching those found in {BufferMarker.setRange} setBufferRange: (bufferRange, options) -> @buffer.setMarkerRange(@id, bufferRange, options) - # Public: Retrieves the screen position of the marker's head. + # Retrieves the screen position of the marker's head. # # Returns a {Point}. getHeadScreenPosition: -> @headScreenPosition ?= @displayBuffer.screenPositionForBufferPosition(@getHeadBufferPosition(), wrapAtSoftNewlines: true) - # Public: Sets the screen position of the marker's head. + # Sets the screen position of the marker's head. # # screenRange - The new {Point} to use # options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition} @@ -56,26 +56,26 @@ class DisplayBufferMarker screenPosition = @displayBuffer.clipScreenPosition(screenPosition, options) @setHeadBufferPosition(@displayBuffer.bufferPositionForScreenPosition(screenPosition, options)) - # Public: Retrieves the buffer position of the marker's head. + # Retrieves the buffer position of the marker's head. # # Returns a {Point}. getHeadBufferPosition: -> @buffer.getMarkerHeadPosition(@id) - # Public: Sets the buffer position of the marker's head. + # Sets the buffer position of the marker's head. # # screenRange - The new {Point} to use # options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition} setHeadBufferPosition: (bufferPosition) -> @buffer.setMarkerHeadPosition(@id, bufferPosition) - # Public: Retrieves the screen position of the marker's tail. + # Retrieves the screen position of the marker's tail. # # Returns a {Point}. getTailScreenPosition: -> @tailScreenPosition ?= @displayBuffer.screenPositionForBufferPosition(@getTailBufferPosition(), wrapAtSoftNewlines: true) - # Public: Sets the screen position of the marker's tail. + # Sets the screen position of the marker's tail. # # screenRange - The new {Point} to use # options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition} @@ -83,20 +83,20 @@ class DisplayBufferMarker screenPosition = @displayBuffer.clipScreenPosition(screenPosition, options) @setTailBufferPosition(@displayBuffer.bufferPositionForScreenPosition(screenPosition, options)) - # Public: Retrieves the buffer position of the marker's tail. + # Retrieves the buffer position of the marker's tail. # # Returns a {Point}. getTailBufferPosition: -> @buffer.getMarkerTailPosition(@id) - # Public: Sets the buffer position of the marker's tail. + # Sets the buffer position of the marker's tail. # # screenRange - The new {Point} to use # options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition} setTailBufferPosition: (bufferPosition) -> @buffer.setMarkerTailPosition(@id, bufferPosition) - # Public: Sets the marker's tail to the same position as the marker's head. + # Sets the marker's tail to the same position as the marker's head. # # This only works if there isn't already a tail position. # @@ -104,11 +104,11 @@ class DisplayBufferMarker placeTail: -> @buffer.placeMarkerTail(@id) - # Public: Removes the tail from the marker. + # Removes the tail from the marker. clearTail: -> @buffer.clearMarkerTail(@id) - # Public: Sets a callback to be fired whenever the marker is changed. + # Sets a callback to be fired whenever the marker is changed. # # callback - A {Function} to execute observe: (callback) -> @@ -116,7 +116,7 @@ class DisplayBufferMarker @on 'changed', callback cancel: => @unobserve(callback) - # Public: Removes the callback that's fired whenever the marker changes. + # Removes the callback that's fired whenever the marker changes. # # callback - A {Function} to remove unobserve: (callback) -> From 34fb03fbd39cf05488d4c282b00b5534d6703fbb Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Tue, 30 Apr 2013 13:36:34 -0700 Subject: [PATCH 35/91] Update DisplayBuffer --- src/app/display-buffer.coffee | 577 +++++++++++++++++----------------- 1 file changed, 288 insertions(+), 289 deletions(-) diff --git a/src/app/display-buffer.coffee b/src/app/display-buffer.coffee index 983ba1b57..1b1601921 100644 --- a/src/app/display-buffer.coffee +++ b/src/app/display-buffer.coffee @@ -45,9 +45,12 @@ class DisplayBuffer ### Public ### + # Sets the visibility of the tokenized buffer. + # + # visible - A {Boolean} indicating of the tokenized buffer is shown setVisible: (visible) -> @tokenizedBuffer.setVisible(visible) - # Public: Defines the limit at which the buffer begins to soft wrap text. + # Defines the limit at which the buffer begins to soft wrap text. # # softWrapColumn - A {Number} defining the soft wrap limit. setSoftWrapColumn: (@softWrapColumn) -> @@ -58,7 +61,7 @@ class DisplayBuffer bufferDelta = 0 @triggerChanged({ start, end, screenDelta, bufferDelta }) - # Public: Gets the screen line for the given screen row. + # Gets the screen line for the given screen row. # # screenRow - A {Number} indicating the screen row. # @@ -66,7 +69,7 @@ class DisplayBuffer lineForRow: (row) -> @lineMap.lineForScreenRow(row) - # Public: Gets the screen lines for the given screen row range. + # Gets the screen lines for the given screen row range. # # startRow - A {Number} indicating the beginning screen row. # endRow - A {Number} indicating the ending screen row. @@ -75,14 +78,14 @@ class DisplayBuffer linesForRows: (startRow, endRow) -> @lineMap.linesForScreenRows(startRow, endRow) - # Public: Gets all the screen lines. + # Gets all the screen lines. # # Returns an {Array} of {ScreenLines}s. getLines: -> @lineMap.linesForScreenRows(0, @lineMap.lastScreenRow()) - # Public: Given starting and ending screen rows, this returns an array of the + # Given starting and ending screen rows, this returns an array of the # buffer rows corresponding to every screen row in the range # # startRow - The screen row {Number} to start at @@ -92,7 +95,7 @@ class DisplayBuffer bufferRowsForScreenRows: (startRow, endRow) -> @lineMap.bufferRowsForScreenRows(startRow, endRow) - # Public: Creates a new fold between two row numbers. + # Creates a new fold between two row numbers. # # startRow - The row {Number} to start folding at # endRow - The row {Number} to end the fold @@ -119,7 +122,7 @@ class DisplayBuffer fold - # Public: Given a {Fold}, determines if it is contained within another fold. + # Given a {Fold}, determines if it is contained within another fold. # # fold - The {Fold} to check # @@ -129,7 +132,7 @@ class DisplayBuffer for otherFold in folds return otherFold if fold != otherFold and fold.isContainedByFold(otherFold) - # Public: Given a starting and ending row, tries to find an existing fold. + # Given a starting and ending row, tries to find an existing fold. # # startRow - A {Number} representing a fold's starting row # endRow - A {Number} representing a fold's ending row @@ -139,7 +142,7 @@ class DisplayBuffer _.find @activeFolds[startRow] ? [], (fold) -> fold.startRow == startRow and fold.endRow == endRow - # Public: Removes any folds found that contain the given buffer row. + # Removes any folds found that contain the given buffer row. # # bufferRow - The buffer row {Number} to check against destroyFoldsContainingBufferRow: (bufferRow) -> @@ -150,7 +153,7 @@ class DisplayBuffer foldsStartingAtBufferRow: (bufferRow) -> new Array((@activeFolds[bufferRow] ? [])...) - # Public: Given a buffer row, this returns the largest fold that starts there. + # Given a buffer row, this returns the largest fold that starts there. # # Largest is defined as the fold whose difference between its start and end points # are the greatest. @@ -162,7 +165,7 @@ class DisplayBuffer return unless folds = @activeFolds[bufferRow] (folds.sort (a, b) -> b.endRow - a.endRow)[0] - # Public: Given a screen row, this returns the largest fold that starts there. + # Given a screen row, this returns the largest fold that starts there. # # Largest is defined as the fold whose difference between its start and end points # are the greatest. @@ -173,7 +176,7 @@ class DisplayBuffer largestFoldStartingAtScreenRow: (screenRow) -> @largestFoldStartingAtBufferRow(@bufferRowForScreenRow(screenRow)) - # Public: Given a buffer row, this returns the largest fold that includes it. + # Given a buffer row, this returns the largest fold that includes it. # # Largest is defined as the fold whose difference between its start and end points # are the greatest. @@ -188,7 +191,7 @@ class DisplayBuffer largestFold = fold if fold.endRow >= bufferRow largestFold - # Public: Given a buffer range, this converts it into a screen range. + # Given a buffer range, this converts it into a screen range. # # bufferRange - A {Range} consisting of buffer positions # @@ -198,7 +201,7 @@ class DisplayBuffer @lineMap.screenRangeForBufferRange( @expandBufferRangeToLineEnds(bufferRange))) - # Public: Given a buffer row, this converts it into a screen row. + # Given a buffer row, this converts it into a screen row. # # bufferRow - A {Number} representing a buffer row # @@ -209,7 +212,7 @@ class DisplayBuffer lastScreenRowForBufferRow: (bufferRow) -> @lineMap.screenPositionForBufferPosition([bufferRow, Infinity]).row - # Public: Given a screen row, this converts it into a buffer row. + # Given a screen row, this converts it into a buffer row. # # screenRow - A {Number} representing a screen row # @@ -217,7 +220,7 @@ class DisplayBuffer bufferRowForScreenRow: (screenRow) -> @lineMap.bufferPositionForScreenPosition([screenRow, 0]).row - # Public: Given a buffer range, this converts it into a screen position. + # Given a buffer range, this converts it into a screen position. # # bufferRange - The {Range} to convert # @@ -225,7 +228,7 @@ class DisplayBuffer screenRangeForBufferRange: (bufferRange) -> @lineMap.screenRangeForBufferRange(bufferRange) - # Public: Given a screen range, this converts it into a buffer position. + # Given a screen range, this converts it into a buffer position. # # screenRange - The {Range} to convert # @@ -233,25 +236,25 @@ class DisplayBuffer bufferRangeForScreenRange: (screenRange) -> @lineMap.bufferRangeForScreenRange(screenRange) - # Public: Gets the number of lines in the buffer. + # Gets the number of lines in the buffer. # # Returns a {Number}. getLineCount: -> @lineMap.getScreenLineCount() - # Public: Gets the number of the last row in the buffer. + # Gets the number of the last row in the buffer. # # Returns a {Number}. getLastRow: -> @getLineCount() - 1 - # Public: Gets the length of the longest screen line. + # Gets the length of the longest screen line. # # Returns a {Number}. maxLineLength: -> @lineMap.maxScreenLineLength - # Public: Given a buffer position, this converts it into a screen position. + # Given a buffer position, this converts it into a screen position. # # bufferPosition - An object that represents a buffer position. It can be either # an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point} @@ -263,7 +266,7 @@ class DisplayBuffer screenPositionForBufferPosition: (position, options) -> @lineMap.screenPositionForBufferPosition(position, options) - # Public: Given a buffer range, this converts it into a screen position. + # Given a buffer range, this converts it into a screen position. # # screenPosition - An object that represents a buffer position. It can be either # an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point} @@ -275,7 +278,7 @@ class DisplayBuffer bufferPositionForScreenPosition: (position, options) -> @lineMap.bufferPositionForScreenPosition(position, options) - # Public: Retrieves the grammar's token scopes for a buffer position. + # Retrieves the grammar's token scopes for a buffer position. # # bufferPosition - A {Point} in the {Buffer} # @@ -283,7 +286,7 @@ class DisplayBuffer scopesForBufferPosition: (bufferPosition) -> @tokenizedBuffer.scopesForPosition(bufferPosition) - # Public: Retrieves the grammar's token for a buffer position. + # Retrieves the grammar's token for a buffer position. # # bufferPosition - A {Point} in the {Buffer}. # @@ -291,13 +294,13 @@ class DisplayBuffer tokenForBufferPosition: (bufferPosition) -> @tokenizedBuffer.tokenForPosition(bufferPosition) - # Public: Retrieves the current tab length. + # Retrieves the current tab length. # # Returns a {Number}. getTabLength: -> @tokenizedBuffer.getTabLength() - # Public: Specifies the tab length. + # Specifies the tab length. # # tabLength - A {Number} that defines the new tab length. setTabLength: (tabLength) -> @@ -312,7 +315,7 @@ class DisplayBuffer reloadGrammar: -> @tokenizedBuffer.reloadGrammar() - # Public: Given a position, this clips it to a real position. + # Given a position, this clips it to a real position. # # For example, if `position`'s row exceeds the row count of the buffer, # or if its column goes beyond a line's length, this "sanitizes" the value @@ -328,6 +331,264 @@ class DisplayBuffer clipScreenPosition: (position, options) -> @lineMap.clipScreenPosition(position, options) + # Given a line, finds the point where it would wrap. + # + # line - The {String} to check + # softWrapColumn - The {Number} where you want soft wrapping to occur + # + # Returns a {Number} representing the `line` position where the wrap would take place. + # Returns `null` if a wrap wouldn't occur. + findWrapColumn: (line, softWrapColumn) -> + return unless line.length > softWrapColumn + + if /\s/.test(line[softWrapColumn]) + # search forward for the start of a word past the boundary + for column in [softWrapColumn..line.length] + return column if /\S/.test(line[column]) + return line.length + else + # search backward for the start of the word on the boundary + for column in [softWrapColumn..0] + return column + 1 if /\s/.test(line[column]) + return softWrapColumn + + # Given a range in screen coordinates, this expands it to the start and end of a line + # + # screenRange - The {Range} to expand + # + # Returns a new {Range}. + expandScreenRangeToLineEnds: (screenRange) -> + screenRange = Range.fromObject(screenRange) + { start, end } = screenRange + new Range([start.row, 0], [end.row, @lineMap.lineForScreenRow(end.row).text.length]) + + # Given a range in buffer coordinates, this expands it to the start and end of a line + # + # screenRange - The {Range} to expand + # + # Returns a new {Range}. + expandBufferRangeToLineEnds: (bufferRange) -> + bufferRange = Range.fromObject(bufferRange) + { start, end } = bufferRange + new Range([start.row, 0], [end.row, Infinity]) + + # Calculates a {Range} representing the start of the {Buffer} until the end. + # + # Returns a {Range}. + rangeForAllLines: -> + new Range([0, 0], @clipScreenPosition([Infinity, Infinity])) + + # Retrieves a {DisplayBufferMarker} based on its id. + # + # id - A {Number} representing a marker id + # + # Returns the {DisplayBufferMarker} (if it exists). + getMarker: (id) -> + @markers[id] ? new DisplayBufferMarker({id, displayBuffer: this}) + + # Retrieves the active markers in the buffer. + # + # Returns an {Array} of existing {DisplayBufferMarker}s. + getMarkers: -> + _.values(@markers) + + # Constructs a new marker at the given screen range. + # + # range - The marker {Range} (representing the distance between the head and tail) + # options - Options to pass to the {BufferMarker} constructor + # + # Returns a {Number} representing the new marker's ID. + markScreenRange: (args...) -> + bufferRange = @bufferRangeForScreenRange(args.shift()) + @markBufferRange(bufferRange, args...) + + # Constructs a new marker at the given buffer range. + # + # range - The marker {Range} (representing the distance between the head and tail) + # options - Options to pass to the {BufferMarker} constructor + # + # Returns a {Number} representing the new marker's ID. + markBufferRange: (args...) -> + @buffer.markRange(args...) + + # Constructs a new marker at the given screen position. + # + # range - The marker {Range} (representing the distance between the head and tail) + # options - Options to pass to the {BufferMarker} constructor + # + # Returns a {Number} representing the new marker's ID. + markScreenPosition: (screenPosition, options) -> + @markBufferPosition(@bufferPositionForScreenPosition(screenPosition), options) + + # Constructs a new marker at the given buffer position. + # + # range - The marker {Range} (representing the distance between the head and tail) + # options - Options to pass to the {BufferMarker} constructor + # + # Returns a {Number} representing the new marker's ID. + markBufferPosition: (bufferPosition, options) -> + @buffer.markPosition(bufferPosition, options) + + # Removes the marker with the given id. + # + # id - The {Number} of the ID to remove + destroyMarker: (id) -> + @buffer.destroyMarker(id) + delete @markers[id] + + # Gets the screen range of the display marker. + # + # id - The {Number} of the ID to check + # + # Returns a {Range}. + getMarkerScreenRange: (id) -> + @getMarker(id).getScreenRange() + + # Modifies the screen range of the display marker. + # + # id - The {Number} of the ID to change + # screenRange - The new {Range} to use + # options - A hash of options matching those found in {BufferMarker.setRange} + setMarkerScreenRange: (id, screenRange, options) -> + @getMarker(id).setScreenRange(screenRange, options) + + # Gets the buffer range of the display marker. + # + # id - The {Number} of the ID to check + # + # Returns a {Range}. + getMarkerBufferRange: (id) -> + @getMarker(id).getBufferRange() + + # Modifies the buffer range of the display marker. + # + # id - The {Number} of the ID to change + # screenRange - The new {Range} to use + # options - A hash of options matching those found in {BufferMarker.setRange} + setMarkerBufferRange: (id, bufferRange, options) -> + @getMarker(id).setBufferRange(bufferRange, options) + + # Retrieves the screen position of the marker's head. + # + # id - The {Number} of the ID to check + # + # Returns a {Point}. + getMarkerScreenPosition: (id) -> + @getMarkerHeadScreenPosition(id) + + # Retrieves the buffer position of the marker's head. + # + # id - The {Number} of the ID to check + # + # Returns a {Point}. + getMarkerBufferPosition: (id) -> + @getMarkerHeadBufferPosition(id) + + # Retrieves the screen position of the marker's head. + # + # id - The {Number} of the ID to check + # + # Returns a {Point}. + getMarkerHeadScreenPosition: (id) -> + @getMarker(id).getHeadScreenPosition() + + # Sets the screen position of the marker's head. + # + # id - The {Number} of the ID to change + # screenRange - The new {Point} to use + # options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition} + setMarkerHeadScreenPosition: (id, screenPosition, options) -> + @getMarker(id).setHeadScreenPosition(screenPosition, options) + + # Retrieves the buffer position of the marker's head. + # + # id - The {Number} of the ID to check + # + # Returns a {Point}. + getMarkerHeadBufferPosition: (id) -> + @getMarker(id).getHeadBufferPosition() + + # Sets the buffer position of the marker's head. + # + # id - The {Number} of the ID to check + # screenRange - The new {Point} to use + # options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition} + setMarkerHeadBufferPosition: (id, bufferPosition) -> + @getMarker(id).setHeadBufferPosition(bufferPosition) + + # Retrieves the screen position of the marker's tail. + # + # id - The {Number} of the ID to check + # + # Returns a {Point}. + getMarkerTailScreenPosition: (id) -> + @getMarker(id).getTailScreenPosition() + + # Sets the screen position of the marker's tail. + # + # id - The {Number} of the ID to change + # screenRange - The new {Point} to use + # options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition} + setMarkerTailScreenPosition: (id, screenPosition, options) -> + @getMarker(id).setTailScreenPosition(screenPosition, options) + + # Retrieves the buffer position of the marker's tail. + # + # id - The {Number} of the ID to check + # + # Returns a {Point}. + getMarkerTailBufferPosition: (id) -> + @getMarker(id).getTailBufferPosition() + + # Sets the buffer position of the marker's tail. + # + # id - The {Number} of the ID to check + # screenRange - The new {Point} to use + # options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition} + setMarkerTailBufferPosition: (id, bufferPosition) -> + @getMarker(id).setTailBufferPosition(bufferPosition) + + # Sets the marker's tail to the same position as the marker's head. + # + # This only works if there isn't already a tail position. + # + # id - A {Number} representing the marker to change + # + # Returns a {Point} representing the new tail position. + placeMarkerTail: (id) -> + @getMarker(id).placeTail() + + # Removes the tail from the marker. + # + # id - A {Number} representing the marker to change + clearMarkerTail: (id) -> + @getMarker(id).clearTail() + + # Identifies if the ending position of a marker is greater than the starting position. + # + # This can happen when, for example, you highlight text "up" in a {Buffer}. + # + # id - A {Number} representing the marker to check + # + # Returns a {Boolean}. + isMarkerReversed: (id) -> + @buffer.isMarkerReversed(id) + + # Identifies if the marker's head position is equal to its tail. + # + # id - A {Number} representing the marker to check + # + # Returns a {Boolean}. + isMarkerRangeEmpty: (id) -> + @buffer.isMarkerRangeEmpty(id) + + # Sets a callback to be fired whenever a marker is changed. + # + # id - A {Number} representing the marker to watch + # callback - A {Function} to execute + observeMarker: (id, callback) -> + @getMarker(id).observe(callback) + ### Internal ### registerFold: (fold) -> @@ -427,268 +688,6 @@ class DisplayBuffer lineFragments - ### Public ### - - # Public: Given a line, finds the point where it would wrap. - # - # line - The {String} to check - # softWrapColumn - The {Number} where you want soft wrapping to occur - # - # Returns a {Number} representing the `line` position where the wrap would take place. - # Returns `null` if a wrap wouldn't occur. - findWrapColumn: (line, softWrapColumn) -> - return unless line.length > softWrapColumn - - if /\s/.test(line[softWrapColumn]) - # search forward for the start of a word past the boundary - for column in [softWrapColumn..line.length] - return column if /\S/.test(line[column]) - return line.length - else - # search backward for the start of the word on the boundary - for column in [softWrapColumn..0] - return column + 1 if /\s/.test(line[column]) - return softWrapColumn - - # Public: Given a range in screen coordinates, this expands it to the start and end of a line - # - # screenRange - The {Range} to expand - # - # Returns a new {Range}. - expandScreenRangeToLineEnds: (screenRange) -> - screenRange = Range.fromObject(screenRange) - { start, end } = screenRange - new Range([start.row, 0], [end.row, @lineMap.lineForScreenRow(end.row).text.length]) - - # Public: Given a range in buffer coordinates, this expands it to the start and end of a line - # - # screenRange - The {Range} to expand - # - # Returns a new {Range}. - expandBufferRangeToLineEnds: (bufferRange) -> - bufferRange = Range.fromObject(bufferRange) - { start, end } = bufferRange - new Range([start.row, 0], [end.row, Infinity]) - - # Public: Calculates a {Range} representing the start of the {Buffer} until the end. - # - # Returns a {Range}. - rangeForAllLines: -> - new Range([0, 0], @clipScreenPosition([Infinity, Infinity])) - - # Public: Retrieves a {DisplayBufferMarker} based on its id. - # - # id - A {Number} representing a marker id - # - # Returns the {DisplayBufferMarker} (if it exists). - getMarker: (id) -> - @markers[id] ? new DisplayBufferMarker({id, displayBuffer: this}) - - # Public: Retrieves the active markers in the buffer. - # - # Returns an {Array} of existing {DisplayBufferMarker}s. - getMarkers: -> - _.values(@markers) - - # Public: Constructs a new marker at the given screen range. - # - # range - The marker {Range} (representing the distance between the head and tail) - # options - Options to pass to the {BufferMarker} constructor - # - # Returns a {Number} representing the new marker's ID. - markScreenRange: (args...) -> - bufferRange = @bufferRangeForScreenRange(args.shift()) - @markBufferRange(bufferRange, args...) - - # Public: Constructs a new marker at the given buffer range. - # - # range - The marker {Range} (representing the distance between the head and tail) - # options - Options to pass to the {BufferMarker} constructor - # - # Returns a {Number} representing the new marker's ID. - markBufferRange: (args...) -> - @buffer.markRange(args...) - - # Public: Constructs a new marker at the given screen position. - # - # range - The marker {Range} (representing the distance between the head and tail) - # options - Options to pass to the {BufferMarker} constructor - # - # Returns a {Number} representing the new marker's ID. - markScreenPosition: (screenPosition, options) -> - @markBufferPosition(@bufferPositionForScreenPosition(screenPosition), options) - - # Public: Constructs a new marker at the given buffer position. - # - # range - The marker {Range} (representing the distance between the head and tail) - # options - Options to pass to the {BufferMarker} constructor - # - # Returns a {Number} representing the new marker's ID. - markBufferPosition: (bufferPosition, options) -> - @buffer.markPosition(bufferPosition, options) - - # Public: Removes the marker with the given id. - # - # id - The {Number} of the ID to remove - destroyMarker: (id) -> - @buffer.destroyMarker(id) - delete @markers[id] - - # Public: Gets the screen range of the display marker. - # - # id - The {Number} of the ID to check - # - # Returns a {Range}. - getMarkerScreenRange: (id) -> - @getMarker(id).getScreenRange() - - # Public: Modifies the screen range of the display marker. - # - # id - The {Number} of the ID to change - # screenRange - The new {Range} to use - # options - A hash of options matching those found in {BufferMarker.setRange} - setMarkerScreenRange: (id, screenRange, options) -> - @getMarker(id).setScreenRange(screenRange, options) - - # Public: Gets the buffer range of the display marker. - # - # id - The {Number} of the ID to check - # - # Returns a {Range}. - getMarkerBufferRange: (id) -> - @getMarker(id).getBufferRange() - - # Public: Modifies the buffer range of the display marker. - # - # id - The {Number} of the ID to change - # screenRange - The new {Range} to use - # options - A hash of options matching those found in {BufferMarker.setRange} - setMarkerBufferRange: (id, bufferRange, options) -> - @getMarker(id).setBufferRange(bufferRange, options) - - # Public: Retrieves the screen position of the marker's head. - # - # id - The {Number} of the ID to check - # - # Returns a {Point}. - getMarkerScreenPosition: (id) -> - @getMarkerHeadScreenPosition(id) - - # Public: Retrieves the buffer position of the marker's head. - # - # id - The {Number} of the ID to check - # - # Returns a {Point}. - getMarkerBufferPosition: (id) -> - @getMarkerHeadBufferPosition(id) - - # Public: Retrieves the screen position of the marker's head. - # - # id - The {Number} of the ID to check - # - # Returns a {Point}. - getMarkerHeadScreenPosition: (id) -> - @getMarker(id).getHeadScreenPosition() - - # Public: Sets the screen position of the marker's head. - # - # id - The {Number} of the ID to change - # screenRange - The new {Point} to use - # options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition} - setMarkerHeadScreenPosition: (id, screenPosition, options) -> - @getMarker(id).setHeadScreenPosition(screenPosition, options) - - # Public: Retrieves the buffer position of the marker's head. - # - # id - The {Number} of the ID to check - # - # Returns a {Point}. - getMarkerHeadBufferPosition: (id) -> - @getMarker(id).getHeadBufferPosition() - - # Public: Sets the buffer position of the marker's head. - # - # id - The {Number} of the ID to check - # screenRange - The new {Point} to use - # options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition} - setMarkerHeadBufferPosition: (id, bufferPosition) -> - @getMarker(id).setHeadBufferPosition(bufferPosition) - - # Public: Retrieves the screen position of the marker's tail. - # - # id - The {Number} of the ID to check - # - # Returns a {Point}. - getMarkerTailScreenPosition: (id) -> - @getMarker(id).getTailScreenPosition() - - # Public: Sets the screen position of the marker's tail. - # - # id - The {Number} of the ID to change - # screenRange - The new {Point} to use - # options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition} - setMarkerTailScreenPosition: (id, screenPosition, options) -> - @getMarker(id).setTailScreenPosition(screenPosition, options) - - # Public: Retrieves the buffer position of the marker's tail. - # - # id - The {Number} of the ID to check - # - # Returns a {Point}. - getMarkerTailBufferPosition: (id) -> - @getMarker(id).getTailBufferPosition() - - # Public: Sets the buffer position of the marker's tail. - # - # id - The {Number} of the ID to check - # screenRange - The new {Point} to use - # options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition} - setMarkerTailBufferPosition: (id, bufferPosition) -> - @getMarker(id).setTailBufferPosition(bufferPosition) - - # Public: Sets the marker's tail to the same position as the marker's head. - # - # This only works if there isn't already a tail position. - # - # id - A {Number} representing the marker to change - # - # Returns a {Point} representing the new tail position. - placeMarkerTail: (id) -> - @getMarker(id).placeTail() - - # Public: Removes the tail from the marker. - # - # id - A {Number} representing the marker to change - clearMarkerTail: (id) -> - @getMarker(id).clearTail() - - # Public: Identifies if the ending position of a marker is greater than the starting position. - # - # This can happen when, for example, you highlight text "up" in a {Buffer}. - # - # id - A {Number} representing the marker to check - # - # Returns a {Boolean}. - isMarkerReversed: (id) -> - @buffer.isMarkerReversed(id) - - # Public: Identifies if the marker's head position is equal to its tail. - # - # id - A {Number} representing the marker to check - # - # Returns a {Boolean}. - isMarkerRangeEmpty: (id) -> - @buffer.isMarkerRangeEmpty(id) - - # Public: Sets a callback to be fired whenever a marker is changed. - # - # id - A {Number} representing the marker to watch - # callback - A {Function} to execute - observeMarker: (id, callback) -> - @getMarker(id).observe(callback) - - ### Internal ### - pauseMarkerObservers: -> marker.pauseEvents() for marker in @getMarkers() From 2bf7adef5c18fd850e996fa226ad7d26af0bf761 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Tue, 30 Apr 2013 15:14:15 -0700 Subject: [PATCH 36/91] A slight modification to EditSession --- src/app/edit-session.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index 98bf361f7..ee4d1d3e9 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -1122,7 +1122,7 @@ class EditSession # Returns an {Array} of {Cursor}s. getCursors: -> new Array(@cursors...) - # Public: Retrieves a single cursor + # Public: Retrieves the most recently added cursor. # # Returns a {Cursor}. getCursor: -> From b0e4ce7bb8903e1ed3954737f3b9bf1e710b8ce1 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 13:50:38 -0700 Subject: [PATCH 37/91] Update EditSession --- src/app/display-buffer.coffee | 4 +- src/app/edit-session.coffee | 683 +++++++++++----------------------- src/app/text-buffer.coffee | 2 + 3 files changed, 229 insertions(+), 460 deletions(-) diff --git a/src/app/display-buffer.coffee b/src/app/display-buffer.coffee index 1b1601921..3c6d84b5a 100644 --- a/src/app/display-buffer.coffee +++ b/src/app/display-buffer.coffee @@ -236,13 +236,13 @@ class DisplayBuffer bufferRangeForScreenRange: (screenRange) -> @lineMap.bufferRangeForScreenRange(screenRange) - # Gets the number of lines in the buffer. + # Gets the number of screen lines. # # Returns a {Number}. getLineCount: -> @lineMap.getScreenLineCount() - # Gets the number of the last row in the buffer. + # Gets the number of the last screen line. # # Returns a {Number}. getLastRow: -> diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index ee4d1d3e9..fc0f97fea 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -10,7 +10,7 @@ Range = require 'range' _ = require 'underscore' fsUtils = require 'fs-utils' -# Public: An `EditSession` manages the states between {Editor}s, {Buffer}s, and the project as a whole. +# An `EditSession` manages the states between {Editor}s, {Buffer}s, and the project as a whole. module.exports = class EditSession registerDeserializer(this) @@ -85,15 +85,13 @@ class EditSession scrollLeft: @getScrollLeft() cursorScreenPosition: @getCursorScreenPosition().serialize() - # Internal: Creates a copy of the current {EditSession}. - # - # Returns an identical `EditSession`. + # Creates a copy of the current {EditSession}.Returns an identical `EditSession`. copy: -> EditSession.deserialize(@serialize(), @project) ### Public ### - # Public: Retrieves the filename of the open file. + # Retrieves the filename of the open file. # # This is `'untitled'` if the file is new and not saved to the disk. # @@ -104,7 +102,7 @@ class EditSession else 'untitled' - # Public: Retrieves the filename of the open file, followed by a dash, then the file's directory. + # Retrieves the filename of the open file, followed by a dash, then the file's directory. # # If the file is brand new, the title is `untitled`. # @@ -117,7 +115,7 @@ class EditSession else 'untitled' - # Public: Compares two `EditSession`s to determine equality. + # Compares two `EditSession`s to determine equality. # # Equality is based on the condition that: # @@ -136,45 +134,47 @@ class EditSession setVisible: (visible) -> @displayBuffer.setVisible(visible) - # Public: Defines the value of the `EditSession`'s `scrollTop` property. + # Defines the value of the `EditSession`'s `scrollTop` property. # # scrollTop - A {Number} defining the `scrollTop`, in pixels. setScrollTop: (@scrollTop) -> - # Public: Gets the value of the `EditSession`'s `scrollTop` property. + + # Gets the value of the `EditSession`'s `scrollTop` property. # # Returns a {Number} defining the `scrollTop`, in pixels. getScrollTop: -> @scrollTop - # Public: Defines the value of the `EditSession`'s `scrollLeft` property. + # Defines the value of the `EditSession`'s `scrollLeft` property. # # scrollLeft - A {Number} defining the `scrollLeft`, in pixels. setScrollLeft: (@scrollLeft) -> - # Public: Gets the value of the `EditSession`'s `scrollLeft` property. + + # Gets the value of the `EditSession`'s `scrollLeft` property. # # Returns a {Number} defining the `scrollLeft`, in pixels. getScrollLeft: -> @scrollLeft - # Public: Defines the limit at which the buffer begins to soft wrap text. + # Defines the limit at which the buffer begins to soft wrap text. # # softWrapColumn - A {Number} defining the soft wrap limit setSoftWrapColumn: (@softWrapColumn) -> @displayBuffer.setSoftWrapColumn(@softWrapColumn) - # Public: Defines whether to use soft tabs. + # Defines whether to use soft tabs. # # softTabs - A {Boolean} which, if `true`, indicates that you want soft tabs. setSoftTabs: (@softTabs) -> - # Public: Retrieves whether soft tabs are enabled. + # Retrieves whether soft tabs are enabled. # # Returns a {Boolean}. getSoftWrap: -> @softWrap - # Public: Defines whether to use soft wrapping of text. + # Defines whether to use soft wrapping of text. # # softTabs - A {Boolean} which, if `true`, indicates that you want soft wraps. setSoftWrap: (@softWrap) -> - # Public: Retrieves that character used to indicate a tab. + # Retrieves that character used to indicate a tab. # # If soft tabs are enabled, this is a space (`" "`) times the {.getTabLength} value. # Otherwise, it's a tab (`\t`). @@ -182,17 +182,17 @@ class EditSession # Returns a {String}. getTabText: -> @buildIndentString(1) - # Public: Retrieves the current tab length. + # Retrieves the current tab length. # # Returns a {Number}. getTabLength: -> @displayBuffer.getTabLength() - # Public: Specifies the tab length. + # Specifies the tab length. # # tabLength - A {Number} that defines the new tab length. setTabLength: (tabLength) -> @displayBuffer.setTabLength(tabLength) - # Public: Given a position, this clips it to a real position. + # Given a position, this clips it to a real position. # # For example, if `position`'s row exceeds the row count of the buffer, # or if its column goes beyond a line's length, this "sanitizes" the value @@ -203,7 +203,7 @@ class EditSession # Returns the new, clipped {Point}. Note that this could be the same as `position` if no clipping was performed. clipBufferPosition: (bufferPosition) -> @buffer.clipPosition(bufferPosition) - # Public: Given a range, this clips it to a real range. + # Given a range, this clips it to a real range. # # For example, if `range`'s row exceeds the row count of the buffer, # or if its column goes beyond a line's length, this "sanitizes" the value @@ -214,7 +214,7 @@ class EditSession # Returns the new, clipped {Point}. Note that this could be the same as `range` if no clipping was performed. clipBufferRange: (range) -> @buffer.clipRange(range) - # Public: Given a buffer row, this retrieves the indentation level. + # Given a buffer row, this retrieves the indentation level. # # bufferRow - A {Number} indicating the buffer row. # @@ -222,7 +222,7 @@ class EditSession indentationForBufferRow: (bufferRow) -> @indentLevelForLine(@lineForBufferRow(bufferRow)) - # Public: This specifies the new indentation level for a buffer row. + # This specifies the new indentation level for a buffer row. # # bufferRow - A {Number} indicating the buffer row. # newLevel - A {Number} indicating the new indentation level. @@ -253,235 +253,127 @@ class EditSession else _.multiplyString("\t", Math.floor(number)) - # Public: Saves the buffer. + # {Delegates to: Buffer.save} save: -> @buffer.save() - # Public: Saves the buffer at a specific path. - # - # path - The path to save at. + # {Delegates to: Buffer.saveAs} saveAs: (path) -> @buffer.saveAs(path) - # Public: Retrieves the current buffer's file extension. - # - # Returns a {String}. + # {Delegates to: Buffer.getExtension} getFileExtension: -> @buffer.getExtension() - # Public: Retrieves the current buffer's file path. - # - # Returns a {String}. + # {Delegates to: Buffer.getPath} getPath: -> @buffer.getPath() - # Public: Retrieves the current buffer's text. - # - # Return a {String}. + # {Delegates to: Buffer.getText} getText: -> @buffer.getText() - # Public: Set the current buffer's text content. - # - # Return a {String}. + # {Delegates to: Buffer.setText} setText: (text) -> @buffer.setText(text) - # Public: Retrieves the current buffer. + # Retrieves the current buffer. # - # Returns a {String}. + # Returns a {Buffer}. getBuffer: -> @buffer - # Public: Retrieves the current buffer's URI. + # Retrieves the current buffer's URI. # # Returns a {String}. getUri: -> @getPath() - # Public: Given a buffer row, identifies if it is blank. - # - # bufferRow - A buffer row {Number} to check - # - # Returns a {Boolean}. + # {Delegates to: Buffer.isRowBlank} isBufferRowBlank: (bufferRow) -> @buffer.isRowBlank(bufferRow) - # Public: Given a buffer row, this finds the next row that's blank. - # - # bufferRow - A buffer row {Number} to check - # - # Returns a {Number}, or `null` if there's no other blank row. + # {Delegates to: Buffer.nextNonBlankRow} nextNonBlankBufferRow: (bufferRow) -> @buffer.nextNonBlankRow(bufferRow) - # Public: Finds the last point in the current buffer. - # - # Returns a {Point} representing the last position. + # {Delegates to: Buffer.getEofPosition} getEofBufferPosition: -> @buffer.getEofPosition() - # Public: Finds the last line in the current buffer. - # - # Returns a {Number}. + # {Delegates to: Buffer.getLastRow} getLastBufferRow: -> @buffer.getLastRow() - # Public: Given a buffer row, this retrieves the range for that line. - # - # row - A {Number} identifying the row - # options - A hash with one key, `includeNewline`, which specifies whether you - # want to include the trailing newline - # - # Returns a {Range}. + # {Delegates to: Buffer.rangeForRow} bufferRangeForBufferRow: (row, options) -> @buffer.rangeForRow(row, options) - # Public: Given a buffer row, this retrieves that line. - # - # row - A {Number} identifying the row - # - # Returns a {String}. + # {Delegates to: Buffer.lineForRow} lineForBufferRow: (row) -> @buffer.lineForRow(row) - # Public: Given a buffer row, this retrieves that line's length. - # - # row - A {Number} identifying the row - # - # Returns a {Number}. + # {Delegates to: Buffer.lineLengthForRow} lineLengthForBufferRow: (row) -> @buffer.lineLengthForRow(row) - # Public: Scans for text in the buffer, calling a function on each match. - # - # regex - A {RegExp} representing the text to find - # range - A {Range} in the buffer to search within - # iterator - A {Function} that's called on each match + # {Delegates to: Buffer.scanInRange} scanInBufferRange: (args...) -> @buffer.scanInRange(args...) - # Public: Scans for text in the buffer _backwards_, calling a function on each match. - # - # regex - A {RegExp} representing the text to find - # range - A {Range} in the buffer to search within - # iterator - A {Function} that's called on each match + # {Delegates to: Buffer.backwardsScanInRange} backwardsScanInBufferRange: (args...) -> @buffer.backwardsScanInRange(args...) - # Public: Identifies if the {Buffer} is modified (and not saved). - # - # Returns a {Boolean}. + # {Delegates to: Buffer.isModified} isModified: -> @buffer.isModified() - # Public: Identifies if the modified buffer should let you know if it's closing + # Identifies if the modified buffer should let you know if it's closing # without being saved. # # Returns a {Boolean}. shouldPromptToSave: -> @isModified() and not @buffer.hasMultipleEditors() - # Public: Given a buffer position, this converts it into a screen position. - # - # bufferPosition - An object that represents a buffer position. It can be either - # an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point} - # options - The same options available to {DisplayBuffer.screenPositionForBufferPosition}. - # - # Returns a {Point}. + # {Delegates to: DisplayBuffer.screenPositionForBufferPosition} screenPositionForBufferPosition: (bufferPosition, options) -> @displayBuffer.screenPositionForBufferPosition(bufferPosition, options) - # Public: Given a buffer range, this converts it into a screen position. - # - # screenPosition - An object that represents a buffer position. It can be either - # an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point} - # options - The same options available to {DisplayBuffer.bufferPositionForScreenPosition}. - # - # Returns a {Point}. + # {Delegates to: DisplayBuffer.bufferPositionForScreenPosition} bufferPositionForScreenPosition: (screenPosition, options) -> @displayBuffer.bufferPositionForScreenPosition(screenPosition, options) - # Public: Given a buffer range, this converts it into a screen position. - # - # bufferRange - The {Range} to convert - # - # Returns a {Range}. + # {Delegates to: DisplayBuffer.screenRangeForBufferRange} screenRangeForBufferRange: (bufferRange) -> @displayBuffer.screenRangeForBufferRange(bufferRange) - # Public: Given a screen range, this converts it into a buffer position. - # - # screenRange - The {Range} to convert - # - # Returns a {Range}. + # {Delegates to: DisplayBuffer.bufferRangeForScreenRange} bufferRangeForScreenRange: (screenRange) -> @displayBuffer.bufferRangeForScreenRange(screenRange) - # Public: Given a position, this clips it to a real position. - # - # For example, if `position`'s row exceeds the row count of the buffer, - # or if its column goes beyond a line's length, this "sanitizes" the value - # to a real position. - # - # position - The {Point} to clip - # options - A hash with the following values: - # :wrapBeyondNewlines - if `true`, continues wrapping past newlines - # :wrapAtSoftNewlines - if `true`, continues wrapping past soft newlines - # :screenLine - if `true`, indicates that you're using a line number, not a row number - # - # Returns the new, clipped {Point}. Note that this could be the same as `position` if no clipping was performed. + + # {Delegates to: DisplayBuffer.clipScreenPosition} clipScreenPosition: (screenPosition, options) -> @displayBuffer.clipScreenPosition(screenPosition, options) - # Public: Gets the line for the given screen row. - # - # screenRow - A {Number} indicating the screen row. - # - # Returns a {String}. + # {Delegates to: DisplayBuffer.lineForRow} lineForScreenRow: (row) -> @displayBuffer.lineForRow(row) - # Public: Gets the lines for the given screen row boundaries. - # - # start - A {Number} indicating the beginning screen row. - # end - A {Number} indicating the ending screen row. - # - # Returns an {Array} of {String}s. + # {Delegates to: DisplayBuffer.linesForRows} linesForScreenRows: (start, end) -> @displayBuffer.linesForRows(start, end) - # Public: Gets the number of screen rows. - # - # Returns a {Number}. + # {Delegates to: DisplayBuffer.getLineCount} getScreenLineCount: -> @displayBuffer.getLineCount() - # Public: Gets the length of the longest screen line. - # - # Returns a {Number}. + # {Delegates to: DisplayBuffer.maxLineLength} maxScreenLineLength: -> @displayBuffer.maxLineLength() - # Public: Gets the number of the last row in the buffer. - # - # Returns a {Number}. + # {Delegates to: DisplayBuffer.getLastRow} getLastScreenRow: -> @displayBuffer.getLastRow() - # Public: Given a starting and ending row, this converts every row into a buffer position. - # - # startRow - The row {Number} to start at - # endRow - The row {Number} to end at (default: {.getLastScreenRow}) - # - # Returns an {Array} of {Range}s. + # {Delegates to: DisplayBuffer.bufferRowsForScreenRows} bufferRowsForScreenRows: (startRow, endRow) -> @displayBuffer.bufferRowsForScreenRows(startRow, endRow) - # Public: Retrieves the grammar's token scopes for a buffer position. - # - # bufferPosition - A {Point} in the {Buffer} - # - # Returns an {Array} of {String}s. + # {Delegates to: DisplayBuffer.bufferRowsForScreenRows} scopesForBufferPosition: (bufferPosition) -> @displayBuffer.scopesForBufferPosition(bufferPosition) - # Public: Retrieves the grammar's token for a buffer position. - # - # bufferPosition - A {Point} in the {Buffer} - # - # Returns a {Token}. + # {Delegates to: DisplayBuffer.tokenForBufferPosition} tokenForBufferPosition: (bufferPosition) -> @displayBuffer.tokenForBufferPosition(bufferPosition) - # Public: Retrieves the grammar's token scopes for the line with the most recently added cursor. + # Retrieves the grammar's token scopes for the line with the most recently added cursor. # # Returns an {Array} of {String}s. getCursorScopes: -> @getCursor().getScopes() - # Internal: - logScreenLines: (start, end) -> @displayBuffer.logLines(start, end) - - # Public: Determines whether the {Editor} will auto indent rows. + # Determines whether the {Editor} will auto indent rows. # # Returns a {Boolean}. shouldAutoIndent: -> config.get("editor.autoIndent") - # Public: Determines whether the {Editor} will auto indent pasted text. + # Determines whether the {Editor} will auto indent pasted text. # # Returns a {Boolean}. shouldAutoIndentPastedText: -> config.get("editor.autoIndentOnPaste") - # Public: Inserts text at the current cursor positions + # Inserts text at the current cursor positions # # text - A {String} representing the text to insert. # options - A set of options equivalent to {Selection.insertText} @@ -489,17 +381,17 @@ class EditSession options.autoIndent ?= @shouldAutoIndent() @mutateSelectedText (selection) -> selection.insertText(text, options) - # Public: Inserts a new line at the current cursor positions. + # Inserts a new line at the current cursor positions. insertNewline: -> @insertText('\n') - # Public: Inserts a new line below the current cursor positions. + # Inserts a new line below the current cursor positions. insertNewlineBelow: -> @transact => @moveCursorToEndOfLine() @insertNewline() - # Public: Inserts a new line above the current cursor positions. + # Inserts a new line above the current cursor positions. insertNewlineAbove: -> @transact => onFirstLine = @getCursorBufferPosition().row is 0 @@ -508,46 +400,46 @@ class EditSession @insertNewline() @moveCursorUp() if onFirstLine - # Public: Indents the current line. + # Indents the current line. # # options - A set of options equivalent to {Selection.indent}. indent: (options={})-> options.autoIndent ?= @shouldAutoIndent() @mutateSelectedText (selection) -> selection.indent(options) - # Public: Performs a backspace, removing the character found behind the cursor position. + # Performs a backspace, removing the character found behind the cursor position. backspace: -> @mutateSelectedText (selection) -> selection.backspace() - # Public: Performs a backspace to the beginning of the current word, removing characters found there. + # Performs a backspace to the beginning of the current word, removing characters found there. backspaceToBeginningOfWord: -> @mutateSelectedText (selection) -> selection.backspaceToBeginningOfWord() - # Public: Performs a backspace to the beginning of the current line, removing characters found there. + # Performs a backspace to the beginning of the current line, removing characters found there. backspaceToBeginningOfLine: -> @mutateSelectedText (selection) -> selection.backspaceToBeginningOfLine() - # Public: Performs a delete, removing the character found ahead of the cursor position. + # Performs a delete, removing the character found ahead of the cursor position. delete: -> @mutateSelectedText (selection) -> selection.delete() - # Public: Performs a delete to the end of the current word, removing characters found there. + # Performs a delete to the end of the current word, removing characters found there. deleteToEndOfWord: -> @mutateSelectedText (selection) -> selection.deleteToEndOfWord() - # Public: Deletes the entire line. + # Deletes the entire line. deleteLine: -> @mutateSelectedText (selection) -> selection.deleteLine() - # Public: Indents the selected rows. + # Indents the selected rows. indentSelectedRows: -> @mutateSelectedText (selection) -> selection.indentSelectedRows() - # Public: Outdents the selected rows. + # Outdents the selected rows. outdentSelectedRows: -> @mutateSelectedText (selection) -> selection.outdentSelectedRows() - # Public: Wraps the lines within a selection in comments. + # Wraps the lines within a selection in comments. # # If the language doesn't have comments, nothing happens. # @@ -560,14 +452,14 @@ class EditSession autoIndentSelectedRows: -> @mutateSelectedText (selection) -> selection.autoIndentSelectedRows() - # Given a buffer range, this converts all `\t` characters to the appopriate {.getTabText} value. + # Given a buffer range, this converts all `\t` characters to the appropriate {.getTabText} value. # # bufferRange - The {Range} to perform the replace in normalizeTabsInBufferRange: (bufferRange) -> return unless @softTabs @scanInBufferRange /\t/, bufferRange, ({replace}) => replace(@getTabText()) - # Public: Performs a cut to the end of the current line. + # Performs a cut to the end of the current line. # # Characters are removed, but the text remains in the clipboard. cutToEndOfLine: -> @@ -576,21 +468,21 @@ class EditSession selection.cutToEndOfLine(maintainPasteboard) maintainPasteboard = true - # Public: Cuts the selected text. + # Cuts the selected text. cutSelectedText: -> maintainPasteboard = false @mutateSelectedText (selection) -> selection.cut(maintainPasteboard) maintainPasteboard = true - # Public: Copies the selected text. + # Copies the selected text. copySelectedText: -> maintainPasteboard = false for selection in @getSelections() selection.copy(maintainPasteboard) maintainPasteboard = true - # Public: Pastes the text in the clipboard. + # Pastes the text in the clipboard. # # options - A set of options equivalent to {Selection.insertText}. pasteText: (options={}) -> @@ -602,96 +494,64 @@ class EditSession @insertText(text, options) - # Public: Undos the last {Buffer} change. + # Undos the last {Buffer} change. undo: -> @buffer.undo(this) - # Public: Redos the last {Buffer} change. + # Redos the last {Buffer} change. redo: -> @buffer.redo(this) - ### Internal ### - - transact: (fn) -> - isNewTransaction = @buffer.transact() - oldSelectedRanges = @getSelectedBufferRanges() - @pushOperation - undo: (editSession) -> - editSession?.setSelectedBufferRanges(oldSelectedRanges) - if fn - result = fn() - @commit() if isNewTransaction - result - - commit: -> - newSelectedRanges = @getSelectedBufferRanges() - @pushOperation - redo: (editSession) -> - editSession?.setSelectedBufferRanges(newSelectedRanges) - @buffer.commit() - - abort: -> - @buffer.abort() - - ### Public ### - - # Public: Folds all the rows. + # Folds all the rows. foldAll: -> @languageMode.foldAll() - # Public: Unfolds all the rows. + # Unfolds all the rows. unfoldAll: -> @languageMode.unfoldAll() - # Public: Folds the current row. + # Folds the current row. foldCurrentRow: -> bufferRow = @bufferPositionForScreenPosition(@getCursorScreenPosition()).row @foldBufferRow(bufferRow) - # Public: Given a buffer row, this folds it. + # Given a buffer row, this folds it. # # bufferRow - A {Number} indicating the buffer row foldBufferRow: (bufferRow) -> @languageMode.foldBufferRow(bufferRow) - # Public: Unfolds the current row. + # Unfolds the current row. unfoldCurrentRow: -> bufferRow = @bufferPositionForScreenPosition(@getCursorScreenPosition()).row @unfoldBufferRow(bufferRow) - # Public: Given a buffer row, this unfolds it. + # Given a buffer row, this unfolds it. # # bufferRow - A {Number} indicating the buffer row unfoldBufferRow: (bufferRow) -> @languageMode.unfoldBufferRow(bufferRow) - # Public: Folds all selections. + # Folds all selections. foldSelection: -> selection.fold() for selection in @getSelections() - # Public: Creates a new fold between two row numbers. - # - # startRow - The row {Number} to start folding at - # endRow - The row {Number} to end the fold - # - # Returns the new {Fold}. + # {Delegates to: DisplayBuffer.createFold} createFold: (startRow, endRow) -> @displayBuffer.createFold(startRow, endRow) - # Public: Removes any {Fold}s found that contain the given buffer row. - # - # bufferRow - The buffer row {Number} to check against + # {Delegates to: DisplayBuffer.destroyFoldsContainingBufferRow} destroyFoldsContainingBufferRow: (bufferRow) -> @displayBuffer.destroyFoldsContainingBufferRow(bufferRow) - # Public: Removes any {Fold}s found that intersect the given buffer row. + # Removes any {Fold}s found that intersect the given buffer row. # # bufferRow - The buffer row {Number} to check against destroyFoldsIntersectingBufferRange: (bufferRange) -> for row in [bufferRange.start.row..bufferRange.end.row] @destroyFoldsContainingBufferRow(row) - # Public: Given the id of a {Fold}, this removes it. + # Given the id of a {Fold}, this removes it. # # foldId - The fold id {Number} to remove destroyFold: (foldId) -> @@ -699,13 +559,13 @@ class EditSession fold.destroy() @setCursorBufferPosition([fold.startRow, 0]) - # Public: Determines if the given row that the cursor is at is folded. + # Determines if the given row that the cursor is at is folded. # # Returns `true` if the row is folded, `false` otherwise. isFoldedAtCursorRow: -> @isFoldedAtScreenRow(@getCursorScreenRow()) - # Public: Determines if the given buffer row is folded. + # Determines if the given buffer row is folded. # # bufferRow - A {Number} indicating the buffer row. # @@ -714,7 +574,7 @@ class EditSession screenRow = @screenPositionForBufferPosition([bufferRow]).row @isFoldedAtScreenRow(screenRow) - # Public: Determines if the given screen row is folded. + # Determines if the given screen row is folded. # # screenRow - A {Number} indicating the screen row. # @@ -722,29 +582,15 @@ class EditSession isFoldedAtScreenRow: (screenRow) -> @lineForScreenRow(screenRow)?.fold? - # Public: Given a buffer row, this returns the largest fold that includes it. - # - # Largest is defined as the fold whose difference between its start and end points - # are the greatest. - # - # bufferRow - A {Number} indicating the buffer row - # - # Returns a {Fold}. + # {Delegates to: DisplayBuffer.largestFoldContainingBufferRow} largestFoldContainingBufferRow: (bufferRow) -> @displayBuffer.largestFoldContainingBufferRow(bufferRow) - # Public: Given a screen row, this returns the largest fold that starts there. - # - # Largest is defined as the fold whose difference between its start and end points - # are the greatest. - # - # screenRow - A {Number} indicating the screen row - # - # Returns a {Fold}. + # {Delegates to: DisplayBuffer.largestFoldStartingAtScreenRow} largestFoldStartingAtScreenRow: (screenRow) -> @displayBuffer.largestFoldStartingAtScreenRow(screenRow) - # Public: Given a buffer row, this returns a suggested indentation level. + # Given a buffer row, this returns a suggested indentation level. # # The indentation level provided is based on the current language. # @@ -754,32 +600,32 @@ class EditSession suggestedIndentForBufferRow: (bufferRow) -> @languageMode.suggestedIndentForBufferRow(bufferRow) - # Public: Indents all the rows between two buffer rows. + # Indents all the rows between two buffer rows. # # startRow - The row {Number} to start at # endRow - The row {Number} to end at autoIndentBufferRows: (startRow, endRow) -> @languageMode.autoIndentBufferRows(startRow, endRow) - # Public: Given a buffer row, this indents it. + # Given a buffer row, this indents it. # # bufferRow - The row {Number} autoIndentBufferRow: (bufferRow) -> @languageMode.autoIndentBufferRow(bufferRow) - # Public: Given a buffer row, this increases the indentation. + # Given a buffer row, this increases the indentation. # # bufferRow - The row {Number} autoIncreaseIndentForBufferRow: (bufferRow) -> @languageMode.autoIncreaseIndentForBufferRow(bufferRow) - # Public: Given a buffer row, this decreases the indentation. + # Given a buffer row, this decreases the indentation. # # bufferRow - The row {Number} autoDecreaseIndentForRow: (bufferRow) -> @languageMode.autoDecreaseIndentForBufferRow(bufferRow) - # Public: Wraps the lines between two rows in comments. + # Wraps the lines between two rows in comments. # # If the language doesn't have comments, nothing happens. # @@ -790,7 +636,7 @@ class EditSession toggleLineCommentsForBufferRows: (start, end) -> @languageMode.toggleLineCommentsForBufferRows(start, end) - # Public: Moves the selected line up one row. + # Moves the selected line up one row. moveLineUp: -> selection = @getSelectedBufferRange() return if selection.start.row is 0 @@ -824,7 +670,7 @@ class EditSession @setSelectedBufferRange(selection.translate([-1]), preserveFolds: true) - # Public: Moves the selected line down one row. + # Moves the selected line down one row. moveLineDown: -> selection = @getSelectedBufferRange() lastRow = @buffer.getLastRow() @@ -862,7 +708,7 @@ class EditSession @setSelectedBufferRange(selection.translate([1]), preserveFolds: true) - # Public: Duplicates the current line. + # Duplicates the current line. # # If more than one cursor is present, only the most recently added one is considered. duplicateLine: -> @@ -888,7 +734,6 @@ class EditSession @setCursorScreenPosition(@getCursorScreenPosition().translate([1])) @foldCurrentRow() if cursorRowFolded - ### Internal ### mutateSelectedText: (fn) -> @@ -910,225 +755,124 @@ class EditSession ### Public ### - # Public: Constructs a new marker at the given screen range. - # - # range - The marker {Range} (representing the distance between the head and tail) - # options - Options to pass to the {BufferMarker} constructor - # - # Returns a {Number} representing the new marker's ID. + # {Delegates to: DisplayBuffer.markScreenRange} markScreenRange: (args...) -> @displayBuffer.markScreenRange(args...) - # Public: Constructs a new marker at the given buffer range. - # - # range - The marker {Range} (representing the distance between the head and tail) - # options - Options to pass to the {BufferMarker} constructor - # - # Returns a {Number} representing the new marker's ID. + # {Delegates to: DisplayBuffer.markBufferRange} markBufferRange: (args...) -> @displayBuffer.markBufferRange(args...) - # Public: Constructs a new marker at the given screen position. - # - # range - The marker {Range} (representing the distance between the head and tail) - # options - Options to pass to the {BufferMarker} constructor - # - # Returns a {Number} representing the new marker's ID. + # {Delegates to: DisplayBuffer.markScreenPosition} markScreenPosition: (args...) -> @displayBuffer.markScreenPosition(args...) - # Public: Constructs a new marker at the given buffer position. - # - # range - The marker {Range} (representing the distance between the head and tail) - # options - Options to pass to the {BufferMarker} constructor - # - # Returns a {Number} representing the new marker's ID. + # {Delegates to: DisplayBuffer.markBufferPosition} markBufferPosition: (args...) -> @displayBuffer.markBufferPosition(args...) - # Public: Removes the marker with the given id. - # - # id - The {Number} of the ID to remove + # {Delegates to: DisplayBuffer.destroyMarker} destroyMarker: (args...) -> @displayBuffer.destroyMarker(args...) - # Public: Gets the number of markers in the buffer. - # - # Returns a {Number}. + # {Delegates to: TextBuffer.destroyMarker} getMarkerCount: -> @buffer.getMarkerCount() - # Public: Gets the screen range of the display marker. - # - # id - The {Number} of the ID to check - # - # Returns a {Range}. + # {Delegates to: DisplayBuffer.getMarkerScreenRange} getMarkerScreenRange: (args...) -> @displayBuffer.getMarkerScreenRange(args...) - # Public: Modifies the screen range of the display marker. - # - # id - The {Number} of the ID to change - # screenRange - The new {Range} to use - # options - A hash of options matching those found in {BufferMarker.setRange} + # {Delegates to: DisplayBuffer.setMarkerScreenRange} setMarkerScreenRange: (args...) -> @displayBuffer.setMarkerScreenRange(args...) - # Public: Gets the buffer range of the display marker. - # - # id - The {Number} of the ID to check - # - # Returns a {Range}. + # {Delegates to: DisplayBuffer.getMarkerBufferRange} getMarkerBufferRange: (args...) -> @displayBuffer.getMarkerBufferRange(args...) - # Public: Modifies the buffer range of the display marker. - # - # id - The {Number} of the ID to check - # screenRange - The new {Range} to use - # options - A hash of options matching those found in {BufferMarker.setRange} + # {Delegates to: DisplayBuffer.setMarkerBufferRange} setMarkerBufferRange: (args...) -> @displayBuffer.setMarkerBufferRange(args...) - # Public: Retrieves the screen position of the marker's head. - # - # id - The {Number} of the ID to check - # - # Returns a {Point}. + # {Delegates to: DisplayBuffer.getMarkerScreenPosition} getMarkerScreenPosition: (args...) -> @displayBuffer.getMarkerScreenPosition(args...) - # Public: Retrieves the buffer position of the marker's head. - # - # id - The {Number} of the ID to check - # - # Returns a {Point}. + # {Delegates to: DisplayBuffer.getMarkerBufferPosition} getMarkerBufferPosition: (args...) -> @displayBuffer.getMarkerBufferPosition(args...) - # Public: Retrieves the screen position of the marker's head. - # - # id - The {Number} of the ID to check - # - # Returns a {Point}. + # {Delegates to: DisplayBuffer.getMarkerHeadScreenPosition} getMarkerHeadScreenPosition: (args...) -> @displayBuffer.getMarkerHeadScreenPosition(args...) - # Public: Sets the screen position of the marker's head. - # - # id - The {Number} of the ID to change - # screenRange - The new {Point} to use - # options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition} + # {Delegates to: DisplayBuffer.setMarkerHeadScreenPosition} setMarkerHeadScreenPosition: (args...) -> @displayBuffer.setMarkerHeadScreenPosition(args...) - # Public: Retrieves the buffer position of the marker's head. - # - # id - The {Number} of the ID to check - # - # Returns a {Point}. + # {Delegates to: DisplayBuffer.getMarkerHeadBufferPosition} getMarkerHeadBufferPosition: (args...) -> @displayBuffer.getMarkerHeadBufferPosition(args...) - # Public: Sets the buffer position of the marker's head. - # - # id - The {Number} of the ID to check - # screenRange - The new {Point} to use - # options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition} + # {Delegates to: DisplayBuffer.setMarkerHeadBufferPosition} setMarkerHeadBufferPosition: (args...) -> @displayBuffer.setMarkerHeadBufferPosition(args...) - # Public: Retrieves the screen position of the marker's tail. - # - # id - The {Number} of the ID to check - # - # Returns a {Point}. + # {Delegates to: DisplayBuffer.getMarkerTailScreenPosition} getMarkerTailScreenPosition: (args...) -> @displayBuffer.getMarkerTailScreenPosition(args...) - # Public: Sets the screen position of the marker's tail. - # - # id - The {Number} of the ID to change - # screenRange - The new {Point} to use - # options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition} + # {Delegates to: DisplayBuffer.setMarkerTailScreenPosition} setMarkerTailScreenPosition: (args...) -> @displayBuffer.setMarkerTailScreenPosition(args...) - # Public: Retrieves the buffer position of the marker's tail. - # - # id - The {Number} of the ID to check - # - # Returns a {Point}. + # {Delegates to: DisplayBuffer.getMarkerTailBufferPosition} getMarkerTailBufferPosition: (args...) -> @displayBuffer.getMarkerTailBufferPosition(args...) - # Public: Sets the buffer position of the marker's tail. - # - # id - The {Number} of the ID to change - # screenRange - The new {Point} to use - # options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition} + # {Delegates to: DisplayBuffer.setMarkerTailBufferPosition} setMarkerTailBufferPosition: (args...) -> @displayBuffer.setMarkerTailBufferPosition(args...) - # Public: Sets a callback to be fired whenever a marker is changed. - # - # id - A {Number} representing the marker to watch - # callback - A {Function} to execute + # {Delegates to: DisplayBuffer.observeMarker} observeMarker: (args...) -> @displayBuffer.observeMarker(args...) - # Public: Sets the marker's tail to the same position as the marker's head. - # - # This only works if there isn't already a tail position. - # - # id - A {Number} representing the marker to change - # - # Returns a {Point} representing the new tail position. + # {Delegates to: DisplayBuffer.placeMarkerTail} placeMarkerTail: (args...) -> @displayBuffer.placeMarkerTail(args...) - # Public: Removes the tail from the marker. - # - # id - A {Number} representing the marker to change + # {Delegates to: DisplayBuffer.clearMarkerTail} clearMarkerTail: (args...) -> @displayBuffer.clearMarkerTail(args...) - # Public: Identifies if the ending position of a marker is greater than the starting position. - # - # This can happen when, for example, you highlight text "up" in a {Buffer}. - # - # id - A {Number} representing the marker to check - # - # Returns a {Boolean}. + # {Delegates to: DisplayBuffer.isMarkerReversed} isMarkerReversed: (args...) -> @displayBuffer.isMarkerReversed(args...) - # Public: Identifies if the marker's head position is equal to its tail. - # - # id - A {Number} representing the marker to check - # - # Returns a {Boolean}. + # {Delegates to: DisplayBuffer.isMarkerRangeEmpty}. isMarkerRangeEmpty: (args...) -> @displayBuffer.isMarkerRangeEmpty(args...) - # Public: Returns `true` if there are multiple cursors in the edit session. + # Returns `true` if there are multiple cursors in the edit session. # # Returns a {Boolean}. hasMultipleCursors: -> @getCursors().length > 1 - # Public: Retrieves all the cursors. + # Retrieves all the cursors. # # Returns an {Array} of {Cursor}s. getCursors: -> new Array(@cursors...) - # Public: Retrieves the most recently added cursor. + # Retrieves the most recently added cursor. # # Returns a {Cursor}. getCursor: -> _.last(@cursors) - # Public: Adds a cursor at the provided `screenPosition`. + # Adds a cursor at the provided `screenPosition`. # # screenPosition - An {Array} of two numbers: the screen row, and the screen column. # @@ -1137,7 +881,7 @@ class EditSession marker = @markScreenPosition(screenPosition, invalidationStrategy: 'never') @addSelection(marker).cursor - # Public: Adds a cursor at the provided `bufferPosition`. + # Adds a cursor at the provided `bufferPosition`. # # bufferPosition - An {Array} of two numbers: the buffer row, and the buffer column. # @@ -1146,7 +890,7 @@ class EditSession marker = @markBufferPosition(bufferPosition, invalidationStrategy: 'never') @addSelection(marker).cursor - # Public: Adds a cursor to the `EditSession`. + # Adds a cursor to the `EditSession`. # # marker - The marker where the cursor should be added # @@ -1157,7 +901,7 @@ class EditSession @trigger 'cursor-added', cursor cursor - # Public: Removes a cursor from the `EditSession`. + # Removes a cursor from the `EditSession`. # # cursor - The cursor to remove # @@ -1165,7 +909,7 @@ class EditSession removeCursor: (cursor) -> _.remove(@cursors, cursor) - # Public: Creates a new selection at the given marker. + # Creates a new selection at the given marker. # # marker - The marker to highlight # options - A hash of options that pertain to the {Selection} constructor. @@ -1187,7 +931,7 @@ class EditSession @trigger 'selection-added', selection selection - # Public: Given a buffer range, this adds a new selection for it. + # Given a buffer range, this adds a new selection for it. # # bufferRange - A {Range} in the buffer # options - A hash of options @@ -1198,14 +942,14 @@ class EditSession marker = @markBufferRange(bufferRange, options) @addSelection(marker, options) - # Public: Given a buffer range, this removes all previous selections and creates a new selection for it. + # Given a buffer range, this removes all previous selections and creates a new selection for it. # # bufferRange - A {Range} in the buffer # options - A hash of options setSelectedBufferRange: (bufferRange, options) -> @setSelectedBufferRanges([bufferRange], options) - # Public: Given an array of buffer ranges, this removes all previous selections and creates new selections for them. + # Given an array of buffer ranges, this removes all previous selections and creates new selections for them. # # bufferRanges - An {Array} of {Range}s in the buffer # options - A hash of options @@ -1223,13 +967,13 @@ class EditSession @addSelectionForBufferRange(bufferRange, options) @mergeIntersectingSelections(options) - # Public: Unselects a given selection. + # Unselects a given selection. # # selection - The {Selection} to remove. removeSelection: (selection) -> _.remove(@selections, selection) - # Public: Clears every selection. TODO + # Clears every selection. TODO clearSelections: -> @consolidateSelections() @getSelection().clear() @@ -1242,12 +986,12 @@ class EditSession else false - # Public: Gets all the selections. + # Gets all the selections. # # Returns an {Array} of {Selection}s. getSelections: -> new Array(@selections...) - # Public: Gets the selection at the specified index. + # Gets the selection at the specified index. # # index - The id {Number} of the selection # @@ -1256,13 +1000,13 @@ class EditSession index ?= @selections.length - 1 @selections[index] - # Public: Gets the last selection, _i.e._ the most recently added. + # Gets the last selection, _i.e._ the most recently added. # # Returns a {Selection}. getLastSelection: -> _.last(@selections) - # Public: Gets all selections, ordered by their position in the buffer. + # Gets all selections, ordered by their position in the buffer. # # Returns an {Array} of {Selection}s. getSelectionsOrderedByBufferPosition: -> @@ -1271,13 +1015,13 @@ class EditSession bRange = b.getBufferRange() aRange.end.compare(bRange.end) - # Public: Gets the very last selection, as it's ordered in the buffer. + # Gets the very last selection, as it's ordered in the buffer. # # Returns a {Selection}. getLastSelectionInBuffer: -> _.last(@getSelectionsOrderedByBufferPosition()) - # Public: Determines if a given buffer range is included in a selection. + # Determines if a given buffer range is included in a {Selection}. # # bufferRange - The {Range} you're checking against # @@ -1286,7 +1030,7 @@ class EditSession _.any @getSelections(), (selection) -> selection.intersectsBufferRange(bufferRange) - # Public: Moves every cursor to a given screen position. + # Moves every cursor to a given screen position. # # position - An {Array} of two numbers: the screen row, and the screen column. # options - An object with properties based on {Cursor.setScreenPosition} @@ -1294,19 +1038,19 @@ class EditSession setCursorScreenPosition: (position, options) -> @moveCursors (cursor) -> cursor.setScreenPosition(position, options) - # Public: Gets the current screen position. + # Gets the current screen position of the most recently added {Cursor}. # # Returns an {Array} of two numbers: the screen row, and the screen column. getCursorScreenPosition: -> @getCursor().getScreenPosition() - # Public: Gets the current cursor's screen row. + # Gets the screen row of the most recently added {Cursor}. # - # Returns the screen row. + # Returns the screen row {Number}. getCursorScreenRow: -> @getCursor().getScreenRow() - # Public: Moves every cursor to a given buffer position. + # Moves every cursor to a given buffer position. # # position - An {Array} of two numbers: the buffer row, and the buffer column. # options - An object with properties based on {Cursor.setBufferPosition} @@ -1314,39 +1058,39 @@ class EditSession setCursorBufferPosition: (position, options) -> @moveCursors (cursor) -> cursor.setBufferPosition(position, options) - # Public: Gets the current buffer position of the cursor. + # Gets the current buffer position of the most recently added {Cursor}. # # Returns an {Array} of two numbers: the buffer row, and the buffer column. getCursorBufferPosition: -> @getCursor().getBufferPosition() - # Public: Gets the screen range of the most recently added {Selection}. + # Gets the screen range of the most recently added {Selection}. # # Returns a {Range}. getSelectedScreenRange: -> @getLastSelection().getScreenRange() - # Public: Gets the buffer range of the most recently added {Selection}. + # Gets the buffer range of the most recently added {Selection}. # # Returns a {Range}. getSelectedBufferRange: -> @getLastSelection().getBufferRange() - # Public: Gets the buffer ranges of all the {Selection}s. + # Gets the buffer ranges of all the {Selection}s. # - # This is ordered by their buffer position. + # This is ordered by their position in the file itself. # # Returns an {Array} of {Range}s. getSelectedBufferRanges: -> selection.getBufferRange() for selection in @getSelectionsOrderedByBufferPosition() - # Public: Gets the currently selected text. + # Gets the selected text of the most recently added {Selection}. # # Returns a {String}. getSelectedText: -> @getLastSelection().getText() - # Public: Given a buffer range, this retrieves the text in that range. + # Given a buffer range, this retrieves the text in that range. # # range - The {Range} you're interested in # @@ -1354,7 +1098,7 @@ class EditSession getTextInBufferRange: (range) -> @buffer.getTextInRange(range) - # Public: Retrieves the range for the current paragraph. + # Retrieves the range for the current paragraph. # # A paragraph is defined as a block of text surrounded by empty lines. # @@ -1362,7 +1106,7 @@ class EditSession getCurrentParagraphBufferRange: -> @getCursor().getCurrentParagraphBufferRange() - # Public: Gets the word located under the cursor. + # Gets the word located under the most recently added {Cursor}. # # options - An object with properties based on {Cursor.getBeginningOfCurrentWordBufferPosition}. # @@ -1370,51 +1114,51 @@ class EditSession getWordUnderCursor: (options) -> @getTextInBufferRange(@getCursor().getCurrentWordBufferRange(options)) - # Public: Moves every cursor up one row. + # Moves every cursor up one row. moveCursorUp: (lineCount) -> @moveCursors (cursor) -> cursor.moveUp(lineCount) - # Public: Moves every cursor down one row. + # Moves every cursor down one row. moveCursorDown: (lineCount) -> @moveCursors (cursor) -> cursor.moveDown(lineCount) - # Public: Moves every cursor left one column. + # Moves every cursor left one column. moveCursorLeft: -> @moveCursors (cursor) -> cursor.moveLeft() - # Public: Moves every cursor right one column. + # Moves every cursor right one column. moveCursorRight: -> @moveCursors (cursor) -> cursor.moveRight() - # Public: Moves every cursor to the top of the buffer. + # Moves every cursor to the top of the buffer. moveCursorToTop: -> @moveCursors (cursor) -> cursor.moveToTop() - # Public: Moves every cursor to the bottom of the buffer. + # Moves every cursor to the bottom of the buffer. moveCursorToBottom: -> @moveCursors (cursor) -> cursor.moveToBottom() - # Public: Moves every cursor to the beginning of the line. + # Moves every cursor to the beginning of the line. moveCursorToBeginningOfLine: -> @moveCursors (cursor) -> cursor.moveToBeginningOfLine() - # Public: Moves every cursor to the first non-whitespace character of the line. + # Moves every cursor to the first non-whitespace character of the line. moveCursorToFirstCharacterOfLine: -> @moveCursors (cursor) -> cursor.moveToFirstCharacterOfLine() - # Public: Moves every cursor to the end of the line. + # Moves every cursor to the end of the line. moveCursorToEndOfLine: -> @moveCursors (cursor) -> cursor.moveToEndOfLine() - # Public: Moves every cursor to the beginning of the current word. + # Moves every cursor to the beginning of the current word. moveCursorToBeginningOfWord: -> @moveCursors (cursor) -> cursor.moveToBeginningOfWord() - # Public: Moves every cursor to the end of the current word. + # Moves every cursor to the end of the current word. moveCursorToEndOfWord: -> @moveCursors (cursor) -> cursor.moveToEndOfWord() - # Public: Moves every cursor to the beginning of the next word. + # Moves every cursor to the beginning of the next word. moveCursorToBeginningOfNextWord: -> @moveCursors (cursor) -> cursor.moveToBeginningOfNextWord() @@ -1423,7 +1167,7 @@ class EditSession fn(cursor) for cursor in @getCursors() @mergeCursors() - # Public: Selects the text from the current cursor position to a given screen position. + # Selects the text from the current cursor position to a given screen position. # # position - An instance of {Point}, with a given `row` and `column`. selectToScreenPosition: (position) -> @@ -1431,55 +1175,55 @@ class EditSession lastSelection.selectToScreenPosition(position) @mergeIntersectingSelections(reverse: lastSelection.isReversed()) - # Public: Selects the text one position right of the cursor. + # Selects the text one position right of the cursor. selectRight: -> @expandSelectionsForward (selection) => selection.selectRight() - # Public: Selects the text one position left of the cursor. + # Selects the text one position left of the cursor. selectLeft: -> @expandSelectionsBackward (selection) => selection.selectLeft() - # Public: Selects all the text one position above the cursor. + # Selects all the text one position above the cursor. selectUp: -> @expandSelectionsBackward (selection) => selection.selectUp() - # Public: Selects all the text one position below the cursor. + # Selects all the text one position below the cursor. selectDown: -> @expandSelectionsForward (selection) => selection.selectDown() - # Public: Selects all the text from the current cursor position to the top of the buffer. + # Selects all the text from the current cursor position to the top of the buffer. selectToTop: -> @expandSelectionsBackward (selection) => selection.selectToTop() - # Public: Selects all the text in the buffer. + # Selects all the text in the buffer. selectAll: -> @expandSelectionsForward (selection) => selection.selectAll() - # Public: Selects all the text from the current cursor position to the bottom of the buffer. + # Selects all the text from the current cursor position to the bottom of the buffer. selectToBottom: -> @expandSelectionsForward (selection) => selection.selectToBottom() - # Public: Selects all the text from the current cursor position to the beginning of the line. + # Selects all the text from the current cursor position to the beginning of the line. selectToBeginningOfLine: -> @expandSelectionsBackward (selection) => selection.selectToBeginningOfLine() - # Public: Selects all the text from the current cursor position to the end of the line. + # Selects all the text from the current cursor position to the end of the line. selectToEndOfLine: -> @expandSelectionsForward (selection) => selection.selectToEndOfLine() - # Public: Selects the current line. + # Selects the current line. selectLine: -> @expandSelectionsForward (selection) => selection.selectLine() - # Public: Moves the current selection down one row. + # Moves the current selection down one row. addSelectionBelow: -> @expandSelectionsForward (selection) => selection.addSelectionBelow() - # Public: Moves the current selection up one row. + # Moves the current selection up one row. addSelectionAbove: -> @expandSelectionsBackward (selection) => selection.addSelectionAbove() - # Public: Transposes the current text selections. + # Transposes the current text selections. # # This only works if there is more than one selection. Each selection is transferred # to the position of the selection after it. The last selection is transferred to the @@ -1495,15 +1239,15 @@ class EditSession else selection.insertText selection.getText().split('').reverse().join('') - # Public: Turns the current selection into upper case. + # Turns the current selection into upper case. upperCase: -> @replaceSelectedText selectWordIfEmpty:true, (text) => text.toUpperCase() - # Public: Turns the current selection into lower case. + # Turns the current selection into lower case. lowerCase: -> @replaceSelectedText selectWordIfEmpty:true, (text) => text.toLowerCase() - # Public: Joins the current line with the one below it. + # Joins the current line with the one below it. # # Multiple cursors are considered equally. If there's a selection in the editor, # all the lines are joined together. @@ -1513,19 +1257,19 @@ class EditSession expandLastSelectionOverLine: -> @getLastSelection().expandOverLine() - # Public: Selects all the text from the current cursor position to the beginning of the word. + # Selects all the text from the current cursor position to the beginning of the word. selectToBeginningOfWord: -> @expandSelectionsBackward (selection) => selection.selectToBeginningOfWord() - # Public: Selects all the text from the current cursor position to the end of the word. + # Selects all the text from the current cursor position to the end of the word. selectToEndOfWord: -> @expandSelectionsForward (selection) => selection.selectToEndOfWord() - # Public: Selects all the text from the current cursor position to the beginning of the next word. + # Selects all the text from the current cursor position to the beginning of the next word. selectToBeginningOfNextWord: -> @expandSelectionsForward (selection) => selection.selectToBeginningOfNextWord() - # Public: Selects the current word. + # Selects the current word. selectWord: -> @expandSelectionsForward (selection) => selection.selectWord() @@ -1539,7 +1283,7 @@ class EditSession else false - # Public: Given a buffer position, this finds all markers that contain the position. + # Given a buffer position, this finds all markers that contain the position. # # bufferPosition - A {Point} to check # @@ -1589,28 +1333,51 @@ class EditSession @setCursorBufferPosition(cursorPosition) if cursorPosition cursorPosition = null - # Public: Retrieves the current {EditSession}'s grammar. + # Retrieves the current {EditSession}'s grammar. # # Returns a {String} indicating the language's grammar rules. getGrammar: -> @displayBuffer.getGrammar() - # Public: Sets the current {EditSession}'s grammar. + # Sets the current {EditSession}'s grammar. # # grammar - A {String} indicating the language's grammar rules. setGrammar: (grammar) -> @displayBuffer.setGrammar(grammar) - # Public: Reloads the current grammar. + # Reloads the current grammar. reloadGrammar: -> @displayBuffer.reloadGrammar() - # Internal: + ### Internal ### + + transact: (fn) -> + isNewTransaction = @buffer.transact() + oldSelectedRanges = @getSelectedBufferRanges() + @pushOperation + undo: (editSession) -> + editSession?.setSelectedBufferRanges(oldSelectedRanges) + if fn + result = fn() + @commit() if isNewTransaction + result + + commit: -> + newSelectedRanges = @getSelectedBufferRanges() + @pushOperation + redo: (editSession) -> + editSession?.setSelectedBufferRanges(newSelectedRanges) + @buffer.commit() + + abort: -> + @buffer.abort() + + logScreenLines: (start, end) -> @displayBuffer.logLines(start, end) + handleGrammarChange: -> @unfoldAll() @trigger 'grammar-changed' - # Internal: getDebugSnapshot: -> [ @displayBuffer.getDebugSnapshot() diff --git a/src/app/text-buffer.coffee b/src/app/text-buffer.coffee index f50df8805..1bd928cd9 100644 --- a/src/app/text-buffer.coffee +++ b/src/app/text-buffer.coffee @@ -373,10 +373,12 @@ class Buffer # Internal: transact: (fn) -> @undoManager.transact(fn) + # Public: Undos the last operation. # # editSession - The {EditSession} associated with the buffer. undo: (editSession) -> @undoManager.undo(editSession) + # Public: Redos the last operation. # # editSession - The {EditSession} associated with the buffer. From 3c5b721177b78d03e3acfa009227cc050c913f33 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 16:04:49 -0700 Subject: [PATCH 38/91] Update Editor --- src/app/editor.coffee | 522 +++++++++++++----------------------------- 1 file changed, 159 insertions(+), 363 deletions(-) diff --git a/src/app/editor.coffee b/src/app/editor.coffee index ff4621e24..a7ee69b1e 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -65,7 +65,9 @@ class Editor extends View redrawOnReattach: false bottomPaddingInLines: 10 - # Public: The constructor for setting up an `Editor` instance. + ### Public ### + + # The constructor for setting up an `Editor` instance. # # editSessionOrOptions - Either an {EditSession}, or an object with one property, `mini`. # If `mini` is `true`, a "miniature" `EditSession` is constructed. @@ -190,482 +192,314 @@ class Editor extends View do (name, method) => @command name, (e) => method.call(this, e); false - ### Public ### - - # Public: Retrieves a single cursor - # - # Returns a {Cursor}. + # {Delegates to: EditSession.getCursor} getCursor: -> @activeEditSession.getCursor() - # Public: Retrieves an array of all the cursors. - # - # Returns a {[Cursor]}. + # {Delegates to: EditSession.getCursors} getCursors: -> @activeEditSession.getCursors() - # Public: Adds a cursor at the provided `screenPosition`. - # - # screenPosition - An {Array} of two numbers: the screen row, and the screen column. - # - # Returns the new {Cursor}. + # {Delegates to: EditSession.addCursorAtScreenPosition} addCursorAtScreenPosition: (screenPosition) -> @activeEditSession.addCursorAtScreenPosition(screenPosition) - # Public: Adds a cursor at the provided `bufferPosition`. - # - # bufferPosition - An {Array} of two numbers: the buffer row, and the buffer column. - # - # Returns the new {Cursor}. + # {Delegates to: EditSession.addCursorAtBufferPosition} addCursorAtBufferPosition: (bufferPosition) -> @activeEditSession.addCursorAtBufferPosition(bufferPosition) - # Public: Moves every cursor up one row. + # {Delegates to: EditSession.moveCursorUp} moveCursorUp: -> @activeEditSession.moveCursorUp() - # Public: Moves every cursor down one row. + # {Delegates to: EditSession.moveCursorDown} moveCursorDown: -> @activeEditSession.moveCursorDown() - # Public: Moves every cursor left one column. + # {Delegates to: EditSession.moveCursorLeft} moveCursorLeft: -> @activeEditSession.moveCursorLeft() - # Public: Moves every cursor right one column. + # {Delegates to: EditSession.moveCursorRight} moveCursorRight: -> @activeEditSession.moveCursorRight() - # Public: Moves every cursor to the beginning of the current word. + # {Delegates to: EditSession.moveCursorToBeginningOfWord} moveCursorToBeginningOfWord: -> @activeEditSession.moveCursorToBeginningOfWord() - # Public: Moves every cursor to the end of the current word. + # {Delegates to: EditSession.moveCursorToEndOfWord} moveCursorToEndOfWord: -> @activeEditSession.moveCursorToEndOfWord() - # Public: Moves the cursor to the beginning of the next word. + # {Delegates to: EditSession.moveCursorToBeginningOfNextWord} moveCursorToBeginningOfNextWord: -> @activeEditSession.moveCursorToBeginningOfNextWord() - # Public: Moves every cursor to the top of the buffer. + # {Delegates to: EditSession.moveCursorToTop} moveCursorToTop: -> @activeEditSession.moveCursorToTop() - # Public: Moves every cursor to the bottom of the buffer. + # {Delegates to: EditSession.moveCursorToBottom} moveCursorToBottom: -> @activeEditSession.moveCursorToBottom() - # Public: Moves every cursor to the beginning of the line. + # {Delegates to: EditSession.moveCursorToBeginningOfLine} moveCursorToBeginningOfLine: -> @activeEditSession.moveCursorToBeginningOfLine() - # Public: Moves every cursor to the first non-whitespace character of the line. + # {Delegates to: EditSession.moveCursorToFirstCharacterOfLine} moveCursorToFirstCharacterOfLine: -> @activeEditSession.moveCursorToFirstCharacterOfLine() - # Public: Moves every cursor to the end of the line. + # {Delegates to: EditSession.moveCursorToEndOfLine} moveCursorToEndOfLine: -> @activeEditSession.moveCursorToEndOfLine() - # Public: Moves the selected line up one row. + # {Delegates to: EditSession.moveLineUp} moveLineUp: -> @activeEditSession.moveLineUp() - # Public: Moves the selected line down one row. + # {Delegates to: EditSession.moveLineDown} moveLineDown: -> @activeEditSession.moveLineDown() - # Public: Sets the cursor based on a given screen position. - # - # position - An {Array} of two numbers: the screen row, and the screen column. - # options - An object with properties based on {Cursor.setScreenPosition}. - # + # {Delegates to: EditSession.setCursorScreenPosition} setCursorScreenPosition: (position, options) -> @activeEditSession.setCursorScreenPosition(position, options) - # Public: Duplicates the current line. - # - # If more than one cursor is present, only the most recently added one is considered. + # {Delegates to: EditSession.duplicateLine} duplicateLine: -> @activeEditSession.duplicateLine() - # Public: Joins the current line with the one below it. - # - # Multiple cursors are considered equally. If there's a selection in the editor, - # all the lines are joined together. + # {Delegates to: EditSession.joinLine} joinLine: -> @activeEditSession.joinLine() - # Public: Gets the current screen position. - # - # Returns an {Array} of two numbers: the screen row, and the screen column. + # {Delegates to: EditSession.getCursorScreenPosition} getCursorScreenPosition: -> @activeEditSession.getCursorScreenPosition() - # Public: Gets the current screen row. - # - # Returns a {Number}. + # {Delegates to: EditSession.getCursorScreenRow} getCursorScreenRow: -> @activeEditSession.getCursorScreenRow() - # Public: Sets the cursor based on a given buffer position. - # - # position - An {Array} of two numbers: the buffer row, and the buffer column. - # options - An object with properties based on {Cursor.setBufferPosition}. - # + # {Delegates to: EditSession.setCursorBufferPosition} setCursorBufferPosition: (position, options) -> @activeEditSession.setCursorBufferPosition(position, options) - # Public: Gets the current buffer position of the cursor. - # - # Returns an {Array} of two numbers: the buffer row, and the buffer column. + # {Delegates to: EditSession.getCursorBufferPosition} getCursorBufferPosition: -> @activeEditSession.getCursorBufferPosition() - # Public: Retrieves the range for the current paragraph. - # - # A paragraph is defined as a block of text surrounded by empty lines. - # - # Returns a {Range}. + # {Delegates to: EditSession.getCurrentParagraphBufferRange} getCurrentParagraphBufferRange: -> @activeEditSession.getCurrentParagraphBufferRange() - # Public: Gets the word located under the cursor. - # - # options - An object with properties based on {Cursor.getBeginningOfCurrentWordBufferPosition}. - # - # Returns a {String}. + # {Delegates to: EditSession.getWordUnderCursor} getWordUnderCursor: (options) -> @activeEditSession.getWordUnderCursor(options) - # Public: Gets the selection at the specified index. - # - # index - The id {Number} of the selection - # - # Returns a {Selection}. + # {Delegates to: EditSession.getSelection} getSelection: (index) -> @activeEditSession.getSelection(index) - # Public: Gets the last selection, _i.e._ the most recently added. - # - # Returns a {Selection}. + # {Delegates to: EditSession.getSelections} getSelections: -> @activeEditSession.getSelections() - # Public: Gets all selections, ordered by their position in the buffer. - # - # Returns an {Array} of {Selection}s. + # {Delegates to: EditSession.getSelectionsOrderedByBufferPosition} getSelectionsOrderedByBufferPosition: -> @activeEditSession.getSelectionsOrderedByBufferPosition() - # Public: Gets the very last selection, as it's ordered in the buffer. - # - # Returns a {Selection}. + # {Delegates to: EditSession.getLastSelectionInBuffer} getLastSelectionInBuffer: -> @activeEditSession.getLastSelectionInBuffer() - # Public: Gets the currently selected text. - # - # Returns a {String}. + # {Delegates to: EditSession.getSelectedText} getSelectedText: -> @activeEditSession.getSelectedText() - # Public: Gets the buffer ranges of all the {Selection}s. - # - # This is ordered by their buffer position. - # - # Returns an {Array} of {Range}s. + # {Delegates to: EditSession.getSelectedBufferRanges} getSelectedBufferRanges: -> @activeEditSession.getSelectedBufferRanges() - # Public: Gets the buffer range of the most recently added {Selection}. - # - # Returns a {Range}. + # {Delegates to: EditSession.getSelectedBufferRange} getSelectedBufferRange: -> @activeEditSession.getSelectedBufferRange() - # Public: Given a buffer range, this removes all previous selections and creates a new selection for it. - # - # bufferRange - A {Range} in the buffer - # options - A hash of options + # {Delegates to: EditSession.setSelectedBufferRange} setSelectedBufferRange: (bufferRange, options) -> @activeEditSession.setSelectedBufferRange(bufferRange, options) - # Public: Given an array of buffer ranges, this removes all previous selections and creates new selections for them. - # - # bufferRanges - An {Array} of {Range}s in the buffer - # options - A hash of options + # {Delegates to: EditSession.setSelectedBufferRanges} setSelectedBufferRanges: (bufferRanges, options) -> @activeEditSession.setSelectedBufferRanges(bufferRanges, options) - # Public: Given a buffer range, this adds a new selection for it. - # - # bufferRange - A {Range} in the buffer - # options - A hash of options - # - # Returns the new {Selection}. + # {Delegates to: EditSession.addSelectionForBufferRange} addSelectionForBufferRange: (bufferRange, options) -> @activeEditSession.addSelectionForBufferRange(bufferRange, options) - # Public: Selects the text one position right of the cursor. + # {Delegates to: EditSession.selectRight} selectRight: -> @activeEditSession.selectRight() - # Public: Selects the text one position left of the cursor. + # {Delegates to: EditSession.selectLeft} selectLeft: -> @activeEditSession.selectLeft() - # Public: Selects all the text one position above the cursor. + # {Delegates to: EditSession.selectUp} selectUp: -> @activeEditSession.selectUp() - # Public: Selects all the text one position below the cursor. + # {Delegates to: EditSession.selectDown} selectDown: -> @activeEditSession.selectDown() - # Public: Selects all the text from the current cursor position to the top of the buffer. + # {Delegates to: EditSession.selectToTop} selectToTop: -> @activeEditSession.selectToTop() - # Public: Selects all the text from the current cursor position to the bottom of the buffer. + # {Delegates to: EditSession.selectToBottom} selectToBottom: -> @activeEditSession.selectToBottom() - # Public: Selects all the text in the buffer. + # {Delegates to: EditSession.selectAll} selectAll: -> @activeEditSession.selectAll() - # Public: Selects all the text from the current cursor position to the beginning of the line. + # {Delegates to: EditSession.selectToBeginningOfLine} selectToBeginningOfLine: -> @activeEditSession.selectToBeginningOfLine() - # Public: Selects all the text from the current cursor position to the end of the line. + # {Delegates to: EditSession.selectToEndOfLine} selectToEndOfLine: -> @activeEditSession.selectToEndOfLine() - # Public: Moves the current selection down one row. + # {Delegates to: EditSession.addSelectionBelow} addSelectionBelow: -> @activeEditSession.addSelectionBelow() - # Public: Moves the current selection up one row. + # {Delegates to: EditSession.addSelectionAbove} addSelectionAbove: -> @activeEditSession.addSelectionAbove() - # Public: Selects all the text from the current cursor position to the beginning of the word. + # {Delegates to: EditSession.selectToBeginningOfWord} selectToBeginningOfWord: -> @activeEditSession.selectToBeginningOfWord() - # Public: Selects all the text from the current cursor position to the end of the word. + # {Delegates to: EditSession.selectToEndOfWord} selectToEndOfWord: -> @activeEditSession.selectToEndOfWord() - # Public: Selects all the text from the current cursor position to the beginning of the next word. + # {Delegates to: EditSession.selectToBeginningOfNextWord} selectToBeginningOfNextWord: -> @activeEditSession.selectToBeginningOfNextWord() - # Public: Selects the current word. + # {Delegates to: EditSession.selectWord} selectWord: -> @activeEditSession.selectWord() - # Public: Selects the current line. + # {Delegates to: EditSession.selectLine} selectLine: -> @activeEditSession.selectLine() - # Public: Selects the text from the current cursor position to a given position. - # - # position - An instance of {Point}, with a given `row` and `column`. + # {Delegates to: EditSession.selectToScreenPosition} selectToScreenPosition: (position) -> @activeEditSession.selectToScreenPosition(position) - # Public: Transposes the current text selections. - # - # This only works if there is more than one selection. Each selection is transferred - # to the position of the selection after it. The last selection is transferred to the - # position of the first. + + # {Delegates to: EditSession.transpose} transpose: -> @activeEditSession.transpose() - # Public: Turns the current selection into upper case. + # {Delegates to: EditSession.upperCase} upperCase: -> @activeEditSession.upperCase() - # Public: Turns the current selection into lower case. + # {Delegates to: EditSession.lowerCase} lowerCase: -> @activeEditSession.lowerCase() - # Public: Clears every selection. TODO + # {Delegates to: EditSession.clearSelections} clearSelections: -> @activeEditSession.clearSelections() - # Public: Performs a backspace, removing the character found behind the cursor position. + # {Delegates to: EditSession.backspace} backspace: -> @activeEditSession.backspace() - # Public: Performs a backspace to the beginning of the current word, removing characters found there. + # {Delegates to: EditSession.backspaceToBeginningOfWord} backspaceToBeginningOfWord: -> @activeEditSession.backspaceToBeginningOfWord() - # Public: Performs a backspace to the beginning of the current line, removing characters found there. + # {Delegates to: EditSession.backspaceToBeginningOfLine} backspaceToBeginningOfLine: -> @activeEditSession.backspaceToBeginningOfLine() - # Public: Performs a delete, removing the character found ahead the cursor position. + # {Delegates to: EditSession.delete} delete: -> @activeEditSession.delete() - # Public: Performs a delete to the end of the current word, removing characters found there. + # {Delegates to: EditSession.deleteToEndOfWord} deleteToEndOfWord: -> @activeEditSession.deleteToEndOfWord() - # Public: Performs a delete to the end of the current line, removing characters found there. + # {Delegates to: EditSession.deleteLine} deleteLine: -> @activeEditSession.deleteLine() - # Public: Performs a cut to the end of the current line. - # - # Characters are removed, but the text remains in the clipboard. + # {Delegates to: EditSession.cutToEndOfLine} cutToEndOfLine: -> @activeEditSession.cutToEndOfLine() - # Public: Inserts text at the current cursor positions. - # - # text - A {String} representing the text to insert. - # options - A set of options equivalent to {Selection.insertText}. + # {Delegates to: EditSession.insertText} insertText: (text, options) -> @activeEditSession.insertText(text, options) - # Public: Inserts a new line at the current cursor positions. + # {Delegates to: EditSession.insertNewline} insertNewline: -> @activeEditSession.insertNewline() - # Internal: - - consolidateSelections: (e) -> e.abortKeyBinding() unless @activeEditSession.consolidateSelections() - - # Public: Inserts a new line below the current cursor positions. + # {Delegates to: EditSession.insertNewlineBelow} insertNewlineBelow: -> @activeEditSession.insertNewlineBelow() - # Public: Inserts a new line above the current cursor positions. + # {Delegates to: EditSession.insertNewlineAbove} insertNewlineAbove: -> @activeEditSession.insertNewlineAbove() - # Public: Indents the current line. - # - # options - A set of options equivalent to {Selection.indent}. + # {Delegates to: EditSession.indent} indent: (options) -> @activeEditSession.indent(options) - # Public: TODO + # {Delegates to: EditSession.autoIndentSelectedRows} autoIndent: (options) -> @activeEditSession.autoIndentSelectedRows() - # Public: Indents the selected rows. + # {Delegates to: EditSession.indentSelectedRows} indentSelectedRows: -> @activeEditSession.indentSelectedRows() - # Public: Outdents the selected rows. + # {Delegates to: EditSession.outdentSelectedRows} outdentSelectedRows: -> @activeEditSession.outdentSelectedRows() - # Public: Cuts the selected text. + # {Delegates to: EditSession.cutSelectedText} cutSelection: -> @activeEditSession.cutSelectedText() - # Public: Copies the selected text. + # {Delegates to: EditSession.copySelectedText} copySelection: -> @activeEditSession.copySelectedText() - # Public: Pastes the text in the clipboard. - # - # options - A set of options equivalent to {Selection.insertText}. + # {Delegates to: EditSession.pasteText} paste: (options) -> @activeEditSession.pasteText(options) - # Public: Undos the last {Buffer} change. + # {Delegates to: EditSession.undo} undo: -> @activeEditSession.undo() - # Public: Redos the last {Buffer} change. + # {Delegates to: EditSession.redo} redo: -> @activeEditSession.redo() - # Public: Creates a new fold between two row numbers. - # - # startRow - The row {Number} to start folding at - # endRow - The row {Number} to end the fold - # - # Returns the new {Fold}. + # {Delegates to: EditSession.createFold} createFold: (startRow, endRow) -> @activeEditSession.createFold(startRow, endRow) - # Public: Folds the current row. + # {Delegates to: EditSession.foldCurrentRow} foldCurrentRow: -> @activeEditSession.foldCurrentRow() - # Public: Unfolds the current row. + # {Delegates to: EditSession.unfoldCurrentRow} unfoldCurrentRow: -> @activeEditSession.unfoldCurrentRow() - # Public: Folds all the rows. + # {Delegates to: EditSession.foldAll} foldAll: -> @activeEditSession.foldAll() - # Public: Unfolds all the rows. + # {Delegates to: EditSession.unfoldAll} unfoldAll: -> @activeEditSession.unfoldAll() - # Public: Folds the most recent selection. + # {Delegates to: EditSession.foldSelection} foldSelection: -> @activeEditSession.foldSelection() - # Public: Given the id of a {Fold}, this removes it. - # - # foldId - The fold id {Number} to remove + # {Delegates to: EditSession.destroyFold} destroyFold: (foldId) -> @activeEditSession.destroyFold(foldId) - # Public: Removes any {Fold}s found that contain the given buffer row. - # - # bufferRow - The buffer row {Number} to check against + # {Delegates to: EditSession.destroyFoldsContainingBufferRow} destroyFoldsContainingBufferRow: (bufferRow) -> @activeEditSession.destroyFoldsContainingBufferRow(bufferRow) - # Public: Determines if the given screen row is folded. - # - # screenRow - A {Number} indicating the screen row. - # - # Returns `true` if the screen row is folded, `false` otherwise. + # {Delegates to: EditSession.isFoldedAtScreenRow} isFoldedAtScreenRow: (screenRow) -> @activeEditSession.isFoldedAtScreenRow(screenRow) - # Public: Determines if the given buffer row is folded. - # - # screenRow - A {Number} indicating the buffer row. - # - # Returns `true` if the buffer row is folded, `false` otherwise. + # {Delegates to: EditSession.isFoldedAtBufferRow} isFoldedAtBufferRow: (bufferRow) -> @activeEditSession.isFoldedAtBufferRow(bufferRow) - # Public: Determines if the given row that the cursor is at is folded. - # - # Returns `true` if the row is folded, `false` otherwise. + # {Delegates to: EditSession.isFoldedAtCursorRow} isFoldedAtCursorRow: -> @activeEditSession.isFoldedAtCursorRow() - # Public: Gets the line for the given screen row. - # - # screenRow - A {Number} indicating the screen row. - # - # Returns a {String}. + # {Delegates to: EditSession.lineForScreenRow} lineForScreenRow: (screenRow) -> @activeEditSession.lineForScreenRow(screenRow) - # Public: Gets the lines for the given screen row boundaries. - # - # start - A {Number} indicating the beginning screen row. - # end - A {Number} indicating the ending screen row. - # - # Returns an {Array} of {String}s. + # {Delegates to: EditSession.linesForScreenRows} linesForScreenRows: (start, end) -> @activeEditSession.linesForScreenRows(start, end) - # Public: Gets the number of screen rows. - # - # Returns a {Number}. + # {Delegates to: EditSession.getScreenLineCount} getScreenLineCount: -> @activeEditSession.getScreenLineCount() - # Public: Defines the limit at which the buffer begins to soft wrap text. - # - # softWrapColumn - A {Number} defining the soft wrap limit + # {Delegates to: EditSession.setSoftWrapColumn} setSoftWrapColumn: (softWrapColumn) -> softWrapColumn ?= @calcSoftWrapColumn() @activeEditSession.setSoftWrapColumn(softWrapColumn) if softWrapColumn - # Public: Gets the length of the longest screen line. - # - # Returns a {Number}. + # {Delegates to: EditSession.maxScreenLineLength} maxScreenLineLength: -> @activeEditSession.maxScreenLineLength() - # Public: Gets the text in the last screen row. - # - # Returns a {String}. + # {Delegates to: EditSession.getLastScreenRow} getLastScreenRow: -> @activeEditSession.getLastScreenRow() - # Public: Given a position, this clips it to a real position. - # - # For example, if `position`'s row exceeds the row count of the buffer, - # or if its column goes beyond a line's length, this "sanitizes" the value - # to a real position. - # - # position - The {Point} to clip - # options - A hash with the following values: - # :wrapBeyondNewlines - if `true`, continues wrapping past newlines - # :wrapAtSoftNewlines - if `true`, continues wrapping past soft newlines - # :screenLine - if `true`, indicates that you're using a line number, not a row number - # - # Returns the new, clipped {Point}. Note that this could be the same as `position` if no clipping was performed. + # {Delegates to: EditSession.clipScreenPosition} clipScreenPosition: (screenPosition, options={}) -> @activeEditSession.clipScreenPosition(screenPosition, options) - # Public: Given a buffer position, this converts it into a screen position. - # - # bufferPosition - An object that represents a buffer position. It can be either - # an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point} - # options - The same options available to {DisplayBuffer.screenPositionForBufferPosition}. - # - # Returns a {Point}. + # {Delegates to: EditSession.screenPositionForBufferPosition} screenPositionForBufferPosition: (position, options) -> @activeEditSession.screenPositionForBufferPosition(position, options) - # Public: Given a buffer range, this converts it into a screen position. - # - # screenPosition - An object that represents a buffer position. It can be either - # an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point} - # options - The same options available to {DisplayBuffer.bufferPositionForScreenPosition}. - # - # Returns a {Point}. + # {Delegates to: EditSession.bufferPositionForScreenPosition} bufferPositionForScreenPosition: (position, options) -> @activeEditSession.bufferPositionForScreenPosition(position, options) - # Public: Given a buffer range, this converts it into a screen position. - # - # bufferRange - The {Range} to convert - # - # Returns a {Range}. + # {Delegates to: EditSession.screenRangeForBufferRange} screenRangeForBufferRange: (range) -> @activeEditSession.screenRangeForBufferRange(range) - # Public: Given a screen range, this converts it into a buffer position. - # - # screenRange - The {Range} to convert - # - # Returns a {Range}. + # {Delegates to: EditSession.bufferRangeForScreenRange} bufferRangeForScreenRange: (range) -> @activeEditSession.bufferRangeForScreenRange(range) - # Public: Given a starting and ending row, this converts every row into a buffer position. - # - # startRow - The row {Number} to start at - # endRow - The row {Number} to end at (default: {.getLastScreenRow}) - # - # Returns an {Array} of {Range}s. + # {Delegates to: EditSession.bufferRowsForScreenRows} bufferRowsForScreenRows: (startRow, endRow) -> @activeEditSession.bufferRowsForScreenRows(startRow, endRow) - # Public: Gets the number of the last row in the buffer. - # - # Returns a {Number}. + # {Delegates to: EditSession.getLastScreenRow} getLastScreenRow: -> @activeEditSession.getLastScreenRow() - # Internal: - - logCursorScope: -> - console.log @activeEditSession.getCursorScopes() - # Public: Emulates the "page down" key, where the last row of a buffer scrolls to become the first. pageDown: -> newScrollTop = @scrollTop() + @scrollView[0].clientHeight @@ -678,13 +512,13 @@ class Editor extends View @activeEditSession.moveCursorUp(@getPageRows()) @scrollTop(newScrollTop, adjustVerticalScrollbar: true) - # Public: Gets the number of actual page rows existing in an editor. + # Gets the number of actual page rows existing in an editor. # # Returns a {Number}. getPageRows: -> Math.max(1, Math.ceil(@scrollView[0].clientHeight / @lineHeight)) - # Public: Set whether invisible characters are shown. + # Set whether invisible characters are shown. # # showInvisibles - A {Boolean} which, if `true`, show invisible characters setShowInvisibles: (showInvisibles) -> @@ -692,7 +526,7 @@ class Editor extends View @showInvisibles = showInvisibles @resetDisplay() - # Public: Defines which characters are invisible. + # Defines which characters are invisible. # # invisibles - A hash defining the invisible characters: The defaults are: # :eol - `\u00ac` @@ -707,7 +541,7 @@ class Editor extends View cr: '\u00a4' @resetDisplay() - # Public: Sets whether you want to show the indentation guides. + # Sets whether you want to show the indentation guides. # # showIndentGuide - A {Boolean} you can set to `true` if you want to see the indentation guides. setShowIndentGuide: (showIndentGuide) -> @@ -715,81 +549,43 @@ class Editor extends View @showIndentGuide = showIndentGuide @resetDisplay() - # Public: Checks out the current HEAD revision of the file. + # {Delegates to: Buffer.checkoutHead} checkoutHead: -> @getBuffer().checkoutHead() - # Public: Replaces the current buffer contents. - # - # text - A {String} containing the new buffer contents. + # {Delegates to: EditSession.setText} setText: (text) -> @activeEditSession.setText(text) - # Public: Retrieves the current buffer contents. - # - # Returns a {String}. + # {Delegates to: EditSession.getText} getText: -> @activeEditSession.getText() - # Public: Retrieves the current buffer's file path. - # - # Returns a {String}. + # {Delegates to: EditSession.getPath} getPath: -> @activeEditSession?.getPath() - # Public: Gets the number of lines in a file. - # - # Returns a {Number}. + # {Delegates to: Buffer.getLineCount} getLineCount: -> @getBuffer().getLineCount() - # Public: Gets the row number of the last line. - # - # Returns a {Number}. + # {Delegates to: Buffer.getLastRow} getLastBufferRow: -> @getBuffer().getLastRow() - # Public: Given a range, returns the lines of text within it. - # - # range - A {Range} object specifying your points of interest - # - # Returns a {String} of the combined lines. + # {Delegates to: Buffer.getTextInRange} getTextInRange: (range) -> @getBuffer().getTextInRange(range) - # Public: Finds the last point in the current buffer. - # - # Returns a {Point} representing the last position. + # {Delegates to: Buffer.getEofPosition} getEofPosition: -> @getBuffer().getEofPosition() - # Public: Given a row, returns the line of text. - # - # row - A {Number} indicating the row. - # - # Returns a {String}. + # {Delegates to: Buffer.lineForRow} lineForBufferRow: (row) -> @getBuffer().lineForRow(row) - # Public: Given a row, returns the length of the line of text. - # - # row - A {Number} indicating the row - # - # Returns a {Number}. + # {Delegates to: Buffer.lineLengthForRow} lineLengthForBufferRow: (row) -> @getBuffer().lineLengthForRow(row) - # Public: Given a buffer row, this retrieves the range for that line. - # - # row - A {Number} identifying the row - # options - A hash with one key, `includeNewline`, which specifies whether you - # want to include the trailing newline - # - # Returns a {Range}. + # {Delegates to: Buffer.rangeForRow} rangeForBufferRow: (row) -> @getBuffer().rangeForRow(row) - # Public: Scans for text in the buffer, calling a function on each match. - # - # regex - A {RegExp} representing the text to find - # range - A {Range} in the buffer to search within - # iterator - A {Function} that's called on each match + # {Delegates to: Buffer.scanInRange} scanInBufferRange: (args...) -> @getBuffer().scanInRange(args...) - # Public: Scans for text in the buffer _backwards_, calling a function on each match. - # - # regex - A {RegExp} representing the text to find - # range - A {Range} in the buffer to search within - # iterator - A {Function} that's called on each match + # {Delegates to: Buffer.backwardsScanInRange} backwardsScanInBufferRange: (args...) -> @getBuffer().backwardsScanInRange(args...) ### Internal ### @@ -982,22 +778,22 @@ class Editor extends View ### Public ### - # Public: Retrieves the {EditSession}'s buffer. + # Retrieves the {EditSession}'s buffer. # # Returns the current {Buffer}. getBuffer: -> @activeEditSession.buffer - # Public: Scrolls the editor to the bottom. + # Scrolls the editor to the bottom. scrollToBottom: -> @scrollBottom(@getScreenLineCount() * @lineHeight) - # Public: Scrolls the editor to the position of the most recently added cursor. + # Scrolls the editor to the position of the most recently added cursor. # # The editor is also centered. scrollToCursorPosition: -> @scrollToBufferPosition(@getCursorBufferPosition(), center: true) - # Public: Scrolls the editor to the given buffer position. + # Scrolls the editor to the given buffer position. # # bufferPosition - An object that represents a buffer position. It can be either # an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point} @@ -1005,7 +801,7 @@ class Editor extends View scrollToBufferPosition: (bufferPosition, options) -> @scrollToPixelPosition(@pixelPositionForBufferPosition(bufferPosition), options) - # Public: Scrolls the editor to the given screen position. + # Scrolls the editor to the given screen position. # # screenPosition - An object that represents a buffer position. It can be either # an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point} @@ -1013,7 +809,7 @@ class Editor extends View scrollToScreenPosition: (screenPosition, options) -> @scrollToPixelPosition(@pixelPositionForScreenPosition(screenPosition), options) - # Public: Scrolls the editor to the given pixel position. + # Scrolls the editor to the given pixel position. # # pixelPosition - An object that represents a pixel position. It can be either # an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point} @@ -1061,7 +857,7 @@ class Editor extends View else if desiredLeft < @scrollView.scrollLeft() @scrollView.scrollLeft(desiredLeft) - # Public: Given a buffer range, this highlights all the folds within that range + # Given a buffer range, this highlights all the folds within that range # # "Highlighting" essentially just adds the `selected` class to the line # @@ -1086,11 +882,11 @@ class Editor extends View @activeEditSession.setScrollTop(@scrollTop()) @activeEditSession.setScrollLeft(@scrollView.scrollLeft()) - # Public: Activates soft tabs in the editor. + # {Delegates to: EditSession.setSoftTabs} toggleSoftTabs: -> @activeEditSession.setSoftTabs(not @activeEditSession.softTabs) - # Public: Activates soft wraps in the editor. + # Activates soft wraps in the editor. toggleSoftWrap: -> @setSoftWrap(not @activeEditSession.getSoftWrap()) @@ -1100,7 +896,7 @@ class Editor extends View else Infinity - # Public: Sets the soft wrap column for the editor. + # Sets the soft wrap column for the editor. # # softWrap - A {Boolean} which, if `true`, sets soft wraps # softWrapColumn - A {Number} indicating the length of a line in the editor when soft @@ -1117,7 +913,7 @@ class Editor extends View @removeClass 'soft-wrap' $(window).off 'resize', @_setSoftWrapColumn - # Public: Sets the font size for the editor. + # Sets the font size for the editor. # # fontSize - A {Number} indicating the font size in pixels. setFontSize: (fontSize) -> @@ -1134,13 +930,13 @@ class Editor extends View else @redrawOnReattach = @attached - # Public: Retrieves the font size for the editor. + # Retrieves the font size for the editor. # # Returns a {Number} indicating the font size in pixels. getFontSize: -> parseInt(@css("font-size")) - # Public: Sets the font family for the editor. + # Sets the font family for the editor. # # fontFamily - A {String} identifying the CSS `font-family`, setFontFamily: (fontFamily) -> @@ -1157,16 +953,16 @@ class Editor extends View @redraw() - # Public: Gets the font family for the editor. + # Gets the font family for the editor. # # Returns a {String} identifying the CSS `font-family`, getFontFamily: -> @css("font-family") - # Public: Clears the CSS `font-family` property from the editor. + # Clears the CSS `font-family` property from the editor. clearFontFamily: -> $('head style.editor-font-family').remove() - # Public: Clears the CSS `font-family` property from the editor. + # Clears the CSS `font-family` property from the editor. redraw: -> return unless @hasParent() return unless @attached @@ -1188,7 +984,7 @@ class Editor extends View splitDown: (items...) -> @getPane()?.splitDown(items...).activeView - # Public: Retrieve's the `Editor`'s pane. + # Retrieve's the `Editor`'s pane. # # Returns a {Pane}. getPane: -> @@ -1489,20 +1285,20 @@ class Editor extends View ### Public ### - # Public: Retrieves the number of the row that is visible and currently at the top of the editor. + # Retrieves the number of the row that is visible and currently at the top of the editor. # # Returns a {Number}. getFirstVisibleScreenRow: -> Math.floor(@scrollTop() / @lineHeight) - # Public: Retrieves the number of the row that is visible and currently at the top of the editor. + # Retrieves the number of the row that is visible and currently at the top of the editor. # # Returns a {Number}. getLastVisibleScreenRow: -> calculatedRow = Math.ceil((@scrollTop() + @scrollView.height()) / @lineHeight) - 1 Math.max(0, Math.min(@getScreenLineCount() - 1, calculatedRow)) - # Public: Given a row number, identifies if it is currently visible. + # Given a row number, identifies if it is currently visible. # # row - A row {Number} to check # @@ -1620,7 +1416,7 @@ class Editor extends View ### Public ### - # Public: Converts a buffer position to a pixel position. + # Converts a buffer position to a pixel position. # # position - An object that represents a buffer position. It can be either # an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point} @@ -1629,7 +1425,7 @@ class Editor extends View pixelPositionForBufferPosition: (position) -> @pixelPositionForScreenPosition(@screenPositionForBufferPosition(position)) - # Public: Converts a screen position to a pixel position. + # Converts a screen position to a pixel position. # # position - An object that represents a screen position. It can be either # an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point} @@ -1700,7 +1496,7 @@ class Editor extends View new Point(row, column) - # Public: Highlights the current line the cursor is on. + # Highlights the current line the cursor is on. highlightCursorLine: -> return if @mini @@ -1711,23 +1507,26 @@ class Editor extends View else @highlightedLine = null - # Public: Retrieves the current {EditSession}'s grammar. - # - # Returns a {String} indicating the language's grammar rules. + # {Delegates to: EditSession.getGrammar} getGrammar: -> @activeEditSession.getGrammar() - # Public: Sets the current {EditSession}'s grammar. This only works for mini-editors. - # - # grammar - A {String} indicating the language's grammar rules. + # {Delegates to: EditSession.setGrammar} setGrammar: (grammar) -> throw new Error("Only mini-editors can explicity set their grammar") unless @mini @activeEditSession.setGrammar(grammar) - # Public: Reloads the current grammar. + # {Delegates to: EditSession.reloadGrammar} reloadGrammar: -> @activeEditSession.reloadGrammar() + # Copies the current file path to the native clipboard. + copyPathToPasteboard: -> + path = @getPath() + pasteboard.write(path) if path? + + ### Internal ### + bindToKeyedEvent: (key, event, callback) -> binding = {} binding[key] = event @@ -1735,7 +1534,6 @@ class Editor extends View @on event, => callback(this, event) - # Internal: Replaces all the currently selected text. replaceSelectedText: (replaceFn) -> selection = @getSelection() return false if selection.isEmpty() @@ -1746,12 +1544,10 @@ class Editor extends View @insertText(text, select: true) true - # Public: Copies the current file path to the native clipboard. - copyPathToPasteboard: -> - path = @getPath() - pasteboard.write(path) if path? + consolidateSelections: (e) -> e.abortKeyBinding() unless @activeEditSession.consolidateSelections() - ### Internal ### + logCursorScope: -> + console.log @activeEditSession.getCursorScopes() transact: (fn) -> @activeEditSession.transact(fn) commit: -> @activeEditSession.commit() From dbae05605bfb01edbf84c295e00090b643af8dcb Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 16:19:39 -0700 Subject: [PATCH 39/91] Update eventemitter --- src/app/event-emitter.coffee | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/app/event-emitter.coffee b/src/app/event-emitter.coffee index ad1b63a60..e50ba68ae 100644 --- a/src/app/event-emitter.coffee +++ b/src/app/event-emitter.coffee @@ -4,7 +4,7 @@ _ = require 'underscore' # # Each event can have more than one handler; that is, an event can trigger multiple functions. module.exports = - # Public: Associates an event name with a function to perform. + # Associates an event name with a function to perform. # # This is called endlessly, until the event is turned {.off}. # @@ -27,7 +27,7 @@ module.exports = @afterSubscribe?() - # Public: Associates an event name with a function to perform only once. + # Associates an event name with a function to perform only once. # # eventName - A {String} name identifying an event # handler - A {Function} that's executed when the event is triggered @@ -38,7 +38,7 @@ module.exports = @on eventName, oneShotHandler - # Public: Triggers a registered event. + # Triggers a registered event. # # eventName - A {String} name identifying an event # args - Any additional arguments to pass over to the event `handler` @@ -55,7 +55,7 @@ module.exports = if handlers = @eventHandlersByEventName?[eventName] handlers.forEach (handler) -> handler(args...) - # Public: Stops executing handlers for a registered event. + # Stops executing handlers for a registered event. # # eventNames - A {String} containing one or more space-separated events. # handler - The {Function} to remove from the event. If not provided, all handlers are removed. @@ -90,20 +90,20 @@ module.exports = @eventHandlersByNamespace = {} @afterUnsubscribe?() if @subscriptionCount() < subscriptionCountBefore - # Public: When called, stops triggering any events. + # When called, stops triggering any events. pauseEvents: -> @pauseCount ?= 0 if @pauseCount++ == 0 @queuedEvents ?= [] - # Public: When called, resumes triggering events. + # When called, resumes triggering events. resumeEvents: -> if --@pauseCount == 0 queuedEvents = @queuedEvents @queuedEvents = null @trigger(event...) for event in queuedEvents - # Public: Identifies how many events are registered. + # Identifies how many events are registered. # # Returns a {Number}. subscriptionCount: -> From 09e9c2f224b154522f178843c783b10310dee740 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 16:25:13 -0700 Subject: [PATCH 40/91] Update file --- src/app/file.coffee | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/app/file.coffee b/src/app/file.coffee index 9476df2b1..2c4afb05d 100644 --- a/src/app/file.coffee +++ b/src/app/file.coffee @@ -7,7 +7,7 @@ _ = require 'underscore' # Public: Represents an individual file in the editor. # -# The entry point for this class is in two locations: +# The entry point for this class is in two locations: # * {Buffer}, which associates text contents with a file # * {Directory}, which associcates the children of a directory as files module.exports = @@ -15,7 +15,7 @@ class File path: null cachedContents: null - # Public: Creates a new file. + # Creates a new file. # # path - A {String} representing the file path # symlink - A {Boolean} indicating if the path is a symlink (default: false) @@ -24,23 +24,23 @@ class File if fs.statSync(@path).isDirectory() throw new Error("#{@path} is a directory") - # Public: Sets the path for the file. + # Sets the path for the file. # # path - A {String} representing the new file path setPath: (@path) -> - # Public: Retrieves the path for the file. + # Retrieves the path for the file. # # Returns a {String}. getPath: -> @path - # Public: Gets the file's basename--that is, the file without any directory information. + # Gets the file's basename--that is, the file without any directory information. # # Returns a {String}. getBaseName: -> fsUtils.base(@path) - # Public: Writes (and saves) new contents to the file. + # Writes (and saves) new contents to the file. # # text - A {String} representing the new contents. write: (text) -> @@ -49,7 +49,7 @@ class File fsUtils.write(@getPath(), text) @subscribeToNativeChangeEvents() if not previouslyExisted and @subscriptionCount() > 0 - # Public: Reads the file. + # Reads the file. # # flushCache - A {Boolean} indicating if the cache should be erased--_i.e._, a force read is performed # @@ -62,7 +62,7 @@ class File else @cachedContents - # Public: Checks to see if a file exists. + # Checks to see if a file exists. # # Returns a {Boolean}. exists: -> @@ -72,7 +72,7 @@ class File afterSubscribe: -> @subscribeToNativeChangeEvents() if @exists() and @subscriptionCount() == 1 - + afterUnsubscribe: -> @unsubscribeFromNativeChangeEvents() if @subscriptionCount() == 0 From eadb3cf98cfd829b3fdd66a54848df7645bf6e8c Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 16:25:49 -0700 Subject: [PATCH 41/91] Update fold --- src/app/fold.coffee | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/fold.coffee b/src/app/fold.coffee index ea6b2d5d9..cdc7c4bc2 100644 --- a/src/app/fold.coffee +++ b/src/app/fold.coffee @@ -24,7 +24,7 @@ class Fold inspect: -> "Fold(#{@startRow}, #{@endRow})" - # Public: Retrieves the buffer row range that a fold occupies. + # Retrieves the buffer row range that a fold occupies. # # includeNewline - A {Boolean} which, if `true`, includes the trailing newline # @@ -37,7 +37,7 @@ class Fold new Range([@startRow, 0], end) - # Public: Retrieves the number of buffer rows a fold occupies. + # Retrieves the number of buffer rows a fold occupies. # # Returns a {Number}. getBufferRowCount: -> @@ -57,7 +57,7 @@ class Fold @displayBuffer.unregisterFold(oldStartRow, this) @displayBuffer.registerFold(this) - # Public: Identifies if a {Range} occurs within a fold. + # Identifies if a {Range} occurs within a fold. # # range - A {Range} to check # @@ -65,7 +65,7 @@ class Fold isContainedByRange: (range) -> range.start.row <= @startRow and @endRow <= range.end.row - # Public: Identifies if a fold is nested within a fold. + # Identifies if a fold is nested within a fold. # # fold - A {Fold} to check # From 9d709ea1523b8908bba9b1f4a1a37fac6cf8318d Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 16:27:23 -0700 Subject: [PATCH 42/91] Update git --- src/app/git.coffee | 68 +++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/app/git.coffee b/src/app/git.coffee index b0fd57336..d77fdc68b 100644 --- a/src/app/git.coffee +++ b/src/app/git.coffee @@ -11,27 +11,13 @@ GitUtils = require 'git-utils' module.exports = class Git - # Public: Creates a new `Git` instance. - # - # path - The git repository to open - # options - A hash with one key: - # :refreshOnWindowFocus - A {Boolean} that identifies if the windows should refresh - # - # Returns a new {Git} object. - @open: (path, options) -> - return null unless path - try - new Git(path, options) - catch e - null - statuses: null upstream: null statusTask: null ### Internal ### - # Internal: Creates a new `Git` object. + # Creates a new `Git` object. # # path - The {String} representing the path to your git working directory # options - A hash with the following keys: @@ -72,7 +58,21 @@ class Git ### Public ### - # Public: Retrieves the git repository. + # Creates a new `Git` instance. + # + # path - The git repository to open + # options - A hash with one key: + # :refreshOnWindowFocus - A {Boolean} that identifies if the windows should refresh + # + # Returns a new {Git} object. + @open: (path, options) -> + return null unless path + try + new Git(path, options) + catch e + null + + # Retrieves the git repository. # # Returns a new `Repository`. getRepo: -> @@ -80,22 +80,22 @@ class Git throw new Error("Repository has been destroyed") @repo - # Public: Reread the index to update any values that have changed since the last time the index was read. + # Reread the index to update any values that have changed since the last time the index was read. refreshIndex: -> @getRepo().refreshIndex() - # Public: Retrieves the path of the repository. + # Retrieves the path of the repository. # # Returns a {String}. getPath: -> @path ?= fsUtils.absolute(@getRepo().getPath()) - # Public: Retrieves the working directory of the repository. + # Retrieves the working directory of the repository. # # Returns a {String}. getWorkingDirectory: -> @getRepo().getWorkingDirectory() - # Public: Retrieves the reference or SHA-1 that `HEAD` points to. + # Retrieves the reference or SHA-1 that `HEAD` points to. # # This can be `refs/heads/master`, or a full SHA-1 if the repository is in a detached `HEAD` state. # @@ -103,7 +103,7 @@ class Git getHead: -> @getRepo().getHead() ? '' - # Public: Retrieves the status of a single path in the repository. + # Retrieves the status of a single path in the repository. # # path - An {String} defining a relative path # @@ -119,7 +119,7 @@ class Git @trigger 'status-changed', path, pathStatus pathStatus - # Public: Identifies if a path is ignored. + # Identifies if a path is ignored. # # path - The {String} path to check # @@ -127,7 +127,7 @@ class Git isPathIgnored: (path) -> @getRepo().isIgnored(@relativize(path)) - # Public: Identifies if a value represents a status code. + # Identifies if a value represents a status code. # # status - The code {Number} to check # @@ -135,7 +135,7 @@ class Git isStatusModified: (status) -> @getRepo().isStatusModified(status) - # Public: Identifies if a path was modified. + # Identifies if a path was modified. # # path - The {String} path to check # @@ -143,7 +143,7 @@ class Git isPathModified: (path) -> @isStatusModified(@getPathStatus(path)) - # Public: Identifies if a status code represents a new path. + # Identifies if a status code represents a new path. # # status - The code {Number} to check # @@ -151,7 +151,7 @@ class Git isStatusNew: (status) -> @getRepo().isStatusNew(status) - # Public: Identifies if a path is new. + # Identifies if a path is new. # # path - The {String} path to check # @@ -159,7 +159,7 @@ class Git isPathNew: (path) -> @isStatusNew(@getPathStatus(path)) - # Public: Makes a path relative to the repository's working directory. + # Makes a path relative to the repository's working directory. # # path - The {String} path to convert # @@ -167,7 +167,7 @@ class Git relativize: (path) -> @getRepo().relativize(path) - # Public: Retrieves a shortened version of {.getHead}. + # Retrieves a shortened version of {.getHead}. # # This removes the leading segments of `refs/heads`, `refs/tags`, or `refs/remotes`. # It also shortenes the SHA-1 of a detached `HEAD` to 7 characters. @@ -176,7 +176,7 @@ class Git getShortHead: -> @getRepo().getShortHead() - # Public: Restore the contents of a path in the working directory and index to the version at `HEAD`. + # Restore the contents of a path in the working directory and index to the version at `HEAD`. # # This is essentially the same as running: # ``` @@ -192,7 +192,7 @@ class Git @getPathStatus(path) if headCheckedOut headCheckedOut - # Public: Retrieves the number of lines added and removed to a path. + # Retrieves the number of lines added and removed to a path. # # This compares the working directory contents of the path to the `HEAD` version. # @@ -202,7 +202,7 @@ class Git getDiffStats: (path) -> @getRepo().getDiffStats(@relativize(path)) - # Public: Identifies if a path is a submodule. + # Identifies if a path is a submodule. # # path - The {String} path to check # @@ -210,7 +210,7 @@ class Git isSubmodule: (path) -> @getRepo().isSubmodule(@relativize(path)) - # Public: Retrieves the status of a directory. + # Retrieves the status of a directory. # # path - The {String} path to check # @@ -222,7 +222,7 @@ class Git directoryStatus |= status if path.indexOf(directoryPath) is 0 directoryStatus - # Public: Retrieves the number of commits the `HEAD` branch is ahead/behind the remote branch it is tracking. + # Retrieves the number of commits the `HEAD` branch is ahead/behind the remote branch it is tracking. # # This is similar to the commit numbers reported by `git status` when a remote tracking branch exists. # @@ -230,7 +230,7 @@ class Git getAheadBehindCounts: -> @getRepo().getAheadBehindCount() - # Public: Retrieves the line diffs comparing the `HEAD` version of the given path and the given text. + # Retrieves the line diffs comparing the `HEAD` version of the given path and the given text. # # This is similar to the commit numbers reported by `git status` when a remote tracking branch exists. # From 7e6eecc6cf489747a6b74c42305983ee1133eb6c Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 16:27:48 -0700 Subject: [PATCH 43/91] Update gutter --- src/app/gutter.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/gutter.coffee b/src/app/gutter.coffee index 484c28c43..000405d6d 100644 --- a/src/app/gutter.coffee +++ b/src/app/gutter.coffee @@ -50,13 +50,13 @@ class Gutter extends View ### Public ### - # Public: Retrieves the containing {Editor}. + # Retrieves the containing {Editor}. # # Returns an {Editor}. getEditor: -> @parentView - # Public: Defines whether to show the gutter or not. + # Defines whether to show the gutter or not. # # showLineNumbers - A {Boolean} which, if `false`, hides the gutter setShowLineNumbers: (showLineNumbers) -> @@ -76,7 +76,7 @@ class Gutter extends View break @renderLineNumbers(renderFrom, renderTo) if performUpdate - + renderLineNumbers: (startScreenRow, endScreenRow) -> editor = @getEditor() maxDigits = editor.getLineCount().toString().length From 7b65a68450078187892bbcd06fdb4842d7324caa Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 16:28:31 -0700 Subject: [PATCH 44/91] Update image-edit-session --- src/app/image-edit-session.coffee | 40 ++++++++++++++++--------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/app/image-edit-session.coffee b/src/app/image-edit-session.coffee index 84ab5470c..f34d77f74 100644 --- a/src/app/image-edit-session.coffee +++ b/src/app/image-edit-session.coffee @@ -8,21 +8,8 @@ module.exports= class ImageEditSession registerDeserializer(this) - # Public: Identifies if a path can be opened by the image viewer. - # - # path - The {String} name of the path to check - # - # Returns a {Boolean}. - @canOpen: (path) -> - _.indexOf([ - '.gif' - '.jpeg' - '.jpg' - '.png' - ], fsUtils.extension(path), true) >= 0 - ### Internal ### - + @deserialize: (state) -> if fsUtils.exists(state.path) project.buildEditSession(state.path) @@ -38,10 +25,25 @@ class ImageEditSession getViewClass: -> require 'image-view' - # Public: Retrieves the filename of the open file. + ### Public ### + + # Identifies if a path can be opened by the image viewer. + # + # path - The {String} name of the path to check + # + # Returns a {Boolean}. + @canOpen: (path) -> + _.indexOf([ + '.gif' + '.jpeg' + '.jpg' + '.png' + ], fsUtils.extension(path), true) >= 0 + + # Retrieves the filename of the open file. # # This is `'untitled'` if the file is new and not saved to the disk. - # + # # Returns a {String}. getTitle: -> if path = @getPath() @@ -49,17 +51,17 @@ class ImageEditSession else 'untitled' - # Public: Retrieves the URI of the current image. + # Retrieves the URI of the current image. # # Returns a {String}. getUri: -> @path - # Public: Retrieves the path of the current image. + # Retrieves the path of the current image. # # Returns a {String}. getPath: -> @path - # Public: Compares two `ImageEditSession`s to determine equality. + # Compares two `ImageEditSession`s to determine equality. # # Equality is based on the condition that the two URIs are the same. # From bb8f4fb0921d2f414761420dd84f61beb77bf0c6 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 16:34:20 -0700 Subject: [PATCH 45/91] Update image-view --- src/app/image-view.coffee | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/app/image-view.coffee b/src/app/image-view.coffee index 2cb79d846..329d98bb1 100644 --- a/src/app/image-view.coffee +++ b/src/app/image-view.coffee @@ -6,12 +6,12 @@ $ = require 'jquery' module.exports = class ImageView extends ScrollView - # Internal: + ### Internal ### + @content: -> @div class: 'image-view', tabindex: -1, => @img outlet: 'image' - # Internal: initialize: (imageEditSession) -> super @@ -29,7 +29,6 @@ class ImageView extends ScrollView @command 'image-view:zoom-out', => @zoomOut() @command 'image-view:reset-zoom', => @resetZoom() - # Internal: afterAttach: (onDom) -> return unless onDom @@ -40,7 +39,9 @@ class ImageView extends ScrollView @active = @is(pane.activeView) @centerImage() if @active and not wasActive - # Public: Places the image in the center of the {Editor}. + ### Public ### + + # Places the image in the center of the {Editor}. centerImage: -> return unless @loaded and @isVisible() @@ -49,7 +50,7 @@ class ImageView extends ScrollView 'left': Math.max((@width() - @image.outerWidth()) / 2, 0) @image.show() - # Public: Indicates the path of the image. + # Indicates the path of the image. # # path - A {String} for the new image path. setPath: (path) -> @@ -60,25 +61,25 @@ class ImageView extends ScrollView else @image.hide() - # Public: Retrieve's the {Editor}'s pane. + # Retrieve's the {Editor}'s pane. # # Returns a {Pane}. getPane: -> @parent('.item-views').parent('.pane').view() - # Public: Zooms the image out. + # Zooms the image out. # # This is done by a factor of `0.9`. zoomOut: -> @adjustSize(0.9) - # Public: Zooms the image in. + # Zooms the image in. # # This is done by a factor of `1.1`. zoomIn: -> @adjustSize(1.1) - # Public: Zooms the image to its normal width and height. + # Zooms the image to its normal width and height. resetZoom: -> return unless @loaded and @isVisible() @@ -98,4 +99,4 @@ class ImageView extends ScrollView @centerImage() setModel: (imageEditSession) -> - @setPath(imageEditSession?.getPath()) \ No newline at end of file + @setPath(imageEditSession?.getPath()) From 9be438d2f78617ad833a244986e43455bd2196a7 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 16:36:04 -0700 Subject: [PATCH 46/91] Update language-mode --- src/app/language-mode.coffee | 37 +++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/app/language-mode.coffee b/src/app/language-mode.coffee index 3e0287706..46d968ded 100644 --- a/src/app/language-mode.coffee +++ b/src/app/language-mode.coffee @@ -14,18 +14,20 @@ class LanguageMode editSession: null currentGrammarScore: null - # Public: Sets up a `LanguageMode` for the given {EditSession}. + ### Internal ### + + destroy: -> + @unsubscribe() + + ### Public ### + + # Sets up a `LanguageMode` for the given {EditSession}. # # editSession - The {EditSession} to associate with constructor: (@editSession) -> @buffer = @editSession.buffer - # Internal: - destroy: -> - @unsubscribe() - - - # Public: Wraps the lines between two rows in comments. + # Wraps the lines between two rows in comments. # # If the language doesn't have comment, nothing happens. # @@ -72,7 +74,7 @@ class LanguageMode for row in [start..end] buffer.insert([row, 0], commentStartString) - # Public: Folds all the foldable lines in the buffer. + # Folds all the foldable lines in the buffer. foldAll: -> for currentRow in [0..@buffer.getLastRow()] [startRow, endRow] = @rowRangeForFoldAtBufferRow(currentRow) ? [] @@ -80,11 +82,16 @@ class LanguageMode @editSession.createFold(startRow, endRow) - # Public: Unfolds all the foldable lines in the buffer. + # Unfolds all the foldable lines in the buffer. unfoldAll: -> for row in [@buffer.getLastRow()..0] fold.destroy() for fold in @editSession.displayBuffer.foldsStartingAtBufferRow(row) + # Given a buffer row, creates a fold at it. + # + # bufferRow - A {Number} indicating the buffer row + # + # Returns the new {Fold}. foldBufferRow: (bufferRow) -> for currentRow in [bufferRow..0] rowRange = @rowRangeForCommentAtBufferRow(currentRow) @@ -94,7 +101,7 @@ class LanguageMode fold = @editSession.displayBuffer.largestFoldStartingAtBufferRow(startRow) return @editSession.createFold(startRow, endRow) unless fold - # Public: Given a buffer row, this unfolds it. + # Given a buffer row, this unfolds it. # # bufferRow - A {Number} indicating the buffer row unfoldBufferRow: (bufferRow) -> @@ -138,7 +145,7 @@ class LanguageMode endRow = currentRow return [startRow, endRow] if startRow isnt endRow - # Public: Given a buffer row, this returns a suggested indentation level. + # Given a buffer row, this returns a suggested indentation level. # # The indentation level provided is based on the current {LanguageMode}. # @@ -164,21 +171,21 @@ class LanguageMode Math.max(desiredIndentLevel, currentIndentLevel) - # Public: Indents all the rows between two buffer row numbers. + # Indents all the rows between two buffer row numbers. # # startRow - The row {Number} to start at # endRow - The row {Number} to end at autoIndentBufferRows: (startRow, endRow) -> @autoIndentBufferRow(row) for row in [startRow..endRow] - # Public: Given a buffer row, this indents it. + # Given a buffer row, this indents it. # # bufferRow - The row {Number} autoIndentBufferRow: (bufferRow) -> @autoIncreaseIndentForBufferRow(bufferRow) @autoDecreaseIndentForBufferRow(bufferRow) - # Public: Given a buffer row, this increases the indentation. + # Given a buffer row, this increases the indentation. # # bufferRow - The row {Number} autoIncreaseIndentForBufferRow: (bufferRow) -> @@ -196,7 +203,7 @@ class LanguageMode if desiredIndentLevel > currentIndentLevel @editSession.setIndentationForBufferRow(bufferRow, desiredIndentLevel) - # Public: Given a buffer row, this decreases the indentation. + # Given a buffer row, this decreases the indentation. # # bufferRow - The row {Number} autoDecreaseIndentForBufferRow: (bufferRow) -> From 46b5731ac4df967ed4d4dfbe971cb0e2119ca36d Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 16:36:18 -0700 Subject: [PATCH 47/91] Update line-map --- src/app/line-map.coffee | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/app/line-map.coffee b/src/app/line-map.coffee index fbf0f3b67..abff5c28c 100644 --- a/src/app/line-map.coffee +++ b/src/app/line-map.coffee @@ -28,7 +28,7 @@ class LineMap for screenLine in maxLengthCandidates @maxScreenLineLength = Math.max(@maxScreenLineLength, screenLine.text.length) - # Public: Gets the line for the given screen row. + # Gets the line for the given screen row. # # screenRow - A {Number} indicating the screen row. # @@ -36,7 +36,7 @@ class LineMap lineForScreenRow: (row) -> @screenLines[row] - # Public: Gets the lines for the given screen row boundaries. + # Gets the lines for the given screen row boundaries. # # start - A {Number} indicating the beginning screen row. # end - A {Number} indicating the ending screen row. @@ -45,7 +45,7 @@ class LineMap linesForScreenRows: (startRow, endRow) -> @screenLines[startRow..endRow] - # Public: Given starting and ending screen rows, this returns an array of the + # Given starting and ending screen rows, this returns an array of the # buffer rows corresponding to every screen row in the range # # startRow - The screen row {Number} to start at @@ -100,7 +100,7 @@ class LineMap column = screenLine.clipScreenColumn(column, options) new Point(row, column) - # Public: Given a buffer position, this converts it into a screen position. + # Given a buffer position, this converts it into a screen position. # # bufferPosition - An object that represents a buffer position. It can be either # an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point} @@ -140,7 +140,7 @@ class LineMap [screenRow, screenLines] - # Public: Given a buffer range, this converts it into a screen position. + # Given a buffer range, this converts it into a screen position. # # screenPosition - An object that represents a buffer position. It can be either # an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point} @@ -163,7 +163,7 @@ class LineMap [bufferRow, screenLine] - # Public: Given a buffer range, this converts it into a screen position. + # Given a buffer range, this converts it into a screen position. # # bufferRange - The {Range} to convert # @@ -174,7 +174,7 @@ class LineMap end = @screenPositionForBufferPosition(bufferRange.end) new Range(start, end) - # Public: Given a screen range, this converts it into a buffer position. + # Given a screen range, this converts it into a buffer position. # # screenRange - The {Range} to convert # From 3dcdf0863e5001420d47ccb673f6eb20dc42f1b7 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 16:36:56 -0700 Subject: [PATCH 48/91] Update line-map --- src/app/line-map.coffee | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/app/line-map.coffee b/src/app/line-map.coffee index abff5c28c..33e76edf9 100644 --- a/src/app/line-map.coffee +++ b/src/app/line-map.coffee @@ -7,6 +7,8 @@ module.exports = class LineMap maxScreenLineLength: 0 + ### Internal ### + constructor: -> @screenLines = [] @@ -28,6 +30,8 @@ class LineMap for screenLine in maxLengthCandidates @maxScreenLineLength = Math.max(@maxScreenLineLength, screenLine.text.length) + ### Public ### + # Gets the line for the given screen row. # # screenRow - A {Number} indicating the screen row. @@ -185,7 +189,8 @@ class LineMap end = @bufferPositionForScreenPosition(screenRange.end) new Range(start, end) - # Internal: + ### Internal ### + logLines: (start=0, end=@screenLineCount() - 1)-> for row in [start..end] line = @lineForScreenRow(row).text From 51a07bdb3130e9284805bec4bd5ff27aee93592f Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 16:37:27 -0700 Subject: [PATCH 49/91] Update point --- src/app/point.coffee | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/app/point.coffee b/src/app/point.coffee index 09d8b5f2c..aafaa3230 100644 --- a/src/app/point.coffee +++ b/src/app/point.coffee @@ -6,7 +6,7 @@ _ = require 'underscore' module.exports = class Point - # Public: Constructs a `Point` from a given object. + # Constructs a `Point` from a given object. # # object - This can be an {Array} (`[startRow, startColumn, endRow, endColumn]`) or an object `{row, column}` # @@ -22,7 +22,7 @@ class Point new Point(row, column) - # Public: Identifies which `Point` is smaller. + # Identifies which `Point` is smaller. # # "Smaller" means that both the `row` and `column` values of one `Point` are less than or equal # to the other. @@ -39,7 +39,7 @@ class Point else point2 - # Public: Creates a new `Point` object. + # Creates a new `Point` object. # # row - A {Number} indicating the row (default: 0) # column - A {Number} indicating the column (default: 0) @@ -47,13 +47,13 @@ class Point # Returns a {Point}, constructor: (@row=0, @column=0) -> - # Public: Creates an identical copy of the `Point`. + # Creates an identical copy of the `Point`. # # Returns a duplicate {Point}. copy: -> new Point(@row, @column) - # Public: Adds the `column`s of two `Point`s together. + # Adds the `column`s of two `Point`s together. # # other - The {Point} to add with # @@ -68,7 +68,7 @@ class Point new Point(row, column) - # Public: Moves a `Point`. + # Moves a `Point`. # # In other words, the `row` values and `column` values are added to each other. # @@ -79,7 +79,7 @@ class Point other = Point.fromObject(other) new Point(@row + other.row, @column + other.column) - # Public: Creates two new `Point`s, split down a `column` value. + # Creates two new `Point`s, split down a `column` value. # # In other words, given a point, this creates `Point(0, column)` and `Point(row, column)`. # @@ -103,7 +103,7 @@ class Point # * If the first `row` is less than `other.row`, returns `-1`. # * If the first `column` is greater than `other.column`, returns `1`. # * If the first `column` is less than `other.column`, returns `-1`. - # + # # Otherwise, returns `0`. compare: (other) -> if @row > other.row @@ -118,7 +118,7 @@ class Point else 0 - # Public: Identifies if two `Point`s are equal. + # Identifies if two `Point`s are equal. # # other - The {Point} to compare against # @@ -128,7 +128,7 @@ class Point other = Point.fromObject(other) @row == other.row and @column == other.column - # Public: Identifies if one `Point` is less than another. + # Identifies if one `Point` is less than another. # # other - The {Point} to compare against # @@ -136,7 +136,7 @@ class Point isLessThan: (other) -> @compare(other) < 0 - # Public: Identifies if one `Point` is less than or equal to another. + # Identifies if one `Point` is less than or equal to another. # # other - The {Point} to compare against # @@ -144,7 +144,7 @@ class Point isLessThanOrEqual: (other) -> @compare(other) <= 0 - # Public: Identifies if one `Point` is greater than another. + # Identifies if one `Point` is greater than another. # # other - The {Point} to compare against # @@ -152,7 +152,7 @@ class Point isGreaterThan: (other) -> @compare(other) > 0 - # Public: Identifies if one `Point` is greater than or equal to another. + # Identifies if one `Point` is greater than or equal to another. # # other - The {Point} to compare against # @@ -160,23 +160,23 @@ class Point isGreaterThanOrEqual: (other) -> @compare(other) >= 0 - # Public: Converts the {Point} to a String. + # Converts the {Point} to a String. # # Returns a {String}. toString: -> "#{@row},#{@column}" - # Public: Converts the {Point} to an Array. + # Converts the {Point} to an Array. # # Returns an {Array}. toArray: -> [@row, @column] ### Internal ### - + inspect: -> "(#{@row}, #{@column})" - + # Internal: serialize: -> @toArray() From 909a951ff0bf986063b216209f95f19d9cee2607 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 16:38:36 -0700 Subject: [PATCH 50/91] Update project --- src/app/project.coffee | 118 ++++++++++++++++++++--------------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/src/app/project.coffee b/src/app/project.coffee index 38a96efcf..3c12330e9 100644 --- a/src/app/project.coffee +++ b/src/app/project.coffee @@ -24,14 +24,6 @@ class Project editSessions: null ignoredPathRegexes: null - # Public: Establishes a new project at a given path. - # - # path - The {String} name of the path - constructor: (path) -> - @setPath(path) - @editSessions = [] - @buffers = [] - ### Internal ### serialize: -> @@ -46,13 +38,21 @@ class Project ### Public ### - # Public: Retrieves the project path. + # Establishes a new project at a given path. + # + # path - The {String} name of the path + constructor: (path) -> + @setPath(path) + @editSessions = [] + @buffers = [] + + # Retrieves the project path. # # Returns a {String}. getPath: -> @rootDirectory?.path - # Public: Sets the project path. + # Sets the project path. # # path - A {String} representing the new path setPath: (path) -> @@ -66,13 +66,13 @@ class Project @trigger "path-changed" - # Public: Retrieves the name of the root directory. + # Retrieves the name of the root directory. # # Returns a {String}. getRootDirectory: -> @rootDirectory - # Public: Retrieves the names of every file (that's not `git ignore`d) in the project. + # Retrieves the names of every file (that's not `git ignore`d) in the project. # # Returns an {Array} of {String}s. getFilePaths: -> @@ -84,7 +84,7 @@ class Project deferred.resolve(paths) deferred.promise() - # Public: Identifies if a path is ignored. + # Identifies if a path is ignored. # # path - The {String} name of the path to check # @@ -96,7 +96,7 @@ class Project @ignoreRepositoryPath(path) - # Public: Identifies if a path is ignored. + # Identifies if a path is ignored. # # path - The {String} name of the path to check # @@ -104,7 +104,7 @@ class Project ignoreRepositoryPath: (path) -> config.get("core.hideGitIgnoredFiles") and git?.isPathIgnored(fsUtils.join(@getPath(), path)) - # Public: Given a path, this resolves it relative to the project directory. + # Given a path, this resolves it relative to the project directory. # # filePath - The {String} name of the path to convert # @@ -113,7 +113,7 @@ class Project filePath = fsUtils.join(@getPath(), filePath) unless filePath[0] == '/' fsUtils.absolute filePath - # Public: Given a path, this makes it relative to the project directory. + # Given a path, this makes it relative to the project directory. # # filePath - The {String} name of the path to convert # @@ -122,27 +122,27 @@ class Project return fullPath unless fullPath.lastIndexOf(@getPath()) is 0 fullPath.replace(@getPath(), "").replace(/^\//, '') - # Public: Identifies if the project is using soft tabs. + # Identifies if the project is using soft tabs. # # Returns a {Boolean}. getSoftTabs: -> @softTabs - # Public: Sets the project to use soft tabs. + # Sets the project to use soft tabs. # # softTabs - A {Boolean} which, if `true`, sets soft tabs setSoftTabs: (@softTabs) -> - # Public: Identifies if the project is using soft wrapping. + # Identifies if the project is using soft wrapping. # # Returns a {Boolean}. getSoftWrap: -> @softWrap - # Public: Sets the project to use soft wrapping. + # Sets the project to use soft wrapping. # # softTabs - A {Boolean} which, if `true`, sets soft wrapping setSoftWrap: (@softWrap) -> - # Public: Given a path to a file, this constructs and associates a new `EditSession`, showing the file. + # Given a path to a file, this constructs and associates a new `EditSession`, showing the file. # # filePath - The {String} path of the file to associate with # editSessionOptions - Options that you can pass to the `EditSession` constructor @@ -154,51 +154,21 @@ class Project else @buildEditSessionForBuffer(@bufferForPath(filePath), editSessionOptions) - # Public: Retrieves all the {EditSession}s in the project; that is, the `EditSession`s for all open files. + # Retrieves all the {EditSession}s in the project; that is, the `EditSession`s for all open files. # # Returns an {Array} of {EditSession}s. getEditSessions: -> new Array(@editSessions...) - ### Internal ### - - buildEditSessionForBuffer: (buffer, editSessionOptions) -> - options = _.extend(@defaultEditSessionOptions(), editSessionOptions) - options.project = this - options.buffer = buffer - editSession = new EditSession(options) - @editSessions.push editSession - @trigger 'edit-session-created', editSession - editSession - - defaultEditSessionOptions: -> - tabLength: @tabLength - softTabs: @getSoftTabs() - softWrap: @getSoftWrap() - - eachEditSession: (callback) -> - callback(editSession) for editSession in @getEditSessions() - @on 'edit-session-created', (editSession) -> callback(editSession) - - eachBuffer: (args...) -> - subscriber = args.shift() if args.length > 1 - callback = args.shift() - - callback(buffer) for buffer in @getBuffers() - if subscriber - subscriber.subscribe this, 'buffer-created', (buffer) -> callback(buffer) - else - @on 'buffer-created', (buffer) -> callback(buffer) - ### Public ### - # Public: Removes an {EditSession} association from the project. + # Removes an {EditSession} association from the project. # # Returns the removed {EditSession}. removeEditSession: (editSession) -> _.remove(@editSessions, editSession) - - # Public: Retrieves all the {Buffer}s in the project; that is, the buffers for all open files. + + # Retrieves all the {Buffer}s in the project; that is, the buffers for all open files. # # Returns an {Array} of {Buffer}s. getBuffers: -> @@ -207,7 +177,7 @@ class Project buffers.push editSession.buffer buffers - # Public: Given a file path, this retrieves or creates a new {Buffer}. + # Given a file path, this retrieves or creates a new {Buffer}. # # If the `filePath` already has a `buffer`, that value is used instead. Otherwise, # `text` is used as the contents of the new buffer. @@ -225,7 +195,7 @@ class Project else @buildBuffer(null, text) - # Public: Given a file path, this sets its {Buffer}. + # Given a file path, this sets its {Buffer}. # # filePath - A {String} representing a path # text - The {String} text to use as a buffer @@ -237,13 +207,13 @@ class Project @trigger 'buffer-created', buffer buffer - # Public: Removes a {Buffer} association from the project. + # Removes a {Buffer} association from the project. # # Returns the removed {Buffer}. removeBuffer: (buffer) -> _.remove(@buffers, buffer) - # Public: Performs a search across all the files in the project. + # Performs a search across all the files in the project. # # regex - A {RegExp} to search with # iterator - A {Function} callback on each file found @@ -299,4 +269,34 @@ class Project new BufferedProcess({command, args, stdout, exit}) deferred + ### Internal ### + + buildEditSessionForBuffer: (buffer, editSessionOptions) -> + options = _.extend(@defaultEditSessionOptions(), editSessionOptions) + options.project = this + options.buffer = buffer + editSession = new EditSession(options) + @editSessions.push editSession + @trigger 'edit-session-created', editSession + editSession + + defaultEditSessionOptions: -> + tabLength: @tabLength + softTabs: @getSoftTabs() + softWrap: @getSoftWrap() + + eachEditSession: (callback) -> + callback(editSession) for editSession in @getEditSessions() + @on 'edit-session-created', (editSession) -> callback(editSession) + + eachBuffer: (args...) -> + subscriber = args.shift() if args.length > 1 + callback = args.shift() + + callback(buffer) for buffer in @getBuffers() + if subscriber + subscriber.subscribe this, 'buffer-created', (buffer) -> callback(buffer) + else + @on 'buffer-created', (buffer) -> callback(buffer) + _.extend Project.prototype, EventEmitter From 6c5f310cba358f4b376722d632d07f81152fc40e Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 16:39:27 -0700 Subject: [PATCH 51/91] Update range --- src/app/range.coffee | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/app/range.coffee b/src/app/range.coffee index 72e1ede1a..834eb4e85 100644 --- a/src/app/range.coffee +++ b/src/app/range.coffee @@ -11,7 +11,7 @@ _ = require 'underscore' module.exports = class Range - # Public: Constructs a `Range` from a given object. + # Constructs a `Range` from a given object. # # object - This can be an {Array} (`[startRow, startColumn, endRow, endColumn]`) or an object `{start: Point, end: Point}` # @@ -24,7 +24,7 @@ class Range else new Range(object.start, object.end) - # Public: Constructs a `Range` from a {Point}, and the delta values beyond that point. + # Constructs a `Range` from a {Point}, and the delta values beyond that point. # # point - A {Point} to start with # rowDelta - A {Number} indicating how far from the starting {Point} the range's row should be @@ -36,7 +36,7 @@ class Range pointB = new Point(point.row + rowDelta, point.column + columnDelta) new Range(pointA, pointB) - # Public: Creates a new `Range` object based on two {Point}s. + # Creates a new `Range` object based on two {Point}s. # # pointA - The first {Point} (default: `0, 0`) # pointB - The second {Point} (default: `0, 0`) @@ -51,13 +51,13 @@ class Range @start = pointB @end = pointA - # Public: Creates an identical copy of the `Range`. + # Creates an identical copy of the `Range`. # # Returns a duplicate {Range}. copy: -> new Range(@start.copy(), @end.copy()) - # Public: Identifies if two `Range`s are equal. + # Identifies if two `Range`s are equal. # # All four points (`start.row`, `start.column`, `end.row`, `end.column`) must be # equal for this method to return `true`. @@ -71,7 +71,7 @@ class Range other.start.isEqual(@start) and other.end.isEqual(@end) - # Public: Identifies if the `Range` is on the same line. + # Identifies if the `Range` is on the same line. # # In other words, if `start.row` is equal to `end.row`. # @@ -79,7 +79,7 @@ class Range isSingleLine: -> @start.row == @end.row - # Public: Identifies if two `Range`s are on the same line. + # Identifies if two `Range`s are on the same line. # # other - A different {Range} to check against # @@ -87,11 +87,7 @@ class Range coversSameRows: (other) -> @start.row == other.start.row && @end.row == other.end.row - # Internal: - inspect: -> - "[#{@start.inspect()} - #{@end.inspect()}]" - - # Public: Adds a new point to the `Range`s `start` and `end`. + # Adds a new point to the `Range`s `start` and `end`. # # point - A new {Point} to add # @@ -99,7 +95,7 @@ class Range add: (point) -> new Range(@start.add(point), @end.add(point)) - # Public: Moves a `Range`. + # Moves a `Range`. # # In other words, the starting and ending `row` values, and the starting and ending # `column` values, are added to each other. @@ -111,7 +107,7 @@ class Range translate: (startPoint, endPoint=startPoint) -> new Range(@start.translate(startPoint), @end.translate(endPoint)) - # Public: Identifies if two `Range`s intersect each other. + # Identifies if two `Range`s intersect each other. # # otherRange - A different {Range} to check against # @@ -122,7 +118,7 @@ class Range else otherRange.intersectsWith(this) - # Public: Identifies if a second `Range` is contained within a first. + # Identifies if a second `Range` is contained within a first. # # otherRange - A different {Range} to check against # options - A hash with a single option: @@ -133,7 +129,7 @@ class Range { start, end } = Range.fromObject(otherRange) @containsPoint(start, {exclusive}) and @containsPoint(end, {exclusive}) - # Public: Identifies if a `Range` contains a {Point}. + # Identifies if a `Range` contains a {Point}. # # point - A {Point} to check against # options - A hash with a single option: @@ -147,7 +143,7 @@ class Range else point.isGreaterThanOrEqual(@start) and point.isLessThanOrEqual(@end) - # Public: Identifies if a `Range` contains a row. + # Identifies if a `Range` contains a row. # # row - A row {Number} to check against # options - A hash with a single option: @@ -156,7 +152,7 @@ class Range containsRow: (row) -> @start.row <= row <= @end.row - # Public: Constructs a union between two `Range`s. + # Constructs a union between two `Range`s. # # otherRange - A different {Range} to unionize with # @@ -166,7 +162,7 @@ class Range end = if @end.isGreaterThan(otherRange.end) then @end else otherRange.end new Range(start, end) - # Public: Identifies if a `Range` is empty. + # Identifies if a `Range` is empty. # # A `Range` is empty if its start {Point} matches its end. # @@ -174,7 +170,7 @@ class Range isEmpty: -> @start.isEqual(@end) - # Public: Calculates the difference between a `Range`s `start` and `end` points. + # Calculates the difference between a `Range`s `start` and `end` points. # # Returns a {Point}. toDelta: -> @@ -185,8 +181,13 @@ class Range columns = @end.column new Point(rows, columns) - # Public: Calculates the number of rows a `Range`s contains. + # Calculates the number of rows a `Range`s contains. # # Returns a {Number}. getRowCount: -> @end.row - @start.row + 1 + + ### Internal ### + + inspect: -> + "[#{@start.inspect()} - #{@end.inspect()}]" From 5c21d7146109f3709f95d9bf57b3c160d4da2a9e Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 16:40:33 -0700 Subject: [PATCH 52/91] Update root-view --- src/app/root-view.coffee | 45 +++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/src/app/root-view.coffee b/src/app/root-view.coffee index cc00344aa..5a055f705 100644 --- a/src/app/root-view.coffee +++ b/src/app/root-view.coffee @@ -98,11 +98,11 @@ class RootView extends View ### Public ### - # Public: Shows a dialog asking if the pane was _really_ meant to be closed. + # Shows a dialog asking if the pane was _really_ meant to be closed. confirmClose: -> @panes.confirmClose() - # Public: Given a filepath, this opens it in Atom. + # Given a filepath, this opens it in Atom. # # Returns the `EditSession` for the file URI. open: (path, options = {}) -> @@ -119,7 +119,7 @@ class RootView extends View activePane.focus() if changeFocus editSession - # Public: Updates the application's title, based on whichever file is open. + # Updates the application's title, based on whichever file is open. updateTitle: -> if projectPath = project.getPath() if item = @getActivePaneItem() @@ -129,19 +129,19 @@ class RootView extends View else @setTitle('untitled') - # Public: Sets the application's title. + # Sets the application's title. # # Returns a {String}. setTitle: (title) -> document.title = title - # Public: Retrieves all of the application's {Editor}s. + # Retrieves all of the application's {Editor}s. # # Returns an {Array} of {Editor}s. getEditors: -> @panes.find('.pane > .item-views > .editor').map(-> $(this).view()).toArray() - # Public: Retrieves all of the modified buffers that are open and unsaved. + # Retrieves all of the modified buffers that are open and unsaved. # # Returns an {Array} of {Buffer}s. getModifiedBuffers: -> @@ -151,13 +151,13 @@ class RootView extends View modifiedBuffers.push item.buffer if item.buffer.isModified() modifiedBuffers - # Public: Retrieves all of the paths to open files. + # Retrieves all of the paths to open files. # # Returns an {Array} of {String}s. getOpenBufferPaths: -> _.uniq(_.flatten(@getEditors().map (editor) -> editor.getOpenBufferPaths())) - # Public: Retrieves the pane that's currently open. + # Retrieves the pane that's currently open. # # Returns an {Pane}. getActivePane: -> @@ -173,29 +173,23 @@ class RootView extends View focusNextPane: -> @panes.focusNextPane() getFocusedPane: -> @panes.getFocusedPane() - # Internal: Destroys everything. - remove: -> - editor.remove() for editor in @getEditors() - project.destroy() - super - - # Public: Saves all of the open buffers. + # Saves all of the open buffers. saveAll: -> @panes.saveAll() - # Public: Fires a callback on each open {Pane}. + # Fires a callback on each open {Pane}. # # callback - A {Function} to call eachPane: (callback) -> @panes.eachPane(callback) - # Public: Retrieves all of the open {Pane}s. + # Retrieves all of the open {Pane}s. # # Returns an {Array} of {Pane}. getPanes: -> @panes.getPanes() - # Public: Given a {Pane}, this fetches its ID. + # Given a {Pane}, this fetches its ID. # # pane - An open {Pane} # @@ -203,21 +197,30 @@ class RootView extends View indexOfPane: (pane) -> @panes.indexOfPane(pane) - # Public: Fires a callback on each open {Editor}. + # Fires a callback on each open {Editor}. # # callback - A {Function} to call eachEditor: (callback) -> callback(editor) for editor in @getEditors() @on 'editor:attached', (e, editor) -> callback(editor) - # Public: Fires a callback on each open {EditSession}. + # Fires a callback on each open {EditSession}. # # callback - A {Function} to call eachEditSession: (callback) -> project.eachEditSession(callback) - # Public: Fires a callback on each open {Buffer}. + # Fires a callback on each open {Buffer}. # # callback - A {Function} to call eachBuffer: (callback) -> project.eachBuffer(callback) + + + ### Internal ### + + # Destroys everything. + remove: -> + editor.remove() for editor in @getEditors() + project.destroy() + super From 75b40f603a843b48fd6489e073c39c342c1b8153 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 16:40:49 -0700 Subject: [PATCH 53/91] Update screen-line --- src/app/screen-line.coffee | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/app/screen-line.coffee b/src/app/screen-line.coffee index 75b58f36a..3bac61dcf 100644 --- a/src/app/screen-line.coffee +++ b/src/app/screen-line.coffee @@ -106,9 +106,6 @@ class ScreenLine breakOutLeadingWhitespace = token.isOnlyWhitespace() if breakOutLeadingWhitespace outputTokens - # Public: Determins if the current line is commented. - # - # Returns a {Boolean}. isComment: -> for token in @tokens continue if token.scopes.length is 1 From ddb09e98e857a7b7001979f6d1e105e415e4f43b Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 16:43:34 -0700 Subject: [PATCH 54/91] Update selection --- src/app/selection.coffee | 111 ++++++++++++++++++++------------------- 1 file changed, 57 insertions(+), 54 deletions(-) diff --git a/src/app/selection.coffee b/src/app/selection.coffee index 1da79a049..36f172503 100644 --- a/src/app/selection.coffee +++ b/src/app/selection.coffee @@ -37,47 +37,49 @@ class Selection clearAutoscroll: -> @needsAutoscroll = null - + ### Public ### - # Public: Identifies if the selection is highlighting anything. + # Identifies if the selection is highlighting anything. # # Returns a {Boolean}. isEmpty: -> @getBufferRange().isEmpty() - # Public: Identifies if the selection is reversed, that is, it is highlighting "up." + # Identifies if the ending position of a marker is greater than the starting position. + # + # This can happen when, for example, you highlight text "up" in a {Buffer}. # # Returns a {Boolean}. isReversed: -> @editSession.isMarkerReversed(@marker) - # Public: Identifies if the selection is a single line. + # Identifies if the selection is a single line. # # Returns a {Boolean}. isSingleScreenLine: -> @getScreenRange().isSingleLine() - # Public: Retrieves the screen range for the selection. + # Retrieves the screen range for the selection. # # Returns a {Range}. getScreenRange: -> @editSession.getMarkerScreenRange(@marker) - # Public: Modifies the screen range for the selection. + # Modifies the screen range for the selection. # # screenRange - The new {Range} to use # options - A hash of options matching those found in {.setBufferRange} setScreenRange: (screenRange, options) -> @setBufferRange(@editSession.bufferRangeForScreenRange(screenRange), options) - # Public: Retrieves the buffer range for the selection. + # Retrieves the buffer range for the selection. # # Returns a {Range}. getBufferRange: -> @editSession.getMarkerBufferRange(@marker) - # Public: Modifies the buffer range for the selection. + # Modifies the buffer range for the selection. # # screenRange - The new {Range} to select # options - A hash of options with the following keys: @@ -92,7 +94,7 @@ class Selection @cursor.needsAutoscroll = false if options.autoscroll? @editSession.setMarkerBufferRange(@marker, bufferRange, options) - # Public: Retrieves the starting and ending buffer rows the selection is highlighting. + # Retrieves the starting and ending buffer rows the selection is highlighting. # # Returns an {Array} of two {Number}s: the starting row, and the ending row. getBufferRowRange: -> @@ -102,22 +104,17 @@ class Selection end = Math.max(start, end - 1) if range.end.column == 0 [start, end] - # Internal: - screenRangeChanged: -> - screenRange = @getScreenRange() - @trigger 'screen-range-changed', screenRange - - # Public: Retrieves the text in the selection. + # Retrieves the text in the selection. # # Returns a {String}. getText: -> @editSession.buffer.getTextInRange(@getBufferRange()) - # Public: Clears the selection, moving the marker to move to the head. + # Clears the selection, moving the marker to move to the head. clear: -> @editSession.clearMarkerTail(@marker) - # Public: Modifies the selection to mark the current word. + # Modifies the selection to mark the current word. # # Returns a {Range}. selectWord: -> @@ -131,7 +128,7 @@ class Selection expandOverWord: -> @setBufferRange(@getBufferRange().union(@cursor.getCurrentWordBufferRange())) - # Public: Selects an entire line in the {Buffer}. + # Selects an entire line in the {Buffer}. # # row - The line {Number} to select (default: the row of the cursor) selectLine: (row=@cursor.getBufferPosition().row) -> @@ -145,7 +142,7 @@ class Selection range = @getBufferRange().union(@cursor.getCurrentLineBufferRange(includeNewline: true)) @setBufferRange(range) - # Public: Selects the text from the current cursor position to a given screen position. + # Selects the text from the current cursor position to a given screen position. # # position - An instance of {Point}, with a given `row` and `column`. selectToScreenPosition: (position) -> @@ -163,61 +160,61 @@ class Selection else if @wordwise @expandOverWord() - # Public: Selects the text from the current cursor position to a given buffer position. + # Selects the text from the current cursor position to a given buffer position. # # position - An instance of {Point}, with a given `row` and `column`. selectToBufferPosition: (position) -> @modifySelection => @cursor.setBufferPosition(position) - # Public: Selects the text one position right of the cursor. + # Selects the text one position right of the cursor. selectRight: -> @modifySelection => @cursor.moveRight() - # Public: Selects the text one position left of the cursor. + # Selects the text one position left of the cursor. selectLeft: -> @modifySelection => @cursor.moveLeft() - # Public: Selects all the text one position above the cursor. + # Selects all the text one position above the cursor. selectUp: -> @modifySelection => @cursor.moveUp() - # Public: Selects all the text one position below the cursor. + # Selects all the text one position below the cursor. selectDown: -> @modifySelection => @cursor.moveDown() - # Public: Selects all the text from the current cursor position to the top of the buffer. + # Selects all the text from the current cursor position to the top of the buffer. selectToTop: -> @modifySelection => @cursor.moveToTop() - # Public: Selects all the text from the current cursor position to the bottom of the buffer. + # Selects all the text from the current cursor position to the bottom of the buffer. selectToBottom: -> @modifySelection => @cursor.moveToBottom() - # Public: Selects all the text in the buffer. + # Selects all the text in the buffer. selectAll: -> @setBufferRange(@editSession.buffer.getRange(), autoscroll: false) - # Public: Selects all the text from the current cursor position to the beginning of the line. + # Selects all the text from the current cursor position to the beginning of the line. selectToBeginningOfLine: -> @modifySelection => @cursor.moveToBeginningOfLine() - # Public: Selects all the text from the current cursor position to the end of the line. + # Selects all the text from the current cursor position to the end of the line. selectToEndOfLine: -> @modifySelection => @cursor.moveToEndOfLine() - # Public: Selects all the text from the current cursor position to the beginning of the word. + # Selects all the text from the current cursor position to the beginning of the word. selectToBeginningOfWord: -> @modifySelection => @cursor.moveToBeginningOfWord() - # Public: Selects all the text from the current cursor position to the end of the word. + # Selects all the text from the current cursor position to the end of the word. selectToEndOfWord: -> @modifySelection => @cursor.moveToEndOfWord() - # Public: Selects all the text from the current cursor position to the beginning of the next word. + # Selects all the text from the current cursor position to the beginning of the next word. selectToBeginningOfNextWord: -> @modifySelection => @cursor.moveToBeginningOfNextWord() - # Public: Moves the selection down one row. + # Moves the selection down one row. addSelectionBelow: -> range = (@goalBufferRange ? @getBufferRange()).copy() nextRow = range.end.row + 1 @@ -235,7 +232,7 @@ class Selection @editSession.addSelectionForBufferRange(range, goalBufferRange: range, suppressMerge: true) break - # Public: Moves the selection up one row. + # Moves the selection up one row. addSelectionAbove: -> range = (@goalBufferRange ? @getBufferRange()).copy() previousRow = range.end.row - 1 @@ -253,7 +250,7 @@ class Selection @editSession.addSelectionForBufferRange(range, goalBufferRange: range, suppressMerge: true) break - # Public: Replaces text at the current selection. + # Replaces text at the current selection. # # text - A {String} representing the text to add # options - A hash containing the following options: @@ -281,9 +278,9 @@ class Selection newBufferRange - # Public: Indents the selection. + # Indents the selection. # - # options - A hash with one key, `autoIndent`. If `true`, the indentation is + # options - A hash with one key, `autoIndent`. If `true`, the indentation is # performed appropriately. Otherwise, {EditSession#getTabText} is used indent: ({ autoIndent }={})-> { row, column } = @cursor.getBufferPosition() @@ -300,7 +297,7 @@ class Selection else @indentSelectedRows() - # Public: If the selection spans multiple rows, indents all of them. + # If the selection spans multiple rows, indents all of them. indentSelectedRows: -> [start, end] = @getBufferRowRange() for row in [start..end] @@ -346,7 +343,7 @@ class Selection desiredIndentString = @editSession.buildIndentString(desiredIndentLevel) line.replace(/^[\t ]*/, desiredIndentString) - # Public: Performs a backspace, removing the character found behind the selection. + # Performs a backspace, removing the character found behind the selection. backspace: -> if @isEmpty() and not @editSession.isFoldedAtScreenRow(@cursor.getScreenRow()) if @cursor.isAtBeginningOfLine() and @editSession.isFoldedAtScreenRow(@cursor.getScreenRow() - 1) @@ -356,12 +353,12 @@ class Selection @deleteSelectedText() - # Public: Performs a backspace to the beginning of the current word, removing characters found there. + # Performs a backspace to the beginning of the current word, removing characters found there. backspaceToBeginningOfWord: -> @selectToBeginningOfWord() if @isEmpty() @deleteSelectedText() - # Public: Performs a backspace to the beginning of the current line, removing characters found there. + # Performs a backspace to the beginning of the current line, removing characters found there. backspaceToBeginningOfLine: -> if @isEmpty() and @cursor.isAtBeginningOfLine() @selectLeft() @@ -369,7 +366,7 @@ class Selection @selectToBeginningOfLine() @deleteSelectedText() - # Public: Performs a delete, removing the character found ahead of the cursor position. + # Performs a delete, removing the character found ahead of the cursor position. delete: -> if @isEmpty() if @cursor.isAtEndOfLine() and fold = @editSession.largestFoldStartingAtScreenRow(@cursor.getScreenRow() + 1) @@ -378,12 +375,12 @@ class Selection @selectRight() @deleteSelectedText() - # Public: Performs a delete to the end of the current word, removing characters found there. + # Performs a delete to the end of the current word, removing characters found there. deleteToEndOfWord: -> @selectToEndOfWord() if @isEmpty() @deleteSelectedText() - # Public: Deletes the selected text. + # Deletes the selected text. deleteSelectedText: -> bufferRange = @getBufferRange() if fold = @editSession.largestFoldContainingBufferRow(bufferRange.end.row) @@ -393,7 +390,7 @@ class Selection @editSession.buffer.delete(bufferRange) unless bufferRange.isEmpty() @cursor?.setBufferPosition(bufferRange.start) - # Public: Deletes the line. + # Deletes the line. deleteLine: -> if @isEmpty() start = @cursor.getScreenRow() @@ -410,7 +407,7 @@ class Selection end-- @editSession.buffer.deleteRows(start, end) - # Public: Joins the current line with the one below it. + # Joins the current line with the one below it. # # If there selection spans more than one line, all the lines are joined together. joinLine: -> @@ -450,27 +447,27 @@ class Selection [start, end] = @getBufferRowRange() @editSession.autoIndentBufferRows(start, end) - # Public: Wraps the selected lines in comments. + # Wraps the selected lines in comments. # # Returns an {Array} of the commented {Ranges}. toggleLineComments: -> @editSession.toggleLineCommentsForBufferRows(@getBufferRowRange()...) - # Public: Performs a cut operation on the selection, until the end of the line. + # Performs a cut operation on the selection, until the end of the line. # # maintainPasteboard - A {Boolean} indicating TODO cutToEndOfLine: (maintainPasteboard) -> @selectToEndOfLine() if @isEmpty() @cut(maintainPasteboard) - # Public: Performs a cut operation on the selection. + # Performs a cut operation on the selection. # # maintainPasteboard - A {Boolean} indicating TODO cut: (maintainPasteboard=false) -> @copy(maintainPasteboard) @delete() - # Public: Performs a copy operation on the selection. + # Performs a copy operation on the selection. # # maintainPasteboard - A {Boolean} indicating TODO copy: (maintainPasteboard=false) -> @@ -484,7 +481,7 @@ class Selection pasteboard.write(text, metadata) - # Public: Folds the selection. + # Folds the selection. fold: -> range = @getBufferRange() @editSession.createFold(range.start.row, range.end.row) @@ -505,7 +502,7 @@ class Selection placeTail: -> @editSession.placeMarkerTail(@marker) - # Public: Identifies if a selection intersects with a given buffer range. + # Identifies if a selection intersects with a given buffer range. # # bufferRange - A {Range} to check against # @@ -513,7 +510,7 @@ class Selection intersectsBufferRange: (bufferRange) -> @getBufferRange().intersectsWith(bufferRange) - # Public: Identifies if a selection intersects with another selection. + # Identifies if a selection intersects with another selection. # # otherSelection - A `Selection` to check against # @@ -521,7 +518,7 @@ class Selection intersectsWith: (otherSelection) -> @getBufferRange().intersectsWith(otherSelection.getBufferRange()) - # Public: Merges two selections together. + # Merges two selections together. # # otherSelection - A `Selection` to merge with # options - A hash of options matching those found in {.setBufferRange} @@ -533,4 +530,10 @@ class Selection @goalBufferRange = otherSelection.goalBufferRange otherSelection.destroy() + ### Internal ### + + screenRangeChanged: -> + screenRange = @getScreenRange() + @trigger 'screen-range-changed', screenRange + _.extend Selection.prototype, EventEmitter From d07751605a85aec8172ae470b472cfd9923ddf16 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 16:46:48 -0700 Subject: [PATCH 55/91] Update textbuffer --- src/app/text-buffer.coffee | 168 ++++++++++++++++++------------------- 1 file changed, 82 insertions(+), 86 deletions(-) diff --git a/src/app/text-buffer.coffee b/src/app/text-buffer.coffee index 1bd928cd9..39cf60ee1 100644 --- a/src/app/text-buffer.coffee +++ b/src/app/text-buffer.coffee @@ -8,7 +8,7 @@ UndoManager = require 'undo-manager' BufferChangeOperation = require 'buffer-change-operation' BufferMarker = require 'buffer-marker' -# Public: Represents the contents of a file. +# Represents the contents of a file. # # The `Buffer` is often associated with a {File}. However, this is not always # the case, as a `Buffer` could be an unsaved chunk of text. @@ -29,7 +29,7 @@ class Buffer invalidMarkers: null refcount: 0 - # Public: Creates a new buffer. + # Creates a new buffer. # # path - A {String} representing the file path # initialText - A {String} setting the starting text @@ -95,17 +95,17 @@ class Buffer @file.on "moved", => @trigger "path-changed", this - + ### Public ### - # Public: Identifies if the buffer belongs to multiple editors. + # Identifies if the buffer belongs to multiple editors. # # For example, if the {Editor} was split. # - # Returns a {Boolean}. + # Returns a {Boolean}. hasMultipleEditors: -> @refcount > 1 - # Public: Reloads a file in the {EditSession}. + # Reloads a file in the {EditSession}. # # Essentially, this performs a force read of the file. reload: -> @@ -115,25 +115,25 @@ class Buffer @triggerModifiedStatusChanged(false) @trigger 'reloaded' - # Public: Rereads the contents of the file, and stores them in the cache. + # Rereads the contents of the file, and stores them in the cache. # # Essentially, this performs a force read of the file on disk. updateCachedDiskContents: -> @cachedDiskContents = @file.read() - # Public: Gets the file's basename--that is, the file without any directory information. + # Gets the file's basename--that is, the file without any directory information. # # Returns a {String}. getBaseName: -> @file?.getBaseName() - # Public: Retrieves the path for the file. + # Retrieves the path for the file. # # Returns a {String}. getPath: -> @file?.getPath() - # Public: Sets the path for the file. + # Sets the path for the file. # # path - A {String} representing the new file path setPath: (path) -> @@ -146,7 +146,7 @@ class Buffer @trigger "path-changed", this - # Public: Retrieves the current buffer's file extension. + # Retrieves the current buffer's file extension. # # Returns a {String}. getExtension: -> @@ -155,25 +155,25 @@ class Buffer else null - # Public: Retrieves the cached buffer contents. + # Retrieves the cached buffer contents. # # Returns a {String}. getText: -> @cachedMemoryContents ?= @getTextInRange(@getRange()) - # Public: Replaces the current buffer contents. + # Replaces the current buffer contents. # # text - A {String} containing the new buffer contents. setText: (text) -> @change(@getRange(), text, normalizeLineEndings: false) - # Public: Gets the range of the buffer contents. + # Gets the range of the buffer contents. # # Returns a new {Range}, from `[0, 0]` to the end of the buffer. getRange: -> new Range([0, 0], [@getLastRow(), @getLastLine().length]) - # Public: Given a range, returns the lines of text within it. + # Given a range, returns the lines of text within it. # # range - A {Range} object specifying your points of interest # @@ -193,13 +193,13 @@ class Buffer return multipleLines.join '' - # Public: Gets all the lines in a file. + # Gets all the lines in a file. # # Returns an {Array} of {String}s. getLines: -> @lines - # Public: Given a row, returns the line of text. + # Given a row, returns the line of text. # # row - A {Number} indicating the row. # @@ -213,7 +213,7 @@ class Buffer suggestedLineEndingForRow: (row) -> @lineEndingForRow(row) ? @lineEndingForRow(row - 1) - # Public: Given a row, returns the length of the line of text. + # Given a row, returns the length of the line of text. # # row - A {Number} indicating the row. # @@ -224,10 +224,10 @@ class Buffer lineEndingLengthForRow: (row) -> (@lineEndingForRow(row) ? '').length - # Public: Given a buffer row, this retrieves the range for that line. + # Given a buffer row, this retrieves the range for that line. # # row - A {Number} identifying the row - # options - A hash with one key, `includeNewline`, which specifies whether you + # options - A hash with one key, `includeNewline`, which specifies whether you # want to include the trailing newline # # Returns a {Range}. @@ -237,25 +237,25 @@ class Buffer else new Range([row, 0], [row, @lineLengthForRow(row)]) - # Public: Gets the number of lines in a file. + # Gets the number of lines in a file. # # Returns a {Number}. getLineCount: -> @getLines().length - # Public: Gets the row number of the last line. + # Gets the row number of the last line. # # Returns a {Number}. getLastRow: -> @getLines().length - 1 - # Public: Finds the last line in the current buffer. + # Finds the last line in the current buffer. # # Returns a {String}. getLastLine: -> @lineForRow(@getLastRow()) - # Public: Finds the last point in the current buffer. + # Finds the last point in the current buffer. # # Returns a {Point} representing the last position. getEofPosition: -> @@ -278,13 +278,13 @@ class Buffer new Point(row, index) - # Public: Given a row, this deletes it from the buffer. + # Given a row, this deletes it from the buffer. # # row - A {Number} representing the row to delete deleteRow: (row) -> @deleteRows(row, row) - # Public: Deletes a range of rows from the buffer. + # Deletes a range of rows from the buffer. # # start - A {Number} representing the starting row # end - A {Number} representing the ending row @@ -303,36 +303,29 @@ class Buffer @delete(new Range(startPoint, endPoint)) - # Public: Adds text to the end of the buffer. + # Adds text to the end of the buffer. # # text - A {String} of text to add append: (text) -> @insert(@getEofPosition(), text) - # Public: Adds text to a specific point in the buffer + # Adds text to a specific point in the buffer # # point - A {Point} in the buffer to insert into # text - A {String} of text to add insert: (point, text) -> @change(new Range(point, point), text) - # Public: Deletes text from the buffer + # Deletes text from the buffer # # range - A {Range} whose text to delete delete: (range) -> @change(range, '') - # Internal: - change: (oldRange, newText, options) -> - oldRange = Range.fromObject(oldRange) - operation = new BufferChangeOperation({buffer: this, oldRange, newText, options}) - range = @pushOperation(operation) - range - - # Public: Given a position, this clips it to a real position. + # Given a position, this clips it to a real position. # # For example, if `position`'s row exceeds the row count of the buffer, - # or if its column goes beyond a line's length, this "sanitizes" the value + # or if its column goes beyond a line's length, this "sanitizes" the value # to a real position. # # Returns the new, clipped {Point}. Note that this could be the same as `position` if no clipping was performed. @@ -346,11 +339,11 @@ class Buffer column = Math.max(position.column, 0) column = Math.min(@lineLengthForRow(row), column) new Point(row, column) - - # Public: Given a range, this clips it to a real range. + + # Given a range, this clips it to a real range. # # For example, if `range`'s row exceeds the row count of the buffer, - # or if its column goes beyond a line's length, this "sanitizes" the value + # or if its column goes beyond a line's length, this "sanitizes" the value # to a real range. # # range - The {Point} to clip @@ -364,33 +357,23 @@ class Buffer prefix: @lines[range.start.row][0...range.start.column] suffix: @lines[range.end.row][range.end.column..] - # Internal: - pushOperation: (operation, editSession) -> - if @undoManager - @undoManager.pushOperation(operation, editSession) - else - operation.do() - - # Internal: - transact: (fn) -> @undoManager.transact(fn) - - # Public: Undos the last operation. + # Undos the last operation. # # editSession - The {EditSession} associated with the buffer. undo: (editSession) -> @undoManager.undo(editSession) - # Public: Redos the last operation. + # Redos the last operation. # # editSession - The {EditSession} associated with the buffer. redo: (editSession) -> @undoManager.redo(editSession) commit: -> @undoManager.commit() abort: -> @undoManager.abort() - # Public: Saves the buffer. + # Saves the buffer. save: -> @saveAs(@getPath()) if @isModified() - # Public: Saves the buffer at a specific path. + # Saves the buffer at a specific path. # # path - The path to save at. saveAs: (path) -> @@ -403,7 +386,7 @@ class Buffer @triggerModifiedStatusChanged(false) @trigger 'saved' - # Public: Identifies if the buffer was modified. + # Identifies if the buffer was modified. # # Returns a {Boolean}. isModified: -> @@ -412,12 +395,12 @@ class Buffer else not @isEmpty() - # Public: Identifies if a buffer is in a git conflict with `HEAD`. + # Identifies if a buffer is in a git conflict with `HEAD`. # # Returns a {Boolean}. isInConflict: -> @conflict - # Public: Identifies if a buffer is empty. + # Identifies if a buffer is empty. # # Returns a {Boolean}. isEmpty: -> @lines.length is 1 and @lines[0].length is 0 @@ -425,13 +408,13 @@ class Buffer getMarkers: -> _.values(@validMarkers) - # Public: Retrieves the quantity of markers in a buffer. + # Retrieves the quantity of markers in a buffer. # # Returns a {Number}. getMarkerCount: -> _.size(@validMarkers) - # Public: Constructs a new marker at a given range. + # Constructs a new marker at a given range. # # range - The marker {Range} (representing the distance between the head and tail) # options - Options to pass to the {BufferMarker} constructor @@ -446,7 +429,7 @@ class Buffer @validMarkers[marker.id] = marker marker.id - # Public: Constructs a new marker at a given position. + # Constructs a new marker at a given position. # # position - The marker {Point}; there won't be a tail # options - Options to pass to the {BufferMarker} constructor @@ -455,7 +438,7 @@ class Buffer markPosition: (position, options) -> @markRange([position, position], _.defaults({noTail: true}, options)) - # Public: Removes the marker with the given id. + # Removes the marker with the given id. # # id - The {Number} of the ID to remove destroyMarker: (id) -> @@ -468,7 +451,7 @@ class Buffer setMarkerPosition: (args...) -> @setMarkerHeadPosition(args...) - # Public: Retrieves the position of the marker's head. + # Retrieves the position of the marker's head. # # id - A {Number} representing the marker to check # @@ -476,7 +459,7 @@ class Buffer getMarkerHeadPosition: (id) -> @validMarkers[id]?.getHeadPosition() - # Public: Sets the position of the marker's head. + # Sets the position of the marker's head. # # id - A {Number} representing the marker to change # position - The new {Point} to place the head @@ -488,7 +471,7 @@ class Buffer setMarkerHeadPosition: (id, position, options) -> @validMarkers[id]?.setHeadPosition(position) - # Public: Retrieves the position of the marker's tail. + # Retrieves the position of the marker's tail. # # id - A {Number} representing the marker to check # @@ -496,7 +479,7 @@ class Buffer getMarkerTailPosition: (id) -> @validMarkers[id]?.getTailPosition() - # Public: Sets the position of the marker's tail. + # Sets the position of the marker's tail. # # id - A {Number} representing the marker to change # position - The new {Point} to place the tail @@ -508,15 +491,15 @@ class Buffer setMarkerTailPosition: (id, position, options) -> @validMarkers[id]?.setTailPosition(position) - # Public: Retrieves the {Range} between a marker's head and its tail. - # + # Retrieves the {Range} between a marker's head and its tail. + # # id - A {Number} representing the marker to check # # Returns a {Range}. getMarkerRange: (id) -> @validMarkers[id]?.getRange() - # Public: Sets the marker's range, potentialy modifying both its head and tail. + # Sets the marker's range, potentialy modifying both its head and tail. # # id - A {Number} representing the marker to change # range - The new {Range} the marker should cover @@ -526,7 +509,7 @@ class Buffer setMarkerRange: (id, range, options) -> @validMarkers[id]?.setRange(range, options) - # Public: Sets the marker's tail to the same position as the marker's head. + # Sets the marker's tail to the same position as the marker's head. # # This only works if there isn't already a tail position. # @@ -536,13 +519,13 @@ class Buffer placeMarkerTail: (id) -> @validMarkers[id]?.placeTail() - # Public: Removes the tail from the marker. + # Removes the tail from the marker. # # id - A {Number} representing the marker to change clearMarkerTail: (id) -> @validMarkers[id]?.clearTail() - # Public: Identifies if the ending position of a marker is greater than the starting position. + # Identifies if the ending position of a marker is greater than the starting position. # # This can happen when, for example, you highlight text "up" in a {Buffer}. # @@ -552,7 +535,7 @@ class Buffer isMarkerReversed: (id) -> @validMarkers[id]?.isReversed() - # Public: Identifies if the marker's head position is equal to its tail. + # Identifies if the marker's head position is equal to its tail. # # id - A {Number} representing the marker to check # @@ -560,14 +543,14 @@ class Buffer isMarkerRangeEmpty: (id) -> @validMarkers[id]?.isRangeEmpty() - # Public: Sets a callback to be fired whenever a marker is changed. + # Sets a callback to be fired whenever a marker is changed. # # id - A {Number} representing the marker to watch # callback - A {Function} to execute observeMarker: (id, callback) -> @validMarkers[id]?.observe(callback) - # Public: Given a buffer position, this finds all markers that contain the position. + # Given a buffer position, this finds all markers that contain the position. # # bufferPosition - A {Point} to check # @@ -579,7 +562,7 @@ class Buffer ids.push(id) if marker.containsPoint(bufferPosition) ids - # Public: Identifies if a character sequence is within a certain range. + # Identifies if a character sequence is within a certain range. # # regex - The {RegExp} to check # startIndex - The starting row {Number} @@ -609,14 +592,14 @@ class Buffer matches - # Public: Scans for text in the buffer, calling a function on each match. + # Scans for text in the buffer, calling a function on each match. # # regex - A {RegExp} representing the text to find # iterator - A {Function} that's called on each match scan: (regex, iterator) -> @scanInRange(regex, @getRange(), iterator) - # Public: Scans for text in a given range, calling a function on each match. + # Scans for text in a given range, calling a function on each match. # # regex - A {RegExp} representing the text to find # range - A {Range} in the buffer to search within @@ -659,7 +642,7 @@ class Buffer break unless global and keepLooping - # Public: Scans for text in a given range _backwards_, calling a function on each match. + # Scans for text in a given range _backwards_, calling a function on each match. # # regex - A {RegExp} representing the text to find # range - A {Range} in the buffer to search within @@ -667,7 +650,7 @@ class Buffer backwardsScanInRange: (regex, range, iterator) -> @scanInRange regex, range, iterator, true - # Public: Given a row, identifies if it is blank. + # Given a row, identifies if it is blank. # # row - A row {Number} to check # @@ -675,7 +658,7 @@ class Buffer isRowBlank: (row) -> not /\S/.test @lineForRow(row) - # Public: Given a row, this finds the next row above it that's empty. + # Given a row, this finds the next row above it that's empty. # # startRow - A {Number} identifying the row to start checking at # @@ -689,7 +672,7 @@ class Buffer return row unless @isRowBlank(row) null - # Public: Given a row, this finds the next row that's blank. + # Given a row, this finds the next row that's blank. # # startRow - A row {Number} to check # @@ -702,7 +685,7 @@ class Buffer return row unless @isRowBlank(row) null - # Public: Identifies if the buffer has soft tabs anywhere. + # Identifies if the buffer has soft tabs anywhere. # # Returns a {Boolean}, usesSoftTabs: -> @@ -711,21 +694,34 @@ class Buffer return match[0][0] != '\t' undefined - # Public: Checks out the current `HEAD` revision of the file. + # Checks out the current `HEAD` revision of the file. checkoutHead: -> path = @getPath() return unless path git?.checkoutHead(path) - # Public: Checks to see if a file exists. + # Checks to see if a file exists. # # Returns a {Boolean}. fileExists: -> @file? && @file.exists() - ### Internal ### + change: (oldRange, newText, options) -> + oldRange = Range.fromObject(oldRange) + operation = new BufferChangeOperation({buffer: this, oldRange, newText, options}) + range = @pushOperation(operation) + range + + pushOperation: (operation, editSession) -> + if @undoManager + @undoManager.pushOperation(operation, editSession) + else + operation.do() + + transact: (fn) -> @undoManager.transact(fn) + scheduleModifiedEvents: -> clearTimeout(@stoppedChangingTimeout) if @stoppedChangingTimeout stoppedChangingCallback = => From 856b07197a5377e59f4391ba1df9156e28672afd Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 16:47:06 -0700 Subject: [PATCH 56/91] Update textbuffer --- src/app/text-buffer.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/text-buffer.coffee b/src/app/text-buffer.coffee index 39cf60ee1..7d12dbaa8 100644 --- a/src/app/text-buffer.coffee +++ b/src/app/text-buffer.coffee @@ -8,7 +8,7 @@ UndoManager = require 'undo-manager' BufferChangeOperation = require 'buffer-change-operation' BufferMarker = require 'buffer-marker' -# Represents the contents of a file. +# Public: Represents the contents of a file. # # The `Buffer` is often associated with a {File}. However, this is not always # the case, as a `Buffer` could be an unsaved chunk of text. From 4d239b46e26e6e646305d77bdd582b25f55c08fd Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 16:47:31 -0700 Subject: [PATCH 57/91] Update textmatescopeselector --- src/app/text-mate-scope-selector.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/text-mate-scope-selector.coffee b/src/app/text-mate-scope-selector.coffee index f21007015..46408c066 100644 --- a/src/app/text-mate-scope-selector.coffee +++ b/src/app/text-mate-scope-selector.coffee @@ -15,13 +15,13 @@ class TextMateScopeSelector source: null matcher: null - # Public: Create a new scope selector. + # Create a new scope selector. # # source - A {String} to parse as a scope selector. constructor: (@source) -> @matcher = TextMateScopeSelector.createParser().parse(@source) - # Public: Check if this scope selector matches the scopes. + # Check if this scope selector matches the scopes. # # scopes - An {Array} of {String}s. # From 31aaa23b20066aa5bfd62c2550d51bb531a49dd8 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 16:48:12 -0700 Subject: [PATCH 58/91] Update tokenizedbuffers --- src/app/tokenized-buffer.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/tokenized-buffer.coffee b/src/app/tokenized-buffer.coffee index 1d3656169..0dd8a1254 100644 --- a/src/app/tokenized-buffer.coffee +++ b/src/app/tokenized-buffer.coffee @@ -67,13 +67,13 @@ class TokenizedBuffer setVisible: (@visible) -> @tokenizeInBackground() if @visible - # Public: Retrieves the current tab length. + # Retrieves the current tab length. # # Returns a {Number}. getTabLength: -> @tabLength - # Public: Specifies the tab length. + # Specifies the tab length. # # tabLength - A {Number} that defines the new tab length. setTabLength: (@tabLength) -> @@ -263,7 +263,7 @@ class TokenizedBuffer stop() position - # Public: Gets the row number of the last line. + # Gets the row number of the last line. # # Returns a {Number}. getLastRow: -> From fb955667f53083ca8fffd2a45e09386b95712123 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 16:51:39 -0700 Subject: [PATCH 59/91] Resolve references to Internal --- src/app/atom-theme.coffee | 4 +- src/app/edit-session.coffee | 4 +- src/app/editor.coffee | 80 ++++++++++++++++++------------------- src/app/line-map.coffee | 2 - src/app/pasteboard.coffee | 2 +- src/app/point.coffee | 2 +- 6 files changed, 46 insertions(+), 48 deletions(-) diff --git a/src/app/atom-theme.coffee b/src/app/atom-theme.coffee index 832bbf0ab..10015a1eb 100644 --- a/src/app/atom-theme.coffee +++ b/src/app/atom-theme.coffee @@ -5,13 +5,13 @@ Theme = require 'theme' module.exports = class AtomTheme extends Theme - # Internal: Given a path, this loads it as a stylesheet. + # Given a path, this loads it as a stylesheet. # # stylesheetPath - A {String} to a stylesheet loadStylesheet: (stylesheetPath)-> @stylesheets[stylesheetPath] = window.loadStylesheet(stylesheetPath) - # Internal: Loads the stylesheets found in a `package.cson` file. + # Loads the stylesheets found in a `package.cson` file. load: -> if fsUtils.extension(@path) in ['.css', '.less'] @loadStylesheet(@path) diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index fc0f97fea..bcb11b9a7 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -232,7 +232,7 @@ class EditSession newIndentString = @buildIndentString(newLevel) @buffer.change([[bufferRow, 0], [bufferRow, currentIndentString.length]], newIndentString) - # Internal: Given a line, this gets the indentation level. + # Given a line, this retrieves the indentation level. # # line - A {String} in the current {Buffer}. # @@ -246,7 +246,7 @@ class EditSession else 0 - # Internal: Constructs the string used for tabs. + # Constructs the string used for tabs. buildIndentString: (number) -> if @softTabs _.multiplyString(" ", number * @getTabLength()) diff --git a/src/app/editor.coffee b/src/app/editor.coffee index a7ee69b1e..177d3c390 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -353,7 +353,7 @@ class Editor extends View # {Delegates to: EditSession.selectToScreenPosition} selectToScreenPosition: (position) -> @activeEditSession.selectToScreenPosition(position) - + # {Delegates to: EditSession.transpose} transpose: -> @activeEditSession.transpose() @@ -681,7 +681,6 @@ class Editor extends View @activeEditSession.finalizeSelections() @syncCursorAnimations() - afterAttach: (onDom) -> return unless onDom @redraw() if @redrawOnReattach @@ -820,43 +819,6 @@ class Editor extends View @scrollVertically(pixelPosition, options) @scrollHorizontally(pixelPosition) - # Internal: Scrolls the editor vertically to a given position. - scrollVertically: (pixelPosition, {center}={}) -> - scrollViewHeight = @scrollView.height() - scrollTop = @scrollTop() - scrollBottom = scrollTop + scrollViewHeight - - if center - unless scrollTop < pixelPosition.top < scrollBottom - @scrollTop(pixelPosition.top - (scrollViewHeight / 2)) - else - linesInView = @scrollView.height() / @lineHeight - maxScrollMargin = Math.floor((linesInView - 1) / 2) - scrollMargin = Math.min(@vScrollMargin, maxScrollMargin) - margin = scrollMargin * @lineHeight - desiredTop = pixelPosition.top - margin - desiredBottom = pixelPosition.top + @lineHeight + margin - if desiredBottom > scrollBottom - @scrollTop(desiredBottom - scrollViewHeight) - else if desiredTop < scrollTop - @scrollTop(desiredTop) - - # Internal: Scrolls the editor horizontally to a given position. - scrollHorizontally: (pixelPosition) -> - return if @activeEditSession.getSoftWrap() - - charsInView = @scrollView.width() / @charWidth - maxScrollMargin = Math.floor((charsInView - 1) / 2) - scrollMargin = Math.min(@hScrollMargin, maxScrollMargin) - margin = scrollMargin * @charWidth - desiredRight = pixelPosition.left + @charWidth + margin - desiredLeft = pixelPosition.left - margin - - if desiredRight > @scrollView.scrollRight() - @scrollView.scrollRight(desiredRight) - else if desiredLeft < @scrollView.scrollLeft() - @scrollView.scrollLeft(desiredLeft) - # Given a buffer range, this highlights all the folds within that range # # "Highlighting" essentially just adds the `selected` class to the line @@ -1043,6 +1005,44 @@ class Editor extends View ### Internal ### + + # Scrolls the editor vertically to a given position. + scrollVertically: (pixelPosition, {center}={}) -> + scrollViewHeight = @scrollView.height() + scrollTop = @scrollTop() + scrollBottom = scrollTop + scrollViewHeight + + if center + unless scrollTop < pixelPosition.top < scrollBottom + @scrollTop(pixelPosition.top - (scrollViewHeight / 2)) + else + linesInView = @scrollView.height() / @lineHeight + maxScrollMargin = Math.floor((linesInView - 1) / 2) + scrollMargin = Math.min(@vScrollMargin, maxScrollMargin) + margin = scrollMargin * @lineHeight + desiredTop = pixelPosition.top - margin + desiredBottom = pixelPosition.top + @lineHeight + margin + if desiredBottom > scrollBottom + @scrollTop(desiredBottom - scrollViewHeight) + else if desiredTop < scrollTop + @scrollTop(desiredTop) + + # Scrolls the editor horizontally to a given position. + scrollHorizontally: (pixelPosition) -> + return if @activeEditSession.getSoftWrap() + + charsInView = @scrollView.width() / @charWidth + maxScrollMargin = Math.floor((charsInView - 1) / 2) + scrollMargin = Math.min(@hScrollMargin, maxScrollMargin) + margin = scrollMargin * @charWidth + desiredRight = pixelPosition.left + @charWidth + margin + desiredLeft = pixelPosition.left - margin + + if desiredRight > @scrollView.scrollRight() + @scrollView.scrollRight(desiredRight) + else if desiredLeft < @scrollView.scrollLeft() + @scrollView.scrollLeft(desiredLeft) + calculateDimensions: -> fragment = $('') @renderedLines.append(fragment) @@ -1526,7 +1526,7 @@ class Editor extends View pasteboard.write(path) if path? ### Internal ### - + bindToKeyedEvent: (key, event, callback) -> binding = {} binding[key] = event diff --git a/src/app/line-map.coffee b/src/app/line-map.coffee index 33e76edf9..4b6dd15bd 100644 --- a/src/app/line-map.coffee +++ b/src/app/line-map.coffee @@ -7,8 +7,6 @@ module.exports = class LineMap maxScreenLineLength: 0 - ### Internal ### - constructor: -> @screenLines = [] diff --git a/src/app/pasteboard.coffee b/src/app/pasteboard.coffee index 7a3384905..e940977f1 100644 --- a/src/app/pasteboard.coffee +++ b/src/app/pasteboard.coffee @@ -5,7 +5,7 @@ module.exports = class Pasteboard signatureForMetadata: null - # Internal: Creates an `md5` hash of some text. + # Creates an `md5` hash of some text. # # text - A {String} to encrypt. # diff --git a/src/app/point.coffee b/src/app/point.coffee index aafaa3230..cf1329e05 100644 --- a/src/app/point.coffee +++ b/src/app/point.coffee @@ -94,7 +94,7 @@ class Point [new Point(0, column), new Point(@row, rightColumn)] - # Internal: Compares two `Point`s. + # Compares two `Point`s. # # other - The {Point} to compare against # From 5a944514f3a95c496d29018720154dddd109ccae Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 16:53:20 -0700 Subject: [PATCH 60/91] Rewrite all hash key options --- src/app/buffer-marker.coffee | 12 ++++++------ src/app/cursor.coffee | 10 +++++----- src/app/display-buffer.coffee | 14 +++++++------- src/app/editor.coffee | 10 +++++----- src/app/git.coffee | 4 ++-- src/app/range.coffee | 4 ++-- src/app/selection.coffee | 10 +++++----- src/app/text-buffer.coffee | 12 ++++++------ 8 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/app/buffer-marker.coffee b/src/app/buffer-marker.coffee index 818f62fa3..80405d435 100644 --- a/src/app/buffer-marker.coffee +++ b/src/app/buffer-marker.coffee @@ -21,8 +21,8 @@ class BufferMarker # # range - The new {Range} the marker should cover # options - A hash of options with the following keys: - # :reverse - if `true`, the marker is reversed; that is, its tail is "above" the head - # :noTail - if `true`, the marker doesn't have a tail + # reverse: if `true`, the marker is reversed; that is, its tail is "above" the head + # noTail: if `true`, the marker doesn't have a tail setRange: (range, options={}) -> @consolidateObserverNotifications false, => range = Range.fromObject(range) @@ -70,8 +70,8 @@ class BufferMarker # # newHeadPosition - The new {Point} to place the head # options - A hash with the following keys: - # :clip - if `true`, the point is [clipped]{Buffer.clipPosition} - # :bufferChanged - if `true`, indicates that the {Buffer} should trigger an event that it's changed + # clip: if `true`, the point is [clipped]{Buffer.clipPosition} + # bufferChanged: if `true`, indicates that the {Buffer} should trigger an event that it's changed # # Returns a {Point} representing the new head position. setHeadPosition: (newHeadPosition, options={}) -> @@ -88,8 +88,8 @@ class BufferMarker # # newHeadPosition - The new {Point} to place the tail # options - A hash with the following keys: - # :clip - if `true`, the point is [clipped]{Buffer.clipPosition} - # :bufferChanged - if `true`, indicates that the {Buffer} should trigger an event that it's changed + # clip: if `true`, the point is [clipped]{Buffer.clipPosition} + # bufferChanged: if `true`, indicates that the {Buffer} should trigger an event that it's changed # # Returns a {Point} representing the new tail position. setTailPosition: (newTailPosition, options={}) -> diff --git a/src/app/cursor.coffee b/src/app/cursor.coffee index c79ca05c2..856af88ae 100644 --- a/src/app/cursor.coffee +++ b/src/app/cursor.coffee @@ -57,7 +57,7 @@ class Cursor # # screenPosition - An {Array} of two numbers: the screen row, and the screen column. # options - An object with the following keys: - # :autoscroll - A {Boolean} which, if `true`, scrolls the {EditSession} to wherever the cursor moves to + # autoscroll: A {Boolean} which, if `true`, scrolls the {EditSession} to wherever the cursor moves to # setScreenPosition: (screenPosition, options={}) -> @changePosition options, => @@ -73,7 +73,7 @@ class Cursor # # bufferPosition - An {Array} of two numbers: the buffer row, and the buffer column. # options - An object with the following keys: - # :autoscroll - A {Boolean} which, if `true`, scrolls the {EditSession} to wherever the cursor moves to + # autoscroll: A {Boolean} which, if `true`, scrolls the {EditSession} to wherever the cursor moves to # setBufferPosition: (bufferPosition, options={}) -> @changePosition options, => @@ -247,7 +247,7 @@ class Cursor # Retrieves the buffer position of where the current word starts. # # options - A hash with one option: - # :wordRegex - A {RegExp} indicating what constitutes a "word" (default: {wordRegExp}) + # wordRegex: A {RegExp} indicating what constitutes a "word" (default: {wordRegExp}) # # Returns a {Range}. getBeginningOfCurrentWordBufferPosition: (options = {}) -> @@ -268,7 +268,7 @@ class Cursor # Retrieves the buffer position of where the current word ends. # # options - A hash with one option: - # :wordRegex - A {RegExp} indicating what constitutes a "word" (default: {wordRegExp}) + # wordRegex: A {RegExp} indicating what constitutes a "word" (default: {wordRegExp}) # # Returns a {Range}. getEndOfCurrentWordBufferPosition: (options = {}) -> @@ -288,7 +288,7 @@ class Cursor # Retrieves the buffer position of where the next word starts. # # options - A hash with one option: - # :wordRegex - A {RegExp} indicating what constitutes a "word" (default: {wordRegExp}) + # wordRegex: A {RegExp} indicating what constitutes a "word" (default: {wordRegExp}) # # Returns a {Range}. getBeginningOfNextWordBufferPosition: (options = {}) -> diff --git a/src/app/display-buffer.coffee b/src/app/display-buffer.coffee index 3c6d84b5a..8c5178306 100644 --- a/src/app/display-buffer.coffee +++ b/src/app/display-buffer.coffee @@ -259,8 +259,8 @@ class DisplayBuffer # bufferPosition - An object that represents a buffer position. It can be either # an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point} # options - A hash of options with the following keys: - # :wrapBeyondNewlines - - # :wrapAtSoftNewlines - + # wrapBeyondNewlines: + # wrapAtSoftNewlines: # # Returns a {Point}. screenPositionForBufferPosition: (position, options) -> @@ -271,8 +271,8 @@ class DisplayBuffer # screenPosition - An object that represents a buffer position. It can be either # an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point} # options - A hash of options with the following keys: - # :wrapBeyondNewlines - - # :wrapAtSoftNewlines - + # wrapBeyondNewlines: + # wrapAtSoftNewlines: # # Returns a {Point}. bufferPositionForScreenPosition: (position, options) -> @@ -323,9 +323,9 @@ class DisplayBuffer # # position - The {Point} to clip # options - A hash with the following values: - # :wrapBeyondNewlines - if `true`, continues wrapping past newlines - # :wrapAtSoftNewlines - if `true`, continues wrapping past soft newlines - # :screenLine - if `true`, indicates that you're using a line number, not a row number + # wrapBeyondNewlines: if `true`, continues wrapping past newlines + # wrapAtSoftNewlines: if `true`, continues wrapping past soft newlines + # screenLine: if `true`, indicates that you're using a line number, not a row number # # Returns the new, clipped {Point}. Note that this could be the same as `position` if no clipping was performed. clipScreenPosition: (position, options) -> diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 177d3c390..4d944e39e 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -529,10 +529,10 @@ class Editor extends View # Defines which characters are invisible. # # invisibles - A hash defining the invisible characters: The defaults are: - # :eol - `\u00ac` - # :space - `\u00b7` - # :tab - `\u00bb` - # :cr - `\u00a4` + # eol: `\u00ac` + # space: `\u00b7` + # tab: `\u00bb` + # cr: `\u00a4` setInvisibles: (@invisibles={}) -> _.defaults @invisibles, eol: '\u00ac' @@ -813,7 +813,7 @@ class Editor extends View # pixelPosition - An object that represents a pixel position. It can be either # an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point} # options - A hash with the following keys: - # :center - if `true`, the position is scrolled such that it's in the center of the editor + # center: if `true`, the position is scrolled such that it's in the center of the editor scrollToPixelPosition: (pixelPosition, options) -> return unless @attached @scrollVertically(pixelPosition, options) diff --git a/src/app/git.coffee b/src/app/git.coffee index d77fdc68b..91fade3a0 100644 --- a/src/app/git.coffee +++ b/src/app/git.coffee @@ -21,7 +21,7 @@ class Git # # path - The {String} representing the path to your git working directory # options - A hash with the following keys: - # :refreshOnWindowFocus - If `true`, {#refreshIndex} and {#refreshStatus} are called on focus + # refreshOnWindowFocus: If `true`, {#refreshIndex} and {#refreshStatus} are called on focus constructor: (path, options={}) -> @repo = GitUtils.open(path) unless @repo? @@ -62,7 +62,7 @@ class Git # # path - The git repository to open # options - A hash with one key: - # :refreshOnWindowFocus - A {Boolean} that identifies if the windows should refresh + # refreshOnWindowFocus: A {Boolean} that identifies if the windows should refresh # # Returns a new {Git} object. @open: (path, options) -> diff --git a/src/app/range.coffee b/src/app/range.coffee index 834eb4e85..5be4519c4 100644 --- a/src/app/range.coffee +++ b/src/app/range.coffee @@ -122,7 +122,7 @@ class Range # # otherRange - A different {Range} to check against # options - A hash with a single option: - # :exclusive - A {Boolean} which, if `true`, indicates that no {Point}s in the `Range` can be equal + # exclusive: A {Boolean} which, if `true`, indicates that no {Point}s in the `Range` can be equal # # Returns a {Boolean}. containsRange: (otherRange, {exclusive} = {}) -> @@ -133,7 +133,7 @@ class Range # # point - A {Point} to check against # options - A hash with a single option: - # :exclusive - A {Boolean} which, if `true`, indicates that no {Point}s in the `Range` can be equal + # exclusive: A {Boolean} which, if `true`, indicates that no {Point}s in the `Range` can be equal # # Returns a {Boolean}. containsPoint: (point, {exclusive} = {}) -> diff --git a/src/app/selection.coffee b/src/app/selection.coffee index 36f172503..04f38381a 100644 --- a/src/app/selection.coffee +++ b/src/app/selection.coffee @@ -83,8 +83,8 @@ class Selection # # screenRange - The new {Range} to select # options - A hash of options with the following keys: - # :preserveFolds - if `true`, the fold settings are preserved after the selection moves - # :autoscroll - if `true`, the {EditSession} scrolls to the new selection + # preserveFolds: if `true`, the fold settings are preserved after the selection moves + # autoscroll: if `true`, the {EditSession} scrolls to the new selection setBufferRange: (bufferRange, options={}) -> bufferRange = Range.fromObject(bufferRange) @needsAutoscroll = options.autoscroll @@ -254,9 +254,9 @@ class Selection # # text - A {String} representing the text to add # options - A hash containing the following options: - # :normalizeIndent - TODO - # :select - if `true`, selects the newly added text - # :autoIndent - if `true`, indents the newly added text appropriately + # normalizeIndent: TODO + # select: if `true`, selects the newly added text + # autoIndent: if `true`, indents the newly added text appropriately insertText: (text, options={}) -> oldBufferRange = @getBufferRange() @editSession.destroyFoldsContainingBufferRow(oldBufferRange.end.row) diff --git a/src/app/text-buffer.coffee b/src/app/text-buffer.coffee index 7d12dbaa8..9e030c63f 100644 --- a/src/app/text-buffer.coffee +++ b/src/app/text-buffer.coffee @@ -464,8 +464,8 @@ class Buffer # id - A {Number} representing the marker to change # position - The new {Point} to place the head # options - A hash with the following keys: - # :clip - if `true`, the point is [clipped]{Buffer.clipPosition} - # :bufferChanged - if `true`, indicates that the {Buffer} should trigger an event that it's changed + # clip: if `true`, the point is [clipped]{Buffer.clipPosition} + # bufferChanged: if `true`, indicates that the {Buffer} should trigger an event that it's changed # # Returns a {Point} representing the new head position. setMarkerHeadPosition: (id, position, options) -> @@ -484,8 +484,8 @@ class Buffer # id - A {Number} representing the marker to change # position - The new {Point} to place the tail # options - A hash with the following keys: - # :clip - if `true`, the point is [clipped]{Buffer.clipPosition} - # :bufferChanged - if `true`, indicates that the {Buffer} should trigger an event that it's changed + # clip: if `true`, the point is [clipped]{Buffer.clipPosition} + # bufferChanged: if `true`, indicates that the {Buffer} should trigger an event that it's changed # # Returns a {Point} representing the new tail position. setMarkerTailPosition: (id, position, options) -> @@ -504,8 +504,8 @@ class Buffer # id - A {Number} representing the marker to change # range - The new {Range} the marker should cover # options - A hash of options with the following keys: - # :reverse - if `true`, the marker is reversed; that is, its tail is "above" the head - # :noTail - if `true`, the marker doesn't have a tail + # reverse: if `true`, the marker is reversed; that is, its tail is "above" the head + # noTail: if `true`, the marker doesn't have a tail setMarkerRange: (id, range, options) -> @validMarkers[id]?.setRange(range, options) From f47eea1e6cb064e9527a0fdfdf4f8b0c174c5e7c Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 17:03:12 -0700 Subject: [PATCH 61/91] :lipstick: --- src/app/config.coffee | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/app/config.coffee b/src/app/config.coffee index 8d4d38afc..c1baf63e8 100644 --- a/src/app/config.coffee +++ b/src/app/config.coffee @@ -110,7 +110,6 @@ class Config _.valueForKeyPath(@settings, keyPath) ? _.valueForKeyPath(@defaultSettings, keyPath) - # Retrieves the setting for the given key as an integer. # # keyPath - The {String} name of the key to retrieve @@ -130,7 +129,7 @@ class Config # or `defaultValue` if the key value isn't greater than zero. getPositiveInt: (keyPath, defaultValue) -> Math.max(@getInt(keyPath), 0) or defaultValue - + # Sets the value for a configuration setting. # # This value is stored in Atom's internal configuration file. @@ -166,7 +165,7 @@ class Config subscription ### Internal ### - + setDefaults: (keyPath, defaults) -> keys = keyPath.split('.') hash = @defaultSettings From 7e15a343f36ce1af5591d581db69aeb7ddc2665c Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 1 May 2013 17:03:20 -0700 Subject: [PATCH 62/91] Add primary/secondary line classes to select list This will be a style that can be used by extending classes to provide two lines that don't wrap with color and padding. This is initially used by symbols view and fuzzy finder. --- .../fuzzy-finder/lib/fuzzy-finder-view.coffee | 6 +++--- .../fuzzy-finder/stylesheets/fuzzy-finder.less | 7 ------- .../symbols-view/lib/symbols-view.coffee | 6 +++--- .../symbols-view/spec/symbols-view-spec.coffee | 18 +++++++++--------- static/select-list.less | 10 ++++++++++ themes/atom-dark-ui/select-list.less | 4 ++-- 6 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee b/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee index 56d6074d8..72b8d03b2 100644 --- a/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee +++ b/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee @@ -37,7 +37,7 @@ class FuzzyFinderView extends SelectList itemForElement: (path) -> $$ -> - @li => + @li class: 'two-lines', => if git? status = git.statuses[path] if git.isStatusNew(status) @@ -59,8 +59,8 @@ class FuzzyFinderView extends SelectList else typeClass = 'text-name' - @div fsUtils.base(path), class: "file #{typeClass}" - @div project.relativize(path), class: 'path' + @div fsUtils.base(path), class: "primary-line file #{typeClass}" + @div project.relativize(path), class: 'secondary-line path' openPath: (path) -> rootView.open(path, {@allowActiveEditorChange}) if path diff --git a/src/packages/fuzzy-finder/stylesheets/fuzzy-finder.less b/src/packages/fuzzy-finder/stylesheets/fuzzy-finder.less index c72afa89a..3371e6fcd 100644 --- a/src/packages/fuzzy-finder/stylesheets/fuzzy-finder.less +++ b/src/packages/fuzzy-finder/stylesheets/fuzzy-finder.less @@ -5,9 +5,6 @@ } .path { - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; color: rgba(0, 0, 0, 0.5); margin-left: 26px; font-size: .9em; @@ -37,10 +34,6 @@ } .file { - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - &:before { font-family: 'Octicons Regular'; font-size: 16px; diff --git a/src/packages/symbols-view/lib/symbols-view.coffee b/src/packages/symbols-view/lib/symbols-view.coffee index b5ff72e57..9881d6a5f 100644 --- a/src/packages/symbols-view/lib/symbols-view.coffee +++ b/src/packages/symbols-view/lib/symbols-view.coffee @@ -25,13 +25,13 @@ class SymbolsView extends SelectList itemForElement: ({position, name, file}) -> $$ -> - @li => - @span name + @li class: 'two-lines', => + @div name, class: 'primary-line' if position text = "Line #{position.row + 1}" else text = fsUtils.base(file) - @div text, class: 'right function-details' + @div text, class: 'secondary-line' toggleFileSymbols: -> if @hasParent() diff --git a/src/packages/symbols-view/spec/symbols-view-spec.coffee b/src/packages/symbols-view/spec/symbols-view-spec.coffee index 8c43ecf8b..f8f87cdb5 100644 --- a/src/packages/symbols-view/spec/symbols-view-spec.coffee +++ b/src/packages/symbols-view/spec/symbols-view-spec.coffee @@ -30,10 +30,10 @@ describe "SymbolsView", -> expect(symbolsView.find('.loading')).toBeEmpty() expect(rootView.find('.symbols-view')).toExist() expect(symbolsView.list.children('li').length).toBe 2 - expect(symbolsView.list.children('li:first').find('span')).toHaveText 'quicksort' - expect(symbolsView.list.children('li:first').find('.function-details')).toHaveText 'Line 1' - expect(symbolsView.list.children('li:last').find('span')).toHaveText 'quicksort.sort' - expect(symbolsView.list.children('li:last').find('.function-details')).toHaveText 'Line 2' + expect(symbolsView.list.children('li:first').find('.primary-line')).toHaveText 'quicksort' + expect(symbolsView.list.children('li:first').find('.secondary-line')).toHaveText 'Line 1' + expect(symbolsView.list.children('li:last').find('.primary-line')).toHaveText 'quicksort.sort' + expect(symbolsView.list.children('li:last').find('.secondary-line')).toHaveText 'Line 2' expect(symbolsView).not.toHaveClass "error" expect(symbolsView.error).not.toBeVisible() @@ -175,7 +175,7 @@ describe "SymbolsView", -> editor.trigger 'symbols-view:go-to-declaration' symbolsView = rootView.find('.symbols-view').view() expect(symbolsView.list.children('li').length).toBe 1 - expect(symbolsView.list.children('li:first').find('span')).toHaveText 'tagged.js' + expect(symbolsView.list.children('li:first').find('.primary-line')).toHaveText 'tagged.js' describe "project symbols", -> it "displays all tags", -> @@ -192,10 +192,10 @@ describe "SymbolsView", -> expect(symbolsView.find('.loading')).toBeEmpty() expect(rootView.find('.symbols-view')).toExist() expect(symbolsView.list.children('li').length).toBe 4 - expect(symbolsView.list.children('li:first').find('span')).toHaveText 'callMeMaybe' - expect(symbolsView.list.children('li:first').find('.function-details')).toHaveText 'tagged.js' - expect(symbolsView.list.children('li:last').find('span')).toHaveText 'thisIsCrazy' - expect(symbolsView.list.children('li:last').find('.function-details')).toHaveText 'tagged.js' + expect(symbolsView.list.children('li:first').find('.primary-line')).toHaveText 'callMeMaybe' + expect(symbolsView.list.children('li:first').find('.secondary-line')).toHaveText 'tagged.js' + expect(symbolsView.list.children('li:last').find('.primary-line')).toHaveText 'thisIsCrazy' + expect(symbolsView.list.children('li:last').find('.secondary-line')).toHaveText 'tagged.js' expect(symbolsView).not.toHaveClass "error" expect(symbolsView.error).not.toBeVisible() diff --git a/static/select-list.less b/static/select-list.less index affeec33c..32ac015bf 100644 --- a/static/select-list.less +++ b/static/select-list.less @@ -19,6 +19,16 @@ li { padding: 10px; display: block; + + &.two-lines { + padding: 5px 10px 5px 10px; + } + + .primary-line, .secondary-line { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + } } li:last-child { diff --git a/themes/atom-dark-ui/select-list.less b/themes/atom-dark-ui/select-list.less index 4af644466..8b854025e 100644 --- a/themes/atom-dark-ui/select-list.less +++ b/themes/atom-dark-ui/select-list.less @@ -36,7 +36,7 @@ } .right, - .path { + .secondary-line { color: #777; } @@ -45,7 +45,7 @@ } .selected .right, - .selected .path { + .selected .secondary-line { color: #ccc; } From aebaf688a9b58f315bdf9dd16e1e1bc8082fd85c Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 1 May 2013 17:13:07 -0700 Subject: [PATCH 63/91] Add secondar-line class to light theme --- themes/atom-light-ui/select-list.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/themes/atom-light-ui/select-list.less b/themes/atom-light-ui/select-list.less index 1864cd06c..df068a0d7 100644 --- a/themes/atom-light-ui/select-list.less +++ b/themes/atom-light-ui/select-list.less @@ -30,12 +30,12 @@ } .selected .right, - .selected .directory { + .selected .secondary-line { color: #333; } .right, - .directory { + .secondary-line { color: #777; } From 698f670350c0c4b641a2ed4366a4613a4fa27757 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 1 May 2013 17:13:46 -0700 Subject: [PATCH 64/91] Remove mini class from grammar selector Grammars were wrapping when miniaturized even though there was plenty of room. --- src/packages/grammar-selector/lib/grammar-selector.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/grammar-selector/lib/grammar-selector.coffee b/src/packages/grammar-selector/lib/grammar-selector.coffee index 6bf1d5721..70499295e 100644 --- a/src/packages/grammar-selector/lib/grammar-selector.coffee +++ b/src/packages/grammar-selector/lib/grammar-selector.coffee @@ -5,7 +5,7 @@ _ = require 'underscore' module.exports = class GrammarSelector extends SelectList - @viewClass: -> "#{super} grammar-selector from-top overlay mini" + @viewClass: -> "#{super} grammar-selector from-top overlay" @activate: -> rootView.command 'grammar-selector:show', '.editor', => new GrammarSelector() From 662ddf9fd6562b9951dfccfa65835fc0635a4a2a Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 1 May 2013 17:21:05 -0700 Subject: [PATCH 65/91] Update documentation to push threshold back past 80% --- src/app/display-buffer.coffee | 5 ++++ src/app/edit-session.coffee | 18 +++++++------- src/app/selection-view.coffee | 2 +- src/app/selection.coffee | 7 +++++- src/app/text-buffer.coffee | 31 +++++++++++++++++++++++-- src/app/text-mate-scope-selector.coffee | 2 +- src/app/token.coffee | 4 ++++ 7 files changed, 56 insertions(+), 13 deletions(-) diff --git a/src/app/display-buffer.coffee b/src/app/display-buffer.coffee index 8c5178306..bc26e908d 100644 --- a/src/app/display-buffer.coffee +++ b/src/app/display-buffer.coffee @@ -306,12 +306,17 @@ class DisplayBuffer setTabLength: (tabLength) -> @tokenizedBuffer.setTabLength(tabLength) + # Retrieves the grammar for the buffer. getGrammar: -> @tokenizedBuffer.grammar + # Sets the grammar for the buffer. + # + # grammar - Sets the new grammar rules setGrammar: (grammar) -> @tokenizedBuffer.setGrammar(grammar) + # Reloads the current grammar. reloadGrammar: -> @tokenizedBuffer.reloadGrammar() diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index bcb11b9a7..31f224edf 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -775,7 +775,7 @@ class EditSession destroyMarker: (args...) -> @displayBuffer.destroyMarker(args...) - # {Delegates to: TextBuffer.destroyMarker} + # {Delegates to: Buffer.getMarkerCount} getMarkerCount: -> @buffer.getMarkerCount() @@ -1276,6 +1276,12 @@ class EditSession expandLastSelectionOverWord: -> @getLastSelection().expandOverWord() + # Selects the buffer range of the given marker. + # + # id - A {Number} indicating the marker's id + # + # Returns a {Boolean} value that is `true` if the marker contains a buffer + # range. selectMarker: (id) -> if bufferRange = @getMarkerBufferRange(id) @setSelectedBufferRange(bufferRange) @@ -1333,19 +1339,15 @@ class EditSession @setCursorBufferPosition(cursorPosition) if cursorPosition cursorPosition = null - # Retrieves the current {EditSession}'s grammar. - # - # Returns a {String} indicating the language's grammar rules. + # {Delegates to: DisplayBuffer.getGrammar} getGrammar: -> @displayBuffer.getGrammar() - # Sets the current {EditSession}'s grammar. - # - # grammar - A {String} indicating the language's grammar rules. + # {Delegates to: DisplayBuffer.setGrammar} setGrammar: (grammar) -> @displayBuffer.setGrammar(grammar) - # Reloads the current grammar. + # {Delegates to: DisplayBuffer.reloadGrammar} reloadGrammar: -> @displayBuffer.reloadGrammar() diff --git a/src/app/selection-view.coffee b/src/app/selection-view.coffee index 2adcf7627..bf9ccc777 100644 --- a/src/app/selection-view.coffee +++ b/src/app/selection-view.coffee @@ -2,10 +2,10 @@ Point = require 'point' Range = require 'range' {View, $$} = require 'space-pen' +# Internal: module.exports = class SelectionView extends View - # Internal: Establishes the DOM for the selection view. @content: -> @div class: 'selection' diff --git a/src/app/selection.coffee b/src/app/selection.coffee index 04f38381a..6ce2c15f8 100644 --- a/src/app/selection.coffee +++ b/src/app/selection.coffee @@ -281,7 +281,7 @@ class Selection # Indents the selection. # # options - A hash with one key, `autoIndent`. If `true`, the indentation is - # performed appropriately. Otherwise, {EditSession#getTabText} is used + # performed appropriately. Otherwise, {EditSession.getTabText} is used indent: ({ autoIndent }={})-> { row, column } = @cursor.getBufferPosition() @@ -499,6 +499,11 @@ class Selection fn() @retainSelection = false + # Sets the marker's tail to the same position as the marker's head. + # + # This only works if there isn't already a tail position. + # + # Returns a {Point} representing the new tail position. placeTail: -> @editSession.placeMarkerTail(@marker) diff --git a/src/app/text-buffer.coffee b/src/app/text-buffer.coffee index 9e030c63f..93a9a2e6f 100644 --- a/src/app/text-buffer.coffee +++ b/src/app/text-buffer.coffee @@ -207,6 +207,11 @@ class Buffer lineForRow: (row) -> @lines[row] + # Given a row, returns its line ending. + # + # row - A {Number} indicating the row. + # + # Returns a {String}, or `undefined` if `row` is the final row. lineEndingForRow: (row) -> @lineEndings[row] unless row is @getLastRow() @@ -221,6 +226,11 @@ class Buffer lineLengthForRow: (row) -> @lines[row].length + # Given a row, returns the length of the line ending + # + # row - A {Number} indicating the row. + # + # Returns a {Number}. lineEndingLengthForRow: (row) -> (@lineEndingForRow(row) ? '').length @@ -366,8 +376,6 @@ class Buffer # # editSession - The {EditSession} associated with the buffer. redo: (editSession) -> @undoManager.redo(editSession) - commit: -> @undoManager.commit() - abort: -> @undoManager.abort() # Saves the buffer. save: -> @@ -405,6 +413,9 @@ class Buffer # Returns a {Boolean}. isEmpty: -> @lines.length is 1 and @lines[0].length is 0 + # Retrieves all the valid markers in the buffer. + # + # Returns an {Array} of {BufferMarker}s. getMarkers: -> _.values(@validMarkers) @@ -445,9 +456,21 @@ class Buffer delete @validMarkers[id] delete @invalidMarkers[id] + # Retrieves the positions of every marker's head. + # + # Returns an {Array} of {Point}s. getMarkerPosition: (args...) -> @getMarkerHeadPosition(args...) + # Sets the positions of every marker's head. + # + # id - A {Number} representing the marker to change + # position - The new {Point} to place the head + # options - A hash with the following keys: + # clip: if `true`, the point is [clipped]{Buffer.clipPosition} + # bufferChanged: if `true`, indicates that the {Buffer} should trigger an event that it's changed + # + # Returns a {Point} representing the new head position. setMarkerPosition: (args...) -> @setMarkerHeadPosition(args...) @@ -708,6 +731,10 @@ class Buffer ### Internal ### + commit: -> @undoManager.commit() + + abort: -> @undoManager.abort() + change: (oldRange, newText, options) -> oldRange = Range.fromObject(oldRange) operation = new BufferChangeOperation({buffer: this, oldRange, newText, options}) diff --git a/src/app/text-mate-scope-selector.coffee b/src/app/text-mate-scope-selector.coffee index 46408c066..d5713356b 100644 --- a/src/app/text-mate-scope-selector.coffee +++ b/src/app/text-mate-scope-selector.coffee @@ -1,7 +1,7 @@ PEG = require 'pegjs' fsUtils = require 'fs-utils' -# Public: Test a stack of scopes to see if they match a scope selector. +# Internal: Test a stack of scopes to see if they match a scope selector. module.exports = class TextMateScopeSelector @parser: null diff --git a/src/app/token.coffee b/src/app/token.coffee index 01707dd2d..5b2725612 100644 --- a/src/app/token.coffee +++ b/src/app/token.coffee @@ -7,10 +7,14 @@ class Token isAtomic: null isHardTab: null + ### Internal ### + constructor: ({@value, @scopes, @isAtomic, @bufferDelta, @isHardTab}) -> @screenDelta = @value.length @bufferDelta ?= @screenDelta + ### Public ### + isEqual: (other) -> @value == other.value and _.isEqual(@scopes, other.scopes) and !!@isAtomic == !!other.isAtomic From 6eb88278ae7651cc78e422273a3f2868b17b117f Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 1 May 2013 17:26:37 -0700 Subject: [PATCH 66/91] Only return false if focused Previously the editor would not gain focus if the underlayer was clicked. --- src/app/editor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/editor.coffee b/src/app/editor.coffee index ed720127f..abfdafc72 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -823,7 +823,7 @@ class Editor extends View @underlayer.on 'mousedown', (e) => @renderedLines.trigger(e) - false + false if @isFocused @overlayer.on 'mousedown', (e) => @overlayer.hide() From 559f76e887eb837f24439712d50b696341b49b30 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Wed, 1 May 2013 17:36:36 -0700 Subject: [PATCH 67/91] Dragging a tab from one pane to another clears all placement markers Closes #528 --- src/packages/tabs/lib/tab-bar-view.coffee | 27 ++++++++++++++--------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/packages/tabs/lib/tab-bar-view.coffee b/src/packages/tabs/lib/tab-bar-view.coffee index 6b524c2a7..6d6ec95c7 100644 --- a/src/packages/tabs/lib/tab-bar-view.coffee +++ b/src/packages/tabs/lib/tab-bar-view.coffee @@ -95,8 +95,7 @@ class TabBarView extends View onDragEnd: (event) => @find(".is-dragging").removeClass 'is-dragging' - @children('.is-drop-target').removeClass 'is-drop-target' - @children('.drop-target-is-after').removeClass 'drop-target-is-after' + @removeDropTargetClasses() onDragOver: (event) => unless event.originalEvent.dataTransfer.getData('atom-event') is 'true' @@ -104,18 +103,16 @@ class TabBarView extends View event.stopPropagation() return + @removeDropTargetClasses() + event.preventDefault() - currentDropTargetIndex = @find(".is-drop-target").index() newDropTargetIndex = @getDropTargetIndex(event) - if newDropTargetIndex != currentDropTargetIndex - @children().removeClass 'is-drop-target drop-target-is-after' - sortableObjects = @find(".sortable") - if newDropTargetIndex < sortableObjects.length - sortableObjects.eq(newDropTargetIndex).addClass 'is-drop-target' - else - sortableObjects.eq(newDropTargetIndex - 1).addClass 'drop-target-is-after' - + sortableObjects = @find(".sortable") + if newDropTargetIndex < sortableObjects.length + sortableObjects.eq(newDropTargetIndex).addClass 'is-drop-target' + else + sortableObjects.eq(newDropTargetIndex - 1).addClass 'drop-target-is-after' onDrop: (event) => unless event.originalEvent.dataTransfer.getData('atom-event') is 'true' @@ -123,6 +120,9 @@ class TabBarView extends View event.stopPropagation() return + @find(".is-dragging").removeClass 'is-dragging' + @removeDropTargetClasses() + event.stopPropagation() dataTransfer = event.originalEvent.dataTransfer @@ -142,6 +142,11 @@ class TabBarView extends View toPane.showItem(item) toPane.focus() + removeDropTargetClasses: -> + console.log rootView.find('.tabs .is-drop-target').length + rootView.find('.tabs .is-drop-target').removeClass 'is-drop-target' + rootView.find('.tabs .drop-target-is-after').removeClass 'drop-target-is-after' + getDropTargetIndex: (event) -> el = $(event.target).closest('.sortable') el = $(event.target).find('.sortable').last() if el.length == 0 From 7a9d4084256b85bfa06f63837ea366eecdc0e3e7 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Wed, 1 May 2013 17:41:25 -0700 Subject: [PATCH 68/91] Add rootView to tab spec --- src/packages/tabs/spec/tabs-spec.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/packages/tabs/spec/tabs-spec.coffee b/src/packages/tabs/spec/tabs-spec.coffee index 953ef3bd7..6d53313f2 100644 --- a/src/packages/tabs/spec/tabs-spec.coffee +++ b/src/packages/tabs/spec/tabs-spec.coffee @@ -32,6 +32,7 @@ describe "TabBarView", -> serialize: -> { deserializer: 'TestView', @title, @longTitle } beforeEach -> + window.rootView = new RootView registerDeserializer(TestView) item1 = new TestView('Item 1') item2 = new TestView('Item 2') From 35cf96e15fde93386e19f3f3dc9490307512fced Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 1 May 2013 20:54:09 -0700 Subject: [PATCH 69/91] Render empty line invisibles at correct position End of line invisibles are not rendered at the correct position for empty lines instead of always after the last indent guide span. Closes #456 --- spec/app/editor-spec.coffee | 16 ++++++++++++ src/app/editor.coffee | 50 ++++++++++++++++++++++++++++-------- static/editor.less | 2 -- themes/atom-dark-syntax.css | 5 ++++ themes/atom-light-syntax.css | 5 ++++ 5 files changed, 65 insertions(+), 13 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 7419ef4ee..7a9f98d07 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -1551,6 +1551,22 @@ describe "Editor", -> expect(editor.renderedLines.find('.line:eq(10) .indent-guide').length).toBe 2 expect(editor.renderedLines.find('.line:eq(10) .indent-guide').text()).toBe ' ' + describe "when the line is empty and end of show invisibles are enabled", -> + it "renders the indent guides interleaved the end of line invisibles", -> + editor.attachToDom() + config.set("editor.showIndentGuide", true) + config.set("editor.showInvisibles", true) + eol = editor.invisibles?.eol + + expect(editor.renderedLines.find('.line:eq(10) .indent-guide').length).toBe 1 + expect(editor.renderedLines.find('.line:eq(10) .indent-guide').text()).toBe "#{eol} " + + editor.setCursorBufferPosition([9]) + editor.indent() + + expect(editor.renderedLines.find('.line:eq(10) .indent-guide').length).toBe 2 + expect(editor.renderedLines.find('.line:eq(10) .indent-guide').text()).toBe "#{eol} " + describe "when soft-wrap is enabled", -> beforeEach -> editor.attachToDom() diff --git a/src/app/editor.coffee b/src/app/editor.coffee index abfdafc72..44037d13a 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -1546,7 +1546,22 @@ class Editor extends View htmlLines.push(@buildLineHtml(line, screenRow++)) htmlLines.join('\n\n') - buildEmptyLineHtml: (screenRow) -> + buildEndOfLineInvisibles: (screenLine) -> + invisibles = [] + for invisible in @getEndOfLineInvisibles(screenLine) + invisibles.push("#{invisible}") + invisibles.join('') + + getEndOfLineInvisibles: (screenLine) -> + return [] unless @showInvisibles and @invisibles + return [] if @mini or screenLine.isSoftWrapped() + + invisibles = [] + invisibles.push(@invisibles.cr) if @invisibles.cr and screenLine.lineEnding is '\r\n' + invisibles.push(@invisibles.eol) if @invisibles.eol + invisibles + + buildEmptyLineHtml: (screenLine, screenRow) -> if not @mini and @showIndentGuide indentation = 0 while --screenRow >= 0 @@ -1557,10 +1572,28 @@ class Editor extends View break if indentation > 0 - indentationHtml = "#{_.multiplyString(' ', @activeEditSession.getTabLength())}" - return _.multiplyString(indentationHtml, indentation) + tabLength = @activeEditSession.getTabLength() + invisibles = @getEndOfLineInvisibles(screenLine) + indentGuideHtml = [] + for level in [0...indentation] + indentLevelHtml = [""] + for characterPosition in [0...tabLength] + if invisible = invisibles.shift() + indentLevelHtml.push("#{invisible}") + else + indentLevelHtml.push(' ') + indentLevelHtml.push("") + indentGuideHtml.push(indentLevelHtml.join('')) - return ' ' unless @showInvisibles + for invisible in invisibles + indentGuideHtml.push("#{invisible}") + return indentGuideHtml.join('') + + invisibles = @buildEndOfLineInvisibles(screenLine) + if invisibles.length > 0 + invisibles + else + ' ' buildLineHtml: (screenLine, screenRow) -> scopeStack = [] @@ -1599,7 +1632,7 @@ class Editor extends View invisibles = @invisibles if @showInvisibles if screenLine.text == '' - html = @buildEmptyLineHtml(screenRow) + html = @buildEmptyLineHtml(screenLine, screenRow) line.push(html) if html else firstNonWhitespacePosition = screenLine.text.search(/\S/) @@ -1615,12 +1648,7 @@ class Editor extends View position += token.value.length popScope() while scopeStack.length > 0 - if invisibles and not @mini and not screenLine.isSoftWrapped() - if invisibles.cr and screenLine.lineEnding is '\r\n' - line.push("#{invisibles.cr}") - if invisibles.eol - line.push("#{invisibles.eol}") - + line.push(@buildEndOfLineInvisibles(screenLine)) unless screenLine.text == '' line.push("") if fold line.push('') diff --git a/static/editor.less b/static/editor.less index 699ffe3bc..cf3a4eb87 100644 --- a/static/editor.less +++ b/static/editor.less @@ -81,13 +81,11 @@ } .editor .invisible-character { - opacity: 0.2; font-weight: normal !important; font-style: normal !important; } .editor .indent-guide { - opacity: 0.2; display: inline-block; box-shadow: inset 1px 0px; } diff --git a/themes/atom-dark-syntax.css b/themes/atom-dark-syntax.css index e14796b22..a095d3018 100644 --- a/themes/atom-dark-syntax.css +++ b/themes/atom-dark-syntax.css @@ -3,6 +3,11 @@ color: #c5c8c6; } +.editor .invisible-character, +.editor .indent-guide { + color: rgba(197, 200, 198, .2); +} + .editor .gutter .line-number.fold, .editor .gutter .line-number:after, .editor .fold-marker:after { diff --git a/themes/atom-light-syntax.css b/themes/atom-light-syntax.css index 248630da8..774a3c561 100644 --- a/themes/atom-light-syntax.css +++ b/themes/atom-light-syntax.css @@ -3,6 +3,11 @@ color: #555; } +.editor .invisible-character, +.editor .indent-guide { + color: rgba(85, 85, 85, .2); +} + .editor .gutter .line-number.fold, .editor .gutter .line-number:after, .editor .fold-marker:after { From 7c8c4bd233935c0a4d5b7ca76020a88833fb4ce8 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 1 May 2013 20:57:16 -0700 Subject: [PATCH 70/91] Remove unused requires --- src/packages/tree-view/lib/directory-view.coffee | 1 - src/packages/tree-view/lib/file-view.coffee | 1 - 2 files changed, 2 deletions(-) diff --git a/src/packages/tree-view/lib/directory-view.coffee b/src/packages/tree-view/lib/directory-view.coffee index e1ebb798f..14bcbd8e6 100644 --- a/src/packages/tree-view/lib/directory-view.coffee +++ b/src/packages/tree-view/lib/directory-view.coffee @@ -2,7 +2,6 @@ FileView = require './file-view' Directory = require 'directory' $ = require 'jquery' -Git = require 'git' module.exports = class DirectoryView extends View diff --git a/src/packages/tree-view/lib/file-view.coffee b/src/packages/tree-view/lib/file-view.coffee index fb1790fa4..2223b8807 100644 --- a/src/packages/tree-view/lib/file-view.coffee +++ b/src/packages/tree-view/lib/file-view.coffee @@ -1,6 +1,5 @@ {View} = require 'space-pen' $ = require 'jquery' -Git = require 'git' fsUtils = require 'fs-utils' module.exports = From ca4cfe358f19c339c26452063b1db376949f3135 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 1 May 2013 21:26:15 -0700 Subject: [PATCH 71/91] Mention update and indent guide fixes --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0e355262..e4cfb3699 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +* Fixed: End of line invisibles rendering incorrectly with the indent guide +* Fixed: Updates not installing automatically on restart * Fixed: Wrap guide not displaying * Fixed: Error when saving with the markdown preview focused From 4dce9d659f36c8694328ccf21991565e77438b9c Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 1 May 2013 21:35:29 -0700 Subject: [PATCH 72/91] Open http/https links in an external browser Listen for all links being clicked and open any http/https hrefs by spawning a call to the 'open' command. Closes #531 --- spec/app/window-spec.coffee | 22 ++++++++++++++++++++++ src/app/window.coffee | 9 +++++++++ 2 files changed, 31 insertions(+) diff --git a/spec/app/window-spec.coffee b/spec/app/window-spec.coffee index a173689c7..e39f451b9 100644 --- a/spec/app/window-spec.coffee +++ b/spec/app/window-spec.coffee @@ -230,3 +230,25 @@ describe "Window", -> event = buildDragEvent("drop", []) window.onDrop(event) expect(atom.open).not.toHaveBeenCalled() + + describe "when a link is clicked", -> + it "opens the http/https links in an external application", -> + ChildProcess = require 'child_process' + spyOn(ChildProcess, 'spawn') + + $("the website").appendTo(document.body).click().remove() + expect(ChildProcess.spawn).toHaveBeenCalled() + expect(ChildProcess.spawn.argsForCall[0][1][0]).toBe "http://github.com" + + ChildProcess.spawn.reset() + $("the website").appendTo(document.body).click().remove() + expect(ChildProcess.spawn).toHaveBeenCalled() + expect(ChildProcess.spawn.argsForCall[0][1][0]).toBe "https://github.com" + + ChildProcess.spawn.reset() + $("the website").appendTo(document.body).click().remove() + expect(ChildProcess.spawn).not.toHaveBeenCalled() + + ChildProcess.spawn.reset() + $("link").appendTo(document.body).click().remove() + expect(ChildProcess.spawn).not.toHaveBeenCalled() diff --git a/src/app/window.coffee b/src/app/window.coffee index a70829762..c477031d5 100644 --- a/src/app/window.coffee +++ b/src/app/window.coffee @@ -27,6 +27,15 @@ window.setUpEnvironment = -> window.pasteboard = new Pasteboard window.keymap = new Keymap() $(document).on 'keydown', keymap.handleKeyEvent + + $(document).on 'click', 'a', (e) -> + location = $(e.target).attr('href') + return unless location or location[0] is '#' + + if location.indexOf('https://') is 0 or location.indexOf('http://') is 0 + require('child_process').spawn('open', [location]) if location + false + keymap.bindDefaultKeys() requireStylesheet 'atom' From 21f1579f73495b17e441c91d6978c36f4745c8e0 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 1 May 2013 21:47:32 -0700 Subject: [PATCH 73/91] Return early when href is a hash --- src/app/window.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/window.coffee b/src/app/window.coffee index c477031d5..b4364401d 100644 --- a/src/app/window.coffee +++ b/src/app/window.coffee @@ -30,7 +30,8 @@ window.setUpEnvironment = -> $(document).on 'click', 'a', (e) -> location = $(e.target).attr('href') - return unless location or location[0] is '#' + return unless location + return if location[0] is '#' if location.indexOf('https://') is 0 or location.indexOf('http://') is 0 require('child_process').spawn('open', [location]) if location From c2118a8cb980a932d34b7695f08ca60dcab38ed0 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 1 May 2013 21:55:37 -0700 Subject: [PATCH 74/91] Remove unneeded trailing if --- src/app/window.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/window.coffee b/src/app/window.coffee index b4364401d..37febf467 100644 --- a/src/app/window.coffee +++ b/src/app/window.coffee @@ -34,7 +34,7 @@ window.setUpEnvironment = -> return if location[0] is '#' if location.indexOf('https://') is 0 or location.indexOf('http://') is 0 - require('child_process').spawn('open', [location]) if location + require('child_process').spawn('open', [location]) false keymap.bindDefaultKeys() From ee621bcace1d42f8c683abf7ddbfc2158a17ab56 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 1 May 2013 22:18:39 -0700 Subject: [PATCH 75/91] Bind link click handler in window.handleEvents() --- spec/app/window-spec.coffee | 2 +- src/app/window.coffee | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/spec/app/window-spec.coffee b/spec/app/window-spec.coffee index e39f451b9..12c629b77 100644 --- a/spec/app/window-spec.coffee +++ b/spec/app/window-spec.coffee @@ -7,7 +7,7 @@ describe "Window", -> beforeEach -> spyOn(atom, 'getPathToOpen').andReturn(project.getPath()) - window.handleWindowEvents() + window.handleEvents() window.deserializeEditorWindow() projectPath = project.getPath() diff --git a/src/app/window.coffee b/src/app/window.coffee index 37febf467..7a58f522c 100644 --- a/src/app/window.coffee +++ b/src/app/window.coffee @@ -28,15 +28,6 @@ window.setUpEnvironment = -> window.keymap = new Keymap() $(document).on 'keydown', keymap.handleKeyEvent - $(document).on 'click', 'a', (e) -> - location = $(e.target).attr('href') - return unless location - return if location[0] is '#' - - if location.indexOf('https://') is 0 or location.indexOf('http://') is 0 - require('child_process').spawn('open', [location]) - false - keymap.bindDefaultKeys() requireStylesheet 'atom' @@ -53,7 +44,7 @@ window.startEditorWindow = -> console.warn "Failed to install `atom` binary" atom.windowMode = 'editor' - handleWindowEvents() + handleEvents() handleDragDrop() config.load() keymap.loadBundledKeymaps() @@ -68,7 +59,7 @@ window.startEditorWindow = -> window.startConfigWindow = -> atom.windowMode = 'config' - handleWindowEvents() + handleEvents() config.load() keymap.loadBundledKeymaps() atom.loadThemes() @@ -120,13 +111,22 @@ window.unloadConfigWindow = -> window.configView = null $(window).off('focus blur before') -window.handleWindowEvents = -> +window.handleEvents = -> $(window).command 'window:toggle-full-screen', => atom.toggleFullScreen() $(window).on 'focus', -> $("body").removeClass('is-blurred') $(window).on 'blur', -> $("body").addClass('is-blurred') $(window).command 'window:close', => confirmClose() $(window).command 'window:reload', => reload() + $(document).on 'click', 'a', (e) -> + location = $(e.target).attr('href') + return unless location + return if location[0] is '#' + + if location.indexOf('https://') is 0 or location.indexOf('http://') is 0 + require('child_process').spawn('open', [location]) + false + window.handleDragDrop = -> $(document).on 'dragover', (e) -> e.preventDefault() From 3de96f1e7ad18d0b9623fd68ed59108638d2d3fa Mon Sep 17 00:00:00 2001 From: probablycorey Date: Thu, 2 May 2013 09:38:36 -0700 Subject: [PATCH 76/91] Only cancel termination if there are AtomWindowControllers open. Sometimes an application has other windows open, like the open panel. Even when these are closed they seem to live on in the application's window array. We now ignore those windows when trying to terminate the app. Closes #534 --- native/atom_application.mm | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/native/atom_application.mm b/native/atom_application.mm index c831239c3..659c4adaf 100644 --- a/native/atom_application.mm +++ b/native/atom_application.mm @@ -280,13 +280,18 @@ // The first call to terminate is canceled so that every window can be closed. // On AtomCefClient the OnBeforeClose method is called when a browser is -// finished closing. Once all windows have finished closing, AtomCefClient calls -// terminate again, which will terminate the application. +// finished closing. Once all browsers have finished closing, AtomCefClient +// calls terminate again, which will complete since no windows will be open. - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { - if (self.windows.count > 0) { - for (NSWindow *window in self.windows) { + bool atomWindowsAreOpen = NO; + for (NSWindow *window in self.windows) { + if ([window.windowController isKindOfClass:[AtomWindowController class]]) { + atomWindowsAreOpen = YES; [window performClose:self]; } + } + + if (atomWindowsAreOpen) { return NSTerminateCancel; } else { From 879ce3b30d38b7a840c411bc493b60499148b4d5 Mon Sep 17 00:00:00 2001 From: Cameron McEfee Date: Thu, 28 Feb 2013 18:07:39 -0800 Subject: [PATCH 77/91] ctrl-| to ctrl-shift-| --- docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 59104b772..ec313c625 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -31,7 +31,7 @@ bindings, but here's a list of a few useful commands. - `meta-shift-f` : open command prompt with `Xx/` for a project-wide search - `meta-\` : focus/open tree view, or close it when it is focused - `meta-shift-\` : open tree view with the current file selected -- `ctrl-w v`, `ctrl-|` : split screen vertically +- `ctrl-w v`, `ctrl-shift-|` : split screen vertically - `ctrl-w s`, `ctrl--` : split screen horizontally - `meta-l` : go to line From 67f96e0fdde07cec77b2f0dc7d17bd4b26f848a8 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Thu, 2 May 2013 10:29:10 -0700 Subject: [PATCH 78/91] Remove shift from all non-alphanumeric key bindings There is no guarantee that the shift values for non-alphanumeric keys will be the same across keyboards. --- docs/getting-started.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index ec313c625..92b31f50e 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -30,8 +30,8 @@ bindings, but here's a list of a few useful commands. - `meta-g` : repeat the last local search - `meta-shift-f` : open command prompt with `Xx/` for a project-wide search - `meta-\` : focus/open tree view, or close it when it is focused -- `meta-shift-\` : open tree view with the current file selected -- `ctrl-w v`, `ctrl-shift-|` : split screen vertically +- `meta-|` : open tree view with the current file selected +- `ctrl-w v`, `ctrl-|` : split screen vertically - `ctrl-w s`, `ctrl--` : split screen horizontally - `meta-l` : go to line @@ -111,14 +111,14 @@ operate on the whole buffer. ### Split Panes -You can split any editor pane horizontally or vertically by using `ctrl-shift-|` or +You can split any editor pane horizontally or vertically by using `ctrl-\` or `ctrl-w v`. Once you have a split pane, you can move focus between them with `ctrl-tab` or `ctrl-w w`. To close a pane, close all tabs inside it. ### Folding -You can fold everything with `ctrl-shift-[` and unfold everything with -`ctrl-shift-]`. Or, you can fold / unfold by a single level with `ctrl-[` and +You can fold everything with `ctrl-{` and unfold everything with +`ctrl-}`. Or, you can fold / unfold by a single level with `ctrl-[` and `ctrl-]`. The user interaction around folds is still a bit rough, but we're planning to improve it soon. From 10ca03f238ce5bbf873b670f483a0041c9a955c3 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 2 May 2013 11:42:12 -0700 Subject: [PATCH 79/91] Don't show indent guide in truly trailing whitespace Lines that are all whitespace are considered trailing whitespace and should display the indent guide. But lines with leading and trailing whitespace should never have the indent guide rendered in the trailing area. --- spec/app/editor-spec.coffee | 9 +++++++++ src/app/token.coffee | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 7a9f98d07..0aca4b840 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -1551,6 +1551,15 @@ describe "Editor", -> expect(editor.renderedLines.find('.line:eq(10) .indent-guide').length).toBe 2 expect(editor.renderedLines.find('.line:eq(10) .indent-guide').text()).toBe ' ' + describe "when the line has leading and trailing whitespace", -> + it "does not display the indent guide in the trailing whitespace", -> + editor.attachToDom() + config.set("editor.showIndentGuide", true) + + editor.insertText("/*\n * \n*/") + expect(editor.renderedLines.find('.line:eq(1) .indent-guide').length).toBe 1 + expect(editor.renderedLines.find('.line:eq(1) .indent-guide')).toHaveClass('leading-whitespace') + describe "when the line is empty and end of show invisibles are enabled", -> it "renders the indent guides interleaved the end of line invisibles", -> editor.attachToDom() diff --git a/src/app/token.coffee b/src/app/token.coffee index 01707dd2d..de78a0457 100644 --- a/src/app/token.coffee +++ b/src/app/token.coffee @@ -92,7 +92,7 @@ class Token "#{match}" if hasTrailingWhitespace classes = [] - classes.push('indent-guide') if hasIndentGuide + classes.push('indent-guide') if hasIndentGuide and not hasLeadingWhitespace classes.push('invisible-character') if invisibles.space classes.push('trailing-whitespace') classes = classes.join(' ') From d294bf4d0509321d5fca0aeece3b5c9d3e0f2f66 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Thu, 2 May 2013 10:33:38 -0700 Subject: [PATCH 80/91] :lipstick: Keep the bind commands close together --- src/app/config-panel.coffee | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/app/config-panel.coffee b/src/app/config-panel.coffee index e0dcb811b..81ec3e0ed 100644 --- a/src/app/config-panel.coffee +++ b/src/app/config-panel.coffee @@ -30,17 +30,6 @@ class ConfigPanel extends View value = @parseValue(type, value) config.set(name, value) - parseValue: (type, value) -> - switch type - when 'int' - intValue = parseInt(value) - value = intValue unless isNaN(intValue) - when 'float' - floatValue = parseFloat(value) - value = floatValue unless isNaN(floatValue) - value = undefined if value == '' - value - bindEditors: -> for editor in @find('.editor[id]').views() do (editor) => @@ -55,3 +44,14 @@ class ConfigPanel extends View editor.getBuffer().one 'contents-modified', => editor.getBuffer().on 'contents-modified', => config.set(name, @parseValue(type, editor.getText())) + + parseValue: (type, value) -> + switch type + when 'int' + intValue = parseInt(value) + value = intValue unless isNaN(intValue) + when 'float' + floatValue = parseFloat(value) + value = floatValue unless isNaN(floatValue) + value = undefined if value == '' + value From 70191ea368f866bd52e8253214d2bbf8a302f573 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Thu, 2 May 2013 11:35:13 -0700 Subject: [PATCH 81/91] Don't store config values that equal their default config value If 'foo' has not been set and has a default value of 1, config.set('foo', 1) will not store the 1. If 'foo' has been set to X and has a default value of 1, config.set('foo', 1) will remove 'foo' from config --- spec/app/config-spec.coffee | 20 ++++++++++++++++++-- src/app/config.coffee | 6 ++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/spec/app/config-spec.coffee b/spec/app/config-spec.coffee index f09fa913b..ec8a81d52 100644 --- a/spec/app/config-spec.coffee +++ b/spec/app/config-spec.coffee @@ -1,12 +1,17 @@ fsUtils = require 'fs-utils' describe "Config", -> - describe ".get(keyPath) and .set(keyPath, value)", -> - it "allows a key path's value to be read and written", -> + describe ".get(keyPath)", -> + it "allows a key path's value to be read", -> expect(config.set("foo.bar.baz", 42)).toBe 42 expect(config.get("foo.bar.baz")).toBe 42 expect(config.get("bogus.key.path")).toBeUndefined() + describe ".set(keyPath, value)", -> + it "allows a key path's value to be written", -> + expect(config.set("foo.bar.baz", 42)).toBe 42 + expect(config.get("foo.bar.baz")).toBe 42 + it "updates observers and saves when a key path is set", -> observeHandler = jasmine.createSpy "observeHandler" config.observe "foo.bar.baz", observeHandler @@ -17,6 +22,17 @@ describe "Config", -> expect(config.save).toHaveBeenCalled() expect(observeHandler).toHaveBeenCalledWith 42 + describe "when the value equals the default value", -> + it "does not store the value", -> + config.setDefaults("foo", same: 1, changes: 1) + expect(config.settings.foo).toBeUndefined() + config.set('foo.same', 1) + config.set('foo.changes', 2) + expect(config.settings.foo).toEqual {changes: 2} + + config.set('foo.changes', 1) + expect(config.settings.foo).toEqual {} + describe ".getPositiveInt(keyPath, defaultValue)", -> it "returns the proper current or default value", -> config.set('editor.preferredLineLength', 0) diff --git a/src/app/config.coffee b/src/app/config.coffee index 8209307cf..a406a9ea2 100644 --- a/src/app/config.coffee +++ b/src/app/config.coffee @@ -127,8 +127,10 @@ class Config # # Returns the `value`. set: (keyPath, value) -> - _.setValueForKeyPath(@settings, keyPath, value) - @update() + if @get(keyPath) != value + value = undefined if _.valueForKeyPath(@defaultSettings, keyPath) == value + _.setValueForKeyPath(@settings, keyPath, value) + @update() value setDefaults: (keyPath, defaults) -> From c13b291c642c7580d5b88bb4a0bd4631cd1cf23e Mon Sep 17 00:00:00 2001 From: probablycorey Date: Thu, 2 May 2013 12:03:48 -0700 Subject: [PATCH 82/91] Update config panel spec --- spec/app/config-panel-spec.coffee | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/spec/app/config-panel-spec.coffee b/spec/app/config-panel-spec.coffee index e79571c7d..c8995a7b1 100644 --- a/spec/app/config-panel-spec.coffee +++ b/spec/app/config-panel-spec.coffee @@ -118,10 +118,21 @@ describe "ConfigPanel", -> class TestPanel extends ConfigPanel @content: -> @div => - @subview "foo.int", new Editor(mini: true, attributes: {id: 'foo.int', type: 'int'}) + @subview "fooInt", new Editor(mini: true, attributes: {id: 'foo.int', type: 'int'}) config.set('foo.int', 1) - spyOn(config, 'set') - new TestPanel + observeHandler = jasmine.createSpy("observeHandler") + config.observe "foo.int", observeHandler + observeHandler.reset() + + testPanel = new TestPanel window.advanceClock(10000) # wait for contents-modified to be triggered - expect(config.set).not.toHaveBeenCalled() + expect(observeHandler).not.toHaveBeenCalled() + + testPanel.fooInt.setText("1") + window.advanceClock(10000) # wait for contents-modified to be triggered + expect(observeHandler).not.toHaveBeenCalled() + + testPanel.fooInt.setText("2") + window.advanceClock(10000) # wait for contents-modified to be triggered + expect(observeHandler).toHaveBeenCalled() From f0c8c044c937d88695e6fc24c00602bb0028e173 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Thu, 2 May 2013 12:05:35 -0700 Subject: [PATCH 83/91] No longer need extra event handler Since config doesn't store default values anymore, this line isn't needed. --- src/app/config-panel.coffee | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/app/config-panel.coffee b/src/app/config-panel.coffee index 81ec3e0ed..279313ecb 100644 --- a/src/app/config-panel.coffee +++ b/src/app/config-panel.coffee @@ -41,9 +41,8 @@ class ConfigPanel extends View value ?= "" editor.setText(value.toString()) - editor.getBuffer().one 'contents-modified', => - editor.getBuffer().on 'contents-modified', => - config.set(name, @parseValue(type, editor.getText())) + editor.getBuffer().on 'contents-modified', => + config.set(name, @parseValue(type, editor.getText())) parseValue: (type, value) -> switch type From f4f74e1171d31a4668957c4cbb58fbf77880c0c4 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Thu, 2 May 2013 12:05:43 -0700 Subject: [PATCH 84/91] :lipstick: --- src/app/config-panel.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app/config-panel.coffee b/src/app/config-panel.coffee index 279313ecb..403f70faf 100644 --- a/src/app/config-panel.coffee +++ b/src/app/config-panel.coffee @@ -17,11 +17,13 @@ class ConfigPanel extends View input = $(input) name = input.attr('id') type = input.attr('type') + @observeConfig name, (value) -> if type is 'checkbox' input.attr('checked', value) else input.val(value) if value + input.on 'change', => value = input.val() if type == 'checkbox' From 1262d3fce905a7c0a70ec8288617cceb1d019b03 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Thu, 2 May 2013 12:22:37 -0700 Subject: [PATCH 85/91] :lipstick: --- src/app/config.coffee | 10 ---------- src/app/root-view.coffee | 1 - 2 files changed, 11 deletions(-) diff --git a/src/app/config.coffee b/src/app/config.coffee index c1baf63e8..f16658c4a 100644 --- a/src/app/config.coffee +++ b/src/app/config.coffee @@ -166,16 +166,6 @@ class Config ### Internal ### - setDefaults: (keyPath, defaults) -> - keys = keyPath.split('.') - hash = @defaultSettings - for key in keys - hash[key] ?= {} - hash = hash[key] - - _.extend hash, defaults - @update() - update: -> return if @configFileHasErrors @save() diff --git a/src/app/root-view.coffee b/src/app/root-view.coffee index 5a055f705..960f9f9e6 100644 --- a/src/app/root-view.coffee +++ b/src/app/root-view.coffee @@ -216,7 +216,6 @@ class RootView extends View eachBuffer: (callback) -> project.eachBuffer(callback) - ### Internal ### # Destroys everything. From 9f4fb29b58651bbf79c293c23bd44a9967b17215 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Thu, 2 May 2013 13:22:08 -0700 Subject: [PATCH 86/91] :lipstick: --- src/app/config.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/config.coffee b/src/app/config.coffee index a406a9ea2..fff281562 100644 --- a/src/app/config.coffee +++ b/src/app/config.coffee @@ -46,7 +46,6 @@ class Config fsUtils.makeDirectory(@configDirPath) - queue = async.queue ({sourcePath, destinationPath}, callback) => fsUtils.copy(sourcePath, destinationPath, callback) queue.drain = done From 616bb487f4b6b829fca53f698f94130bfa80712d Mon Sep 17 00:00:00 2001 From: probablycorey Date: Thu, 2 May 2013 13:24:08 -0700 Subject: [PATCH 87/91] Correct config.observe documentation --- src/app/config.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/config.coffee b/src/app/config.coffee index fff281562..b92d86f56 100644 --- a/src/app/config.coffee +++ b/src/app/config.coffee @@ -144,7 +144,7 @@ class Config # Public: Establishes an event listener for a given key. # - # Whenever the value of the key is changed, a callback is fired. + # `callback` is fired immediately and whenever the value of the key is changed # # keyPath - The {String} name of the key to watch # callback - The {Function} that fires when the. It is given a single argument, `value`, From b64f6c5c27799aa32b2654f0967c468bc597ce64 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 2 May 2013 13:45:33 -0700 Subject: [PATCH 88/91] Upgrade icon to orange cube circles --- native/mac/atom.icns | Bin 759704 -> 204864 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/native/mac/atom.icns b/native/mac/atom.icns index d438c6be9b60e509522b8cab45dc67b8888fffa6..0a8747abeb9be22a89f546db5e6cfe536acf3990 100644 GIT binary patch literal 204864 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%0Ax%6A87z!2vBApfDydo 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 z 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 F{sHCU{2>4U literal 759704 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%0AEV8!F?5?FI6fkyk za|^0?u)oHsqR}4>K&=nN0|k0I+SGsAni{FkIwKtOo` z%YZck&i0Ou!0q;U8@r0$3=0?sThak$zyJa@F*P+d&ud81Gc?+rfdGS41Zruis{^FK zYRoFCYN|lKoNNtb0K>b)3X%-f>r}7+C<4TQsO~BN#AEH=&>sf473>wFq7lq+vt5Qx zs4WC6hSoQL(R*}t^mKHzHPzMCP_QbbLQ!T`RaI62tLLSKsv*hnPvJ58UV#PI68ntZHo(P=^?^a5a4eLmX$i~0aY3V)Eu{j!9?QK(*^Ru@HKT! ztn6Ju3U;tSqf%7pl$8-jt0A`d#N8xA@}Sgp_rrri0|UKXZFQyTQQj_qQUHO1xoQ>H z-hv0bVwnmul^Mm9(b!nuwW8jYg67Zz^GRJ@7vt<7mzGmd1Q&A@738Fb+k*A$N=hn9 zM-Nm&s^JMifC)W`e)U5mXGRBmdwTi?hK2_Fd%Bt{)53^$)>JaYWp|eUF|(*74W_Iq zY{CQ}iYu=7*MZRj+8SCW&Y`IVCFRvMHPz)s`FRCJrN#N#!J4oVouZPQL?tAb5bsH? zH9VV6jJHrid^|}+FaPkwyqb=_zMii3+T1829*483>`61nSz>{+C}wn2E`trFOtP91 zv~{&~u^#ccrIj`H)dlfB4rcl~8X7wKIKPa%{16poWkp4WqkQEM=dnb85E2jf^r5j# zf{Uvg?8@)r>}YT2=pGDLwDoj%wAN&YI}^K4gkeEUXn{Gf*#QuELonw+u7JA>^-Rf$ zh2_T^Y73*BjWuCaUS(C3nu?05lA=LWRsmK)Q4wsOI9d+L#V7ocj%r653Dmj03o#cb z2e942*3K<7r@pJRy|bldbcBQf!~tU!fZssYH8BRXp>LplY^W`-w4$c5Hp9nU3#Ezz zFa|p_R8>^~#*`He;X*Hj0<1(MFJ1=GddCOD9?T??cgtwDiyMLBN)K|2vtx<_ryWQD z_5qo7JtGt2BNPWL1w#weP)znwIaOa**SdIQu&1pd%iB~PXwXPehtyycw~8{*p`@f7 zQ5Y((sEAZRiXSS4_Q%J20XFd$kia-z>bguY{y4U3s#m6!9_#krw6JN z+{{f)sZ++7VzgIZN8cf#ap=TgU9h<((4q-zFjCD?L!n?5TClGQR%TU-C<>NGB7rhF z(Ne&?Q6Nn)09vdWs)GrPyLk;zmjeT}DzF1=&I4v923vCG)KEu5RjP-j5w&`P4LNj; zh?KI1!Jh73vZ|)G7OVkjQq&Iszk&T*zw0_9d-$xTJ04MmwIP6G}c#Vcv}FP)Ymih&Z?+utqgXmY!1`_ zEcr+JV4<=w_ygJ~O3d;W8D&QDiV6xal15Ub7~+nO^&|srM51eS_h60_(B=v|r8pp| z`5O3;no2CJihB|b%?QcI8|v$F{VdR!z`~k_)&dWG4c+vb72!iY%4m$Ot8jA z_~_dH-tN-mx}N@?Y$rW!Ks_bC-6T~0{i6T zffEG_A^yl%zuhYrP&2#4cK4OIfI&W z&J)9xHab9^o{nZ{MV1bT7PA^iWV_iQMVSY1k%CfWUbZsOgG7Kg`J)F5A*Prp4^Ttw zUi`SamG$(**gL`wlGKYITk6Gy6}Tp`1X<0DP}k;WLM2tSad3OziJ{)!5*K}KQ0y^k z_>>jsssroT)KEYj$PK_QWe~Bw2$zgXOT?~e2%uDosxLMg1dOVW!0+l5)Y9GJWkfg|8V`{8#4_DTA7C7tan1_{CmKTPZYiO!Fmz0~PsKY3FKu*f46lGY6LtZ^L zw*ZAiDo~?_kO!L(MFjI9a7W7zR0wYF6oQ@$sPYQCx^k$wLF#JJLDN1`3ltVKs&30h|%hT-Y zJItRCv4lr^QZEYuAyQmeoE*s2ogJ_ZDE@3&Z~*_*f`Q71vL?|34HkhVHEoT-M!E)Q za2cYft8JT9URsvr;Y(j4FaG6)9kUkbDY)MqD@_qKyax zB?K)fAz&9iXOMpFeH%Jjcth*x23@ z1+F-)=|RlQEmE-T7*m7Xk)htcA_qOR0jLo7>*|6c2vjO)|FYKZu69>NDjHEJiV7Q* zt(1`RZs}#w^71ee0wzK76a7(xvMkXR0WhQC}mhBMTu2W-ZiZxK>>gt z0pJM9rvX(5^C6C7!PFv&O8AaoR)ZOD2opQH;)vAD*TnjlA#PoOW^ z!K=KkDix$jjr^|e#-d0QFrifd@b@SxAU)Dclavq&ND45DQ4S$1CwK5b9>f~%1GG`o z7$7Uyo^2QOILqMr=EejZ4D8}I#za(goGfVRNI(N+aNp?IY05Cui4()8M|&I6U^O<4 zw2rdE^1Ku$RYhe*Fv*ZA{^@0LN>r};W0LIwJIUnaWWk0{k$rg(dpOueP0cSLXn?O^ zJR2|%Bs2fxO^s=`pp@Nb}Et(_|aitMrv=PD5wTm^#Q+Wo0CG zNXuR{LW%-$4_1Z7p>4R+$_TZ^pR}CGGZ@|n&OmHTB-&?AyBTdVuvY=ei&n0j5rgw| z|Ai|yWhoUtGggkK0&@nQzuOxe6+)B5e-YjlvjF6WkTVuu(}(hNMJuCq5>^ zEQ~t=qXz@2B&DBn;_m(V-_G#zpwqg~v=wR23io;YPF`d#XI?V8cq)JWTo)VLk@M1| zWP+uB3%=U4KG#i1(}QL{x)3cwC-O01Tn`!yNsiK-5123>42LRM70?v6-h1+nZ})DW z6UBHm_jLE3mN(Sd&1Xsesf(9UPKS%uzl`axMyz$)sZCf^{2K&JGdNgl9xL zC`70d>7q&09<3pX47@f&P7cH7jNL1khw0wYH-K69&8jcfubkJ{L*b$Do|-odr;o*G z4oYDzeti1CdQBIjS*=JsU)Kz$)qttH=&uw zgy_QF22n{qp;MQCm7@%tv~rZ&%H8vbG*IZ>U5j zb{djBZNpym#%77col#iJK1nzq!joz|l!rj1=mPKBJ2EG4vx<)U z{T43yVrrb5=d2YY9Gu-IZT4uGC z*{i=faOAk^gz51k2X?MrKF!WrW}Cfw{>+?(8Eztplq{6Uevn#~=7ih3Tm!X)B1Wi> zlm$XDAA7YHfv=POX93B{1TPry9)-s7adDhBx%a?|VaALvM%t$__#fA}+Bur+8 zSo1{!w2E3rmQmQbO%AVVd`}Xk$VPF6`G(}I_$m?73kuUmyGZb2T+N*whOJ*SZTbA! z6T{@#S8U;Tz$2AfhfSD~w{XmY`5HGE&J@~4CF1GQW+K)ln^9)))$k##3J9U90m4Fm@7<5 z5u7R!Ojm2Z?U2b#5E2|<)p4y8cQcvI(+lhwj^)M#wjdSSGiC9HsTepqLet2Zxw^RG z^e2y-yTH@l&e~`A?0NIDlU<~4{in~)ogRUyFAm6>;fS&%Sul_-s3Z9kO;hdJa!NDjNOADSbX6b5||%=O!Ylh`J;N!Ev*In;15Gkw1UB{7`lVFN7htZvLa{^dhjE%AKbbYC?_&QBs69sU0l;|-+T^FH#{bo>@efs&TGQ* zFXP-HoN<$$q(2gyotzoR&*g!$7KPX#2?vf4kxMqL->`n8S}vkvrc@j=RgH8oR8*$8 zJ({GMfZ|@0)uC8yw5N&0rY1Ov`a~d~3!^mf_xFzd0)CX28tNJ2Mw+`e28nUdv-g>r z8)MDb8e3br4ftZk`qg=P(_$Sh8L7UNIBD8=nt!@9O6aN%+$5*s!~&aP%s3kXZ31GU zxJ&DKJg2a5E==I>=f&i$P;wK(9bzW(6u2T&5A;AzCskxEPLbKkWbSYZmo6RNQ$2U~ zv@y|6Dv9oZ8N)@`l-uIzGy~|1YZyNuT^)t&F1i@G_ATb|ng@k(T9>a^_!k?8DPRn> z(Qk}GNb}CgnaZKlkW#a=8o`?i0z{%PM?xBuBWS5GGT5XY%4r&%jCfl zwuH?PN(ZXOSi<6?L7%sq&ucCZ4Wd|#k2{vB(!*2j-b?|>myQcmgj(JO%Hx+);>J#$ zox5;|!jiNnu}%26so9yTL2+}JMhh~;Jea+Xw3Ns^(6kl_`GHfWcpza1lGt?Wuz+VA z+=HYVZ_hd3EDmw^$WS!H>fuf#4sw#LE$y*se7zwrcgdVo7fbF)0#QgL3mTq1Gk3+R zyeJw0)R^ls8VZ!ylruESilnyn?ii+>>a2?TRg_>$p2N0$}sGZtkSZr+^gZir%uK#Mox>zd-&ipbN zBh^Al(pEUhiA3_@EAkf4m^38P)e@U8Ic2nLT2kLx_$}PDV^eO1H(zYlE|=HVF90Eg z*}IRO`SIH?$GMQvLBB!jZ-qm?YsdiAta*#`rcYe9G*BwTnMa2dCq~*Cheff3-|LI{ z!_`@8X8gcNH<^f!%t+h-F(PB3VAT3u2X?KT-j6TrZ1M%q!e8kXn7ixP*;C)`$a2D| z+ZBR2#6TDSUL&${=H)J%Jw$2cI&DEOsSP7jVQJDPLCX?aLf_e=EbMy>Rb{FtPZ-wA z-@#JA=L@js3Wa>jXBgj>y}vZQIrj%V%dZlT}&Ma^^1hV(IeSto}iA3e%hzmNws6@p)zVJOmM>d8M?ElHVJowN;0tVYQLsk(z zC3K%OL&0Tg!Z`E!QrEE5(UY^Xr({o=GIjc_8Iz{Y%$Xiw-m$|no{5*2!aX`Q(aSrk zZ`$ZlY5n8Fd|aUWtL==fQ3Sl=peZ>%@O;!#EeUoqcvg5mzlSX13Pjma`XWq4Jl4=MJbRiu9!MK39jHWn<>YB{TDwg& zPZ9~$B9$;pq}|=5-D;kWn;eVOS;x^y4q>wcJ&k4t+D@G-wUSwSs%AzABvLg*EBFLv zs1QFfa~zEx432=DWBd{J8h_vxE&d7y!DKDZPynTm!et~ZHi6KG$3n<~yiMYimDFz3 z^dV6Cw9&J${n*v?M3!mOlgv4TNv4dR3?s%9Q-yWJ{EjB;c}?tW@l3?A7`YY+Y}zE} z*|erd%E+W7WvlF{^>}Ee*o5ykai%vVXc8eA8*L=0Dz;2IFHvnp79HCmrHNdPr!_-F z3}2YLIQOVw01Yg~%8Y4&n6HfFQyNwJaf@hmCiWz1Eq#k8<9}ogF$47Q9)72`U-9&< z?Hl7@h8l4bW)(wY%ta5R4r8YDr)8$5<2!y*GIUyh9CtfXd*NkqTP!_UQ-l~#WH(W= zdvqYW5FKc%OI8P)fvyaWJ(jJ0N~X#l$3W;|bY9^r(sO()FdeZ3&`Q({Bc~EVRl+f* zQ`@h3#$qWvCZt}X1RP$e>^#?rq~ zV>`Cp#?z5mStH_?VVxo4Ni!^)N+j09Ge=@hlLyI&34kW-IZB({iUavZVv=@(`J9@R|P=!2PeHfi}SPn$2W z#44ouO?HD0Ud?DaisU1ivGxK~x{1KTb;8WPeC(qtOnE9FkI_S4cQY~B&ok&M5|Ha6 zMH{-%*2T?bnrp$6n0}TR6_JH(SjJRJ>O@@%X-gNKL2FZ2Xxgc5yF-5O zSYSVlnbhCMDQGw|Kh^>@KyAnzQ3dqHVv8>AOxpgyGc+~ji*b%djkCzz<~yWeUz#{M zdtNlx?P^q3h4+ihy0mZG{v^+!dv`N#$Hc_#MzcyRki|~So<4nyJFfV1^9)@}737`5V11i*>0n~g_B>C&<0oA|{oI_d zM!rPN0~tOJy&^s=U=yLaX}6B;+O+wZ*R);d&Rx26Gv&j*1YsLs$lJnVwp4aZD@^nlTe;7#@zvP2=Ko@{jFQMMaSGtwTy905Y7f!y@t1 zCwN@|%P}MR0YCvYgZ-T7mYxXs_S)ER=XOKoj2=v+D!e(Q214Zm0-mFCO`oH3;1HOk z_Q{Cvqiwx<#;EG<+`D(Xp&%ZSPSVUmbx1Z)2k1Qq<@Coe=>*C#y`@C|P-qJ6B2X@T z-TnK&{dWIW&EOCUO+j#gOhiXYz&|)iC=gu#`py{=U z>lnPm$~A*9fY2XKwJ)=B1{#ow>XGmXL52vP#P-@*UsrbfX2YGTvg(?-fm~Q9Fo+AE z5MV%%TO%uHIg(Ki>jNN)U@~+ikvbgILC=YrTYYm~>iVC~?Hn?psH(JoAaw`97Fxv0 zY5717(RKH1WaU&$e`4i&6Dy|=bdma%>D2|qPh8Z13-!m5@-#XQLov6aW*4Ltq$ni% z3+PvKV&yzJ`paG6;l{CYYGUP5!OEFYu$LZ{h^YtW00_Xkx~fqe;0EbGd{0%?C?y66 zCuDtUnzuK|FGA&13T-z92g_xQQ>AKyh+$IN(X*(13Gsx7#>7SrDQx&U6j9c2L#W>Y zBa3VH1|pcH175`s6b7+!#JF(^0td?(F`1*Nuc)_DFwJ>@$3Ee3Q-r-iq^+pEHkg(L z1)~wP-CR{M*iS_w3u;El9Avmxk+~K?=76|?`6OlzF=vA2TF^QpW-hwV*@m5=1h_Tj zurbuIcAX!iY);S|g@HBD92(n%j%e_nfX(T1*c^eFh6v2#nbcs0BhjLn7=!SsRoBOd zBKXQtbHPDDVSB1B#h}3&YEH`w$8lqvE;%|1o~{E?b8W_`LCv9(DFBPGXi!mZcunIp z)Lc~b?)p>FkaELAX{BO#{jw`+RNjE`h??_caOV{IBd49w=@@W<(;w7aUr=)z1WohU zMruwK(1&1rdh66&Mn3jEA~6biY9nbQU{$Mof; zGQD*OgJP1~(WFN1VZsxl5%q`;n^j#kBm|w%!x(cCEUeN$U}M##XdlLx=nf1!QeVO$ z$9WNQ^PCVR@1H_Q*I01C24(28dXv@gRsBh{|aN1}OirfUM0$TY_ zDytp~85W=`9?eB#Nx|WmIr2<(p-5Q@R`Hrl%z#}$U#L@z38|nbW2z#=#>6y7=rBCx z+|AWNWOGR_Y#m6#l_EVR&{bU=#$-qN;{30LyO#LRJ4i>4Wf zIgXjbJR@9#i%UlIm{4(jT#&}<1v6L&hW^SO)yI{j)iEACJldF>6zB~aJqNkUUgsrZ zPM4d)Oi~NX3^GIr<8pHtY(dwE9!qNrdr@#TI4~%Px|<6`&aNm;bSJqON?;Qg>TJ}U zfi$DR<*W&pYdYaME|-$5O4J7eO0jRIXFG}l5|a@T9=WeRKM0~e?7Suz4ME0pXmNGA z8y=kr&=uM#Fio7;kc1Z}HiY$oeY^t8B@@HKB=WFO41iX~Vl@*K(JTTvLX*!mELLJp zqsL)ruoL)$$n}amS(E2NlbU*58w&;JDmGG^#NE51tLiZ-lK1vog9#(nz7NcwE-JL zC{rUYCv3#!QW6sfPMW`Zy#`MJ`ZaCRz)(jn2rT1}IpaoTE^+AEv)8WIGY!q_ zuUB8Wcx21`F)5K@;S`Q-6&A9-?qpaXMgm+Gb(J57vw17VUMcVE3gOb#$Vu*~Oi|_S zJ$b35yn-n=zFKs8%LqA?dYO$CVRLPUPaH(_Vj^>{{?^T^qKgF=iz}`(4Xx{HuAJI3 zYcL`KFoqEa>URKjrM3XgW4Ka06v>yW8AnFZ)zQJDC$r&LVR=<`b$QX5?}D)WO-wCnaak#q#RviVJ&k$0Y}O5fJFpE9Xo}*%};_ z;LQ^?*OH^=FuOK2T%QA62?T6h=a{JQu&AWb3-=Y))Yn!O<$p0PHz5(n)lDu$TR2+e7Yo4}-)Cnxnk zb8B}jXE|u|<#5UH(D0=6jc2RtYHEHySARX#mxJbP0n7oKgXut<6i#`!u6{@5oZB!e z*a34(Lz$5Pfte_)qpo}y&}G;h`0D6FM0+XV3u}oWW!TZ%n@8-p zQd4>5P*%8yhnGSTGxtP!&9(2w54=>HY440yCLFS!T)DVOUQZ+|b3*!dS4>S~XFb-BKwY;A&zqpOSi_Ua9C4xLlk z>)YDXqyQcKkxV8Ubg3*=MBE(VAzi`EwH!I79~DLXRm}YAx;-dMM%E6n7acT0g1C@j zfbZav>JeVFsN6zlU2SN%S>LcL+|db|v$ONMs@=}u=CncC;B(u!!Lr6IY$;KETeee! z*pNO%;9Q5Xqu@+&Cyn^HKfoPw!Ex2rlGf&B4p0h zx|=tzUq3#|+mXaQhlLeKTp4*&d&q`zJIFIOAcvaM@ja)wBuYq4BmGFryz^|HF2~I= z2@3LjBh#T=s9`E>fUdN0N3W=bBtJ7Qc-p$M=2XtRuX0_m%|ux+#GDKTGm41{!Ph+KrCwNOUP#6^oW~F89u5v z#*Tak%>B5Shyg!U*Q^f3Ea?%34F#P^rwH;&*4{cEIrl1%>6a(Tfd~o|+-ofo5xVyXo>5*^KA+|Y763dA_=;(OKd4^2579^SAkCl^)u3g&^3)jHU zXZW$o+M~ULPSwwGad%UEbNB88=DzCQXZP>lyVbBtW@qbusD7$qMQKUVS|v=sR2;mk zxNJJwM(8Nu15$js0tFl|Wj(Yv1}`=m3Q#2M2>@!e5BrV(@Pevq-}mwK4xC$5RkhUL zwa1Ct8O{nfuY>m=JbIM*Th~Vq?>6MS+S(}ct8+xMw9}=<-}SYUh!c;Ll#CZ(iKA&e zh@upw)!=4aaoT|Bm}ntqnW0c|mnGpO1PA)29HTr1NB8S%##&mt?5WHV%Pa%7TrIgY#d++xs}~`?GLkkJFT}~wDu{g~Hsb#rMK=V* z9*&e;)561u!KLMm>rB4UyRfGfHPr72S9p24hn=dK>geXa( za(M%D`=izFI8UKvr)shtT!YTse(-3@W1}YzZ`Yi3lR59IoGZb6Y%cD3w!XgZgaW#) z2BT|^!h~p?5bTX+$c%<0kt>I#tr5`(qwAz$p|he8JKskrx6-j%!i~?bRw}NAJ`lRdwr>J-)0gzp??*?hq=; zDfH~E`;RiIq(={KS08tlIqj*ODVB-Dw-uM?2MUMdLhKjz0t!Sml5~*9DNLZvk#w@& ziJCFuoH&~5WI1q6cG#Kf>bV#ouk50V@?#@i0#4RWmpggxZMgIBk@~UfiR`y~^;H|K zWKR2Pr&-vIKUrEf-yTALucMbQZ*W5xftE3b3A2w2^-=tXNV;Yz$r@i6BwdJ#KsrYG zslV6Qi`8Wl6?FN=HThs=S;?|qr|M=R)HJiY;qHUq9z9~7h#&p-@ZPO!RfEJ*=R@_= zlp6}mPW45QLn7uYcNbpX?qz{>jN;Hb3VUNBG6Ei0I<5YFm~cz9j&>KCgVa2K?@5L5 zQ4}0^;-aQx(@-N=0dnu$l9K zqqTL{Z!WWObh1r3TU&FzUUjogLtRaE%{GNpCRUuOsjMm(A;u>csU${-Da-RNU)rxU zN6RuqI*96XqemoNP;w%-x*FwQPNwu`ifamzP?(0K!-3qq^^0R%?Hp`F7N4o8 zsjIE6tE(wJFvbqsrsJ%NYuCQ=(g}UgL^t&Lt=SfJp(;(PckZHz#@Oxw_i24bgI3#_>oTH!C-He)iBFF!)kg^1hm? zn(1O8K2OFHty76x3SlS?T^^ zjxvNSKcWM)rR$_)r8NgumX^kdp2vJUQ-n<@;>(s^DqLs9C!<9(h2l~#qjc?_Z`F~9 zs>%i{a7e?sor+^zh0sY|9Oa8@zIRSLR$f}TF~J_8KpbYob_4emmtV+~#T69>qFs&d zqc%!Z3HA7P>n|1NVESY9n$Y^wz;rqU(}7*3fpf-H_>Gm-DmN;P(+?E{&p_02cCwpU zaYA9`G5tbWX+dtNmCV{w)^p?KveFfPLVn-E0&jeR7XUi)MQDk`7~=z96gw{!4mG2A z9no|M%Mwke4VsQYZ}I@2HS?NUR=ZT85zo~MTu64c01)KpXs0SG@UgSC3SC)TUUq7x zpDb#{rSghh@tFO>wBnN>sIbNn0Oa-GYdU>>9i87kDH-b?9Z7xNNSM$jl6na zsM+sFH{a+gpF)oloB`E+hEKd2Sy~iqBgfUp_?-}3j(=5HQ~kqGYY~n$qV$r(6kn%3 zfWo`2h*7}Y;151qxEX2z@`bRjnlh2r@ai-wtDPQo?=qXJIX(kCK> zThM-fo@1`o&T!*!Izx;SW(Bclj6+x3xbx-xC^{^&30qWLRaaM4aeR_Dc4etlvh?bD zLfo{7+2>(V!BC0#if=D}=YVJ+p@`y7K+?4UNk<_ge;?&u2&ho0J(_aF7O^8F&31Q! zG;14tx@xS{Mrt3s;hLOyr|GWY_-A2G#g+6&Ex6%>y( zM?ud~I;|dI40Kw6VS`I+Cb~fm&rpb{Eocgd(b?Jh9Ic-zaO$(Jpse!jbRV(EVMKmu zN#T)MJtQ^4#)I>qqk10q5jBQxY1`Y%T)u6nsjjLxINFu4 zP105*eCykjK9FL$NY1Z)s<&+lN?c!T8MZ$3<5Xh z&|9j@HI-*Z(!Rxkbb%qhJ|6RK-no0bt}NA=Yyrw4kPbM7z2DH4Co66?R9E!2L=jL@ z8bP{`Z~zhr6zi(1s!Px9ni6FvuwVqZ74QP28&=V9v#z`_+DvR_lFRGkr}XsiR>5PKBJ%hKF*HoW4 zRaRBa)HJKEymH~t8m5;!j6Y*z$Z1WpuO4%@1eznI9aRPOpglkNVu24aNU&RcGjl87 z0XbWa78I41s;)LIKDqn+)slnZro_^<_wrPDB#-Xz<(D#k+T7VwCk%}BcLvmD3%o|# znl1)^BuOhO46%f~2|R^>LOKi@!(vS?J}9Fd3=2agG~t_>itT-4`j4KZo;Wz#Nv!N0 zZNn#=u8Ets!o|VS1#yk8bWsr6hdq`ca5{~=gX?t>XRi*0ONHvIBswtk@Vc9eBzdJP zVGptHh`LY_aDwh)%#2LU%q{pr3sW-@N7Oaph&pb8cX#BjMt$=7HoTE@v zW9n_LH{g$Vc$KQ-q47LhwT%hc7AqNMf>mzu5eKxL<{O>GjLqZEUk)^w|JA4r`JA$5$cF7hXkN@GlORy6ohQG$S4&0x!IiX-aG;(eBy`!B7ri{MO7 z?WGE$TPz5t)6?K|DqD`GgZhqNDw!6LRZ9c0&ykV^d_0W<{LzUto3hD8z!QWj0f$F% zbBi`Atags3(;=D;>>x+enQ-l-QK40Z;K8z@V@^VPVhd`a&@AbEVKAs8^jHSBJz%fK zMB<3VPHn#C>4~M_@@%k1;p1Y)(6vX05ybF zR%vt&{0rWp&GB^lu;?W1YJfVGr6HMI*Kdoq;;IXJnoix#`2NSQ4yVlz44s# z-^A@m80PyNx^zSVHVtP9v&5gSrsJa>;c4RsiLS#uLstnJ5j$-lc8nEYYRR>Q<|6%v zhVVyUyu4OM^tq|ezT&+cVn=l$n3zr^uOoJ3Cnc6s;GqhwS6{lc$llD{+;4l~#ULD< zR6=|pC{>3%KDgtbwB_JBQ$Csv+seUpAK}?zzWbib=QsC{9Dk^=sGliG4jPR^K!?f~ zY~AO4x}%1AW{NwYPUb$li!Wb1S6F&3)vVFSM_JrC)T~Q8!s~PiufuZX?ux2}Mj{Kj z`r!G?Czg1db2b1ELA_weKi9x@OgC*aKHlGm)(Hg`*1iF93vh};W4sk_77Lz}bxk^c z(&f|7%=r-OI-z4xvH+VuCJlF22yZqw{Zs?jwQhv#EI4C}0%^z_;kxc`hU>Zjt~2@9 za2+$48L0PqD!Mbx`Rk7u`#AU26?phmUtndO_DQVFnN{Il8Vf6eUMCEgXBI)T~Zz0%DZm{-9%O5)H2Bp|{kL$77B z`uigR)rA78`v}EyP#xg~fa;V4)wOs5)gi2k&S&BcxoShnA5@af8t#L}Lh=zw5zUEB zcnzmhQR=yuaXMZvz#sseViHcU&wf#{O@IBBW%B~K@D0x2I20RG1t@pQ-^1xvvh0?A zJIX5$4a=ftA2?m~UiQ(LFzCEcMMEOavd<>IjME{_m&#kq6S~l!X*%3) zWfEIqYS0K{ipNHdy2tJfc}~+QCq87)yhPI>#n}s(&ds?o^Bj}P^l3uuCq@j9YyA>S zw}X8+l*C#D1e<`RJIp>E;Y%T`S6Mo!9*x=O{tBgoK0lzIJ;b4O#xxB=loKDbyL~Zl zRepLJl8*7vQXm4SjWb$f0y;fS0y=dgNXL6Wrjh6O{z=N1^K90ULl zvA@IurtAC~Oh>{*<58Z=K=(RK2ct$XotR)cqPrrZPp~DCoIJ|GbR<-0CES)~3 zG5Ir0XYdD1$D}kNm=0YH*j1N<>5w80cD~1yXY53R=`b@8TIOiFfYWT5(vzd<)ErCa zOvKe+v2@LW0MZaXXX#W?UBi29VQ#mW7(#taH=1#bla?d0 zQgJXHJ{TJ?U0dXyqXdP0fIh*lQ_Ww{bV!>(v6$!r_GF;Owg*tq*cdj>W870V+Z7V3 z!cDC(q+5qLtY92p=jmELPd>--beMtY^>!MbE+k6kLH?+td1hNt85clsME!P9y6 ztY^P=)=WXh)ez5jbil1(d$}6f=Sc0-&~%-cNnEl*&~&|e#=}}*Jh_BZ2rq)yGr|rS z8Z;T7KOcxW&IFoLTmob(8ENHd_Tp3gR$9i=!fY7ja@$~;F1-L&frtN-XmP3865 zk22UckE;(a84(*y5JL22_InDXkl@M#&E3`;RJey?;6rwr1EL!a4&JHr4%cG4*MHP- zt@KLeT@`C^*ps9%j-G3;q306E-C&;{%^6AbT)$zH=5IZFL(LlBJ-=>rbYMX84R)1( z2y*9{V3+35Iasd98um-jJ<6eL3x89w9d4dlr%ny=207>E*Lwv%=lTE%|If*}iT!)` z!H%_`eKemE{(_t{jEM~E5t%$8@6>G-+x*w_dHu&fX4U?%<4fT=RgP;0OzFQ@bfI2D5iroWt;0dZUX$H?Dw%)ZP;Tu zaE|hQg@mPK?yg4TZ(d`c_V?kqxmLu@fyC#~FNM=K_Ceu8hV5FvdqRi<4nJ~|DH{P8 zYjMIocCG-&yH~imgfYLcE2A-cHQbzWW#75mwp-DCXit?j80C!sf8h8N0S5aS-xi zEpshlmjWJYcU5RIU?LXo__fnl2zs7BmnXWIdr>`}p8cg`Kj3-C(B?$PGz4<8(7ywXBJy<9YT7u8KBdb03XOgGu8Yvw_#x2WcSm zm}nis=9uud95e(c4^;iYU)gLge^2*btI(L%R}P?@2V0YX%{hCWWAp7fY>tOpuT5+; z;2Z!HkaNOG&)7l2m&v&%Am`AB+^!f+J|v3MrFcIs+q6R$a^PH0fbW>6>=-Z1br09D zMR&2ZzYlldusPR5>=_LLC-y04Pslj+hkhe$PG{`MWVrIYcw7dGO(|yCMPZol zk*b%Wx#j`h+3eH4VD!8_6_I%l)U44JwXXw*%xz)IfXtyybaNG30i+=!=B&PAAIAx} z8*dN>IbyEUxY4xd(4nhmTtwDG_U?$VNW$}Ite8+92)(ux81$OYK8VA@atoeU$+8bO zExF0C#%D$oF=w}oZSeUMF$ea_N<85yyIcT~1_2ZNr$wpew2^9#!5+thX>yGEnq^Cq z!mx#MP8Ll>95-l#_Fl=}2>0>yoqU;PpYBa^b_idKk6M4cKLzmGZ1!G|-E(9P%j-EZ z7k7g#K;-OoWDeE|{n(t4xx{lUdn^jHP&iULzF_9WN}sRU3Z?siL-^eLlY`xenRAHQ ziEn;c6>K$;eHvv0y8)?tz zG$SjS#Az?fPUAy6#i$uTxgk-(?9aqp$Ec`~>5tfFE6BGBCHFWGXGFLUUcP5ktlr)@ zCpU7gnretS2Wzjqhsb|FR!l;kEdg^@0OqX3^6f0UO!NYnvv`qhP7oW5v}GD#Zow1w zSzaK#Lrf5E_wx7kRL;GR9vq`^4GdN|GYTW_(10Ag{(iEa$6&jPQ)Mp$b7F_BEW44r z4E_R`L%TGZ@PMY~)j5lpictNI4B!g(Prd=bbpd5;B(!-B1I|84MeMymsV*siIrrBt%Dm zpj^zf&BV$vNq%T;rW$nU=OJKPU1C{f`Zl`yyH!@>j zB6hYCyC1+$OF_y(xuT(hiiY1I82v%Y5om5k1|*`9lvB=o%(8nUh?IjPoUk7i&%e*E z!aVdTn8|iJ3{&V=!=A*{xI525!bJ?l0jF4Yjr3JM4nZ$2$(-fzK3oDqE)5%LDo`D; zl9f{*z)tpp3*hTZbM0cE9yRPzT&{QjVJ2(1!^+Oar-GgDzMElN9S#M1Z7Gg}ty_q8 zP+R^?#aaHDisNRv-?*DBTQbPQD|Fps_URg>OH?hJi_8^%7iY4aFS*%RdtHGYl1)Yj z){H_BFRh+^lF9!+XgJt*7(ynjTSy21wa}2j;6R^2RJ4mnE@Q6tm04GD#2o^OGfRGk#_73?>e)IV~TkkV5W%B-?5c$fd8r;BbHRbFxQR zvfO?J49+JGU40)-Iurp6P7!;9o$ui0bvlRbc*4fo?Pqoc_&2e9DJD(DGxkaZ$G<`A z*P|E*X}kuA!9Z2#63KN2|3wh0d5CM0Tv7aMbfog;S%Z^&Vr zTvx~xh3qP^N~SI9QwwL~$_wm`E@Pu90*bG5IGWawBqpjUCKgA%!3?N^fmz_w0hC^0 zd-3>^p~StpMc-g&%bgP*<#pHe9?D zwiBxLYoMFKkmsPA3Mu1MIKmlt4a0-C`9~aL*k%XgTwEjT*?IPkQ8#kf4%eJzZYAt8 zp_L>FPVw1bVdifvyW0UbvsHi)G1gFLxo|KA4k%5Wo(67X!c565b9R|lp3*xVXMx2? zgygm0A_0w-AN zG}fBba2(LarG}$4bR5yv(a(FzJ@!|%8xd_T{eQ;9+>%nsF0pfzugYON4;M?_E8trk z!@3xZcnp;Cuo9NN48)579>mbmmRAWj0eOp@770BOR@mQX_CxmO2saJE=IpoO8J_i! zU18@WkF1}`etZats=cb&>zK{EJ%FuIc$@Eu6>>Eck&HKBYl(!dsX{x$571C+{@(K* zvDf>d1b|)Ok=&ezm#bL)&DIW1wu5g?XY~&&EM-EkdWO}zF&_ECp!^%5A&waHYhk7c z^u5NaHA_ywrAw%y*fzf`SIUr^k22n1=}}lNV%Jj@+oTU7HssJS zD5)c6vOu@N;p$+VfzOJ5LUMiqY&0H1mFn~Mk zV=Ip`tZwNfC#yz8O)BwRh{n|zl2K}KN<_@n3*G);ta+GgnFF8-FWY~|7^|!aOLD*U*c1Xu`zLg_O zW!X?6MWFCSDLhm{Lksx{mi>y4bxNz0;z3%POmDH=1#%iKV?W=3!|bzcR}BiMBGQjg zKj?9`tJzA0)N2{`w};zf=%{Kt0F(OJ*Gd6@4ExXzKqw>b#BIm0$}pMmpS)2QHRcIo zcUZZvuxa$E0vw#Sgk{$$(1|$qt3f4^mIj5+!EOP2$Is428o3+G>(;6u>*V|=Ec;_J z;50!td&5mk`z66?7&EvU1gDwC-C=+2fjAU5F)?rir|BW#8}vXmaz+lh%brHoGU%<1 zWE!$mI~zMW+Geqjdk~o>OFfD6QpNXZV)1AVnYMtvMr0ZXb1|a!-1Gx(|4K_}Q>5*27rBbSEz6QmD*w#8{fwRO>Z!tYTSoB*mgYnWzHCw- zvqPDci9X}YfFc|yAkcUu^GUBW9gLhdn$v&>lsg z03c~iVBz?dF&lq@UHxTOnw!8Jd0Mp54wa}4N~4XHZ}kOawR_Vj(n?U89zkiI>(y+t zXM>!uCOL)Mk#RC`l-pU$LsSRvKz@4CUm~(nJtxxiVMO_S&jWNQU)9?Ig>Y~W3L%JD zXpxn{>L#0VFq)p9(#JEZHiK=w!I675Mgz0T+SV;P>%a|$?eg$IKKm^GjU?K7w(RR$ zkfWabysE#u#LSFRt&w#>8|*Hlh%e1J3=L|id+M(lY|CwSFegxraFfn0Y((CfyBODJ z1+#mIqkd&4i8TzG9)}IUl`#}q{lISiOfl}e2bc}NSMJG-1?`6Ty9LA7Hg&z8!Rq8& z5Pzl%{;XF+25WSxPb3lpgeQ!cx#jFl72E#t*#&X-*u{LV!JQ<*O=`u(^RWb5z!nQ# zud&n3_@)Bi$%hauH2&>s{=!kclnNvZcOQ5QkDq5s+?fuzv)u@0K5V#FUU?JEXnuWP zMua`4CTzU?NA?HCN)KBZ91C3CB;>3Ww*T;lY+nnk0}E53`;ZN#Pt|Ob$9Jkre!kN1 z7!PjnB-%_j$lIgG4-BhSem#pdd3L98$J8Vb%5ntF4w|#oM)ta^{TEU9zp>-t4M7EDB)YgD#&Kps>0x#cKmpikT2;iE2?YGl z8B?Rr0h;jv6_{!`vsU2DxE0F$RAVCTUgaVnBPbIrS@Cs+fcdKTnfC`}{ ziw^XY7Z zBTRsS=9#GUYoM7q2by8kP-q1Yg${(~Qx~HTIMA%$6Lz#2O+O;G0m9&b)79HAd-aRyQQIdLYK#IpVP zwv=@QaW$2+0QGpd0LF z>@+Y!z@E^Zd_^_8fcAfC=O4%!zJCL9){MJuN+|#_IE>54GK|unb)?ktOT=2 zk49L)M-DC;4|FG-bB;MPY-G+b#~C8DP$@>DFY>5h3tmuXdKM86*mT<8Fbw3D(kSD1 zbm}YAnJ$?p)QJ+7s;zi9@gVWwQjCE)Dn!(ofWMw?a7ODen$M{-RH%_U>rUz1y1~%B zfjXlZ!4JK~p8111GY|cRU1&;9EFOpEX{a;I8(c~F6Lp4Ei+@mO>(ND+sA`;6x4}n! z&YW=$7BOeE*)}p~*td|G9LtHAGgvIFP||?FoCV=n;bTs3=9{KHVW*jrOW2CM9cmj@ z>Lun3=Eh3P63Oi-c`x18fRoCf0m;{f1I|LL+4DGF!0u5?{sf#EaXH;Mi;0E7=>&wG z*&b!tosO`M;XkwYdf?oJMZsZblxGh0&|qgqd=5J!+7y<6<}I3M;Rg+&U_$Tt4T$!G zJA(_ujYuPR_DMTUPPad}vkX*NM`#*Yg01`3VD!0$1O9_KYYpa1zdPk;qfGvEj4>9O zJ1(qv%GPhfJt=CFF}00W$qVL8$CS(GhJ=@xRal$mHUS|H@UOUc)1ZQzeomcrqI_;d zouP-Z?x^1d=H?hO!p`U-qToMaXPy5Zb~e&}@IW5V&4Bm6*Z+Fpe?9R3wI0wm=1W7t zQ_(A>r@duc{SVdxoh|{R)}FYANX(1Z@3oh=X7v;_d+(L#5V+tpLJV(y{Z{g2ika5? zt3$ikO?7WB`NiM=F@N=^Uw1?{f7eyekN6Iv?B9MBPw4P2YoH%> z{BIZC_{OS#~Htq8i=+)d-+$dg2?|~0mF&VY5w)=>(BmU4Xls+ z>$15Yyx#XeRUq}hG+tBl<*oj84Vo?cZ;jXd^l9-R4zps08^h0{}>rfdJx8ia!}d55nBy8o;KJrI(3r?2_{s0XZXzth6$VdMWn2Z+kv zdGYL%zi)|JBj*0@U%&bO%^uL*^6m?#CsvyNy$YnipW>lW82z0J_<}TePp`Ruqx^PP z-c#Xt?6Zjfy8?OduXqp%|EE(H`1|`TpI!ccbU^b9@3naLvD;r)VB-5No@I|4{#6A& zZg}72vq^td{@7#wh|96|$ z2jJtks=Du`UVqwyoZd?@Z++;D*V~`)*0;Wwf7tYT`QzV9F>ih7iP!ZA+$R5!ukzR3 z;H@9Bbg*+|k)4W zf*2wX#D{o2b$Mg+dAtc9qI6`Rc#Hna`?&stR3PCk&FAs5K1BKF-yQ(z{2>AW@o#ND zkC*-aJK+55p5M#W>Gb|*K;p~g{pru|-3>kV*TlDR!TTM7M{isE&wIlE>3glf(&y#8 z_5VxXXZcs!y|vuu|6uYS{gB_^yzxCRTJwMJ_s2ivyjs#*zI$&{ zKJolj2k=6WlH?t}X1}gC{AJ0-|^3jy~*7&-=%(j^DnU9;o*I z$3<&?py@ji#ZCzRW%2m2rw)(#x{SV$@WGeNKZG45<3R}NT z)A$FE{NG#u*8~6Sf&ZWNK>rk}jHhMOZ(2x@k8#tEqzxh`n`aLmk6d;wo&jAf?m2v8 zDvzi8S$_|Y5I+x(t|7yRruHAyhsU%3C@Z3DXi0b*muW+LtTl1%8tpS>`mwDMVR7Gf z>bT_xeZTqZZu*<7tNWS%S(Ss`v@bg~DXH82RqzCro&HlTw<4VuzD}PS>nBer`t`e= zeqmGiU-|h?s6E=^Q`4Jz5f%BPlg+}8_I&X8l)OxS-SK*SiKlZy|6NNuL`+?@>FecZ zKU+9|gUN!t!0G3ATvX~@+*B7NntSS}R^sp5K0MP<@$qznQ#a?{dKUC+_e0}nlom|u z7X00iwjZZ=_{}Kv^u>X@m2D0+dGcHTJ^H2hI^VfqtE;_9J2!n zVN08<`d9TP6b#$YEbz&IQPK&W+ix#;@Oi@*KX$h-9M!kgj2C zhkX5|qWJ42F`>l}}79d_u%_Gs6zBCV%gl6Ty& z?9{jQ+HH0}FTG)){NCaF=6X$s4|ZtFzmRVJ;NJe=QQ;;IK_Pdax}}>>4LwtIyW``Z zW#j{&_Zww*-etqUfvz{2JDt?e|Lx4pg98VSe42J|)|DB9 zkK1k=^26@Kzx}**yXfiQ9@>tH%!;84MWRg&K374-o9pC z{Ug=6?xx?n{^)Z0{I?b7zIwL$*_T5$CTwhXvCMHmW(UXhpQZED1;3l8o25&B512kO zW?#48KkK#d;9RkuJpcBA6`%h+VQWHC>nqcH^}951g^gKQ(8&o^?Wz|%-T(9(r%v12 ze4Fse{0&tH?%Xi^^(Uj>KR(cYWYZl30(VUi{i566y2a=E%k>L?2tSs&X{1R^$%^^w zd)WT?!_W7-Zy4QkS@ojnV;%CQoB2+=JtO$&@oO^eJ$d_oi~r4Z+j^Y`&*rzbbZ{si zEDZSkN7=RI4r!5I_nVGfdhVxQll04)ulCzC`m*iGwoye}zuIbHIPv6Ot6rCUrnLRZ zy6xF&bK5a{4;-Gc=Cgwt-EwLbSDzg88hyoR@U1w{DePg#hRA+#8(XK}Z290&$Kvce zK@)3at7kNMuuY?g^yh{-Y%6>6ah>wom63Z2#xf5h&YF2TcCqWoer?y$F`*l4 zQqbp~YYZ=K?4V8p1eo1;ex8c(v066{G}*hMuvgi*TL(rwS-#jY-P%jmeb!eI+eJl- zW?0(V+O~IX7UbS^sou3gO}`(#t$*-0L6ZDaV$o-vx}IpIohkexbxURT8?(F2R_g9Te4DAr}@buH$PY!N)dS>g2GWqRsWqTG}+IdnH(R}mp z#DzMWI!9N3$8QoT-}XWO>d{ZCgZDZeh`8~L9bW$X?aAz~Sy@@V%!2be$LvV`X@x=d z-EY3xJSOlNau4D27JB9_S>k|fV~_8g{k`<;@@K8Jwxk~HC*M}ji(k9?$*L}oSBW+R zE#IfD^YOGd~?DpUi04B2;j>(=`Z zA1qu~UH#3@ok!cs<3|kXmVf`j!_Ut)EdH{t`q8m0*%6N;!NT0?%KK3@H8tyVb90Aw z`Q(#lN3s?#j#+lBVDaL``ID-nCiN+{-Bfq%h$^DVu)I_C2X3d|HqV||{q+9*MZY{~ zkSt%ZV#0;Php*La-(D~{J^lA~Hb;&bF35WH$m+NxdrN4_<_ky(m}<1UM{&*qi9~X& zJXiW`!Pa5Azo-9hKW*K2(K7q!AH02yeeCV{UEY)Tds>c}{7`sl7+uS3P?4 z=z{%}375a_^<+ZdGt1;BA00XL^wgn=W#i6V%e5ZAsd>3mYqrV3hZ*^)k8^q*n>y)O zkA$*;?XK5|KmMdck?XlKS1U%fthAzrpxkDHSR@>QsXz4Z-cuh zj@}9NsYivhGa|qsQ!(ZJ7`gwbBMfULDyV1t6>bH*2u#b(v#rh|uxw8k_NAKvccKF# zgT;k1h_eb@%7%sp%dum}7sABh2jJPp&EUOy)A}9GDgI`+22s^`# zp-PA4j`Uikj-WGCA_yV{4c$61`xtI6SJ3ZNK;yQZBY%vXMmmpy0i8!F3!Jwpg>~Aw zb?iW#gaJuuzh+?c`W$m$ z)d4)m3~t~x`q|mp*D+UrXWu~IO=BZtyEPE<61o#E$H<(+YaISeOBiiG#$(dZp0R+3 zp;<vL0M8+NzxR8;cNitVnQ;EEV(f3=eD@%s@ZtiCa~QnF?|i;z@Y<6`t95ara6He5 zx+1Tw;A@xzgz*{Wt98*L0Mf>Kws(~F7T-#EflnJ90JXveolPnwQlPBkjrb)R=FK5A#l^c9mX>}U z9{tjXKm6e@r^)J{XPpC8QqMZrmo`9F?w!YfJ4W*t2iL5*j(KnY{Nj9Bm|HOHWub09 zqrQuF8ib19R^*$pXJm$rUTdrxIo4rL&Ew4Ze-`T~)2R$FvRcEshU+ot&iI@zXlxp# zAQ$HsD$EYH(~rf}A(_!?S0_U|g zc<@h9bv>dkG!7CK2+PPo7)&=#;eb8`uYR5>!pFGPc&zJY_3$ikVATOU3rrLQi+N)# zYP}9)|EnnKn;G@DX*ko2@Q(J}5{p7FGHS;#X`>;=mN0@E%GZTG+j*^}^o+;KNsgkn z(~xVPp*LB*)WZ4}8Cc5@oZ-IAW7G=+L#2$2mo?*S%h1SJjIGR^DRU=|l!ddW7_AwJ zfn65_X-r?j%(S6HI-@&zA%YI7F?VYY)aj}P&$>=Pyg=V3;fTKxjAgbqRVOD%qXKXs zF%^iT^Y$Za2@kLk`b)I^uPh#}Mg`%}pYKoQ{GSiTOS<*D-~I0Oq<1mW|E;0n;p@3K z-sd9OnbW5k?Pu9YLx>+^l}#FkEMKmpt>^q|h|PIt@Nod-TbHI}*Ex4}64gK~(_jqW zvp}eUri<1G?NV;9)wpJt`^&)QU1i4=*Ol%2FDvUeY%K$fxb{DN=2SUz7r5_0-pZs2<~Iil70J zS4YblG7Gy01C(N1;K2XdFDK`w6{Vb694MQvczL<{4R0;mF5Fv2h6jsdua@I-ql5FT zQxx_U9KP@Na_@)#OPM}(Ed5$3BeM8soj`Lpw6Avq8eW0c@WKIzpY+Ka5%XxCxJsj_ z+E&_xufy`(a+A%t%(dmVcg@48j*E!En&Wok@51jNXPw|F;IT+2f2nXjwe(R$-%fVsx8$h$T;Q;Uhi@yb5|Bv7Jjo$Zy`~~Ky8oEj>Q&n)21{;IsG7Dzp zX_(D$G6G^q8m!5c^kwxo22u?s?I~Mn)EYv;>#*7kknrgwdJ z*?kcsKgbPYPiy%3(16JZV4goZfEnz0j^mo)Qf~W&pDqu5_V+UtkQ}E0ns#udEj{@s z^wucHk-6>|%$|W}#L`B-hAeLR=h3i3oIL{1=|uPfKJqOQ1n1ty%rwW_m%`*|zrtMO zLL-hT3#y<_?r*dH@^4nB25{m}kvbLlQ`CHkR`FIIi+X>Vt-0UFJoH)(jV-OYeK$Ql z<$N^Csxq214t29?s0S4m$KWE|ij;aWhC0GUsa5X|jywM}3LS#ZK=?KM_7daij4EZo zZ6mBBXoF#$>At~o$v1pkdF7kGsZ0zHlwn|EPl*B=Mwn9^fTRC3#y@2gK)tgE9x5OE zvHzvavz8$;PASAmbod)QB(~j&2#yqHKY&%hISwu(Jhb5ebQFs?OWPR6S7#u&N@I-x zY6Jo6LUW6lq+58Q-}+q}qaz&-4=iG!9fYC2Ff?q9DaPL7c=BUBL;$w)XGBugZ!K5f{MNF4+vYNY zwDAFD2nR5P^*aq>SaZ;}zu8cZNiGF}ZqXBMd zUPedXv2WkLtruT>@m&u-_~5BjTK)434*bavia(>uOYheP{PmKA+R8&eyJqKIaonavO!)l8OA(5stF|4>{e9zvo-gY0Osk6fGA8KCWjVp*I-u5}t3rBfrywBU|cT6dzF zw1Qq>Ys@;61}lz{E8Bu;rPnj-2$yX8uX`APPtAEl8DNhL509=H82EeBGt=*U>s#OY z(>!nSlk6in)1kF`fCH-zV1*jI=RNP4B-H=zp@D(FPuzcj#>Kt;{M?)0QjF3S?oRPl zXcas5)E#Zx9D@0t`!n?suF@dq!pQACTGzo`V{znjG}J=4MA-vFeHTUttKCks+=p*~ zK;1fu>Ohu=<0sZnmWhecp3&)2XYMa0i2zQ8YW32m4M}CH#&(AJfc(EnnESKsAb8jeC!;b`L2i5t;Qf_8g377bM0ARisD80Iwe*K`Bf#aim7h87^NYYRCL=t z5#4f@N^)o-)l8;M9SK>nJNeo%7NGh_gu} zOd=!a-L<27T@Xwy`lf+A@n7Rp;K|F1zisjp4;>5j!)LDTPxz4s*R|K`fQ=rkr+ZR1 z)~o+qVC*;Ax{!t2zwq<_`TyQ~!womya_60QzU1F<^pbY~-}_ho%DOkd;~hUdG&1s& z%M%+X$bAF?#F7d2|M)Jb=@bjvm z&R{imdr7#@Njt^~@F&-l_i;O5{l!<7nG;9L>8BpUArUk}gT|{ddM*wcWNuALUm#ON zzGa}6)SgDn6oq;Mh%s9seCwt=)XrTF^5Q_dV}(-d3fr>YG5t~_c@?L+u*!M{(>D9` zeFiwKe%5oYKYskg+poCdiUSWk@W4G4?o0FUOWFZ2>c4J$%a)%VA0PitjBNPisZ%WQ z9cL2{;l4`SjdWEtsum~IBqJjd%4VuYjM}TymZ^jqfkze70#X!~a~*?<8#@V%3P%7j ztU#%-ENSfRwQu?Ma>dJUEZnxthMzXy7Z1_#&YP5{92|P|;)$uiFCZ-`Hz=aPGY2;sMl`blXqkT(L z<3*Z~hsma_(dHcjGwL*_I+qqPRQ`p->7(_Py9##T&s15{>sP!oZGm_m%Gx|TJM(S( z_Fc4Z=gys9e(bTwj@L#nt-mix2k@?Uy=&<0Z-4s_@ZiZ$uU)t9l9`#Q!ibQ$en!+z zn|q@d`eZ2Hl-6T@RmY++UPoa$I`XK>{%00H9;cM=R*Aa{gR-;Ax zL)|vV@047Jq+xhl%@KHJWPQ2fjqfOz-|$-Y(epwMng$9$G-yM6jb7u|*!{T)Xys97 zAR0P?wBdRUx7apa^0IR1-aEJ}a5PS!nV@Jo!kQ||nrL-?JzmKdDe=F6xvnkjqZ(#2 z{vMB@HBpassUxf`_Hpc`@VUyvJR`Q?MZONFIpgp_UeUI#*Wkzoj0Tvty^T8s_dfja z!w>f0zC=!5at?s|`n&e*+4EED)~@|N-gP(3i#2^yOvvvUp1$)7>4lY(P*9U@vCT~I#sJvYkU@F zB7*Rw@{eboJ;1qd0@k-)yl>yqWtU%mE7Jn~ zwD6d|TPRcYiIQaD-8ReVH!5{qKLQ-1YfSmV@?r%Io@;=K29i&G|8X8($vjf!(K)_X{=0S%4g_{vZ0u0#Ihx3zLW?YD z)Z<%PcMeHdI!?Qc)8M)}EN&UTjCVG?dB=_&cRcaL6OXq(zE+Qw>tCzXL6>{@w{6+- zL00+yE{3z_%$c+0^r_RlKASh#MyaZ?R#j0Uu!>GWQVfTpn`lr*ArvRdM5f9q3>Du> z)j(CIs$7BG^&xy=JA&69yO9x#a^9-)Hf2;C^}asl>fAocTKlrhlzxGYA1Y^;*OWas zeN%Y_qyON@5b$&orR*9n<+H!|bLGP znJw~>FY>H~>fyJ11h(|bNcmFv7NX3OxEzzOhQc3tW^Rn%t4=8LP9B+RGz9TFf&6#A zU!#chvtplw(*;^2TF6<)W8pe2U@P{awHwxb;3L2Nk^dy@uhmpJ_cco05C8BFUqHD3 zAJ?v3_njz0e?orl?e`1{p+_kZy2cq>t-6+cjnGj`ZHmHmidogOGK~_o5ra|jOxBMM z=U5e8C9ff?eN;neqg)-$H3i$Ij5Bc*uKl+9UYoqukgYSzuD6k0mz3+j@ttL2!^RlD zb+$~D^5m_bDZl?~zm$kU=ygusSEhLO|JZ@Y%0cdcJ#qh6%E5;pEN4z0kFjPHl_dDF zILlLx$DS(dc3)h^)=h>6;fR`3ff=SyhabK>&OywACx+#}ywD`~A87Fmyw6>C+2NRQ zlb}tfan6|OPo{OL$BXn)*BC5re5zA|dL5ivL$Qj{cV+UGYhg#W)RU*eS5~~dbbNkp z{#$nM+`07VV~_n2#ZACHh+hjQU$YM2`@jGDZ$z;FZF0lpP3{Og&Rl=`Ebr4tdz%Aj z0(9hU3vnue3fogoM=DYjkE6=co9{>SQcSsp7?};b#@6-ch?h~m%eM}y2G9ymoP;lk zr0&Gmg4-mww>W9aZy0T!3O>7LtXy{UHh6ddQ7n32;*%WwY7Pw?Gs^FOn^1#VaGyEOTY_jex%<*G$QylIlHSQ3d&*VHvtE-~ZG+5&FECP*LHHFW zS037*$-i}zWyL8@Z)JOEQ6@wI{Xl->MHgLkA$tb?h{Zt&{n|=jlMdiVe&k2Kh57z3 zty{NluRHmUA3siL%UgFlAxG&r*C^eK9~I=fDVHLSE_DjkL~gl?=067$6)7XN*CwH}s@JJaWmLD*ysD_Jo%x2TX{Hg;NBZGd6g$E_)7UGIQ@#GNZZ8Q*C#=g&$I*jl&`N%*Q}JUO7hZAu6aC$7o}QucWGeZCM4ARF~%=_YJ{-B2D&}>_QgoYsPCH?bjhFQbk zW|Zd)&uPQ_)S2?w=Ra0XJbkdkE+}#9w3JJ3etTKFd27#LVpxI??%rO|NWV(Wx=_0r zVd^*q3tE-tjQCtT`io;5@vZC6gY-yS_#ggO^cvhAZ5{nKYI3w@+LO1$#t7TUn#imx zYjyK*Q7NyZ&VPCBYhQbjZNFC1*Hi@X13&Ns-^pjG{x5!$r&#FovE8~LR~0&1gXA*$ z=nPQ01s|e8@;;!W@FR4!ic`8@>?vyN} zoP8R8(~s~|CB3SI)i8B785xCbJzz!QxpuS8#J;QYsPFI?Lq4`^RK0n4xqR{$f3`fu zf}Z`JTOg}2n}Q1$i0M&k)2Gf6ASp8{p};<+cmDK=GO%_%VgFuU(80aMj--)wBV~>| zDv1K9TWJHXtCJ*9t@PW6L?*9p=*fA_=-|;O4VuHKwY!d}7hVUy>Kw(Z@xykF&&lif z?zy-&K7@Yp057@P%81`??iRd-+l04phv0yyzu42)gah~>Z{)q{ zrknmXpUM4aJia@6iYNH0@pCU4!;T?G5ilr(pE)oBb+pdNf)o)mf2-1ZrV+$|RBG!w zN?Xg(i+UV5jR{-40?&853m9yEv;1?SbRh&wnmTM7+XKi za{>p-kw@;QUFS4Xy5djQ$BLYsO0Rutq_#Cxnc?ijL0qgud;b}=iIlm!OIsspyJsmG6&K_BUpe!CvhhyB~PNhb_1#<^AaHX?z8tOdqyo5{)b-|4;EV@G+4k43*G_$V(z)w9R2P@Whu0=DcK3WQ+2j_6ecl130k z5C#TNM`e|@8zsAuQ>FC#cP&*_t;!M@onqUkbK4r)qwsX&BfLG8RN?Ir_%TeBU*`}y zEbH)@?;k1`u zxd3K+jlmCnR=TOr(~*bnzNMVtp&*MZm+IEyi(m8RtT{Bkiffw=$rtNNTW-^FRgE1^ z;X$Oj8GoW2fs-d4`N6-?L|ySpDe$ek#1u}zxQX=AcvKxqz1v`(t>kHV2@G-b9fT{B zaGSFZK4jRRx|O>q@I_~0WX0m(7_j~gk>~fUgz;iKf6+z&hW*2XgFnU2zW;HN&Fx1q z{Zn)Ao9grS@WRb|eZ z3P)b+$NJ6jn6E)K95upr?lBMqY)W6k?dn*r!LX(GDpvm2@unZG-vM$n`+UCepl z{sXLMbg$$FL>)n^LkP@@RgA_ky+s(cyS6R>7W5;nmP>~`5WjhDD-N#PTsE>dz)zK% zM0YLIf2fYFzxa(lhIM4cy8PYRCs^ColZ(uCu|2nmW|f z2D(5Jg`TP`UO^z}eqopg_ zMdG!tl^reID(WwAs2T-F5#wZRuOU-U_`+)tV?@*wdW;FYdJ2|v*C;Hjqi%)Zd>7yB zyYqlm>)R=Ff5y7a`z|jVw(pGb>yAdT!|A7(qol*48hszb{G$z(kTtaDxvK&G6{e>_7SD( z%A+b#G`q&&T_EGSg{eNtx5NuPeYJR$RT#7lbm`$t_|m&PGEsJ1es#9?h8D`!GWy?p z%cshjQzv`Th5g&6;nnI^9$^IHi1dB8elv92*jGJ@U&T=v=J~VH)RT{t!w=oVHv@Qc zahGN}-EqyUc@Sw+c;EW7?ElQsbV8OG)QMie?03x39 z*t}-!neu^a@J?y%J8p^kI^4jpt#l=I#|~cb>&hMV8$W2!wWV+6v%EYGu0MYL_1C|m z1N5RfyyzSNxB9-7Z*Bfl9^!0ajg(pjCqIvQ1{;Ji{tV;g_L zpQCFAj;8hnHDzqeuCn#Qi#P)YKOgAno0=~7-1gb*?4#qr7H?A&p2|J^9mz;B6soBF zdANvt>-5@@Z<)MEeej6UU^olx3pm8rgQmDO7(|2zGQ%6!l?$%CE{|3VM+|g$E;t58 zqfP#Y#=5(&@=+K%Id99-w|H_iuTmlJsyrl3`Yq2~J~H-wg!Gd-DGcty&QIQ^p)G00 zwe^K*AK`o2R=k_c?C6}M#wig|1Uh}&-c3o$j`OXo3;cT`fxW5lBKcD#^CCg^AO6UX zd@DQrer{xBe7zsz@HO2kWsIlOL>B;E6Nmyffp*6%%UgEO3FZ^7!+bcMJc_dsLgkT<6<_qq*O%cDRwPNaUA=whwT7syV1On_ z`8flOLzGVHiHCKpALI8P+!8hke?}qYU+&R0jlc=;TOV`s!pB{)lr+ z_Q8)R;5Bo6%KXwxFWuaV&gbKc!U3?if9IOPz8~iatqs1w!=C~$RT(IVDh!p&{o5L& zO4bu@6ErmqGt-VH9#y-xXr%@!NK+JxV-$!yVYP4FY5a9GRNbi4DWZ6Jd&#=3Z$rfz z3i|*K29_SxwW@G&Ai(z@naK|hmWvBN2ioy%7nY3|?2568p?+Y2SD8Khl`rRZop35& zDCOK3r#dAutPu|<={>hff!}B}zbSRSRar+}@zB^>Sr)^@3&(Zr;k$TR@NiJHJ-(u> znHVWMeJ~0BSRnlLMY&j*#J!iO;}jv`G}DdB1KFTVP)~ofbd5x3EzBk#%U4D_!mR5# z)RT31rSO>iv=^v$pSG>A^K#QiF{#Jx^!^%(cw!S@myM1 zcr63fzrOkAo1b}~u|hbn{&rNIS2#Tb8E*GYut@grhSm&Uso|esq014~Q9`2)_q(Uy z(y&S+*&1CfZy;5k>eUsZf;LrS@Tjsz1v|r`Pz9xKA_WbKe@$_0PjRkQ>Za)K5VTJH zX-L`&ijh&Z_GA63oHatQZu$WS&zn*OPdZxYUsE>kxs-Qwtaq?Ynjri?aL<>@=@Wdp zr^{;w9|s5=X%gNNQxZ;UJ?ZJ_uvxD^jG#J@iktbSb1>S-Nj^AD@q*A}PdwCnn~`;W zmH2L+Pw*XrZnKu=Hi`!d2L z?4hRM-_c->i;W^)aj3lL$&50cxG1T}rm*p^Jb3%9EV}Xfa*c!n z+Xq+rv~9W!a`s=Ztz)u&@D$h3$F;yaIPzIz^IxTfBJrXxfR)8FC(F@C@8?NNwg96^ zv@|d%8~0vO#y4(`G5cZ9n$GpWD1uAtT#x-zSP zD~;WGJG5{;b?D1u-Db@RZJVRCev5EC#@U!@BoID%tokD^y)_)(l2q3-WNW3ZITP1# z)V-P6>3?v|HP^gVaOZ0>9zAap3w`hAmEHdYcUtZiK=4pK6~b!V>|bk%Wry985-fiACj;88k#y$KJ{zA&|7u4e*IN^ z#HqqalVEJPEic@KD7MX+29FMZ+XQ!x^2~~jXX0mj>pLAbd4%_0VobTJy*l4a_rD&p{-_C{JOT*w=D7i zSi81r@Bm5kqCe1Mf6XC8k1P}CPS0u-W4~6cyh$1Ko7eO)#M=o;nwdHb(cjIyg!bbH z4jeeQaUwO&k3Z*a1inAACCjC3|kslZu+?t`XVb?_o{{xUeW8C|H^1eIE+0#Cr4rR18 ziM!E7UFQI*a8lAD8IF`Y%4Vdctl=3&7e-gVy{I&b4IKMORN=AvO&>m3jvsh5Jh7SF z0DReT<;#KFJ*{VfcQ3NgS2g%RxDdbxTNYtgj?tH z*2`#59eLB_E&i!10{(0pD0Q(hG=W!jLC^w^gk}GYzqOsZSB>8`l_v8lOljbC7jmym4JRJkS1|mk!{E-}9aexTW&b<6~nR{1o@}j4RDmEHXJlk&(#7QPps{ z&0Q;h6?~PkbxjIIg^cV|rs%68GzE#$tyB`rH9#6gv>X!>EA%d}C-lzft=F3Z(04`> z5Y60H*eX5LC5ecjh9e@iS6@}{LA~#`maMCdNfj@b2Tt4X_s^m3zxvSCgvySao=GhS`Yd_{o z2lZmu!1mhKF<6e$6`oNPV+UT^z^}CF$|Gai)`+5We(A~TwGS`FMcDOM_iwoNLnQE8 z27rG>mGds*KJOd=5BLl&jg0(jcKTi8mwLSAXHppW-8zL$4V?k=NCcvCH9^FC zQ(hlWtx92a1($F;%BF*;ib-}=$_{44N~Xq{)G4XTsX~ZTB8k{TRn_1j4zvqAkDbC* z6{ImI!e}XqiUno*!2DAhQo=7Rto}oC^42Id-f;BjeP4kF@?zlh(9vQ$>6Qob zO&nBkGN_YMYlH{35o$Ap;6dN%-};q+AnSRhRRgtO`h@4Fo_K&4i=O+ijv$BEZDi5% z;?$Q9ky%HRh8HZrEwYk_;Me4qD2WO>FT3|o`trP`4rZJ71y9NB*KPH-L6@q`>fg${+NY9lgF~%aY2$4|xtyP$`<|<>zWOgU zIOogpymJ7XCMW+E^Zvi;DC(1ahN&inQb%x=SOw&oQMZ*v04f&sBJ{3J6&1Q~3(A_+ zsVFLC8wFAnr{YL+E~#J%Q;8k`Pz|Xou1Zq-)9C905x2rJ9dUH&Dhk}Tjx1f4#+(Y4 zk;Ekt0N1w5D8{)j{#eVtf1kmRol}<=|CVEqK2T2a4a4vRg9~jP^n}(OEe!|hJeMjy zc9cy=l*!W;HVZ2b!v>sJhgO|QtKWkKQd7J#>?Cgs0z^`dLwzh-ULO1a5@)2Ns?#!Z zy2|1jBLD0^- z-rInAGSg1|k-`V+_EP$-I%aO1P3WjNHXhrD@Rg~_X8c`4-jBZg|B zD1!pDTq$wXNLybKB)F(3<~d53B3SCH#8DdR*u0Kd!c&=R-@>n%}cJ3{M!=vDqCi&9O!3Xb7*j;N;o5DJZ*7kLNPd*i-y}?!aV-Ouf zJlFAvdsW(suW;L9rv0||;ge`Umoy81?$pt8{NSU&^$v|#^RiN27q;VN*Q56JE`aSK zGmXOvB>Rjpc7|e}_55j+R`YL@lQ>jaR5?`2Y$YDjTFV>nYkiC#Ks`s#MfM!vcqE`X z@$iXjQzDSA-(>J7xL7G?HP=4l1jx3pRwmBDlcTWQ7s8ZQUD?Wdz|ZmsF)wjAKhk;Q z0N(rF_m0A#f6nv$o1FKb<+0Ox+pa3KnkG|g>Xa0LD>4l-3Sgl^RNx$wPn!rQ!nL55 zRb}bIPGDO{@!E$YjpuvdI1K{G0iuPYmO>;b-%>eQG>5*9pq_nlo^>XF~@6?1853s^A%U|Q~9M@Ccd zf)??zen=s|@z`3mX7|nl>Y2hP;B`o!!{q+8$E z;o{SdK9eE5I%f~`IGyxCegh`%rqDo}y2f&D0I2wbQL6|sk-dGA*T>sZ=(z&OX($d&xa9mI?Zb%6AD-RaY(zjq}bFXZ`o z;{aGS{kw$zZ1M{q)3x$ zW<7I^0PR%qtf`_zDQOqQaWqmd1dMVUHX z3w=%wmA!~lSmo{AhU#EKx6HCQf9qM}ajs|=xGUNO-{_gN)`o5nk{9U1-#=fT96T=^ z0Hgm6gM({+*oB#syv)-PR3q02RLBUgiYP)$CAV)DVzg~K*BfMwQ<M(@wFN0*1-_2eYF(2ae6vo{*=Y(Q{^PD3S0CY0aVkTTnE_7C&l~oS+UAz^TUG< zKj9>?FcA5e-|?Up&;q(_7g{+4zt)ZtdA26gv>I(mmr0}dT<6nL;BXP%(Kh`Dh1$Qk zM+Q0t``~ArVBn>Ix@nZB4HtE_$*_lSBvBi=&Z3NrIK_#lL2o{Go=RP-pj0#!kT zgCMJL6>wFU2;4I3c_iPmTCs~H^!PQ`63%MhF)X7N+66C;=J~6d)8NvEG7TY$AuP_T zGFF_Vxo>2=Om5%N!&9>Rng2h*C%we0waKH!(o0rGWGV)KN2(5PM!*oFPIt6*N3+Gp zMLdr!r4KJG>l8S$=;TuGdel+U+kt0zHehD@bZedsLF+HroBaSCiItzwmOecKJG4?# zjgs!Va&fM2MymK)H#`bT70=KWI_$P$+-PW|R2k?5YMYgGWdXF4nRwVHa#3b2*?{?( zWhu9;f=Wi?XKjmPg+IcDknk<*9pGjYf5S`##Cv{m@jaJce)*-Xn-}8vf;xa7e9wFK zvgr2@-7h}H^Zji7L$h){hNiGka6NApwy3HATX-2|=p=1^`zaLRN6EVU z+R_ww1?BN6DEO&3(dIbR#9~fQBBIbqc@t;du`)hnvcEW?J z@WDukBtRUqMr2(bPm=khHp*I7`CMrTTmT5$ReozzLvh6MS9+@4tNcK&(VV(MuY6IT znp|uruuSo+K)?18WGhP9otXZ~98e#f3h>Xi-T_EI^33*Ks`&P8W>ESdw+8EGfmZlD z9-C!7k6oVYYI%70`w9Q|uqwLZoqqDW8Hj{^_WWH!!Pa0=ovvrzj)`^|Mhu3+?nV5-I)agb-Fb#(=mYh_97jOs40WW`^pbxW}A9PM-Nt= zM{w~9*m2D0nYaoh#?H}!CU9vlZOtLHHp)42p{(Ls`RVfl)E@+{QNa4v*YkONK^(x3 z{^*a6;sO5=3>eRDKbYE#F-8KT{HUcy*%+$==%uq#uC!Hz2){E(m56Kw8$mgWay`%7 zZLyD$l7*;6+RULue(g5(fahq6#Q2YpXsf5L!ga)~%2Ac78kKnHp`t+PQ>R59Nd=FR zbDhvu9QoNMRcy-7ckR1pAj-^Ws&dTZEZRIfaM<{>e(R3#pyEM0?CS924~NdGNEN3j z9WcBxBk7a1Vak_1a4q1-vo% z4AX!)wkfydn&9R=mt`RkFli^e{Nx|)$j&cql3|W@SzshtH;#x}8QnEP@?@>X?d`?t zO?Q>!yh$F^0Ydq%6qS|g&TM@{O{Bn&+lJ?%I7~FI&|o5jQ+3py?%bi zOXeuF8m`Gq7=)8={7% zpLjIzR6OB&Y9MJZ2^0XumN;DuN&{N?sVsT z_pqO*FB9F-pHG9T4-0eCWdVI%nBq3|)Jg7ooZ)4xEO;XCu%13L6N=NJ8)a`^i#VV~ z9T09J2g-B=Mggq@$nbFAVIK2k{sXa9 z={!aml(c6n48o!)Ci#vAGwL#e8``|BZ013k$?X@Gbz8QU@pbFVuugzmXZ>s>>cflI zRFzw8sCxtO+=&3@@=d!LUTQJNZOEB3r^?icqh;pQi89TbcIHkWi_n)SU-omj8XUq@ zye-sKk#z^zp;& z|36F@l@^sg3ZFWv8%blKfd}C=2hsAo0{Wp6bck=m!R98Nl&`kZ_-X`G@yvo2dAVBv zo=raDSG!v-Gw^Qe!QIv90@m(Wi0Cgf@8J9G4Dc0WE+&OG^GIeYNoGKXyXcSRUMP?lx+r!&?Dc71dIlkmUfW;ZmN9Hde2vS-W|aB$jCeJdN=uquMt25tKy@~ zRhXQ^9F=lFMT8S|^sl4Q49r~|U02rcxuop6{JOGz&qZZo{l>CpWQ6t|+qBXD4hPNu zlZaMeHdGTrG zfuGI~O~Kf4zWu|Ang+RZGPY?u8>n`b_1hT~wq8&sF#6GTli5$?@y-Gl=6iijw{S>}16hF=F%~=${6p*i!Oq3ZR<(?R|bC-B{Kc1Q!lS~ z!ln`*+~7CDXSdh;{XH5vBF+&3%$z<|&T=d6)R9Bw6d%MqbL2poKJsLlJ#i#chzxm4 zHH5XV7@k%D5S2k%R0l_3=~e-1}n)kgHGoH&al%F)jIyuxWPln^6i|Wnq`ZcryO*|wzZCB%l#FGb*j_US=5o) z(Qj$?Y&p%-frh*7(_y|RaKo;Bxz=ba?Lnv4x4ml4nub#r<)1#y7fF+C`7MC|Vjt5w zBE8`aTZ#U5=Y=UgG&Q+>7bE@VGB`TMItyO21%M4i1rxdh(`$Zo`QJ^JSFWt6f)g3W zK^r}+r~mDjUtf;D@{Q%d=RaB=|IBX_HS)0zA-QlfH84`PUvopb z`t{#fHf*~fqYiqI=1o8fiS;0if*%)MG#42Dv7RXlJ%F}r?ZSQQmU(UJx~&)8-#!43 ztQjr5nYc`D0Ea+$zui{0?Yk`N2-7^Yj_TK)G{NY5yKxDgJZ!-FM&f z5(PYO4&dE>%yVJky*$X#cl^ZhjAT(Nn9~+Tis3l%K`h@4EhV<@z^%6Z7K9 z2%s^aPtui9-i|w}P!ZHT3o}zkxF5|6E2mjtTr{*oa?3;zO1Y2G(}kZQ777iq2glZx zaW<(WO!PGk%_&slthakCu}M z9%gskqd0?O#We&K+J!O|MrF{Lgyy){c%jh_GH-RR8ZD)YI@ymQ96tC&+BT=r)$ZRZXUSxaT(gQ25=Kcdl$FJL;jId7e=i~vYu@< zQv2hl18^;6bYflL>14;&GxsJ8KXsT_;|YIdNkGdT_-j_+C<{P=jrGMZ|TRnKLr_uG?}!8As7v zAzs440EH<)bqZdHLsK|$*9b=l2mNgz4Bd6v)vO;dA3l1BIpQO{H}9b`#bVJci$#mf z9hZm>RA7(^ZW@`&oI41FwreLh#7=ZVhX3;@(&^*A00jIygGn1`igB8!7IH{=G%}aL zRUDJZj}x#yuyhPnHu09x2D3e1fkTJywn|l{&!-OlAnv zb4Q3euJ&g$5d5fX3%~`ihUumq?<6^;8N-Qf+ka)b;Hn$SwrgKoM%k%3W1!bL0??pt zU)lZ2o6795zf*4e5C5_v*KtXindG~2%57OFFyC9l>MaodCU*pW=J~iI@Vq#H4}S22 zBXbLL-^U2p=L0`=w35Lxq0WO<;;b@PZ3~kkSXOSYxao~$-NsE0E)n)HJlr~3_)6`U z%O`&KH_HKB?D52}TMcN@_>Xbt>ynj5t$Rjmf7+;+KT@8&>()$vq*=I)*Kw%SkG`gVoDEI}C$NLOzd_MI;`f}U z=fwfA+2s0>I|!u3OWwjjTVEK6a<=2*Ni~vzS>JK64 zZ5aPuzxP|^lOO)I7=?lx>_!z~NBNP0%=YO$ucO4Bv#~#wyWTsdf5Hj)pL*zGlFBm6 zU5eAkj+7_vxkJs)E4ejj=l`4bUR*XY4;|mKlb3elTmazoR+|)-N?`KPPYmk!{Cqja zmJ9cn8P+mRGEF$jrk-Pu+#P4I;3DD@3d%j`_4F~yFy_1e3}A+NjQnXvuQ_lsYKT*# zqh{Pu(I_f3sNgt}jE)pUxq=83bFG1<9Y;!IM808JrBo)>7%K8vtfNLoE09TBc-r`x zIl3W=iT=ZP%z9~lrkp+c6d^4yz+fRp{9KdSw0m!0sQ5D=vphUrMtANmJ2ZN}CS-VT zl;B52(g-xr?0&?u)Z zs#*}^gNMRyy8{y~!UO5-(@>?Ae?~pYvy2ArMe$CNYd>H5Z~v}5e*5RjRp0U6vJpCh zyExNFKVis~Z}|)5sk^@z{?<2H*Fi7*>SUG3ix%puQNRNI6ODY&HT(Df>>Uq0@I*(* zb8>jj9e{88;Wo$nc;KgR=EQ9E{t&4FDo}^LB{h@(DqQ|$E{NiAyKmPeG8GGuqofTO z|AU|ZMEQf?{BOzfLzH!OwJLFh2(YHC9+h%kRC13w7H-F#Rw3p%2j}Kd;4VKxK_Hns zqX4#_C3^99h*3oWjslW4$m|LHm8fd);Uw_?4nQ>p4OdLvkh_(KW~)Q`c;&6HWb9XPNw z&)18q0}$-U*4+I=Sm(TLOcBdC4{agce~ zv4`&|58QiadH9Z7%R#~L`!2Hoaf+U!KhL=XfMGYY`hUap)D&Mz znT?PgfgF(~y6xbSzUm1%^3Ss24O1%fcZtx zfz_>wca2nLg)Q?{+?q`r1YdW%0nZUu!%`UlrBD=}=gYPOhGFH?tUvgCzVpLjcE4@E z=rZ1ke?4>8mz4>o555#*mdLiIY~r`~lFQ23SKP<~+L5w#*KQWB z=)?LX=dvdbJqflsj*~r98x!kdAiqcX5ae!7YP?An3c|rmU*` z5_(f7W>#4eK4tNeO+$@mBH$EEdey*uqFiWQ1@%@sqtI!%kic!dQUx0eJu(b5ocJAEyQ+!n9CLSg6PCfPI$wol#rKsiqM?87H2 zXu?^<)!J2Q*08lY7lkC(O`J2#xB?!eQQgW*Sy1I+zupO0nw??|U}Noozw0(`ESGJ#G1Dc#9iU?rZ>bOu zr*~_K@EqxvxSMj|k8dsa-TLYB$UR>!Ck`KC-$4=^7g?t<41Q@{4VT(_ttr~ZG zD^#OR=bjGjw+q8NQ%-vqC*^bK0+vVH$d5Yh@mUCZThTY5%JB#9EKf5P@g**%&;>1% ztbJtP26@4uD-&<=Rrl-QQt{Q{WUXSk@8Wgq*1Z`A@JqsbF4A-A0C>yq{-MFaw`pL0 zzb*vBw1iGj(-?~0ScCxmQyK2O%Z(P*D_R-h!t>*VQoy6ctA>r9-{{pb_B<$?$ABW z4no%s9{KWZp~DY*?78mMgoM}fmEYZEQNt$2bK#KF3XXH(MYn{FLAbCqgBQ5?jqfZ| zAOFoV#cQ|N4oDHSSjQCFG^Q>mhApiCWQ5N_!+=g{moFZrMLyW4=OQ5r6`nkit`x_S zc$IqQ0WXfRHqNzjWlgoLS;xZXr7tVv6UdK7X-Av5S300;bRcZXI>$PUegYOR@j{q` zx87Dh|M^ds`@eKcIY~66;kdA?Obo3hRVcMZ+pnU=6Z0d1-03j~C{A7hU`czp&5#p3U@}IsgRm%@Y$78~wOPHvT}o zi*ivqsEA^ar_r{BIf)B{FhoAB7J*VFbyaT{QsTAEq+OKUMkHmUC#?=fHK|Omg#yu& z*RasM7z4ts^EnJd!6Jl~R3<~_2$<$I+5pT7#Y)JC*qRb~+sK{`uQd>-1`AAmbOr~w zYcaw!!G*vJbp}_xBDaxLB#lBxurjF=&Wp{z=;g02d=ZobGcM-}NKa=i; z8T+g}s!TP?Sje1M9;jrH7R9V13w*-xTLcC2X=bH1LdolF&n8cqWhx-u)RiCZjn@g; zuQHisuCs<)b$eLMyY%aBEc>ox3bbCu?__61l5ou%BfQ@PVQXgQ%F}m!vE2HZk7M+= zmXky}%0(XgbAwF9pKEaO+@ua=RQ967so`#!I3{(+=FDn!@W+6yA-voP;mWGY*pmz_ z6+-Qw^V|ZZUK|dTw5f%>4NuUWIUW+K1`ZC$R5Q0;P0d-+2U$7=`&aknefZ3|*XBp> z`ubkQTZ68==N`hK=O8_24&Z}q{9(u6UzZ4skQv5b!< zJQfqdCUKz?8XF&DwVd0L7hK3f(<{r7NBH>WeYclm58TQ9=BLUcPNpAc)7pubxcZAi zbw|jF;b-n_KjcU0yL|UEteoRZQ=%g3Q9uLR%QP3rh@#k?H+I3kviHibEBmgyvFzBh zx2&7s$vQO4m4ErG?1Uo-SMPnX=@HgW?qH<9>+_#1hYmc-tHh==!pplX;8~eg;-a2Z zBdL6`NaxjLC5_b?*+zV;0p+5@A#&zVYZoJ>m33oy5SA&PuzG_+b?o6`rW_R?VMXr1 zcEAy)NmmenC9j z={a)%Cr_Sy&HDB0ukwq&bD8^B15?1h`U`Ti$pUI2KGe&>(8pC-=H9rBb0lkf%3W0- zW5sxg72`#3v^qf!r(qj#6`%^E!ZrapLh5`{PG2s(nNWo=+appWqYB2d7=PywnkN=- z&*R7(VT1u#mN&p(v6~-Hzwy@QiC_VUTJ@(~MG>7>t&?ep!xSst87hHBtHnu!{&zH)fyKgPCJeRP< z3Vhf}Ybc`c>J$YLguyk&4=v&o=LbF-fmE1R4UYa3S?Jv89>*qsRrom(8rzsHuSPPv zz+?T^?PZkPUfdB)ijUw7wDa>esZL>*?+AYAXMe0b!oB)g7KYO)9e9f_qdEK$wh`p0Yx;`!reH$7(#fG7UGS^muM^qNK?SEF2TMgn6LbY;Vv(uBtO5x`$qDn65-E)CjLm$F*nUju7B3rt^ri)l zDkutd6a=;FxkBUGmN6)lK`>2W?OTOaWz2H~sc)Y#f~feZK&c-%)yS-$H|fy!*uh6x z=sU`0m>p#UPq1ai*AXSICi_(PymS$4!rO^F{kCQA{&M{KS7&3?p}TJ>XW4b=^XmB? zBDlmE>ZG7edK(`!p3t9LUEoh$cTZ+|VDK7TtJ9Nj^K8i;U}M&{ORg?^ue*sS{&*mZ z9h9=HV_P+N@YE65rS|03T#tyL9C-Zkau1)?c+90N!#Fwb@VxFA6f$ASm zqi6BYbK(F#@PQAkCG>uaJjrO!38$5bVV)NXr4lX^;#DOq%rt!L zD9?cjzLb0MrEH{mimH`~@t_vcM#b=0g(F6t zkFpJyU3nXs-=8^hqMTr-e{9$E zE*;XZ?R|uMWaExJ4AeEtt<1R-hw>C>07N(Iz#*JCT8{RYX%_r;@aW^A6rF&O)ma?# zz|fkZchb$r)917J=Q(izc&k@WPEKAr$BW@J`m1P~55&RG2ur~z)ZBw6&+)Tzy9&q4 zxl>27rF7E;yF;>Vb-25j13!4nXR6V5g13S&{dtd7wnl+mbl*DnI-sLn{zwT}WR zOmXGcU@Q)llOY)0aHfF%hBQ9UDKzBZe-V5SPkk)0Z%iCGC`%slm$yZJ6Nz ztI0^X{<@G{m>O&d<;b;3JaV+WldDHxJ-3}rO+H7`7&FHHwWAN*#m>YNwOwF=hx5A* zjzEJ^)^;-`(6Dvq|`t% zPd3y<(V79()z>;=MhRiKWind<`f`aA2OlpNu&HLqPvRm6?eNws zw5TCt9YG-?*8rm*VA`Rh64&-)-x--H(>=Oa*@s~aj!Yy15TZ}7xqy0@5b_KU0zGoa zAG68i!m|H0Zz_9kcth?WI4zPVCJoHw{`pDP5!_a5xPFA4a|iGEERNs~9!5F@N;)xs zhaEbfs{CNdOFWr>5000yoqJi-dv)1!?Tz_1;edPcAwf3F8=LyT&4zJCb&z=Sj$6v7 zxJ`NA?YHHtMOK<#b4S^C)6p`zc}tmtwQ*AH-1JMwJ}sz&6dD6uc&g0hcU|z)*|vGN zJggB^bFvk6`3mkpF;%$-#{XOt_8cCD_MbOd$%!z2C5SO~XX>#I4I z7WMT{Y%061zOn9Tq`HvSqq1N4?C-H7a4zduwh0~3=@g|>uO5O`^rVYeVlR~*X^GS6 z>$~{ap+h$U_IJhc*-Fp414#J4wD?wyq>jR{42HB@96g3c0F}rXuY!zKLYig#_oI_1 zeTt5I>O&(u9!pXI`)~&PZuojWk@XPQilmAq=U!VU%+}B~=4#N2&zkeZqfOYo+hz8j zrd6T5HJES{T$*!<@Tj9>iSWSJf6{5xp=Ym3ES{7g3z?06FS_$JJTd3H z3^N5FTmN!~@ZT-K-UAor(Z?Pr$9MzHS3dS@W$!CrTQ0uot!3@5y}TC@N5Ixon!(4g z4;p#1FfTN5tzgSVSHQ3P%F}n=QjXknTbX6Aep5gNgKa z6Ln`L9nu_#;FX2$&iP1ZwlssMeF(6NXuU1)Ei^j69l+Go)GG=9_q*-a6--kIsV5Kw zMS3-(flm1~HVs2!9S<9G9Hq`4U!keeruf{Sh`^Jcf7m%{jCu zY52}Zg;oFH_3h8qdYG#a{Fy3Vb>#NCDoHishI2bAn0B77Du`ZQEh7`yt)45*{mkJj zW);3-Zyw+NGJ<)llKeHkqW~vBBnS+CI)GzG+4uw>JG`?Q20l!)K62+rf3-Zo{qtSy zC%E|a-&8j3zlH~sj2h|)1S@RDI}iqVymmA1E!a?Y@QUt3*S@+Oy#0^LA*Kgs*=(~g z$Sy>lV|dvsUdQ6zE7^X%BZejp1Oyqm4RXf2`TYW$vF`rJ@0Qy>@zL`1fycsX@$wE! zjVxpiqc0ByojG~(8QY-ySsnD5`Miahz4#I;KbnCn|0+BAWRz5y(PXBflT#UFFCAab zMRS$^(k*P^1}3dCl5?f52t)m=uLrA6f~??QYk1Ycx3chZsHNxd1qRcay*Iq6jIQ4V z%vx^)8(8oE`0r+4gZiDeL8`)3Cldt(|JtVNy=c=R{h_rPY?k4xZ;j^3$+tY)(|~6^ z0$}6MJJ|g~6d+o0(m%ia!cyrQ?tOxjVN|i|72Nk|6 z$Sc*hZY&!&zP#+-zrQ^3vTNCs@MT_UwX0loEr!qSMejdX8sGtKT`zGoTZ;})aSQLW zAN#Fx`)588#WqY{`S^8+14$pS5&31dI!<$|QzMpt5fAWuU@G#aqm1AjBX8?)9+^ob zDft~QEz?LU4{K)43*_iQZf8=V%7ytgFkED7#^sum-0=2orgC7V%;Q)(FimD%N8!8Y z!8!G7TLnmLRyJM!@`V3@0S^2k^dDun=4~JQ_0a5;M$nP7kwtW&;uxnSY;{0BboDsY zYv`>D&X$ymj~+exYK|ZA;@L>gssniUyWc%Vxca6gl(^mN?~UeQ+nA7W2&VL_#H0w> z@=mEa39}`6nIY&nn_Z4y`ASBP%VAI$!KhfuPUfiFZ($?JeP3pj^MRJiz$&O_lyz%p z`k+sR(43A!Rq!ft3>@La=(|2tkhV?%O2LmNRk>?Mbp+IpK|)TZ9>Rs%riAT%{dK9J ztE;f7m-^IQWVjz5UysKpbd`PV>*z5r zGzv@a<*}1V$^fGC0k!m#kz9u(9OT=avoHfky?~%xr)}pEl^eJkeb@5pT-sozLE69V zf*V~pys~tHSKK*9APUn2Za9K)UY}O zDgYjQz*QQY8i;~4XUaM^*Ux^5Zvxc$=>WL*e--!a_s=qC&HZGBCrOox9D70|Ph+lS zwN71((!Q!;(3p{Lpv*8|b^GgvoxAG$=24=k1WI+)+rOiH=E+COJUhwM+Avo)3=C_9 znV$1YU-awZo7!XlEp&{qX`w?<87$3cVLPtStS&!SHjjsWR+)Irgb zD9ZYRN+1uYYu}JmcwmrHEZ0nc#hhzxJX368OTX{OV*Sk9@lw}-*szcO#(RbMN<=em6(&a7 zF}N|?qm1@f^SlSm+hR~2A1^)hl`rH48tzDw*~-9oZcgsGsO)?7TQUNk6PQA)g0nwi zW@VXH8F6*_ush1z6RiDLcG$nkcInB>oJ$Le!6e9f_r}A5zcoZGvDUe*`RD8SQFY=}JDe93^xv^`)PE)PI1l#}-0YMgJmqqq1kcAKul8}9!_xp36^UELW zosJ2w*W8o*{?GHA<$J#SS)TKp<5y3F$PAi>vv?>nmPgGon%>dalcP?K;9qI8F1z?m z7LgN?8hGpC#|fEHSY7$>84sB*Kk@d~l!3wf`u&G=5y79}#=Wq5`UtMl6uFaU4}2Rn zdudt1u0KOqo~{FS3CT2;N8J0}3(KlU_(U98RN+;<7OiH`aq3hj{Ki{qj`I=?LvyGC zF`s;3Z;b`43S5#5FTtOx@h<@&LGbDCSwm@MYS*PhTB;2=EL$y~n$Ro1^|X~_883rs zJPeRImVl8vSopL0&YLo;9C+Oy<+$**Zz&Vmki%7j1`Szi}wl zTyX%*Hul%^K}F5`V;%w*+NvY1{ED&%A7Gh>y7mpE#b)K?RQ4xu&qGjvAGH;>C3azs zYYwz2pZ#eTeyTfs=Av@^FaKtldD0ndQaUmYRy^a-BB$CSHy*d4UU&=tNQzx)TjGw|or2~lJOH=mbBel|IAwuIbW%5du`mIl1>Of;0dM}^FF=Kvt-KjVE z0|tYx^(Z^CcMbwzN%xO;%89CS&3zXieWaic;~*1%OI~+gc7Mz(8m?f}1LECb`G??DJzf2B;fvo^4ldGcSp7)A!N7Eg(~Q9qLQ_D(&OAat<+7}#g&z!cd{8CS7uQCEkGpOmTHl3fs^yF?Dh|_7$%!GZrk#cILKP!j|V!>_<7S z-g7%ez@$#16Igr38-dfysH5hykKY|-^)1(yEvp_Z1NO103-yRYWM*!v6E~p;FWz7J z3@6e9sE3)Dt^wcxm;`iqB@YkL768d34{h-q-z(fIqZkvw&CNVs_@NwZQ8oN#V7@vN zKhxxm^`+;PhM`JCi0MI5R=xr*aIHW56dNi^ZVBUO+8$oD%zoMH%luPbn<1(+x#s^U zX8ynTiI11(*01B+1}0EyagmJk7QS?eSL;Cvzk&`i!S|3;kV@t5HDGdOoy}{xxg-xq z_~#{a0Q>jvS~_y%*tu?AA=A@hX#^-^BS<5ZAhf4LTZVEgf&64{jp&H3bZ4@~0=6bk z-*PpJVdk>zbtaH3j8f6?Pnb8iEIs>O7I4);RYeQ_B1A1wqL2k#qbys|jbi&y6_j*f4&+tb zU{(UQki2ZEwPWj+9uA>H7#etZSi7YG3yeK9;RpK0l?jWFXCu`!%bcalY5D1sg8D96 zUJc*8Bbf>qkMXbP)8ePstS(bmzN!r59iKz0j4V?J8F{@re#UIJ!=6$mE?8bx-*sbo z=FaQN_I0Z=G3kE&p-ZUzmyi5Uumx8K_HnwBX9Tohy02~BGTc2Et*SZFJk&ptVaE16 z2PCj;3{ru-Br*4={xIfv?NuzP1?_^^7*fL9SesZ zEsx*yeYW#vF^WbHGrg)52H~ijYEW$zY8}pAzmul%7fW6M8Fsu=-4#zZc^I7E& zeblv|-qv2qj%FL}g)hVK*>rT&_zCe5Zdpy1(Cq}(Bz(+{HBXkSF1x7QbJKO%<#y8i zOUtr1zPn66@eFz*;EO}8*ak&jO%7*UtA%tbrj;q|ZnXN2>&vru-&l4rEDJ9qzseu! zYK9dTiSf5N0MHLy&t@3Pf~UwAJ$3`wPCJqwhZ-IOLE0&&d{lmoz~`Dr9>RBe9O6;O z+*NksVSlYn0*Bwg%3M(RS7jtDDhZS1yY(q^>+`3Knttw~j=_`XmgT?nE3EmSALF<9 zSa>>|JrFPW^xvgd(}rWhwyX}W_L$H+Q1&^vT38aGL7v5Nk!GFd5tn4(ujQLgG9fsB z&YU?5SFKw0fFO<}ykri5cmHqjqYpp*4dbA!8MH*oln{s^sJtymIHWx(W`I^E|S*6?Yp)jR`=AjOGG=)yknNvumR?Lk78gT$a zSSD-G_2{qm(t28dl-vHU`2p^G?Fx@ppAU70^F98;lU|kAex7R}uPH`8A=jDdiF8j7 zaAeob7hYWMxc(}RH=}34P{!Xz_7=E-;gd_OkKwJ#Q7ma?tGv8w6`vOT=;)|LeEK_W zcA3nFL2GWkwrpTAk!v;NosP27Ypwji;~?D%PhK|27k`|#k&lTYPi8uGf|_~Kl`(Y2 z=o){3n-zO@@%tI2pe$o=Kp5(kGEz4Az($>dk;+=9-0OBz#{6~FAuv0A3q10Qy&(q2 zb0%ypKXM|w{JtB#qXCYt@&O>%=_3x7@BZW8l(nlK=W}h&E}*Qs)oAcJb#Nm|FVaU7 zc?I0`o~Q7_YYp&4{qupsZjb`(ap(kxFP8t`IDkRy__IJ9UArzZ{0Re)N_wod`)W(3 zAy<#cT_-^<3KoUQ*Kw8BC<3Kx8UXQk`y3oB z&wX|-GETY(-10|7$J3*vO&Kk3IZq~XYjcAryFZ?12{?xWR}$|Z?|pJS&$p*#&IzZN zMW>!sCeECj9u44ReS*8DP2!#O;H{g=Prm-Ga?911m(84M=sS2{>DkJNUo(=mEE9f~ z?*(QoKcy^v^Lxvr#m8kOppL|#Q&C1EIkaRpi;X7GD|m*Flb*i$%Ch;2Hut=Akif#U z)E{TmDU7cg#gR1evp#2jgR^i_nJqKpO4585ZXAbcl*{(gsG6`1P?D>E)4lV#MgfQJ zD`(Za1)A!Q&SnGD?eqZRK!9C&=PokX)UeTI!jj|5l2^RGOkcblV`6J`>wtZ7&>h8H ziaMI%!-H3Ux7>Ky#pT)6tAeYOv(13QgN}ah2k{0+I-X>4)#`h1EAvi0lODnQ${02` zb@{crYp@>a9>W2qBVP6zPViZh`GK{!Ud{Z#6Twwp9AIeVk?Q+)Zlx$A+9Jq&K5d;2hj5aCjdRy?gh*oJ>a?UJ?huSAX*-O`0^tR!!mL zP=yY)W<-r3Jz(AtklrJ+6H22d0s>Yu9-qy95G9gf^aR6OOaeDD8K$NcZ1uP3v@FzbJo$CXgEHAi8Os?|8WXx_ z>8iIK5V9P4PWBYDMTvkzynlFBupapbwBx0x$KfJv-8gVRXLrQl3w66Bd}~*8u=;&hFj9> z9{sL9eB?Sl^118gvgFn0l*MPCSB5gL;G5PMC;6n$v-2j*;>09AI-1N8ZcqQ{s!SB_ zVhOOY0F0-0A4x7$3xo?zI#ZWoovGg0p{5!>%n-ui}R>)@s8GAl^`@pPaIB+ zuJ*=4M_S#~5_r)g`7d6QOF64kjx|?ThlNw(VA+Ke8@Kf2a{M_TETh?|B~FmC<8ZKJ zw{I+;{o6k+TR0*-t$z}M503-Ex~M~*9|GwewGrP!^gX!L{1vb2lEkAr5HYL{PRGV6 z!qaEYoY}W}_3A@2tPS8VaD7P}09$`8k_eCc7RlOGr|abyyK)$m?84KPqSdU!0R?af zQ@h16_{K2GMPVV<&qZTw0MLM{XRRW7@`sn<1g4gme0x5`ZfytB+1c5roSChpJE54oc^*jw|AjUG7vHg-0U&SQfYjPZQZ7C`|>tn@l znZgKSE1&*ecJcY;{yT0-oxK*nqcL@N^+&O|uieecxw>V~qBhj9w-14*2QChwa8iHbdhToVrvoQJi@Zw<%1 zW=I**GNkiV)ShINU)9x}Y+}$i=h$+>+kQR69r2c4KZ=?$bWi!l-~3rw{lugB)K~bH zs||sUrQ&7X8CnXzo6pjp*_9Voc;D46jJzqJj3G3diQwNhEK+VGMvR!lx|^ekzRUE( z4KIlUfY?{FLMcQ!`B4?B3PRx-Vl-S$bDZcAm(FN7o)@|E{wdLl|eWW~c{pIDjxBo^tiVZ-; zK_72$6`T%iINunI9rsGk_*+;W=e5{F*ImvIKp7(m_%onpV{n7d*f$6R_^}Mb(y!ZuIRb-sQ(-KlWql}Cop{5ECHpnD`6H-LxUZrT`s8Am0Ns7U$3vPa~jn!8VTy}mL z@~+>>$-J$+Io&X1>6_l0ndql@eOLuAlWX1Hh_gbA07TB12(jX<5LL|DBdK@_J2Sn+ zS0_O_XoohHl5`>Yx6%!KNQ+_+D_^QJ;-0#Ou$YOH)8hcNKUdFAV^j_b#>W+2r?aj>oWe(>a zl8!8}0&of~*YDxD#&6@;E=d;YHesg$r&}X2wty&>zX+}|Kt^@4q8Vc#ccZACC8#< zwnDYWKDyQ8rp$nz5t%%50?_#lk42x(Sbq~=|6TUUkCa=lz5?gVq8i~@Km712)?re| zTQL;J3ccc&d|i6F!{$&v{A(>)Pj0{*#A-(vm{*6c$8hF<{LF=H_2niYh=TGuFhLIE zJ7RY*9?jfA-FK0yr>SC1o7utc?7K}ze;acL#zmukd*bY(IhRF zH;?r-UZ{h0Q4#f3!z<8QA>L(%x3Wrv?jKK7US zK3{`(h+&0QAA=n^YiKggl_b%w_bE{81fTvgNG|CQjvb&zua7OBE4}Vrn>KCwdY9|4 zE=Sx0pcS0Scx0Mg`qq6>5go6{;1H5~1%n~$DM^pP5Z??@>|1`zG(%Jw%_D5PXDLgZ z=hoZJt-`CGcl~|0mqCn|m;KVe$)*#EqvKuPJ0DAn<-(e^Ol+mA2wW8=v-u4t5g{%# zAY65Xzg>@m8Fcd5@*7mt)}o}Yt}+cttpMG?GH-iFZG04uI=D@`#fG&WfI8J7Zp$I4 z%>0wdp}|yMJMCTfRQcXT=a+ka{KIbH357b|)-j|8MLiUba)EPi^5+LM##1?2D}JGyKVTHQl1024sxl^z_yrc%+GVdeg$Bz|R+(gI)zRggk>$+O zGk(+fC9lR0lJX$N&&Hn1&iz0&etM5t=|`p0$CvWLeYchi|L#M1)m^FLFOEie`W;Dn z5nX+WqmJ+xGOAqaGleSOyYz;)#HnwV6=%yDyr_@u!3IY9ckkKrzrP2tV#SI@j7ffj z&lCsv9YFKEO|c{-f`e!Yhn)PcMj~+tAh8OtcG)!Q+M>b?Er4=vR3Q_ZvdWCqk*-%K zbmF$KU6y6Wv%`%`PWy+!jI$@$6L@|@*~(##Z9Hinaj+hR(e69`_iz+_9g+1)hWNBO zjzkE9(B>@2(hJfS(ka`NL&p-@IVOA2DQA@FbLYf3jI^vf>FR47YrEQP{K1{&r&nBD zzWK?&E05lHcX}Gmc{F~h$kHT!ow@{$G}TbA?K%7lAypxpQw|SMvcO8gYLwO!p5#x* zD|oP*O-r7-^}6!>s)w>G?nveVY&9z*tKql%@BlP+@x{?Y?C9iY+#0Mu({wuG6dbr=5P-JZyiKg{*yhF0$T#@cHud+&+ zb-&~RhN0HM90ilZ@M`V$?b|<7Qx5O1&iIJJ2`8Lz5VHDqZ z35!8{--Of|*3Y(D|WSr+YHM#%*KmbWZ zK~!M~3s5}AeaG|b84f%dTq;i7@=s-HM%|oH!`brUQ)xR?XUms#kZJFO8}3a3vw;NZ zRk`as{XOg@Ka(xGmcQaPWfVIeSw>tP>)RW($32-Y|HgZ7FW>&eUzc0ihHEdpcCWrT zR`S}t&6kXpmKlkw6bJXj)Ia?lv}x7IYnl2i3~8(7<*~tXwmdP!r0JwFPz{#?=HIgV ziSpzv*YMtdcbPDEF&}BMuAutwI3#NkJwYDiWMIAvX4BUv${scdZ9SLD`_7ojpRqrM zGGQoQDcsQ({R*;?NqRkrW4f^2#BGuI@O#NxkIZStk)(tWqw3ZhP)EX35{6 zR^0eoLtx<4ee%AG*4F3&@0~w?{#TxP<{3^{IlOSh4&arqe&t&S4;pe7J%GI8lfvfx zB($=sA{W9TBEhtdLH$v0OsPz&t)+%(XpC!$mx&&P+Pta?(CT~ssYfu9p=I2x1-+T! zR?lD-&WxXTG%E!+lo!^m0=i0MD^mq&K5^4y*H-V)P^B3}gB2P&0k*i%-RSHPYEB}p z1!=o5j3HB5SN{69mzTZq+%j>>6pXRb*>v1@&4V|uFE@PUGv#|1e44M!p2;hx*5arT zI|mIf!=}tGLwO65dKu!liD(S7gV*>L%cF{>tl>3zn&HH_cy6f6lv-8}aStsTq+c8E z^R2~%8D++b)632cYnZX;KoOkMeoiJ@!`#H$yKmvM-)Sr&ngxu?nKm~dyQOv1QM1{L zaA`IZ-SynMoCYOrJ-Noft5JzldO96*0+Pt!ZeoXb=uY0+jka!OojR|q0m8$ ze>vU$C9isGZTTr9tuFK#wRv^9;6uNcWxjbUWK{r4{gn5rYy(|9h>HxA1Ah>s3tZ)UCB#?h$Ogv)!c%#kiHX{m25N0pH`^LCzZ~?5ulP*}(0lBUdWf^DxtndbryO^BIqmK5E6ZN?$}(o0I{}e{|F);L{G(}Tp1Ses0BJy$ zzw-5uf4Dqw*R7Ri3_>^tjg?Mo88!d7auQ3QXCD8GG6rKB!frbUaEdO!J&>>PQgcPX za{Nn-gz~=)X`r+!pTNy!D@`3bMe{oh*~R_>gC@=_lb4@X=Ccjf5>6~W?##E84fo$( zHb3(;ysOUGUV_7{C;5bU)6-9IILKu7bL2b#UR`w6$}C!17B+F9=$QG-%8;>>aU|k({Dx4CCcYxrXzQYL>FHQ2<5hgg z+@h+CYANTTI5~JAc6SlKNBhJ2MedK-0i1sN>F;8zspEIx0GtS{9vtMsT5?;f>_nTw zRI2{dQ>%RHeg9y7LuImpl)3#f!4VBDdA$1HILbwtQQFFUgn%R2^uz-(_Ba7smz1nn zxcbg_?0&BL0$tInVAKhWD3M)5=&03z1vjRJvR<80r=%q#@N1r0hk~_EDcQ{o*`Ud@ z%h6}Pt-S2b?_kV7w+v<74%Pj{DbVCNj1BF<*ZMgY2wSEaPeZeia~*QKgHhAz{kjXd(( zFZ9}Yd>6}jhD@8!TKKn=Qon_r)4`poJhBsd2-E)sW z$hi60GL3^uMvom+hXRUEGziLh7^e)4p0R)(c9w7+qqK?V0kGKl}G( z)kEly+i^M9;y+=NM;=wl>dUOBb^R+I;VJpT@6Z|yqo;WkM#DGocve4!k1kior*2VO z;%ZvE*8ym6g!7pPV9a}Xp$?D_uWU-~*|U3rd(EjVX#qjD&UoYP$)Qr&BFeye5948| z1afvrH8%1|xaqbYxtC}eE2FvhpA5?DMf+!jFc~9NX&uY@EX%6%ARbFP0FxfPg8kWm zF`th)+WX#=2X^1s@#Pqn47>dG$r~=Os~1T$e+V$6SWtH}h|o!_yWRTFx-)mdz26Ol z22)5EC%c)u8S8th&$8FNrOaAQ@cfWBfV*zwLJwUE_g#(10By>*x|# zSTKAHGgKl<)DS#LAH9jO;}(`_vpCUb`3dFG8@|UPsBh6b0~Rz60*^r+K;m}?tn~9N z0$ch0yw0=xet@ON_8j)^+RnSWEjj&nHcn>6S#RYG!bQ9mb8Sa`Gl36**{teggO4~U zk&06eSRo8u^Y+wJh zLMFG=d&^Z`3#D^r#+j!&#>N+O*Zk2XbE<&}>arB~y~Zp!ON02a)yfM&q;a!W`n^0$sMcLa>en4+&LpZ9*mn0;3Jp5K&eX`+>{993x%$>bLfs zK&LKwMYx2`M0|z#$oE+`>?)iH0t0nk00$NW4_xR%d&R+^!K?JvB@W)Vpcr?7%76(o z%hFezT^65qW*IwiVwU;(ip(nTi!lUsDy=_PCjf`wG#V^h>j>R?JNW4-p^$FZ1&Udr zHWYX+R?)Zy;FQOYn#p3Ex0Q(;7q*L*b>#R-w63S+FK{Nov!##LdERj+meJ$JDTS;@*b98O4a*Kg;OdcN^4;`?A9rCxoF+Yxy|n1k zC(Zm1j#=?YocP)ca)Z(_IDkd3J+F*qzrDx4e?fVU&yDw^Uw$uFs$G_vZ%4{ZIJu{(AR33%#_icKrT!8U=da)}}4beeh zX^7(9qm$>>uu)k@JNiKm9in$Je$vDt25ouuNMyZNkAw^VqG!U)uuEd3)!Gi(LYxqB z=6CfizgLj_$z(c=KS$&M7=!eU9zB{B=o&@s0x6_0MIu9YRa%9iL1!XGp%Cm$8)8s2 zdwOCr)!43l^RDL8>qQvyj(d$#!!!PX^;0rJ2dqpC1Ic3wH2du=dk}1P^P3t_YMm-CDbTq@pqN6&WAz zj9t8e~@pbfEJXA%chFE{0&5L}Z zH>I;7*bVl(0l!Nh2K&6vkix9|`Zxz*%B#;xtLZy>Bg0xjcENnTN2_bk;NsFp?+6z z%n4=GPTZ?Qy7og-ZatupFzxFha++Te2f522pHt_^cuJX zMHoKIG%1A$J(>6BfwCZM*V*&hT=T8LG`5;*v0PJ{2mg1lWJ*mJb_M5MoF#?Ae5*-n z3R)Z-84i5+Yw7MTro|sZm$=K6oWtl2&pZ7M>{c_TJb2lc$`01o+s&^|2);`j6}!=g zI2hvTt&@0--{&0eIcgH8#k{5*!}xqG%Xtmjtc}x9xIT^b`&$?vf9DJTT!ygk-pV(< zi-RR6r>v0O=U-lhRltwn&Fqz*#8Z+~O# zJXk&k$yHF+FMo9i18MyQv8Q|9=db~w>2Y;3j}x%0Mn=KC$k?8Za?^1PfWFH<`X^-? zlb1{Q);f4xj(=Olwg@tOYoeo_aFtN7AEAuKK6x zUg(i?5F&qo@-O(<^gcYO#e++7Q)!I8J_L@i#NBIX<2JGvL4IlIY=sg}eZ~{K@~a7n z`NjXB8b=l?62Oorm5izkAe`=BkW`@%Fxk=#!a`H7tGc!vfasK{dQT0n3Rvbh;X1KJ zbUb}iaw~VOcI%ewR0gQ6#K}nd2y{Vfi#d?zhp)brG49T?lof+RY4Q6@&{lfp%9nFW z@91*>x4uxea@ws{D-Ko3s}m4^gP7GJLG{qP4-PJUDC{xxCg$ zv7o!Yc}e;H#TS(AyaOM{IAIChZj#bUDr+4;2o2@jN;LP@>zaL#F%qNUlOY&3jAozR zyt0vqO0A(9EG4R9D@*!^fb1eP{3KIAUaQ@L$bFZ`YQu9fYQ*+B+_+>fX9k218aqJa z>?tF>Oac?<8VE-<^1$D|0YfSf8GuJeF?ZIRp$k{89eKh9?lAvr)$liSt?$gG1Ai zwC*urfLJl1eJSgHgegQh^Qp&`mz}-=&=S_&>*eqq8U<9*tapd;_lO<9AtM(-sb{QC zcQ0L(jyPk3yaf7>_{tnflL2TEaqTNK^El2ksVa~F%t8uU+&pN7L%sa-z8Wd%Ff9z@ z>b(qh;gMr3@5Sc{Cj30K;<&f}HWO(K{zQe~#YxJkeA_vYoy#}!^D918KhiQg!L;w`C(GBr@QL!^JscVd-SWx3 z?%nst?!P|`%W-er$wsBa=`^GZ{W%!}d({QFo;0UONX6Qod1qtn+ zM>4v&N>NxYyAmKT+l2XWKt}^zyGc8UpW6D|`YsRhMLubTl?U1!$E5>+#x`WoEA0cY z>ogo@xdF+uY~i;29sjn>JL|ll)tsg{4MZLjW-Tmzblu0yUQ{0c9*%R}DvX6M9@QN} zWKJE?31Jf_FZYiaUlzXlZDrY;-kmuRd7&e;7dDTz{p)W0e!1wQAIceq_BsF%xrG(z zvsY=VJXd`>hH#BxlcV8~x&>|-8CGv9Je`MSyYdc&t3#>TDz|~BFf&XDtNCM(!RLHQMXc~;lIO3BXLKd`WRiq`9n+ zU1i0d@4O~@_y1KMzVfSO&F$CG+VXXx>Fi@yg&tyA)I*))rkA7HE^5i?ug#WOQDBl4 zl&$_`W=bBu_Of#IgB=#NfWb}Z@Gi#HDf%AD%O_hY z&fLtm0$w$KC)lhz0fP+~9@;hW{U3UnYmxT1MMtl}PuJ(03q+NXrG8-bF}F9DZvP{f zUs%5J$-iY9hEtE2e~MlL(cnGi7j$dyRikWl)jX0CGNH5asixGwl?Pyl?n!(duE>`T zuJM#LpSYVZb35v)&z*BKJ-wZd52ginDi_}2ha+|XR^3`$y_ANQPfBTRt3!amP7(B6 z=7BQC)_OBr=88gIiV@!?x5BiJuG~v!er|`vQdsI~ul|G=HYhaWy@!2anvC)%T%D5X z25L&ab?k$;=d*uVR=(%omGN{ztA*F9Y@Nok5y!EdIWUafcb>TJJ9JUqrV9Zvjqh+| zC%f8?S#VrA?seyunM_>yd2-iziBV2w#p}>58_JcR`DeC_z9xdThapaxutFKQi4(Ak zTnQ0F;~5izZyV=^Hp|IBi0nIRL7C4C{ZOo4sx(RsPLApT`Uh>#JYIXD%VWHue_G{X zjrT8ACg=!+Z9PslV%OzZ_6Jyf?=58`n~~&|8bqY$MG0jOvG9b|i9Aaqk1SSF;p@j- z_Wat!=94#mw>IP^R}FUzeAeQ!;@l6GxhK9d8=E-4VDL-IS#08Y`+3n~d!dnmMc|Mr{sYdf#|xSspum zIh41;uJA?Wa6*&);gs!_k$EEQKM7G`GbZnHxTBAROR!zS<`|MpUY$aeJXBUfvkaFE zflGXY3wL3R-J(N`$hb2C?DC_fGEIZA>u-pV5?2LedDEQ;e2(ttEua32@=Ut=$(}#5 z)^GGjkDE}Ae%(9Dv3&71Y}_R1U=aqIynE=FGMhytXS|oLE8~5?gI8Y+Zoxg9R%XjR zKPq4R&>xiBufHb7;U~r6LnrVE9=xPW9{DpIbfOU?+poseXKC9vbQF66o?T`xTns;| zL3*{($dv;9<26>R@%ppBu6p<(PwLNJM9ar*l$4@zBIa?pVY;s~hmG#T*ginr)sVzl zc{ZhXRc6+2z#AQiBQeyw0+r$}uMsZJ+P;2Gx%p%Nsocc|B>Q+B$#g-FFV^RV7!#(? z=EI#2mgVpJ{W9{XIZVLTaDe!Zt%J3ME@RiP5s6H9&ryqA~e7dh)`8rnIT=x!@(Aq)^(dnbNr)O8W(~f z%JLGHZVy*^dkB_{;#p>x2S(T}%0@OsS%B zN-u-5J>9l0Wv^~TIGqC_Z*xek{NY}Y*a5`Li#)`54~pH4y!SGxD!Rs&KtB2IGDg$= zZ}%^jN_sn9Q%E>he}2)M=octu*O7 z{l5LnA1m88JX03E{+DUdIr;NSYMeTEVSQypoxUHJjEPM2 z?OFX~x#%UEcwG=Pn}#()+169#RJL*`cWlmO}n%|H~ef zbvBcfD!#g*A$rv~0GX>W{QD>}dAR<5!$UtQH+=Ya%CYbGKgxpFzca?~>Ot$Z zb2pMzjiZh~sf-%^A6dA#sZ8f=gAwB=W@zGTM_-FN4BPkE{pIr?{?BC%d-x4y5md|! zqml>K-?jvkmKPZ`c*OvTKY${b-q6i()@Pvf^t+>@$<(wIB44en)|W@~8qL*T;Bl=( zUgG2pe29$^AHG<57kqvZt}WpgxE`?s$d_kSs3(_z2(uvot|C|!nFopM^2T7X7`un2 zt1zuMVDXe8E}l~slgFNe@GCs|DRYHe8O2{qc6pp&V`>e@6TAd!-dy=Z8Ox_Quxoc} z0Il_|a__f3&zFMFmXqH3>ns%>RfdpPBTMVUj*yF%9UCx^TW7Ypx~0FzZ9RI^HRbXz zekv1v4PPsa9H#8wmb)e*(61_IiMt@Gp4Er?gYp|8N*D|IzQz5aaj0g7{be z2K2w0>re*Bk3!nrup6#FAHcX;=cq9{wx6-|_;S~GE(j9A8RF8aMTxsXcmqkf-W(2b znFByx%2o%Ro{xG#@60_N@BI9S%ce)}Ehqo#@0AfOZgMeFh9J~EoIDGc9hZ73TGvv5 z5B??lB|dfIHRbF7@E17~!Lt{tZb6SsKsM=Fa&0ZJ>FRAW@G6L8CVO=P>W=dL7lCwx zuoBu;y7*Z>y1+F&G8>@1PdfD))LkOE+DHMf!^ehE4AO`JFZoj9@c#T93Vty_dZ>zw zU6o3-e?dP2$>)|3lNo;V$_Soq88!GA9<751dZsj2C8GjVf$#_ugk9@`9WA{p;}PPf z%d=PyR~ciR6Mw;v`$Iypj+)-^3A!|+D&j$U9#7nGMcKxtAt#*sZ_4xqi?UYT7DpWI zjm10j?1;x%<>9+Fmuo)%`Em>2>#H2yNv;_dR3lAEQlJ(5Dw!$;0EJgCqCp~#MZ9e_ zX+7iR!QWu~;8Ek)Dr{QqnGmfZm~1Z~fopn<-DCfY^&D;x1MAwy&#K}SA->N0x^I?EkKb2TzU#MHo_ul{ zGhsZhJgY&biY~iq;K8S3*g)mhFMhUMb>SDYyRsX^G^3WMHZE;gr9P@N@?1Qd{)mFO z>!=%l9lu*^!@p*!#;qK3c4Vi=!OotBZ~+j52A=p@-nyC%Da7cb%prKJnTk^L*8JK_ z8b!+zu^{bNth`anzd<}|4;3B|FAfbiAs$KSsyKWQm84Ec5Zo9c!cFj%>6IB0 zDbX?}&LG|t#`4cHDv!jaM19@hSQ1PnNN?ih$qS^ z232X&nrC9D3co5`tyAh-@GcI9oA9q?AzmltO#5IC)?`Q6C z=6~(l-{A>9zph?e|2%W`^`=LD!P@XNEjbUNiE<5&`OVY+^j-}@IUDMnLDiqS7^=dc zMA_%Lf-IdLJ#}XC%m4P%9mFLd7i_-tEk2PqH@`uzWE720w@C_lnvPnRq1LMIsT(z} z;!M2=+WeW+B(Yaky1HL0(VorI^5wpANc00Quqhjz=GsCUD)pE0>STi#U7>Jb26m+( zPVxvX_=tx9B5lhqvUax(BIc`<7#0YFIm9|E>gzxlN_x1_>p3WNTMEnij@vGlj8M2; zHyBN`4w=!sphg}ThJ-zNlkG);v@wQGO4|61OkO>ai~v++OsY#ll%H~A3Zz37B; zl3&?)1A4s=U0f5w$7)FO!VrT|e>xp7UcOmw_n^LV)Cs4RQ_g*FnZ5KlRwlwSLW?Yx>xXR^h5=YLPVQywyk3 zn+~ccQ34eq@jjYQWTaI6@R@j?z3Swet;h)*rJgR6Uh!zfScezup@el zy({2c)u+zc;{-;&m4kf(J*(kJ9Yv^W^l z35iHv^K;*1QSl^=d}tnjs*{L}$lLM>Cv=gsO&t0>klEBWTqZ-6E!U($cPpQU4qXwW z{!YO9TOR8;oN(k0K!mBeF{}~Pl-)$r(zTXo?)_JpR8jU`7_og7+&uhbL}iraRkh@b zk=7oD+Fu3L^7daN0Em<@t--4`Qc8=oEYc3{{DOiFh(Jf3s7P{9z5Cw0lKlQ@1xLpkAVRDK=V)g^8U#t;F(wBVb( ziLQBp!{XvHaLT;0{0;9c3s^onbT~(FRhukq-cycZA<5%6TwN~z!Y9kt=h+7Pq5I3C zCmtzFStdM~^#Ong9>RD2V(iozWy#sU%vWr4%VXDER9;y9SfveQgKGyHgel>#25CtQ zXlI>*4ocqv?xq$o(@1(;;Ul~mfoX6Iu1iKTy;D_fYBcaPC4Sa*0%`gqeDENn=(Xh$*md`XDr#;s+lC{?GFy(lTQ%R$dUPFgim+=?89FYTHHxgv5yMz>sPa+!9@lZZD zggS=S?tk_2Ehz+%S0|u<6P&a52Wf_+&gVFk7_-cyB{UxAF^D{I5_nB3tIAd@t2mHR zW(!2d*T*nlTzzJam``|*E-M+s&s=r_&IO00d`TP0>m*O%x$VNwmutRxVU*T)$yuq! znS>ixJzV^lXxwb(378Btq;~4+W43x^_=x1OA41~;g{ z;W=K;BMR%eh?q0)+;oK6Sh|Anc^~H?Zr{4K?3>Dh7ZUv5Kj{@z_`q%Tg_RXP{x5}} z*YYnW&d_I_1w*8&1t2i$zG3$L7hAA7^JbjPL=nzvT_-?ougKd zcH$R?PMuedX5*Gc97gKGp7?g^GdH`Qm1%?am0P~>PvzQ6&ZkaxEp$;4u%5i}va)Uc z>ay~K|Fw)?aCC-isenHN@T6m>&n?Hk_xD-2G_E{(?Zp|Q4L}~%B9#NT;GI=};v``y zTk$lX^kM*Fc780Ah zZOZ(}K)y^ng79KX`~?B=?fec_=k2bDB5S=8Fj}`K@kkCjBOKS|yBs%OUKbl?Ri_an zmLWu^d#hjJUtarlcqIlVwEARRwokI0%pvHHqD+AW^O}NX2mx-?AB4!OqVA%{FlO<} z^0Ie)u*_b5QjA|v3T!-vf7GCz<*HA8q+H2KJmRINZ*@5%|Lg;Il^Z_#pUM*~Hc4Yr z^}JdMFkFj1nLYBCo%9OQx#wAu)dwAx06Yr4kX zLY#^UK=5y!ziO3reCq0Qk~IzHfB~yh;4uFjs{Jqk{P@Q|-gnF~$NVyg&h;2;iIhOQ za|Us3U7E@`jlX)?nhsHF5jkz$G*+1xI32@kt5+FvNI~_Ab)}Z?a=kayq8e27$xZQ+ z);R+2#YMO~nL3)t7T2eo`~DcI0HQ?XnaX&7#MbrYn;-v6j^Mg6EsS0JD1|@>^`2wV zX_436^JBKc-c%+pI*z>s`PvVo=mqd|avyhf)iiP z*4blFJ`6=iu>JXR&o$pZq>S=aT?mc`Ih1oTpDQoGn3#DdzkbbflIhQ^{;6fU=N>}; z^5zh452Z;$qXTxu$+*0BH}>FU+b(`PnTYdw0xKc!_{NvYT82gahjHR@VDPXqgQLx- z&Yd4T9lMXB;kxJh-(W8RX6A9E!u4dHg=fB_tmLDs$qSa^tZhXrK5jfWhw=Wp8@^jE z{OkWxo_g3X4{L+c8Xj{hb9+yFcWf=|@4k_>38Txnxvbm_Lnz8ujIAhjJh@Ho2hDiOB-vaE!*BvKk6BU1O`ckwn!ltx&gaFO z*FB9RU2F%E$8Dv3W~ zW$wVntBa@T|7WP&?3YYLSV)v?qx!V;cK721NYnxNvqj&xaeaCG-aBD$m7%yQ=X%}& zLp8eDt|AmK^ACVt-xxSYvxl^a$F3~L@cqSf9CEhk2EI;7c}zhb7R zw7Rt??|VI5@gS%N>P3A@T~8+AF#jB}0~j%C)MgF_VN(Ui?3ExH(hS-A>fu0{bpJ(Q z@{E7^E1^w0B~!n=kC%(?e)|FOH?qmYdj0ip7@DJ`Q zb5G?p-y6>>VO`0*IjGOTGGIh?va@V)MgwgZC5Y`bx(&`8qm&%CX zpISnPlV@O5wmk9E^8EU>Wfa?*+l`9@u%KC?Lq}q~tjgMbK-rU(wLR_O*6c3|MAX`< zBN82SIL2>x-&dcl<@Yt54q*m7yZc+;C|fpdtm9|okyaBPX#g}^`5=l>P2H;i#CGiD zEymChEVPXq8NIrt zJTln}amM#rRC*S^Mt$JhpJ9K;XUeko{inPZ>`%0B2xg!5CY(-Rx$pdsl-=wDDZb(_ zAF3W#-3ni*&sTZ%eZKMmS;zaxDM7uUq>s@Vo@@-WhQDjTdv@Z>e$=#z)n z+j&l_jiy#+Pzpk2yvFB9%Fd8Xkx??B!mh$;a5eM@AMNkj<_I9hs&!Dzyt{^sDhpoz zhO&YqxkfQlEV^RorkXS9_CLvKJm3DzKbCFm)#q1y)+w{aksA4V=~@JyGo+E$(2MYB z&Gxbd_uUu&_wwvRcQHZuI~qGTYBy2My$;UCP>GS_j(_7MJ7X%mHB$FIX-K(LfNg-3yB6=`Hv(@k3S!*7?}eC@g7*Zw$X4c>2Ht}4y~#U>jcgSLH))Z{DBr^*${GidJf|%8mNBFbaFA&+{xjZER-XMf zw(UA({C&uB7RLYJm6w+9eD0s<%`iLQcwZq21b7L-8GLkh?H;SmKjNgrmLWW#z-Eke z?VUGem*ENX76m8JND$@hgFkUnnKXSy-JzHq*5`Zmp>&b&{r;utCJWDh?G1`}*|P0m z9%0=4W6SV~M^UQcnDWB#$AboCo!DxAElz)?aoe45Gy1s_=hc?DwfJfE0bq;2qZ{{U zrXG<^AG)Vp`Zs@4Rz31yHxsWTr*Py2JybrBU6vQHKx)eL>2YvMdJqm_+)=a2xao5c z&%S~6n19+-4mO0^V2V068uer^A3@Gxl;W!lJ*$c>H z6XX2}3)$~+v}Y;4IDO=lnat5F&YXY~txob9nofD2Mv8K2qVyb&KA06l{zDf{6UUU=bu-{GJuSFW54Lcg|q z&+Y+xcI^(~wJXzg%fgt-kdTPxIq~-*!oxJZr#UJkWKM!i*lvNU{M#Kcr5o8lzPfBCzYY}ARKDMz`;+H9k-_i_ntC3 zgVx2?@Let&WB1;)?&|$+f53Bs&x1@gep~1rUWp%xJTF!m=XPBWUjCKxwU7KsS@Za#^=*i~ zKj<~IYij9aZc^&y#`Ip}k`m2mYx(UQWV-2*pOmp|4c<3lS`}TFPoo<#aXK51;shSM zr|jJPT*lxEs?uexRnF?7anVO(Ww`k0SqZ_j<3-aY<1G^>;sc?7!>10IUTFkAbu@a? z3nahwSl-itv9pIc8TexXbeMmtrXFTti!EQid;%^1ufpCT9%ZG$^=FDpIStK_!V@z0 zn9>Q&^h&UqVKl!eR0{j2hA^)y)8f2FA$k$@NoER4<6nH{IXT{{k8P%vl{tLJKO3dH z;me;aKj0ldxnsN$f-o&iuuhfAAl?m!MnnH}m7ocq+o}{?HZnJ`?k6|%CExfmZqEF) z@^*!_1q~wV=^x^}Xs8cde`VRRnQ^tA-y~7sPv}ZU4&mJR46WLI^*q4I6Vj5>V8-B^ zaSO{Xd|G_2F@AWE2|b^?I9qxR(J{UbpxwvtIV?#>APQ4(m?Ji?E8n2yzw?G`;8}e; zQW4YuDcl8lGkEMI&M!Q>Oki1cctV=a$QIw9anl*=4_W_c`S#!barxmlzLw8-T?bKl ztkE>XfL|0)H=3bj%Q^TG_Q@n~2u*%XPU7bz-!Argc<#a57@94}vh7|0%ESpwm|Dj0 z=3vvqcj9C>b@d3Z(Pe+E2J49P?g*CndlQoG-bS7 zopGYiV^@E(T>Q~L9Pmr#^ZT z(}o=^4_*FMw(I&(S@rP!al*=3aCYg{qxM!wnQEk!&tYxql5O_9nxoEhV0c%9cxG0e z_&r;mFB=}<2=i%k%gAZ71EZIZ^uCEx(@WU&(4Fj9yosR;3=kn0pZduhc;eFChX>qO z9j*GTF6*G0%o<*nsWcvPX3~~8(xOvl5@&~*@S)P~BA`8d?Z|?%fdg-PVcWJ#%yzip zh#kQF_us$cxE0602MwL6187|%nW2)_)~oZ99kV1dL1u=jGNTg|&%)3ugwcQgpu)6_ zr8^7YE-<<%8K>8D(N4zvuX+nD|2f42CP+#tjsK`2d&||I;kDBDudwor`GFMJ;$4Ml z9pI6_wXFBSi)*~IXcKU4-Nd+mOc~Fcfaq4>Sex*u zgNc9#rJTIb@$y`R^P!^=NmVc(QKjZw+usu zQztMpfd}$&09*Z*Bd3n1uf;Yi;%ar2O!kU(5N0 zI!g7Ub&Zv+u%$J+K>5VkBZ%h|6u`)*f%Fe#?S&?jrYkzmy_^g29Nz_uoHV11I%-a2 z*9w@g&!fv{E#X{<+sob;wnR^iZ@bkVDE1y!ee1nlF!BK}>J?~&e**un!I^j2hG5^mR z|5cy#gH>K`Idzb*u2EPH> z!+7K1xT$5{%imZ|fA?>drKfSK&zO;+N11Edge!1xH|GYfS1fuu{CRcgx zLtFcx@q}+&OzX9jK4wG(&2-(d5-&L>}-%q+Bbo@xj|mABU2To;I&CULXYX zjhu{gn7)87JAV`h+~i+zL}!3wK1i!q?t933Pr6-p5`%l{<~ebs#U3ngF@khtyl-0Q zt}x6H$l%PGoasGdYyW-c&Yh3el85)VtM>2;w^E!E{M&tfegA#)=FQ~=Ue^dz!X%^` zRh=DIX`28oE77+03OWxdgU91v>+&hgHmQinYF^92hnmzJ15; zvSID&^7L)jl{L3~uk2vGyFosrjyzE(Fe~5d`SG>oWD^l)(TDQRyN?}=hcgs%J9L*d zON+DY0Lg@-uQ0bW1HNn9wv5B&Pg`d%O;KR#>R+eS`OW`c7FUg5d_p;vclwJ?cv%_4 z5Xa@ac^!#OQ$>0kei^)r<+`8yj~~n%A^B1b!AY@hskJ=o_-=aB0qOA&888VyrMK}Q zh9i<)=#QgO!M&5Q@RULHiw0vDUkWaN|NqTd1ZQR+!Un^udbRfUYs=lA{Zme2+Dr|t zH_{n5c`6NIXX;5g35Jep3$^9CdQ4ED;|@w~7HJOyevhJ;p)DvESom6$vPar+s9{p9n1 zUe7}0m~lgIsKULA*3^S$z`-3}$)gq;KN?*sO*4KQ!swNJrGR;xo~5V3bK7)>_*FOn zx=u$HUYa4mf&~ZQ$9wvQ4Ikxp-!j&Jz3!0;qb3^*gQQGhsZ0?DDP&QsQ3&H?fLDV` z(%sgKi8a4jP<1rGs|!=cEj_uMa?ZbDS^11!%kQhcnSBS#518=#5nWmr>j*IQH&`Ih z@HNUbe-Qv_buI$DK*_ocrfCRXrKe)Vh4k=1ry$*3p1`soA+Sl4<^verc#L|+;$s== z7?UEDO;$TK;fJ*jVh#I9#-`&kESQz=5Oy*Nc#u}C;TnY#hrp596|-h+DyhM*9`j`) zc%=0N&mOLcs|W;MWp<^#qi-*L`U7L8lv$^q&8fS;!9g6aWuwlq&;V?Ilu5(J)zOu2 zA?(e#M0xYWKP?ab=$h~<{1ewsjv9gvUwIh9Z*k+f#TlRZ5Qr1C1|$1M5TRTk%50%2 z@}PXWh-jRR^9p1$8LD)CV`N;%_;7WDWs{ziajC)n6d#h{8+a0PcJ6!L28s zeDW`Y)pU_lNE*B{;5LEV5FuNtGthXdfHHfFQfAg&1~ign8UX4u%(68OSALAb-U!(Cj& zk6-@~5|d_ZOFtyYHkR#`BX2E3Zj}B2d>AwD*fM>|GP=x_Z!Sw7w9_N6hG!REXU*dX zQgC1y#!A9*GiI|8XjvIDW@6d7Wn<|Ys7ue#WZ047`SV|UzKjjSI((>3q#0!;WH<3;&JC|SWtzKN6A21hf8 zu1deWwp8epzv*b6ruN?hZgR&D(($n)~4yQ{CZNx`r$ysT(ocids!LS;avhQ&V=2E^$&-;g6 zk;A^cBo2Vu4=!E0^wo4pmMe{Y4h6(k!njDhAqK{^5tMFyQp{QzR8^2?2=58dd;aO& zW4Jt%0|ns9{azR~aMJ8@5;M-zmmC-P*61YHEY_*tb?F7=noGWtycHv$bG|bbpZsr(U&eCo;sVwtu)P=g*gvJLzw74m#ND?BPwUd~4dNH2@1Al= zLZw39RKvGEd8y2Z%)O-5^1X80Kwa-KP|QnA_>uf3cQV8Ic-G+N$L@_xMz~lgj;4a} zBcut;bL@C#RoVO`UnG*h)hS;69gQWVtM(2L;U*215$D{KFE&D)LyAV1c`_^@T^xMg z$=(N@Gyf+XzysECB;loU0EHESquC$eTt5rm+Ty3Eh%_VF;xeqlbluPI;c*GH74;5j^?a+gERLkgk$p1ui^o3 zn(dWyPx8J~$-~+53SXu@QLqX~sSm64{WwIlAZ#|J- z#K;*K8m+|c=QnUF)}Dcf2S#=w{07&?Z=-vu;AMWtoqI5%L6c{d`L8;c&vido z=A8HndLh0bBvev!@TT8Wyg(QEqL2J>x#99l`C{>fGMtul#`2eCsN?HKUoAfM<7>;) zKmAF*AFys6dmN(I@VB<6+y;kg^wl8j^%<%Tb@)=w=Nh>3UnggIgFMeYd1}K`^Q(`> zSG{XGhNIf@T-nMOmt$ROJ!aC2oPdsZ(lIBO=jjFP*t9M(v*z$H245SeR(GY%I>3ke z#4`?;GSUEtKzP5>8lmtZhAym1pSYX9(t4om*s;@z!c9!N{}q$&boq`fykrhw!GZmxH@H=jv{Wj>qq6%Pkl6-RD{Wv1&EHtU|d=r;vx`*qw%6FVK~nXck}1oa(Mj& z$ZC1TGlHUpp!gv{1H0!mpiSJ9Pr)l};%vbBSdcPf`Kip-`#CDzWTq*$KE0|ucnikQ z9@neyxuvYdnE~6$*v0&)b1Gt#S?YTy> zI`F#B{!O{;v!BQoU1`m5Xp@dUkY`g6;^rXc5PX8>kqQCtzGJo6IkCs z=2{0haX*lcY~;l_HXj+cfH{RR6F6Fn*Lutk$XR)yVOxhN_R0p2kOofsd0hvOM~o|z zIBEAd#`(v*?(NJu%;hs>Uenpp0f4Xgrm}>0>cG9_{%?P|T*`L=5C7zLl8`L~xGvxT z`}j{=aaz9kb8}Pc8-v4m^!m%o`c;oYXMOEwnea%wn$buQ1;p9Vc>1&V?K!WUe#`#|adfO84m|I5vDq~RrV0IUey zw{YR2SHQ&Owu-*1t;Ez8UlCTKD{~aMjEFEuV|HyND!RZ8FN3Jw%M4 z%X`Y(-p_^}Gk{G-3i(0F=H7G=Po64=PdE-{M`J0o9 z5_Li(Ym`RyNqU@tK#i%6rRDKj1*!Sfa<&lulOLvu&`>@?nSK1rbF`MUXafg%=lsA8 z95oi=WQ@Iw{beJ&?mTtNHD&+y?R5M3z=$t%vcRT-5**`l=LaS-_GW&7p~IlQky+BZ zkDYRqkFBTl9;Bc+L;)$7?*)dAEMpe2LC2fl$I$A%Wy&JnPB6?6RjY7z-(|tGu!rYq z0sSW6maD$QLZuzGMJH1o>XDyMZKth#RhfjWLKkI6p>+>jeM#B0_UY;X!XtRo_(Lt? zo4T~16Bx^P9s3xU`)z@*OD!vZ#WOq&9Rf;4p-bn|>PWoF*2}LKx;#zx;;ZqC zGrteB0`Q1m0a{lbaQ13FvK!uiUOjZ!(D%BS!Fd3YuZD}Ez<|meh^LwufJAa{TJ}hX zp>;y1>w=38#-b-y`}}9v2iyVUj$(cPxn;u4c~LBreBC!?@V@e`&wRA3=1a27xT?|A z(qD0O2>vtzoxh6dm^?Tlgl6oi%s&xEKY{XWXhO{JLY$hA1!x{=R;WB5GIkZgWb$z1 zta0AcvnAD@-8;(20eq%Rx*zPgr~MGd0`8nsTPe$W_y*lmz7M$b;(snr@G9-Nb3ec~ z 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%0ASFUj-Nb!-}I@|ubG5!XG#!e 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