diff --git a/lib/github-package.js b/lib/github-package.js index f0c88c6c99..99aa8fb59c 100644 --- a/lib/github-package.js +++ b/lib/github-package.js @@ -549,6 +549,29 @@ export default class GithubPackage { * projects and pane items. */ async getNextContext(usePath = null) { + // Internal utility function to normalize paths not contained within a git + // working tree. + const workdirForNonGitPath = async sourcePath => { + const containingRoot = this.project.getDirectories().find(root => root.contains(sourcePath)); + if (containingRoot) { + return containingRoot.getPath(); + /* istanbul ignore else */ + } else if (!(await fs.stat(sourcePath)).isDirectory()) { + return path.dirname(sourcePath); + } else { + return sourcePath; + } + }; + + // Internal utility function to identify the working directory to use for + // an arbitrary (file or directory) path. + const workdirForPath = async sourcePath => { + return (await Promise.all([ + this.workdirCache.find(sourcePath), + workdirForNonGitPath(sourcePath), + ])).find(Boolean); + }; + // Identify paths that *could* contribute a git working directory to the pool. This is drawn from // the roots of open projects, the currently locked context if one is present, and the path of the // open workspace item. @@ -574,7 +597,8 @@ export default class GithubPackage { const workdirs = new Set( await Promise.all( Array.from(candidatePaths, async candidatePath => { - const workdir = (await this.workdirCache.find(candidatePath)) || candidatePath; + const workdir = await workdirForPath(candidatePath); + // Note the workdirs associated with the active pane item and the first open project so we can // prefer them later. if (candidatePath === activeItemPath) { @@ -582,6 +606,7 @@ export default class GithubPackage { } else if (candidatePath === this.project.getPaths()[0]) { firstProjectWorkdir = workdir; } + return workdir; }), ), @@ -593,7 +618,17 @@ export default class GithubPackage { // 1 - Explicitly requested workdir. This is either selected by the user from a context tile or // deserialized from package state. Choose this context only if it still exists in the pool. if (usePath) { - const stateContext = this.contextPool.getContext(usePath); + // Normalize usePath in a similar fashion to the way we do activeItemPath. + let useWorkdir = usePath; + if (usePath === activeItemPath) { + useWorkdir = activeItemWorkdir; + } else if (usePath === this.project.getPaths()[0]) { + useWorkdir = firstProjectWorkdir; + } else { + useWorkdir = await workdirForPath(usePath); + } + + const stateContext = this.contextPool.getContext(useWorkdir); if (stateContext.isPresent()) { return stateContext; } diff --git a/test/github-package.test.js b/test/github-package.test.js index 67d50bcddd..dc1e07b410 100644 --- a/test/github-package.test.js +++ b/test/github-package.test.js @@ -455,6 +455,29 @@ describe('GithubPackage', function() { assert.strictEqual(githubPackage.getActiveWorkdir(), workdirPath2); }); + it('uses a context associated with a project root for paths within that root', async function() { + const workdirPath4 = await getTempDir(); + await fs.mkdir(path.join(workdirPath4, 'subdir')); + const itemPath4 = path.join(workdirPath4, 'subdir/file.txt'); + await fs.writeFile(itemPath4, 'content\n'); + + await contextUpdateAfter(githubPackage, () => project.setPaths([workdirPath1, workdirPath4])); + + assert.strictEqual(githubPackage.getActiveWorkdir(), workdirPath1); + await contextUpdateAfter(githubPackage, () => atomEnv.workspace.open(itemPath4)); + assert.strictEqual(githubPackage.getActiveWorkdir(), workdirPath4); + }); + + it('uses a context of the containing directory for file item paths not in any root', async function() { + const workdirPath4 = await getTempDir(); + const itemPath4 = path.join(workdirPath4, 'file.txt'); + await fs.writeFile(itemPath4, 'content\n'); + + assert.strictEqual(githubPackage.getActiveWorkdir(), workdirPath1); + await contextUpdateAfter(githubPackage, () => atomEnv.workspace.open(itemPath4)); + assert.strictEqual(githubPackage.getActiveWorkdir(), workdirPath4); + }); + it('does nothing if the context is locked', async function() { const itemPath2 = path.join(workdirPath2, 'c.txt');