Merge remote-tracking branch 'origin/master' into status-bar-buffer-modified-indicator

This commit is contained in:
Will Farrington 2012-10-07 09:13:08 -10:00
commit 09024e0dc8
49 changed files with 755 additions and 563 deletions

View File

@ -6,4 +6,5 @@ requireExtension 'command-panel'
requireExtension 'keybindings-view'
requireExtension 'snippets'
requireExtension 'status-bar'
requireExtension 'wrap-guide'
requireExtension 'markdown-preview'

3
.github Normal file
View File

@ -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

1
.pairs
View File

@ -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

1
docs/extensions/intro.md Normal file
View File

@ -0,0 +1 @@
## Extensions

View File

@ -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.

View File

@ -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;
}
```

View File

@ -1,64 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<style>
body {
background: blue;
}
#flexbox-container {
background: black;
top: 100px;
left: 100px;
height: 200px;
width: 400px;
display: -webkit-flex;
-webkit-flex-direction: row;
overflow: hidden;
border: 1px solid navy;
-webkit-align-items: stretch;
}
#flexbox-item-1 {
background: green;
overflow: auto;
-webkit-flex: 1;
}
#flexbox-item-2 {
overflow: scroll;
background: orange;
-webkit-flex: 1;
}
</style>
</head>
<body>
<div id="flexbox-container">
<div id="flexbox-item-1">
<div id="flexbox-item-1-content">
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.
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
<div>10</div>
<div>11</div>
<div>12</div>
<div>13</div>
<div>14</div>
<div>15</div>
</div>
</div>
<div id="flexbox-item-2">
My cross size (height) should also be equal to the height of my flexbox container, but I don't overflow.
</div>
</div>
</body>
</html>

View File

@ -1,84 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>wtf</title>
<link href="static/reset.css" rel="stylesheet">
<style>
html, body {
height: 100%;
width: 100%;
background-color: black;
display: -webkit-box;
-webkit-box-flex: 1;
-webkit-box-orient: vertical;
}
.row {
background-color: green;
display: -webkit-box;
-webkit-box-flex: 1;
-webkit-box-orient: horizontal;
overflow: hidden;
}
.column {
background-color: red;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-box-flex: 1;
overflow: hidden;
}
.top {
background-color: orange;
height: 200px;
}
.left {
background-color: purple;
width: 100px;
}
.middle {
background-color: blue;
display: -webkit-box;
-webkit-box-flex: 1;
}
.right {
width: 200px;
}
.bottom {
height: 100px;
}
.i {
overflow: scroll;
}
.full-height-child {
background: black;
color: white;
display: -webkit-box;
-webkit-box-flex: 1;
}
</style>
</head>
<body>
<div class='column'>
<div class='i top'>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. </div>
<div class='row'>
<div class='i left'>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. </div>
<div class='i middle'>
<div class="full-height-child">
I am a child of a flexbox item. My height should be exactly the height of my container.
</div>
</div>
<div class='i right'>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. </div>
</div>
<div class='i bottom'>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. </div>
</div>
</body>
</html>

View File

@ -1,53 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<style>
body {
background: blue;
}
#flexbox-container {
background: black;
position: absolute;*
top: 100px;
left: 100px;
height: 200px;
width: 200px;
display: -webkit-flex;
-webkit-flex-direction: column;
overflow: hidden;
border: 1px solid navy;
}
#flexbox-item-1 {
background: green;
position: relative;
overflow: auto;
-webkit-flex: 1 0 100%;
}
#flexbox-item-1-content {
width: -webkit-fill-available;
min-height: -webkit-fill-available;
height: -webkit-fill-available;
background: purple;
}
#flexbox-item-2 {
background: orange;
}
</style>
</head>
<body>
<div id="flexbox-container">
<div id="flexbox-item-1">
<div id="flexbox-item-1-content">
I should be 100% of the height of my container, right?
</div>
</div>
<div id="flexbox-item-2">
I don't have a flex property, so I should have an auto height
</div>
</div>
</body>
</html>

3
docs/intro.md Normal file
View File

@ -0,0 +1,3 @@
## The Definitive Guide to Atom
Welcome!

24
docs/styling.md Normal file
View File

@ -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;
}
```

View File

@ -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

View File

@ -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")

View File

@ -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-+/

View File

@ -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", ->

View File

@ -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

View File

@ -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'

View File

@ -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()

View File

@ -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)

View File

@ -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", ->

View File

0
spec/fixtures/tree-view/dir2/file2 vendored Normal file
View File

View File

13
spec/fixtures/tree-view/tree-view.js vendored Normal file
View File

@ -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));
};

1
spec/fixtures/tree-view/tree-view.txt vendored Normal file
View File

@ -0,0 +1 @@
Some text.

View File

@ -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", ->

View File

@ -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()

View File

@ -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 = $('<pre class="line" style="position: absolute; visibility: hidden;"><span>x</span></div>')
@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("<pre #{attributePairs.join(' ')}>")
@ -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')

View File

@ -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')

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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))

View File

@ -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

View File

@ -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())

View File

@ -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()

View File

@ -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()

View File

@ -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) =>

View File

@ -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)

View File

@ -1,4 +1,4 @@
{View, $$} = require 'space-pen'
{View} = require 'space-pen'
fs = require 'fs'
$ = require 'jquery'

View File

@ -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()

View File

@ -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')

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}