Merge remote-tracking branch 'origin/dev' into better-anchors

This commit is contained in:
Nathan Sobo
2013-02-02 16:47:09 -07:00
98 changed files with 2051 additions and 740 deletions

View File

@@ -26,15 +26,12 @@ class AtomPackage extends Package
@metadata = fs.readObject(metadataPath)
loadKeymaps: ->
for keymapPath in @getKeymapPaths()
keymap.load(keymapPath)
getKeymapPaths: ->
if keymaps = @metadata?.keymaps
keymaps.map (relativePath) =>
keymaps = keymaps.map (relativePath) =>
fs.resolve(@keymapsDirPath, relativePath, ['cson', 'json', ''])
keymap.load(keymapPath) for keymapPath in keymaps
else
fs.list(@keymapsDirPath)
keymap.loadDirectory(@keymapsDirPath)
loadStylesheets: ->
for stylesheetPath in @getStylesheetPaths()

View File

@@ -3,9 +3,16 @@ Theme = require 'theme'
module.exports =
class AtomTheme extends Theme
loadStylesheet: (stylesheetPath)->
@stylesheets[stylesheetPath] = fs.read(stylesheetPath)
load: ->
json = fs.read(fs.join(@path, "package.json"))
for stylesheetName in JSON.parse(json).stylesheets
stylesheetPath = fs.join(@path, stylesheetName)
@stylesheets[stylesheetPath] = fs.read(stylesheetPath)
if /\.css$/.test(@path)
@loadStylesheet @path
else
json = fs.read(fs.join(@path, "package.json"))
for stylesheetName in JSON.parse(json).stylesheets
stylesheetPath = fs.join(@path, stylesheetName)
@loadStylesheet stylesheetPath
super

View File

@@ -50,15 +50,21 @@ _.extend atom,
.filter (name) -> not _.contains(disabledPackages, name)
loadThemes: ->
themeNames = config.get("core.themes") ? ['Atom - Dark', 'IR_Black']
themeNames = config.get("core.themes") ? ['atom-dark-ui', 'atom-dark-syntax']
themeNames = [themeNames] unless _.isArray(themeNames)
@loadTheme(themeName) for themeName in themeNames
@loadUserStylesheet()
loadTheme: (name) ->
@loadedThemes.push Theme.load(name)
loadUserStylesheet: ->
userStylesheetPath = fs.join(config.configDirPath, 'user.css')
if fs.isFile(userStylesheetPath)
applyStylesheet(userStylesheetPath, fs.read(userStylesheetPath), 'userTheme')
getAtomThemeStylesheets: ->
themeNames = config.get("core.themes") ? ['Atom - Dark', 'IR_Black']
themeNames = config.get("core.themes") ? ['atom-dark-ui', 'atom-dark-syntax']
themeNames = [themeNames] unless _.isArray(themeNames)
open: (args...) ->

View File

@@ -4,7 +4,7 @@ EventEmitter = require 'event-emitter'
configDirPath = fs.absolute("~/.atom")
configJsonPath = fs.join(configDirPath, "config.json")
userInitScriptPath = fs.join(configDirPath, "atom.coffee")
userInitScriptPath = fs.join(configDirPath, "user.coffee")
bundledPackagesDirPath = fs.join(resourcePath, "src/packages")
bundledThemesDirPath = fs.join(resourcePath, "themes")
vendoredPackagesDirPath = fs.join(resourcePath, "vendor/packages")

View File

@@ -18,7 +18,7 @@ class EditSession
if fs.exists(state.buffer)
session = project.buildEditSessionForPath(state.buffer)
else
console.warn "Could not build edit session for path '#{state.buffer}' because that file no longer exists"
console.warn "Could not build edit session for path '#{state.buffer}' because that file no longer exists" if state.buffer
session = project.buildEditSessionForPath(null)
session.setScrollTop(state.scrollTop)
session.setScrollLeft(state.scrollLeft)

View File

