Files
atom/src/app/project.coffee
Kevin Sawicki 6f5e29d48e Throw exception when repository fails to open
Removes need to check if the repository is valid before
each native API call and also removes calling into the
native-side if no repository can be opened for the project
page.
2013-01-08 21:15:41 -08:00

200 lines
5.4 KiB
CoffeeScript

fs = require 'fs'
_ = require 'underscore'
$ = require 'jquery'
Range = require 'range'
Buffer = require 'buffer'
EditSession = require 'edit-session'
EventEmitter = require 'event-emitter'
Directory = require 'directory'
ChildProcess = require 'child-process'
Git = require 'git'
module.exports =
class Project
@deserialize: (state) ->
new Project(state.path, state.grammarOverridesByPath)
tabLength: 2
autoIndent: true
softTabs: true
softWrap: false
rootDirectory: null
editSessions: null
ignoredPathRegexes: null
grammarOverridesByPath: null
constructor: (path, @grammarOverridesByPath={}) ->
@setPath(path)
@editSessions = []
@buffers = []
serialize: ->
path: @getPath()
grammarOverridesByPath: @grammarOverridesByPath
destroy: ->
editSession.destroy() for editSession in @getEditSessions()
addGrammarOverrideForPath: (path, grammar) ->
@grammarOverridesByPath[path] = grammar.scopeName
removeGrammarOverrideForPath: (path) ->
delete @grammarOverridesByPath[path]
grammarOverrideForPath: (path) ->
syntax.grammarForScopeName(@grammarOverridesByPath[path])
grammarForFilePath: (path, contents) ->
@grammarOverrideForPath(path) or syntax.grammarForFilePath(path, contents)
getPath: ->
@rootDirectory?.path
setPath: (path) ->
@rootDirectory?.off()
if path?
directory = if fs.isDirectory(path) then path else fs.directory(path)
@rootDirectory = new Directory(directory)
try
@repo = new Git(path)
catch e
@repo = null
else
@rootDirectory = null
@repo = null
@trigger "path-changed"
getRootDirectory: ->
@rootDirectory
getFilePaths: ->
deferred = $.Deferred()
fs.getAllFilePathsAsync @getPath(), (paths) =>
paths = paths.filter (path) => not @isPathIgnored(path)
deferred.resolve(paths)
deferred.promise()
isPathIgnored: (path) ->
for segment in path.split("/")
ignoredNames = config.get("core.ignoredNames") or []
return true if _.contains(ignoredNames, segment)
@ignoreRepositoryPath(path)
ignoreRepositoryPath: (path) ->
config.get("core.hideGitIgnoredFiles") and @repo.isPathIgnored(fs.join(@getPath(), path))
resolve: (filePath) ->
filePath = fs.join(@getPath(), filePath) unless filePath[0] == '/'
fs.absolute filePath
relativize: (fullPath) ->
fullPath.replace(@getPath(), "").replace(/^\//, '')
getAutoIndent: -> @autoIndent
setAutoIndent: (@autoIndent) ->
getSoftTabs: -> @softTabs
setSoftTabs: (@softTabs) ->
getSoftWrap: -> @softWrap
setSoftWrap: (@softWrap) ->
buildEditSessionForPath: (filePath, editSessionOptions={}) ->
@buildEditSession(@bufferForPath(filePath), editSessionOptions)
buildEditSession: (buffer, editSessionOptions) ->
options = _.extend(@defaultEditSessionOptions(), editSessionOptions)
options.project = this
options.buffer = buffer
editSession = new EditSession(options)
@editSessions.push editSession
@trigger 'edit-session-created', editSession
editSession
defaultEditSessionOptions: ->
tabLength: @tabLength
autoIndent: @getAutoIndent()
softTabs: @getSoftTabs()
softWrap: @getSoftWrap()
getEditSessions: ->
new Array(@editSessions...)
removeEditSession: (editSession) ->
_.remove(@editSessions, editSession)
getBuffers: ->
buffers = []
for editSession in @editSessions when not _.include(buffers, editSession.buffer)
buffers.push editSession.buffer
buffers
bufferForPath: (filePath) ->
if filePath?
filePath = @resolve(filePath)
if filePath
buffer = _.find @buffers, (buffer) -> buffer.getPath() == filePath
buffer or @buildBuffer(filePath)
else
else
@buildBuffer()
buildBuffer: (filePath) ->
buffer = new Buffer(filePath, this)
@buffers.push buffer
@trigger 'buffer-created', buffer
buffer
removeBuffer: (buffer) ->
_.remove(@buffers, buffer)
scan: (regex, iterator) ->
command = "#{require.resolve('ag')} --ackmate '#{regex.source}' '#{@getPath()}'"
bufferedData = ""
state = 'readingPath'
path = null
readPath = (line) ->
if /^[0-9,; ]+:/.test(line)
state = 'readingLines'
else if /^:/.test line
path = line.substr(1)
else
path += ('\n' + line)
readLine = (line) ->
if line.length == 0
state = 'readingPath'
path = null
else
colonIndex = line.indexOf(':')
matchInfo = line.substring(0, colonIndex)
lineText = line.substring(colonIndex + 1)
readMatches(matchInfo, lineText)
readMatches = (matchInfo, lineText) ->
[lineNumber, matchPositionsText] = matchInfo.match(/(\d+);(.+)/)[1..]
row = parseInt(lineNumber) - 1
matchPositions = matchPositionsText.split(',').map (positionText) -> positionText.split(' ').map (pos) -> parseInt(pos)
for [column, length] in matchPositions
range = new Range([row, column], [row, column + length])
match = lineText.substr(column, length)
iterator({path, range, match})
ChildProcess.exec command , bufferLines: true, stdout: (data) ->
lines = data.split('\n')
lines.pop() # the last segment is a spurios '' because data always ends in \n due to bufferLines: true
for line in lines
readPath(line) if state is 'readingPath'
readLine(line) if state is 'readingLines'
_.extend Project.prototype, EventEmitter