Merge branch 'master' into ns-ro/ripgrep-scan

This commit is contained in:
Rafael Oleza 2019-05-20 15:45:13 +02:00
commit 9d1406cb05
22 changed files with 337 additions and 278 deletions

13
package-lock.json generated
View File

@ -455,11 +455,6 @@
}
}
},
"@atom/notify": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@atom/notify/-/notify-1.3.3.tgz",
"integrity": "sha512-AExln6/wUVEo4mpkMmJu1n37RxQ1qKlwvGKaMncL5UkIEXYzKtjs303OS5T9WZ0Ks/YDUOn7B9Np3t4BIYbiYg=="
},
"@atom/nsfw": {
"version": "1.0.22",
"resolved": "https://registry.npmjs.org/@atom/nsfw/-/nsfw-1.0.22.tgz",
@ -4372,8 +4367,8 @@
}
},
"language-c": {
"version": "https://www.atom.io/api/packages/language-c/versions/0.60.16/tarball",
"integrity": "sha512-cg6q/mAeHpj7rMe9J3NoA9cfz+g0zKkPRhZFSGaspTEUPGGFZj+tJcBBpbIeC2vl1bhoL7e8G41fzA3hjC7+4Q==",
"version": "https://www.atom.io/api/packages/language-c/versions/0.60.17/tarball",
"integrity": "sha512-jGu5Eo/QqqmMM1r62nYJhLdgop/pf4RUUK+udYyp0mOQpU3qPRT+9lVv14/9SDpwkiBB1DQSLJekhVobcD0McQ==",
"requires": {
"tree-sitter-c": "^0.13.13",
"tree-sitter-cpp": "^0.13.15"
@ -4440,8 +4435,8 @@
}
},
"language-json": {
"version": "https://www.atom.io/api/packages/language-json/versions/1.0.0/tarball",
"integrity": "sha512-oq7QgVaMbFVggiERCRihooIvzeELhpoPQey9dZVeP6ICjYKsVOa75jqaYBfBIWoIpigsVUpZKY6GJtz9D5GsnQ==",
"version": "https://www.atom.io/api/packages/language-json/versions/1.0.1/tarball",
"integrity": "sha512-mgUFtXCqseXW6bi4oIub1c+lh7qqXN9o8kjf+ljJe8yB8JYSZeRAm5zhqvrrUZO6WDQv4lplq2bJP8vMiKtaxg==",
"requires": {
"tree-sitter-json": "^0.13.1"
}

View File

@ -12,9 +12,8 @@
"url": "https://github.com/atom/atom/issues"
},
"license": "MIT",
"electronVersion": "2.0.18",
"electronVersion": "3.1.9",
"dependencies": {
"@atom/notify": "1.3.3",
"@atom/nsfw": "1.0.22",
"@atom/source-map-support": "^0.3.4",
"@atom/watcher": "1.3.1",
@ -78,7 +77,7 @@
"jasmine-tagged": "^1.1.4",
"key-path-helpers": "^0.4.0",
"keybinding-resolver": "https://www.atom.io/api/packages/keybinding-resolver/versions/0.39.0/tarball",
"language-c": "https://www.atom.io/api/packages/language-c/versions/0.60.16/tarball",
"language-c": "https://www.atom.io/api/packages/language-c/versions/0.60.17/tarball",
"language-clojure": "https://www.atom.io/api/packages/language-clojure/versions/0.22.8/tarball",
"language-coffee-script": "https://www.atom.io/api/packages/language-coffee-script/versions/0.50.0/tarball",
"language-csharp": "https://www.atom.io/api/packages/language-csharp/versions/1.1.0/tarball",
@ -90,7 +89,7 @@
"language-hyperlink": "https://www.atom.io/api/packages/language-hyperlink/versions/0.17.1/tarball",
"language-java": "https://www.atom.io/api/packages/language-java/versions/0.31.3/tarball",
"language-javascript": "https://www.atom.io/api/packages/language-javascript/versions/0.130.0/tarball",
"language-json": "https://www.atom.io/api/packages/language-json/versions/1.0.0/tarball",
"language-json": "https://www.atom.io/api/packages/language-json/versions/1.0.1/tarball",
"language-less": "https://www.atom.io/api/packages/language-less/versions/0.34.3/tarball",
"language-make": "https://www.atom.io/api/packages/language-make/versions/0.23.0/tarball",
"language-mustache": "https://www.atom.io/api/packages/language-mustache/versions/0.14.5/tarball",
@ -230,7 +229,7 @@
"welcome": "0.36.9",
"whitespace": "0.37.7",
"wrap-guide": "0.41.0",
"language-c": "0.60.16",
"language-c": "0.60.17",
"language-clojure": "0.22.8",
"language-coffee-script": "0.50.0",
"language-csharp": "1.1.0",
@ -242,7 +241,7 @@
"language-hyperlink": "0.17.1",
"language-java": "0.31.3",
"language-javascript": "0.130.0",
"language-json": "1.0.0",
"language-json": "1.0.1",
"language-less": "0.34.3",
"language-make": "0.23.0",
"language-mustache": "0.14.5",

View File

@ -5,17 +5,17 @@ const CONFIG = require('../config')
const semver = require('semver')
module.exports = function () {
// Chromedriver should be specified as ~x.y where x and y match Electron major/minor
// Chromedriver should be specified as ^n.x where n matches the Electron major version
const chromedriverVer = buildMetadata.dependencies['electron-chromedriver']
const mksnapshotVer = buildMetadata.dependencies['electron-mksnapshot']
// Always use tilde on electron-chromedriver so that it can pick up the best patch version
if (!chromedriverVer.startsWith('~')) {
throw new Error(`electron-chromedriver version in script/package.json should start with a tilde to match latest patch version.`)
// Always use caret on electron-chromedriver so that it can pick up the best minor/patch versions
if (!chromedriverVer.startsWith('^')) {
throw new Error(`electron-chromedriver version in script/package.json should start with a caret to match latest patch version.`)
}
if (!mksnapshotVer.startsWith('~')) {
throw new Error(`electron-mksnapshot version in script/package.json should start with a tilde to match latest patch version.`)
if (!mksnapshotVer.startsWith('^')) {
throw new Error(`electron-mksnapshot version in script/package.json should start with a caret to match latest patch version.`)
}
const electronVer = CONFIG.appMetadata.electronVersion

View File

@ -69,7 +69,6 @@ module.exports = function (packagedAppPath) {
requiredModuleRelativePath === path.join('..', 'node_modules', 'yauzl', 'index.js') ||
requiredModuleRelativePath === path.join('..', 'node_modules', 'winreg', 'lib', 'registry.js') ||
requiredModuleRelativePath === path.join('..', 'node_modules', '@atom', 'fuzzy-native', 'lib', 'main.js') ||
requiredModuleRelativePath === path.join('..', 'node_modules', '@atom', 'notify', 'lib', 'bin-path.js') ||
requiredModuleRelativePath === path.join('..', 'node_modules', 'vscode-ripgrep', 'lib', 'index.js') ||
// The startup-time script is used by both the renderer and the main process and having it in the
// snapshot causes issues.
@ -106,22 +105,36 @@ module.exports = function (packagedAppPath) {
{env: Object.assign({}, process.env, {ELECTRON_RUN_AS_NODE: 1})}
)
const generatedStartupBlobPath = path.join(CONFIG.buildOutputPath, 'snapshot_blob.bin')
console.log(`Generating startup blob at "${generatedStartupBlobPath}"`)
childProcess.execFileSync(
path.join(CONFIG.repositoryRootPath, 'script', 'node_modules', 'electron-mksnapshot', 'bin', 'mksnapshot'),
['--no-use_ic', snapshotScriptPath, '--startup_blob', generatedStartupBlobPath]
console.log('Generating startup blob with mksnapshot')
childProcess.spawnSync(
process.execPath, [
path.join(CONFIG.repositoryRootPath, 'script', 'node_modules', 'electron-mksnapshot', 'mksnapshot.js'),
snapshotScriptPath,
'--output_dir',
CONFIG.buildOutputPath
]
)
let startupBlobDestinationPath
if (process.platform === 'darwin') {
startupBlobDestinationPath = `${packagedAppPath}/Contents/Frameworks/Electron Framework.framework/Resources/snapshot_blob.bin`
startupBlobDestinationPath = `${packagedAppPath}/Contents/Frameworks/Electron Framework.framework/Resources`
} else {
startupBlobDestinationPath = path.join(packagedAppPath, 'snapshot_blob.bin')
startupBlobDestinationPath = packagedAppPath
}
console.log(`Moving generated startup blob into "${startupBlobDestinationPath}"`)
fs.unlinkSync(startupBlobDestinationPath)
fs.renameSync(generatedStartupBlobPath, startupBlobDestinationPath)
const snapshotBinaries = ['v8_context_snapshot.bin', 'snapshot_blob.bin']
for (let snapshotBinary of snapshotBinaries) {
const destinationPath = path.join(startupBlobDestinationPath, snapshotBinary)
console.log(`Moving generated startup blob into "${destinationPath}"`)
try {
fs.unlinkSync(destinationPath)
} catch (err) {
// Doesn't matter if the file doesn't exist already
if (!err.code || err.code !== 'ENOENT') {
throw err
}
}
fs.renameSync(path.join(CONFIG.buildOutputPath, snapshotBinary), destinationPath)
}
})
}

View File

@ -112,7 +112,6 @@ function buildAsarUnpackGlobExpression () {
path.join('**', 'node_modules', 'dugite', 'git', '**'),
path.join('**', 'node_modules', 'github', 'bin', '**'),
path.join('**', 'node_modules', 'vscode-ripgrep', 'bin', '**'),
path.join('**', 'node_modules', '@atom', 'notify', 'bin', '**'),
path.join('**', 'resources', 'atom.png')
]

View File

@ -1911,9 +1911,9 @@
}
},
"electron-chromedriver": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/electron-chromedriver/-/electron-chromedriver-2.0.0.tgz",
"integrity": "sha512-kERk/Wzhc9RzW9jUKXA5kJc4m8BlL6c9p5QH+CrIlst0saeqZL1Up7vzD4ZOnuBDpAVBBYJ4jhkAKIssf8ZlXg==",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/electron-chromedriver/-/electron-chromedriver-3.0.0.tgz",
"integrity": "sha512-xWivZRiPTtDFJt+qXv7Ax/Dmhxj0iqESOxoLZ2szu3fv6k1vYDUDJUMHfdfVAke9D2gBRIgChuGb5j3YEt6hxQ==",
"requires": {
"electron-download": "^4.1.0",
"extract-zip": "^1.6.5"
@ -2008,12 +2008,13 @@
}
},
"electron-mksnapshot": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/electron-mksnapshot/-/electron-mksnapshot-2.0.0.tgz",
"integrity": "sha512-OoZwZJNKgHP+DwhCGVTJEuDSeb478hOzAbHeg7dKGCHDbKKmUWmjGc+pEjxGutpqQ3Mn8hCdLzdx2c/lAJcTLA==",
"version": "3.0.10",
"resolved": "https://registry.npmjs.org/electron-mksnapshot/-/electron-mksnapshot-3.0.10.tgz",
"integrity": "sha512-Toy6sAC3t9tgvq1kUYsx+4TRNPDj7Bzoo+1gx5FD8Q0YCS+tq+ter62Ot6dBXCKG9SwoaGBz84b++MgO0VobYw==",
"requires": {
"electron-download": "^4.1.0",
"extract-zip": "^1.6.5"
"extract-zip": "^1.6.5",
"temp": "^0.8.3"
}
},
"electron-osx-sign": {

View File

@ -10,9 +10,9 @@
"coffeelint": "1.15.7",
"colors": "1.1.2",
"donna": "1.0.16",
"electron-chromedriver": "~2.0",
"electron-chromedriver": "^3.0.0",
"electron-link": "0.4.0",
"electron-mksnapshot": "~2.0",
"electron-mksnapshot": "^3.0.10",
"electron-packager": "12.2.0",
"electron-winstaller": "2.6.4",
"eslint": "^5.16.0",

View File

@ -48,7 +48,6 @@ const resourcePath = CONFIG.repositoryRootPath
let executablePath
if (process.platform === 'darwin') {
const executablePaths = glob.sync(path.join(CONFIG.buildOutputPath, '*.app'))
assert(executablePaths.length > 0, `No application found in ${CONFIG.buildOutputPath} to run tests against`)
assert(executablePaths.length === 1, `More than one application to run tests against was found. ${executablePaths.join(',')}`)
executablePath = path.join(executablePaths[0], 'Contents', 'MacOS', path.basename(executablePaths[0], '.app'))
} else if (process.platform === 'linux') {

View File

@ -1,7 +1,7 @@
resources:
containers:
- container: atom-linux-ci
image: daviwil/atom-linux-ci:latest
image: atomeditor/atom-linux-ci:latest
jobs:

View File

@ -78,10 +78,7 @@ jobs:
IS_SIGNED_ZIP_BRANCH: $(IsSignedZipBranch)
displayName: Build Atom
- powershell: |
# Normalize %TEMP% as a long (non 8.3) path to avoid assertion failures comparing paths in temp folders
$env:TEMP = (Get-Item -LiteralPath $env:TEMP).FullName
node script\vsts\windows-run.js script\test.cmd
- script: node script\vsts\windows-run.js script\test.cmd
env:
CI: true
CI_PROVIDER: VSTS

View File

@ -3,7 +3,7 @@ trigger: none # No CI builds, only PR builds
resources:
containers:
- container: atom-linux-ci
image: daviwil/atom-linux-ci:latest
image: atomeditor/atom-linux-ci:latest
jobs:

View File

@ -6,7 +6,7 @@ trigger:
resources:
containers:
- container: atom-linux-ci
image: daviwil/atom-linux-ci:latest
image: atomeditor/atom-linux-ci:latest
jobs:

View File

@ -6,7 +6,7 @@ import path from 'path'
import { promisify } from 'util'
import { CompositeDisposable } from 'event-kit'
import { PathWatcherManager } from '../src/path-watcher'
import { watchPath, stopAllWatchers } from '../src/path-watcher'
temp.track()
@ -17,7 +17,7 @@ const realpath = promisify(fs.realpath)
const tempMkdir = promisify(temp.mkdir)
describe('PathWatcherManager', function () {
describe('watchPath', function () {
let subs
beforeEach(function () {
@ -26,6 +26,7 @@ describe('PathWatcherManager', function () {
afterEach(async function () {
subs.dispose()
await stopAllWatchers()
})
function waitForChanges (watcher, ...fileNames) {
@ -50,136 +51,124 @@ describe('PathWatcherManager', function () {
})
}
describe('in "native" mode', () => {
let manager
describe('watchPath()', function () {
it('resolves the returned promise when the watcher begins listening', async function () {
const rootDir = await tempMkdir('atom-fsmanager-test-')
beforeEach(function () {
manager = new PathWatcherManager('native')
const watcher = await watchPath(rootDir, {}, () => {})
expect(watcher.constructor.name).toBe('PathWatcher')
})
afterEach(async function () {
await manager.stopAllWatchers()
it('reuses an existing native watcher and resolves getStartPromise immediately if attached to a running watcher', async function () {
const rootDir = await tempMkdir('atom-fsmanager-test-')
const watcher0 = await watchPath(rootDir, {}, () => {})
const watcher1 = await watchPath(rootDir, {}, () => {})
expect(watcher0.native).toBe(watcher1.native)
})
describe('watchPath()', function () {
it('resolves the returned promise when the watcher begins listening', async function () {
const rootDir = await tempMkdir('atom-fsmanager-test-')
it("reuses existing native watchers even while they're still starting", async function () {
const rootDir = await tempMkdir('atom-fsmanager-test-')
const watcher = await manager.watchPath(rootDir, {}, () => {})
expect(watcher.constructor.name).toBe('PathWatcher')
})
const [watcher0, watcher1] = await Promise.all([
watchPath(rootDir, {}, () => {}),
watchPath(rootDir, {}, () => {})
])
expect(watcher0.native).toBe(watcher1.native)
})
it('reuses an existing native watcher and resolves getStartPromise immediately if attached to a running watcher', async function () {
const rootDir = await tempMkdir('atom-fsmanager-test-')
it("doesn't attach new watchers to a native watcher that's stopping", async function () {
const rootDir = await tempMkdir('atom-fsmanager-test-')
const watcher0 = await manager.watchPath(rootDir, {}, () => {})
const watcher1 = await manager.watchPath(rootDir, {}, () => {})
const watcher0 = await watchPath(rootDir, {}, () => {})
const native0 = watcher0.native
expect(watcher0.native).toBe(watcher1.native)
})
watcher0.dispose()
const watcher1 = await watchPath(rootDir, {}, () => {})
it("reuses existing native watchers even while they're still starting", async function () {
const rootDir = await tempMkdir('atom-fsmanager-test-')
expect(watcher1.native).not.toBe(native0)
})
const [watcher0, watcher1] = await Promise.all([
manager.watchPath(rootDir, {}, () => {}),
manager.watchPath(rootDir, {}, () => {})
])
expect(watcher0.native).toBe(watcher1.native)
})
it('reuses an existing native watcher on a parent directory and filters events', async function () {
const rootDir = await tempMkdir('atom-fsmanager-test-').then(realpath)
const rootFile = path.join(rootDir, 'rootfile.txt')
const subDir = path.join(rootDir, 'subdir')
const subFile = path.join(subDir, 'subfile.txt')
it("doesn't attach new watchers to a native watcher that's stopping", async function () {
const rootDir = await tempMkdir('atom-fsmanager-test-')
await mkdir(subDir)
const watcher0 = await manager.watchPath(rootDir, {}, () => {})
const native0 = watcher0.native
// Keep the watchers alive with an undisposed subscription
const rootWatcher = await watchPath(rootDir, {}, () => {})
const childWatcher = await watchPath(subDir, {}, () => {})
watcher0.dispose()
const watcher1 = await manager.watchPath(rootDir, {}, () => {})
expect(rootWatcher.native).toBe(childWatcher.native)
expect(rootWatcher.native.isRunning()).toBe(true)
expect(watcher1.native).not.toBe(native0)
})
const firstChanges = Promise.all([
waitForChanges(rootWatcher, subFile),
waitForChanges(childWatcher, subFile)
])
await writeFile(subFile, 'subfile\n', { encoding: 'utf8' })
await firstChanges
it('reuses an existing native watcher on a parent directory and filters events', async function () {
const rootDir = await tempMkdir('atom-fsmanager-test-').then(realpath)
const rootFile = path.join(rootDir, 'rootfile.txt')
const subDir = path.join(rootDir, 'subdir')
const subFile = path.join(subDir, 'subfile.txt')
const nextRootEvent = waitForChanges(rootWatcher, rootFile)
await writeFile(rootFile, 'rootfile\n', { encoding: 'utf8' })
await nextRootEvent
})
await mkdir(subDir)
it('adopts existing child watchers and filters events appropriately to them', async function () {
const parentDir = await tempMkdir('atom-fsmanager-test-')
.then(realpath)
// Keep the watchers alive with an undisposed subscription
const rootWatcher = await manager.watchPath(rootDir, {}, () => {})
const childWatcher = await manager.watchPath(subDir, {}, () => {})
// Create the directory tree
const rootFile = path.join(parentDir, 'rootfile.txt')
const subDir0 = path.join(parentDir, 'subdir0')
const subFile0 = path.join(subDir0, 'subfile0.txt')
const subDir1 = path.join(parentDir, 'subdir1')
const subFile1 = path.join(subDir1, 'subfile1.txt')
expect(rootWatcher.native).toBe(childWatcher.native)
expect(rootWatcher.native.isRunning()).toBe(true)
await mkdir(subDir0)
await mkdir(subDir1)
await Promise.all([
writeFile(rootFile, 'rootfile\n', { encoding: 'utf8' }),
writeFile(subFile0, 'subfile 0\n', { encoding: 'utf8' }),
writeFile(subFile1, 'subfile 1\n', { encoding: 'utf8' })
])
const firstChanges = Promise.all([
waitForChanges(rootWatcher, subFile),
waitForChanges(childWatcher, subFile)
])
await writeFile(subFile, 'subfile\n', { encoding: 'utf8' })
await firstChanges
// Begin the child watchers and keep them alive
const subWatcher0 = await watchPath(subDir0, {}, () => {})
const subWatcherChanges0 = waitForChanges(subWatcher0, subFile0)
const nextRootEvent = waitForChanges(rootWatcher, rootFile)
await writeFile(rootFile, 'rootfile\n', { encoding: 'utf8' })
await nextRootEvent
})
const subWatcher1 = await watchPath(subDir1, {}, () => {})
const subWatcherChanges1 = waitForChanges(subWatcher1, subFile1)
it('adopts existing child watchers and filters events appropriately to them', async function () {
const parentDir = await tempMkdir('atom-fsmanager-test-')
.then(realpath)
expect(subWatcher0.native).not.toBe(subWatcher1.native)
// Create the directory tree
const rootFile = path.join(parentDir, 'rootfile.txt')
const subDir0 = path.join(parentDir, 'subdir0')
const subFile0 = path.join(subDir0, 'subfile0.txt')
const subDir1 = path.join(parentDir, 'subdir1')
const subFile1 = path.join(subDir1, 'subfile1.txt')
// Create the parent watcher
const parentWatcher = await watchPath(parentDir, {}, () => {})
const parentWatcherChanges = waitForChanges(
parentWatcher,
rootFile,
subFile0,
subFile1
)
await mkdir(subDir0)
await mkdir(subDir1)
await Promise.all([
writeFile(rootFile, 'rootfile\n', { encoding: 'utf8' }),
writeFile(subFile0, 'subfile 0\n', { encoding: 'utf8' }),
writeFile(subFile1, 'subfile 1\n', { encoding: 'utf8' })
])
expect(subWatcher0.native).toBe(parentWatcher.native)
expect(subWatcher1.native).toBe(parentWatcher.native)
// Begin the child watchers and keep them alive
const subWatcher0 = await manager.watchPath(subDir0, {}, () => {})
const subWatcherChanges0 = waitForChanges(subWatcher0, subFile0)
// Ensure events are filtered correctly
await Promise.all([
appendFile(rootFile, 'change\n', { encoding: 'utf8' }),
appendFile(subFile0, 'change\n', { encoding: 'utf8' }),
appendFile(subFile1, 'change\n', { encoding: 'utf8' })
])
const subWatcher1 = await manager.watchPath(subDir1, {}, () => {})
const subWatcherChanges1 = waitForChanges(subWatcher1, subFile1)
expect(subWatcher0.native).not.toBe(subWatcher1.native)
// Create the parent watcher
const parentWatcher = await manager.watchPath(parentDir, {}, () => {})
const parentWatcherChanges = waitForChanges(
parentWatcher,
rootFile,
subFile0,
subFile1
)
expect(subWatcher0.native).toBe(parentWatcher.native)
expect(subWatcher1.native).toBe(parentWatcher.native)
// Ensure events are filtered correctly
await Promise.all([
appendFile(rootFile, 'change\n', { encoding: 'utf8' }),
appendFile(subFile0, 'change\n', { encoding: 'utf8' }),
appendFile(subFile1, 'change\n', { encoding: 'utf8' })
])
await Promise.all([
subWatcherChanges0,
subWatcherChanges1,
parentWatcherChanges
])
})
await Promise.all([
subWatcherChanges0,
subWatcherChanges1,
parentWatcherChanges
])
})
})
})

View File

@ -59,7 +59,7 @@ describe('Project', () => {
})
})
it('does not deserialize paths that are now files', async () => {
it('does not deserialize paths that are now files', () => {
const childPath = path.join(temp.mkdirSync('atom-spec-project'), 'child')
fs.mkdirSync(childPath)
@ -70,21 +70,22 @@ describe('Project', () => {
grammarRegistry: atom.grammars
})
atom.project.setPaths([childPath])
await stopAllWatchers()
const state = atom.project.serialize()
fs.rmdirSync(childPath)
fs.writeFileSync(childPath, 'surprise!\n')
let err = null
try {
await deserializedProject.deserialize(state, atom.deserializers)
} catch (e) {
err = e
}
waitsForPromise(() =>
deserializedProject.deserialize(state, atom.deserializers).catch(e => {
err = e
})
)
expect(deserializedProject.getPaths()).toEqual([])
expect(err.missingProjectPaths).toEqual([childPath])
runs(() => {
expect(deserializedProject.getPaths()).toEqual([])
expect(err.missingProjectPaths).toEqual([childPath])
})
})
it('does not include unretained buffers in the serialized state', () => {
@ -1055,20 +1056,18 @@ describe('Project', () => {
afterEach(() => sub.dispose())
const waitForEvents = paths => {
const remaining = new Set(paths.map(path => fs.realpathSync(path)))
const remaining = new Set(paths.map(p => fs.realpathSync(p)))
return new Promise((resolve, reject) => {
checkCallback = () => {
for (let event of events) {
remaining.delete(event.path)
}
if (remaining.size === 0) {
clearTimeout(timeout)
resolve()
}
}
const expire = () => {
clearTimeout(interval)
checkCallback = () => {}
console.error('Paths not seen:', remaining)
reject(
@ -1076,33 +1075,37 @@ describe('Project', () => {
)
}
const interval = setInterval(checkCallback, 100)
const timeout = setTimeout(expire, 2000)
checkCallback()
setTimeout(expire, 2000)
})
}
it('reports filesystem changes within project paths', async () => {
it('reports filesystem changes within project paths', () => {
const dirOne = temp.mkdirSync('atom-spec-project-one')
const fileOne = path.join(dirOne, 'file-one.txt')
const fileTwo = path.join(dirOne, 'file-two.txt')
const dirTwo = temp.mkdirSync('atom-spec-project-two')
const fileThree = path.join(dirTwo, 'file-three.txt')
await stopAllWatchers()
// Ensure that all preexisting watchers are stopped
waitsForPromise(() => stopAllWatchers())
atom.project.setPaths([dirOne])
runs(() => atom.project.setPaths([dirOne]))
waitsForPromise(() => atom.project.getWatcherPromise(dirOne))
await atom.project.getWatcherPromise(dirOne)
runs(() => {
expect(atom.project.watcherPromisesByPath[dirTwo]).toEqual(undefined)
expect(atom.project.watcherPromisesByPath[dirTwo]).toEqual(undefined)
fs.writeFileSync(fileThree, 'three\n')
fs.writeFileSync(fileTwo, 'two\n')
fs.writeFileSync(fileOne, 'one\n')
})
fs.writeFileSync(fileThree, 'three\n')
fs.writeFileSync(fileTwo, 'two\n')
fs.writeFileSync(fileOne, 'one\n')
waitsForPromise(() => waitForEvents([fileOne, fileTwo]))
await waitForEvents([fileOne, fileTwo])
expect(events.some(event => event.path === fileThree)).toBeFalsy()
runs(() =>
expect(events.some(event => event.path === fileThree)).toBeFalsy()
)
})
})

View File

@ -14,7 +14,11 @@ describe('TextEditorRegistry', function () {
assert: atom.assert,
config: atom.config,
grammarRegistry: atom.grammars,
packageManager: { deferredActivationHooks: null }
packageManager: {
getActivatePromise() {
return initialPackageActivation
}
}
})
editor = new TextEditor({ autoHeight: false })
@ -112,32 +116,22 @@ describe('TextEditorRegistry', function () {
})
it('does not update the editor before the initial packages have loaded', async function () {
let didActivateInitialPackagesCallback
registry = new TextEditorRegistry({
assert: atom.assert,
config: atom.config,
grammarRegistry: atom.grammars,
packageManager: {
deferredActivationHooks: [],
onDidActivateInitialPackages (callback) {
didActivateInitialPackagesCallback = callback
}
}
let resolveActivatePromise
initialPackageActivation = new Promise(resolve => {
resolveActivatePromise = resolve
})
atom.config.set('core.fileEncoding', 'utf16le')
registry.maintainConfig(editor)
await initialPackageActivation
await Promise.resolve()
expect(editor.getEncoding()).toBe('utf8')
atom.config.set('core.fileEncoding', 'utf16be')
await initialPackageActivation
await Promise.resolve()
expect(editor.getEncoding()).toBe('utf8')
didActivateInitialPackagesCallback()
resolveActivatePromise()
await initialPackageActivation
expect(editor.getEncoding()).toBe('utf16be')
})

View File

@ -319,9 +319,9 @@ h2 {
it('returns a disposable allowing styles applied by the given path to be removed', function () {
const cssPath = require.resolve('./fixtures/css.css')
expect(getComputedStyle(document.body).fontWeight).not.toBe('bold')
expect(getComputedStyle(document.body).fontWeight).not.toBe('700')
const disposable = atom.themes.requireStylesheet(cssPath)
expect(getComputedStyle(document.body).fontWeight).toBe('bold')
expect(getComputedStyle(document.body).fontWeight).toBe('700')
let styleElementRemovedHandler
atom.styles.onDidRemoveStyleElement(

View File

@ -329,17 +329,17 @@ const configSchema = {
default: 40
},
fileSystemWatcher: {
description: 'Choose the underlying implementation used to watch for filesystem changes. Emulating changes will miss any events caused by applications other than Atom, but may help prevent crashes or freezes. Polling may be useful for network drives, but will be more costly in terms of CPU overhead.<br>This setting will require a relaunch of Atom to take effect.',
description: 'Choose the underlying implementation used to watch for filesystem changes. Emulating changes will miss any events caused by applications other than Atom, but may help prevent crashes or freezes.',
type: 'string',
default: 'native',
enum: [
{
value: 'native',
description: 'Native operating system APIs (@atom/nsfw)'
description: 'Native operating system APIs'
},
{
value: 'experimental',
description: 'Experimental (@atom/notify)'
description: 'Experimental filesystem watching library'
},
{
value: 'poll',
@ -351,11 +351,6 @@ const configSchema = {
}
]
},
fileSystemWatcherPollInterval: {
description: "If the 'Polling' option is selected for the file system watcher, this will be the interval between polls.",
type: 'number',
default: 1000
},
useTreeSitterParsers: {
type: 'boolean',
default: true,

View File

@ -271,18 +271,17 @@ class AtomApplication extends EventEmitter {
async launch (options) {
if (!this.configFilePromise) {
this.configFilePromise = this.configFile.watch()
this.configFilePromise = this.configFile.watch().then(disposable => {
this.disposable.add(disposable)
this.config.onDidChange('core.titleBar', () => this.promptForRestart())
this.config.onDidChange('core.colorProfile', () => this.promptForRestart())
})
// TodoElectronIssue: In electron v2 awaiting the watcher causes some delay
// in Windows machines, which affects directly the startup time.
if (process.platform === 'win32') {
this.configFilePromise.then(disposable => this.disposable.add(disposable))
} else {
this.disposable.add(await this.configFilePromise)
if (process.platform !== 'win32') {
await this.configFilePromise
}
this.config.onDidChange('core.titleBar', () => this.promptForRestart())
this.config.onDidChange('core.colorProfile', () => this.promptForRestart())
this.config.onDidChange('core.fileSystemWatcher', () => this.promptForRestart())
}
let optionsForWindowsToOpen = []

View File

@ -90,6 +90,7 @@ module.exports = class PackageManager {
this.packagesCache = packageJSON._atomPackages != null ? packageJSON._atomPackages : {}
this.packageDependencies = packageJSON.packageDependencies != null ? packageJSON.packageDependencies : {}
this.triggeredActivationHooks.clear()
this.activatePromise = null
}
/*
@ -114,6 +115,14 @@ module.exports = class PackageManager {
return this.emitter.on('did-activate-initial-packages', callback)
}
getActivatePromise() {
if (this.activatePromise) {
return this.activatePromise
} else {
return Promise.resolve()
}
}
// Public: Invoke the given callback when a package is activated.
//
// * `callback` A {Function} to be invoked when a package is activated.
@ -659,11 +668,13 @@ module.exports = class PackageManager {
const packages = this.getLoadedPackagesForTypes(types)
promises = promises.concat(activator.activatePackages(packages))
}
return Promise.all(promises).then(() => {
this.activatePromise = Promise.all(promises).then(() => {
this.triggerDeferredActivationHooks()
this.initialPackagesActivated = true
this.emitter.emit('did-activate-initial-packages')
this.activatePromise = null
})
return this.activatePromise
}
registerURIHandlerForPackage (packageName, handler) {

View File

@ -2,9 +2,8 @@ const fs = require('fs')
const path = require('path')
const {Emitter, Disposable, CompositeDisposable} = require('event-kit')
const NotifyWatcher = require('@atom/notify')
const nsfw = require('@atom/nsfw')
const watcher = require('@atom/watcher')
const {NativeWatcherRegistry} = require('./native-watcher-registry')
// Private: Associate native watcher action flags with descriptive String equivalents.
@ -385,7 +384,12 @@ class PathWatcher {
return this.normalizedPathPromise
}
// Private: Return a {Promise} that will resolve when the underlying native watcher is ready to begin sending events.
// Private: Return a {Promise} that will resolve the first time that this watcher is attached to a native watcher.
getAttachedPromise () {
return this.attachedPromise
}
// Extended: Return a {Promise} that will resolve when the underlying native watcher is ready to begin sending events.
// When testing filesystem watchers, it's important to await this promise before making filesystem changes that you
// intend to assert about because there will be a delay between the instantiation of the watcher and the activation
// of the underlying OS resources that feed its events.
@ -540,18 +544,50 @@ class PathWatcherManager {
// Private: Access the currently active manager instance, creating one if necessary.
static active () {
if (!this.activeManager) {
this.activeManager = new PathWatcherManager(
atom.config.get('core.fileSystemWatcher'),
atom.config.get('core.fileSystemWatcherPollInterval')
)
this.activeManager = new PathWatcherManager(atom.config.get('core.fileSystemWatcher'))
this.sub = atom.config.onDidChange('core.fileSystemWatcher', ({newValue}) => { this.transitionTo(newValue) })
}
return this.activeManager
}
// Private: Replace the active {PathWatcherManager} with a new one that creates [NativeWatchers]{NativeWatcher}
// based on the value of `setting`.
static async transitionTo (setting) {
const current = this.active()
if (this.transitionPromise) {
await this.transitionPromise
}
if (current.setting === setting) {
return
}
current.isShuttingDown = true
let resolveTransitionPromise = () => {}
this.transitionPromise = new Promise(resolve => {
resolveTransitionPromise = resolve
})
const replacement = new PathWatcherManager(setting)
this.activeManager = replacement
await Promise.all(
Array.from(current.live, async ([root, native]) => {
const w = await replacement.createWatcher(root, {}, () => {})
native.reattachTo(w.native, root, w.native.options || {})
})
)
current.stopAllWatchers()
resolveTransitionPromise()
this.transitionPromise = null
}
// Private: Initialize global {PathWatcher} state.
constructor (setting, pollInterval) {
constructor (setting) {
this.setting = setting
this.pollInterval = pollInterval
this.live = new Map()
const initLocal = NativeConstructor => {
@ -572,9 +608,15 @@ class PathWatcherManager {
if (setting === 'atom') {
initLocal(AtomNativeWatcher)
} else if (setting === 'native') {
} else if (setting === 'experimental') {
//
} else if (setting === 'poll') {
//
} else {
initLocal(NSFWNativeWatcher)
}
this.isShuttingDown = false
}
useExperimentalWatcher () {
@ -582,54 +624,66 @@ class PathWatcherManager {
}
// Private: Create a {PathWatcher} tied to this global state. See {watchPath} for detailed arguments.
async watchPath (rootPath, options, eventCallback) {
async createWatcher (rootPath, options, eventCallback) {
if (this.isShuttingDown) {
await this.constructor.transitionPromise
return PathWatcherManager.active().createWatcher(rootPath, options, eventCallback)
}
if (this.useExperimentalWatcher()) {
if (!this.notifyWatcher) {
const options = {
transformBinPath: (binPath) => binPath.replace(/\bapp\.asar\b/, 'app.asar.unpacked'),
onError: error => {
throw new Error(`Error watching file system: ${error}`)
}
}
if (this.setting === 'poll') {
options.pollInterval = this.pollInterval
}
this.notifyWatcher = new NotifyWatcher(options)
if (this.setting === 'poll') {
options.poll = true
}
const watch = await this.notifyWatcher.watchPath(rootPath, event => {
if (event.action === 'error') {
watch.emitter.emit('error', event.description)
throw new Error(`Error watching file system at "${event.path}": ${event.description}`)
} else {
eventCallback(event)
}
})
watch.emitter = new Emitter()
watch.onDidError = function (handler) {
return this.emitter.on('error', handler)
}
return watch
} else {
const w = new PathWatcher(this.nativeRegistry, rootPath, options)
w.onDidChange(eventCallback)
await w.getStartPromise()
const w = await watcher.watchPath(rootPath, options, eventCallback)
this.live.set(rootPath, w.native)
return w
}
const w = new PathWatcher(this.nativeRegistry, rootPath, options)
w.onDidChange(eventCallback)
await w.getStartPromise()
return w
}
// Private: Directly access the {NativeWatcherRegistry}.
getRegistry () {
if (this.useExperimentalWatcher()) {
return watcher.getRegistry()
}
return this.nativeRegistry
}
// Private: Sample watcher usage statistics. Only available for experimental watchers.
status () {
if (this.useExperimentalWatcher()) {
return watcher.status()
}
return {}
}
// Private: Return a {String} depicting the currently active native watchers.
print () {
if (this.useExperimentalWatcher()) {
return watcher.printWatchers()
}
return this.nativeRegistry.print()
}
// Private: Stop all living watchers.
//
// Returns a {Promise} that resolves when all native watcher resources are disposed.
async stopAllWatchers () {
stopAllWatchers () {
if (this.useExperimentalWatcher()) {
await this.notifyWatcher.kill()
this.notifyWatcher = null
} else {
await Promise.all(
Array.from(this.live, ([, w]) => w.stop())
)
return watcher.stopAllWatchers()
}
return Promise.all(
Array.from(this.live, ([, w]) => w.stop())
)
}
}
@ -673,7 +727,7 @@ class PathWatcherManager {
// ```
//
function watchPath (rootPath, options, eventCallback) {
return PathWatcherManager.active().watchPath(rootPath, options, eventCallback)
return PathWatcherManager.active().createWatcher(rootPath, options, eventCallback)
}
// Private: Return a Promise that resolves when all {NativeWatcher} instances associated with a FileSystemManager
@ -682,4 +736,24 @@ function stopAllWatchers () {
return PathWatcherManager.active().stopAllWatchers()
}
module.exports = {watchPath, stopAllWatchers, PathWatcherManager}
// Private: Show the currently active native watchers in a formatted {String}.
watchPath.printWatchers = function () {
return PathWatcherManager.active().print()
}
// Private: Access the active {NativeWatcherRegistry}.
watchPath.getRegistry = function () {
return PathWatcherManager.active().getRegistry()
}
// Private: Sample usage statistics for the active watcher.
watchPath.status = function () {
return PathWatcherManager.active().status()
}
// Private: Configure @atom/watcher ("experimental") directly.
watchPath.configure = function (...args) {
return watcher.configure(...args)
}
module.exports = {watchPath, stopAllWatchers}

View File

@ -4449,7 +4449,7 @@ class NodePool {
if (element) {
element.className = className || ''
element.styleMap.forEach((value, key) => {
element.attributeStyleMap.forEach((value, key) => {
if (!style || style[key] == null) element.style[key] = ''
})
if (style) Object.assign(element.style, style)

View File

@ -38,19 +38,10 @@ const EDITOR_PARAMS_BY_SETTING_KEY = [
module.exports =
class TextEditorRegistry {
constructor ({config, assert, packageManager}) {
this.assert = assert
this.config = config
this.assert = assert
this.packageManager = packageManager
this.clear()
this.initialPackageActivationPromise = new Promise((resolve) => {
// TODO: Remove this usage of a private property of PackageManager.
// Should PackageManager just expose a promise-based API like this?
if (packageManager.deferredActivationHooks) {
packageManager.onDidActivateInitialPackages(resolve)
} else {
resolve()
}
})
}
deserialize (state) {
@ -216,7 +207,7 @@ class TextEditorRegistry {
}
async updateAndMonitorEditorSettings (editor, oldLanguageMode) {
await this.initialPackageActivationPromise
await this.packageManager.getActivatePromise()
this.updateEditorSettingsForLanguageMode(editor, oldLanguageMode)
this.subscribeToSettingsForEditorScope(editor)
}