@@ -14,7 +14,6 @@ _ = require 'underscore'
module.exports =
class Editor extends View
@configDefaults:
fontFamily: "Inconsolata, Monaco, Courier"
fontSize: 20
showInvisibles: false
autosave: false
@@ -352,11 +351,11 @@ class Editor extends View
@hiddenInput.on 'focus', =>
@rootView()?.editorFocused(this)
@isFocused = true
@addClass 'focused'
@addClass 'is-focused'
@hiddenInput.on 'focusout', =>
@isFocused = false
@removeClass 'focused'
@removeClass 'is-focused'
@autosave() if config.get "editor.autosave"
@underlayer.on 'click', (e) =>
@@ -697,6 +696,7 @@ class Editor extends View
parseInt(@css("font-size"))
setFontFamily: (fontFamily) ->
return if fontFamily == undefined
headTag = $("head")
styleTag = headTag.find("style.font-family")
if styleTag.length == 0
@@ -804,6 +804,10 @@ class Editor extends View
@overlayer.append(view)
calculateDimensions: ->
if not @isOnDom()
detachedEditorParent = _.last(@parents()) ? this
$(document.body).append(detachedEditorParent)
fragment = $('<pre class="line" style="position: absolute; visibility: hidden;"><span>x</span></div>')
@renderedLines.append(fragment)
@@ -815,6 +819,8 @@ class Editor extends View
@height(@lineHeight) if @mini
fragment.remove()
$(detachedEditorParent).detach()
updateLayerDimensions: ->
@gutter.calculateWidth()

View File

@@ -1,4 +1,3 @@
$ = require 'jquery'
_ = require 'underscore'
Subscriber = require 'subscriber'
GitRepository = require 'git-repository'
@@ -6,9 +5,9 @@ GitRepository = require 'git-repository'
module.exports =
class Git
@open: (path) ->
@open: (path, options) ->
try
new Git(path)
new Git(path, options)
catch e
null
@@ -24,9 +23,12 @@ class Git
working_dir_typechange: 1 << 10
ignore: 1 << 14
constructor: (path) ->
constructor: (path, options={}) ->
@repo = new GitRepository(path)
@subscribe $(window), 'focus', => @refreshIndex()
refreshIndexOnFocus = options.refreshIndexOnFocus ? true
if refreshIndexOnFocus
$ = require 'jquery'
@subscribe $(window), 'focus', => @refreshIndex()
getRepo: ->
unless @repo?

View File

@@ -35,7 +35,7 @@ class Keymap
@loadDirectory(fs.join(config.configDirPath, 'keymaps'))
loadDirectory: (directoryPath) ->
@load(filePath) for filePath in fs.list(directoryPath)
@load(filePath) for filePath in fs.list(directoryPath, ['.cson', '.json'])
load: (path) ->
@add(fs.readObject(path))

View File

@@ -1,4 +1,4 @@
Task = require 'Task'
Task = require 'task'
module.exports =
class LoadTextMatePackagesTask extends Task

View File

@@ -44,17 +44,17 @@ class TextMateTheme extends Theme
'color': @translateColor(foreground)
@rulesets.push
selector: '.editor.focused .cursor'
selector: '.editor.is-focused .cursor'
properties:
'border-color': @translateColor(caret)
@rulesets.push
selector: '.editor.focused .selection .region'
selector: '.editor.is-focused .selection .region'
properties:
'background-color': @translateColor(selection)
@rulesets.push
selector: '.editor.focused .line-number.cursor-line-no-selection, .editor.focused .line.cursor-line'
selector: '.editor.is-focused .line-number.cursor-line-no-selection, .editor.is-focused .line.cursor-line'
properties:
'background-color': @translateColor(lineHighlight)
@@ -75,8 +75,8 @@ class TextMateTheme extends Theme
if fontStyle
fontStyles = fontStyle.split(/\s+/)
# properties['font-weight'] = 'bold' if _.contains(fontStyles, 'bold')
# properties['font-style'] = 'italic' if _.contains(fontStyles, 'italic')
properties['font-weight'] = 'bold' if _.contains(fontStyles, 'bold')
properties['font-style'] = 'italic' if _.contains(fontStyles, 'italic')
properties['text-decoration'] = 'underline' if _.contains(fontStyles, 'underline')
properties['color'] = @translateColor(foreground) if foreground

View File

@@ -11,7 +11,7 @@ class Theme
if fs.exists(name)
path = name
else
path = fs.resolve(config.themeDirPaths..., name, ['', '.tmTheme'])
path = fs.resolve(config.themeDirPaths..., name, ['', '.tmTheme', '.css'])
throw new Error("No theme exists named '#{name}'") unless path

View File

