diff --git a/.atom/default-config.coffee b/.atom/default-config.coffee
index 6e5f185fd..50661bcd8 100644
--- a/.atom/default-config.coffee
+++ b/.atom/default-config.coffee
@@ -6,4 +6,5 @@ requireExtension 'command-panel'
requireExtension 'keybindings-view'
requireExtension 'snippets'
requireExtension 'status-bar'
-
+requireExtension 'wrap-guide'
+requireExtension 'markdown-preview'
diff --git a/.github b/.github
new file mode 100644
index 000000000..97fbb4616
--- /dev/null
+++ b/.github
@@ -0,0 +1,3 @@
+[docs]
+ title = The Guide to Atom
+ manifest = intro.md, styling.md, extensions/intro.md, extensions/markdown-preview.md, extensions/wrap-guide.md
diff --git a/.pairs b/.pairs
index 307920a6c..be6f30427 100644
--- a/.pairs
+++ b/.pairs
@@ -2,6 +2,7 @@ pairs:
ns: Nathan Sobo; nathan
cj: Corey Johnson; cj
dg: David Graham; dgraham
+ ks: Kevin Sawicki; kevin
email:
domain: github.com
#global: true
diff --git a/docs/extensions/intro.md b/docs/extensions/intro.md
new file mode 100644
index 000000000..01c1c040e
--- /dev/null
+++ b/docs/extensions/intro.md
@@ -0,0 +1 @@
+## Extensions
diff --git a/docs/extensions/markdown-preview.md b/docs/extensions/markdown-preview.md
new file mode 100644
index 000000000..2de97fedc
--- /dev/null
+++ b/docs/extensions/markdown-preview.md
@@ -0,0 +1,7 @@
+### Markdown Preview
+
+The `markdown-preview` extension displays the rendered HTML for the markdown
+in the current editor.
+
+It can be activated from the editor using the `meta-P` key-binding and is
+currently enabled for `.md` and `.markdown` files.
diff --git a/docs/extensions/wrap-guide.md b/docs/extensions/wrap-guide.md
new file mode 100644
index 000000000..1a26be703
--- /dev/null
+++ b/docs/extensions/wrap-guide.md
@@ -0,0 +1,31 @@
+### Wrap Guide
+
+The `wrap-guide` extension places a vertical line in each editor at a certain
+column to guide your formatting so lines do not exceed a certain width.
+
+By default the wrap-guide is placed at the 80th column.
+
+#### Configuration
+
+You can configure where this column is on a per-path basis using the following
+configuration data options:
+
+```coffeescript
+wrapGuideConfig =
+ getGuideColumn: (path, defaultColumn) ->
+ if path.indexOf('.mm', path.length - 3) is -1
+ return defaultColumn
+ else
+ return -1 # Disable the guide for Objective-C files
+requireExtension 'wrap-guide', wrapGuideConfig
+```
+
+You can configure the color and/or width of the line by adding the following
+CSS to a custom stylesheet:
+
+```css
+.wrap-guide {
+ width: 10px;
+ background-color: red;
+}
+```
diff --git a/docs/flexbox-test-2.html b/docs/flexbox-test-2.html
deleted file mode 100644
index 482f2e6e9..000000000
--- a/docs/flexbox-test-2.html
+++ /dev/null
@@ -1,64 +0,0 @@
-
-
-
-
-
-
-
-
-
- My cross size (height) should be equal to the height of my flexbox container. I should overflow, because my height should be too short to contain all my content.
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
-
-
- My cross size (height) should also be equal to the height of my flexbox container, but I don't overflow.
-
-
-
-
-
-
diff --git a/docs/flexbox-test-old.html b/docs/flexbox-test-old.html
deleted file mode 100644
index 683c44842..000000000
--- a/docs/flexbox-test-old.html
+++ /dev/null
@@ -1,84 +0,0 @@
-
-
-
- wtf
-
-
-
-
-
-
Keffiyeh mustache pickled post-ironic, lomo vegan food truck helvetica direct trade nostrud. Assumenda odio brunch, DIY non anim delectus sunt aliqua organic VHS nihil pork belly accusamus. Bushwick vice high life tumblr mumblecore ullamco. High life pop-up lomo, pariatur exercitation odio helvetica food truck ex williamsburg stumptown hoodie ea polaroid jean shorts. Keytar sed sapiente, mumblecore fixie +1 cred occaecat accusamus. Fanny pack authentic dolor, id four loko dolore ex +1 pop-up. Thundercats cliche aliqua, fugiat irony marfa chambray banksy kogi organic selvage VHS DIY. Id mumblecore nisi, brunch narwhal nostrud vegan squid before they sold out. Sriracha laborum nesciunt, in salvia you probably haven't heard of them mustache VHS commodo squid proident williamsburg. Magna vero gentrify labore, non american apparel occaecat put a bird on it mlkshk DIY occupy eu pinterest aute cliche. Nihil delectus commodo voluptate nostrud. Tattooed tempor skateboard, sed tumblr nostrud chambray put a bird on it non salvia helvetica consectetur mcsweeney's incididunt. Est semiotics ut yr, fanny pack leggings voluptate carles. Ea odd future hoodie cred. Ex small batch wayfarers sartorial. Delectus mumblecore skateboard, kogi esse keytar vinyl sriracha before they sold out typewriter marfa odd future viral mollit. Polaroid biodiesel street art viral cupidatat art party, post-ironic minim. Irony qui reprehenderit, put a bird on it eiusmod iphone labore skateboard. Craft beer keffiyeh echo park, 3 wolf moon thundercats gentrify dolor beard VHS ullamco cillum post-ironic qui chambray. Sartorial cred ex, aliqua trust fund est consectetur put a bird on it in nisi cupidatat sapiente art party freegan. Mlkshk authentic velit laborum. Est tattooed hella pickled qui flexitarian. Mustache wes anderson food truck, cardigan selvage organic wayfarers VHS irure typewriter irony. Kale chips pitchfork four loko before they sold out. Quis blog proident jean shorts voluptate, photo booth high life post-ironic odio hella whatever. Vinyl 3 wolf moon qui, officia non artisan et helvetica cosby sweater velit street art proident quinoa reprehenderit. Odd future fap put a bird on it laboris, kale chips tempor duis velit. Voluptate mlkshk brooklyn nihil. Hoodie sustainable excepteur next level. Anim qui aliqua officia keffiyeh semiotics. Est brooklyn pop-up photo booth, jean shorts banksy mumblecore. Consequat typewriter ennui put a bird on it odio. Reprehenderit narwhal master cleanse, messenger bag sed wayfarers vinyl adipisicing ex nesciunt. Mollit carles ethnic craft beer shoreditch, incididunt veniam laboris small batch authentic dreamcatcher proident you probably haven't heard of them seitan. Id art party narwhal assumenda farm-to-table brooklyn. Seitan artisan adipisicing put a bird on it aute lomo. Next level letterpress pitchfork, master cleanse Austin small batch scenester mlkshk trust fund hella accusamus laboris iphone lo-fi. Minim consectetur fanny pack occupy, lo-fi twee cupidatat nostrud laborum sint. Master cleanse consectetur excepteur enim food truck banksy. Qui nisi truffaut helvetica excepteur. Quinoa banksy non four loko tattooed keffiyeh pickled, ex semiotics quis odd future consectetur flexitarian. Quis readymade 8-bit nisi.
-
-
Keffiyeh mustache pickled post-ironic, lomo vegan food truck helvetica direct trade nostrud. Assumenda odio brunch, DIY non anim delectus sunt aliqua organic VHS nihil pork belly accusamus. Bushwick vice high life tumblr mumblecore ullamco. High life pop-up lomo, pariatur exercitation odio helvetica food truck ex williamsburg stumptown hoodie ea polaroid jean shorts. Keytar sed sapiente, mumblecore fixie +1 cred occaecat accusamus. Fanny pack authentic dolor, id four loko dolore ex +1 pop-up. Thundercats cliche aliqua, fugiat irony marfa chambray banksy kogi organic selvage VHS DIY. Id mumblecore nisi, brunch narwhal nostrud vegan squid before they sold out. Sriracha laborum nesciunt, in salvia you probably haven't heard of them mustache VHS commodo squid proident williamsburg. Magna vero gentrify labore, non american apparel occaecat put a bird on it mlkshk DIY occupy eu pinterest aute cliche. Nihil delectus commodo voluptate nostrud. Tattooed tempor skateboard, sed tumblr nostrud chambray put a bird on it non salvia helvetica consectetur mcsweeney's incididunt. Est semiotics ut yr, fanny pack leggings voluptate carles. Ea odd future hoodie cred. Ex small batch wayfarers sartorial. Delectus mumblecore skateboard, kogi esse keytar vinyl sriracha before they sold out typewriter marfa odd future viral mollit. Polaroid biodiesel street art viral cupidatat art party, post-ironic minim. Irony qui reprehenderit, put a bird on it eiusmod iphone labore skateboard. Craft beer keffiyeh echo park, 3 wolf moon thundercats gentrify dolor beard VHS ullamco cillum post-ironic qui chambray. Sartorial cred ex, aliqua trust fund est consectetur put a bird on it in nisi cupidatat sapiente art party freegan. Mlkshk authentic velit laborum. Est tattooed hella pickled qui flexitarian. Mustache wes anderson food truck, cardigan selvage organic wayfarers VHS irure typewriter irony. Kale chips pitchfork four loko before they sold out. Quis blog proident jean shorts voluptate, photo booth high life post-ironic odio hella whatever. Vinyl 3 wolf moon qui, officia non artisan et helvetica cosby sweater velit street art proident quinoa reprehenderit. Odd future fap put a bird on it laboris, kale chips tempor duis velit. Voluptate mlkshk brooklyn nihil. Hoodie sustainable excepteur next level. Anim qui aliqua officia keffiyeh semiotics. Est brooklyn pop-up photo booth, jean shorts banksy mumblecore. Consequat typewriter ennui put a bird on it odio. Reprehenderit narwhal master cleanse, messenger bag sed wayfarers vinyl adipisicing ex nesciunt. Mollit carles ethnic craft beer shoreditch, incididunt veniam laboris small batch authentic dreamcatcher proident you probably haven't heard of them seitan. Id art party narwhal assumenda farm-to-table brooklyn. Seitan artisan adipisicing put a bird on it aute lomo. Next level letterpress pitchfork, master cleanse Austin small batch scenester mlkshk trust fund hella accusamus laboris iphone lo-fi. Minim consectetur fanny pack occupy, lo-fi twee cupidatat nostrud laborum sint. Master cleanse consectetur excepteur enim food truck banksy. Qui nisi truffaut helvetica excepteur. Quinoa banksy non four loko tattooed keffiyeh pickled, ex semiotics quis odd future consectetur flexitarian. Quis readymade 8-bit nisi.
-
-
- I am a child of a flexbox item. My height should be exactly the height of my container.
-
-
-
Keffiyeh mustache pickled post-ironic, lomo vegan food truck helvetica direct trade nostrud. Assumenda odio brunch, DIY non anim delectus sunt aliqua organic VHS nihil pork belly accusamus. Bushwick vice high life tumblr mumblecore ullamco. High life pop-up lomo, pariatur exercitation odio helvetica food truck ex williamsburg stumptown hoodie ea polaroid jean shorts. Keytar sed sapiente, mumblecore fixie +1 cred occaecat accusamus. Fanny pack authentic dolor, id four loko dolore ex +1 pop-up. Thundercats cliche aliqua, fugiat irony marfa chambray banksy kogi organic selvage VHS DIY. Id mumblecore nisi, brunch narwhal nostrud vegan squid before they sold out. Sriracha laborum nesciunt, in salvia you probably haven't heard of them mustache VHS commodo squid proident williamsburg. Magna vero gentrify labore, non american apparel occaecat put a bird on it mlkshk DIY occupy eu pinterest aute cliche. Nihil delectus commodo voluptate nostrud. Tattooed tempor skateboard, sed tumblr nostrud chambray put a bird on it non salvia helvetica consectetur mcsweeney's incididunt. Est semiotics ut yr, fanny pack leggings voluptate carles. Ea odd future hoodie cred. Ex small batch wayfarers sartorial. Delectus mumblecore skateboard, kogi esse keytar vinyl sriracha before they sold out typewriter marfa odd future viral mollit. Polaroid biodiesel street art viral cupidatat art party, post-ironic minim. Irony qui reprehenderit, put a bird on it eiusmod iphone labore skateboard. Craft beer keffiyeh echo park, 3 wolf moon thundercats gentrify dolor beard VHS ullamco cillum post-ironic qui chambray. Sartorial cred ex, aliqua trust fund est consectetur put a bird on it in nisi cupidatat sapiente art party freegan. Mlkshk authentic velit laborum. Est tattooed hella pickled qui flexitarian. Mustache wes anderson food truck, cardigan selvage organic wayfarers VHS irure typewriter irony. Kale chips pitchfork four loko before they sold out. Quis blog proident jean shorts voluptate, photo booth high life post-ironic odio hella whatever. Vinyl 3 wolf moon qui, officia non artisan et helvetica cosby sweater velit street art proident quinoa reprehenderit. Odd future fap put a bird on it laboris, kale chips tempor duis velit. Voluptate mlkshk brooklyn nihil. Hoodie sustainable excepteur next level. Anim qui aliqua officia keffiyeh semiotics. Est brooklyn pop-up photo booth, jean shorts banksy mumblecore. Consequat typewriter ennui put a bird on it odio. Reprehenderit narwhal master cleanse, messenger bag sed wayfarers vinyl adipisicing ex nesciunt. Mollit carles ethnic craft beer shoreditch, incididunt veniam laboris small batch authentic dreamcatcher proident you probably haven't heard of them seitan. Id art party narwhal assumenda farm-to-table brooklyn. Seitan artisan adipisicing put a bird on it aute lomo. Next level letterpress pitchfork, master cleanse Austin small batch scenester mlkshk trust fund hella accusamus laboris iphone lo-fi. Minim consectetur fanny pack occupy, lo-fi twee cupidatat nostrud laborum sint. Master cleanse consectetur excepteur enim food truck banksy. Qui nisi truffaut helvetica excepteur. Quinoa banksy non four loko tattooed keffiyeh pickled, ex semiotics quis odd future consectetur flexitarian. Quis readymade 8-bit nisi.
-
-
Keffiyeh mustache pickled post-ironic, lomo vegan food truck helvetica direct trade nostrud. Assumenda odio brunch, DIY non anim delectus sunt aliqua organic VHS nihil pork belly accusamus. Bushwick vice high life tumblr mumblecore ullamco. High life pop-up lomo, pariatur exercitation odio helvetica food truck ex williamsburg stumptown hoodie ea polaroid jean shorts. Keytar sed sapiente, mumblecore fixie +1 cred occaecat accusamus. Fanny pack authentic dolor, id four loko dolore ex +1 pop-up. Thundercats cliche aliqua, fugiat irony marfa chambray banksy kogi organic selvage VHS DIY. Id mumblecore nisi, brunch narwhal nostrud vegan squid before they sold out. Sriracha laborum nesciunt, in salvia you probably haven't heard of them mustache VHS commodo squid proident williamsburg. Magna vero gentrify labore, non american apparel occaecat put a bird on it mlkshk DIY occupy eu pinterest aute cliche. Nihil delectus commodo voluptate nostrud. Tattooed tempor skateboard, sed tumblr nostrud chambray put a bird on it non salvia helvetica consectetur mcsweeney's incididunt. Est semiotics ut yr, fanny pack leggings voluptate carles. Ea odd future hoodie cred. Ex small batch wayfarers sartorial. Delectus mumblecore skateboard, kogi esse keytar vinyl sriracha before they sold out typewriter marfa odd future viral mollit. Polaroid biodiesel street art viral cupidatat art party, post-ironic minim. Irony qui reprehenderit, put a bird on it eiusmod iphone labore skateboard. Craft beer keffiyeh echo park, 3 wolf moon thundercats gentrify dolor beard VHS ullamco cillum post-ironic qui chambray. Sartorial cred ex, aliqua trust fund est consectetur put a bird on it in nisi cupidatat sapiente art party freegan. Mlkshk authentic velit laborum. Est tattooed hella pickled qui flexitarian. Mustache wes anderson food truck, cardigan selvage organic wayfarers VHS irure typewriter irony. Kale chips pitchfork four loko before they sold out. Quis blog proident jean shorts voluptate, photo booth high life post-ironic odio hella whatever. Vinyl 3 wolf moon qui, officia non artisan et helvetica cosby sweater velit street art proident quinoa reprehenderit. Odd future fap put a bird on it laboris, kale chips tempor duis velit. Voluptate mlkshk brooklyn nihil. Hoodie sustainable excepteur next level. Anim qui aliqua officia keffiyeh semiotics. Est brooklyn pop-up photo booth, jean shorts banksy mumblecore. Consequat typewriter ennui put a bird on it odio. Reprehenderit narwhal master cleanse, messenger bag sed wayfarers vinyl adipisicing ex nesciunt. Mollit carles ethnic craft beer shoreditch, incididunt veniam laboris small batch authentic dreamcatcher proident you probably haven't heard of them seitan. Id art party narwhal assumenda farm-to-table brooklyn. Seitan artisan adipisicing put a bird on it aute lomo. Next level letterpress pitchfork, master cleanse Austin small batch scenester mlkshk trust fund hella accusamus laboris iphone lo-fi. Minim consectetur fanny pack occupy, lo-fi twee cupidatat nostrud laborum sint. Master cleanse consectetur excepteur enim food truck banksy. Qui nisi truffaut helvetica excepteur. Quinoa banksy non four loko tattooed keffiyeh pickled, ex semiotics quis odd future consectetur flexitarian. Quis readymade 8-bit nisi.
-
-
-
-
diff --git a/docs/flexbox-test.html b/docs/flexbox-test.html
deleted file mode 100644
index c301a8ab7..000000000
--- a/docs/flexbox-test.html
+++ /dev/null
@@ -1,53 +0,0 @@
-
-
-
-
-
-
-
-
-
- I should be 100% of the height of my container, right?
-
-
-
- I don't have a flex property, so I should have an auto height
-
-
-
-
diff --git a/docs/intro.md b/docs/intro.md
new file mode 100644
index 000000000..402cf5a27
--- /dev/null
+++ b/docs/intro.md
@@ -0,0 +1,3 @@
+## The Definitive Guide to Atom
+
+Welcome!
diff --git a/docs/styling.md b/docs/styling.md
new file mode 100644
index 000000000..83adf8132
--- /dev/null
+++ b/docs/styling.md
@@ -0,0 +1,24 @@
+## Styling Tweaks
+
+### Cursor Line Highlighting
+
+Atom highlights the background color of the entire line where the cursor
+currently is and also changes the foreground color of the line number in the
+gutter.
+
+You can change the background color using the following CSS:
+
+```css
+.editor.focused .line.cursor-line,
+.editor.focused .line-number.cursor-line-number-background {
+ background-color: green;
+}
+```
+
+You can change the line number foreground color using the following CSS:
+
+```css
+.editor.focused .line-number.cursor-line-number {
+ color: blue;
+}
+```
diff --git a/docs/file-modification b/notes/file-modification
similarity index 100%
rename from docs/file-modification
rename to notes/file-modification
diff --git a/notes/kyles-little-things.md b/notes/kyles-little-things.md
new file mode 100644
index 000000000..2f8f21efd
--- /dev/null
+++ b/notes/kyles-little-things.md
@@ -0,0 +1,6 @@
+## The Little Things™
+I recently switched over to Sublime Text 2 and for the most part it's been pretty awesome. But I've been noticing a lot of little things that I really appreciate in an editor, so I thought I'd note them down.
+
+1. Indenting soft-wrapped lines http://share.kyleneath.com/captures/_upsell.html.erb-20120127-231402.png
+2. Respecting Chrome-like tab behavior (drag between windows/panes, `⌘+Shift+T` to get last closed tab back, `⌘+N` for a new tab in your current pane)
+3. Indent markers http://share.kyleneath.com/captures/billing_dependency.rb-20120127-232754.png
diff --git a/spec/app/edit-session-spec.coffee b/spec/app/edit-session-spec.coffee
index b392cac8f..670152485 100644
--- a/spec/app/edit-session-spec.coffee
+++ b/spec/app/edit-session-spec.coffee
@@ -1360,6 +1360,19 @@ describe "EditSession", ->
editSession.toggleLineCommentsInSelection()
expect(buffer.lineForRow(4)).toBe " while(items.length > 0) {"
+ it "uncomments when the line lacks the trailing whitespace in the comment regex", ->
+ editSession.setSelectedBufferRange([[10, 0], [10, 0]])
+ editSession.toggleLineCommentsInSelection()
+
+ expect(buffer.lineForRow(10)).toBe "// "
+ expect(editSession.getSelectedBufferRange()).toEqual [[10, 3], [10, 3]]
+ editSession.backspace()
+ expect(buffer.lineForRow(10)).toBe "//"
+
+ editSession.toggleLineCommentsInSelection()
+ expect(buffer.lineForRow(10)).toBe ""
+ expect(editSession.getSelectedBufferRange()).toEqual [[10, 0], [10, 0]]
+
describe ".undo() and .redo()", ->
it "undoes/redoes the last change", ->
editSession.insertText("foo")
diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee
index 45f942ca7..7ff41560c 100644
--- a/spec/app/editor-spec.coffee
+++ b/spec/app/editor-spec.coffee
@@ -157,7 +157,7 @@ describe "Editor", ->
editSession = editor.activeEditSession
spyOn(editSession, 'destroy').andCallThrough()
spyOn(editor, "remove").andCallThrough()
- editor.trigger "close"
+ editor.trigger "core:close"
expect(editSession.destroy).toHaveBeenCalled()
expect(editor.remove).not.toHaveBeenCalled()
expect(editor.getBuffer()).toBe buffer
@@ -167,13 +167,13 @@ describe "Editor", ->
expect(editor.mini).toBeFalsy()
expect(editor.editSessions.length).toBe 1
spyOn(editor, 'remove').andCallThrough()
- editor.trigger 'close'
+ editor.trigger 'core:close'
spyOn(editSession, 'destroy').andCallThrough()
expect(editor.remove).toHaveBeenCalled()
miniEditor = new Editor(mini: true)
spyOn(miniEditor, 'remove').andCallThrough()
- miniEditor.trigger 'close'
+ miniEditor.trigger 'core:close'
expect(miniEditor.remove).not.toHaveBeenCalled()
describe "when buffer is modified", ->
@@ -181,7 +181,7 @@ describe "Editor", ->
spyOn(editor, 'remove').andCallThrough()
spyOn(atom, 'confirm')
editor.insertText("I AM CHANGED!")
- editor.trigger "close"
+ editor.trigger "core:close"
expect(editor.remove).not.toHaveBeenCalled()
expect(atom.confirm).toHaveBeenCalled()
@@ -494,7 +494,7 @@ describe "Editor", ->
expect(editor.getCursorView().position()).toEqual { top: 5 * editor.lineHeight, left: 5 * editor.charWidth }
# ensure we clean up font size subscription
- editor.trigger('close')
+ editor.trigger('core:close')
rootView.setFontSize(22)
expect(editor.css('font-size')).toBe '30px'
@@ -1494,18 +1494,18 @@ describe "Editor", ->
describe "width", ->
it "sets the width based on largest line number", ->
- expect(editor.gutter.lineNumbers.outerWidth()).toBe editor.charWidth * 2
+ expect(editor.gutter.lineNumbers.outerWidth()).toBe(editor.charWidth * 2 + editor.gutter.calculateLineNumberPadding())
it "updates the width and the left position of the scroll view when total number of lines gains a digit", ->
editor.setText("")
- expect(editor.gutter.lineNumbers.outerWidth()).toBe editor.charWidth * 1
+ expect(editor.gutter.lineNumbers.outerWidth()).toBe(editor.charWidth * 1 + editor.gutter.calculateLineNumberPadding())
expect(parseInt(editor.scrollView.css('left'))).toBe editor.gutter.outerWidth()
for i in [1..9] # Ends on an empty line 10
editor.insertText "#{i}\n"
- expect(editor.gutter.lineNumbers.outerWidth()).toBe editor.charWidth * 2
+ expect(editor.gutter.lineNumbers.outerWidth()).toBe(editor.charWidth * 2 + editor.gutter.calculateLineNumberPadding())
expect(parseInt(editor.scrollView.css('left'))).toBe editor.gutter.outerWidth()
describe "when lines are inserted", ->
@@ -1589,18 +1589,113 @@ describe "Editor", ->
expect(miniEditor.gutter).toBeHidden()
expect(miniEditor.scrollView.css('left')).toBe '0px'
- it "highlights the line where the initial cursor position is", ->
- { row, column } = editor.getCursorBufferPosition()
- expect(row).toBe 0
+ it "doesn't highlight the only line", ->
+ miniEditor = new Editor(mini: true)
+ miniEditor.attachToDom()
+ expect(miniEditor.getCursorBufferPosition().row).toBe 0
+ expect(miniEditor.find('.line.cursor-line').length).toBe 0
+
+
+ describe "gutter line highlighting", ->
+ beforeEach ->
+ editor.attachToDom(heightInLines: 5.5)
+
+ describe "when there is no wrapping", ->
+ it "highlights the line where the initial cursor position is", ->
+ expect(editor.getCursorBufferPosition().row).toBe 0
+ expect(editor.find('.line-number.cursor-line-number').length).toBe 1
+ expect(editor.find('.line-number.cursor-line-number').text()).toBe "1"
+
+ it "updates the highlighted line when the cursor position changes", ->
+ editor.setCursorBufferPosition([1,0])
+ expect(editor.getCursorBufferPosition().row).toBe 1
+ expect(editor.find('.line-number.cursor-line-number').length).toBe 1
+ expect(editor.find('.line-number.cursor-line-number').text()).toBe "2"
+
+ describe "when there is wrapping", ->
+ beforeEach ->
+ editor.attachToDom(30)
+ editor.setSoftWrap(true)
+ setEditorWidthInChars(editor, 20)
+
+ it "highlights the line where the initial cursor position is", ->
+ expect(editor.getCursorBufferPosition().row).toBe 0
+ expect(editor.find('.line-number.cursor-line-number.cursor-line-number-background').length).toBe 1
+ expect(editor.find('.line-number.cursor-line-number.cursor-line-number-background').text()).toBe "1"
+
+ it "updates the highlighted line when the cursor position changes", ->
+ editor.setCursorBufferPosition([1,0])
+ expect(editor.getCursorBufferPosition().row).toBe 1
+ expect(editor.find('.line-number.cursor-line-number.cursor-line-number-background').length).toBe 1
+ expect(editor.find('.line-number.cursor-line-number.cursor-line-number-background').text()).toBe "2"
+
+ describe "when the selection spans multiple lines", ->
+ beforeEach ->
+ editor.attachToDom(30)
+
+ it "doesn't highlight the backround", ->
+ editor.getSelection().setBufferRange(new Range([0,0],[2,0]))
+ expect(editor.getSelection().isSingleScreenLine()).toBe false
+ expect(editor.find('.line-number.cursor-line-number').length).toBe 1
+ expect(editor.find('.line-number.cursor-line-number.cursor-line-number-background').length).toBe 0
+ expect(editor.find('.line-number.cursor-line-number').text()).toBe "3"
+
+ it "when a newline is deleted with backspace, the line number of the new cursor position is highlighted", ->
+ editor.setCursorScreenPosition([1,0])
+ editor.backspace()
expect(editor.find('.line-number.cursor-line-number').length).toBe 1
expect(editor.find('.line-number.cursor-line-number').text()).toBe "1"
+ expect(editor.find('.line-number.cursor-line-number-background').length).toBe 1
+ expect(editor.find('.line-number.cursor-line-number-background').text()).toBe "1"
- it "updates the highlighted line when the cursor position changes", ->
- editor.setCursorBufferPosition([1,0])
- { row, column } = editor.getCursorBufferPosition()
- expect(row).toBe 1
- expect(editor.find('.line-number.cursor-line-number').length).toBe 1
- expect(editor.find('.line-number.cursor-line-number').text()).toBe "2"
+ describe "line highlighting", ->
+ beforeEach ->
+ editor.attachToDom(30)
+
+ describe "when there is no wrapping", ->
+ it "highlights the line where the initial cursor position is", ->
+ expect(editor.getCursorBufferPosition().row).toBe 0
+ expect(editor.find('.line.cursor-line').length).toBe 1
+ expect(editor.find('.line.cursor-line').text()).toBe buffer.lineForRow(0)
+
+ it "updates the highlighted line when the cursor position changes", ->
+ editor.setCursorBufferPosition([1,0])
+ expect(editor.getCursorBufferPosition().row).toBe 1
+ expect(editor.find('.line.cursor-line').length).toBe 1
+ expect(editor.find('.line.cursor-line').text()).toBe buffer.lineForRow(1)
+
+ it "when a newline is deleted with backspace, the line of the new cursor position is highlighted", ->
+ editor.setCursorScreenPosition([1,0])
+ editor.backspace()
+ expect(editor.find('.line.cursor-line').length).toBe 1
+
+ describe "when there is wrapping", ->
+ beforeEach ->
+ editor.setSoftWrap(true)
+ setEditorWidthInChars(editor, 20)
+
+ it "highlights the line where the initial cursor position is", ->
+ expect(editor.getCursorBufferPosition().row).toBe 0
+ expect(editor.find('.line.cursor-line').length).toBe 1
+ expect(editor.find('.line.cursor-line').text()).toBe 'var quicksort = '
+
+ it "updates the highlighted line when the cursor position changes", ->
+ editor.setCursorBufferPosition([1,0])
+ expect(editor.getCursorBufferPosition().row).toBe 1
+ expect(editor.find('.line.cursor-line').length).toBe 1
+ expect(editor.find('.line.cursor-line').text()).toBe ' var sort = '
+
+ describe "when there is a selection", ->
+ it "highlights if the selection is contained to one line", ->
+ editor.getSelection().setBufferRange(new Range([0,0],[0,1]))
+ expect(editor.getSelection().isSingleScreenLine()).toBe true
+ expect(editor.find('.line.cursor-line').length).toBe 1
+ expect(editor.find('.line.cursor-line').text()).toBe buffer.lineForRow(0)
+
+ it "doesn't highlight if the selection spans multiple lines", ->
+ editor.getSelection().setBufferRange(new Range([0,0],[2,0]))
+ expect(editor.getSelection().isSingleScreenLine()).toBe false
+ expect(editor.find('.line.cursor-line').length).toBe 0
describe "folding", ->
beforeEach ->
@@ -1612,7 +1707,7 @@ describe "Editor", ->
describe "when a fold-selection event is triggered", ->
it "folds the lines covered by the selection into a single line with a fold class", ->
editor.getSelection().setBufferRange(new Range([4, 29], [7, 4]))
- editor.trigger 'fold-selection'
+ editor.trigger 'editor:fold-selection'
expect(editor.renderedLines.find('.line:eq(4)')).toHaveClass('fold')
expect(editor.renderedLines.find('.line:eq(5)').text()).toBe '8'
@@ -1623,7 +1718,7 @@ describe "Editor", ->
describe "when a fold placeholder line is clicked", ->
it "removes the associated fold and places the cursor at its beginning", ->
editor.setCursorBufferPosition([3,0])
- editor.trigger 'fold-current-row'
+ editor.trigger 'editor:fold-current-row'
editor.find('.fold.line').mousedown()
@@ -1636,10 +1731,10 @@ describe "Editor", ->
describe "when the unfold-current-row event is triggered when the cursor is on a fold placeholder line", ->
it "removes the associated fold and places the cursor at its beginning", ->
editor.setCursorBufferPosition([3,0])
- editor.trigger 'fold-current-row'
+ editor.trigger 'editor:fold-current-row'
editor.setCursorBufferPosition([3,0])
- editor.trigger 'unfold-current-row'
+ editor.trigger 'editor:unfold-current-row'
expect(editor.find('.fold')).not.toExist()
expect(editor.renderedLines.find('.line:eq(4)').text()).toMatch /4-+/
diff --git a/spec/app/root-view-spec.coffee b/spec/app/root-view-spec.coffee
index 72c239418..8e1c2ca81 100644
--- a/spec/app/root-view-spec.coffee
+++ b/spec/app/root-view-spec.coffee
@@ -542,13 +542,13 @@ describe "RootView", ->
describe "font size adjustment", ->
it "increases/decreases font size when increase/decrease-font-size events are triggered", ->
fontSizeBefore = rootView.getFontSize()
- rootView.trigger 'increase-font-size'
+ rootView.trigger 'root-view:increase-font-size'
expect(rootView.getFontSize()).toBe fontSizeBefore + 1
- rootView.trigger 'increase-font-size'
+ rootView.trigger 'root-view:increase-font-size'
expect(rootView.getFontSize()).toBe fontSizeBefore + 2
- rootView.trigger 'decrease-font-size'
+ rootView.trigger 'root-view:decrease-font-size'
expect(rootView.getFontSize()).toBe fontSizeBefore + 1
- rootView.trigger 'decrease-font-size'
+ rootView.trigger 'root-view:decrease-font-size'
expect(rootView.getFontSize()).toBe fontSizeBefore
it "does not allow the font size to be less than 1", ->
diff --git a/spec/extensions/autocomplete-spec.coffee b/spec/extensions/autocomplete-spec.coffee
index 442390f47..d9adea327 100644
--- a/spec/extensions/autocomplete-spec.coffee
+++ b/spec/extensions/autocomplete-spec.coffee
@@ -64,28 +64,28 @@ describe "Autocomplete", ->
expect(autocomplete.matchesList.find('li:eq(1)')).toHaveText('shift')
it 'autocompletes word when there is only a suffix', ->
- editor.getBuffer().insert([10,0] ,"extra:e:extra")
+ editor.getBuffer().insert([10,0] ,"extra:n:extra")
editor.setCursorBufferPosition([10,6])
autocomplete.attach()
- expect(editor.lineForBufferRow(10)).toBe "extra:while:extra"
- expect(editor.getCursorBufferPosition()).toEqual [10,10]
- expect(editor.getSelection().getBufferRange()).toEqual [[10,6], [10,10]]
+ expect(editor.lineForBufferRow(10)).toBe "extra:function:extra"
+ expect(editor.getCursorBufferPosition()).toEqual [10,13]
+ expect(editor.getSelection().getBufferRange()).toEqual [[10,6], [10,13]]
- expect(autocomplete.matchesList.find('li').length).toBe 1
- expect(autocomplete.matchesList.find('li:eq(0)')).toHaveText('while')
+ expect(autocomplete.matchesList.find('li').length).toBe 2
+ expect(autocomplete.matchesList.find('li:eq(0)')).toHaveText('function')
+ expect(autocomplete.matchesList.find('li:eq(1)')).toHaveText('return')
- it 'autocompletes word when there is a prefix and suffix', ->
+ it 'autocompletes word when there is a single prefix and suffix match', ->
editor.getBuffer().insert([8,43] ,"q")
editor.setCursorBufferPosition([8,44])
autocomplete.attach()
expect(editor.lineForBufferRow(8)).toBe " return sort(left).concat(pivot).concat(quicksort(right));"
- expect(editor.getCursorBufferPosition()).toEqual [8,48]
- expect(editor.getSelection().getBufferRange()).toEqual [[8,44], [8,48]]
+ expect(editor.getCursorBufferPosition()).toEqual [8,52]
+ expect(editor.getSelection().getBufferRange().isEmpty()).toBeTruthy()
- expect(autocomplete.matchesList.find('li').length).toBe 1
- expect(autocomplete.matchesList.find('li:eq(0)')).toHaveText('quicksort')
+ expect(autocomplete.matchesList.find('li').length).toBe 0
it "show's that there are no matches found when there is no prefix or suffix", ->
editor.setCursorBufferPosition([10, 0])
@@ -102,10 +102,9 @@ describe "Autocomplete", ->
expect(editor.lineForBufferRow(10)).toBe "extra:shift:extra"
expect(editor.getCursorBufferPosition()).toEqual [10,11]
- expect(editor.getSelection().getBufferRange()).toEqual [[10,7],[10,11]]
+ expect(editor.getSelection().getBufferRange().isEmpty()).toBeTruthy()
- expect(autocomplete.matchesList.find('li').length).toBe 1
- expect(autocomplete.matchesList.find('li:eq(0)')).toHaveText('shift')
+ expect(autocomplete.matchesList.find('li').length).toBe 0
it 'autocompletes word when there is only a suffix', ->
editor.getBuffer().insert([10,0] ,"extra:current:extra")
@@ -124,23 +123,34 @@ describe "Autocomplete", ->
autocomplete.attach()
expect(editor.lineForBufferRow(5)).toBe " concat = items.shift();"
- expect(editor.getCursorBufferPosition()).toEqual [5,11]
- expect(editor.getSelection().getBufferRange()).toEqual [[5,7], [5,11]]
+ expect(editor.getCursorBufferPosition()).toEqual [5,12]
+ expect(editor.getSelection().getBufferRange().isEmpty()).toBeTruthy()
- expect(autocomplete.matchesList.find('li').length).toBe 1
- expect(autocomplete.matchesList.find('li:eq(0)')).toHaveText('concat')
+ expect(autocomplete.matchesList.find('li').length).toBe 0
+
+ it 'replaces selection with selected match, moves the cursor to the end of the match, and removes the autocomplete menu', ->
+ editor.getBuffer().insert([10,0] ,"extra:sort:extra")
+ editor.setSelectedBufferRange [[10,7], [10,9]]
+ autocomplete.attach()
+
+ expect(editor.lineForBufferRow(10)).toBe "extra:shift:extra"
+ expect(editor.getCursorBufferPosition()).toEqual [10,11]
+ expect(editor.getSelection().isEmpty()).toBeTruthy()
+ expect(editor.find('.autocomplete')).not.toExist()
describe 'autocomplete:confirm event', ->
- it 'replaces selection with selected match, moves the cursor to the end of the match, and removes the autocomplete menu', ->
- editor.getBuffer().insert([10,0] ,"extra:sort:extra")
- editor.setSelectedBufferRange [[10,7], [10,9]]
- autocomplete.attach()
- miniEditor.trigger "autocomplete:confirm"
+ describe "where there are matches", ->
+ describe "where there is no selection", ->
+ it "closes the menu and moves the cursor to the end", ->
+ editor.getBuffer().insert([10,0] ,"extra:sh:extra")
+ editor.setCursorBufferPosition([10,8])
+ autocomplete.attach()
+ miniEditor.trigger "autocomplete:confirm"
- expect(editor.lineForBufferRow(10)).toBe "extra:shift:extra"
- expect(editor.getCursorBufferPosition()).toEqual [10,11]
- expect(editor.getSelection().isEmpty()).toBeTruthy()
- expect(editor.find('.autocomplete')).not.toExist()
+ expect(editor.lineForBufferRow(10)).toBe "extra:shift:extra"
+ expect(editor.getCursorBufferPosition()).toEqual [10,11]
+ expect(editor.getSelection().isEmpty()).toBeTruthy()
+ expect(editor.find('.autocomplete')).not.toExist()
describe "when there are no matches", ->
it "closes the menu without changing the buffer", ->
@@ -190,13 +200,13 @@ describe "Autocomplete", ->
editor.setCursorBufferPosition([10,6])
autocomplete.attach()
- miniEditor.trigger "move-up"
+ miniEditor.trigger "core:move-up"
expect(editor.lineForBufferRow(10)).toBe "extra:concat:extra"
expect(autocomplete.find('li:eq(0)')).not.toHaveClass('selected')
expect(autocomplete.find('li:eq(1)')).not.toHaveClass('selected')
expect(autocomplete.find('li:eq(7)')).toHaveClass('selected')
- miniEditor.trigger "move-up"
+ miniEditor.trigger "core:move-up"
expect(editor.lineForBufferRow(10)).toBe "extra:right:extra"
expect(autocomplete.find('li:eq(0)')).not.toHaveClass('selected')
expect(autocomplete.find('li:eq(7)')).not.toHaveClass('selected')
@@ -213,10 +223,10 @@ describe "Autocomplete", ->
expect(matchesList.height()).toBeLessThan matchesList[0].scrollHeight
matchCount = matchesList.find('li').length
- miniEditor.trigger 'move-up'
+ miniEditor.trigger 'core:move-up'
expect(matchesList.scrollBottom()).toBe matchesList[0].scrollHeight
- miniEditor.trigger 'move-up' for i in [1...matchCount]
+ miniEditor.trigger 'core:move-up' for i in [1...matchCount]
expect(matchesList.scrollTop()).toBe 0
describe 'move-down event', ->
@@ -225,12 +235,12 @@ describe "Autocomplete", ->
editor.setCursorBufferPosition([10,7])
autocomplete.attach()
- miniEditor.trigger "move-down"
+ miniEditor.trigger "core:move-down"
expect(editor.lineForBufferRow(10)).toBe "extra:shift:extra"
expect(autocomplete.find('li:eq(0)')).not.toHaveClass('selected')
expect(autocomplete.find('li:eq(1)')).toHaveClass('selected')
- miniEditor.trigger "move-down"
+ miniEditor.trigger "core:move-down"
expect(editor.lineForBufferRow(10)).toBe "extra:sort:extra"
expect(autocomplete.find('li:eq(0)')).toHaveClass('selected')
expect(autocomplete.find('li:eq(1)')).not.toHaveClass('selected')
@@ -246,10 +256,10 @@ describe "Autocomplete", ->
expect(matchesList.height()).toBeLessThan matchesList[0].scrollHeight
matchCount = matchesList.find('li').length
- miniEditor.trigger 'move-down' for i in [1...matchCount]
+ miniEditor.trigger 'core:move-down' for i in [1...matchCount]
expect(matchesList.scrollBottom()).toBe matchesList[0].scrollHeight
- miniEditor.trigger 'move-down'
+ miniEditor.trigger 'core:move-down'
expect(matchesList.scrollTop()).toBe 0
describe "when a match is clicked in the match list", ->
@@ -421,10 +431,10 @@ describe "Autocomplete", ->
autocomplete.detach()
expect(miniEditor.getText()).toBe ''
- editor.trigger 'move-down'
+ editor.trigger 'core:move-down'
expect(editor.getCursorBufferPosition().row).toBe 1
- editor.trigger 'move-up'
+ editor.trigger 'core:move-up'
expect(editor.getCursorBufferPosition().row).toBe 0
diff --git a/spec/extensions/command-panel-spec.coffee b/spec/extensions/command-panel-spec.coffee
index ac2440e7a..f5a953fb6 100644
--- a/spec/extensions/command-panel-spec.coffee
+++ b/spec/extensions/command-panel-spec.coffee
@@ -320,15 +320,15 @@ describe "CommandPanel", ->
rootView.trigger 'command-panel:toggle'
- commandPanel.miniEditor.trigger 'move-up'
+ commandPanel.miniEditor.trigger 'core:move-up'
expect(commandPanel.miniEditor.getText()).toBe 's/twinkies/wheatgrass/g'
- commandPanel.miniEditor.trigger 'move-up'
+ commandPanel.miniEditor.trigger 'core:move-up'
expect(commandPanel.miniEditor.getText()).toBe 's/war/peace/g'
- commandPanel.miniEditor.trigger 'move-up'
+ commandPanel.miniEditor.trigger 'core:move-up'
expect(commandPanel.miniEditor.getText()).toBe 's/war/peace/g'
- commandPanel.miniEditor.trigger 'move-down'
+ commandPanel.miniEditor.trigger 'core:move-down'
expect(commandPanel.miniEditor.getText()).toBe 's/twinkies/wheatgrass/g'
- commandPanel.miniEditor.trigger 'move-down'
+ commandPanel.miniEditor.trigger 'core:move-down'
expect(commandPanel.miniEditor.getText()).toBe ''
describe "when the preview list is focused with search operations", ->
@@ -345,30 +345,30 @@ describe "CommandPanel", ->
expect(previewList.find('li:eq(0)')).toHaveClass 'selected'
expect(previewList.getSelectedOperation()).toBe previewList.getOperations()[0]
- previewList.trigger 'move-up'
+ previewList.trigger 'core:move-up'
expect(previewList.find('li:eq(0)')).toHaveClass 'selected'
expect(previewList.getSelectedOperation()).toBe previewList.getOperations()[0]
- previewList.trigger 'move-down'
+ previewList.trigger 'core:move-down'
expect(previewList.find('li:eq(1)')).toHaveClass 'selected'
expect(previewList.getSelectedOperation()).toBe previewList.getOperations()[1]
- previewList.trigger 'move-down'
+ previewList.trigger 'core:move-down'
expect(previewList.find('li:eq(2)')).toHaveClass 'selected'
expect(previewList.getSelectedOperation()).toBe previewList.getOperations()[2]
- previewList.trigger 'move-up'
+ previewList.trigger 'core:move-up'
expect(previewList.find('li:eq(1)')).toHaveClass 'selected'
expect(previewList.getSelectedOperation()).toBe previewList.getOperations()[1]
- _.times previewList.getOperations().length, -> previewList.trigger 'move-down'
+ _.times previewList.getOperations().length, -> previewList.trigger 'core:move-down'
expect(previewList.find('li:last')).toHaveClass 'selected'
expect(previewList.getSelectedOperation()).toBe _.last(previewList.getOperations())
expect(previewList.scrollBottom()).toBeCloseTo previewList.prop('scrollHeight'), -1
- _.times previewList.getOperations().length, -> previewList.trigger 'move-up'
+ _.times previewList.getOperations().length, -> previewList.trigger 'core:move-up'
describe "when command-panel:execute is triggered on the preview list", ->
it "opens the operation's buffer, selects the search result, and focuses the active editor", ->
@@ -376,7 +376,7 @@ describe "CommandPanel", ->
executeHandler = jasmine.createSpy('executeHandler')
commandPanel.on 'command-panel:execute', executeHandler
- _.times 4, -> previewList.trigger 'move-down'
+ _.times 4, -> previewList.trigger 'core:move-down'
operation = previewList.getSelectedOperation()
previewList.trigger 'command-panel:execute'
diff --git a/spec/extensions/fuzzy-finder-spec.coffee b/spec/extensions/fuzzy-finder-spec.coffee
index d5b2d6552..8eaf86496 100644
--- a/spec/extensions/fuzzy-finder-spec.coffee
+++ b/spec/extensions/fuzzy-finder-spec.coffee
@@ -21,7 +21,7 @@ describe 'FuzzyFinder', ->
it "shows the FuzzyFinder or hides it and returns focus to the active editor if it already showing", ->
rootView.attachToDom()
expect(rootView.find('.fuzzy-finder')).not.toExist()
- rootView.find('.editor').trigger 'split-right'
+ rootView.find('.editor').trigger 'editor:split-right'
[editor1, editor2] = rootView.find('.editor').map -> $(this).view()
rootView.trigger 'fuzzy-finder:toggle-file-finder'
@@ -67,7 +67,7 @@ describe 'FuzzyFinder', ->
describe "when a path is highlighted", ->
it "opens the file associated with that path in the editor", ->
- finder.trigger 'move-down'
+ finder.trigger 'core:move-down'
selectedLi = finder.find('li:eq(1)')
expectedPath = rootView.project.resolve(selectedLi.text())
@@ -96,7 +96,7 @@ describe 'FuzzyFinder', ->
it "shows the FuzzyFinder or hides it and returns focus to the active editor if it already showing", ->
rootView.attachToDom()
expect(rootView.find('.fuzzy-finder')).not.toExist()
- rootView.find('.editor').trigger 'split-right'
+ rootView.find('.editor').trigger 'editor:split-right'
[editor1, editor2] = rootView.find('.editor').map -> $(this).view()
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
@@ -211,28 +211,27 @@ describe 'FuzzyFinder', ->
expect(finder.find('li:eq(0)')).toHaveClass "selected"
expect(finder.find('li:eq(2)')).not.toHaveClass "selected"
- finder.miniEditor.trigger keydownEvent('down')
- finder.miniEditor.trigger keydownEvent('down')
+ finder.miniEditor.trigger 'core:move-down'
+ finder.miniEditor.trigger 'core:move-down'
expect(finder.find('li:eq(0)')).not.toHaveClass "selected"
expect(finder.find('li:eq(2)')).toHaveClass "selected"
- finder.miniEditor.trigger keydownEvent('up')
+ finder.miniEditor.trigger 'core:move-up'
expect(finder.find('li:eq(0)')).not.toHaveClass "selected"
expect(finder.find('li:eq(1)')).toHaveClass "selected"
expect(finder.find('li:eq(2)')).not.toHaveClass "selected"
- it "does not fall off the end or begining of the list", ->
- expect(finder.find('li:first')).toHaveClass "selected"
- finder.miniEditor.trigger keydownEvent('up')
+ it "wraps around when at the end or begining of the list", ->
expect(finder.find('li:first')).toHaveClass "selected"
- for i in [1..finder.pathList.children().length+2]
- finder.miniEditor.trigger keydownEvent('down')
-
+ finder.miniEditor.trigger 'core:move-up'
expect(finder.find('li:last')).toHaveClass "selected"
+ finder.miniEditor.trigger 'core:move-down'
+ expect(finder.find('li:first')).toHaveClass "selected"
+
describe "when the fuzzy finder loses focus", ->
it "detaches itself", ->
rootView.attachToDom()
diff --git a/spec/extensions/tree-view-spec.coffee b/spec/extensions/tree-view-spec.coffee
index 9dc278fb4..2914f2f02 100644
--- a/spec/extensions/tree-view-spec.coffee
+++ b/spec/extensions/tree-view-spec.coffee
@@ -10,14 +10,14 @@ describe "TreeView", ->
[rootView, project, treeView, sampleJs, sampleTxt] = []
beforeEach ->
- rootView = new RootView(require.resolve('fixtures/'))
+ rootView = new RootView(require.resolve('fixtures/tree-view'))
project = rootView.project
rootView.activateExtension(TreeView)
treeView = rootView.find(".tree-view").view()
treeView.root = treeView.find('> li:first').view()
- sampleJs = treeView.find('.file:contains(sample.js)')
- sampleTxt = treeView.find('.file:contains(sample.txt)')
+ sampleJs = treeView.find('.file:contains(tree-view.js)')
+ sampleTxt = treeView.find('.file:contains(tree-view.txt)')
expect(treeView.root.directory.subscriptionCount()).toBeGreaterThan 0
@@ -27,21 +27,21 @@ describe "TreeView", ->
describe ".initialize(project)", ->
it "renders the root of the project and its contents alphabetically with subdirectories first in a collapsed state", ->
expect(treeView.root.find('> .header .disclosure-arrow')).toHaveText('▾')
- expect(treeView.root.find('> .header .name')).toHaveText('fixtures/')
+ expect(treeView.root.find('> .header .name')).toHaveText('tree-view/')
rootEntries = treeView.root.find('.entries')
subdir0 = rootEntries.find('> li:eq(0)')
expect(subdir0.find('.disclosure-arrow')).toHaveText('▸')
- expect(subdir0.find('.name')).toHaveText('dir/')
+ expect(subdir0.find('.name')).toHaveText('dir1/')
expect(subdir0.find('.entries')).not.toExist()
- subdir2 = rootEntries.find('> li:eq(3)')
+ subdir2 = rootEntries.find('> li:eq(1)')
expect(subdir2.find('.disclosure-arrow')).toHaveText('▸')
- expect(subdir2.find('.name')).toHaveText('zed/')
+ expect(subdir2.find('.name')).toHaveText('dir2/')
expect(subdir2.find('.entries')).not.toExist()
- expect(rootEntries.find('> .file:contains(sample.js)')).toExist()
- expect(rootEntries.find('> .file:contains(sample.txt)')).toExist()
+ expect(rootEntries.find('> .file:contains(tree-view.js)')).toExist()
+ expect(rootEntries.find('> .file:contains(tree-view.txt)')).toExist()
it "selects the rootview", ->
expect(treeView.selectedEntry()).toEqual treeView.root
@@ -84,7 +84,7 @@ describe "TreeView", ->
newRootView?.deactivate()
it "restores expanded directories and selected file when deserialized", ->
- treeView.find('.directory:contains(zed)').click()
+ treeView.find('.directory:contains(dir1)').click()
sampleJs.click()
newRootView = RootView.deserialize(rootView.serialize())
rootView.deactivate() # Deactivates previous TreeView
@@ -94,8 +94,8 @@ describe "TreeView", ->
newTreeView = newRootView.find(".tree-view").view()
expect(newTreeView).toExist()
- expect(newTreeView.selectedEntry()).toMatchSelector(".file:contains(sample.js)")
- expect(newTreeView.find(".directory:contains(zed)")).toHaveClass("expanded")
+ expect(newTreeView.selectedEntry()).toMatchSelector(".file:contains(tree-view.js)")
+ expect(newTreeView.find(".directory:contains(dir1)")).toHaveClass("expanded")
it "restores the focus state of the tree view", ->
rootView.attachToDom()
@@ -112,7 +112,7 @@ describe "TreeView", ->
expect(newTreeView).toMatchSelector ':focus'
it "restores the scroll top when toggled", ->
- rootView.height(100)
+ rootView.height(5)
rootView.attachToDom()
expect(treeView).toBeVisible()
treeView.focus()
@@ -162,11 +162,11 @@ describe "TreeView", ->
describe "if the current file has a path", ->
it "shows and focuses the tree view and selects the file", ->
- rootView.open('dir/a')
+ rootView.open('dir1/file1')
rootView.trigger 'tree-view:reveal-active-file'
expect(treeView.hasParent()).toBeTruthy()
expect(treeView.focus).toHaveBeenCalled()
- expect(treeView.selectedEntry().getPath()).toMatch /dir\/a$/
+ expect(treeView.selectedEntry().getPath()).toMatch /dir1\/file1$/
describe "if the current file has no path", ->
it "shows and focuses the tree view, but does not attempt to select a specific file", ->
@@ -196,7 +196,7 @@ describe "TreeView", ->
describe "when a directory's disclosure arrow is clicked", ->
it "expands / collapses the associated directory", ->
- subdir = treeView.root.find('.entries > li:contains(dir/)').view()
+ subdir = treeView.root.find('.entries > li:contains(dir1/)').view()
expect(subdir.disclosureArrow).toHaveText('▸')
expect(subdir.find('.entries')).not.toExist()
@@ -211,10 +211,10 @@ describe "TreeView", ->
expect(subdir.find('.entries')).not.toExist()
it "restores the expansion state of descendant directories", ->
- child = treeView.root.find('.entries > li:contains(dir/)').view()
+ child = treeView.root.find('.entries > li:contains(dir1/)').view()
child.disclosureArrow.click()
- grandchild = child.find('.entries > li:contains(a-dir/)').view()
+ grandchild = child.find('.entries > li:contains(sub-dir1/)').view()
grandchild.disclosureArrow.click()
treeView.root.disclosureArrow.click()
@@ -222,16 +222,16 @@ describe "TreeView", ->
treeView.root.disclosureArrow.click()
# previously expanded descendants remain expanded
- expect(treeView.root.find('> .entries > li:contains(dir/) > .entries > li:contains(a-dir/) > .entries').length).toBe 1
+ expect(treeView.root.find('> .entries > li:contains(dir1/) > .entries > li:contains(sub-dir1/) > .entries').length).toBe 1
# collapsed descendants remain collapsed
- expect(treeView.root.find('> .entries > li.contains(zed/) > .entries')).not.toExist()
+ expect(treeView.root.find('> .entries > li.contains(dir2/) > .entries')).not.toExist()
it "when collapsing a directory, removes change subscriptions from the collapsed directory and its descendants", ->
- child = treeView.root.entries.find('li:contains(dir/)').view()
+ child = treeView.root.entries.find('li:contains(dir1/)').view()
child.disclosureArrow.click()
- grandchild = child.entries.find('li:contains(a-dir/)').view()
+ grandchild = child.entries.find('li:contains(sub-dir1/)').view()
grandchild.disclosureArrow.click()
expect(treeView.root.directory.subscriptionCount()).toBe 1
@@ -250,20 +250,20 @@ describe "TreeView", ->
sampleJs.trigger clickEvent(originalEvent: { detail: 1 })
expect(sampleJs).toHaveClass 'selected'
- expect(rootView.getActiveEditor().getPath()).toBe require.resolve('fixtures/sample.js')
+ expect(rootView.getActiveEditor().getPath()).toBe require.resolve('fixtures/tree-view/tree-view.js')
expect(rootView.getActiveEditor().isFocused).toBeFalsy()
sampleTxt.trigger clickEvent(originalEvent: { detail: 1 })
expect(sampleTxt).toHaveClass 'selected'
expect(treeView.find('.selected').length).toBe 1
- expect(rootView.getActiveEditor().getPath()).toBe require.resolve('fixtures/sample.txt')
+ expect(rootView.getActiveEditor().getPath()).toBe require.resolve('fixtures/tree-view/tree-view.txt')
expect(rootView.getActiveEditor().isFocused).toBeFalsy()
describe "when a file is double-clicked", ->
it "selects the file and opens it in the active editor on the first click, then changes focus to the active editor on the second", ->
sampleJs.trigger clickEvent(originalEvent: { detail: 1 })
expect(sampleJs).toHaveClass 'selected'
- expect(rootView.getActiveEditor().getPath()).toBe require.resolve('fixtures/sample.js')
+ expect(rootView.getActiveEditor().getPath()).toBe require.resolve('fixtures/tree-view/tree-view.js')
expect(rootView.getActiveEditor().isFocused).toBeFalsy()
sampleJs.trigger clickEvent(originalEvent: { detail: 2 })
@@ -288,15 +288,15 @@ describe "TreeView", ->
describe "when a new file is opened in the active editor", ->
it "is selected in the tree view if the file's entry visible", ->
sampleJs.click()
- rootView.open(require.resolve('fixtures/sample.txt'))
+ rootView.open(require.resolve('fixtures/tree-view/tree-view.txt'))
expect(sampleTxt).toHaveClass 'selected'
expect(treeView.find('.selected').length).toBe 1
it "selected a file's parent dir if the file's entry is not visible", ->
- rootView.open(require.resolve('fixtures/dir/a-dir/oh-git'))
+ rootView.open(require.resolve('fixtures/tree-view/dir1/sub-dir1/sub-file1'))
- dirView = treeView.root.find('.directory:contains(dir)').view()
+ dirView = treeView.root.find('.directory:contains(dir1)').view()
expect(dirView).toHaveClass 'selected'
describe "when a different editor becomes active", ->
@@ -314,11 +314,11 @@ describe "TreeView", ->
afterEach ->
expect(treeView.find('.selected').length).toBeLessThan 2
- describe "move-down", ->
+ describe "core:move-down", ->
describe "when a collapsed directory is selected", ->
it "skips to the next directory", ->
treeView.root.find('.directory:eq(0)').click()
- treeView.trigger 'move-down'
+ treeView.trigger 'core:move-down'
expect(treeView.root.find('.directory:eq(1)')).toHaveClass 'selected'
describe "when an expanded directory is selected", ->
@@ -327,7 +327,7 @@ describe "TreeView", ->
subdir.expand()
subdir.click()
- treeView.trigger 'move-down'
+ treeView.trigger 'core:move-down'
expect(subdir.entries.find('.entry:first')).toHaveClass 'selected'
@@ -337,7 +337,7 @@ describe "TreeView", ->
subdir1.expand()
subdir1.entries.find('.entry:last').click()
- treeView.trigger 'move-down'
+ treeView.trigger 'core:move-down'
expect(treeView.root.find('.entries > .entry:eq(2)')).toHaveClass 'selected'
@@ -346,21 +346,21 @@ describe "TreeView", ->
beforeEach ->
nested = treeView.root.find('.directory:eq(2)').view()
- expect(nested.find('.header').text()).toContain 'nested'
+ expect(nested.find('.header').text()).toContain 'nested/'
nested.expand()
nested2 = nested.entries.find('.entry:last').view()
nested2.click()
describe "when the directory is collapsed", ->
it "selects the entry after its grandparent directory", ->
- treeView.trigger 'move-down'
+ treeView.trigger 'core:move-down'
expect(nested.next()).toHaveClass 'selected'
describe "when the directory is expanded", ->
it "selects the entry after its grandparent directory", ->
nested2.expand()
nested2.find('.file').remove() # kill the .gitkeep file, which has to be there but screws the test
- treeView.trigger 'move-down'
+ treeView.trigger 'core:move-down'
expect(nested.next()).toHaveClass 'selected'
describe "when the last entry of the last directory is selected", ->
@@ -368,11 +368,11 @@ describe "TreeView", ->
lastEntry = treeView.root.find('> .entries .entry:last')
lastEntry.click()
- treeView.trigger 'move-down'
+ treeView.trigger 'core:move-down'
expect(lastEntry).toHaveClass 'selected'
- describe "move-up", ->
+ describe "core:move-up", ->
describe "when there is an expanded directory before the currently selected entry", ->
it "selects the last entry in the expanded directory", ->
lastDir = treeView.root.find('.directory:last').view()
@@ -380,7 +380,7 @@ describe "TreeView", ->
lastDir.expand()
fileAfterDir.click()
- treeView.trigger 'move-up'
+ treeView.trigger 'core:move-up'
expect(lastDir.find('.entry:last')).toHaveClass 'selected'
describe "when there is an entry before the currently selected entry", ->
@@ -388,7 +388,7 @@ describe "TreeView", ->
lastEntry = treeView.root.find('.entry:last')
lastEntry.click()
- treeView.trigger 'move-up'
+ treeView.trigger 'core:move-up'
expect(lastEntry.prev()).toHaveClass 'selected'
@@ -398,16 +398,69 @@ describe "TreeView", ->
subdir.expand()
subdir.find('> .entries > .entry:first').click()
- treeView.trigger 'move-up'
+ treeView.trigger 'core:move-up'
expect(subdir).toHaveClass 'selected'
describe "when there is no parent directory or previous entry", ->
it "does not change the selection", ->
treeView.root.click()
- treeView.trigger 'move-up'
+ treeView.trigger 'core:move-up'
expect(treeView.root).toHaveClass 'selected'
+ describe "core:move-to-top", ->
+ it "scrolls to the top", ->
+ treeView.height(100)
+ treeView.attachToDom()
+ $(element).view().expand() for element in treeView.find('.directory')
+ expect(treeView.prop('scrollHeight')).toBeGreaterThan treeView.outerHeight()
+
+ expect(treeView.scrollTop()).toBe 0
+
+ entryCount = treeView.find(".entry").length
+ _.times entryCount, -> treeView.moveDown()
+ expect(treeView.scrollTop()).toBeGreaterThan 0
+
+ treeView.trigger 'core:move-to-top'
+ expect(treeView.scrollTop()).toBe 0
+
+ describe "core:move-to-bottom", ->
+ it "scrolls to the bottom", ->
+ treeView.height(100)
+ treeView.attachToDom()
+ $(element).view().expand() for element in treeView.find('.directory')
+ expect(treeView.prop('scrollHeight')).toBeGreaterThan treeView.outerHeight()
+
+ expect(treeView.scrollTop()).toBe 0
+ treeView.trigger 'core:move-to-bottom'
+ expect(treeView.scrollBottom()).toBe treeView.prop('scrollHeight')
+
+ describe "core:page-up", ->
+ it "scrolls up a page", ->
+ treeView.height(5)
+ treeView.attachToDom()
+ $(element).view().expand() for element in treeView.find('.directory')
+ expect(treeView.prop('scrollHeight')).toBeGreaterThan treeView.outerHeight()
+
+ expect(treeView.scrollTop()).toBe 0
+ treeView.scrollToBottom()
+ scrollTop = treeView.scrollTop()
+ expect(scrollTop).toBeGreaterThan 0
+
+ treeView.trigger 'core:page-up'
+ expect(treeView.scrollTop()).toBe scrollTop - treeView.height()
+
+ describe "core:page-down", ->
+ it "scrolls down a page", ->
+ treeView.height(5)
+ treeView.attachToDom()
+ $(element).view().expand() for element in treeView.find('.directory')
+ expect(treeView.prop('scrollHeight')).toBeGreaterThan treeView.outerHeight()
+
+ expect(treeView.scrollTop()).toBe 0
+ treeView.trigger 'core:page-down'
+ expect(treeView.scrollTop()).toBe treeView.height()
+
describe "movement outside of viewable region", ->
it "scrolls the tree view to the selected item", ->
treeView.height(100)
@@ -485,9 +538,9 @@ describe "TreeView", ->
describe "tree-view:open-selected-entry", ->
describe "when a file is selected", ->
it "opens the file in the editor and focuses it", ->
- treeView.root.find('.file:contains(sample.js)').click()
+ treeView.root.find('.file:contains(tree-view.js)').click()
treeView.root.trigger 'tree-view:open-selected-entry'
- expect(rootView.getActiveEditor().getPath()).toBe require.resolve('fixtures/sample.js')
+ expect(rootView.getActiveEditor().getPath()).toBe require.resolve('fixtures/tree-view/tree-view.js')
expect(rootView.getActiveEditor().isFocused).toBeTruthy()
describe "when a directory is selected", ->
@@ -590,6 +643,7 @@ describe "TreeView", ->
describe "when the path with a trailing '/' is changed and confirmed", ->
describe "when no file or directory exists at the given path", ->
it "adds a directory and closes the dialog", ->
+ treeView.attachToDom()
newPath = fs.join(dirPath, "new/dir")
addDialog.miniEditor.insertText("new/dir/")
addDialog.trigger 'tree-view:confirm'
@@ -597,6 +651,22 @@ describe "TreeView", ->
expect(fs.isDirectory(newPath)).toBeTruthy()
expect(addDialog.parent()).not.toExist()
expect(rootView.getActiveEditor().getPath()).not.toBe newPath
+ expect(treeView).toMatchSelector(':focus')
+ expect(rootView.getActiveEditor().isFocused).toBeFalsy()
+ expect(dirView.find('.directory.selected:contains(new/)').length).toBe(1)
+
+ it "selects the created directory", ->
+ treeView.attachToDom()
+ newPath = fs.join(dirPath, "new2/")
+ addDialog.miniEditor.insertText("new2/")
+ addDialog.trigger 'tree-view:confirm'
+ expect(fs.exists(newPath)).toBeTruthy()
+ expect(fs.isDirectory(newPath)).toBeTruthy()
+ expect(addDialog.parent()).not.toExist()
+ expect(rootView.getActiveEditor().getPath()).not.toBe newPath
+ expect(treeView).toMatchSelector(':focus')
+ expect(rootView.getActiveEditor().isFocused).toBeFalsy()
+ expect(dirView.find('.directory.selected:contains(new2/)').length).toBe(1)
describe "when a file or directory already exists at the given path", ->
it "shows an error message and does not close the dialog", ->
@@ -736,7 +806,7 @@ describe "TreeView", ->
temporaryFilePath = null
beforeEach ->
- temporaryFilePath = fs.join(require.resolve('fixtures'), 'temporary')
+ temporaryFilePath = fs.join(require.resolve('fixtures/tree-view'), 'temporary')
if fs.exists(temporaryFilePath)
fs.remove(temporaryFilePath)
waits(20)
diff --git a/spec/extensions/wrap-guide-spec.coffee b/spec/extensions/wrap-guide-spec.coffee
index 0aef8f95e..0aa024cdb 100644
--- a/spec/extensions/wrap-guide-spec.coffee
+++ b/spec/extensions/wrap-guide-spec.coffee
@@ -32,7 +32,7 @@ describe "WrapGuide", ->
it "updates the wrap guide position", ->
initial = wrapGuide.position().left
expect(initial).toBeGreaterThan(0)
- rootView.trigger('increase-font-size')
+ rootView.trigger('root-view:increase-font-size')
expect(wrapGuide.position().left).toBeGreaterThan(initial)
describe "overriding getGuideColumn", ->
diff --git a/spec/fixtures/nested/nested2/.gitkeep b/spec/fixtures/tree-view/dir1/file1
similarity index 100%
rename from spec/fixtures/nested/nested2/.gitkeep
rename to spec/fixtures/tree-view/dir1/file1
diff --git a/spec/fixtures/tree-view/dir1/sub-dir1/sub-file1 b/spec/fixtures/tree-view/dir1/sub-dir1/sub-file1
new file mode 100644
index 000000000..e69de29bb
diff --git a/spec/fixtures/tree-view/dir2/file2 b/spec/fixtures/tree-view/dir2/file2
new file mode 100644
index 000000000..e69de29bb
diff --git a/spec/fixtures/tree-view/nested/nested2/.gitkeep b/spec/fixtures/tree-view/nested/nested2/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/spec/fixtures/tree-view/tree-view.js b/spec/fixtures/tree-view/tree-view.js
new file mode 100644
index 000000000..fb33b0b43
--- /dev/null
+++ b/spec/fixtures/tree-view/tree-view.js
@@ -0,0 +1,13 @@
+var quicksort = function () {
+ var sort = function(items) {
+ if (items.length <= 1) return items;
+ var pivot = items.shift(), current, left = [], right = [];
+ while(items.length > 0) {
+ current = items.shift();
+ current < pivot ? left.push(current) : right.push(current);
+ }
+ return sort(left).concat(pivot).concat(sort(right));
+ };
+
+ return sort(Array.apply(this, arguments));
+};
\ No newline at end of file
diff --git a/spec/fixtures/tree-view/tree-view.txt b/spec/fixtures/tree-view/tree-view.txt
new file mode 100644
index 000000000..3e715502b
--- /dev/null
+++ b/spec/fixtures/tree-view/tree-view.txt
@@ -0,0 +1 @@
+Some text.
diff --git a/spec/stdlib/fs-spec.coffee b/spec/stdlib/fs-spec.coffee
index 30bd57e25..afc17089c 100644
--- a/spec/stdlib/fs-spec.coffee
+++ b/spec/stdlib/fs-spec.coffee
@@ -35,7 +35,7 @@ describe "fs", ->
expect(fs.exists(null)).toBe false
describe ".join(paths...)", ->
- it "concatenates the given paths with the directory seperator", ->
+ it "concatenates the given paths with the directory separator", ->
expect(fs.join('a')).toBe 'a'
expect(fs.join('a', 'b', 'c')).toBe 'a/b/c'
expect(fs.join('/a/b/', 'c', 'd')).toBe '/a/b/c/d'
@@ -83,7 +83,7 @@ describe "fs", ->
expect(paths.length).toBeGreaterThan 0
for path in paths
- expect(path).not.toMatch /dir/
+ expect(path).not.toMatch /\/dir\//
describe ".lastModified(path)", ->
it "returns a Date object representing the time the file was last modified", ->
diff --git a/src/app/cursor-view.coffee b/src/app/cursor-view.coffee
index 326162536..69708acc9 100644
--- a/src/app/cursor-view.coffee
+++ b/src/app/cursor-view.coffee
@@ -13,10 +13,10 @@ class CursorView extends View
hidden: false
initialize: (@cursor, @editor) ->
- @cursor.on 'change-screen-position.cursor-view', (position, { bufferChange }) =>
+ @cursor.on 'change-screen-position.cursor-view', (screenPosition, { bufferChange }) =>
@updateAppearance()
@removeIdleClassTemporarily() unless bufferChange
- @trigger 'cursor-move', bufferChange: bufferChange
+ @trigger 'cursor-move', {bufferChange}
@cursor.on 'destroy.cursor-view', => @remove()
diff --git a/src/app/editor.coffee b/src/app/editor.coffee
index 2946bd866..aef3fc3aa 100644
--- a/src/app/editor.coffee
+++ b/src/app/editor.coffee
@@ -91,68 +91,68 @@ class Editor extends View
bindKeys: ->
editorBindings =
- 'move-right': @moveCursorRight
- 'move-left': @moveCursorLeft
- 'move-down': @moveCursorDown
- 'move-up': @moveCursorUp
- 'move-to-next-word': @moveCursorToNextWord
- 'move-to-previous-word': @moveCursorToPreviousWord
- 'select-right': @selectRight
- 'select-left': @selectLeft
- 'select-up': @selectUp
- 'select-down': @selectDown
- 'select-word': @selectWord
- 'newline': @insertNewline
- 'indent': @indent
- 'indent-selected-rows': @indentSelectedRows
- 'outdent-selected-rows': @outdentSelectedRows
- 'backspace': @backspace
- 'backspace-to-beginning-of-word': @backspaceToBeginningOfWord
- 'delete': @delete
- 'delete-to-end-of-word': @deleteToEndOfWord
- 'delete-line': @deleteLine
- 'cut-to-end-of-line': @cutToEndOfLine
- 'cut': @cutSelection
- 'copy': @copySelection
- 'paste': @paste
- 'undo': @undo
- 'redo': @redo
- 'move-to-top': @moveCursorToTop
- 'move-to-bottom': @moveCursorToBottom
- 'move-to-beginning-of-line': @moveCursorToBeginningOfLine
- 'move-to-end-of-line': @moveCursorToEndOfLine
- 'move-to-first-character-of-line': @moveCursorToFirstCharacterOfLine
- 'move-to-beginning-of-word': @moveCursorToBeginningOfWord
- 'move-to-end-of-word': @moveCursorToEndOfWord
- 'select-to-top': @selectToTop
- 'select-to-bottom': @selectToBottom
- 'select-to-end-of-line': @selectToEndOfLine
- 'select-to-beginning-of-line': @selectToBeginningOfLine
- 'select-to-end-of-word': @selectToEndOfWord
- 'select-to-beginning-of-word': @selectToBeginningOfWord
- 'select-all': @selectAll
- 'page-down': @pageDown
- 'page-up': @pageUp
+ 'core:move-left': @moveCursorLeft
+ 'core:move-right': @moveCursorRight
+ 'core:move-to-top': @moveCursorToTop
+ 'core:move-to-bottom': @moveCursorToBottom
+ 'core:page-down': @pageDown
+ 'core:page-up': @pageUp
+ 'core:select-up': @selectUp
+ 'core:select-down': @selectDown
+ 'core:select-left': @selectLeft
+ 'core:select-right': @selectRight
+ 'core:select-to-top': @selectToTop
+ 'core:select-to-bottom': @selectToBottom
+ 'core:select-all': @selectAll
+ 'core:backspace': @backspace
+ 'core:delete': @delete
+ 'core:undo': @undo
+ 'core:redo': @redo
+ 'core:cut': @cutSelection
+ 'core:copy': @copySelection
+ 'core:paste': @paste
+ 'editor:move-to-next-word': @moveCursorToNextWord
+ 'editor:move-to-previous-word': @moveCursorToPreviousWord
+ 'editor:select-word': @selectWord
+ 'editor:newline': @insertNewline
+ 'editor:indent': @indent
+ 'editor:indent-selected-rows': @indentSelectedRows
+ 'editor:outdent-selected-rows': @outdentSelectedRows
+ 'editor:backspace-to-beginning-of-word': @backspaceToBeginningOfWord
+ 'editor:delete-to-end-of-word': @deleteToEndOfWord
+ 'editor:delete-line': @deleteLine
+ 'editor:cut-to-end-of-line': @cutToEndOfLine
+ 'editor:move-to-beginning-of-line': @moveCursorToBeginningOfLine
+ 'editor:move-to-end-of-line': @moveCursorToEndOfLine
+ 'editor:move-to-first-character-of-line': @moveCursorToFirstCharacterOfLine
+ 'editor:move-to-beginning-of-word': @moveCursorToBeginningOfWord
+ 'editor:move-to-end-of-word': @moveCursorToEndOfWord
+ 'editor:select-to-end-of-line': @selectToEndOfLine
+ 'editor:select-to-beginning-of-line': @selectToBeginningOfLine
+ 'editor:select-to-end-of-word': @selectToEndOfWord
+ 'editor:select-to-beginning-of-word': @selectToBeginningOfWord
unless @mini
_.extend editorBindings,
- 'save': @save
- 'newline-below': @insertNewlineBelow
- 'toggle-soft-wrap': @toggleSoftWrap
- 'fold-all': @foldAll
- 'unfold-all': @unfoldAll
- 'fold-current-row': @foldCurrentRow
- 'unfold-current-row': @unfoldCurrentRow
- 'fold-selection': @foldSelection
- 'split-left': @splitLeft
- 'split-right': @splitRight
- 'split-up': @splitUp
- 'split-down': @splitDown
- 'close': @close
- 'show-next-buffer': @loadNextEditSession
- 'show-previous-buffer': @loadPreviousEditSession
- 'toggle-line-comments': @toggleLineCommentsInSelection
- 'log-cursor-scope': @logCursorScope
+ 'core:move-up': @moveCursorUp
+ 'core:move-down': @moveCursorDown
+ 'core:close': @close
+ 'editor:save': @save
+ 'editor:newline-below': @insertNewlineBelow
+ 'editor:toggle-soft-wrap': @toggleSoftWrap
+ 'editor:fold-all': @foldAll
+ 'editor:unfold-all': @unfoldAll
+ 'editor:fold-current-row': @foldCurrentRow
+ 'editor:unfold-current-row': @unfoldCurrentRow
+ 'editor:fold-selection': @foldSelection
+ 'editor:split-left': @splitLeft
+ 'editor:split-right': @splitRight
+ 'editor:split-up': @splitUp
+ 'editor:split-down': @splitDown
+ 'editor:show-next-buffer': @loadNextEditSession
+ 'editor:show-previous-buffer': @loadPreviousEditSession
+ 'editor:toggle-line-comments': @toggleLineCommentsInSelection
+ 'editor:log-cursor-scope': @logCursorScope
for name, method of editorBindings
do (name, method) =>
@@ -343,6 +343,9 @@ class Editor extends View
else
@gutter.addClass('drop-shadow')
+ @on 'cursor-move', => @highlightCursorLine()
+ @on 'selection-change', => @highlightCursorLine()
+
selectOnMousemoveUntilMouseup: ->
moveHandler = (e) => @selectToScreenPosition(@screenPositionFromMouseEvent(e))
@on 'mousemove', moveHandler
@@ -415,6 +418,9 @@ class Editor extends View
@activeEditSession.on "buffer-path-change", =>
@trigger 'editor-path-change'
+ @activeEditSession.getSelection().on 'change-screen-range', =>
+ @trigger 'selection-change'
+
@trigger 'editor-path-change'
@renderWhenAttached()
@@ -640,7 +646,7 @@ class Editor extends View
addCursorView: (cursor) ->
cursorView = new CursorView(cursor, this)
@cursorViews.push(cursorView)
- @renderedLines.append(cursorView)
+ @appendToLinesView(cursorView)
cursorView
removeCursorView: (cursorView) ->
@@ -664,7 +670,7 @@ class Editor extends View
addSelectionView: (selection) ->
selectionView = new SelectionView({editor: this, selection})
@selectionViews.push(selectionView)
- @renderedLines.append(selectionView)
+ @appendToLinesView(selectionView)
selectionView
removeSelectionView: (selectionView) ->
@@ -674,9 +680,12 @@ class Editor extends View
cursorView.remove() for cursorView in @getCursorViews()
selectionView.remove() for selectionView in @getSelectionViews()
+ appendToLinesView: (view) ->
+ @renderedLines.append(view)
+
calculateDimensions: ->
fragment = $('x ')
- @renderedLines.append(fragment)
+ @appendToLinesView(fragment)
lineRect = fragment[0].getBoundingClientRect()
charRect = fragment.find('span')[0].getBoundingClientRect()
@@ -746,6 +755,7 @@ class Editor extends View
if renderedLines
@gutter.renderLineNumbers(renderFrom, renderTo)
+ @highlightCursorLine()
@updatePaddingOfRenderedLines()
updatePaddingOfRenderedLines: ->
@@ -809,11 +819,21 @@ class Editor extends View
charHeight = @charHeight
lines = @activeEditSession.linesForScreenRows(startRow, endRow)
activeEditSession = @activeEditSession
+ cursorScreenRow = @getCursorScreenPosition().row
+ mini = @mini
- buildLineHtml = (line) => @buildLineHtml(line)
- $$ -> @raw(buildLineHtml(line)) for line in lines
+ buildLineHtml = (line, lineClasses) => @buildLineHtml(line, lineClasses)
+ $$ ->
+ row = startRow
+ for line in lines
+ if mini or row isnt cursorScreenRow
+ lineClasses = null
+ else
+ lineClasses = ' cursor-line'
+ @raw(buildLineHtml(line, lineClasses))
+ row++
- buildLineHtml: (screenLine) ->
+ buildLineHtml: (screenLine, lineClasses) ->
scopeStack = []
line = []
@@ -845,6 +865,8 @@ class Editor extends View
else
lineAttributes = { class: 'line' }
+ lineAttributes.class += lineClasses if lineClasses
+
attributePairs = []
attributePairs.push "#{attributeName}=\"#{value}\"" for attributeName, value of lineAttributes
line.push("")
@@ -932,3 +954,12 @@ class Editor extends View
@screenPositionFromPixelPosition
top: pageY - @scrollView.offset().top + @scrollTop()
left: pageX - @scrollView.offset().left + @scrollView.scrollLeft()
+
+ highlightCursorLine: ->
+ return if @mini
+
+ @cursorScreenRow = @getCursorScreenPosition().row
+ screenRow = @cursorScreenRow - @firstRenderedScreenRow
+ @find('pre.line.cursor-line').removeClass('cursor-line')
+ if @getSelection().isSingleScreenLine()
+ @find("pre.line:eq(#{screenRow})").addClass('cursor-line')
diff --git a/src/app/gutter.coffee b/src/app/gutter.coffee
index c43f9828c..0361de79f 100644
--- a/src/app/gutter.coffee
+++ b/src/app/gutter.coffee
@@ -1,4 +1,4 @@
-{View, $$$} = require 'space-pen'
+{View, $$, $$$} = require 'space-pen'
$ = require 'jquery'
_ = require 'underscore'
@@ -9,47 +9,64 @@ class Gutter extends View
@div class: 'gutter', =>
@div outlet: 'lineNumbers', class: 'line-numbers'
- cursorBufferRow: -1
firstScreenRow: -1
+ highestNumberWidth: null
afterAttach: (onDom) ->
- @editor()?.on 'cursor-move', => @highlightCursorLine()
+ return if @attached or not onDom
+ @attached = true
+
+ editor = @editor()
+ highlightCursorLine = => @highlightCursorLine()
+ editor.on 'cursor-move', highlightCursorLine
+ editor.on 'selection-change', highlightCursorLine
+ @calculateWidth()
editor: ->
@parentView
+ calculateLineNumberPadding: ->
+ widthTesterElement = $$ -> @div {class: 'line-number'}, ""
+ widthTesterElement.width(0)
+ @append(widthTesterElement)
+ lineNumberPadding = widthTesterElement.outerWidth()
+ widthTesterElement.remove()
+ lineNumberPadding
+
renderLineNumbers: (startScreenRow, endScreenRow) ->
@firstScreenRow = startScreenRow
lastScreenRow = -1
- currentCursorBufferRow = @cursorBufferRow
rows = @editor().bufferRowsForScreenRows(startScreenRow, endScreenRow)
+ cursorScreenRow = @editor().getCursorScreenPosition().row
@lineNumbers[0].innerHTML = $$$ ->
for row in rows
- rowClass = null
- if row isnt currentCursorBufferRow
- rowClass = 'line-number'
+ if row == lastScreenRow
+ rowValue = '•'
else
- rowClass = 'line-number cursor-line-number'
- @div {class: rowClass}, if row == lastScreenRow then '•' else row + 1
+ rowValue = row + 1
+ @div {class: 'line-number'}, rowValue
lastScreenRow = row
@calculateWidth()
@highlightCursorLine()
calculateWidth: ->
- width = @editor().getLineCount().toString().length * @editor().charWidth
- if width != @cachedWidth
- @cachedWidth = width
- @lineNumbers.width(width)
+ highestNumberWidth = @editor().getLineCount().toString().length * @editor().charWidth
+ if highestNumberWidth != @highestNumberWidth
+ @highestNumberWidth = highestNumberWidth
+ @lineNumbers.width(highestNumberWidth + @calculateLineNumberPadding())
@widthChanged?(@outerWidth())
highlightCursorLine: ->
- return if @firstScreenRow < 0
+ cursorScreenRow = @editor().getCursorScreenPosition().row
+ screenRowIndex = cursorScreenRow - @firstScreenRow
- newCursorBufferRow = @editor().getCursorBufferPosition().row
- if newCursorBufferRow isnt @cursorBufferRow
- @cursorBufferRow = newCursorBufferRow
- screenRow = @cursorBufferRow - @firstScreenRow
- @find('.line-number.cursor-line-number').removeClass('cursor-line-number')
- @find(".line-number:eq(#{screenRow})").addClass('cursor-line-number')
+ currentLineNumberRow = @find(".line-number.cursor-line-number")
+ currentLineNumberRow.removeClass('cursor-line-number')
+ currentLineNumberRow.removeClass('cursor-line-number-background')
+
+ newLineNumberRow = @find(".line-number:eq(#{screenRowIndex})")
+ newLineNumberRow.addClass('cursor-line-number')
+ if @editor().getSelection().isSingleScreenLine()
+ newLineNumberRow.addClass('cursor-line-number-background')
diff --git a/src/app/keymaps/apple.coffee b/src/app/keymaps/apple.coffee
index b65aa1467..e0b661b61 100644
--- a/src/app/keymaps/apple.coffee
+++ b/src/app/keymaps/apple.coffee
@@ -1,15 +1,17 @@
+window.keymap.bindKeys 'body'
+ 'meta-up': 'core:move-to-top'
+ 'meta-down': 'core:move-to-bottom'
+ 'meta-shift-up': 'core:select-to-top'
+ 'meta-shift-down': 'core:select-to-bottom'
+
window.keymap.bindKeys '.editor'
- 'meta-up': 'move-to-top'
- 'meta-down': 'move-to-bottom'
- 'meta-right': 'move-to-end-of-line'
- 'meta-left': 'move-to-beginning-of-line'
- 'alt-left': 'move-to-beginning-of-word'
- 'alt-right': 'move-to-end-of-word'
- 'meta-shift-up': 'select-to-top'
- 'meta-shift-down': 'select-to-bottom'
- 'meta-shift-left': 'select-to-beginning-of-line'
- 'meta-shift-right': 'select-to-end-of-line'
- 'alt-shift-left': 'select-to-beginning-of-word'
- 'alt-shift-right': 'select-to-end-of-word'
- 'alt-backspace': 'backspace-to-beginning-of-word'
- 'alt-delete': 'delete-to-end-of-word'
+ 'meta-right': 'editor:move-to-end-of-line'
+ 'meta-left': 'editor:move-to-beginning-of-line'
+ 'alt-left': 'editor:move-to-beginning-of-word'
+ 'alt-right': 'editor:move-to-end-of-word'
+ 'meta-shift-left': 'editor:select-to-beginning-of-line'
+ 'meta-shift-right': 'editor:select-to-end-of-line'
+ 'alt-shift-left': 'editor:select-to-beginning-of-word'
+ 'alt-shift-right': 'editor:select-to-end-of-word'
+ 'alt-backspace': 'editor:backspace-to-beginning-of-word'
+ 'alt-delete': 'editor:delete-to-end-of-word'
diff --git a/src/app/keymaps/atom.coffee b/src/app/keymaps/atom.coffee
index 5f2db2507..4ad888c02 100644
--- a/src/app/keymaps/atom.coffee
+++ b/src/app/keymaps/atom.coffee
@@ -1,10 +1,27 @@
-window.keymap.bindKeys '*'
- 'meta-w': 'close'
+window.keymap.bindKeys 'body'
'alt-meta-i': 'toggle-dev-tools'
- right: 'move-right'
- left: 'move-left'
- down: 'move-down'
- up: 'move-up'
- pagedown: 'page-down'
- pageup: 'page-up'
- 'meta-S': 'save-all'
+ 'meta-w': 'core:close'
+ up: 'core:move-up'
+ down: 'core:move-down'
+ left: 'core:move-left'
+ right: 'core:move-right'
+ 'shift-up': 'core:select-up'
+ 'shift-down': 'core:select-down'
+ 'shift-left': 'core:select-left'
+ 'shift-right': 'core:select-right'
+ 'meta-a': 'core:select-all'
+ 'backspace': 'core:backspace'
+ 'shift-backspace': 'core:backspace'
+ 'delete': 'core:delete'
+ 'meta-z': 'core:undo'
+ 'meta-Z': 'core:redo'
+ 'meta-x': 'core:cut'
+ 'meta-c': 'core:copy'
+ 'meta-v': 'core:paste'
+ pageup: 'core:page-up'
+ pagedown: 'core:page-down'
+
+ 'meta-S': 'root-view:save-all'
+ 'meta-+': 'root-view:increase-font-size'
+ 'meta--': 'root-view:decrease-font-size'
+ 'ctrl-w w': 'root-view:focus-next-pane'
diff --git a/src/app/keymaps/editor.coffee b/src/app/keymaps/editor.coffee
index 5d6315b13..37e1f35c8 100644
--- a/src/app/keymaps/editor.coffee
+++ b/src/app/keymaps/editor.coffee
@@ -1,40 +1,25 @@
window.keymap.bindKeys '.editor',
- 'meta-s': 'save'
- 'shift-right': 'select-right'
- 'shift-left': 'select-left'
- 'shift-up': 'select-up'
- 'shift-down': 'select-down'
- 'meta-a': 'select-all'
- 'enter': 'newline'
- 'meta-enter': 'newline-below'
- 'tab': 'indent'
- 'backspace': 'backspace'
- 'shift-backspace': 'backspace'
- 'delete': 'delete'
- 'meta-d': 'delete-line'
- 'meta-x': 'cut'
- 'meta-c': 'copy'
- 'meta-v': 'paste'
- 'meta-z': 'undo'
- 'meta-Z': 'redo'
- 'alt-meta-w': 'toggle-soft-wrap'
- 'ctrl-[': 'fold-current-row'
- 'ctrl-]': 'unfold-current-row'
- 'ctrl-{': 'fold-all'
- 'ctrl-}': 'unfold-all'
- 'alt-meta-ctrl-f': 'fold-selection'
- 'alt-meta-left': 'split-left'
- 'alt-meta-right': 'split-right'
- 'alt-meta-up': 'split-up'
- 'alt-meta-down': 'split-down'
- 'shift-tab': 'outdent-selected-rows'
- 'meta-[': 'outdent-selected-rows'
- 'meta-]': 'indent-selected-rows'
- 'meta-{': 'show-previous-buffer'
- 'meta-}': 'show-next-buffer'
- 'meta-+': 'increase-font-size'
- 'meta--': 'decrease-font-size'
- 'meta-/': 'toggle-line-comments'
- 'ctrl-w w': 'focus-next-pane'
- 'ctrl-W': 'select-word'
- 'meta-alt-p': 'log-cursor-scope'
+ 'meta-s': 'editor:save'
+ 'enter': 'editor:newline'
+ 'meta-enter': 'editor:newline-below'
+ 'tab': 'editor:indent'
+ 'meta-d': 'editor:delete-line'
+ 'alt-meta-w': 'editor:toggle-soft-wrap'
+ 'ctrl-[': 'editor:fold-current-row'
+ 'ctrl-]': 'editor:unfold-current-row'
+ 'ctrl-{': 'editor:fold-all'
+ 'ctrl-}': 'editor:unfold-all'
+ 'alt-meta-ctrl-f': 'editor:fold-selection'
+ 'alt-meta-left': 'editor:split-left'
+ 'alt-meta-right': 'editor:split-right'
+ 'alt-meta-up': 'editor:split-up'
+ 'alt-meta-down': 'editor:split-down'
+ 'shift-tab': 'editor:outdent-selected-rows'
+ 'meta-[': 'editor:outdent-selected-rows'
+ 'meta-]': 'editor:indent-selected-rows'
+ 'meta-{': 'editor:show-previous-buffer'
+ 'meta-}': 'editor:show-next-buffer'
+ 'meta-/': 'editor:toggle-line-comments'
+ 'ctrl-W': 'editor:select-word'
+ 'meta-alt-p': 'editor:log-cursor-scope'
+
diff --git a/src/app/keymaps/emacs.coffee b/src/app/keymaps/emacs.coffee
index 5ece06a79..4574e188d 100644
--- a/src/app/keymaps/emacs.coffee
+++ b/src/app/keymaps/emacs.coffee
@@ -1,22 +1,22 @@
-window.keymap.bindKeys '*',
- 'ctrl-f': 'move-right'
- 'ctrl-b': 'move-left'
- 'ctrl-p': 'move-up'
- 'ctrl-n': 'move-down'
+window.keymap.bindKeys 'body',
+ 'ctrl-p': 'core:move-up'
+ 'ctrl-n': 'core:move-down'
+ 'ctrl-b': 'core:move-left'
+ 'ctrl-f': 'core:move-right'
+ 'ctrl-P': 'core:select-up'
+ 'ctrl-N': 'core:select-down'
+ 'ctrl-F': 'core:select-right'
+ 'ctrl-B': 'core:select-left'
+ 'ctrl-h': 'core:backspace'
+ 'ctrl-d': 'core:delete'
window.keymap.bindKeys '.editor',
- 'ctrl-F': 'select-right'
- 'ctrl-B': 'select-left'
- 'ctrl-P': 'select-up'
- 'ctrl-N': 'select-down'
- 'alt-f': 'move-to-end-of-word'
- 'alt-F': 'select-to-end-of-word'
- 'alt-b': 'move-to-beginning-of-word'
- 'alt-B': 'select-to-beginning-of-word'
- 'ctrl-a': 'move-to-first-character-of-line'
- 'ctrl-e': 'move-to-end-of-line'
- 'ctrl-h': 'backspace'
- 'ctrl-d': 'delete'
- 'alt-h': 'backspace-to-beginning-of-word'
- 'alt-d': 'delete-to-end-of-word'
- 'ctrl-k': 'cut-to-end-of-line'
+ 'alt-f': 'editor:move-to-end-of-word'
+ 'alt-F': 'editor:select-to-end-of-word'
+ 'alt-b': 'editor:move-to-beginning-of-word'
+ 'alt-B': 'editor:select-to-beginning-of-word'
+ 'ctrl-a': 'editor:move-to-first-character-of-line'
+ 'ctrl-e': 'editor:move-to-end-of-line'
+ 'alt-h': 'editor:backspace-to-beginning-of-word'
+ 'alt-d': 'editor:delete-to-end-of-word'
+ 'ctrl-k': 'editor:cut-to-end-of-line'
diff --git a/src/app/language-mode.coffee b/src/app/language-mode.coffee
index 24cba1c52..1c7604c6d 100644
--- a/src/app/language-mode.coffee
+++ b/src/app/language-mode.coffee
@@ -49,7 +49,9 @@ class LanguageMode
scopes = @tokenizedBuffer.scopesForPosition(range.start)
return unless commentString = TextMateBundle.lineCommentStringForScope(scopes[0])
- commentRegex = new OnigRegExp("^\s*" + _.escapeRegExp(commentString))
+ commentRegexString = _.escapeRegExp(commentString)
+ commentRegexString = commentRegexString.replace(/(\s+)$/, '($1)?')
+ commentRegex = new OnigRegExp("^\s*#{commentRegexString}")
shouldUncomment = commentRegex.test(@editSession.lineForBufferRow(range.start.row))
diff --git a/src/app/root-view.coffee b/src/app/root-view.coffee
index 282af030d..38cb4f1e9 100644
--- a/src/app/root-view.coffee
+++ b/src/app/root-view.coffee
@@ -73,10 +73,10 @@ class RootView extends View
@project.setPath(path) unless @project.getRootDirectory()
@setTitle(path)
- @on 'increase-font-size', => @setFontSize(@getFontSize() + 1)
- @on 'decrease-font-size', => @setFontSize(@getFontSize() - 1)
- @on 'focus-next-pane', => @focusNextPane()
- @on 'save-all', => @saveAll()
+ @on 'root-view:increase-font-size', => @setFontSize(@getFontSize() + 1)
+ @on 'root-view:decrease-font-size', => @setFontSize(@getFontSize() - 1)
+ @on 'root-view:focus-next-pane', => @focusNextPane()
+ @on 'root-view:save-all', => @saveAll()
afterAttach: (onDom) ->
@focus() if onDom
diff --git a/src/app/selection.coffee b/src/app/selection.coffee
index eca63c7ff..0e0b711f7 100644
--- a/src/app/selection.coffee
+++ b/src/app/selection.coffee
@@ -31,6 +31,9 @@ class Selection
isReversed: ->
not @isEmpty() and @cursor.getBufferPosition().isLessThan(@anchor.getBufferPosition())
+ isSingleScreenLine: ->
+ @getScreenRange().isSingleLine()
+
getScreenRange: ->
if @anchor
new Range(@anchor.getScreenPosition(), @cursor.getScreenPosition())
diff --git a/src/extensions/autocomplete/autocomplete.coffee b/src/extensions/autocomplete/autocomplete.coffee
index 104d16fc0..b38ff7ca2 100644
--- a/src/extensions/autocomplete/autocomplete.coffee
+++ b/src/extensions/autocomplete/autocomplete.coffee
@@ -54,13 +54,15 @@ class Autocomplete extends View
@cancel()
@miniEditor.getBuffer().on 'change', (e) =>
- @filterMatches() if @hasParent()
+ if @hasParent()
+ @filterMatches()
+ @renderMatchList()
- @miniEditor.preempt 'move-up', =>
+ @miniEditor.preempt 'core:move-up', =>
@selectPreviousMatch()
false
- @miniEditor.preempt 'move-down', =>
+ @miniEditor.preempt 'core:move-down', =>
@selectNextMatch()
false
@@ -110,10 +112,16 @@ class Autocomplete extends View
originalCursorPosition = @editor.getCursorScreenPosition()
@filterMatches()
- @editor.append(this)
- @setPosition(originalCursorPosition)
- @miniEditor.focus()
+ if @filteredMatches.length is 1
+ @currentMatchIndex = 0
+ @replaceSelectedTextWithMatch @selectedMatch()
+ @confirm()
+ else
+ @renderMatchList()
+ @editor.appendToLinesView(this)
+ @setPosition(originalCursorPosition)
+ @miniEditor.focus()
detach: ->
@miniEditor.off("focusout")
@@ -125,12 +133,12 @@ class Autocomplete extends View
setPosition: (originalCursorPosition) ->
{ left, top } = @editor.pixelPositionForScreenPosition(originalCursorPosition)
- top -= @editor.scrollTop()
+ height = @outerHeight()
potentialTop = top + @editor.lineHeight
- potentialBottom = potentialTop + @outerHeight()
+ potentialBottom = potentialTop - @editor.scrollTop() + height
if potentialBottom > @editor.outerHeight()
- @css(left: left, bottom: @editor.outerHeight() - top, top: 'inherit')
+ @css(left: left, top: top - height, bottom: 'inherit')
else
@css(left: left, top: potentialTop, bottom: 'inherit')
@@ -166,7 +174,6 @@ class Autocomplete extends View
filterMatches: ->
@filteredMatches = fuzzyFilter(@allMatches, @miniEditor.getText(), key: 'word')
- @renderMatchList()
renderMatchList: ->
@matchesList.empty()
diff --git a/src/extensions/command-panel/command-panel.coffee b/src/extensions/command-panel/command-panel.coffee
index a19979f4e..ea5912b2f 100644
--- a/src/extensions/command-panel/command-panel.coffee
+++ b/src/extensions/command-panel/command-panel.coffee
@@ -62,9 +62,8 @@ class CommandPanel extends View
@rootView.on 'command-panel:repeat-relative-address-in-reverse', => @repeatRelativeAddressInReverse()
@rootView.on 'command-panel:set-selection-as-regex-address', => @setSelectionAsLastRelativeAddress()
- @miniEditor.off 'move-up move-down'
- @miniEditor.on 'move-up', => @navigateBackwardInHistory()
- @miniEditor.on 'move-down', => @navigateForwardInHistory()
+ @on 'core:move-up', => @navigateBackwardInHistory()
+ @on 'core:move-down', => @navigateForwardInHistory()
@previewList.hide()
diff --git a/src/extensions/command-panel/preview-list.coffee b/src/extensions/command-panel/preview-list.coffee
index cebe520c9..733749b0c 100644
--- a/src/extensions/command-panel/preview-list.coffee
+++ b/src/extensions/command-panel/preview-list.coffee
@@ -10,8 +10,8 @@ class PreviewList extends View
operations: null
initialize: (@rootView) ->
- @on 'move-down', => @selectNextOperation()
- @on 'move-up', => @selectPreviousOperation()
+ @on 'core:move-down', => @selectNextOperation()
+ @on 'core:move-up', => @selectPreviousOperation()
@on 'command-panel:execute', => @executeSelectedOperation()
@on 'mousedown', 'li', (e) =>
diff --git a/src/extensions/fuzzy-finder/fuzzy-finder.coffee b/src/extensions/fuzzy-finder/fuzzy-finder.coffee
index f016017f1..3e75dd98c 100644
--- a/src/extensions/fuzzy-finder/fuzzy-finder.coffee
+++ b/src/extensions/fuzzy-finder/fuzzy-finder.coffee
@@ -26,13 +26,13 @@ class FuzzyFinder extends View
@rootView.on 'fuzzy-finder:toggle-buffer-finder', => @toggleBufferFinder()
@on 'fuzzy-finder:cancel', => @detach()
- @on 'move-up', => @moveUp()
- @on 'move-down', => @moveDown()
+ @on 'core:move-up', => @moveUp()
+ @on 'core:move-down', => @moveDown()
@on 'fuzzy-finder:select-path', => @select()
@on 'mousedown', 'li', (e) => @entryClicked(e)
@miniEditor.getBuffer().on 'change', => @populatePathList() if @hasParent()
- @miniEditor.off 'move-up move-down'
+ @miniEditor.off 'core:move-up core:move-down'
toggleFileFinder: ->
if @hasParent()
@@ -93,18 +93,22 @@ class FuzzyFinder extends View
false
moveUp: ->
- @findSelectedLi()
- .filter(':not(:first-child)')
- .removeClass('selected')
- .prev()
- .addClass('selected')
+ selected = @findSelectedLi().removeClass('selected')
+
+ if selected.filter(':not(:first-child)').length is 0
+ selected = @pathList.children('li:last')
+ else
+ selected = selected.prev()
+ selected.addClass('selected')
moveDown: ->
- @findSelectedLi()
- .filter(':not(:last-child)')
- .removeClass('selected')
- .next()
- .addClass('selected')
+ selected = @findSelectedLi().removeClass('selected')
+
+ if selected.filter(':not(:last-child)').length is 0
+ selected = @pathList.children('li:first')
+ else
+ selected = selected.next()
+ selected.addClass('selected')
findMatches: (query) ->
fuzzyFilter(@paths, query, maxResults: @maxResults)
diff --git a/src/extensions/markdown-preview/markdown-preview.coffee b/src/extensions/markdown-preview/markdown-preview.coffee
index 2785610a3..8601b8b85 100644
--- a/src/extensions/markdown-preview/markdown-preview.coffee
+++ b/src/extensions/markdown-preview/markdown-preview.coffee
@@ -1,4 +1,4 @@
-{View, $$} = require 'space-pen'
+{View} = require 'space-pen'
fs = require 'fs'
$ = require 'jquery'
diff --git a/src/extensions/tree-view/dialog.coffee b/src/extensions/tree-view/dialog.coffee
index da9685ea5..4d002aa3a 100644
--- a/src/extensions/tree-view/dialog.coffee
+++ b/src/extensions/tree-view/dialog.coffee
@@ -12,7 +12,7 @@ class Dialog extends View
initialize: ({path, @onConfirm, select} = {}) ->
@miniEditor.focus()
- @on 'tree-view:confirm', => @confirm()
+ @on 'tree-view:confirm', => @onConfirm(@miniEditor.getText())
@on 'tree-view:cancel', => @cancel()
@miniEditor.on 'focusout', => @remove()
@@ -24,8 +24,7 @@ class Dialog extends View
range = [[0, path.length - baseName.length], [0, path.length - extension.length]]
@miniEditor.setSelectedBufferRange(range)
- confirm: ->
- return if @onConfirm(@miniEditor.getText()) is false
+ close: ->
@remove()
$('#root-view').focus()
diff --git a/src/extensions/tree-view/tree-view.coffee b/src/extensions/tree-view/tree-view.coffee
index 7d4ba054c..a3caff950 100644
--- a/src/extensions/tree-view/tree-view.coffee
+++ b/src/extensions/tree-view/tree-view.coffee
@@ -42,18 +42,27 @@ class TreeView extends View
root: null
focusAfterAttach: false
scrollTopAfterAttach: -1
+ selectedPath: null
initialize: (@rootView) ->
@on 'click', '.entry', (e) => @entryClicked(e)
- @on 'move-up', => @moveUp()
- @on 'move-down', => @moveDown()
+ @on 'core:move-up', => @moveUp()
+ @on 'core:move-down', => @moveDown()
+ @on 'core:move-to-top', => @scrollToTop()
+ @on 'core:move-to-bottom', => @scrollToBottom()
+ @on 'core:page-up', => @pageUp()
+ @on 'core:page-down', => @pageDown()
@on 'tree-view:expand-directory', => @expandDirectory()
@on 'tree-view:collapse-directory', => @collapseDirectory()
@on 'tree-view:open-selected-entry', => @openSelectedEntry(true)
@on 'tree-view:move', => @moveSelectedEntry()
@on 'tree-view:add', => @add()
@on 'tree-view:remove', => @removeSelectedEntry()
- @on 'tree-view:directory-modified', => @selectActiveFile()
+ @on 'tree-view:directory-modified', =>
+ if @hasFocus()
+ @selectEntryForPath(@selectedPath) if @selectedPath
+ else
+ @selectActiveFile()
@on 'tree-view:unfocus', => @rootView.focus()
@rootView.on 'tree-view:toggle', => @toggle()
@rootView.on 'tree-view:reveal-active-file', => @revealActiveFile()
@@ -69,7 +78,7 @@ class TreeView extends View
serialize: ->
directoryExpansionStates: @root?.serializeEntryExpansionStates()
selectedPath: @selectedEntry()?.getPath()
- hasFocus: @is(':focus')
+ hasFocus: @hasFocus()
attached: @hasParent()
scrollTop: @scrollTop()
@@ -77,7 +86,7 @@ class TreeView extends View
@root?.unwatchEntries()
toggle: ->
- if @is(':focus')
+ if @hasFocus()
@detach()
@rootView.focus()
else
@@ -91,6 +100,9 @@ class TreeView extends View
@scrollTopAfterAttach = @scrollTop()
super
+ hasFocus: ->
+ @is(':focus')
+
entryClicked: (e) ->
entry = $(e.currentTarget).view()
switch e.originalEvent?.detail ? 1
@@ -194,7 +206,7 @@ class TreeView extends View
moveSelectedEntry: ->
entry = @selectedEntry()
return unless entry
- oldPath = @selectedEntry().getPath()
+ oldPath = entry.getPath()
dialog = new Dialog
prompt: "Enter the new path for the file:"
@@ -206,9 +218,9 @@ class TreeView extends View
try
fs.makeTree(directoryPath) unless fs.exists(directoryPath)
fs.move(oldPath, newPath)
+ dialog.close()
catch e
dialog.showError("Error: " + e.message + " Try a different path:")
- return false
@rootView.append(dialog)
@@ -236,21 +248,23 @@ class TreeView extends View
path: relativeDirectoryPath
select: false
onConfirm: (relativePath) =>
- endsWithDirectorySeperator = /\/$/.test(relativePath)
+ endsWithDirectorySeparator = /\/$/.test(relativePath)
path = @rootView.project.resolve(relativePath)
try
if fs.exists(path)
pathType = if fs.isFile(path) then "file" else "directory"
dialog.showError("Error: A #{pathType} already exists at path '#{path}'. Try a different path:")
- false
- else if endsWithDirectorySeperator
+ else if endsWithDirectorySeparator
fs.makeTree(path)
+ dialog.cancel()
+ @entryForPath(path).buildEntries()
+ @selectEntryForPath(path)
else
fs.write(path, "")
@rootView.open(path)
+ dialog.close()
catch e
dialog.showError("Error: " + e.message + " Try a different path:")
- return false
@rootView.append(dialog)
@@ -259,6 +273,8 @@ class TreeView extends View
selectEntry: (entry) ->
return false unless entry.get(0)
+ entry = entry.view() unless entry instanceof View
+ @selectedPath = entry.getPath()
@find('.selected').removeClass('selected')
entry.addClass('selected')
diff --git a/src/extensions/vim-mode/index.coffee b/src/extensions/vim-mode/index.coffee
index 7391ddfe3..abeaf11c1 100644
--- a/src/extensions/vim-mode/index.coffee
+++ b/src/extensions/vim-mode/index.coffee
@@ -32,10 +32,10 @@ class VimMode
'i': 'insert'
'd': 'delete'
'x': 'delete-right'
- 'h': 'move-left'
- 'j': 'move-down'
- 'k': 'move-up'
- 'l': 'move-right'
+ 'h': 'core:move-left'
+ 'j': 'core:move-down'
+ 'k': 'core:move-up'
+ 'l': 'core:move-right'
'w': 'move-to-next-word'
'b': 'move-to-previous-word'
'}': 'move-to-next-paragraph'
@@ -47,10 +47,10 @@ class VimMode
'insert': => @activateInsertMode()
'delete': => @delete()
'delete-right': => new commands.DeleteRight(@editor)
- 'move-left': => new motions.MoveLeft(@editor)
- 'move-up': => new motions.MoveUp(@editor)
- 'move-down': => new motions.MoveDown @editor
- 'move-right': => new motions.MoveRight @editor
+ 'core:move-left': => new motions.MoveLeft(@editor)
+ 'core:move-up': => new motions.MoveUp(@editor)
+ 'core:move-down': => new motions.MoveDown @editor
+ 'core:move-right': => new motions.MoveRight @editor
'move-to-next-word': => new motions.MoveToNextWord(@editor)
'move-to-previous-word': => new motions.MoveToPreviousWord(@editor)
'move-to-next-paragraph': => new motions.MoveToNextParagraph(@editor)
diff --git a/src/stdlib/jquery-extensions.coffee b/src/stdlib/jquery-extensions.coffee
index adf11ea32..e46fceca8 100644
--- a/src/stdlib/jquery-extensions.coffee
+++ b/src/stdlib/jquery-extensions.coffee
@@ -6,12 +6,24 @@ $.fn.scrollBottom = (newValue) ->
else
@scrollTop() + @height()
+$.fn.scrollToTop = ->
+ @scrollTop(0)
+
+$.fn.scrollToBottom = ->
+ @scrollTop(@prop('scrollHeight'))
+
$.fn.scrollRight = (newValue) ->
if newValue?
@scrollLeft(newValue - @width())
else
@scrollLeft() + @width()
+$.fn.pageUp = ->
+ @scrollTop(@scrollTop() - @height())
+
+$.fn.pageDown = ->
+ @scrollTop(@scrollTop() + @height())
+
$.fn.containsElement = (element) ->
(element[0].compareDocumentPosition(this[0]) & 8) == 8
diff --git a/static/editor.css b/static/editor.css
index 63df5ef61..70087e223 100644
--- a/static/editor.css
+++ b/static/editor.css
@@ -16,8 +16,6 @@
position: absolute;
height: 100%;
overflow: hidden;
- padding-left: 0.4em;
- padding-right: 0.8em;
color: rgba(255, 255, 255, .3);
text-align: right;
}
@@ -26,10 +24,19 @@
position: relative;
}
-.line-number.cursor-line-number {
+.editor .gutter .line-number {
+ padding-left: 0.4em;
+ padding-right: 0.8em;
+}
+
+.editor.focused .line-number.cursor-line-number {
color: rgba(255, 255, 255, .6);
}
+.editor.focused .line.cursor-line, .editor.focused .line-number.cursor-line-number-background {
+ background-color: rgba(255, 255, 255, .12);
+}
+
.editor.mini .gutter {
display: none;
}
diff --git a/static/markdown-preview.css b/static/markdown-preview.css
index da83bb5a5..6c1c7265a 100644
--- a/static/markdown-preview.css
+++ b/static/markdown-preview.css
@@ -65,6 +65,20 @@
border-bottom: 1px solid #CCC;
}
+.markdown-body h3 {
+ font-size: 18px;
+ font-weight: bold;
+ margin-top: 20px;
+ margin-bottom: 10px;
+}
+
+.markdown-body h4 {
+ font-size: 16px;
+ font-weight: bold;
+ margin-top: 20px;
+ margin-bottom: 10px;
+}
+
.markdown-body p {
margin-bottom: 15px;
}