')
- content.append(otherElt)
- view.insertBefore(otherElt)
- expect(attachHandler).toHaveBeenCalled()
- expect(subviewAttachHandler).toHaveBeenCalled()
-
- describe "when attached to an element that is not on the DOM", ->
- it "does not trigger an attach event", ->
- fragment = $('
')
- fragment.append view
- expect(attachHandler).not.toHaveBeenCalled()
-
diff --git a/spec/stdlib/template/builder-spec.coffee b/spec/stdlib/template/builder-spec.coffee
index c5ab6584f..5c4695255 100644
--- a/spec/stdlib/template/builder-spec.coffee
+++ b/spec/stdlib/template/builder-spec.coffee
@@ -1,5 +1,4 @@
Builder = require 'template/builder'
-Template = require 'template'
describe "Builder", ->
builder = null
@@ -74,28 +73,3 @@ describe "Builder", ->
builder.raw ' '
expect(builder.toHtml()).toBe ' '
- describe ".subview(name, template, attrs)", ->
- template = null
-
- beforeEach ->
- template = class extends Template
- content: (params) ->
- @div =>
- @h2 params.title
- @div "I am a subview"
-
- viewProperties:
- foo: "bar"
-
- it "inserts a view built from the given template with the given params", ->
- builder.tag 'div', ->
- builder.tag 'h1', "Superview"
- builder.subview 'sub', template.build(title: "Subview")
-
- fragment = builder.toFragment()
- expect(fragment.find("h1:contains(Superview)")).toExist()
- expect(fragment.find("h2:contains(Subview)")).toExist()
- subview = fragment.sub
- expect(subview).toMatchSelector ':has(h2):contains(I am a subview)'
- expect(subview.foo).toBe 'bar'
-
diff --git a/src/atom/cursor.coffee b/src/atom/cursor.coffee
index 108bd2d94..727186c6c 100644
--- a/src/atom/cursor.coffee
+++ b/src/atom/cursor.coffee
@@ -1,120 +1,119 @@
-Template = require 'template'
+{View} = require 'space-pen'
Point = require 'point'
_ = require 'underscore'
module.exports =
-class Cursor extends Template
- content: ->
+class Cursor extends View
+ @content: ->
@pre class: 'cursor idle', style: 'position: absolute;', => @raw ' '
- viewProperties:
- editor: null
+ editor: null
- initialize: (@editor) ->
- @one 'attach', => @updateAppearance()
+ initialize: (@editor) ->
+ @one 'attach', => @updateAppearance()
- bufferChanged: (e) ->
- @setPosition(e.postRange.end)
+ bufferChanged: (e) ->
+ @setPosition(e.postRange.end)
- setPosition: (point) ->
- point = Point.fromObject(point)
- @point = @editor.clipPosition(point)
- @goalColumn = null
- @updateAppearance()
- @trigger 'cursor:position-changed'
+ setPosition: (point) ->
+ point = Point.fromObject(point)
+ @point = @editor.clipPosition(point)
+ @goalColumn = null
+ @updateAppearance()
+ @trigger 'cursor:position-changed'
- @removeClass 'idle'
- window.clearTimeout(@idleTimeout) if @idleTimeout
- @idleTimeout = window.setTimeout (=> @addClass 'idle'), 200
+ @removeClass 'idle'
+ window.clearTimeout(@idleTimeout) if @idleTimeout
+ @idleTimeout = window.setTimeout (=> @addClass 'idle'), 200
- getPosition: -> _.clone(@point)
+ getPosition: -> _.clone(@point)
- setColumn: (column) ->
- { row } = @getPosition()
- @setPosition {row, column}
+ setColumn: (column) ->
+ { row } = @getPosition()
+ @setPosition {row, column}
- getColumn: ->
- @getPosition().column
+ getColumn: ->
+ @getPosition().column
- getRow: ->
- @getPosition().row
+ getRow: ->
+ @getPosition().row
- moveUp: ->
- { row, column } = @getPosition()
- column = @goalColumn if @goalColumn?
- if row > 0
- @setPosition({row: row - 1, column: column})
- else
- @moveToLineStart()
+ moveUp: ->
+ { row, column } = @getPosition()
+ column = @goalColumn if @goalColumn?
+ if row > 0
+ @setPosition({row: row - 1, column: column})
+ else
+ @moveToLineStart()
- @goalColumn = column
+ @goalColumn = column
- moveDown: ->
- { row, column } = @getPosition()
- column = @goalColumn if @goalColumn?
- if row < @editor.buffer.numLines() - 1
- @setPosition({row: row + 1, column: column})
- else
- @moveToLineEnd()
+ moveDown: ->
+ { row, column } = @getPosition()
+ column = @goalColumn if @goalColumn?
+ if row < @editor.buffer.numLines() - 1
+ @setPosition({row: row + 1, column: column})
+ else
+ @moveToLineEnd()
- @goalColumn = column
+ @goalColumn = column
- moveToLineEnd: ->
- { row } = @getPosition()
- @setPosition({ row, column: @editor.buffer.getLine(row).length })
+ moveToLineEnd: ->
+ { row } = @getPosition()
+ @setPosition({ row, column: @editor.buffer.getLine(row).length })
- moveToLineStart: ->
- { row } = @getPosition()
- @setPosition({ row, column: 0 })
+ moveToLineStart: ->
+ { row } = @getPosition()
+ @setPosition({ row, column: 0 })
- moveRight: ->
- { row, column } = @getPosition()
- if column < @editor.buffer.getLine(row).length
- column++
- else if row < @editor.buffer.numLines() - 1
- row++
- column = 0
- @setPosition({row, column})
+ moveRight: ->
+ { row, column } = @getPosition()
+ if column < @editor.buffer.getLine(row).length
+ column++
+ else if row < @editor.buffer.numLines() - 1
+ row++
+ column = 0
+ @setPosition({row, column})
- moveLeft: ->
- { row, column } = @getPosition()
- if column > 0
- column--
- else if row > 0
- row--
- column = @editor.buffer.getLine(row).length
+ moveLeft: ->
+ { row, column } = @getPosition()
+ if column > 0
+ column--
+ else if row > 0
+ row--
+ column = @editor.buffer.getLine(row).length
- @setPosition({row, column})
+ @setPosition({row, column})
- updateAppearance: ->
- position = @editor.pixelPositionFromPoint(@point)
- @css(position)
- @autoScrollVertically(position)
- @autoScrollHorizontally(position)
+ updateAppearance: ->
+ position = @editor.pixelPositionFromPoint(@point)
+ @css(position)
+ @autoScrollVertically(position)
+ @autoScrollHorizontally(position)
- autoScrollVertically: (position) ->
- linesInView = @editor.height() / @height()
- maxScrollMargin = Math.floor((linesInView - 1) / 2)
- scrollMargin = Math.min(@editor.vScrollMargin, maxScrollMargin)
- margin = scrollMargin * @height()
- desiredTop = position.top - margin
- desiredBottom = position.top + @height() + margin
+ autoScrollVertically: (position) ->
+ linesInView = @editor.height() / @height()
+ maxScrollMargin = Math.floor((linesInView - 1) / 2)
+ scrollMargin = Math.min(@editor.vScrollMargin, maxScrollMargin)
+ margin = scrollMargin * @height()
+ desiredTop = position.top - margin
+ desiredBottom = position.top + @height() + margin
- if desiredBottom > @editor.scrollBottom()
- @editor.scrollBottom(desiredBottom)
- else if desiredTop < @editor.scrollTop()
- @editor.scrollTop(desiredTop)
+ if desiredBottom > @editor.scrollBottom()
+ @editor.scrollBottom(desiredBottom)
+ else if desiredTop < @editor.scrollTop()
+ @editor.scrollTop(desiredTop)
- autoScrollHorizontally: (position) ->
- charsInView = @editor.width() / @width()
- maxScrollMargin = Math.floor((charsInView - 1) / 2)
- scrollMargin = Math.min(@editor.hScrollMargin, maxScrollMargin)
- margin = scrollMargin * @width()
- desiredRight = position.left + @width() + margin
- desiredLeft = position.left - margin
+ autoScrollHorizontally: (position) ->
+ charsInView = @editor.width() / @width()
+ maxScrollMargin = Math.floor((charsInView - 1) / 2)
+ scrollMargin = Math.min(@editor.hScrollMargin, maxScrollMargin)
+ margin = scrollMargin * @width()
+ desiredRight = position.left + @width() + margin
+ desiredLeft = position.left - margin
- if desiredRight > @editor.scrollRight()
- @editor.scrollRight(desiredRight)
- else if desiredLeft < @editor.scrollLeft()
- @editor.scrollLeft(desiredLeft)
+ if desiredRight > @editor.scrollRight()
+ @editor.scrollRight(desiredRight)
+ else if desiredLeft < @editor.scrollLeft()
+ @editor.scrollLeft(desiredLeft)
diff --git a/src/atom/editor.coffee b/src/atom/editor.coffee
index 0384eda2f..0e80de218 100644
--- a/src/atom/editor.coffee
+++ b/src/atom/editor.coffee
@@ -1,4 +1,4 @@
-Template = require 'template'
+{View} = require 'space-pen'
Buffer = require 'buffer'
Point = require 'point'
Cursor = require 'cursor'
@@ -11,224 +11,223 @@ $$ = require 'template/builder'
_ = require 'underscore'
module.exports =
-class Editor extends Template
- content: ->
+class Editor extends View
+ @content: ->
@div class: 'editor', tabindex: -1, =>
@div outlet: 'lines'
@input class: 'hidden-input', outlet: 'hiddenInput'
- viewProperties:
- vScrollMargin: 2
- hScrollMargin: 10
- cursor: null
- buffer: null
- selection: null
- lineHeight: null
- charWidth: null
+ vScrollMargin: 2
+ hScrollMargin: 10
+ cursor: null
+ buffer: null
+ selection: null
+ lineHeight: null
+ charWidth: null
- initialize: () ->
- requireStylesheet 'editor.css'
- requireStylesheet 'theme/twilight.css'
- @bindKeys()
- @buildCursorAndSelection()
- @handleEvents()
- @setBuffer(new Buffer)
+ initialize: () ->
+ requireStylesheet 'editor.css'
+ requireStylesheet 'theme/twilight.css'
+ @bindKeys()
+ @buildCursorAndSelection()
+ @handleEvents()
+ @setBuffer(new Buffer)
- bindKeys: ->
- atom.bindKeys '*',
- right: 'move-right'
- left: 'move-left'
- down: 'move-down'
- up: 'move-up'
- 'shift-right': 'select-right'
- 'shift-left': 'select-left'
- 'shift-up': 'select-up'
- 'shift-down': 'select-down'
- enter: 'newline'
- backspace: 'delete-left'
- delete: 'delete-right'
- 'meta-x': 'cut'
- 'meta-c': 'copy'
- 'meta-v': 'paste'
+ bindKeys: ->
+ atom.bindKeys '*',
+ right: 'move-right'
+ left: 'move-left'
+ down: 'move-down'
+ up: 'move-up'
+ 'shift-right': 'select-right'
+ 'shift-left': 'select-left'
+ 'shift-up': 'select-up'
+ 'shift-down': 'select-down'
+ enter: 'newline'
+ backspace: 'delete-left'
+ delete: 'delete-right'
+ 'meta-x': 'cut'
+ 'meta-c': 'copy'
+ 'meta-v': 'paste'
- @on 'move-right', => @moveCursorRight()
- @on 'move-left', => @moveCursorLeft()
- @on 'move-down', => @moveCursorDown()
- @on 'move-up', => @moveCursorUp()
- @on 'select-right', => @selectRight()
- @on 'select-left', => @selectLeft()
- @on 'select-up', => @selectUp()
- @on 'select-down', => @selectDown()
- @on 'newline', => @insertNewline()
- @on 'delete-left', => @deleteLeft()
- @on 'delete-right', => @deleteRight()
- @on 'cut', => @cutSelection()
- @on 'copy', => @copySelection()
- @on 'paste', => @paste()
+ @on 'move-right', => @moveCursorRight()
+ @on 'move-left', => @moveCursorLeft()
+ @on 'move-down', => @moveCursorDown()
+ @on 'move-up', => @moveCursorUp()
+ @on 'select-right', => @selectRight()
+ @on 'select-left', => @selectLeft()
+ @on 'select-up', => @selectUp()
+ @on 'select-down', => @selectDown()
+ @on 'newline', => @insertNewline()
+ @on 'delete-left', => @deleteLeft()
+ @on 'delete-right', => @deleteRight()
+ @on 'cut', => @cutSelection()
+ @on 'copy', => @copySelection()
+ @on 'paste', => @paste()
- buildCursorAndSelection: ->
- @cursor = Cursor.build(this)
- @append(@cursor)
+ buildCursorAndSelection: ->
+ @cursor = new Cursor(this)
+ @append(@cursor)
- @selection = Selection.build(this)
- @append(@selection)
+ @selection = new Selection(this)
+ @append(@selection)
- handleEvents: ->
- @on 'focus', =>
- @hiddenInput.focus()
- false
+ handleEvents: ->
+ @on 'focus', =>
+ @hiddenInput.focus()
+ false
- @on 'mousedown', (e) =>
- clickCount = e.originalEvent.detail
+ @on 'mousedown', (e) =>
+ clickCount = e.originalEvent.detail
- if clickCount == 1
- @setCursorPosition @pointFromMouseEvent(e)
- @selectTextOnMouseMovement()
- else if clickCount == 2
- @selection.selectWord()
- @selectTextOnMouseMovement()
+ if clickCount == 1
+ @setCursorPosition @pointFromMouseEvent(e)
+ @selectTextOnMouseMovement()
+ else if clickCount == 2
+ @selection.selectWord()
+ @selectTextOnMouseMovement()
- @hiddenInput.on "textInput", (e) =>
- @insertText(e.originalEvent.data)
+ @hiddenInput.on "textInput", (e) =>
+ @insertText(e.originalEvent.data)
- @on 'cursor:position-changed', =>
- @hiddenInput.css(@pixelPositionFromPoint(@cursor.getPosition()))
+ @on 'cursor:position-changed', =>
+ @hiddenInput.css(@pixelPositionFromPoint(@cursor.getPosition()))
- @one 'attach', =>
- @calculateDimensions()
- @hiddenInput.width(@charWidth)
- @focus()
+ @one 'attach', =>
+ @calculateDimensions()
+ @hiddenInput.width(@charWidth)
+ @focus()
- selectTextOnMouseMovement: ->
- moveHandler = (e) => @selectToPosition(@pointFromMouseEvent(e))
- @on 'mousemove', moveHandler
- $(document).one 'mouseup', => @off 'mousemove', moveHandler
+ selectTextOnMouseMovement: ->
+ moveHandler = (e) => @selectToPosition(@pointFromMouseEvent(e))
+ @on 'mousemove', moveHandler
+ $(document).one 'mouseup', => @off 'mousemove', moveHandler
- buildLineElement: (row) ->
- tokens = @highlighter.tokensForRow(row)
- $$.pre class: 'line', ->
- if tokens.length
- for token in tokens
- classes = token.type.split('.').map((c) -> "ace_#{c}").join(' ')
- @span { class: token.type.replace('.', ' ') }, token.value
- else
- @raw ' '
-
- setBuffer: (@buffer) ->
- @highlighter = new Highlighter(@buffer)
-
- @lines.empty()
- for row in [0..@buffer.lastRow()]
- line = @buildLineElement(row)
- @lines.append line
-
- @setCursorPosition(row: 0, column: 0)
-
- @buffer.on 'change', (e) =>
- @cursor.bufferChanged(e)
-
- @highlighter.on 'change', (e) =>
- { preRange, postRange } = e
-
- if postRange.end.row > preRange.end.row
- # update, then insert elements
- for row in [preRange.start.row..postRange.end.row]
- if row <= preRange.end.row
- @updateLineElement(row)
- else
- @insertLineElement(row)
- else
- # traverse in reverse... remove, then update elements
- for row in [preRange.end.row..preRange.start.row]
- if row > postRange.end.row
- @removeLineElement(row)
- else
- @updateLineElement(row)
-
- updateLineElement: (row) ->
- @getLineElement(row).replaceWith(@buildLineElement(row))
-
- insertLineElement: (row) ->
- @getLineElement(row).before(@buildLineElement(row))
-
- removeLineElement: (row) ->
- @getLineElement(row).remove()
-
- getLineElement: (row) ->
- @lines.find("pre.line:eq(#{row})")
-
- clipPosition: ({row, column}) ->
- row = Math.min(Math.max(0, row), @buffer.numLines() - 1)
- column = Math.min(Math.max(0, column), @buffer.getLine(row).length)
- new Point(row, column)
-
- pixelPositionFromPoint: ({row, column}) ->
- { top: row * @lineHeight, left: column * @charWidth }
-
- pointFromPixelPosition: ({top, left}) ->
- { row: Math.floor(top / @lineHeight), column: Math.floor(left / @charWidth) }
-
- pointFromMouseEvent: (e) ->
- { pageX, pageY } = e
- @pointFromPixelPosition
- top: pageY - @lines.offset().top
- left: pageX - @lines.offset().left
-
- calculateDimensions: ->
- fragment = $('
x
')
- @lines.append(fragment)
- @charWidth = fragment.width()
- @lineHeight = fragment.outerHeight()
- fragment.remove()
-
- scrollBottom: (newValue) ->
- if newValue?
- @scrollTop(newValue - @height())
+ buildLineElement: (row) ->
+ tokens = @highlighter.tokensForRow(row)
+ $$.pre class: 'line', ->
+ if tokens.length
+ for token in tokens
+ classes = token.type.split('.').map((c) -> "ace_#{c}").join(' ')
+ @span { class: token.type.replace('.', ' ') }, token.value
else
- @scrollTop() + @height()
+ @raw ' '
- scrollRight: (newValue) ->
- if newValue?
- @scrollLeft(newValue - @width())
+ setBuffer: (@buffer) ->
+ @highlighter = new Highlighter(@buffer)
+
+ @lines.empty()
+ for row in [0..@buffer.lastRow()]
+ line = @buildLineElement(row)
+ @lines.append line
+
+ @setCursorPosition(row: 0, column: 0)
+
+ @buffer.on 'change', (e) =>
+ @cursor.bufferChanged(e)
+
+ @highlighter.on 'change', (e) =>
+ { preRange, postRange } = e
+
+ if postRange.end.row > preRange.end.row
+ # update, then insert elements
+ for row in [preRange.start.row..postRange.end.row]
+ if row <= preRange.end.row
+ @updateLineElement(row)
+ else
+ @insertLineElement(row)
else
- @scrollLeft() + @width()
+ # traverse in reverse... remove, then update elements
+ for row in [preRange.end.row..preRange.start.row]
+ if row > postRange.end.row
+ @removeLineElement(row)
+ else
+ @updateLineElement(row)
- getCursor: -> @cursor
- getSelection: -> @selection
+ updateLineElement: (row) ->
+ @getLineElement(row).replaceWith(@buildLineElement(row))
- getCurrentLine: -> @buffer.getLine(@getCursorRow())
- getSelectedText: -> @selection.getText()
- moveCursorUp: -> @cursor.moveUp()
- moveCursorDown: -> @cursor.moveDown()
- moveCursorRight: -> @cursor.moveRight()
- moveCursorLeft: -> @cursor.moveLeft()
- setCursorPosition: (point) -> @cursor.setPosition(point)
- getCursorPosition: -> @cursor.getPosition()
- setCursorRow: (row) -> @cursor.setRow(row)
- getCursorRow: -> @cursor.getRow()
- setCursorColumn: (column) -> @cursor.setColumn(column)
- getCursorColumn: -> @cursor.getColumn()
+ insertLineElement: (row) ->
+ @getLineElement(row).before(@buildLineElement(row))
- selectRight: -> @selection.selectRight()
- selectLeft: -> @selection.selectLeft()
- selectUp: -> @selection.selectUp()
- selectDown: -> @selection.selectDown()
- selectToPosition: (position) ->
- @selection.selectToPosition(position)
+ removeLineElement: (row) ->
+ @getLineElement(row).remove()
- insertText: (text) -> @selection.insertText(text)
- insertNewline: -> @selection.insertNewline()
+ getLineElement: (row) ->
+ @lines.find("pre.line:eq(#{row})")
- cutSelection: -> @selection.cut()
- copySelection: -> @selection.copy()
- paste: -> @selection.insertText(atom.native.readFromPasteboard())
+ clipPosition: ({row, column}) ->
+ row = Math.min(Math.max(0, row), @buffer.numLines() - 1)
+ column = Math.min(Math.max(0, column), @buffer.getLine(row).length)
+ new Point(row, column)
- deleteLeft: ->
- @selectLeft() if @selection.isEmpty()
- @selection.delete()
+ pixelPositionFromPoint: ({row, column}) ->
+ { top: row * @lineHeight, left: column * @charWidth }
- deleteRight: ->
- @selectRight() if @selection.isEmpty()
- @selection.delete()
+ pointFromPixelPosition: ({top, left}) ->
+ { row: Math.floor(top / @lineHeight), column: Math.floor(left / @charWidth) }
+
+ pointFromMouseEvent: (e) ->
+ { pageX, pageY } = e
+ @pointFromPixelPosition
+ top: pageY - @lines.offset().top
+ left: pageX - @lines.offset().left
+
+ calculateDimensions: ->
+ fragment = $('
x
')
+ @lines.append(fragment)
+ @charWidth = fragment.width()
+ @lineHeight = fragment.outerHeight()
+ fragment.remove()
+
+ scrollBottom: (newValue) ->
+ if newValue?
+ @scrollTop(newValue - @height())
+ else
+ @scrollTop() + @height()
+
+ scrollRight: (newValue) ->
+ if newValue?
+ @scrollLeft(newValue - @width())
+ else
+ @scrollLeft() + @width()
+
+ getCursor: -> @cursor
+ getSelection: -> @selection
+
+ getCurrentLine: -> @buffer.getLine(@getCursorRow())
+ getSelectedText: -> @selection.getText()
+ moveCursorUp: -> @cursor.moveUp()
+ moveCursorDown: -> @cursor.moveDown()
+ moveCursorRight: -> @cursor.moveRight()
+ moveCursorLeft: -> @cursor.moveLeft()
+ setCursorPosition: (point) -> @cursor.setPosition(point)
+ getCursorPosition: -> @cursor.getPosition()
+ setCursorRow: (row) -> @cursor.setRow(row)
+ getCursorRow: -> @cursor.getRow()
+ setCursorColumn: (column) -> @cursor.setColumn(column)
+ getCursorColumn: -> @cursor.getColumn()
+
+ selectRight: -> @selection.selectRight()
+ selectLeft: -> @selection.selectLeft()
+ selectUp: -> @selection.selectUp()
+ selectDown: -> @selection.selectDown()
+ selectToPosition: (position) ->
+ @selection.selectToPosition(position)
+
+ insertText: (text) -> @selection.insertText(text)
+ insertNewline: -> @selection.insertNewline()
+
+ cutSelection: -> @selection.cut()
+ copySelection: -> @selection.copy()
+ paste: -> @selection.insertText(atom.native.readFromPasteboard())
+
+ deleteLeft: ->
+ @selectLeft() if @selection.isEmpty()
+ @selection.delete()
+
+ deleteRight: ->
+ @selectRight() if @selection.isEmpty()
+ @selection.delete()
diff --git a/src/atom/file-finder.coffee b/src/atom/file-finder.coffee
index fbb181d37..15a98c4b1 100644
--- a/src/atom/file-finder.coffee
+++ b/src/atom/file-finder.coffee
@@ -1,67 +1,66 @@
$ = require 'jquery'
-Template = require 'template'
+{View} = require 'space-pen'
stringScore = require 'stringscore'
module.exports =
-class FileFinder extends Template
- content: ->
+class FileFinder extends View
+ @content: ->
@div class: 'file-finder', =>
@link rel: 'stylesheet', href: "#{require.resolve('file-finder.css')}?#{(new Date).getTime()}"
@ol outlet: 'urlList'
@input outlet: 'input', input: 'populateUrlList'
- viewProperties:
- urls: null
- maxResults: null
+ urls: null
+ maxResults: null
- initialize: ({@urls, @selected}) ->
- @maxResults = 10
+ initialize: ({@urls, @selected}) ->
+ @maxResults = 10
- @populateUrlList()
- atom.bindKeys ".file-finder",
- 'up': 'move-up'
- 'down': 'move-down'
- 'enter': 'select'
+ @populateUrlList()
+ atom.bindKeys ".file-finder",
+ 'up': 'move-up'
+ 'down': 'move-down'
+ 'enter': 'select'
- @on 'move-up', => @moveUp()
- @on 'move-down', => @moveDown()
- @on 'select', => @select()
+ @on 'move-up', => @moveUp()
+ @on 'move-down', => @moveDown()
+ @on 'select', => @select()
- populateUrlList: ->
- @urlList.empty()
- for url in @findMatches(@input.val())
- @urlList.append $("
#{url}")
+ populateUrlList: ->
+ @urlList.empty()
+ for url in @findMatches(@input.val())
+ @urlList.append $("
#{url}")
- @urlList.children('li:first').addClass 'selected'
+ @urlList.children('li:first').addClass 'selected'
- findSelectedLi: ->
- @urlList.children('li.selected')
+ findSelectedLi: ->
+ @urlList.children('li.selected')
- select: ->
- filePath = @findSelectedLi().text()
- @selected(filePath) if filePath and @selected
- @remove()
+ select: ->
+ filePath = @findSelectedLi().text()
+ @selected(filePath) if filePath and @selected
+ @remove()
- moveUp: ->
- @findSelectedLi()
- .filter(':not(:first-child)')
- .removeClass('selected')
- .prev()
- .addClass('selected')
+ moveUp: ->
+ @findSelectedLi()
+ .filter(':not(:first-child)')
+ .removeClass('selected')
+ .prev()
+ .addClass('selected')
- moveDown: ->
- @findSelectedLi()
- .filter(':not(:last-child)')
- .removeClass('selected')
- .next()
- .addClass('selected')
+ moveDown: ->
+ @findSelectedLi()
+ .filter(':not(:last-child)')
+ .removeClass('selected')
+ .next()
+ .addClass('selected')
- findMatches: (query) ->
- if not query
- urls = @urls
- else
- scoredUrls = ({url, score: stringScore(url, query)} for url in @urls)
- scoredUrls.sort (a, b) -> a.score > b.score
- urls = (urlAndScore.url for urlAndScore in scoredUrls when urlAndScore.score > 0)
+ findMatches: (query) ->
+ if not query
+ urls = @urls
+ else
+ scoredUrls = ({url, score: stringScore(url, query)} for url in @urls)
+ scoredUrls.sort (a, b) -> a.score > b.score
+ urls = (urlAndScore.url for urlAndScore in scoredUrls when urlAndScore.score > 0)
- urls.slice 0, @maxResults
+ urls.slice 0, @maxResults
diff --git a/src/atom/root-view.coffee b/src/atom/root-view.coffee
index 24855b14d..fb7b90fd7 100644
--- a/src/atom/root-view.coffee
+++ b/src/atom/root-view.coffee
@@ -2,7 +2,7 @@ $ = require 'jquery'
fs = require 'fs'
_ = require 'underscore'
-Template = require 'template'
+{View} = require 'space-pen'
Buffer = require 'buffer'
Editor = require 'editor'
FileFinder = require 'file-finder'
@@ -11,58 +11,57 @@ GlobalKeymap = require 'global-keymap'
VimMode = require 'vim-mode'
module.exports =
-class RootView extends Template
- content: ->
+class RootView extends View
+ @content: ->
@div id: 'app-horizontal', =>
@div id: 'app-vertical', outlet: 'vertical', =>
@div id: 'main', outlet: 'main', =>
- @subview 'editor', Editor.build()
+ @subview 'editor', new Editor
- viewProperties:
- globalKeymap: null
+ globalKeymap: null
- initialize: ({url}) ->
- @editor.keyEventHandler = atom.globalKeymap
- @createProject(url)
+ initialize: ({url}) ->
+ @editor.keyEventHandler = atom.globalKeymap
+ @createProject(url)
- atom.bindKeys '*'
- 'meta-s': 'save'
- 'meta-w': 'close'
- 'meta-t': 'toggle-file-finder'
- 'alt-meta-i': 'show-console'
+ atom.bindKeys '*'
+ 'meta-s': 'save'
+ 'meta-w': 'close'
+ 'meta-t': 'toggle-file-finder'
+ 'alt-meta-i': 'show-console'
- @on 'toggle-file-finder', => @toggleFileFinder()
- @on 'show-console', -> window.showConsole()
+ @on 'toggle-file-finder', => @toggleFileFinder()
+ @on 'show-console', -> window.showConsole()
- @on 'focusout', (e) =>
- # if anything but the editor and its input loses focus, restore focus to the editor
- unless $(e.target).closest('.editor').length
- @editor.focus()
+ @on 'focusout', (e) =>
+ # if anything but the editor and its input loses focus, restore focus to the editor
+ unless $(e.target).closest('.editor').length
+ @editor.focus()
- createProject: (url) ->
- if url
- @project = new Project(fs.directory(url))
- @editor.setBuffer(@project.open(url)) if fs.isFile(url)
+ createProject: (url) ->
+ if url
+ @project = new Project(fs.directory(url))
+ @editor.setBuffer(@project.open(url)) if fs.isFile(url)
- bindKeys: (selector, bindings) ->
- @globalKeymap.bindKeys(selector, bindings)
+ bindKeys: (selector, bindings) ->
+ @globalKeymap.bindKeys(selector, bindings)
- addPane: (view) ->
- pane = $('
')
- pane.append(view)
- @main.after(pane)
+ addPane: (view) ->
+ pane = $('
')
+ pane.append(view)
+ @main.after(pane)
- toggleFileFinder: ->
- return unless @project
+ toggleFileFinder: ->
+ return unless @project
- if @fileFinder and @fileFinder.parent()[0]
- @fileFinder.remove()
- @fileFinder = null
- else
- @project.getFilePaths().done (paths) =>
- relativePaths = (path.replace(@project.url, "") for path in paths)
- @fileFinder = FileFinder.build
- urls: relativePaths
- selected: (relativePath) => @editor.setBuffer(@project.open(relativePath))
- @addPane @fileFinder
- @fileFinder.input.focus()
+ if @fileFinder and @fileFinder.parent()[0]
+ @fileFinder.remove()
+ @fileFinder = null
+ else
+ @project.getFilePaths().done (paths) =>
+ relativePaths = (path.replace(@project.url, "") for path in paths)
+ @fileFinder = new FileFinder
+ urls: relativePaths
+ selected: (relativePath) => @editor.setBuffer(@project.open(relativePath))
+ @addPane @fileFinder
+ @fileFinder.input.focus()
diff --git a/src/atom/selection.coffee b/src/atom/selection.coffee
index 52c12c731..5a8767f35 100644
--- a/src/atom/selection.coffee
+++ b/src/atom/selection.coffee
@@ -1,150 +1,149 @@
Cursor = require 'cursor'
Range = require 'range'
-Template = require 'template'
+{View} = require 'space-pen'
$$ = require 'template/builder'
module.exports =
-class Selection extends Template
- content: ->
+class Selection extends View
+ @content: ->
@div()
- viewProperties:
- anchor: null
- modifyingSelection: null
- regions: null
+ anchor: null
+ modifyingSelection: null
+ regions: null
- initialize: (@editor) ->
- @regions = []
- @cursor = @editor.cursor
- @cursor.on 'cursor:position-changed', =>
- if @modifyingSelection
- @updateAppearance()
- else
- @clearSelection()
-
- clearSelection: ->
- @anchor = null
- @updateAppearance()
-
- updateAppearance: ->
- @clearRegions()
-
- range = @getRange()
- return if range.isEmpty()
-
- rowSpan = range.end.row - range.start.row
-
- if rowSpan == 0
- @appendRegion(1, range.start, range.end)
+ initialize: (@editor) ->
+ @regions = []
+ @cursor = @editor.cursor
+ @cursor.on 'cursor:position-changed', =>
+ if @modifyingSelection
+ @updateAppearance()
else
- @appendRegion(1, range.start, null)
- if rowSpan > 1
- @appendRegion(rowSpan - 1, { row: range.start.row + 1, column: 0}, null)
- @appendRegion(1, { row: range.end.row, column: 0 }, range.end)
+ @clearSelection()
- appendRegion: (rows, start, end) ->
- { lineHeight, charWidth } = @editor
- css = {}
- css.top = start.row * lineHeight
- css.left = start.column * charWidth
- css.height = lineHeight * rows
- if end
- css.width = end.column * charWidth - css.left
- else
- css.right = 0
+ clearSelection: ->
+ @anchor = null
+ @updateAppearance()
- region = $$.div(class: 'selection').css(css)
- @append(region)
- @regions.push(region)
+ updateAppearance: ->
+ @clearRegions()
- clearRegions: ->
- region.remove() for region in @regions
- @regions = []
+ range = @getRange()
+ return if range.isEmpty()
- getRange: ->
- if @anchor
- new Range(@anchor.getPosition(), @cursor.getPosition())
- else
- new Range(@cursor.getPosition(), @cursor.getPosition())
+ rowSpan = range.end.row - range.start.row
- setRange: (range) ->
- @cursor.setPosition(range.start)
- @modifySelection =>
- @cursor.setPosition(range.end)
+ if rowSpan == 0
+ @appendRegion(1, range.start, range.end)
+ else
+ @appendRegion(1, range.start, null)
+ if rowSpan > 1
+ @appendRegion(rowSpan - 1, { row: range.start.row + 1, column: 0}, null)
+ @appendRegion(1, { row: range.end.row, column: 0 }, range.end)
- getText: ->
- @editor.buffer.getTextInRange @getRange()
+ appendRegion: (rows, start, end) ->
+ { lineHeight, charWidth } = @editor
+ css = {}
+ css.top = start.row * lineHeight
+ css.left = start.column * charWidth
+ css.height = lineHeight * rows
+ if end
+ css.width = end.column * charWidth - css.left
+ else
+ css.right = 0
- insertText: (text) ->
- @editor.buffer.change(@getRange(), text)
+ region = $$.div(class: 'selection').css(css)
+ @append(region)
+ @regions.push(region)
- insertNewline: ->
- @insertText('\n')
+ clearRegions: ->
+ region.remove() for region in @regions
+ @regions = []
- delete: ->
- range = @getRange()
- @editor.buffer.change(range, '') unless range.isEmpty()
+ getRange: ->
+ if @anchor
+ new Range(@anchor.getPosition(), @cursor.getPosition())
+ else
+ new Range(@cursor.getPosition(), @cursor.getPosition())
- isEmpty: ->
- @getRange().isEmpty()
+ setRange: (range) ->
+ @cursor.setPosition(range.start)
+ @modifySelection =>
+ @cursor.setPosition(range.end)
- modifySelection: (fn) ->
- @placeAnchor()
- @modifyingSelection = true
- fn()
- @modifyingSelection = false
+ getText: ->
+ @editor.buffer.getTextInRange @getRange()
- placeAnchor: ->
- return if @anchor
- cursorPosition = @cursor.getPosition()
- @anchor = { getPosition: -> cursorPosition }
+ insertText: (text) ->
+ @editor.buffer.change(@getRange(), text)
- selectWord: ->
- row = @cursor.getRow()
- column = @cursor.getColumn()
+ insertNewline: ->
+ @insertText('\n')
- line = @editor.buffer.getLine(row)
- leftSide = line[0...column].split('').reverse().join('') # reverse left side
- rightSide = line[column..]
+ delete: ->
+ range = @getRange()
+ @editor.buffer.change(range, '') unless range.isEmpty()
- regex = /^\w*/
- startOffset = -regex.exec(leftSide)?[0]?.length or 0
- endOffset = regex.exec(rightSide)?[0]?.length or 0
+ isEmpty: ->
+ @getRange().isEmpty()
- range = new Range([row, column + startOffset], [row, column + endOffset])
- @setRange range
+ modifySelection: (fn) ->
+ @placeAnchor()
+ @modifyingSelection = true
+ fn()
+ @modifyingSelection = false
- selectRight: ->
- @modifySelection =>
- @cursor.moveRight()
+ placeAnchor: ->
+ return if @anchor
+ cursorPosition = @cursor.getPosition()
+ @anchor = { getPosition: -> cursorPosition }
- selectLeft: ->
- @modifySelection =>
- @cursor.moveLeft()
+ selectWord: ->
+ row = @cursor.getRow()
+ column = @cursor.getColumn()
- selectUp: ->
- @modifySelection =>
- @cursor.moveUp()
+ line = @editor.buffer.getLine(row)
+ leftSide = line[0...column].split('').reverse().join('') # reverse left side
+ rightSide = line[column..]
- selectDown: ->
- @modifySelection =>
- @cursor.moveDown()
+ regex = /^\w*/
+ startOffset = -regex.exec(leftSide)?[0]?.length or 0
+ endOffset = regex.exec(rightSide)?[0]?.length or 0
- selectToPosition: (position) ->
- @modifySelection =>
- @cursor.setPosition(position)
+ range = new Range([row, column + startOffset], [row, column + endOffset])
+ @setRange range
- moveCursorToLineEnd: ->
- @cursor.moveToLineEnd()
+ selectRight: ->
+ @modifySelection =>
+ @cursor.moveRight()
- moveCursorToLineStart: ->
- @cursor.moveToLineStart()
+ selectLeft: ->
+ @modifySelection =>
+ @cursor.moveLeft()
- cut: ->
- @copy()
- @delete()
+ selectUp: ->
+ @modifySelection =>
+ @cursor.moveUp()
- copy: ->
- return if @isEmpty()
- text = @editor.buffer.getTextInRange @getRange()
- atom.native.writeToPasteboard text
+ selectDown: ->
+ @modifySelection =>
+ @cursor.moveDown()
+
+ selectToPosition: (position) ->
+ @modifySelection =>
+ @cursor.setPosition(position)
+
+ moveCursorToLineEnd: ->
+ @cursor.moveToLineEnd()
+
+ moveCursorToLineStart: ->
+ @cursor.moveToLineStart()
+
+ cut: ->
+ @copy()
+ @delete()
+
+ copy: ->
+ return if @isEmpty()
+ text = @editor.buffer.getTextInRange @getRange()
+ atom.native.writeToPasteboard text
diff --git a/src/atom/window.coffee b/src/atom/window.coffee
index 5cf069031..0f74dbe8e 100644
--- a/src/atom/window.coffee
+++ b/src/atom/window.coffee
@@ -14,7 +14,7 @@ windowAdditions =
startup: ->
@menuItemActions = {}
- @rootView = RootView.build(url: $atomController.url?.toString())
+ @rootView = new RootView(url: $atomController.url?.toString())
$('body').append @rootView
@registerEventHandlers()
@bindMenuItems()
diff --git a/src/stdlib/template.coffee b/src/stdlib/template.coffee
deleted file mode 100644
index e2587e606..000000000
--- a/src/stdlib/template.coffee
+++ /dev/null
@@ -1,76 +0,0 @@
-$ = require 'jquery'
-_ = require 'underscore'
-Builder = require 'template/builder'
-
-module.exports =
-class Template
- @events: 'blur change click dblclick error focus input keydown
- keypress keyup load mousedown mousemove mouseout mouseover
- mouseup resize scroll select submit unload'.split /\s+/
-
- @buildTagMethod: (name) ->
- this.prototype[name] = (args...) -> @builder.tag(name, args...)
-
- @buildTagMethod(name) for name in Builder.elements.normal
- @buildTagMethod(name) for name in Builder.elements.void
-
- @build: (attributes) ->
- (new this).build(attributes)
-
- @toHtml: (attributes) ->
- (new this).toHtml(attributes)
-
- build: (attributes={}) ->
- @builder = new Builder
- @content(attributes)
- view = @builder.toFragment()
- @bindEvents(view)
- if @viewProperties
- $.extend(view, @viewProperties)
- view.attr('triggerAttachEvents', true)
- view.initialize?(attributes)
- view
-
- toHtml: (attributes) ->
- @builder = new Builder
- @content(attributes)
- @builder.toHtml()
-
- subview: (args...) ->
- @builder.subview.apply(@builder, args)
-
- raw: (text) ->
- @builder.raw(text)
-
- bindEvents: (view) ->
- for eventName in this.constructor.events
- selector = "[#{eventName}]"
- elements = view.find(selector).add(view.filter(selector))
-
- elements.each ->
- elt = $(this)
- methodName = elt.attr(eventName)
- elt.on eventName, (event) -> view[methodName](event, elt)
-
-$.fn.view = ->
- this.data('view')
-
-# Trigger attach event when views are added to the DOM
-triggerAttachEvent = (elt) ->
- if elt.attr?('triggerAttachEvents') and elt.parents('html').length
- elt.find('[triggerAttachEvents]').add(elt).trigger('attach')
-
-_.each ['append', 'prepend', 'after', 'before'], (methodName) ->
- originalMethod = $.fn[methodName]
- $.fn[methodName] = (args...) ->
- result = originalMethod.apply(this, args)
- triggerAttachEvent(args[0])
- result
-
-_.each ['prependTo', 'appendTo', 'insertAfter', 'insertBefore'], (methodName) ->
- originalMethod = $.fn[methodName]
- $.fn[methodName] = (args...) ->
- result = originalMethod.apply(this, args)
- triggerAttachEvent(this)
- result
-
diff --git a/vendor/space-pen.coffee b/vendor/space-pen.coffee
new file mode 100644
index 000000000..648f8f5ef
--- /dev/null
+++ b/vendor/space-pen.coffee
@@ -0,0 +1,161 @@
+# Modified from 26fca5374e546fd8cc2f12d1140f915185611bdc
+# Add require 'jquery'
+$ = jQuery = require('jquery')
+
+elements =
+ 'a abbr address article aside audio b bdi bdo blockquote body button
+ canvas caption cite code colgroup datalist dd del details dfn div dl dt em
+ fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 head header hgroup
+ html i iframe ins kbd label legend li map mark menu meter nav noscript object
+ ol optgroup option output p pre progress q rp rt ruby s samp script section
+ select small span strong style sub summary sup table tbody td textarea tfoot
+ th thead time title tr u ul video area base br col command embed hr img input
+ keygen link meta param source track wbrk'.split /\s+/
+
+voidElements =
+ 'area base br col command embed hr img input keygen link meta param
+ source track wbr'.split /\s+/
+
+events =
+ 'blur change click dblclick error focus input keydown
+ keypress keyup load mousedown mousemove mouseout mouseover
+ mouseup resize scroll select submit unload'.split /\s+/
+
+idCounter = 0
+
+class View extends jQuery
+ elements.forEach (tagName) ->
+ View[tagName] = (args...) -> @builder.tag(tagName, args...)
+
+ @subview: (name, view) -> @builder.subview(name, view)
+ @text: (string) -> @builder.text(string)
+ @raw: (string) -> @builder.raw(string)
+
+ constructor: (params={}) ->
+ postProcessingSteps = @buildHtml(params)
+ @constructor = jQuery # sadly, jQuery assumes this.constructor == jQuery in pushStack
+ @wireOutlets(this)
+ @bindEventHandlers(this)
+ @find('*').andSelf().data('view', this)
+ @attr('triggerAttachEvents', true)
+ step(this) for step in postProcessingSteps
+ @initialize?(params)
+
+ buildHtml: (params) ->
+ @constructor.builder = new Builder
+ @constructor.content(params)
+ [html, postProcessingSteps] = @constructor.builder.buildHtml()
+ @constructor.builder = null
+ jQuery.fn.init.call(this, html)
+ postProcessingSteps
+
+ wireOutlets: (view) ->
+ @find('[outlet]').each ->
+ element = $(this)
+ view[element.attr('outlet')] = element
+
+ bindEventHandlers: (view) ->
+ for eventName in events
+ selector = "[#{eventName}]"
+ elements = view.find(selector).add(view.filter(selector))
+ elements.each ->
+ element = $(this)
+ methodName = element.attr(eventName)
+ element.on eventName, (event) -> view[methodName](event, element)
+
+class Builder
+ constructor: ->
+ @document = []
+ @postProcessingSteps = []
+
+ buildHtml: ->
+ [@document.join(''), @postProcessingSteps]
+
+ tag: (name, args...) ->
+ options = @extractOptions(args)
+
+ @openTag(name, options.attributes)
+
+ if name in voidElements
+ if (options.text? or options.content?)
+ throw new Error("Self-closing tag #{name} cannot have text or content")
+ else
+ options.content?()
+ @text(options.text) if options.text
+ @closeTag(name)
+
+ openTag: (name, attributes) ->
+ attributePairs =
+ for attributeName, value of attributes
+ "#{attributeName}=\"#{value}\""
+
+ attributesString =
+ if attributePairs.length
+ " " + attributePairs.join(" ")
+ else
+ ""
+
+ @document.push "<#{name}#{attributesString}>"
+
+ closeTag: (name) ->
+ @document.push "#{name}>"
+
+ text: (string) ->
+ escapedString = string
+ .replace(/&/g, '&')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''')
+ .replace(//g, '>')
+
+ @document.push escapedString
+
+ raw: (string) ->
+ @document.push string
+
+ subview: (outletName, subview) ->
+ subviewId = "subview-#{++idCounter}"
+ @tag 'div', id: subviewId
+ @postProcessingSteps.push (view) ->
+ view[outletName] = subview
+ subview.parentView = view
+ view.find("div##{subviewId}").replaceWith(subview)
+
+ extractOptions: (args) ->
+ options = {}
+ for arg in args
+ type = typeof(arg)
+ if type is "function"
+ options.content = arg
+ else if type is "string" or type is "number"
+ options.text = arg.toString()
+ else
+ options.attributes = arg
+ options
+
+jQuery.fn.view = -> this.data('view')
+
+# Trigger attach event when views are added to the DOM
+triggerAttachEvent = (element) ->
+ if element.attr?('triggerAttachEvents') and element.parents('html').length
+ element.find('[triggerAttachEvents]').add(element).trigger('attach')
+
+for methodName in ['append', 'prepend', 'after', 'before']
+ do (methodName) ->
+ originalMethod = $.fn[methodName]
+ jQuery.fn[methodName] = (args...) ->
+ result = originalMethod.apply(this, args)
+ triggerAttachEvent(args[0])
+ result
+
+for methodName in ['prependTo', 'appendTo', 'insertAfter', 'insertBefore']
+ do (methodName) ->
+ originalMethod = $.fn[methodName]
+ jQuery.fn[methodName] = (args...) ->
+ result = originalMethod.apply(this, args)
+ triggerAttachEvent(this)
+ result
+
+(exports ? this).View = View
+
+