@@ -28,6 +28,7 @@ windowAdditions =
$(window).on 'core:close', => @close()
$(window).command 'window:close', => @close()
$(window).on 'focus blur', => $("body").toggleClass("is-focused")
# This method is intended only to be run when starting a normal application
# Note: RootView assigns itself on window on initialization so that

View File

@@ -6,9 +6,8 @@ describe "CommandLogger", ->
beforeEach ->
rootView = new RootView(require.resolve('fixtures/sample.js'))
atom.loadPackage('command-logger').getInstance()
commandLogger = atom.loadPackage('command-logger')
editor = rootView.getActiveEditor()
commandLogger = CommandLogger.instance
afterEach ->
rootView.deactivate()
@@ -44,9 +43,11 @@ describe "CommandLogger", ->
describe "when an event is ignored", ->
it "does not create a node for that event", ->
commandLogger.ignoredEvents.push 'editor:delete-line'
commandLoggerView = commandLogger.getInstance()
commandLoggerView.ignoredEvents.push 'editor:delete-line'
editor.trigger 'editor:delete-line'
nodes = commandLogger.createNodes()
commandLoggerView.eventLog = commandLogger.eventLog
nodes = commandLoggerView.createNodes()
for node in nodes
continue unless node.name is 'Editor'
for child in node.children

View File

@@ -34,6 +34,7 @@ class CommandLoggerView extends ScrollView
super
@command 'core:cancel', => @detach()
@on 'blur', => @detach() unless document.activeElement is this[0]
toggle: (@eventLog={}) ->
if @hasParent()
@@ -143,10 +144,10 @@ class CommandLoggerView extends ScrollView
.append('div')
.style('width', "#{w}px")
.style('height', "#{h}px")
.append('svg:svg')
.append('svg')
.attr('width', w)
.attr('height', h)
.append('svg:g')
.append('g')
.attr('transform', 'translate(.5,.5)')
nodes = treemap.nodes(root).filter((d) -> not d.children)
@@ -154,17 +155,17 @@ class CommandLoggerView extends ScrollView
cell = svg.selectAll('g')
.data(nodes)
.enter()
.append('svg:g')
.append('g')
.attr('class', 'node')
.attr('transform', (d) -> "translate(#{d.x},#{d.y})")
.on('click', (d) -> if node is d.parent then zoom(root) else zoom(d.parent))
cell.append('svg:rect')
cell.append('rect')
.attr('width', (d) -> d.dx - 1)
.attr('height', (d) -> d.dy - 1)
.style('fill', (d) -> color(d.parent.name))
cell.append('svg:foreignObject')
cell.append('foreignObject')
.attr('width', (d) -> d.dx - 1)
.attr('height', (d) -> d.dy - 1)
.attr('class', 'foreign-object')
@@ -180,8 +181,11 @@ class CommandLoggerView extends ScrollView
@focus()
detach: ->
super()
return if @detaching
@detaching = true
super
@rootView.focus()
@detaching = false
serialize: ->
eventLog: @eventLog

View File

@@ -438,13 +438,13 @@ describe "CommandPanel", ->
expect(previewList.getSelectedOperation()).toBe previewList.getOperations()[0]
describe "when core:confirm is triggered on the preview list", ->
it "opens the operation's buffer, selects and scrolls to the search result, and focuses the active editor", ->
it "opens the operation's buffer, selects and scrolls to the search result, and refocuses the preview list", ->
rootView.height(200)
rootView.attachToDom()
waitsForPromise -> commandPanel.execute('X x/apply/') # use apply because it is at the end of the file
runs ->
spyOn(rootView, 'focus')
spyOn(previewList, 'focus')
executeHandler = jasmine.createSpy('executeHandler')
commandPanel.on 'core:confirm', executeHandler
@@ -458,13 +458,13 @@ describe "CommandPanel", ->
expect(editSession.getSelectedBufferRange()).toEqual operation.getBufferRange()
expect(editSession.getSelectedBufferRange()).toEqual operation.getBufferRange()
expect(editor.isScreenRowVisible(editor.getCursorScreenRow())).toBeTruthy()
expect(rootView.focus).toHaveBeenCalled()
expect(previewList.focus).toHaveBeenCalled()
expect(executeHandler).not.toHaveBeenCalled()
describe "when an operation in the preview list is clicked", ->
it "opens the operation's buffer, selects the search result, and focuses the active editor", ->
spyOn(rootView, 'focus')
it "opens the operation's buffer, selects the search result, and refocuses the preview list", ->
spyOn(previewList, 'focus')
operation = previewList.getOperations()[4]
previewList.find('li.operation:eq(4) span').mousedown()
@@ -473,4 +473,4 @@ describe "CommandPanel", ->
editSession = rootView.getActiveEditSession()
expect(editSession.buffer.getPath()).toBe project.resolve(operation.getPath())
expect(editSession.getSelectedBufferRange()).toEqual operation.getBufferRange()
expect(rootView.focus).toHaveBeenCalled()
expect(previewList.focus).toHaveBeenCalled()

