mirror of
https://github.com/atom/atom.git
synced 2026-02-14 00:25:08 -05:00
Merge remote-tracking branch 'origin/dev' into better-anchors
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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...) ->
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Task = require 'Task'
|
||||
Task = require 'task'
|
||||
|
||||
module.exports =
|
||||
class LoadTextMatePackagesTask extends Task
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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: ->
|
||||
|
||||
16
src/packages/editor-stats/index.coffee
Normal file
16
src/packages/editor-stats/index.coffee
Normal 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)
|
||||
2
src/packages/editor-stats/keymaps/editor-stats.cson
Normal file
2
src/packages/editor-stats/keymaps/editor-stats.cson
Normal file
@@ -0,0 +1,2 @@
|
||||
'body':
|
||||
'meta-alt-s': 'editor-stats:toggle'
|
||||
46
src/packages/editor-stats/spec/editor-stats-spec.coffee
Normal file
46
src/packages/editor-stats/spec/editor-stats-spec.coffee
Normal 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
|
||||
103
src/packages/editor-stats/src/editor-stats-view.coffee
Normal file
103
src/packages/editor-stats/src/editor-stats-view.coffee
Normal 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()
|
||||
29
src/packages/editor-stats/src/stats.coffee
Normal file
29
src/packages/editor-stats/src/stats.coffee
Normal 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}"
|
||||
45
src/packages/editor-stats/stylesheets/editor-stats.css
Normal file
45
src/packages/editor-stats/stylesheets/editor-stats.css
Normal 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;
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
20
src/packages/fuzzy-finder/src/load-paths-handler.coffee
Normal file
20
src/packages/fuzzy-finder/src/load-paths-handler.coffee
Normal 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)
|
||||
17
src/packages/fuzzy-finder/src/load-paths-task.coffee
Normal file
17
src/packages/fuzzy-finder/src/load-paths-task.coffee
Normal 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)
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Task = require 'Task'
|
||||
Task = require 'task'
|
||||
TextMatePackage = require 'text-mate-package'
|
||||
|
||||
module.exports =
|
||||
|
||||
@@ -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
79
src/stdlib/cson.coffee
Normal 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}")
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user