View File

@@ -83,7 +83,7 @@ class PreviewList extends ScrollView
editSession = @rootView.open(operation.getPath())
bufferRange = operation.execute(editSession)
editSession.setSelectedBufferRange(bufferRange, autoscroll: true) if bufferRange
@rootView.focus()
@focus()
false
getPathCount: ->

View File

@@ -0,0 +1,16 @@
DeferredAtomPackage = require 'deferred-atom-package'
Stats = require './src/stats'
module.exports =
class EditorStats extends DeferredAtomPackage
loadEvents: ['editor-stats:toggle']
instanceClass: 'editor-stats/src/editor-stats-view'
stats: new Stats
activate: (rootView) ->
super
rootView.on 'keydown', => @stats.track()
rootView.on 'mouseup', => @stats.track()
onLoadEvent: (event, instance) -> instance.toggle(@stats)

View File

@@ -0,0 +1,2 @@
'body':
'meta-alt-s': 'editor-stats:toggle'

View File

@@ -0,0 +1,46 @@
$ = require 'jquery'
RootView = require 'root-view'
EditorStats = require 'editor-stats/src/editor-stats-view'
describe "EditorStats", ->
[rootView, editorStats, time] = []
simulateKeyUp = (key) ->
e = $.Event "keydown", keyCode: key.charCodeAt(0)
rootView.trigger(e)
simulateClick = ->
e = $.Event "mouseup"
rootView.trigger(e)
beforeEach ->
rootView = new RootView(require.resolve('fixtures/sample.js'))
date = new Date()
mins = date.getMinutes()
hours = date.getHours()
mins = if mins == 60 then '01' else mins + 1
time = "#{hours}:#{mins}"
editorStatsPackage = atom.loadPackage('editor-stats')
editorStatsPackage.getInstance()
editorStats = editorStatsPackage.stats
editorStats.clear()
afterEach ->
rootView.deactivate()
describe "when a keyup event is triggered", ->
it "records the number of times a keyup is triggered", ->
simulateKeyUp('a')
expect(editorStats.eventLog[time]).toBe 1
simulateKeyUp('b')
expect(editorStats.eventLog[time]).toBe 2
describe "when a mouseup event is triggered", ->
it "records the number of times a mouseup is triggered", ->
simulateClick()
expect(editorStats.eventLog[time]).toBe 1
simulateClick()
expect(editorStats.eventLog[time]).toBe 2

View File

@@ -0,0 +1,103 @@
ScrollView = require 'scroll-view'
d3 = require 'd3.v3'
_ = require 'underscore'
$ = require 'jquery'
module.exports =
class EditorStatsView extends ScrollView
@activate: (rootView, state) ->
@instance = new EditorStatsView(rootView)
@content: (rootView) ->
@div class: 'editor-stats-wrapper', tabindex: -1, =>
@div class: 'editor-stats', outlet: 'editorStats'
pt: 15
pl: 10
pb: 3
pr: 25
initialize: (@rootView) ->
super
resizer = =>
return unless @isOnDom()
@draw()
@update()
@subscribe $(window), 'resize', _.debounce(resizer, 300)
draw: ->
@editorStats.empty()
@x ?= d3.scale.ordinal().domain d3.range(@stats.hours * 60)
@y ?= d3.scale.linear()
w = @rootView.vertical.width()
h = @height()
data = d3.entries @stats.eventLog
max = d3.max data, (d) -> d.value
@x.rangeBands [0, w - @pl - @pr], 0.2
@y.domain([0, max]).range [h - @pt - @pb, 0]
@xaxis ?= d3.svg.axis().scale(@x).orient('top')
.tickSize(-h + @pt + @pb)
.tickFormat (d) =>
d = new Date(@stats.startDate.getTime() + (d * 6e4))
mins = d.getMinutes()
mins = "0#{mins}" if mins <= 9
"#{d.getHours()}:#{mins}"
vis = d3.select(@editorStats.get(0)).append('svg')
.attr('width', w)
.attr('height', h)
.append('g')
.attr('transform', "translate(#{@pl},#{@pt})")
vis.append('g')
.attr('class', 'x axis')
.call(@xaxis)
.selectAll('g')
.classed('minor', (d, i) -> i % 5 == 0 && i % 15 != 0)
.style 'display', (d, i) ->
if i % 15 == 0 || i % 5 == 0 || i == data.length - 1
'block'
else
'none'
@bars = vis.selectAll('rect.bar')
.data(data)
.enter().append('rect')
.attr('x', (d, i) => @x i)
.attr('height', (d, i) => h - @y(d.value) - @pt - @pb)
.attr('y', (d) => @y(d.value))
.attr('width', @x.rangeBand())
.attr('class', 'bar')
clearInterval(@updateInterval)
updater = => @update() if @isOnDom()
setTimeout(updater, 100)
@updateInterval = setInterval(updater, 5000)
update: ->
newData = d3.entries @stats.eventLog
max = d3.max newData, (d) -> d.value
@y.domain [0, max]
h = @height()
@bars.data(newData).transition()
.attr('height', (d, i) => h - @y(d.value) - @pt - @pb)
.attr('y', (d, i) => @y(d.value))
@bars.classed('max', (d, i) -> d.value == max)
toggle: (@stats) ->
if @hasParent()
@detach()
else
@attach()
attach: ->
@rootView.vertical.append(@)
@draw()
detach: ->
super()
clearInterval(@updateInterval)
@rootView.focus()

View File

@@ -0,0 +1,29 @@
module.exports =
class Stats
startDate: new Date
hours: 6
eventLog: []
constructor: ->
date = new Date(@startDate)
future = new Date(date.getTime() + (36e5 * @hours))
@eventLog[@time(date)] = 0
while date < future
@eventLog[@time(date)] = 0
clear: ->
@eventLog = []
track: ->
date = new Date
times = @time date
@eventLog[times] ?= 0
@eventLog[times] += 1
@eventLog.shift() if @eventLog.length > (@hours * 60)
time: (date) ->
date.setTime(date.getTime() + 6e4)
hour = date.getHours()
minute = date.getMinutes()
"#{hour}:#{minute}"

View File

@@ -0,0 +1,45 @@
.editor-stats-wrapper {
padding: 5px;
box-sizing: border-box;
border-top: 1px solid rgba(255, 255, 255, 0.05);
z-index: 9999;
}
.editor-stats {
height: 50px;
width: 100%;
background: #1d1f21;
border: 1px solid rgba(0, 0, 0, 0.3);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
border-right: 1px solid rgba(255, 255, 255, 0.1);
}
.editor-stats rect.bar {
fill: rgba(255, 255, 255, 0.2);
shape-rendering: crispedges;
}
.editor-stats rect.bar.max {
fill: rgba(0, 163, 255, 1);
}
.editor-stats text {
font-size: 10px;
fill: rgba(255, 255, 255, 0.2);
font-family: Courier;
}
.editor-stats .minor text {
display: none;
}
.editor-stats line {
stroke: #ccc;
stroke-opacity: 0.05;
stroke-width: 1px;
shape-rendering: crispedges;
}
.editor-stats path.domain {
fill: none;
}

View File

@@ -1,8 +1,8 @@
DeferredAtomPackage = require 'deferred-atom-package'
LoadPathsTask = require './src/load-paths-task'
module.exports =
class FuzzyFinder extends DeferredAtomPackage
loadEvents: [
'fuzzy-finder:toggle-file-finder'
'fuzzy-finder:toggle-buffer-finder'
@@ -11,7 +11,18 @@ class FuzzyFinder extends DeferredAtomPackage
instanceClass: 'fuzzy-finder/src/fuzzy-finder-view'
activate: (rootView) ->
super
if rootView.project.getPath()?
callback = (paths) => @projectPaths = paths
new LoadPathsTask(rootView, callback).start()
onLoadEvent: (event, instance) ->
if @projectPaths? and not @instance.projectPaths?
@instance.projectPaths = @projectPaths
@instance.reloadProjectPaths = false
switch event.type
when 'fuzzy-finder:toggle-file-finder'
instance.toggleFileFinder()

View File

@@ -1,5 +1,6 @@
RootView = require 'root-view'
FuzzyFinder = require 'fuzzy-finder/src/fuzzy-finder-view'
LoadPathsTask = require 'fuzzy-finder/src/load-paths-task'
$ = require 'jquery'
{$$} = require 'space-pen'
fs = require 'fs'
@@ -48,14 +49,13 @@ describe 'FuzzyFinder', ->
expect(finder.find(".loading")).toBeVisible()
expect(finder.find(".loading")).toHaveText "Indexing..."
waitsForPromise ->
rootView.project.getFilePaths().done (foundPaths) -> paths = foundPaths
waitsFor ->
finder.list.children('li').length > 0
waitsFor "all project paths to load", 5000, ->
if finder.projectPaths?.length > 0
paths = finder.projectPaths
true
runs ->
expect(finder.list.children('li').length).toBe paths.length, finder.maxResults
expect(finder.list.children('li').length).toBe paths.length
for path in paths
expect(finder.list.find("li:contains(#{fs.base(path)})")).toExist()
expect(finder.list.children().first()).toHaveClass 'selected'
@@ -222,15 +222,15 @@ describe 'FuzzyFinder', ->
describe "cached file paths", ->
it "caches file paths after first time", ->
spyOn(rootView.project, "getFilePaths").andCallThrough()
spyOn(LoadPathsTask.prototype, "start").andCallThrough()
rootView.trigger 'fuzzy-finder:toggle-file-finder'
waitsFor ->
finder.list.children('li').length > 0
runs ->
expect(rootView.project.getFilePaths).toHaveBeenCalled()
rootView.project.getFilePaths.reset()
expect(finder.loadPathsTask.start).toHaveBeenCalled()
finder.loadPathsTask.start.reset()
rootView.trigger 'fuzzy-finder:toggle-file-finder'
rootView.trigger 'fuzzy-finder:toggle-file-finder'
@@ -238,45 +238,44 @@ describe 'FuzzyFinder', ->
finder.list.children('li').length > 0
runs ->
expect(rootView.project.getFilePaths).not.toHaveBeenCalled()
expect(finder.loadPathsTask.start).not.toHaveBeenCalled()
it "doesn't cache buffer paths", ->
spyOn(rootView.project, "getFilePaths").andCallThrough()
spyOn(rootView, "getOpenBufferPaths").andCallThrough()
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
waitsFor ->
finder.list.children('li').length > 0
runs ->
expect(rootView.project.getFilePaths).not.toHaveBeenCalled()
rootView.project.getFilePaths.reset()
expect(rootView.getOpenBufferPaths).toHaveBeenCalled()
rootView.getOpenBufferPaths.reset()
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
rootView.trigger 'fuzzy-finder:toggle-file-finder'
waitsFor ->
finder.list.children('li').length > 0
runs ->
expect(rootView.project.getFilePaths).toHaveBeenCalled()
expect(rootView.getOpenBufferPaths).toHaveBeenCalled()
it "busts the cache when the window gains focus", ->
spyOn(rootView.project, "getFilePaths").andCallThrough()
spyOn(LoadPathsTask.prototype, "start").andCallThrough()
rootView.trigger 'fuzzy-finder:toggle-file-finder'
waitsFor ->
finder.list.children('li').length > 0
runs ->
expect(rootView.project.getFilePaths).toHaveBeenCalled()
rootView.project.getFilePaths.reset()
expect(finder.loadPathsTask.start).toHaveBeenCalled()
finder.loadPathsTask.start.reset()
$(window).trigger 'focus'
rootView.trigger 'fuzzy-finder:toggle-file-finder'
rootView.trigger 'fuzzy-finder:toggle-file-finder'
expect(rootView.project.getFilePaths).toHaveBeenCalled()
expect(finder.loadPathsTask.start).toHaveBeenCalled()
describe "path ignoring", ->
it "ignores paths that match entries in config.fuzzyFinder.ignoredNames", ->
spyOn(rootView.project, "getFilePaths").andCallThrough()
config.set("fuzzyFinder.ignoredNames", ["tree-view.js"])
rootView.trigger 'fuzzy-finder:toggle-file-finder'
finder.maxItems = Infinity
@@ -293,7 +292,6 @@ describe 'FuzzyFinder', ->
beforeEach ->
editor = rootView.getActiveEditor()
rootView.attachToDom()
spyOn(rootView.project, "getFilePaths").andCallThrough()
it "opens the fuzzy finder window when there are multiple matches", ->
editor.setText("sample")

View File

@@ -3,6 +3,7 @@ SelectList = require 'select-list'
_ = require 'underscore'
$ = require 'jquery'
fs = require 'fs'
LoadPathsTask = require 'fuzzy-finder/src/load-paths-task'
module.exports =
class FuzzyFinderView extends SelectList
@@ -126,16 +127,9 @@ class FuzzyFinderView extends SelectList
@setLoading("Indexing...")
if @reloadProjectPaths
@rootView.project.getFilePaths().done (paths) =>
ignoredNames = config.get("fuzzyFinder.ignoredNames") or []
ignoredNames = ignoredNames.concat(config.get("core.ignoredNames") or [])
@loadPathsTask?.terminate()
callback = (paths) =>
@projectPaths = paths
if ignoredNames
@projectPaths = @projectPaths.filter (path) ->
for segment in path.split("/")
return false if _.contains(ignoredNames, segment)
return true
@reloadProjectPaths = false
listedItems =
if options.filter?
@@ -146,6 +140,8 @@ class FuzzyFinderView extends SelectList
@setArray(listedItems)
options.done(listedItems) if options.done?
@loadPathsTask = new LoadPathsTask(@rootView, callback)
@loadPathsTask.start()
populateOpenBufferPaths: ->
@paths = @rootView.getOpenBufferPaths().map (path) =>

View File

@@ -0,0 +1,20 @@
fs = require 'fs'
_ = require 'underscore'
Git = require 'git'
module.exports =
loadPaths: (rootPath, ignoredNames, ignoreGitIgnoredFiles) ->
paths = []
repo = Git.open(rootPath, refreshIndexOnFocus: false) if ignoreGitIgnoredFiles
isIgnored = (path) ->
for segment in path.split('/')
return true if _.contains(ignoredNames, segment)
return true if repo?.isPathIgnored(fs.join(rootPath, path))
false
onFile = (path) ->
paths.push(path) unless isIgnored(path)
onDirectory = (path) ->
not isIgnored(path)
fs.traverseTree(rootPath, onFile, onDirectory)
repo?.destroy()
callTaskMethod('pathsLoaded', paths)

View File

@@ -0,0 +1,17 @@
Task = require 'task'
module.exports =
class LoadPathsTask extends Task
constructor: (@rootView, @callback)->
super('fuzzy-finder/src/load-paths-handler')
started: ->
ignoredNames = config.get("fuzzyFinder.ignoredNames") ? []
ignoredNames = ignoredNames.concat(config.get("core.ignoredNames") ? [])
ignoreGitIgnoredFiles = config.get("core.hideGitIgnoredFiles")
rootPath = @rootView.project.getPath()
@callWorkerMethod('loadPaths', rootPath, ignoredNames, ignoreGitIgnoredFiles)
pathsLoaded: (paths) ->
@terminate()
@callback(paths)

View File

@@ -277,6 +277,7 @@ describe "Snippets extension", ->
it "terminates the worker when loading completes", ->
jasmine.unspy(LoadSnippetsTask.prototype, 'loadAtomSnippets')
spyOn(console, "warn")
spyOn(Worker.prototype, 'terminate').andCallThrough()
snippets.loaded = false
snippets.loadAll()
@@ -284,6 +285,8 @@ describe "Snippets extension", ->
waitsFor "all snippets to load", 5000, -> snippets.loaded
runs ->
expect(console.warn).toHaveBeenCalled()
expect(console.warn.argsForCall[0]).toMatch /Error reading snippets file '.*?\/spec\/fixtures\/packages\/package-with-snippets\/snippets\/junk-file'/
expect(Worker.prototype.terminate).toHaveBeenCalled()
expect(Worker.prototype.terminate.calls.length).toBe 1

View File

@@ -1,4 +1,4 @@
Task = require 'Task'
Task = require 'task'
TextMatePackage = require 'text-mate-package'
module.exports =

View File

@@ -770,6 +770,9 @@ describe "TreeView", ->
treeView.trigger "tree-view:move"
moveDialog = rootView.find(".tree-view-dialog").view()
afterEach ->
waits 50 # The move specs cause too many false positives because of their async nature, so wait a little bit before we cleanup
it "opens a move dialog with the file's current path (excluding extension) populated", ->
extension = fs.extension(filePath)
fileNameWithoutExtension = fs.base(filePath, extension)

79
src/stdlib/cson.coffee Normal file
View File

@@ -0,0 +1,79 @@
_ = require 'underscore'
module.exports =
stringifyIndent: (level=0) -> _.multiplyString(' ', Math.max(level, 0))
stringifyString: (string) ->
string = JSON.stringify(string)
string = string[1...-1] # Remove surrounding double quotes
string = string.replace(/\\"/g, '"') # Unescape escaped double quotes
string = string.replace(/'/g, '\\\'') # Escape single quotes
"'#{string}'" # Wrap in single quotes
stringifyBoolean: (boolean) -> "#{boolean}"
stringifyNumber: (number) -> "#{number}"
stringifyNull: -> 'null'
stringifyArray: (array, indentLevel=0) ->
return '[]' if array.length is 0
cson = '[\n'
for value in array
cson += @stringifyIndent(indentLevel + 2)
if _.isString(value)
cson += @stringifyString(value)
else if _.isBoolean(value)
cson += @stringifyBoolean(value)
else if _.isNumber(value)
cson += @stringifyNumber(value)
else if _.isNull(value)
cson += @stringifyNull(value)
else if _.isArray(value)
cson += @stringifyArray(value, indentLevel + 2)
else if _.isObject(value)
cson += @stringifyObject(value, indentLevel + 2)
else
throw new Error("Unrecognized type for array value: #{value}")
cson += '\n'
"#{cson}#{@stringifyIndent(indentLevel)}]"
stringifyObject: (object, indentLevel=0) ->
cson = ''
prefix = ''
for key, value of object
continue if value is undefined
if _.isFunction(value)
throw new Error("Function specified as value to key: #{key}")
cson += "#{prefix}#{@stringifyIndent(indentLevel)}'#{key}':"
if _.isString(value)
cson += " #{@stringifyString(value)}"
else if _.isBoolean(value)
cson += " #{@stringifyBoolean(value)}"
else if _.isNumber(value)
cson += " #{@stringifyNumber(value)}"
else if _.isNull(value)
cson += " #{@stringifyNull(value)}"
else if _.isArray(value)
cson += " #{@stringifyArray(value, indentLevel)}"
else if _.isObject(value)
cson += "\n#{@stringifyObject(value, indentLevel + 2)}"
else
throw new Error("Unrecognized value type for key: #{key} with value: #{value}")
prefix = '\n'
cson
stringify: (object) ->
throw new Error("Cannot stringify undefined object") if object is undefined
throw new Error("Cannot stringify function: #{object}") if _.isFunction(object)
return @stringifyString(object) if _.isString(object)
return @stringifyBoolean(object) if _.isBoolean(object)
return @stringifyNumber(object) if _.isNumber(object)
return @stringifyNull(object) if _.isNull(object)
return @stringifyArray(object) if _.isArray(object)
return @stringifyObject(object) if _.isObject(object)
throw new Error("Unrecognized type to stringify: #{object}")

View File

@@ -59,11 +59,16 @@ module.exports =
# Returns an array with all the names of files contained
# in the directory path.
list: (rootPath) ->
list: (rootPath, extensions) ->
paths = []
onPath = (path) =>
paths.push(@join(rootPath, path))
false
if extensions
onPath = (path) =>
paths.push(@join(rootPath, path)) if _.contains(extensions, @extension(path))
false
else
onPath = (path) =>
paths.push(@join(rootPath, path))
false
@traverseTree(rootPath, onPath, onPath)
paths

View File

@@ -25,6 +25,9 @@ $.fn.pageUp = ->
$.fn.pageDown = ->
@scrollTop(@scrollTop() + @height())
$.fn.isOnDom = ->
@closest(document.body).length is 1
$.fn.containsElement = (element) ->
(element[0].compareDocumentPosition(this[0]) & 8) == 8