Merge branch 'sqlite3' into backports-electron-19-changes-to-12

This commit is contained in:
Maurício Szabo 2023-03-15 00:06:03 -03:00
commit 8174b679e0
763 changed files with 67084 additions and 10146 deletions

View File

@ -4,7 +4,11 @@ module.exports = {
commonjs: true,
es2021: true,
},
extends: ["eslint:recommended", "plugin:node/recommended"],
extends: [
"eslint:recommended",
"plugin:node/recommended",
"plugin:jsdoc/recommended"
],
overrides: [],
parserOptions: {
ecmaVersion: "latest"
@ -17,6 +21,9 @@ module.exports = {
}
]
},
plugins: [
"jsdoc"
],
globals: {
atom: "writeable"
}

42
.github/workflows/documentation.yml vendored Normal file
View File

@ -0,0 +1,42 @@
name: Documentation
on:
push:
branches: [ "master" ]
workflow_dispatch:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
jobs:
documentation:
runs-on: ubuntu-latest
permissions:
contents: write
strategy:
matrix:
node-version: [16.x]
steps:
- name: Checkout the Latest Code
uses: actions/checkout@v3
- name: Setup NodeJS - ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Install Dependencies
run: yarn install
- name: Generate Public JSDocs
run: yarn run js-docs
- name: Generate Private JSDocs
run: yarn run private-js-docs
- name: Commit All Changes
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: GH Action Documentation

View File

@ -1,10 +1,10 @@
name: Editor tests
on:
- push
- pull_request
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ATOM_JASMINE_REPORTER: list
jobs:
tests:
name: tests
@ -13,7 +13,8 @@ jobs:
!startsWith(github.event.pull_request.title, '[skip-editor-ci]')
strategy:
matrix:
os: [ubuntu-20.04, macos-latest, windows-2019]
# os: [ubuntu-20.04, macos-latest, windows-2019]
os: [ubuntu-20.04, macos-latest]
fail-fast: false
runs-on: ${{ matrix.os }}
steps:
@ -34,4 +35,4 @@ jobs:
- name: Run tests
uses: GabrielBB/xvfb-action@v1
with:
run: yarn start --test spec
run: node script/run-tests.js spec

View File

@ -1,9 +1,9 @@
name: Package tests for Pulsar on Linux
on:
- push
- pull_request
env:
APM_PATH: ./apm/node_modules/atom-package-manager/bin/apm
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ATOM_JASMINE_REPORTER: list
jobs:
setup:
name: Build Editor
@ -12,12 +12,6 @@ jobs:
!startsWith(github.event.pull_request.title, '[skip-package-ci]')
runs-on: ubuntu-20.04
steps:
- name: Install windows-build-tools
if: ${{ matrix.os == 'windows-latest' }}
run: |
npm i --global windows-build-tools@4.0.0
npm config set msvs_version 2019
- name: Checkout the latest code
uses: actions/checkout@v2
@ -35,60 +29,144 @@ jobs:
- name: build dependencies
run: yarn build:apm
- name: Cache node_modules
id: cache-node
uses: actions/cache@v3
with:
path: node_modules
key: linux-modules-${{ hashFiles('package.json') }}
- name: build the editor
run: (yarn dist deb || yarn dist deb) && mv binaries/*deb pulsar.deb
- name: Cache apm
id: cache-apm
- name: Cache pulsar
id: cache-pulsar
uses: actions/cache@v3
with:
path: apm
key: linux-apm-${{ hashFiles('apm/package.json') }}
path: pulsar.deb
key: pulsar-${{ github.sha }}
test:
name: Test Packages
name: Package
needs: setup
runs-on: ubuntu-20.04
strategy:
fail-fast: false
max-parallel: 8
matrix:
include:
- filter: autocomplete|language
descr: syntax-related
- filter: dark|light
descr: themes
- filter: git-diff|go-to-line|grammar-selector|line-ending-selector|autoflow|autosave|symbols-view|spell-check|bracket-matcher|whitespace|wrap-guide|snippets|encoding-selector
descr: editor related
- filter: deprecation-cop|dev-live-reload|incompatible-packages|package-generator|exception-reporting|metrics|update-package-dependencies|styleguide
descr: development helpers
- filter: welcome|about|background-tips|tabs|tree-view|archive-view|markdown-preview|status-bar|settings-view|notifications
descr: UI elements
- filter: image-view|bookmarks|keybinding-resolver|link|timecop
descr: others
- package: "atom-dark-syntax"
- package: "atom-dark-ui"
- package: "atom-light-syntax"
- package: "atom-light-ui"
- package: "base16-tomorrow-dark-theme"
- package: "base16-tomorrow-light-theme"
- package: "one-dark-ui"
- package: "one-light-ui"
- package: "one-dark-syntax"
- package: "one-light-syntax"
- package: "solarized-dark-syntax"
- package: "solarized-light-syntax"
- package: "about"
- package: "archive-view"
- package: "autocomplete-atom-api"
- package: "autocomplete-css"
- package: "autocomplete-html"
- package: "autocomplete-plus"
- package: "autocomplete-snippets"
- package: "autoflow"
- package: "autosave"
- package: "background-tips"
- package: "bookmarks"
- package: "bracket-matcher"
- package: "command-palette"
- package: "dalek"
- package: "deprecation-cop"
- package: "dev-live-reload"
- package: "encoding-selector"
- package: "exception-reporting"
- package: "find-and-replace"
- package: "fuzzy-finder"
- package: "github"
- package: "git-diff"
- package: "go-to-line"
- package: "grammar-selector"
- package: "image-view"
- package: "incompatible-packages"
- package: "keybinding-resolver"
- package: "line-ending-selector"
- package: "link"
- package: "markdown-preview"
- package: "notifications"
- package: "open-on-github"
- package: "package-generator"
- package: "settings-view"
- package: "snippets"
- package: "spell-check"
- package: "status-bar"
- package: "styleguide"
- package: "symbols-view"
- package: "tabs"
- package: "timecop"
- package: "tree-view"
- package: "update-package-dependencies"
- package: "welcome"
- package: "whitespace"
- package: "wrap-guide"
- package: "language-c"
- package: "language-clojure"
- package: "language-coffee-script"
- package: "language-csharp"
- package: "language-css"
- package: "language-gfm"
- package: "language-git"
- package: "language-go"
- package: "language-html"
- package: "language-hyperlink"
- package: "language-java"
- package: "language-javascript"
- package: "language-json"
- package: "language-less"
- package: "language-make"
- package: "language-mustache"
- package: "language-objective-c"
- package: "language-perl"
- package: "language-php"
- package: "language-property-list"
- package: "language-python"
- package: "language-ruby"
- package: "language-ruby-on-rails"
- package: "language-rust-bundled"
- package: "language-sass"
- package: "language-shellscript"
- package: "language-source"
- package: "language-sql"
- package: "language-text"
- package: "language-todo"
- package: "language-toml"
- package: "language-typescript"
- package: "language-xml"
- package: "language-yaml"
steps:
- name: Checkout the latest code
uses: actions/checkout@v2
- name: Setup NodeJS
uses: actions/setup-node@v3
with:
node-version: 16
- name: Install Dependencies
run: yarn install || yarn install
- name: Restore node_modules from Cache
id: restore-node
- name: Build Dependencies
run: yarn build || yarn build
- name: Restore pulsar from Cache
id: restore-pulsar
uses: actions/cache@v3
with:
path: node_modules
key: linux-modules-${{ hashFiles('package.json') }}
path: pulsar.deb
key: pulsar-${{ github.sha }}
- name: Restore apm from Cache
id: restore-apm
uses: actions/cache@v3
with:
path: apm
key: linux-apm-${{ hashFiles('apm/package.json') }}
- name: Install Pulsar
run: sudo dpkg -i pulsar.deb && sudo apt-get -f install -y
- name: Run Package Tests for ${{ matrix.descr }}
uses: GabrielBB/xvfb-action@v1
with:
run: node -e "require('./script/run-package-tests')(/${{ matrix.filter }}/)"
- name: Install xvfb
run: sudo apt-get install -y xvfb
- name: Run ${{ matrix.package }} Tests
run: Xvfb :1 & cd node_modules/${{ matrix.package }} && if test -d spec; then DISPLAY=:1 pulsar --test spec; fi
# run: node -e "require('./script/run-package-tests')(/${{ matrix.package }}/)"

View File

@ -1,14 +1,304 @@
# Pulsar next version
# Changelog
- Format inspired by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- Format defined in [Pulsar Change Log](PENDING_APPROVAL)
- Project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
## [Unreleased]
## 1.102.0
- Fixed a bug where `pulsar` on Windows could never trigger
- Fixed `github` package shelling out to `git` on macOS
- Fixed minor bugs found during fixes to tests
- Improved our testing infastructure to aide in finding and fixing further bugs
- Updated many dependencies of Pulsar and its core packages
- New Pulsar Icon on macOS
- Selected text is styled by default
- Restored `right-clicked` CSS class on tags
- Fixed syntax highlighting on C++
- Updated JavaScript snippets to modern ES6 syntax
- PPM no longer assumes `master` for git branches
### Pulsar
- Added: implement signing and notarizing for macOS, PR #4 lol [@Meadowsys](https://github.com/pulsar-edit/pulsar/pull/387)
- Fixed: Pin `python` brew installation to `3.10` during MacOS Intel Cirrus Build [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/384)
- Update: Bump `ppm` to `a46537c0b7f0eaaef5404ef88003951fdc988c65` [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/383)
- Added: Add new macOS icon [@mdibella-dev](https://github.com/pulsar-edit/pulsar/pull/372)
- Fixed: type $ as # [@Meadowsys](https://github.com/pulsar-edit/pulsar/pull/378)
- Update: deps: Update github to v0.36.14-pretranspiled-take-2 [@DeeDeeG](https://github.com/pulsar-edit/pulsar/pull/375)
- Added: add style to selected text by default [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/238)
- Added: Set Max Concurrent Package Tests [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/376)
- Fixed: c++ fixes [@icecream17](https://github.com/pulsar-edit/pulsar/pull/369)
- Update: deps: Update github to v0.36.14-pretranspiled [@DeeDeeG](https://github.com/pulsar-edit/pulsar/pull/373)
- Update `coffeescript` [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/361)
- Updated: Misc Dependency Updates [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/362)
- Added: Bundle `autocomplete-plus` [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/358)
- Fixed: Add LICENSE.md to extra resources (resourcesPath) [@Daeraxa](https://github.com/pulsar-edit/pulsar/pull/354)
- Fixed: Get Windows `pulsar` Working [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/340)
- Fixed: Restore `right-clicked` class on a right-clicked tab [@savetheclocktower](https://github.com/pulsar-edit/pulsar/pull/368)
- Updated: ppm: Update submodule to commit 4645ba2905747897b0 [@DeeDeeG](https://github.com/pulsar-edit/pulsar/pull/371)
- Added: Machine decaf tabs spec [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/367)
- Added: Manually Decaf `tabs` package Specs [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/357)
- Fixed: Uncomment and fix a settings-view package test [@DeeDeeG](https://github.com/pulsar-edit/pulsar/pull/366)
- Added: Decaf Changes from Manual and Machine Decaf to Main [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/356)
- Added: Manual decafe tabs [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/352)
- Added: Organize failing tests [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/307)
- Fixed: autocomplete-snippets: Fix repo URL [@DeeDeeG](https://github.com/pulsar-edit/pulsar/pull/341)
- Updated: update apm message to pulsar -p [@Daeraxa](https://github.com/pulsar-edit/pulsar/pull/337)
- Fixed: Replace incorrect spellings of 'macOS' with the correct one [@mdibella-dev](https://github.com/pulsar-edit/pulsar/pull/336)
- Changed: use `let` and `const` in js snippets [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/326)
- Fixed: Fix URI to correct address [@mdibella-dev](https://github.com/pulsar-edit/pulsar/pull/335)
- Updated: update copyright year (2023) [@icecream17](https://github.com/pulsar-edit/pulsar/pull/332)
### ppm
- Fixed: fix: Don't assume `master` when checking git packages for upgrades [@savetheclocktower](https://github.com/pulsar-edit/ppm/pull/56)
- Fixed: meta: Normalize package.json and lockfile line endings [@DeeDeeG](https://github.com/pulsar-edit/ppm/pull/54)
- Update: spec: Fixtures Node v10.20.1 --> Electron v12.2.3 [@DeeDeeG](https://github.com/pulsar-edit/ppm/pull/52)
- Fixed: Fix .com links, pulsar rebranding and rebranding readme [@Daeraxa](https://github.com/pulsar-edit/ppm/pull/48)
### github
- Fixed: lib: Rebrand getAtomAppName() function (fix shelling out to `git` on macOS) [@DeeDeeG](https://github.com/pulsar-edit/github/pull/13)
- Fixed: meta: Revert "main" to "./lib/index", no dist (fix package on `master` branch) [@DeeDeeG](https://github.com/pulsar-edit/github/pull/12)
## 1.101.0-beta
- Fixed a bug where macOS menus like "Open" don't do anything
- Fixed a bug where macOS wouldn't open files by dragging them onto the dock.
- Fixed a bug where devtools won't open (https://github.com/pulsar-edit/pulsar/issues/260)
- Fixed a bug where the editor refused to open with the message "GPU process
isn't usable. Goodbye" (https://github.com/pulsar-edit/pulsar/issues/233)
- Fixed logo artifacts on Linux
- Fixed Windows Taskbar Icon being 'Cut in Half'
- Fixed commands like `--version`, `--package` or `--help` did not show outputs
- Fixed additional flags not being sent to `--package`
- Small improvement on the binary size
- Fixed "install command line tools" on Mac and Windows
- Cached queries for featured packages (featured packages will load faster, and
fewer errors on the settings-view regarding package info)
- Added warning when `settings-view` is disabled, describing how to re-enable it
### Pulsar
- Added: script: Clean up `pulsar` and `ppm` on uninstall [@DeeDeeG](https://github.com/pulsar-edit/pulsar/pull/297)
- Added: increase search query delay [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/289)
- Fixed: update `packages/README.md` [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/317)
- Fixed: Fix Windows Icon being cut in half [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/318)
- Removed: remove unused json [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/309)
- Added: add ignored `package-lock.json` to packages [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/308)
- Rebrand: Rebrand AppUserModelID - Ensure Pulsar is separated as its own App Icon on Windows [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/315)
- Removed: remove fs-plus from image-view package [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/305)
- Added: Additional Bundling of Core Packages [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/314)
- Fixed: Resolve some `about` package tests (6 Resolved Tests) [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/310)
- Fixed: Fix Package Test Cache Issue [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/302)
- Fixed: Resolve all Tests within `language-html` (Resolves 2 Failing Tests) [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/300)
- Fixed: Resolve all Tests within `language-javascript` (Resolves 24 Failing Tests) [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/299)
- Fixed: Resolve 40 Failing `image-view` Tests [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/293)
- Added: Added changelog entries that we missed [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/292)
- Removed: meta: Delete preinstall script from package.json [@DeeDeeG](https://github.com/pulsar-edit/pulsar/pull/296)
- Added: Improve macOS Builds [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/280)
- Fixed: Fix `archive-view` [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/294)
- Added: Improved Windows Builds [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/279)
- Added: More Bundles [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/290)
- Fixed: Fix macOS open without window [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/291)
- Removed: delete workflow from language-java [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/285)
- Removed: Remove handlers for opening things on Mac [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/288)
- Rebrand: Rebranding and relinking to new site [@Daeraxa](https://github.com/pulsar-edit/pulsar/pull/282)
- Added: script: symlink ppm in post-install.sh (for .deb and .rpm packages) [@DeeDeeG](https://github.com/pulsar-edit/pulsar/pull/273)
- Added: Add --no-sandbox to start script [@Daeraxa](https://github.com/pulsar-edit/pulsar/pull/276)
- Added: exclude directories from build [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/265)
- Added: add warning when settings-view is disabled [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/243)
- Fixed: Fix typo [@snowcatridge10](https://github.com/pulsar-edit/pulsar/pull/267)
- Fixed: Fix install on packaged code [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/269)
- Fixed: Fix Logo weirdness [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/271)
- Fixed: Fix installing shell commands to path (macOS) [@DeeDeeG](https://github.com/pulsar-edit/pulsar/pull/263)
- Fixed: 🍎 Fix wrong app name resolution in pulsar.sh on Mac [@soupertonic](https://github.com/pulsar-edit/pulsar/pull/252)
- Fixed: Postinstall error with rm usr/bin/pulsar [@Spiker985](https://github.com/pulsar-edit/pulsar/pull/228)
- Added: Made changes to the main.js file. [@CatPerson136](https://github.com/pulsar-edit/pulsar/pull/232)
- Added: Add `--no-sandbox` to Linux Launch [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/262)
- Removed: removed unused files [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/219)
- Rebrand: rebrand package publish domain [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/245)
- Removed: remove metrics code from welcome package [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/244)
- Fixed: Deep cache for settings view [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/250)
- Fixed: fix syntax error in `packages/README.md` [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/248)
- Removed: remove package.json dependencies [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/169)
- Added: `underscore-plus` to dependencies [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/218)
### ppm
- Added: Convert body params to query params [@Spiker985](https://github.com/pulsar-edit/ppm/pull/47)
- Fixed: src: Update Electron header download URL [@DeeDeeG](https://github.com/pulsar-edit/ppm/pull/43)
## v1.100.0-beta
- Bump to Electron 12 and Node 14
- Added a rebranded API
- Added a rebranding API
- Removed experimental file watchers on the editor
- Ability to install packages from git repositories
- New backend
- Better error messages when we can't install a package
- New Pulsar Package Repository Backend
- Better error messages when installing a package fails
- Config watching fixes
- Bump tree-sitter to 0.20.1 and all grammars to their recent versions
- Native support for Apple Silicon
- Change Log Window added to Welcome Package
- Native support for Apple Silicon and ARM Linux
- Removed Benchmark Startup Mode
- Removed all telemetry from Core Editor
- New Pulsar Website
- New Test Runner to Improve Testing
- Added Apple Silicon support to `github` Package v0.36.13
# Atom v1.6.0
See https://atom.io/releases
### Pulsar
- Added: Incorporate settings-view to core [@Daeraxa](https://github.com/pulsar-edit/pulsar/pull/220)
- Added: Bundle `autocomplete-css` && `autocomplete-html` [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/212)
- Added: add or update `packages/*/package-lock.json` [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/209)
- Fixed: Organize our Exclusions/Inclusions [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/208)
- Added: Bundle `package-generator` [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/207)
- Fixed: meta: Don't exclude 'loophole' or 'pegjs' packages [@DeeDeeG](https://github.com/pulsar-edit/pulsar/pull/206)
- Fixed: Fix `dugite` [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/201)
- Bumped: ppm: Update ppm submodule (new Electron headers download URL) [@DeeDeeG](https://github.com/pulsar-edit/pulsar/pull/198)
- Removed: Revert "Merge pull request #184 from pulsar-edit/bump-autocomplete-plus" [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/196)
- Bumped: Bump GitHub package [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/186)
- Fixed: CI (Windows): Use npm (not yarn) to install ppm [@DeeDeeG](https://github.com/pulsar-edit/pulsar/pull/185)
- Bumped: Bumped `autocomplete-plus` [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/184)
- Added: Adding test runner missing files [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/183)
- Fixed: fix about package test [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/180)
- Added: Add tar.gz target to electron-builder [@Daeraxa](https://github.com/pulsar-edit/pulsar/pull/178)
- Fixed: Cleanup/standardize pulsar.sh [@Spiker985](https://github.com/pulsar-edit/pulsar/pull/175)
- Fixed: Update LICENSE.md [@Daeraxa](https://github.com/pulsar-edit/pulsar/pull/171)
- Removed: remove old scripts [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/168)
- Fixed: Fix Codacy Ignore [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/167)
- Added: New ChangeLog Format [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/166)
- Fixed: shorten task description if too long [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/163)
- Fixed: Improve Package Tests [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/161)
- Removed: Metric docs from `welcome` [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/159)
- Fixed: PostInstall of `ppm` [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/153)
- Fixed: Unmerged Menus ignoring separators [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/151)
- Removed: `mkdirp` [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/150)
- Fixed: `--package` exiting incorrectly [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/149)
- Bumped: `ppm` submodule [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/144)
- Fixed: undefined `nsole` [@jonian](https://github.com/pulsar-edit/pulsar/pull/142)
- Fixed: Git tab in Binaries [@benonymus](https://github.com/pulsar-edit/pulsar/pull/140)
- Fixed: `yarn.lock` versions [@jonian](https://github.com/pulsar-edit/pulsar/pull/139)
- Added: `dist` & `binaries` to `gitignore` [@jonain](https://github.com/pulsar-edit/pulsar/pull/138)
- Bumped: `ppm` submodule to allow Git Package Install [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/131)
- Bumped: `settings-view` 0.261.9 -> 0.261.10 [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/130)
- Removed: Unused code fragments from build scripts [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/128)
- Added: Ability to run `ppm` from `pulsar` CLI [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/125)
- Fixed: base16 URL to use WayBack Machine [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/121)
- Removed: `fs-plus` from `exception-reporting` [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/118)
- Removed: Benchmark Startup Mode Part 2 [@DeeDeeG](https://github.com/pulsar-edit/pulsar/pull/115)
- Removed: Unused scripts [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/114)
- Bumped: `background-tips` 0.28.0 -> 0.28.1 [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/111)
- Removed: Tooling bloat [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/110)
- Bumped: `snippets` NA -> 1.6.1 [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/107)
- Removed: Benchmark Startup mode [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/105)
- Added: Binaries for Intel Mac & ARM Linux [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/101)
- Added: `yarn dist` accepts arguments [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/97)
- Fixed: Load core packages `README.md` [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/96)
- Fixed: Unlock terminal on Linux [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/82)
- Added: Aliases to workflow for link generation [@kaosine](https://github.com/pulsar-edit/pulsar/pull/78)
- Fixed: Hooked `NSFW` directly [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/77)
- Bumped: `settings-view` 0.261.8 -> 0.261.9 [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/72)
- Bumped: `.nvmrc` 12.18 -> 16 [@Daeraxa](https://github.com/pulsar-edit/pulsar/pull/71)
- Bumped: `ppm` submodule for new backend [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/68)
- Removed: Experimental and internal watchers [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/67)
- Fixed: Improvements for windows binaries [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/66)
- Fixed: Improvements for binary building [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/63)
- Bumped: `async` 3.2.0 -> 3.2.4 [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/59)
- Removed: Mystery/Ghost Submodule [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/51)
- Removed: Telemetry and Remote Crash Reports [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/40)
- Added: Bundled `language-c` into the editor [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/33)
- Bumped: `electron` 11.5.0 -> 12.2.3 [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/28)
- Fixed: `yarn install` due to syntax error [@Daeraxa](https://github.com/pulsar-edit/pulsar/pull/16)
- Added: Bundled most language grammars into the editor [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/14)
- Bumped: `autocomplete-html` 0.8.8 -> 0.8.9 [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/14)
- Bumped: `tree-sitter` NA -> 0.20.0 [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/14)
- Added: Branding Config on Global Atom API [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/7)
- Added: `yarn` as method to build editor. [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/6)
- Bumped: `fs-admin` 0.15.0 -> 0.19.0 [@kaosine](https://github.com/pulsar-edit/pulsar/pull/4)
- Bumped: `text-buffer` 13.18.5 -> 13.18.6 [@kaosine](https://github.com/pulsar-edit/pulsar/pull/4)
- Decaffeinate: Numerous efforts from many contributors to decaffeinate the editor:
* [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/112)
* [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/45)
* [@Spiker985](https://github.com/pulsar-edit/pulsar/pull/29)
* [@fabianfiorotto](https://github.com/pulsar-edit/pulsar/pull/13)
- Rebrand: Numerous efforts from many contributors to rebrand the editor:
* [@Daeraxa](https://github.com/pulsar-edit/pulsar/pull/190)
* [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/173)
* [@Daeraxa](https://github.com/pulsar-edit/pulsar/pull/172)
* [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/156)
* [@Daeraxa](https://github.com/pulsar-edit/pulsar/pull/145)
* [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/136)
* [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/126)
* [@ElectronicsArchiver](https://github.com/pulsar-edit/pulsar/pull/123)
* [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/122)
* [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/120)
* [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/103)
* [@Daeraxa](https://github.com/pulsar-edit/pulsar/pull/83)
* [@Spiker985](https://github.com/pulsar-edit/pulsar/pull/81)
* [@kaosine](https://github.com/pulsar-edit/pulsar/pull/65)
* [@Spiker985](https://github.com/pulsar-edit/pulsar/pull/58)
* [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/54)
* [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/22)
* [@Spiker985](https://github.com/pulsar-edit/pulsar/pull/17)
* [@softcode589](https://github.com/pulsar-edit/pulsar/pull/11)
* [@LandarXT](https://github.com/pulsar-edit/pulsar/pull/10)
* [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/8)
- Tests: Numerous efforts from many contributors to improve our tests:
* [@icecream17](https://github.com/pulsar-edit/pulsar/pull/152)
* [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/141)
* [@DeeDeeG](https://github.com/pulsar-edit/pulsar/pull/116)
* [@Spiker985](https://github.com/pulsar-edit/pulsar/pull/109)
* [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/70)
* [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/50)
* [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/48)
* [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/46)
* [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/42)
* [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/41)
* [@fabianfiorotto](https://github.com/pulsar-edit/pulsar/pull/36)
* [@fabianfiorotto](https://github.com/pulsar-edit/pulsar/pull/35)
* [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/18)
### ppm
- Fixed: ppm PostInstall [@mauricioszabo](https://github.com/pulsar-edit/ppm/pull/41)
- Added: Better `help` command display [@mauricioszabo](https://github.com/pulsar-edit/ppm/pull/40)
- Fixed: Empty Featured Packages [@jonian](https://github.com/pulsar-edit/ppm/pull/38)
- Fixed: Use ppm as basename in `getResourcePath` [@jonain](https://github.com/pulsar-edit/ppm/pull/36)
- Fixed: Installation from Git [@mauricioszabo](https://github.com/pulsar-edit/ppm/pull/34)
- Added: Ability to define tag to install [@mauricioszabo](https://github.com/pulsar-edit/ppm/pull/13)
- Added: Our new Pulsar Package Repository Backend [@confused-Techie](https://github.com/pulsar-edit/ppm/pull/5)
- Bumped: `electron` to 12 [@mauricioszabo](https://github.com/pulsar-edit/ppm/pull/2)
- Rebrand: Numerous efforts from many contributors to rebrand ppm:
* [@Sertonix](https://github.com/pulsar-edit/ppm/pull/12)
* [@softcode589](https://github.com/pulsar-edit/ppm/pull/7)
* [@mauricioszabo](https://github.com/pulsar-edit/ppm/pull/6)
- Tests: Numerous efforts from many contributors to improve our tests:
* [@DeeDeeG](https://github.com/pulsar-edit/ppm/pull/39)
### autocomplete-html
- Fixed: Finding the proper Node version [@mauricioszabo](https://github.com/pulsar-edit/autocomplete-html/pull/1)
### settings-view
- Added: Remember Scroll Position [@jonian](https://github.com/pulsar-edit/settings-view/pull/12)
- Removed: Support for deprecated packages [@Sertonix](https://github.com/pulsar-edit/settings-view/pull/6)
- Added: Better errors when search fails [@mauricioszabo](https://github.com/pulsar-edit/settings-view/pull/2)
- Rebrand: Numerous efforts from many contributors to rebrand settings-view:
* [@mauricioszabo](https://github.com/pulsar-edit/settings-view/pull/7)
* [@softcode589](https://github.com/pulsar-edit/settings-view/pull/3)
* [@mauricioszabo](https://github.com/pulsar-edit/settings-view/pull/1)
- Tests: Numerous efforts from many contributors to improve our tests:
* [@confused-Techie](https://github.com/pulsar-edit/settings-view/pull/10)
### snippets
- Added: Proper Testing [@confused-Techie](https://github.com/pulsar-edit/snippets/pull/4)
- Removed: `fs-plus` [@Sertonix](https://github.com/pulsar-edit/snippets/pull/2)
- Fixed: Fix open Snippets URI [@Sertonix](https://github.com/pulsar-edit/snippets/pull/1)
### background-tips
- Bumped: `background-tips` 0.28.0 -> 0.28.1 [@confused-Techie](https://github.com/pulsar-edit/background-tips/pull/4)
- Rebrand: Numerous efforts from many contributors to rebrand background-tips:
* [@Sertonix](https://github.com/pulsar-edit/background-tips/pull/5)
* [@Sertonix](https://github.com/pulsar-edit/background-tips/pull/2)
* [@Sertonix](https://github.com/pulsar-edit/background-tips/pull/1)
## Atom v1.6.0
- See https://atom.io/releases

View File

@ -1,5 +1,5 @@
# VERSION: 0.2
# DESCRIPTION: Image to build Atom
# DESCRIPTION: Image to build Pulsar
FROM ubuntu:20.04

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2022 Pulsar-Edit
Copyright (c) 2022-2023 Pulsar-Edit
Original work copyright (c) 2011-2022 GitHub Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy

View File

@ -26,12 +26,13 @@
<br>
*A Community-led Hyper-Hackable Text Editor, built on **[Electron]**,*
*and based on everything we love about our favorite editors.*
*A Community-led Hyper-Hackable Text Editor,*
*Forked from [Atom], built on [Electron].*
*We designed it to be deeply customizable, but still*
*Designed to be deeply customizable, but still*
*approachable using the default configuration.*
<br>
<br>
@ -44,6 +45,7 @@
[OpenCollective]: https://opencollective.com/pulsar-edit
[Discussions]: https://github.com/orgs/pulsar-edit/discussions
[Electron]: https://github.com/electron/electron
[Atom]: https://github.blog/2022-06-08-sunsetting-atom/
[Discord]: https://discord.gg/7aEbB9dGRT 'Join the Pulsar Discord today!'
[Crowdin]: https://crowdin.pulsar-edit.dev
[Status]: https://cirrus-ci.com/github/pulsar-edit/pulsar/master
@ -64,7 +66,7 @@
<!---------------------------{ Images }--------------------------->
[Preview]: https://user-images.githubusercontent.com/378023/49132478-f4b77680-f31f-11e8-9e10-e8454d8d9b7e.png 'Preview of the editor.'
[Preview]: resources/readme.png 'Preview of the editor.'
<!---------------------------{ Badges }--------------------------->

11
docs/.jsdoc.json Normal file
View File

@ -0,0 +1,11 @@
{
"recurseDepth": 10,
"source": {
"include": ["src", "packages"],
"exclude": ["node_modules"],
"excludePattern": "((^|\\/|\\\\)_|node_modules)"
},
"opts": {
"recurse": true
}
}

View File

@ -0,0 +1,298 @@
## Classes
<dl>
<dt><a href="#AtomEnvironment">AtomEnvironment</a></dt>
<dd><p>Pulsar global for dealing with packages, themes, menus, and the window.</p>
<p>An instance of this class is always available as the <code>atom</code> global.</p>
</dd>
<dt><a href="#Clipboard">Clipboard</a></dt>
<dd></dd>
</dl>
## Constants
<dl>
<dt><a href="#etch">etch</a></dt>
<dd></dd>
<dt><a href="#fs">fs</a></dt>
<dd></dd>
<dt><a href="#dalek">dalek</a></dt>
<dd></dd>
<dt><a href="#assert">assert</a></dt>
<dd></dd>
</dl>
## Functions
<dl>
<dt><a href="#beforeEach">beforeEach()</a></dt>
<dd></dd>
<dt><a href="#beforeEach">beforeEach()</a></dt>
<dd></dd>
<dt><a href="#conditionPromise">conditionPromise()</a></dt>
<dd></dd>
<dt><a href="#destroy">destroy()</a></dt>
<dd></dd>
<dt><a href="#destroyChildren">destroyChildren()</a></dt>
<dd></dd>
<dt><a href="#releaseChildren">releaseChildren()</a></dt>
<dd></dd>
<dt><a href="#subscribeToRepository">subscribeToRepository()</a></dt>
<dd></dd>
<dt><a href="#updateDiffs">updateDiffs()</a></dt>
<dd></dd>
<dt><a href="#beforeEach">beforeEach()</a></dt>
<dd></dd>
<dt><a href="#beforeEach">beforeEach()</a></dt>
<dd></dd>
<dt><a href="#beforeEach">beforeEach()</a></dt>
<dd></dd>
<dt><a href="#conditionPromise">conditionPromise()</a></dt>
<dd></dd>
<dt><a href="#beforeEach">beforeEach()</a></dt>
<dd></dd>
</dl>
<a name="AtomEnvironment"></a>
## AtomEnvironment
Pulsar global for dealing with packages, themes, menus, and the window.
An instance of this class is always available as the `atom` global.
**Kind**: global class
* [AtomEnvironment](#AtomEnvironment)
* _instance_
* [.clipboard](#AtomEnvironment+clipboard) : [<code>Clipboard</code>](#Clipboard)
* [.deserializers](#AtomEnvironment+deserializers) : <code>DeserializerManager</code>
* [.views](#AtomEnvironment+views) : <code>ViewRegistry</code>
* [.notifications](#AtomEnvironment+notifications) : <code>NotificationManager</code>
* [.config](#AtomEnvironment+config) : <code>Config</code>
* [.keymaps](#AtomEnvironment+keymaps) : <code>KeymapManager</code>
* [.tooltips](#AtomEnvironment+tooltips) : <code>TooltipManager</code>
* [.commands](#AtomEnvironment+commands) : <code>CommandRegistry</code>
* [.grammars](#AtomEnvironment+grammars) : <code>GrammarRegistry</code>
* [.styles](#AtomEnvironment+styles) : <code>StyleManager</code>
* [.packages](#AtomEnvironment+packages) : <code>PackageManager</code>
* [.themes](#AtomEnvironment+themes) : <code>ThemeManager</code>
* [.menu](#AtomEnvironment+menu) : <code>MenuManager</code>
* [.contextMenu](#AtomEnvironment+contextMenu) : <code>ContextMenuManager</code>
* [.project](#AtomEnvironment+project) : <code>Project</code>
* [.textEditors](#AtomEnvironment+textEditors) : <code>TextEditorRegistry</code>
* [.workspace](#AtomEnvironment+workspace) : <code>Workspace</code>
* [.history](#AtomEnvironment+history) : <code>HistoryManager</code>
* _Messaging the User_
* [.beep()](#AtomEnvironment+beep)
* _static_
* _Event Subscription_
* [.onDidBeep(callback)](#AtomEnvironment.onDidBeep) ⇒ <code>Disposable</code>
<a name="AtomEnvironment+clipboard"></a>
### atomEnvironment.clipboard : [<code>Clipboard</code>](#Clipboard)
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+deserializers"></a>
### atomEnvironment.deserializers : <code>DeserializerManager</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+views"></a>
### atomEnvironment.views : <code>ViewRegistry</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+notifications"></a>
### atomEnvironment.notifications : <code>NotificationManager</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+config"></a>
### atomEnvironment.config : <code>Config</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+keymaps"></a>
### atomEnvironment.keymaps : <code>KeymapManager</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+tooltips"></a>
### atomEnvironment.tooltips : <code>TooltipManager</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+commands"></a>
### atomEnvironment.commands : <code>CommandRegistry</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+grammars"></a>
### atomEnvironment.grammars : <code>GrammarRegistry</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+styles"></a>
### atomEnvironment.styles : <code>StyleManager</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+packages"></a>
### atomEnvironment.packages : <code>PackageManager</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+themes"></a>
### atomEnvironment.themes : <code>ThemeManager</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+menu"></a>
### atomEnvironment.menu : <code>MenuManager</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+contextMenu"></a>
### atomEnvironment.contextMenu : <code>ContextMenuManager</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+project"></a>
### atomEnvironment.project : <code>Project</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+textEditors"></a>
### atomEnvironment.textEditors : <code>TextEditorRegistry</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+workspace"></a>
### atomEnvironment.workspace : <code>Workspace</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+history"></a>
### atomEnvironment.history : <code>HistoryManager</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+beep"></a>
### atomEnvironment.beep()
Visually and audibly trigger a beep.
**Kind**: instance method of [<code>AtomEnvironment</code>](#AtomEnvironment)
**Category**: Messaging the User
**Emits**: <code>event:beep</code>
<a name="AtomEnvironment.onDidBeep"></a>
### AtomEnvironment.onDidBeep(callback) ⇒ <code>Disposable</code>
Invoke the given callback whenever [::beep](::beep) is called.
**Kind**: static method of [<code>AtomEnvironment</code>](#AtomEnvironment)
**Returns**: <code>Disposable</code> - on which `.dispose()` can be called to unsubscribe.
**Category**: Event Subscription
| Param | Type | Description |
| --- | --- | --- |
| callback | <code>function</code> | Function to be called whenever [::beep](::beep) is called. |
<a name="Clipboard"></a>
## Clipboard
**Kind**: global class
<a name="new_Clipboard_new"></a>
### new Clipboard()
Represents the clipboard used for copying and pasting in Pulsar.
An instance of this class is always available as the `atom.clipboard` global.
**Example**
```js
// returns 'hello'
atom.clipboard.write('hello');
console.log(atom.clipboard.read());
```
<a name="etch"></a>
## etch
**Kind**: global constant
**Jsx**: etch.dom
<a name="fs"></a>
## fs
**Kind**: global constant
**Babel**:
<a name="dalek"></a>
## dalek
**Kind**: global constant
**Babel**:
<a name="assert"></a>
## assert
**Kind**: global constant
**Babel**:
<a name="beforeEach"></a>
## beforeEach()
**Kind**: global function
**Babel**:
<a name="beforeEach"></a>
## beforeEach()
**Kind**: global function
**Babel**:
<a name="conditionPromise"></a>
## conditionPromise()
**Kind**: global function
**Babel**:
<a name="destroy"></a>
## destroy()
**Kind**: global function
**Describe**: Handles tear down of destructables and subscriptions.
Does not handle release of memory. This method should only be called
just before this object is freed, and should only tear down the main
object components that are guarunteed to exist at all times.
<a name="destroyChildren"></a>
## destroyChildren()
**Kind**: global function
**Describe**: Destroys this objects children (non-freeing), it's intended
to be an ease-of use function for maintaing this object. This method
should only tear down objects that are selectively allocated upon
repository discovery.
Example: this.diffs only exists when we have a repository.
<a name="releaseChildren"></a>
## releaseChildren()
**Kind**: global function
**Describe**: The memory releasing complement function of `destroyChildren`.
frees the memory allocated at all child object storage locations
when there is no repository.
<a name="subscribeToRepository"></a>
## subscribeToRepository()
**Kind**: global function
**Describe**: handles all subscriptions based on the repository in focus
<a name="updateDiffs"></a>
## updateDiffs()
**Kind**: global function
**Describe**: Uses text markers in the target editor to visualize
git modifications, additions, and deletions. The current algorithm
just redraws the markers each call.
<a name="beforeEach"></a>
## beforeEach()
**Kind**: global function
**Babel**:
<a name="beforeEach"></a>
## beforeEach()
**Kind**: global function
**Babel**:
<a name="beforeEach"></a>
## beforeEach()
**Kind**: global function
**Babel**:
<a name="conditionPromise"></a>
## conditionPromise()
**Kind**: global function
**Babel**:
<a name="beforeEach"></a>
## beforeEach()
**Kind**: global function
**Babel**:

View File

@ -0,0 +1,306 @@
## Classes
<dl>
<dt><a href="#AtomEnvironment">AtomEnvironment</a></dt>
<dd><p>Pulsar global for dealing with packages, themes, menus, and the window.</p>
<p>An instance of this class is always available as the <code>atom</code> global.</p>
</dd>
<dt><a href="#Clipboard">Clipboard</a></dt>
<dd></dd>
</dl>
## Constants
<dl>
<dt><a href="#etch">etch</a></dt>
<dd></dd>
<dt><a href="#fs">fs</a></dt>
<dd></dd>
<dt><a href="#dalek">dalek</a></dt>
<dd></dd>
<dt><a href="#assert">assert</a></dt>
<dd></dd>
</dl>
## Functions
<dl>
<dt><a href="#beforeEach">beforeEach()</a></dt>
<dd></dd>
<dt><a href="#beforeEach">beforeEach()</a></dt>
<dd></dd>
<dt><a href="#conditionPromise">conditionPromise()</a></dt>
<dd></dd>
<dt><a href="#destroy">destroy()</a></dt>
<dd></dd>
<dt><a href="#destroyChildren">destroyChildren()</a></dt>
<dd></dd>
<dt><a href="#releaseChildren">releaseChildren()</a></dt>
<dd></dd>
<dt><a href="#subscribeToRepository">subscribeToRepository()</a></dt>
<dd></dd>
<dt><a href="#updateDiffs">updateDiffs()</a></dt>
<dd></dd>
<dt><a href="#beforeEach">beforeEach()</a></dt>
<dd></dd>
<dt><a href="#beforeEach">beforeEach()</a></dt>
<dd></dd>
<dt><a href="#beforeEach">beforeEach()</a></dt>
<dd></dd>
<dt><a href="#conditionPromise">conditionPromise()</a></dt>
<dd></dd>
<dt><a href="#beforeEach">beforeEach()</a></dt>
<dd></dd>
</dl>
<a name="AtomEnvironment"></a>
## AtomEnvironment
Pulsar global for dealing with packages, themes, menus, and the window.
An instance of this class is always available as the `atom` global.
**Kind**: global class
* [AtomEnvironment](#AtomEnvironment)
* _instance_
* [.clipboard](#AtomEnvironment+clipboard) : [<code>Clipboard</code>](#Clipboard)
* [.deserializers](#AtomEnvironment+deserializers) : <code>DeserializerManager</code>
* [.views](#AtomEnvironment+views) : <code>ViewRegistry</code>
* [.notifications](#AtomEnvironment+notifications) : <code>NotificationManager</code>
* [.config](#AtomEnvironment+config) : <code>Config</code>
* [.keymaps](#AtomEnvironment+keymaps) : <code>KeymapManager</code>
* [.tooltips](#AtomEnvironment+tooltips) : <code>TooltipManager</code>
* [.commands](#AtomEnvironment+commands) : <code>CommandRegistry</code>
* [.grammars](#AtomEnvironment+grammars) : <code>GrammarRegistry</code>
* [.styles](#AtomEnvironment+styles) : <code>StyleManager</code>
* [.packages](#AtomEnvironment+packages) : <code>PackageManager</code>
* [.themes](#AtomEnvironment+themes) : <code>ThemeManager</code>
* [.menu](#AtomEnvironment+menu) : <code>MenuManager</code>
* [.contextMenu](#AtomEnvironment+contextMenu) : <code>ContextMenuManager</code>
* [.project](#AtomEnvironment+project) : <code>Project</code>
* [.textEditors](#AtomEnvironment+textEditors) : <code>TextEditorRegistry</code>
* [.workspace](#AtomEnvironment+workspace) : <code>Workspace</code>
* [.history](#AtomEnvironment+history) : <code>HistoryManager</code>
* _Messaging the User_
* [.beep()](#AtomEnvironment+beep)
* _static_
* [.preloadPackages](#AtomEnvironment.preloadPackages) ℗
* _Event Subscription_
* [.onDidBeep(callback)](#AtomEnvironment.onDidBeep) ⇒ <code>Disposable</code>
<a name="AtomEnvironment+clipboard"></a>
### atomEnvironment.clipboard : [<code>Clipboard</code>](#Clipboard)
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+deserializers"></a>
### atomEnvironment.deserializers : <code>DeserializerManager</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+views"></a>
### atomEnvironment.views : <code>ViewRegistry</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+notifications"></a>
### atomEnvironment.notifications : <code>NotificationManager</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+config"></a>
### atomEnvironment.config : <code>Config</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+keymaps"></a>
### atomEnvironment.keymaps : <code>KeymapManager</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+tooltips"></a>
### atomEnvironment.tooltips : <code>TooltipManager</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+commands"></a>
### atomEnvironment.commands : <code>CommandRegistry</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+grammars"></a>
### atomEnvironment.grammars : <code>GrammarRegistry</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+styles"></a>
### atomEnvironment.styles : <code>StyleManager</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+packages"></a>
### atomEnvironment.packages : <code>PackageManager</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+themes"></a>
### atomEnvironment.themes : <code>ThemeManager</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+menu"></a>
### atomEnvironment.menu : <code>MenuManager</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+contextMenu"></a>
### atomEnvironment.contextMenu : <code>ContextMenuManager</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+project"></a>
### atomEnvironment.project : <code>Project</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+textEditors"></a>
### atomEnvironment.textEditors : <code>TextEditorRegistry</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+workspace"></a>
### atomEnvironment.workspace : <code>Workspace</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+history"></a>
### atomEnvironment.history : <code>HistoryManager</code>
**Kind**: instance property of [<code>AtomEnvironment</code>](#AtomEnvironment)
<a name="AtomEnvironment+beep"></a>
### atomEnvironment.beep()
Visually and audibly trigger a beep.
**Kind**: instance method of [<code>AtomEnvironment</code>](#AtomEnvironment)
**Category**: Messaging the User
**Emits**: <code>event:beep</code>
<a name="AtomEnvironment.preloadPackages"></a>
### AtomEnvironment.preloadPackages ℗
Returns output of `preloadPackages()` for this Classes Instance of `Packages`.
**Kind**: static property of [<code>AtomEnvironment</code>](#AtomEnvironment)
**Access**: private
<a name="AtomEnvironment.onDidBeep"></a>
### AtomEnvironment.onDidBeep(callback) ⇒ <code>Disposable</code>
Invoke the given callback whenever [::beep](::beep) is called.
**Kind**: static method of [<code>AtomEnvironment</code>](#AtomEnvironment)
**Returns**: <code>Disposable</code> - on which `.dispose()` can be called to unsubscribe.
**Category**: Event Subscription
| Param | Type | Description |
| --- | --- | --- |
| callback | <code>function</code> | Function to be called whenever [::beep](::beep) is called. |
<a name="Clipboard"></a>
## Clipboard
**Kind**: global class
<a name="new_Clipboard_new"></a>
### new Clipboard()
Represents the clipboard used for copying and pasting in Pulsar.
An instance of this class is always available as the `atom.clipboard` global.
**Example**
```js
// returns 'hello'
atom.clipboard.write('hello');
console.log(atom.clipboard.read());
```
<a name="etch"></a>
## etch
**Kind**: global constant
**Jsx**: etch.dom
<a name="fs"></a>
## fs
**Kind**: global constant
**Babel**:
<a name="dalek"></a>
## dalek
**Kind**: global constant
**Babel**:
<a name="assert"></a>
## assert
**Kind**: global constant
**Babel**:
<a name="beforeEach"></a>
## beforeEach()
**Kind**: global function
**Babel**:
<a name="beforeEach"></a>
## beforeEach()
**Kind**: global function
**Babel**:
<a name="conditionPromise"></a>
## conditionPromise()
**Kind**: global function
**Babel**:
<a name="destroy"></a>
## destroy()
**Kind**: global function
**Describe**: Handles tear down of destructables and subscriptions.
Does not handle release of memory. This method should only be called
just before this object is freed, and should only tear down the main
object components that are guarunteed to exist at all times.
<a name="destroyChildren"></a>
## destroyChildren()
**Kind**: global function
**Describe**: Destroys this objects children (non-freeing), it's intended
to be an ease-of use function for maintaing this object. This method
should only tear down objects that are selectively allocated upon
repository discovery.
Example: this.diffs only exists when we have a repository.
<a name="releaseChildren"></a>
## releaseChildren()
**Kind**: global function
**Describe**: The memory releasing complement function of `destroyChildren`.
frees the memory allocated at all child object storage locations
when there is no repository.
<a name="subscribeToRepository"></a>
## subscribeToRepository()
**Kind**: global function
**Describe**: handles all subscriptions based on the repository in focus
<a name="updateDiffs"></a>
## updateDiffs()
**Kind**: global function
**Describe**: Uses text markers in the target editor to visualize
git modifications, additions, and deletions. The current algorithm
just redraws the markers each call.
<a name="beforeEach"></a>
## beforeEach()
**Kind**: global function
**Babel**:
<a name="beforeEach"></a>
## beforeEach()
**Kind**: global function
**Babel**:
<a name="beforeEach"></a>
## beforeEach()
**Kind**: global function
**Babel**:
<a name="conditionPromise"></a>
## conditionPromise()
**Kind**: global function
**Babel**:
<a name="beforeEach"></a>
## beforeEach()
**Kind**: global function
**Babel**:

View File

@ -11,3 +11,7 @@
// console.log(`Saved! ${editor.getPath()}`)
// )
// );
//
// See the Pulsar Launch manual for more information on this file and how to
// customize it.
// https://pulsar-edit.dev/docs/launch-manual/sections/core-hacking/#the-init-file

View File

@ -18,15 +18,15 @@
# 'ctrl-p': 'core:move-down'
#
# You can find more information about keymaps in these guides:
# * http://flight-manual.atom.io/using-atom/sections/basic-customization/#customizing-keybindings
# * http://flight-manual.atom.io/behind-atom/sections/keymaps-in-depth/
# * https://pulsar-edit.dev/docs/launch-manual/sections/using-pulsar/#customizing-keybindings
# * https://pulsar-edit.dev/docs/launch-manual/sections/behind-pulsar#keymaps-in-depth
#
# If you're having trouble with your keybindings not working, try the
# Keybinding Resolver: `Cmd+.` on macOS and `Ctrl+.` on other platforms. See the
# Debugging Guide for more information:
# * http://flight-manual.atom.io/hacking-atom/sections/debugging/#check-the-keybindings
# * https://pulsar-edit.dev/docs/launch-manual/sections/core-hacking/#check-your-keybindings
#
# This file uses CoffeeScript Object Notation (CSON).
# If you are unfamiliar with CSON, you can read more about it in the
# Pulsar Flight Manual:
# http://flight-manual.atom.io/using-atom/sections/basic-customization/#configuring-with-cson
# Pulsar Launch Manual:
# https://pulsar-edit.dev/docs/launch-manual/sections/using-pulsar/#configuring-with-cson

View File

@ -17,5 +17,5 @@
#
# This file uses CoffeeScript Object Notation (CSON).
# If you are unfamiliar with CSON, you can read more about it in the
# Pulsar Flight Manual:
# http://flight-manual.atom.io/using-atom/sections/basic-customization/#_cson
# Pulsar Launch Manual:
# https://pulsar-edit.dev/docs/launch-manual/sections/using-pulsar/#configuring-with-cson

View File

@ -1,10 +1,10 @@
/*
* Your Stylesheet
*
* This stylesheet is loaded when Atom starts up and is reloaded automatically
* This stylesheet is loaded when Pulsar starts up and is reloaded automatically
* when it is changed and saved.
*
* Add your own CSS or Less to fully customize Atom.
* Add your own CSS or Less to fully customize Pulsar.
* If you are unfamiliar with Less, you can read more about it here:
* http://lesscss.org
*/

25
hooks/README.md Normal file
View File

@ -0,0 +1,25 @@
### Contents
| Filename | Description |
| - | - |
| post-checkout | This hook executes after a branch checkout, or branch switch has occurred. |
| post-merge | This hook executes after a branch merge has occurred |
| update_editor.sh | The actual brains of the hooks. Performs a yarn install, yarn build, yarn build:apm, and syncs all submodules. |
### Disclaimer
These hooks are not guaranteed. These were made out of convenience and presented to the org as an optional tool for usage.
### Usage
There are several ways to apply these hooks:
- You can manually copy the files over to the `<pulsar-repo-root>/.git/hooks` folder and validate that they are executable - the effect should be immediate. This is the preferred option for Windows.
- You can use manage_hooks.sh to copy/symlink the hooks you choose. This is the preferred option for Linux/macOS.
- Your mileage may vary on macOS as it has not been tested outright, but should work in theory.
### Instructions
- Open your favorite terminal
- Navigate to `<pulsar-repo-root>/hooks`.
- IMPORTANT: The bash completions will only work within this directory, and are activated when using exactly `./manage-hooks.sh`.
- If you have bash-completions, source the `manage_hooks-completion.bash` file to allow for auto-complete ie `source manage_hooks-completion.bash`.
- Allow the auto-complete responses to guide you.
- Standard commands are `list`, `install` and `remove`
- The `install` and `remove` commands require the hook you wish to install, followed by an optional parameter for `copy` vs `symlink` with symlink being the default.
- A symbolically linked hook allows you to receive updates in the future. If you plan on adjusting your hook(s), you probably want to `copy` the files to the `<pulsar-repo-root>/.git/hooks` directory

View File

@ -0,0 +1,59 @@
#!/bin/bash
#Not gonna lie, this is some magical stuff, but I will try to explain it
#Automatic variables used by complete
#COMPREPLY = An array of autocompletions to pass back to the shell
#COMP_WORDS = The passed-in list of parameters
#COMP_CWORD = The index of the current word as related to the count of
#parameters
#compgen = Generates an array of autocompletions to pass back to the shell
#based on provided parameters
function __completion() {
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
opts="list install remove"
#Determine what "level" of autocomplete we are operating on
#1 is no params
#2 is 1 param, in this instance the command
#3 etc
case ${COMP_CWORD} in
1)
#Generate a Word-list from opts from the current word
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
;;
2)
case ${prev} in
install)
#Generate a list of filenames (and append "all") based on the current
#directory and current word while ignoring select files
COMPREPLY=( $(compgen -W "all $(ls --ignore='*.md' --ignore='*.sh' --ignore='*.bash')" -- ${cur}) )
;;
remove)
#Generate a list of filenames (and append "all") based on the
#.git/hooks directory and current word while ignoring the sample
#files
COMPREPLY=( $(compgen -W "all $(ls ../.git/hooks --ignore='*.sample')" -- ${cur}) )
;;
esac
;;
3)
case "${COMP_WORDS[COMP_CWORD-2]}" in
install)
#When attempting to autocomplete for the third parameter ie copy
#/symbolic AND the command is install
#Generate a list of Words from the provded string
COMPREPLY=( $(compgen -W "copy symbolic" -- ${cur}) )
;;
esac
;;
esac
}
#Use the function defined above, when an autocomplete event for
#./manage_hooks.sh is fired. This is why you must be in the proper directory
complete -F __completion ./manage_hooks.sh

83
hooks/manage_hooks.sh Executable file
View File

@ -0,0 +1,83 @@
#!/bin/bash
USAGEMSG="Usage: manage_hooks <action> <hook> <option>"
if [[ $# -lt 1 ]] || [[ $# -gt 3 ]]; then
echo "$USAGEMSG"
exit 1
fi
action="$1"
case $1 in
list)
ls --color=if-tty -l ../.git/hooks --ignore='*.sample'
;;
install)
#Grab our list of files if all was passed.
#Couldn't get * to function without globbing
if [[ "$2" == "all" ]]; then
hooks=( $(readlink -f $(ls --ignore='*.md' --ignore='*.sh' --ignore='*.bash')) )
else
hooks=( $(readlink -f "$2") )
fi
#Default to symbolic
if [[ -z $3 ]]; then
option="symbolic"
else
option="$3"
fi
case $option in
copy)
#If copying, copy the update_editor file as well
hooks+=( "$(readlink -f "update_editor.sh")" "${hooks[@]}" )
for hook in "${hooks[@]}"; do
#If the file doesnt already exist copy it over
if [[ ! -f "../.git/hooks/$(basename "$hook")" ]]; then
echo "$hook"
cp "$hook" "../.git/hooks"
fi
done
;;
symbolic)
#Switch to the git hook directory, because symlink hooks are required
#to be relative
cd "../.git/hooks"
for hook in "${hooks[@]}"; do
#If the file doesnt already exist, symlink it
if [[ ! -f "$(basename $hook)" ]]; then
echo "$(readlink -f .)/$(basename $hook)"
ln --symbolic --relative "../../hooks/$(basename "$hook")"
fi
done
;;
esac
;;
remove)
#Switch to the git hook directory, so we dont have to deal with
#relative file paths
cd "../.git/hooks"
#Grab the canonical path to the file, readlink always follows symlinks
if [[ "$2" == "all" ]]; then
hooks=( $(realpath --no-symlinks $(ls --ignore='*.sample' --ignore='*.sh')) )
else
hooks=( $(realpath --no-symlinks "$2") )
fi
for hook in "${hooks[@]}"; do
echo "$hook"
rm "$hook"
done
;;
*)
echo "$USAGEMSG"
exit 1
;;
esac

24
hooks/post-checkout Executable file
View File

@ -0,0 +1,24 @@
#!/bin/bash
#This hook launches after a branch change, or branch checkout. It cannot affect
#the outcome of a git switch or git checkout with the exception that the hook
#exit status becomes the exit status of the antecedent command
#See https://git-scm.com/docs/githooks#_post_checkout
if [[ -L "$0" ]]; then
#If our launched script is a link, grab the root
hookRoot="$(dirname $(readlink --canonicalize "$0"))"
else
#Otherwise use the launched script directory
hookRoot="$(dirname "$0")"
fi
if [[ "$1" == "$2" ]]; then
#Previous HEAD and new HEAD are the same, ignore
exit 0
fi
if [[ "$3" == '1' ]]; then
#A branch checkout will always be flagged as 1, and a file checkout as 0
"${hookRoot}/update_editor.sh" $0
fi

15
hooks/post-merge Executable file
View File

@ -0,0 +1,15 @@
#!/bin/bash
#This hook launches after a git pull. It cannot affect a git merge, and will not
#be executed if a merge fails
#See https://git-scm.com/docs/githooks#_post_merge
if [[ -L "$0" ]]; then
#If our launched script is a link, grab the root
hookRoot="$(dirname $(readlink --canonicalize $0))"
else
#Otherwise use the launched script directory
hookRoot="$(dirname "$0")"
fi
"${hookRoot}/update_editor.sh" $0

56
hooks/update_editor.sh Executable file
View File

@ -0,0 +1,56 @@
#!/bin/bash
case $1 in
*post-checkout)
ACTION='Branch change'
;;
*post-merge)
ACTION='Remote pull'
;;
*)
ACTION="Unknown event ($1)"
;;
esac
echo "${ACTION} occurred, rebuilding editor"
export ATOM_ELECTRON_VERSION=$(cat package.json | rg --trim --replace "" '"electronVersion": "' | rg --replace "" '",')
replacement="1.100.$(date +'%Y%m%d%H%k%M')"
filter='("version": ")[0-9\.]+(",)'
regex="s/$filter/\1$replacement\2/"
sed --regexp-extended --in-place "$regex" package.json
echo ' Installing editor packages'
yarn install &> /dev/null
if [[ $? == 0 ]]; then
echo ' Install completed successfully'
else
echo ' Install failed'
exit 1
fi
echo ' Rebuilding modules'
yarn build &> /dev/null
if [[ $? == 0 ]]; then
echo ' Module build completed successfully'
else
echo ' Module build failed'
exit 1
fi
echo ' Rebuilding PPM'
if [[ -d "ppm" ]]; then
yarn build:apm &> /dev/null
if [[ $? == 0 ]]; then
echo ' PPM build completed successfully'
else
echo ' PPM build failed'
exit 1
fi
else
echo ' PPM folder not found'
fi
git submodule sync && git submodule update

View File

@ -2,7 +2,7 @@
"name": "pulsar",
"author": "Pulsar Community <noreply@pulsar-edit.com>",
"productName": "Pulsar",
"version": "1.63.0-dev",
"version": "1.102.0-dev",
"description": "A Community-led Hyper-Hackable Text Editor",
"branding": {
"id": "pulsar",
@ -25,7 +25,7 @@
"@babel/core": "7.18.6",
"@electron/remote": "2.0.8",
"about": "file:packages/about",
"archive-view": "https://codeload.github.com/atom/archive-view/legacy.tar.gz/refs/tags/v0.66.0",
"archive-view": "file:packages/archive-view",
"async": "3.2.4",
"atom-dark-syntax": "file:packages/atom-dark-syntax",
"atom-dark-ui": "file:packages/atom-dark-ui",
@ -34,32 +34,30 @@
"atom-light-ui": "file:packages/atom-light-ui",
"atom-select-list": "^0.8.1",
"autocomplete-atom-api": "https://codeload.github.com/atom/autocomplete-atom-api/legacy.tar.gz/refs/tags/v0.10.7",
"autocomplete-css": "https://codeload.github.com/atom/autocomplete-css/legacy.tar.gz/refs/tags/v0.17.5",
"autocomplete-html": "https://github.com/pulsar-edit/autocomplete-html.git#v0.8.9",
"autocomplete-plus": "https://codeload.github.com/atom/autocomplete-plus/legacy.tar.gz/refs/tags/v2.42.5",
"autocomplete-snippets": "https://codeload.github.com/atom/autocomplete-snippets/legacy.tar.gz/refs/tags/v1.12.1",
"autocomplete-css": "file:packages/autocomplete-css",
"autocomplete-html": "file:packages/autocomplete-html",
"autocomplete-plus": "file:./packages/autocomplete-plus",
"autocomplete-snippets": "file:packages/autocomplete-snippets",
"autoflow": "file:packages/autoflow",
"autosave": "https://codeload.github.com/atom/autosave/legacy.tar.gz/refs/tags/v0.24.6",
"babel-preset-atomic": "^5.0.0",
"background-tips": "https://codeload.github.com/pulsar-edit/background-tips/legacy.tar.gz/refs/tags/v0.28.1",
"background-tips": "file:packages/background-tips",
"base16-tomorrow-dark-theme": "file:packages/base16-tomorrow-dark-theme",
"base16-tomorrow-light-theme": "file:packages/base16-tomorrow-light-theme",
"bookmarks": "https://codeload.github.com/atom/bookmarks/legacy.tar.gz/refs/tags/v0.46.0",
"bracket-matcher": "https://github.com/pulsar-edit/bracket-matcher.git#use-napi-oniguruma",
"bracket-matcher": "https://github.com/pulsar-edit/bracket-matcher.git#c877977",
"chai": "4.3.4",
"clear-cut": "^2.0.2",
"coffee-script": "1.12.7",
"coffeescript": "1.12.7",
"color": "3.1.3",
"command-palette": "https://codeload.github.com/atom/command-palette/legacy.tar.gz/refs/tags/v0.43.5",
"command-palette": "file:packages/command-palette",
"dalek": "file:packages/dalek",
"dedent": "^0.7.0",
"deprecation-cop": "file:packages/deprecation-cop",
"dev-live-reload": "file:packages/dev-live-reload",
"devtron": "1.4.0",
"document-register-element": "^1.14.10",
"electron-notarize": "1.0.0",
"electron-osx-sign": "0.5.0",
"encoding-selector": "https://codeload.github.com/atom/encoding-selector/legacy.tar.gz/refs/tags/v0.23.9",
"encoding-selector": "file:packages/encoding-selector",
"etch": "0.14.1",
"event-kit": "^2.5.3",
"exception-reporting": "file:packages/exception-reporting",
@ -70,14 +68,14 @@
"fs-admin": "0.19.0",
"fs-plus": "^3.1.1",
"fstream": "1.0.12",
"fuzzaldrin": "^2.1",
"fuzzy-finder": "https://github.com/pulsar-edit/fuzzy-finder.git#simplify-code",
"git-diff": "file:packages/git-diff",
"git-utils": "5.7.1",
"glob": "^7.1.1",
"go-to-line": "file:packages/go-to-line",
"grammar-selector": "file:packages/grammar-selector",
"grim": "2.0.3",
"image-view": "https://codeload.github.com/atom/image-view/legacy.tar.gz/refs/tags/v0.64.0",
"image-view": "file:packages/image-view",
"incompatible-packages": "file:packages/incompatible-packages",
"jasmine-json": "~0.0",
"jasmine-reporters": "1.1.0",
@ -122,7 +120,7 @@
"line-ending-selector": "file:packages/line-ending-selector",
"line-top-index": "0.3.1",
"link": "file:packages/link",
"markdown-preview": "https://codeload.github.com/atom/markdown-preview/legacy.tar.gz/refs/tags/v0.160.2",
"markdown-preview": "file:./packages/markdown-preview",
"minimatch": "^3.0.3",
"mocha": "6.2.3",
"mocha-junit-reporter": "2.0.0",
@ -137,44 +135,45 @@
"one-light-syntax": "file:packages/one-light-syntax",
"one-light-ui": "file:packages/one-light-ui",
"oniguruma": "https://github.com/Aerijo/napi-oniguruma.git",
"open-on-github": "https://codeload.github.com/atom/open-on-github/legacy.tar.gz/refs/tags/v1.3.2",
"package-generator": "https://codeload.github.com/atom/package-generator/legacy.tar.gz/refs/tags/v1.3.0",
"package-generator": "file:packages/package-generator",
"pathwatcher": "^8.1.2",
"postcss": "8.2.10",
"postcss-selector-parser": "6.0.4",
"prebuild-install": "6.0.0",
"property-accessors": "^1.1.3",
"resolve": "1.18.1",
"scandal": "^3.2.0",
"scoped-property-store": "^0.17.0",
"scrollbar-style": "^4.0.1",
"season": "^6.0.2",
"semver": "7.3.2",
"semver": "7.3.8",
"service-hub": "^0.7.4",
"settings-view": "https://github.com/pulsar-edit/settings-view.git#v0.261.11",
"settings-view": "file:packages/settings-view",
"sinon": "9.2.1",
"snippets": "https://codeload.github.com/pulsar-edit/snippets/legacy.tar.gz/refs/tags/v1.6.1",
"snippets": "github:pulsar-edit/snippets#bb00f909c6c645b173f27346875d8fa0c7af09f7",
"solarized-dark-syntax": "file:packages/solarized-dark-syntax",
"solarized-light-syntax": "file:packages/solarized-light-syntax",
"source-map-support": "0.5.21",
"spell-check": "https://codeload.github.com/atom/spell-check/legacy.tar.gz/refs/tags/v0.77.1",
"status-bar": "https://codeload.github.com/atom/status-bar/legacy.tar.gz/refs/tags/v1.8.17",
"styleguide": "https://codeload.github.com/atom/styleguide/legacy.tar.gz/refs/tags/v0.49.12",
"sqlite3": "4",
"status-bar": "file:packages/status-bar",
"styleguide": "file:./packages/styleguide",
"superstring": "https://github.com/pulsar-edit/superstring-wasm.git#536739fef8f8cab757970b5aa56397e2f9493a02",
"symbols-view": "https://codeload.github.com/atom/symbols-view/legacy.tar.gz/refs/tags/v0.118.4",
"tabs": "https://codeload.github.com/atom/tabs/legacy.tar.gz/refs/tags/v0.110.2",
"temp": "0.9.2",
"tabs": "file:packages/tabs",
"temp": "0.9.4",
"text-buffer": "https://github.com/pulsar-edit/text-buffer.git#f8702c877",
"timecop": "https://codeload.github.com/atom/timecop/legacy.tar.gz/refs/tags/v0.36.2",
"tree-sitter": "https://github.com/pulsar-edit/node-tree-sitter.git#c304335",
"tree-view": "https://github.com/pulsar-edit/tree-view.git#4274c7a",
"typescript-simple": "8.0.6",
"underscore-plus": "^1.7.0",
"update-package-dependencies": "file:./packages/update-package-dependencies",
"vscode-ripgrep": "1.9.0",
"welcome": "file:packages/welcome",
"whitespace": "https://codeload.github.com/atom/whitespace/legacy.tar.gz/refs/tags/v0.37.8",
"winreg": "^1.2.1",
"wrap-guide": "https://codeload.github.com/atom/wrap-guide/legacy.tar.gz/refs/tags/v0.41.0",
"yargs": "16.1.0"
"wrap-guide": "file:./packages/wrap-guide",
"yargs": "17.6.2"
},
"packageDependencies": {
"atom-dark-syntax": "file:./packages/atom-dark-syntax",
@ -190,50 +189,50 @@
"solarized-dark-syntax": "file:./packages/solarized-dark-syntax",
"solarized-light-syntax": "file:./packages/solarized-light-syntax",
"about": "file:./packages/about",
"archive-view": "0.66.0",
"archive-view": "file:./packages/archive-view",
"autocomplete-atom-api": "0.10.7",
"autocomplete-css": "0.17.5",
"autocomplete-html": "0.8.9",
"autocomplete-plus": "2.42.5",
"autocomplete-snippets": "1.12.1",
"autocomplete-css": "file:./packages/autocomplete-css",
"autocomplete-html": "file:./packages/autocomplete-html",
"autocomplete-plus": "file:./packages/autocomplete-plus",
"autocomplete-snippets": "file:./packages/autocomplete-snippets",
"autoflow": "file:./packages/autoflow",
"autosave": "0.24.6",
"background-tips": "0.28.0",
"bookmarks": "0.46.0",
"background-tips": "file:./packages/background-tips",
"bookmarks": "file:./packages/bookmarks",
"bracket-matcher": "0.92.0",
"command-palette": "0.43.5",
"command-palette": "file:./packages/command-palette",
"dalek": "file:./packages/dalek",
"deprecation-cop": "file:./packages/deprecation-cop",
"dev-live-reload": "file:./packages/dev-live-reload",
"encoding-selector": "0.23.9",
"encoding-selector": "file:./packages/encoding-selector",
"exception-reporting": "file:./packages/exception-reporting",
"find-and-replace": "0.220.1",
"fuzzy-finder": "1.14.3",
"git-diff": "file:./packages/git-diff",
"go-to-line": "file:./packages/go-to-line",
"grammar-selector": "file:./packages/grammar-selector",
"image-view": "0.64.0",
"image-view": "file:./packages/image-view",
"incompatible-packages": "file:./packages/incompatible-packages",
"keybinding-resolver": "0.39.1",
"line-ending-selector": "file:./packages/line-ending-selector",
"link": "file:./packages/link",
"markdown-preview": "0.160.2",
"markdown-preview": "file:./packages/markdown-preview",
"notifications": "0.72.1",
"open-on-github": "1.3.2",
"package-generator": "1.3.0",
"settings-view": "0.261.9",
"open-on-github": "file:./packages/open-on-github",
"package-generator": "file:./packages/package-generator",
"settings-view": "file:./packages/settings-view",
"snippets": "1.6.1",
"spell-check": "0.77.1",
"status-bar": "1.8.17",
"styleguide": "0.49.12",
"status-bar": "file:./packages/status-bar",
"styleguide": "file:./packages/styleguide",
"symbols-view": "0.118.4",
"tabs": "0.110.2",
"tabs": "file:./packages/tabs",
"timecop": "0.36.2",
"tree-view": "0.229.1",
"update-package-dependencies": "file:./packages/update-package-dependencies",
"welcome": "file:./packages/welcome",
"whitespace": "0.37.8",
"wrap-guide": "0.41.0",
"wrap-guide": "file:./packages/wrap-guide",
"language-c": "file:./packages/language-c",
"language-clojure": "file:./packages/language-clojure",
"language-coffee-script": "file:./packages/language-coffee-script",
@ -271,31 +270,23 @@
},
"private": true,
"scripts": {
"preinstall": "node -e 'process.exit(0)'",
"build": "electron-rebuild",
"build:apm": "cd ppm && yarn install",
"start": "electron --enable-logging . -f",
"dist": "node script/electron-builder.js"
},
"standard-engine": "./script/node_modules/standard",
"standard": {
"env": {
"atomtest": true,
"browser": true,
"jasmine": true,
"node": true
},
"globals": [
"atom"
]
"start": "electron --no-sandbox --enable-logging . -f",
"dist": "node script/electron-builder.js",
"js-docs": "jsdoc2md --files src --configure docs/.jsdoc.json > ./docs/Pulsar-API-Documentation.md",
"private-js-docs": "jsdoc2md --private --files src --configure docs/.jsdoc.json > ./docs/Source-Code-Documentation.md"
},
"devDependencies": {
"@electron/notarize": "^1.2.3",
"@playwright/test": "1.22.2",
"electron": "12.2.3",
"electron-builder": "23.3.1",
"electron-rebuild": "3.2.7",
"eslint": "^8.27.0",
"eslint": "^8.33.0",
"eslint-plugin-jsdoc": "^39.7.4",
"eslint-plugin-node": "^11.1.0",
"jsdoc-to-markdown": "^8.0.0",
"playwright": "1.22.2",
"playwright-core": "1.22.2",
"random-seed": "0.3.0",

View File

@ -3,7 +3,7 @@
This folder contains core packages that are bundled with Pulsar releases. Not all core packages are kept here; please
see the table below for the location of every core package.
> **NOTE:** There is an ongoing effort to migrate more Atom packages from their individual repositories to this folder.
> **NOTE:** There is an ongoing effort to migrate more Pulsar packages from their individual repositories to this folder.
See [RFC 003](https://github.com/atom/atom/blob/master/docs/rfcs/003-consolidate-core-packages.md) for more details.
| Package | Where to find it | Migration issue |
@ -14,30 +14,30 @@ See [RFC 003](https://github.com/atom/atom/blob/master/docs/rfcs/003-consolidate
| **atom-light-syntax** | [`./atom-light-syntax`](./atom-light-syntax) | |
| **atom-light-ui** | [`./atom-light-ui`](./atom-light-ui) | |
| **autocomplete-atom-api** | [`atom/autocomplete-atom-api`][autocomplete-atom-api] | |
| **autocomplete-css** | [`atom/autocomplete-css`][autocomplete-css] | |
| **autocomplete-html** | [`atom/autocomplete-html`][autocomplete-html] | |
| **autocomplete-plus** | [`atom/autocomplete-plus`][autocomplete-plus] | |
| **autocomplete-snippets** | [`atom/autocomplete-snippets`][autocomplete-snippets] | |
| **autocomplete-css** | [`./autocomplete-css`](./autocomplete-css) | |
| **autocomplete-html** | [`./autocomplete-html`](./autocomplete-html) | |
| **autocomplete-plus** | [`./autocomplete-plus`][./autocomplete-plus] | |
| **autocomplete-snippets** | [`./autocomplete-snippets`](./autocomplete-snippets) | |
| **autoflow** | [`./autoflow`](./autoflow) | |
| **autosave** | [`atom/autosave`][autosave] | [#17834](https://github.com/atom/atom/issues/17834) |
| **background-tips** | [`atom/background-tips`][background-tips] | [#17835](https://github.com/atom/atom/issues/17835) |
| **autosave** | [`pulsar-edit/autosave`][autosave] | [#17834](https://github.com/atom/atom/issues/17834) |
| **background-tips** | [`./background-tips`](./background-tips) | |
| **base16-tomorrow-dark-theme** | [`./base16-tomorrow-dark-theme`](./base16-tomorrow-dark-theme) | |
| **base16-tomorrow-light-theme** | [`./base16-tomorrow-light-theme`](./base16-tomorrow-light-theme) | |
| **bookmarks** | [`atom/bookmarks`][bookmarks] | [#18273](https://github.com/atom/atom/issues/18273) |
| **bookmarks** | [`./bookmarks`](./bookmarks) | |
| **bracket-matcher** | [`atom/bracket-matcher`][bracket-matcher] | |
| **command-palette** | [`atom/command-palette`][command-palette] | |
| **dalek** | [`./dalek`](./dalek) | [#17838](https://github.com/atom/atom/issues/17838) |
| **command-palette** | [`./command-palette`](./command-palette) | |
| **dalek** | [`./dalek`](./dalek) | |
| **deprecation-cop** | [`./deprecation-cop`](./deprecation-cop) | |
| **dev-live-reload** | [`./dev-live-reload`](./dev-live-reload) | |
| **encoding-selector** | [`atom/encoding-selector`][encoding-selector] | [#17841](https://github.com/atom/atom/issues/17841) |
| **encoding-selector** | [`./encoding-selector`](./encoding-selector) | |
| **exception-reporting** | [`./exception-reporting`](./exception-reporting) | |
| **find-and-replace** | [`atom/find-and-replace`][find-and-replace] | |
| **fuzzy-finder** | [`atom/fuzzy-finder`][fuzzy-finder] | |
| **github** | [`atom/github`][github] | |
| **find-and-replace** | [`pulsar-edit/find-and-replace`][find-and-replace] | |
| **fuzzy-finder** | [`pulsar-edit/fuzzy-finder`][fuzzy-finder] | |
| **github** | [`pulsar-edit/github`][github] | |
| **git-diff** | [`./git-diff`](./git-diff) | |
| **go-to-line** | [`./go-to-line`](./go-to-line) | |
| **grammar-selector** | [`./grammar-selector`](./grammar-selector) | |
| **image-view** | [`atom/image-view`][image-view] | [#18274](https://github.com/atom/atom/issues/18274) |
| **image-view** | [`./image-view`](./image-view) | |
| **incompatible-packages** | [`./incompatible-packages`](./incompatible-packages) | |
| **keybinding-resolver** | [`atom/keybinding-resolver`][keybinding-resolver] | [#18275](https://github.com/atom/atom/issues/18275) |
| **language-c** | [`./language-c`](./language-c) | |
@ -76,59 +76,40 @@ See [RFC 003](https://github.com/atom/atom/blob/master/docs/rfcs/003-consolidate
| **language-yaml** | [`./language-yaml`](./language-yaml) | |
| **line-ending-selector** | [`./line-ending-selector`](./line-ending-selector) | |
| **link** | [`./link`](./link) | |
| **markdown-preview** | [`atom/markdown-preview`][markdown-preview] | |
| **markdown-preview** | [`./markdown-preview`][./markdown-preview] | |
| **notifications** | [`atom/notifications`][notifications] | [#18277](https://github.com/atom/atom/issues/18277) |
| **one-dark-syntax** | [`./one-dark-syntax`](./one-dark-syntax) | |
| **one-dark-ui** | [`./one-dark-ui`](./one-dark-ui) | |
| **one-light-syntax** | [`./one-light-syntax`](./one-light-syntax) | |
| **one-light-ui** | [`./one-light-ui`](./one-light-ui) | |
| **open-on-github** | [`atom/open-on-github`][open-on-github] | [#18278](https://github.com/atom/atom/issues/18278) |
| **package-generator** | [`atom/package-generator`][package-generator] | [#18279](https://github.com/atom/atom/issues/18279) |
| **settings-view** | [`atom/settings-view`][settings-view] | |
| **snippets** | [`atom/snippets`][snippets] | |
| **open-on-github** | [`./open-on-github`](./open-on-github) | |
| **settings-view** | [`./settings-view`](./settings-view) | |
| **package-generator** | [`./package-generator`](./package-generator) | |
| **snippets** | [`pulsar-edit/snippets`][snippets] | |
| **solarized-dark-syntax** | [`./solarized-dark-syntax`](./solarized-dark-syntax) | |
| **solarized-light-syntax** | [`./solarized-light-syntax`](./solarized-light-syntax) | |
| **spell-check** | [`atom/spell-check`][spell-check] | |
| **status-bar** | [`atom/status-bar`][status-bar] | [#18282](https://github.com/atom/atom/issues/18282) |
| **styleguide** | [`atom/styleguide`][styleguide] | [#18283](https://github.com/atom/atom/issues/18283) |
| **symbols-view** | [`atom/symbols-view`][symbols-view] | |
| **tabs** | [`atom/tabs`][tabs] | |
| **timecop** | [`atom/timecop`][timecop] | [#18272](https://github.com/atom/atom/issues/18272) |
| **tree-view** | [`atom/tree-view`][tree-view] | |
| **status-bar** | [`./status-bar`](./status-bar) | |
| **styleguide** | [`./styleguide`][./styleguide] | |
| **symbols-view** | [`pulsar-edit/symbols-view`][symbols-view] | |
| **tabs** | [`./tabs`](./tabs) | |
| **timecop** | [`pulsar-edit/timecop`][timecop] | [#18272](https://github.com/atom/atom/issues/18272) |
| **tree-view** | [`pulsar-edit/tree-view`][tree-view] | |
| **update-package-dependencies** | [`./update-package-dependencies`](./update-package-dependencies) | |
| **welcome** | [`./welcome`](./welcome) | |
| **whitespace** | [`atom/whitespace`][whitespace] | |
| **wrap-guide** | [`atom/wrap-guide`][wrap-guide] | [#18286](https://github.com/atom/atom/issues/18286) |
| **whitespace** | [`./whitespace`](./whitespace) | |
| **wrap-guide** | [`./wrap-guide`][./wrap-guide] | |
[archive-view]: https://github.com/pulsar-edit/archive-view
[autocomplete-atom-api]: https://github.com/pulsar-edit/autocomplete-atom-api
[autocomplete-css]: https://github.com/pulsar-edit/autocomplete-css
[autocomplete-html]: https://github.com/pulsar-edit/autocomplete-html
[autocomplete-plus]: https://github.com/pulsar-edit/autocomplete-plus
[autocomplete-snippets]: https://github.com/pulsar-edit/autocomplete-snippets
[autosave]: https://github.com/pulsar-edit/autosave
[background-tips]: https://github.com/pulsar-edit/background-tips
[bookmarks]: https://github.com/pulsar-edit/bookmarks
[bracket-matcher]: https://github.com/pulsar-edit/bracket-matcher
[command-palette]: https://github.com/pulsar-edit/command-palette
[encoding-selector]: https://github.com/pulsar-edit/encoding-selector
[find-and-replace]: https://github.com/pulsar-edit/find-and-replace
[fuzzy-finder]: https://github.com/pulsar-edit/fuzzy-finder
[github]: https://github.com/pulsar-edit/github
[image-view]: https://github.com/pulsar-edit/image-view
[keybinding-resolver]: https://github.com/pulsar-edit/keybinding-resolver
[markdown-preview]: https://github.com/pulsar-edit/markdown-preview
[notifications]: https://github.com/pulsar-edit/notifications
[open-on-github]: https://github.com/pulsar-edit/open-on-github
[package-generator]: https://github.com/pulsar-edit/package-generator
[settings-view]: https://github.com/pulsar-edit/settings-view
[snippets]: https://github.com/pulsar-edit/snippets
[spell-check]: https://github.com/pulsar-edit/spell-check
[status-bar]: https://github.com/pulsar-edit/status-bar
[styleguide]: https://github.com/pulsar-edit/styleguide
[symbols-view]: https://github.com/pulsar-edit/symbols-view
[tabs]: https://github.com/pulsar-edit/tabs
[timecop]: https://github.com/pulsar-edit/timecop
[tree-view]: https://github.com/pulsar-edit/tree-view
[whitespace]: https://github.com/pulsar-edit/whitespace
[wrap-guide]: https://github.com/pulsar-edit/wrap-guide

View File

@ -1,20 +1,22 @@
Copyright (c) 2015 Machisté N. Quintana
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
Copyright (c) 2022 Pulsar-Edit
Original work copyright (c) 2015 Machisté N. Quintana
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,21 +1,17 @@
# About package
View useful information about your Atom installation.
View useful information about your Pulsar installation.
![About Atom](https://cloud.githubusercontent.com/assets/16760489/19395499/69bbb780-922d-11e6-9779-2b8327027ea5.png)
![About Pulsar](https://cloud.githubusercontent.com/assets/16760489/19395499/69bbb780-922d-11e6-9779-2b8327027ea5.png)
This is a package for [Atom](https://atom.io), a community-led hyper-hackable text editor
This is a package for [Pulsar](https://pulsar-edit.dev), a community-led hyper-hackable text editor
## Usage
This package provides a cross-platform "About Atom" view that displays information about your Atom installation, which currently includes the current version, the license, and the Terms of Use.
This package provides a cross-platform "About Pulsar" view that displays information about your Pulsar installation, which currently includes the current version, the license, and the Terms of Use.
## Contributing
Always feel free to help out! Whether it's filing bugs and feature requests
or working on some of the open issues, Atom's [contributing guide](https://github.com/atom/atom/blob/master/CONTRIBUTING.md)
will help get you started while the [guide for contributing to packages](https://github.com/atom/atom/blob/master/docs/contributing-to-packages.md)
or working on some of the open issues, Pulsar's [contributing guide](https://github.com/pulsar-edit/.github/blob/main/CONTRIBUTING.md)
will help get you started while the [guide for contributing to packages](https://pulsar-edit.dev/docs/launch-manual/sections/core-hacking/#contributing-to-packages)
has some extra information.
## License
[MIT License](https://opensource.org/licenses/MIT) - see the [LICENSE](https://github.com/atom/about/blob/master/LICENSE.md) for more details.

View File

@ -1,7 +1,7 @@
const { Disposable } = require('atom');
const etch = require('etch');
const { shell } = require('electron');
const AtomLogo = require('./atom-logo'); //Update to new Pulsar logo (L#83)
const AtomLogo = require('./atom-logo');
const EtchComponent = require('../etch-component');
const UpdateView = require('./update-view');
@ -46,6 +46,7 @@ module.exports = class AboutView extends EtchComponent {
handleTermsOfUseClick(e) {
e.preventDefault();
shell.openExternal('https://atom.io/terms'); //If we use this then this URL will need updating but button disabled (L#182)
// TODO Update to Privacy Policy once `pulsar-edit.github.io` #161 is resolved
}
handleHowToUpdateClick(e) {
@ -80,8 +81,7 @@ module.exports = class AboutView extends EtchComponent {
{ className: 'about-header' },
$.a(
{ className: 'about-atom-io', href: `${atom.branding.urlWeb}`, },
//$(AtomLogo)
'Pulsar' //Remove and reinstate above line when available
$(AtomLogo)
),
$.div(
{ className: 'about-header-info' },
@ -224,4 +224,3 @@ module.exports = class AboutView extends EtchComponent {
return 'info';
}
};

View File

@ -125,7 +125,7 @@ let UpdateManager = class UpdateManager {
getReleaseNotesURLForVersion(appVersion) {
// Dev versions will not have a releases page
if (appVersion.indexOf('dev') > -1) {
return 'https://atom.io/releases';
return 'https://pulsar-edit.dev/download.html';
}
if (!appVersion.startsWith('v')) {

File diff suppressed because it is too large Load Diff

View File

@ -10,8 +10,8 @@
"atom": ">=1.7 <2.0.0"
},
"dependencies": {
"etch": "0.9.0",
"semver": "^5.5.0"
"etch": "^0.14.1",
"semver": "^7.3.8"
},
"consumedServices": {
"status-bar": {

View File

@ -25,7 +25,7 @@ describe('About', () => {
});
describe('when the about:about-atom command is triggered', () => {
it('shows the About Atom view', async () => {
it('shows the About Pulsar view', async () => {
// Attaching the workspaceElement to the DOM is required to allow the
// `toBeVisible()` matchers to work. Anything testing visibility or focus
// requires that the workspaceElement is on the DOM. Tests that attach the
@ -40,7 +40,7 @@ describe('About', () => {
});
});
describe('when the Atom version number is clicked', () => {
describe('when the Pulsar version number is clicked', () => {
it('copies the version number to the clipboard', async () => {
await atom.workspace.open('atom://about');
jasmine.attachToDOM(workspaceElement);

View File

@ -60,7 +60,7 @@ describe('the status bar', () => {
});
});
it('continues to show the squirrel until Atom is updated to the new version', async () => {
it('continues to show the squirrel until Pulsar is updated to the new version', async () => {
MockUpdater.finishDownloadingUpdate('42.0.0');
expect(workspaceElement).toContain('.about-release-notes');
@ -81,7 +81,7 @@ describe('the status bar', () => {
expect(workspaceElement).not.toContain('.about-release-notes');
});
it('does not show the view if Atom is updated to a newer version than notified', async () => {
it('does not show the view if Pulsar is updated to a newer version than notified', async () => {
MockUpdater.finishDownloadingUpdate('42.0.0');
await atom.packages.deactivatePackage('about');
@ -125,7 +125,7 @@ describe('the status bar', () => {
});
});
it('continues to show the squirrel until Atom is updated to the new version', async () => {
it('continues to show the squirrel until Pulsar is updated to the new version', async () => {
MockUpdater.finishDownloadingUpdate('42.0.0');
expect(workspaceElement).toContain('.about-release-notes');
@ -146,7 +146,7 @@ describe('the status bar', () => {
expect(workspaceElement).not.toContain('.about-release-notes');
});
it('does not show the view if Atom is updated to a newer version than notified', async () => {
it('does not show the view if Pulsar is updated to a newer version than notified', async () => {
MockUpdater.finishDownloadingUpdate('42.0.0');
await atom.packages.deactivatePackage('about');

View File

@ -8,26 +8,27 @@ describe('UpdateManager', () => {
});
describe('::getReleaseNotesURLForVersion', () => {
it('returns atom.io releases when dev version', () => {
it('returns pulsar-edit download page when dev version', () => {
expect(
updateManager.getReleaseNotesURLForVersion('1.7.0-dev-e44b57d')
).toContain('atom.io/releases');
).toContain('pulsar-edit.dev/download');
});
it('returns the page for the release when not a dev version', () => {
expect(updateManager.getReleaseNotesURLForVersion('1.7.0')).toContain(
'atom-ide-community/atom/releases/tag/v1.7.0'
expect(updateManager.getReleaseNotesURLForVersion('1.100.0')).toContain(
'pulsar-edit/pulsar/releases/tag/v1.100.0'
);
expect(updateManager.getReleaseNotesURLForVersion('v1.7.0')).toContain(
'atom-ide-community/atom/releases/tag/v1.7.0'
expect(updateManager.getReleaseNotesURLForVersion('v1.100.0')).toContain(
'pulsar-edit/pulsar/releases/tag/v1.100.0'
);
// TODO: Since we no longer follow release channels, is it useful to continue testing their state?
expect(
updateManager.getReleaseNotesURLForVersion('1.7.0-beta10')
).toContain('atom-ide-community/atom/releases/tag/v1.7.0-beta10');
updateManager.getReleaseNotesURLForVersion('1.100.0-beta10')
).toContain('pulsar-edit/pulsar/releases/tag/v1.100.0-beta10');
expect(
updateManager.getReleaseNotesURLForVersion('1.7.0-nightly10')
updateManager.getReleaseNotesURLForVersion('1.100.0-nightly10')
).toContain(
'atom-ide-community/atom-nightly-releases/releases/tag/v1.7.0-nightly10'
'pulsar-edit/pulsar-nightly-releases/releases/tag/v1.100.0-nightly10'
);
});
});

View File

@ -61,7 +61,7 @@ describe('UpdateView', () => {
let args = shell.openExternal.mostRecentCall.args;
expect(shell.openExternal).toHaveBeenCalled();
expect(args[0]).toContain('installing-atom');
expect(args[0]).toContain('installing-pulsar');
});
});
@ -241,120 +241,120 @@ describe('UpdateView', () => {
).toBe('Check now');
});
describe('when core.automaticallyUpdate is toggled', () => {
beforeEach(async () => {
expect(atom.config.get('core.automaticallyUpdate')).toBe(true);
atom.autoUpdater.checkForUpdate.reset();
});
it('shows the auto update UI', async () => {
expect(
aboutElement.querySelector('.about-auto-updates input').checked
).toBe(true);
expect(
aboutElement.querySelector('.about-default-update-message')
).toBeVisible();
expect(
aboutElement.querySelector('.about-default-update-message')
.textContent
).toBe('Pulsar will check for updates automatically');
atom.config.set('core.automaticallyUpdate', false);
await scheduler.getNextUpdatePromise();
expect(
aboutElement.querySelector('.about-auto-updates input').checked
).toBe(false);
expect(
aboutElement.querySelector('.about-default-update-message')
).toBeVisible();
expect(
aboutElement.querySelector('.about-default-update-message')
.textContent
).toBe('Automatic updates are disabled please check manually');
});
it('updates config and the UI when the checkbox is used to toggle', async () => {
expect(
aboutElement.querySelector('.about-auto-updates input').checked
).toBe(true);
aboutElement.querySelector('.about-auto-updates input').click();
await scheduler.getNextUpdatePromise();
expect(atom.config.get('core.automaticallyUpdate')).toBe(false);
expect(
aboutElement.querySelector('.about-auto-updates input').checked
).toBe(false);
expect(
aboutElement.querySelector('.about-default-update-message')
).toBeVisible();
expect(
aboutElement.querySelector('.about-default-update-message')
.textContent
).toBe('Automatic updates are disabled please check manually');
aboutElement.querySelector('.about-auto-updates input').click();
await scheduler.getNextUpdatePromise();
expect(atom.config.get('core.automaticallyUpdate')).toBe(true);
expect(
aboutElement.querySelector('.about-auto-updates input').checked
).toBe(true);
expect(
aboutElement.querySelector('.about-default-update-message')
).toBeVisible();
expect(
aboutElement.querySelector('.about-default-update-message')
.textContent
).toBe('Pulsar will check for updates automatically');
});
describe('checking for updates', function() {
afterEach(() => {
this.updateView = null;
});
it('checks for update when the about page is shown', () => {
expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled();
this.updateView = new UpdateView({
updateManager: updateManager,
availableVersion: '9999.0.0',
viewUpdateReleaseNotes: () => {}
});
expect(atom.autoUpdater.checkForUpdate).toHaveBeenCalled();
});
it('does not check for update when the about page is shown and the update manager is not in the idle state', () => {
atom.autoUpdater.getState.andReturn('downloading');
updateManager.resetState();
expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled();
this.updateView = new UpdateView({
updateManager: updateManager,
availableVersion: '9999.0.0',
viewUpdateReleaseNotes: () => {}
});
expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled();
});
it('does not check for update when the about page is shown and auto updates are turned off', () => {
atom.config.set('core.automaticallyUpdate', false);
expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled();
this.updateView = new UpdateView({
updateManager: updateManager,
availableVersion: '9999.0.0',
viewUpdateReleaseNotes: () => {}
});
expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled();
});
});
});
// describe('when core.automaticallyUpdate is toggled', () => {
// beforeEach(async () => {
// expect(atom.config.get('core.automaticallyUpdate')).toBe(true);
// atom.autoUpdater.checkForUpdate.reset();
// });
//
// it('shows the auto update UI', async () => {
// expect(
// aboutElement.querySelector('.about-auto-updates input').checked
// ).toBe(true);
// expect(
// aboutElement.querySelector('.about-default-update-message')
// ).toBeVisible();
// expect(
// aboutElement.querySelector('.about-default-update-message')
// .textContent
// ).toBe('Pulsar will check for updates automatically');
//
// atom.config.set('core.automaticallyUpdate', false);
// await scheduler.getNextUpdatePromise();
//
// expect(
// aboutElement.querySelector('.about-auto-updates input').checked
// ).toBe(false);
// expect(
// aboutElement.querySelector('.about-default-update-message')
// ).toBeVisible();
// expect(
// aboutElement.querySelector('.about-default-update-message')
// .textContent
// ).toBe('Automatic updates are disabled please check manually');
// });
//
// it('updates config and the UI when the checkbox is used to toggle', async () => {
// expect(
// aboutElement.querySelector('.about-auto-updates input').checked
// ).toBe(true);
//
// aboutElement.querySelector('.about-auto-updates input').click();
// await scheduler.getNextUpdatePromise();
//
// expect(atom.config.get('core.automaticallyUpdate')).toBe(false);
// expect(
// aboutElement.querySelector('.about-auto-updates input').checked
// ).toBe(false);
// expect(
// aboutElement.querySelector('.about-default-update-message')
// ).toBeVisible();
// expect(
// aboutElement.querySelector('.about-default-update-message')
// .textContent
// ).toBe('Automatic updates are disabled please check manually');
//
// aboutElement.querySelector('.about-auto-updates input').click();
// await scheduler.getNextUpdatePromise();
//
// expect(atom.config.get('core.automaticallyUpdate')).toBe(true);
// expect(
// aboutElement.querySelector('.about-auto-updates input').checked
// ).toBe(true);
// expect(
// aboutElement.querySelector('.about-default-update-message')
// ).toBeVisible();
// expect(
// aboutElement.querySelector('.about-default-update-message')
// .textContent
// ).toBe('Pulsar will check for updates automatically');
// });
//
// describe('checking for updates', function() {
// afterEach(() => {
// this.updateView = null;
// });
//
// it('checks for update when the about page is shown', () => {
// expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled();
//
// this.updateView = new UpdateView({
// updateManager: updateManager,
// availableVersion: '9999.0.0',
// viewUpdateReleaseNotes: () => {}
// });
//
// expect(atom.autoUpdater.checkForUpdate).toHaveBeenCalled();
// });
//
// it('does not check for update when the about page is shown and the update manager is not in the idle state', () => {
// atom.autoUpdater.getState.andReturn('downloading');
// updateManager.resetState();
// expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled();
//
// this.updateView = new UpdateView({
// updateManager: updateManager,
// availableVersion: '9999.0.0',
// viewUpdateReleaseNotes: () => {}
// });
//
// expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled();
// });
//
// it('does not check for update when the about page is shown and auto updates are turned off', () => {
// atom.config.set('core.automaticallyUpdate', false);
// expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled();
//
// this.updateView = new UpdateView({
// updateManager: updateManager,
// availableVersion: '9999.0.0',
// viewUpdateReleaseNotes: () => {}
// });
//
// expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled();
// });
// });
// });
});
});

View File

@ -0,0 +1,20 @@
# Archive view package
Adds support for browsing archive files in Pulsar with the following extensions:
* `.egg`
* `.epub`
* `.jar`
* `.love`
* `.nupkg`
* `.tar`
* `.tar.gz`
* `.tgz`
* `.war`
* `.whl`
* `.xpi`
* `.zip`
Select a file to extract it to a temp file and open it in a new editor.
![](./resources/preview.png)

View File

@ -0,0 +1,3 @@
'.archive-editor':
'k': 'core:move-up'
'j': 'core:move-down'

View File

@ -0,0 +1,208 @@
/** @babel */
/** @jsx etch.dom */
import fs from 'fs'
import humanize from 'humanize-plus'
import archive from 'ls-archive'
import {CompositeDisposable, Disposable, Emitter, File} from 'atom'
import etch from 'etch'
import FileView from './file-view'
import DirectoryView from './directory-view'
export default class ArchiveEditorView {
constructor (archivePath) {
this.disposables = new CompositeDisposable()
this.emitter = new Emitter()
this.path = archivePath
this.file = new File(this.path)
this.entries = []
etch.initialize(this)
this.refresh()
this.disposables.add(this.file.onDidChange(() => this.refresh()))
this.disposables.add(this.file.onDidRename(() => this.refresh()))
this.disposables.add(this.file.onDidDelete(() => this.destroy()))
const focusHandler = () => this.focusSelectedFile()
this.element.addEventListener('focus', focusHandler)
this.disposables.add(new Disposable(() => this.element.removeEventListener('focus', focusHandler)))
}
update () {}
render () {
return (
<div className='archive-editor' tabIndex='-1'>
<div className='archive-container'>
<div ref='loadingMessage' className='padded icon icon-hourglass text-info'>{`Loading archive\u2026`}</div>
<div ref='errorMessage' className='padded icon icon-alert text-error' />
<div className='inset-panel'>
<div ref='summary' className='panel-heading' />
<ol ref='tree' className='archive-tree padded list-tree has-collapsable-children' />
</div>
</div>
</div>
)
}
copy () {
return new ArchiveEditorView(this.path)
}
destroy () {
while (this.entries.length > 0) {
this.entries.pop().destroy()
}
this.disposables.dispose()
this.emitter.emit('did-destroy')
etch.destroy(this)
}
onDidDestroy (callback) {
return this.emitter.on('did-destroy', callback)
}
onDidChangeTitle (callback) {
return this.emitter.on('did-change-title', callback)
}
serialize () {
return {
deserializer: this.constructor.name,
path: this.path
}
}
getPath () {
return this.file.getPath()
}
getTitle () {
return this.path ? this.file.getBaseName() : 'untitled'
}
getURI () {
return this.path
}
refresh () {
this.refs.summary.style.display = 'none'
this.refs.tree.style.display = 'none'
this.refs.loadingMessage.style.display = ''
this.refs.errorMessage.style.display = 'none'
if (this.path !== this.getPath()) {
this.path = this.getPath()
this.emitter.emit('did-change-title')
}
const originalPath = this.path
archive.list(this.path, {tree: true}, (error, entries) => {
if (originalPath !== this.path) {
return
}
if (error != null) {
let message = 'Reading the archive file failed'
if (error.message) {
message += `: ${error.message}`
}
this.refs.errorMessage.style.display = ''
this.refs.errorMessage.textContent = message
} else {
this.createTreeEntries(entries)
this.updateSummary()
}
// We hide the loading message _after_ creating the archive tree
// to avoid forced reflows.
this.refs.loadingMessage.style.display = 'none'
})
}
createTreeEntries (entries) {
while (this.entries.length > 0) {
this.entries.pop().destroy()
}
let index = 0
for (const entry of entries) {
if (entry.isDirectory()) {
const entryView = new DirectoryView(this, index, this.path, entry)
this.entries.push(entryView)
} else {
const entryView = new FileView(this, index, this.path, entry)
this.entries.push(entryView)
}
index++
}
this.selectFileAfterIndex(-1)
// Wait until selecting (focusing) the first file before appending the entries
// to avoid a double-forced reflow when focusing.
for (const entry of this.entries) {
this.refs.tree.appendChild(entry.element)
}
this.refs.tree.style.display = ''
}
updateSummary () {
const fileCount = this.entries.filter((entry) => entry instanceof FileView).length
const fileLabel = fileCount === 1 ? '1 file' : `${humanize.intComma(fileCount)} files`
const directoryCount = this.entries.filter((entry) => entry instanceof DirectoryView).length
const directoryLabel = directoryCount === 1 ? '1 folder' : `${humanize.intComma(directoryCount)} folders`
this.refs.summary.style.display = ''
let fileSize
try {
fileSize = fs.statSync(this.path)?.size;
} catch (e) {}
if (fileSize == null) fileSize = -1
this.refs.summary.textContent = `${humanize.fileSize(fileSize)} with ${fileLabel} and ${directoryLabel}`
}
focusSelectedFile () {
const selectedFile = this.refs.tree.querySelector('.selected')
if (selectedFile) {
selectedFile.focus()
}
}
selectFileBeforeIndex (index) {
for (let i = index - 1; i >= 0; i--) {
const previousEntry = this.entries[i]
if (previousEntry instanceof FileView) {
previousEntry.select()
break
} else {
if (previousEntry.selectLastFile()) {
break
}
}
}
}
selectFileAfterIndex (index) {
for (let i = index + 1; i < this.entries.length; i++) {
const nextEntry = this.entries[i]
if (nextEntry instanceof FileView) {
nextEntry.select()
break
} else {
if (nextEntry.selectFirstFile()) {
break
}
}
}
}
focus () {
this.focusSelectedFile()
}
}

View File

@ -0,0 +1,74 @@
const fs = require('fs')
const path = require('path')
const {Disposable} = require('atom')
const getIconServices = require('./get-icon-services')
const ArchiveEditorView = require('./archive-editor-view')
module.exports = {
activate () {
this.disposable = atom.workspace.addOpener((filePath = '') => {
// Check that filePath exists before opening, in case a remote URI was given
if (!isPathSupported(filePath)) return;
let isFile = false
try {
isFile = fs.statSync(filePath)?.isFile()
} catch (e) {}
if (isFile) {
return new ArchiveEditorView(filePath)
}
})
},
deactivate () {
this.disposable.dispose()
for (const item of atom.workspace.getPaneItems()) {
if (item instanceof ArchiveEditorView) {
item.destroy()
}
}
},
consumeElementIcons (service) {
getIconServices().setElementIcons(service)
return new Disposable(() => getIconServices().resetElementIcons())
},
consumeFileIcons (service) {
getIconServices().setFileIcons(service)
return new Disposable(() => getIconServices().resetFileIcons())
},
deserialize (params = {}) {
let isFile = false
try {
isFile = fs.statSync(params.path)?.isFile()
} catch (e) {}
if (isFile) {
return new ArchiveEditorView(params.path)
} else {
console.warn(`Can't build ArchiveEditorView for path "${params.path}"; file no longer exists`)
}
}
}
function isPathSupported (filePath) {
switch (path.extname(filePath)) {
case '.egg':
case '.epub':
case '.jar':
case '.love':
case '.nupkg':
case '.tar':
case '.tgz':
case '.war':
case '.whl':
case '.xpi':
case '.zip':
return true
case '.gz':
return path.extname(path.basename(filePath, '.gz')) === '.tar'
default:
return false
}
}

View File

@ -0,0 +1,36 @@
const fs = require('fs')
const path = require('path')
class DefaultFileIcons {
iconClassForPath (filePath) {
const extension = path.extname(filePath).toLowerCase()
const base = path.basename(filePath, extension).toLowerCase();
let isSymbolicLinkSync = false
try {
fs.lstatSync(filePath)?.isSymbolicLink();
} catch (e) {}
if (isSymbolicLinkSync) return 'icon-file-symlink-file'
if (base === 'readme' && ['','.markdown','.md','.mdown','.mkd','.mkdown','.rmd','.ron'].includes(extension)) {
return 'icon-book'
}
if (['.bz2','.egg','.epub','.gem','.gz','.jar','.lz','.lzma','.lzo','.rar','.tar','.tgz','.war','.whl','.xpi','.xz','.z','.zip'].includes(extension)) {
return 'icon-file-zip'
}
if (['.gif','.ico','.jpeg','.jpg','.png','.tif','.tiff','.webp'].includes(extension)) {
return 'icon-file-media'
}
if (extension === ".pdf") return 'icon-file-pdf'
if (['.ds_store','.a','.exe','.o','.pyc','.pyo','.so','.woff'].includes(extension)) {
return 'icon-file-binary'
}
return 'icon-file-text'
}
}
module.exports = new DefaultFileIcons()

View File

@ -0,0 +1,132 @@
/** @babel */
import {CompositeDisposable, Disposable} from 'atom'
import FileView from './file-view'
import getIconServices from './get-icon-services'
export default class DirectoryView {
constructor (parentView, indexInParentView, archivePath, entry) {
this.disposables = new CompositeDisposable()
this.entries = []
this.parentView = parentView
this.indexInParentView = indexInParentView
this.element = document.createElement('li')
this.element.classList.add('list-nested-item', 'entry')
const listItem = document.createElement('span')
listItem.classList.add('list-item')
const clickHandler = (event) => {
event.stopPropagation()
event.preventDefault()
this.element.classList.toggle('collapsed')
}
listItem.addEventListener('click', clickHandler)
this.disposables.add(new Disposable(() => { listItem.removeEventListener('click', clickHandler) }))
const entrySpan = document.createElement('span')
entrySpan.textContent = entry.getName()
listItem.appendChild(entrySpan)
this.element.appendChild(listItem)
this.entry = entry
this.entrySpan = entrySpan
getIconServices().updateDirectoryIcon(this)
this.entriesTree = document.createElement('ol')
this.entriesTree.classList.add('list-tree')
let index = 0
for (const child of entry.children) {
if (child.isDirectory()) {
const entryView = new DirectoryView(this, index, archivePath, child)
this.entries.push(entryView)
this.entriesTree.appendChild(entryView.element)
} else {
const entryView = new FileView(this, index, archivePath, child)
this.entries.push(entryView)
this.entriesTree.appendChild(entryView.element)
}
index++
}
this.element.appendChild(this.entriesTree)
}
destroy () {
if (this.iconDisposable) {
this.iconDisposable.dispose()
this.iconDisposable = null
}
while (this.entries.length > 0) {
this.entries.pop().destroy()
}
this.disposables.dispose()
this.element.remove()
}
selectFileBeforeIndex (index) {
for (let i = index - 1; i >= 0; i--) {
const previousEntry = this.entries[i]
if (previousEntry instanceof FileView) {
previousEntry.select()
return
} else {
if (previousEntry.selectLastFile()) {
return
}
}
}
this.parentView.selectFileBeforeIndex(this.indexInParentView)
}
selectFileAfterIndex (index) {
for (let i = index + 1; i < this.entries.length; i++) {
const nextEntry = this.entries[i]
if (nextEntry instanceof FileView) {
nextEntry.select()
return
} else {
if (nextEntry.selectFirstFile()) {
return
}
}
}
this.parentView.selectFileAfterIndex(this.indexInParentView)
}
selectFirstFile () {
for (const entry of this.entries) {
if (entry instanceof FileView) {
entry.select()
return true
} else {
if (entry.selectFirstFile()) {
return true
}
}
}
return false
}
selectLastFile () {
for (var i = this.entries.length - 1; i >= 0; i--) {
const entry = this.entries[i]
if (entry instanceof FileView) {
entry.select()
return true
} else {
if (entry.selectLastFile()) {
return true
}
}
}
return false
}
}

View File

@ -0,0 +1,112 @@
/** @babel */
import {CompositeDisposable, Disposable} from 'atom'
import path from 'path'
import fs from 'fs'
import temp from 'temp'
import archive from 'ls-archive'
import getIconServices from './get-icon-services'
export default class FileView {
constructor (parentView, indexInParentView, archivePath, entry) {
this.disposables = new CompositeDisposable()
this.parentView = parentView
this.indexInParentView = indexInParentView
this.archivePath = archivePath
this.entry = entry
this.element = document.createElement('li')
this.element.classList.add('list-item', 'entry')
this.element.tabIndex = -1
this.name = document.createElement('span')
getIconServices().updateFileIcon(this)
this.name.textContent = this.entry.getName()
this.element.appendChild(this.name)
const clickHandler = () => {
this.select()
this.openFile()
}
this.element.addEventListener('click', clickHandler)
this.disposables.add(new Disposable(() => { this.element.removeEventListener('click', clickHandler) }))
this.disposables.add(atom.commands.add(this.element, {
'core:confirm': () => {
if (this.isSelected()) {
this.openFile()
}
},
'core:move-down': () => {
if (this.isSelected()) {
this.parentView.selectFileAfterIndex(this.indexInParentView)
}
},
'core:move-up': () => {
if (this.isSelected()) {
this.parentView.selectFileBeforeIndex(this.indexInParentView)
}
}
}))
}
destroy () {
this.disposables.dispose()
this.element.remove()
}
isSelected () {
return this.element.classList.contains('selected')
}
logError (message, error) {
console.error(message, error.stack != null ? error.stack : error)
}
openFile () {
archive.readFile(this.archivePath, this.entry.getPath(), (error, contents) => {
if (error != null) {
this.logError(`Error reading: ${this.entry.getPath()} from ${this.archivePath}`, error)
} else {
temp.mkdir('atom-', (error, tempDirPath) => {
if (error != null) {
this.logError(`Error creating temp directory: ${tempDirPath}`, error)
} else {
const tempArchiveDirPath = path.join(tempDirPath, path.basename(this.archivePath))
fs.mkdir(tempArchiveDirPath, {recursive:true}, error => {
if (error != null) {
this.logError(`Error creating archive directory ${tempArchiveDirPath}`, error)
} else {
const tempFilePath = path.join(tempArchiveDirPath, this.entry.getName())
fs.writeFile(tempFilePath, contents, error => {
if (error != null) {
this.logError(`Error writing to ${tempFilePath}`, error)
} else {
atom.workspace.open(tempFilePath)
}
})
}
})
}
})
}
})
}
select () {
this.element.focus()
const archiveEditorElement = this.element.closest('.archive-editor')
// On initial tree creation, it is not possible for any entries to be selected
// (The entries also haven't been added to the DOM yet)
if (archiveEditorElement) {
for (const selected of archiveEditorElement.querySelectorAll('.selected')) {
selected.classList.remove('selected')
}
}
this.element.classList.add('selected')
}
}

View File

@ -0,0 +1,72 @@
const DefaultFileIcons = require('./default-file-icons')
const {Emitter, CompositeDisposable} = require('atom')
const path = require('path')
let iconServices
module.exports = function getIconServices () {
if (!iconServices) iconServices = new IconServices()
return iconServices
}
class IconServices {
constructor () {
this.emitter = new Emitter()
this.elementIcons = null
this.elementIconDisposables = new CompositeDisposable()
this.fileIcons = DefaultFileIcons
}
onDidChange (callback) {
return this.emitter.on('did-change', callback)
}
resetElementIcons () {
this.setElementIcons(null)
}
resetFileIcons () {
this.setFileIcons(DefaultFileIcons)
}
setElementIcons (service) {
if (service !== this.elementIcons) {
if (this.elementIconDisposables != null) {
this.elementIconDisposables.dispose()
}
if (service) { this.elementIconDisposables = new CompositeDisposable() }
this.elementIcons = service
return this.emitter.emit('did-change')
}
}
setFileIcons (service) {
if (service !== this.fileIcons) {
this.fileIcons = service
return this.emitter.emit('did-change')
}
}
updateDirectoryIcon (view) {
view.entrySpan.classList.add('directory', 'icon', 'icon-file-directory')
if (this.elementIcons) {
view.iconDisposable = this.elementIcons(view.entrySpan, view.entry.path, {isDirectory: true})
}
}
updateFileIcon (view) {
const nameClasses = ['file', 'icon']
if (this.elementIcons) {
const fullPath = path.join(view.archivePath, view.entry.path)
const disposable = this.elementIcons(view.name, fullPath)
view.disposables.add(disposable)
this.elementIconDisposables.add(disposable)
} else {
let typeClass = this.fileIcons.iconClassForPath(view.entry.path, 'archive-view') || []
if (!Array.isArray(typeClass) && typeClass) {
typeClass = typeClass.toString().split(/\s+/g)
}
nameClasses.push(...typeClass)
}
view.name.classList.add(...nameClasses)
}
}

513
packages/archive-view/package-lock.json generated Normal file
View File

@ -0,0 +1,513 @@
{
"name": "archive-view",
"version": "0.66.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "archive-view",
"version": "0.66.0",
"license": "MIT",
"dependencies": {
"etch": "^0.14.1",
"humanize-plus": "~1.8.2",
"ls-archive": "1.3.4",
"temp": "^0.9.4"
},
"engines": {
"atom": "*"
}
},
"node_modules/async": {
"version": "0.2.10",
"resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
"integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ=="
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/block-stream": {
"version": "0.0.9",
"resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
"integrity": "sha512-OorbnJVPII4DuUKbjARAe8u8EfqOmkEEaSFIyoQ7OjTHn6kafxWl0wLgoZ2rXaYd7MyLcDaU4TmhfxtwgcccMQ==",
"dependencies": {
"inherits": "~2.0.0"
},
"engines": {
"node": "0.4 || >=0.5.8"
}
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/buffer-crc32": {
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
"integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
"engines": {
"node": "*"
}
},
"node_modules/colors": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz",
"integrity": "sha512-OsSVtHK8Ir8r3+Fxw/b4jS1ZLPXkV6ZxDRJQzeD7qo0SqMXWrHDM71DgYzPMHY8SFJ0Ao+nNU2p1MmwdzKqPrw==",
"engines": {
"node": ">=0.1.90"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
"node_modules/etch": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/etch/-/etch-0.14.1.tgz",
"integrity": "sha512-+IwqSDBhaQFMUHJu4L/ir0dhDoW5IIihg4Z9lzsIxxne8V0PlSg0gnk2STaKWjGJQnDR4cxpA+a/dORX9kycTA=="
},
"node_modules/fd-slicer": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
"integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
"dependencies": {
"pend": "~1.2.0"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
},
"node_modules/fstream": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
"integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
"dependencies": {
"graceful-fs": "^4.1.2",
"inherits": "~2.0.0",
"mkdirp": ">=0.5 0",
"rimraf": "2"
},
"engines": {
"node": ">=0.6"
}
},
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/graceful-fs": {
"version": "4.2.10",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
},
"node_modules/humanize-plus": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/humanize-plus/-/humanize-plus-1.8.2.tgz",
"integrity": "sha512-jaLeQyyzjjINGv7O9JJegjsaUcWjSj/1dcXvLEgU3pGdqCdP1PiC/uwr+saJXhTNBHZtmKnmpXyazgh+eceRxA==",
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/ls-archive": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/ls-archive/-/ls-archive-1.3.4.tgz",
"integrity": "sha512-7GmjZOckV+gzm4PM1/LcWIsZIRsSkAVmIchoEf5xjquNKU0Ti5KUvGQ3dl/7VsbZIduMOPwRDXrvpo3LVJ0Pmg==",
"dependencies": {
"async": "~0.2.9",
"colors": "~0.6.2",
"optimist": "~0.5.2",
"rimraf": "~2.2.6",
"tar": "^2.2.1",
"yauzl": "^2.9.1"
},
"bin": {
"lsa": "bin/lsa"
}
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/minimist": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
"integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dependencies": {
"minimist": "^1.2.6"
},
"bin": {
"mkdirp": "bin/cmd.js"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/optimist": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.5.2.tgz",
"integrity": "sha512-r9M8ZpnM9SXV5Wii7TCqienfcaY3tAiJe9Jchof87icbmbruKgK0xKXngmrnowTDnEawmmI1Qbha59JEoBkBGA==",
"dependencies": {
"wordwrap": "~0.0.2"
}
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/pend": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
"integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="
},
"node_modules/rimraf": {
"version": "2.2.8",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz",
"integrity": "sha512-R5KMKHnPAQaZMqLOsyuyUmcIjSeDm+73eoqQpaXA7AZ22BL+6C+1mcUscgOsNd8WVlJuvlgAPsegcx7pjlV0Dg==",
"bin": {
"rimraf": "bin.js"
}
},
"node_modules/tar": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz",
"integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==",
"deprecated": "This version of tar is no longer supported, and will not receive security updates. Please upgrade asap.",
"dependencies": {
"block-stream": "*",
"fstream": "^1.0.12",
"inherits": "2"
}
},
"node_modules/temp": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz",
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
"dependencies": {
"mkdirp": "^0.5.1",
"rimraf": "~2.6.2"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/temp/node_modules/rimraf": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
}
},
"node_modules/wordwrap": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
"integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"node_modules/yauzl": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
"integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
"dependencies": {
"buffer-crc32": "~0.2.3",
"fd-slicer": "~1.1.0"
}
}
},
"dependencies": {
"async": {
"version": "0.2.10",
"resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
"integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ=="
},
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"block-stream": {
"version": "0.0.9",
"resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
"integrity": "sha512-OorbnJVPII4DuUKbjARAe8u8EfqOmkEEaSFIyoQ7OjTHn6kafxWl0wLgoZ2rXaYd7MyLcDaU4TmhfxtwgcccMQ==",
"requires": {
"inherits": "~2.0.0"
}
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"buffer-crc32": {
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
"integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="
},
"colors": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz",
"integrity": "sha512-OsSVtHK8Ir8r3+Fxw/b4jS1ZLPXkV6ZxDRJQzeD7qo0SqMXWrHDM71DgYzPMHY8SFJ0Ao+nNU2p1MmwdzKqPrw=="
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
"etch": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/etch/-/etch-0.14.1.tgz",
"integrity": "sha512-+IwqSDBhaQFMUHJu4L/ir0dhDoW5IIihg4Z9lzsIxxne8V0PlSg0gnk2STaKWjGJQnDR4cxpA+a/dORX9kycTA=="
},
"fd-slicer": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
"integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
"requires": {
"pend": "~1.2.0"
}
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
},
"fstream": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
"integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
"requires": {
"graceful-fs": "^4.1.2",
"inherits": "~2.0.0",
"mkdirp": ">=0.5 0",
"rimraf": "2"
}
},
"glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"graceful-fs": {
"version": "4.2.10",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
},
"humanize-plus": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/humanize-plus/-/humanize-plus-1.8.2.tgz",
"integrity": "sha512-jaLeQyyzjjINGv7O9JJegjsaUcWjSj/1dcXvLEgU3pGdqCdP1PiC/uwr+saJXhTNBHZtmKnmpXyazgh+eceRxA=="
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"requires": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"ls-archive": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/ls-archive/-/ls-archive-1.3.4.tgz",
"integrity": "sha512-7GmjZOckV+gzm4PM1/LcWIsZIRsSkAVmIchoEf5xjquNKU0Ti5KUvGQ3dl/7VsbZIduMOPwRDXrvpo3LVJ0Pmg==",
"requires": {
"async": "~0.2.9",
"colors": "~0.6.2",
"optimist": "~0.5.2",
"rimraf": "~2.2.6",
"tar": "^2.2.1",
"yauzl": "^2.9.1"
}
},
"minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
"integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g=="
},
"mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"requires": {
"minimist": "^1.2.6"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"requires": {
"wrappy": "1"
}
},
"optimist": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.5.2.tgz",
"integrity": "sha512-r9M8ZpnM9SXV5Wii7TCqienfcaY3tAiJe9Jchof87icbmbruKgK0xKXngmrnowTDnEawmmI1Qbha59JEoBkBGA==",
"requires": {
"wordwrap": "~0.0.2"
}
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="
},
"pend": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
"integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="
},
"rimraf": {
"version": "2.2.8",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz",
"integrity": "sha512-R5KMKHnPAQaZMqLOsyuyUmcIjSeDm+73eoqQpaXA7AZ22BL+6C+1mcUscgOsNd8WVlJuvlgAPsegcx7pjlV0Dg=="
},
"tar": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz",
"integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==",
"requires": {
"block-stream": "*",
"fstream": "^1.0.12",
"inherits": "2"
}
},
"temp": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz",
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
"requires": {
"mkdirp": "^0.5.1",
"rimraf": "~2.6.2"
},
"dependencies": {
"rimraf": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
"requires": {
"glob": "^7.1.3"
}
}
}
},
"wordwrap": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
"integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw=="
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"yauzl": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
"integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
"requires": {
"buffer-crc32": "~0.2.3",
"fd-slicer": "~1.1.0"
}
}
}
}

View File

@ -0,0 +1,33 @@
{
"name": "archive-view",
"version": "0.66.0",
"description": "View the files and folders inside archive files",
"main": "./lib/archive-editor",
"dependencies": {
"etch": "^0.14.1",
"humanize-plus": "~1.8.2",
"ls-archive": "1.3.4",
"temp": "^0.9.4"
},
"repository": "https://github.com/pulsar-edit/pulsar",
"license": "MIT",
"engines": {
"atom": "*"
},
"deserializers": {
"ArchiveEditor": "deserialize",
"ArchiveEditorView": "deserialize"
},
"consumedServices": {
"atom.file-icons": {
"versions": {
"1.0.0": "consumeFileIcons"
}
},
"file-icons.element-icons": {
"versions": {
"1.0.0": "consumeElementIcons"
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

View File

@ -0,0 +1,46 @@
const {it, fit, ffit, fffit, beforeEach, afterEach, conditionPromise} = require('./async-spec-helpers') // eslint-disable-line no-unused-vars
const path = require('path')
const ArchiveEditor = require('../lib/archive-editor')
const ArchiveEditorView = require('../lib/archive-editor-view')
describe('ArchiveEditor', () => {
const tarPath = path.join(__dirname, 'fixtures', 'nested.tar')
// Don't log during specs
beforeEach(() => spyOn(console, 'warn'))
describe('.deserialize', () => {
it('returns undefined if no file exists at the given path', () => {
const editor1 = new ArchiveEditorView(tarPath)
const state = editor1.serialize()
editor1.destroy()
const editor2 = ArchiveEditor.deserialize(state)
expect(editor2).toBeDefined()
editor2.destroy()
state.path = 'bogus'
expect(ArchiveEditor.deserialize(state)).toBeUndefined()
})
})
describe('.deactivate()', () => {
it('removes all ArchiveEditorViews from the workspace and does not open any new ones', async () => {
const getArchiveEditorViews = () => {
return atom.workspace.getPaneItems().filter(item => item instanceof ArchiveEditorView)
}
await atom.packages.activatePackage('archive-view')
await atom.workspace.open(path.join(__dirname, 'fixtures', 'nested.tar'))
await atom.workspace.open(path.join(__dirname, 'fixtures', 'invalid.zip'))
await atom.workspace.open()
expect(getArchiveEditorViews().length).toBe(2)
await atom.packages.deactivatePackage('archive-view')
expect(getArchiveEditorViews().length).toBe(0)
await atom.workspace.open(path.join(__dirname, 'fixtures', 'nested.tar'))
expect(getArchiveEditorViews().length).toBe(0)
})
})
})

View File

@ -0,0 +1,276 @@
const {Disposable, File} = require('atom')
const getIconServices = require('../lib/get-icon-services')
const {it, fit, ffit, fffit, beforeEach, afterEach, conditionPromise} = require('./async-spec-helpers') // eslint-disable-line no-unused-vars
async function condition (handler) {
if (jasmine.isSpy(window.setTimeout)) {
jasmine.useRealClock()
}
return conditionPromise(handler)
}
describe('ArchiveEditorView', () => {
let archiveEditorView, onDidChangeCallback, onDidRenameCallback, onDidDeleteCallback
beforeEach(async () => {
spyOn(File.prototype, 'onDidChange').andCallFake(function (callback) {
if (/\.tar$/.test(this.getPath())) {
onDidChangeCallback = callback
}
return new Disposable()
})
spyOn(File.prototype, 'onDidRename').andCallFake(function (callback) {
if (/\.tar$/.test(this.getPath())) {
onDidRenameCallback = callback
}
return new Disposable()
})
spyOn(File.prototype, 'onDidDelete').andCallFake(function (callback) {
if (/\.tar$/.test(this.getPath())) {
onDidDeleteCallback = callback
}
return new Disposable()
})
await atom.packages.activatePackage('archive-view')
archiveEditorView = await atom.workspace.open('nested.tar')
})
describe('.constructor()', () => {
it('displays the files and folders in the archive file', async () => {
expect(archiveEditorView.element).toExist()
await condition(() => archiveEditorView.element.querySelectorAll('.entry').length > 0)
const directoryElements = archiveEditorView.element.querySelectorAll('.directory')
expect(directoryElements.length).toBe(6)
expect(directoryElements[0].textContent).toBe('d1')
expect(directoryElements[1].textContent).toBe('d2')
expect(directoryElements[2].textContent).toBe('d3')
expect(directoryElements[3].textContent).toBe('d4')
expect(directoryElements[4].textContent).toBe('da')
expect(directoryElements[5].textContent).toBe('db')
const fileElements = archiveEditorView.element.querySelectorAll('.file')
expect(fileElements.length).toBe(3)
expect(fileElements[0].textContent).toBe('f1.txt')
expect(fileElements[1].textContent).toBe('f2.txt')
expect(fileElements[2].textContent).toBe('fa.txt')
})
it('selects the first file', async () => {
await condition(() => archiveEditorView.element.querySelectorAll('.entry').length > 0)
expect(archiveEditorView.element.querySelector('.selected').textContent).toBe('f1.txt')
})
})
describe('.copy()', () => {
it('returns a new ArchiveEditorView for the same file', () => {
const newArchiveView = archiveEditorView.copy()
expect(newArchiveView.getPath()).toBe(archiveEditorView.getPath())
})
})
describe('archive summary', () => {
beforeEach(async () => {
await atom.workspace.open('multiple-entries.zip')
archiveEditorView = atom.workspace.getActivePaneItem()
jasmine.attachToDOM(atom.views.getView(atom.workspace))
})
it('shows correct statistics', async () => {
await condition(() => archiveEditorView.element.querySelectorAll('.entry').length > 0)
const heading = archiveEditorView.element.querySelector('.inset-panel .panel-heading')
expect(heading).not.toBe(null)
expect(heading.textContent).toBe('704 bytes with 4 files and 1 folder')
})
})
describe('when core:move-up/core:move-down is triggered', () => {
let selectedEntry
const dispatch = (command) => {
atom.commands.dispatch(archiveEditorView.element.querySelector('.selected'), command)
selectedEntry = archiveEditorView.element.querySelector('.selected').textContent
return true
}
it('selects the next/previous file', async () => {
await condition(() => archiveEditorView.element.querySelectorAll('.entry').length > 0)
expect(archiveEditorView.element).toBeDefined()
dispatch('core:move-up') && expect(selectedEntry).toBe('f1.txt')
dispatch('core:move-down') && expect(selectedEntry).toBe('f2.txt')
dispatch('core:move-down') && expect(selectedEntry).toBe('fa.txt')
dispatch('core:move-down') && expect(selectedEntry).toBe('fa.txt')
dispatch('core:move-up') && expect(selectedEntry).toBe('f2.txt')
dispatch('core:move-up') && expect(selectedEntry).toBe('f1.txt')
})
})
describe('when a file is clicked', () => {
it('copies the contents to a temp file and opens it in a new editor', async () => {
await condition(() => archiveEditorView.element.querySelectorAll('.entry').length > 0)
archiveEditorView.element.querySelectorAll('.file')[2].click()
await condition(() => atom.workspace.getActivePane().getItems().length > 1)
expect(atom.workspace.getActivePaneItem().getText()).toBe('hey there\n')
expect(atom.workspace.getActivePaneItem().getTitle()).toBe('fa.txt')
})
})
describe('when a directory is clicked', () => {
it('collapses/expands itself', async () => {
await condition(() => archiveEditorView.element.querySelectorAll('.entry').length > 0)
let directory = archiveEditorView.element.querySelectorAll('.list-nested-item.entry')[0]
expect(directory.classList.contains('collapsed')).toBeFalsy()
directory.querySelector('.list-item').click()
expect(directory.classList.contains('collapsed')).toBeTruthy()
directory.querySelector('.list-item').click()
expect(directory.classList.contains('collapsed')).toBeFalsy()
})
})
describe('when core:confirm is triggered', () => {
it('copies the contents to a temp file and opens it in a new editor', async () => {
await condition(() => archiveEditorView.element.querySelectorAll('.entry').length > 0)
atom.commands.dispatch(archiveEditorView.element.querySelector('.file'), 'core:confirm')
await condition(() => atom.workspace.getActivePane().getItems().length > 1)
expect(atom.workspace.getActivePaneItem().getText()).toBe('')
expect(atom.workspace.getActivePaneItem().getTitle()).toBe('f1.txt')
})
})
describe('when the file is modified', () => {
it('refreshes the view', async () => {
await condition(() => archiveEditorView.element.querySelectorAll('.entry').length > 0)
spyOn(archiveEditorView, 'refresh')
onDidChangeCallback()
expect(archiveEditorView.refresh).toHaveBeenCalled()
})
})
describe('when the file is renamed', () => {
it('refreshes the view and updates the title', async () => {
spyOn(File.prototype, 'getPath').andReturn('nested-renamed.tar')
await condition(() => archiveEditorView.element.querySelectorAll('.entry').length > 0)
spyOn(archiveEditorView, 'refresh').andCallThrough()
spyOn(archiveEditorView, 'getTitle')
onDidRenameCallback()
expect(archiveEditorView.refresh).toHaveBeenCalled()
expect(archiveEditorView.getTitle).toHaveBeenCalled()
})
})
describe('when the file is removed', () => {
it('destroys the view', async () => {
await condition(() => archiveEditorView.element.querySelectorAll('.entry').length > 0)
expect(atom.workspace.getActivePane().getItems().length).toBe(1)
onDidDeleteCallback()
expect(atom.workspace.getActivePaneItem()).toBeUndefined()
})
})
describe('when the file is invalid', () => {
beforeEach(async () => {
await atom.workspace.open('invalid.zip')
archiveEditorView = atom.workspace.getActivePaneItem()
jasmine.attachToDOM(atom.views.getView(atom.workspace))
})
it('shows the error', async () => {
await condition(() => archiveEditorView.refs.errorMessage.offsetHeight > 0)
expect(archiveEditorView.refs.errorMessage.textContent.length).toBeGreaterThan(0)
})
})
describe('FileIcons', () => {
async function openFile () {
await atom.workspace.open('file-icons.zip')
archiveEditorView = atom.workspace.getActivePaneItem()
jasmine.attachToDOM(atom.views.getView(atom.workspace))
}
describe('Icon service', () => {
const service = { iconClassForPath () {} }
beforeEach(() => openFile())
it('provides a default service', () => {
expect(getIconServices().fileIcons).toBeDefined()
expect(getIconServices().fileIcons).not.toBeNull()
})
it('allows the default to be overridden', () => {
getIconServices().setFileIcons(service)
expect(getIconServices().fileIcons).toBe(service)
})
it('allows service to be reset without hassle', () => {
getIconServices().setFileIcons(service)
getIconServices().resetFileIcons()
expect(getIconServices().fileIcons).not.toBe(service)
})
})
describe('Class handling', () => {
function findEntryContainingText (text) {
for (const entry of archiveEditorView.element.querySelectorAll('.list-item.entry')) {
if (entry.textContent.includes(text)) { return entry }
}
return null
}
function checkMultiClass () {
expect(findEntryContainingText('adobe.pdf').querySelector('.file.icon').className).toBe('file icon text pdf-icon document')
expect(findEntryContainingText('spacer.gif').querySelector('.file.icon').className).toBe('file icon binary gif-icon image')
expect(findEntryContainingText('font.ttf').querySelector('.file.icon').className).toBe('file icon binary ttf-icon font')
}
it('displays default file-icons', async () => {
await openFile()
await condition(() => archiveEditorView.element.querySelectorAll('.entry').length > 0)
expect(findEntryContainingText('adobe.pdf').querySelector('.file.icon.icon-file-pdf').length).not.toBe(0)
expect(findEntryContainingText('spacer.gif').querySelector('.file.icon.icon-file-media').length).not.toBe(0)
expect(findEntryContainingText('sunn.o').querySelector('.file.icon.icon-file-binary').length).not.toBe(0)
})
it('allows multiple classes to be passed', async () => {
getIconServices().setFileIcons({
iconClassForPath: (path) => {
switch (path.match(/\w*$/)[0]) {
case 'pdf': return 'text pdf-icon document'
case 'ttf': return 'binary ttf-icon font'
case 'gif': return 'binary gif-icon image'
}
}
})
await openFile()
await condition(() => archiveEditorView.element.querySelectorAll('.entry').length > 0)
checkMultiClass()
})
it('allows an array of classes to be passed', async () => {
getIconServices().setFileIcons({
iconClassForPath: (path) => {
switch (path.match(/\w*$/)[0]) {
case 'pdf': return ['text', 'pdf-icon', 'document']
case 'ttf': return ['binary', 'ttf-icon', 'font']
case 'gif': return ['binary', 'gif-icon', 'image']
}
}
})
await openFile()
await condition(() => archiveEditorView.element.querySelectorAll('.entry').length > 0)
checkMultiClass()
})
it('identifies context to icon-service providers', async () => {
getIconServices().setFileIcons({
iconClassForPath: (path, context) => `icon-${context}`
})
await openFile()
await condition(() => archiveEditorView.element.querySelectorAll('.entry').length > 0)
const icons = findEntryContainingText('adobe.pdf').querySelectorAll('.file.icon-archive-view')
expect(icons.length).not.toBe(0)
})
})
})
})

View File

@ -0,0 +1,103 @@
/** @babel */
export function beforeEach (fn) {
global.beforeEach(function () {
const result = fn()
if (result instanceof Promise) {
waitsForPromise(() => result)
}
})
}
export function afterEach (fn) {
global.afterEach(function () {
const result = fn()
if (result instanceof Promise) {
waitsForPromise(() => result)
}
})
}
['it', 'fit', 'ffit', 'fffit'].forEach(function (name) {
module.exports[name] = function (description, fn) {
if (fn === undefined) {
global[name](description)
return
}
global[name](description, function () {
const result = fn()
if (result instanceof Promise) {
waitsForPromise(() => result)
}
})
}
})
export async function conditionPromise (condition, description = 'anonymous condition') {
const startTime = Date.now()
while (true) {
await timeoutPromise(100)
if (await condition()) {
return
}
if (Date.now() - startTime > 5000) {
throw new Error('Timed out waiting on ' + description)
}
}
}
export function timeoutPromise (timeout) {
return new Promise(function (resolve) {
global.setTimeout(resolve, timeout)
})
}
function waitsForPromise (fn) {
const promise = fn()
global.waitsFor('spec promise to resolve', function (done) {
promise.then(done, function (error) {
jasmine.getEnv().currentSpec.fail(error)
done()
})
})
}
export function emitterEventPromise (emitter, event, timeout = 15000) {
return new Promise((resolve, reject) => {
const timeoutHandle = setTimeout(() => {
reject(new Error(`Timed out waiting for '${event}' event`))
}, timeout)
emitter.once(event, () => {
clearTimeout(timeoutHandle)
resolve()
})
})
}
export function promisify (original) {
return function (...args) {
return new Promise((resolve, reject) => {
args.push((err, ...results) => {
if (err) {
reject(err)
} else {
resolve(...results)
}
})
return original(...args)
})
}
}
export function promisifySome (obj, fnNames) {
const result = {}
for (const fnName of fnNames) {
result[fnName] = promisify(obj[fnName])
}
return result
}

Binary file not shown.

View File

@ -0,0 +1 @@
invalid

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,24 @@
@import "ui-variables";
.archive-editor {
background-color: @inset-panel-background-color;
overflow: auto;
contain: strict;
.archive-container {
height: 100%;
width: 100%;
.inset-panel {
border-width: 0;
.panel-heading {
border-radius: 0;
}
.archive-tree {
padding: 5px;
}
}
}
}

View File

@ -1,20 +0,0 @@
Copyright (c) 2014 GitHub Inc.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,8 +1,8 @@
# Atom Dark Syntax theme
# Pulsar Dark Syntax theme
A dark syntax theme for Atom.
A dark syntax theme for Pulsar.
This theme is installed by default with Atom and can be activated by going to
This theme is installed by default with Pulsar and can be activated by going to
the _Themes_ section in the Settings view (`cmd-,`) and selecting it from the
_Syntax Themes_ dropdown menu.

View File

@ -0,0 +1,16 @@
{
"name": "atom-dark-syntax",
"version": "0.29.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "atom-dark-syntax",
"version": "0.29.1",
"license": "MIT",
"engines": {
"atom": ">0.50.0"
}
}
}
}

View File

@ -1,20 +0,0 @@
Copyright (c) 2014 GitHub Inc.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,8 +1,8 @@
# Atom Dark UI theme
# Pulsar Dark UI theme
A dark UI theme for Atom.
A dark UI theme for Pulsar.
This theme is installed by default with Atom and can be activated by going to
This theme is installed by default with Pulsar and can be activated by going to
the _Themes_ section in the Settings view (`cmd-,`) and selecting it from the
_UI Themes_ drop-down menu.

16
packages/atom-dark-ui/package-lock.json generated Normal file
View File

@ -0,0 +1,16 @@
{
"name": "atom-dark-ui",
"version": "0.53.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "atom-dark-ui",
"version": "0.53.3",
"license": "MIT",
"engines": {
"atom": ">0.40.0"
}
}
}
}

View File

@ -1,20 +0,0 @@
Copyright (c) 2014 GitHub Inc.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,8 +1,8 @@
# Atom Light Syntax theme
# Pulsar Light Syntax theme
A light syntax theme for Atom.
A light syntax theme for Pulsar.
This theme is installed by default with Atom and can be activated by going to
This theme is installed by default with Pulsar and can be activated by going to
the _Themes_ section in the Settings view (`cmd-,`) and selecting it from the
_Syntax Themes_ dropdown menu.

View File

@ -0,0 +1,16 @@
{
"name": "atom-light-syntax",
"version": "0.29.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "atom-light-syntax",
"version": "0.29.1",
"license": "MIT",
"engines": {
"atom": ">0.40.0"
}
}
}
}

View File

@ -1,20 +0,0 @@
Copyright (c) 2014 GitHub Inc.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,8 +1,8 @@
# Atom Light UI theme
# Pulsar Light UI theme
A light UI theme for Atom.
A light UI theme for Pulsar.
This theme is installed by default with Atom and can be activated by going to
This theme is installed by default with Pulsar and can be activated by going to
the _Themes_ section in the Settings view (`cmd-,`) and selecting it from the
_UI Themes_ drop-down menu.

16
packages/atom-light-ui/package-lock.json generated Normal file
View File

@ -0,0 +1,16 @@
{
"name": "atom-light-ui",
"version": "0.46.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "atom-light-ui",
"version": "0.46.3",
"license": "MIT",
"engines": {
"atom": ">0.50.0"
}
}
}
}

1
packages/autocomplete-css/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

View File

@ -0,0 +1,12 @@
# CSS Autocomplete package
CSS property name and value autocompletions in Pulsar. Uses the
[autocomplete-plus](https://github.com/pulsar-edit/autocomplete-plus) package.
This is powered by the list of CSS property and values [here](https://github.com/adobe/brackets/blob/master/src/extensions/default/CSSCodeHints/CSSProperties.json).
![css-completions](https://cloud.githubusercontent.com/assets/671378/6357910/b9ecbe7c-bc1c-11e4-89b1-033e626c891f.gif)
You can update the prebuilt list of property names and values by running the `update.coffee` file at the root of the repository and then checking in the changed `properties.json` file.
`sorted-property-names.json` is updated manually - take a look at https://developer.microsoft.com/en-us/microsoft-edge/platform/usage/ and https://www.chromestatus.com/metrics/css/popularity for guidance.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,85 @@
path = require 'path'
fs = require 'fs'
request = require 'request'
mdnCSSURL = 'https://developer.mozilla.org/en-US/docs/Web/CSS'
mdnJSONAPI = 'https://developer.mozilla.org/en-US/search.json?topic=css&highlight=false'
PropertiesURL = 'https://raw.githubusercontent.com/adobe/brackets/master/src/extensions/default/CSSCodeHints/CSSProperties.json'
fetch = ->
propertiesPromise = new Promise (resolve) ->
request {json: true, url: PropertiesURL}, (error, response, properties) ->
if error?
console.error(error.message)
resolve(null)
if response.statusCode isnt 200
console.error("Request for CSSProperties.json failed: #{response.statusCode}")
resolve(null)
resolve(properties)
propertiesPromise.then (properties) ->
return unless properties?
MAX = 10
queue = Object.keys(properties)
running = []
docs = {}
new Promise (resolve) ->
checkEnd = ->
resolve(docs) if queue.length is 0 and running.length is 0
removeRunning = (propertyName) ->
index = running.indexOf(propertyName)
running.splice(index, 1) if index > -1
runNext = ->
checkEnd()
if queue.length isnt 0
propertyName = queue.pop()
running.push(propertyName)
run(propertyName)
run = (propertyName) ->
url = "#{mdnJSONAPI}&q=#{propertyName}"
request {json: true, url}, (error, response, searchResults) ->
if not error? and response.statusCode is 200
handleRequest(propertyName, searchResults)
else
console.error "Req failed #{url}; #{response.statusCode}, #{error}"
removeRunning(propertyName)
checkEnd()
runNext()
handleRequest = (propertyName, searchResults) ->
if searchResults.documents?
for doc in searchResults.documents
if doc.url is "#{mdnCSSURL}/#{propertyName}"
docs[propertyName] = filterExcerpt(propertyName, doc.excerpt)
break
return
runNext() for [0..MAX]
return
filterExcerpt = (propertyName, excerpt) ->
beginningPattern = /^the (css )?[a-z-]+ (css )?property (is )?(\w+)/i
excerpt = excerpt.replace beginningPattern, (match) ->
matches = beginningPattern.exec(match)
firstWord = matches[4]
firstWord[0].toUpperCase() + firstWord.slice(1)
periodIndex = excerpt.indexOf('.')
excerpt = excerpt.slice(0, periodIndex + 1) if periodIndex > -1
excerpt
# Save a file if run from the command line
if require.main is module
fetch().then (docs) ->
if docs?
fs.writeFileSync(path.join(__dirname, 'property-docs.json'), "#{JSON.stringify(docs, null, ' ')}\n")
else
console.error 'No docs'
module.exports = fetch

View File

@ -0,0 +1,114 @@
[
"a",
"b",
"blockquote",
"body",
"br",
"button",
"canvas",
"code",
"div",
"em",
"form",
"footer",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"head",
"header",
"hgroup",
"hr",
"html",
"i",
"iframe",
"img",
"input",
"label",
"li",
"nav",
"ol",
"p",
"pre",
"select",
"span",
"strong",
"sub",
"summary",
"table",
"td",
"textarea",
"th",
"title",
"tr",
"ul",
"abbr",
"address",
"area",
"article",
"aside",
"audio",
"base",
"bdi",
"bdo",
"big",
"caption",
"cite",
"col",
"colgroup",
"command",
"datalist",
"dd",
"del",
"details",
"dfn",
"dialog",
"dl",
"dt",
"embed",
"fieldset",
"figcaption",
"figure",
"ilayer",
"ins",
"kbd",
"keygen",
"legend",
"link",
"main",
"map",
"mark",
"marquee",
"menu",
"meta",
"meter",
"noscript",
"object",
"optgroup",
"option",
"output",
"param",
"progress",
"q",
"rp",
"rt",
"ruby",
"samp",
"script",
"section",
"small",
"source",
"style",
"sup",
"tbody",
"tfoot",
"thead",
"time",
"track",
"tt",
"var",
"video",
"wbr"
]

View File

@ -0,0 +1,6 @@
provider = require './provider'
module.exports =
activate: ->
getProvider: -> provider

View File

@ -0,0 +1,318 @@
COMPLETIONS = require('../completions.json')
firstInlinePropertyNameWithColonPattern = /{\s*(\S+)\s*:/ # .example { display: }
inlinePropertyNameWithColonPattern = /(?:;.+?)*;\s*(\S+)\s*:/ # .example { display: block; float: left; color: } (match the last one)
propertyNameWithColonPattern = /^\s*(\S+)\s*:/ # display:
propertyNamePrefixPattern = /[a-zA-Z]+[-a-zA-Z]*$/
pseudoSelectorPrefixPattern = /:(:)?([a-z]+[a-z-]*)?$/
tagSelectorPrefixPattern = /(^|\s|,)([a-z]+)?$/
importantPrefixPattern = /(![a-z]+)$/
cssDocsURL = "https://developer.mozilla.org/en-US/docs/Web/CSS"
module.exports =
selector: '.source.css, .source.sass, .source.css.postcss'
disableForSelector: '.source.css .comment, .source.css .string, .source.sass .comment, .source.sass .string, .source.css.postcss .comment, source.css.postcss .string'
properties: COMPLETIONS.properties
pseudoSelectors: COMPLETIONS.pseudoSelectors
tags: COMPLETIONS.tags
# Tell autocomplete to fuzzy filter the results of getSuggestions(). We are
# still filtering by the first character of the prefix in this provider for
# efficiency.
filterSuggestions: true
getSuggestions: (request) ->
completions = null
scopes = request.scopeDescriptor.getScopesArray()
isSass = hasScope(scopes, 'source.sass', true)
if @isCompletingValue(request)
completions = @getPropertyValueCompletions(request)
else if @isCompletingPseudoSelector(request)
completions = @getPseudoSelectorCompletions(request)
else
if isSass and @isCompletingNameOrTag(request)
completions = @getPropertyNameCompletions(request)
.concat(@getTagCompletions(request))
else if not isSass and @isCompletingName(request)
completions = @getPropertyNameCompletions(request)
if not isSass and @isCompletingTagSelector(request)
tagCompletions = @getTagCompletions(request)
if tagCompletions?.length
completions ?= []
completions = completions.concat(tagCompletions)
completions
onDidInsertSuggestion: ({editor, suggestion}) ->
setTimeout(@triggerAutocomplete.bind(this, editor), 1) if suggestion.type is 'property'
triggerAutocomplete: (editor) ->
atom.commands.dispatch(atom.views.getView(editor), 'autocomplete-plus:activate', {activatedManually: false})
isCompletingValue: ({scopeDescriptor, bufferPosition, prefix, editor}) ->
scopes = scopeDescriptor.getScopesArray()
beforePrefixBufferPosition = [bufferPosition.row, Math.max(0, bufferPosition.column - prefix.length - 1)]
beforePrefixScopes = editor.scopeDescriptorForBufferPosition(beforePrefixBufferPosition)
beforePrefixScopesArray = beforePrefixScopes.getScopesArray()
previousBufferPosition = [bufferPosition.row, Math.max(0, bufferPosition.column - 1)]
previousScopes = editor.scopeDescriptorForBufferPosition(previousBufferPosition)
previousScopesArray = previousScopes.getScopesArray()
(hasScope(scopes, 'meta.property-list.css') and prefix.trim() is ":") or
(hasScope(previousScopesArray, 'meta.property-value.css')) or
(hasScope(scopes, 'meta.property-list.scss') and prefix.trim() is ":") or
(hasScope(previousScopesArray, 'meta.property-value.scss')) or
(hasScope(scopes, 'meta.property-list.postcss') and prefix.trim() is ":") or
(hasScope(previousScopesArray, 'meta.property-value.postcss')) or
(hasScope(scopes, 'source.sass', true) and (hasScope(scopes, 'meta.property-value.sass') or
(not hasScope(beforePrefixScopesArray, 'entity.name.tag.css') and prefix.trim() is ":")
))
isCompletingName: ({scopeDescriptor, bufferPosition, prefix, editor}) ->
scopes = scopeDescriptor.getScopesArray()
isAtTerminator = prefix.endsWith(';')
isAtParentSymbol = prefix.endsWith('&')
isVariable = hasScope(scopes, 'variable.css') or
hasScope(scopes, 'variable.scss') or
hasScope(scopes, 'variable.var.postcss')
isInPropertyList = not isAtTerminator and
(hasScope(scopes, 'meta.property-list.css') or
hasScope(scopes, 'meta.property-list.scss') or
hasScope(scopes, 'meta.property-list.postcss'))
return false unless isInPropertyList
return false if isAtParentSymbol or isVariable
previousBufferPosition = [bufferPosition.row, Math.max(0, bufferPosition.column - prefix.length - 1)]
previousScopes = editor.scopeDescriptorForBufferPosition(previousBufferPosition)
previousScopesArray = previousScopes.getScopesArray()
return false if hasScope(previousScopesArray, 'entity.other.attribute-name.class.css') or
hasScope(previousScopesArray, 'entity.other.attribute-name.id.css') or
hasScope(previousScopesArray, 'entity.other.attribute-name.id') or
hasScope(previousScopesArray, 'entity.other.attribute-name.parent-selector.css') or
hasScope(previousScopesArray, 'entity.name.tag.reference.scss') or
hasScope(previousScopesArray, 'entity.name.tag.scss') or
hasScope(previousScopesArray, 'entity.name.tag.reference.postcss') or
hasScope(previousScopesArray, 'entity.name.tag.postcss')
isAtBeginScopePunctuation = hasScope(scopes, 'punctuation.section.property-list.begin.bracket.curly.css') or
hasScope(scopes, 'punctuation.section.property-list.begin.bracket.curly.scss') or
hasScope(scopes, 'punctuation.section.property-list.begin.postcss')
isAtEndScopePunctuation = hasScope(scopes, 'punctuation.section.property-list.end.bracket.curly.css') or
hasScope(scopes, 'punctuation.section.property-list.end.bracket.curly.scss') or
hasScope(scopes, 'punctuation.section.property-list.end.postcss')
if isAtBeginScopePunctuation
# * Disallow here: `canvas,|{}`
# * Allow here: `canvas,{| }`
prefix.endsWith('{')
else if isAtEndScopePunctuation
# * Disallow here: `canvas,{}|`
# * Allow here: `canvas,{ |}`
not prefix.endsWith('}')
else
true
isCompletingNameOrTag: ({scopeDescriptor, bufferPosition, editor}) ->
scopes = scopeDescriptor.getScopesArray()
prefix = @getPropertyNamePrefix(bufferPosition, editor)
return @isPropertyNamePrefix(prefix) and
hasScope(scopes, 'meta.selector.css') and
not hasScope(scopes, 'entity.other.attribute-name.id.css.sass') and
not hasScope(scopes, 'entity.other.attribute-name.class.sass')
isCompletingTagSelector: ({editor, scopeDescriptor, bufferPosition}) ->
scopes = scopeDescriptor.getScopesArray()
tagSelectorPrefix = @getTagSelectorPrefix(editor, bufferPosition)
return false unless tagSelectorPrefix?.length
previousBufferPosition = [bufferPosition.row, Math.max(0, bufferPosition.column - 1)]
previousScopes = editor.scopeDescriptorForBufferPosition(previousBufferPosition)
previousScopesArray = previousScopes.getScopesArray()
if hasScope(scopes, 'meta.selector.css') or hasScope(previousScopesArray, 'meta.selector.css')
true
else if hasScope(scopes, 'source.css.scss', true) or hasScope(scopes, 'source.css.less', true) or hasScope(scopes, 'source.css.postcss', true)
not hasScope(previousScopesArray, 'meta.property-value.scss') and
not hasScope(previousScopesArray, 'meta.property-value.css') and
not hasScope(previousScopesArray, 'meta.property-value.postcss') and
not hasScope(previousScopesArray, 'support.type.property-value.css')
else
false
isCompletingPseudoSelector: ({editor, scopeDescriptor, bufferPosition}) ->
scopes = scopeDescriptor.getScopesArray()
previousBufferPosition = [bufferPosition.row, Math.max(0, bufferPosition.column - 1)]
previousScopes = editor.scopeDescriptorForBufferPosition(previousBufferPosition)
previousScopesArray = previousScopes.getScopesArray()
if (hasScope(scopes, 'meta.selector.css') or hasScope(previousScopesArray, 'meta.selector.css')) and not hasScope(scopes, 'source.sass', true)
true
else if hasScope(scopes, 'source.css.scss', true) or hasScope(scopes, 'source.css.less', true) or hasScope(scopes, 'source.sass', true) or hasScope(scopes, 'source.css.postcss', true)
prefix = @getPseudoSelectorPrefix(editor, bufferPosition)
if prefix
previousBufferPosition = [bufferPosition.row, Math.max(0, bufferPosition.column - prefix.length - 1)]
previousScopes = editor.scopeDescriptorForBufferPosition(previousBufferPosition)
previousScopesArray = previousScopes.getScopesArray()
not hasScope(previousScopesArray, 'meta.property-name.scss') and
not hasScope(previousScopesArray, 'meta.property-value.scss') and
not hasScope(previousScopesArray, 'meta.property-value.postcss') and
not hasScope(previousScopesArray, 'support.type.property-name.css') and
not hasScope(previousScopesArray, 'support.type.property-value.css') and
not hasScope(previousScopesArray, 'support.type.property-name.postcss')
else
false
else
false
isPropertyValuePrefix: (prefix) ->
prefix = prefix.trim()
prefix.length > 0 and prefix isnt ':'
isPropertyNamePrefix: (prefix) ->
return false unless prefix?
prefix = prefix.trim()
prefix.length > 0 and prefix.match(/^[a-zA-Z-]+$/)
getImportantPrefix: (editor, bufferPosition) ->
line = editor.getTextInRange([[bufferPosition.row, 0], bufferPosition])
importantPrefixPattern.exec(line)?[1]
getPreviousPropertyName: (bufferPosition, editor) ->
{row, column} = bufferPosition
while row >= 0
line = editor.lineTextForBufferRow(row)
line = line.substr(0, column) if row is bufferPosition.row
propertyName = inlinePropertyNameWithColonPattern.exec(line)?[1]
propertyName ?= firstInlinePropertyNameWithColonPattern.exec(line)?[1]
propertyName ?= propertyNameWithColonPattern.exec(line)?[1]
return propertyName if propertyName
row--
return
getPropertyValueCompletions: ({bufferPosition, editor, prefix, scopeDescriptor}) ->
property = @getPreviousPropertyName(bufferPosition, editor)
values = @properties[property]?.values
return null unless values?
scopes = scopeDescriptor.getScopesArray()
addSemicolon = not lineEndsWithSemicolon(bufferPosition, editor) and not hasScope(scopes, 'source.sass', true)
completions = []
if @isPropertyValuePrefix(prefix)
for value in values when firstCharsEqual(value, prefix)
completions.push(@buildPropertyValueCompletion(value, property, addSemicolon))
else if not hasScope(scopes, 'keyword.other.unit.percentage.css') and # CSS
not hasScope(scopes, 'keyword.other.unit.scss') and # SCSS (TODO: remove in Atom 1.19.0)
not hasScope(scopes, 'keyword.other.unit.css') # Less, Sass (TODO: remove in Atom 1.19.0)
# Don't complete here: `width: 100%|`
for value in values
completions.push(@buildPropertyValueCompletion(value, property, addSemicolon))
if importantPrefix = @getImportantPrefix(editor, bufferPosition)
# attention: règle dangereux
completions.push
type: 'keyword'
text: '!important'
displayText: '!important'
replacementPrefix: importantPrefix
description: "Forces this property to override any other declaration of the same property. Use with caution."
descriptionMoreURL: "#{cssDocsURL}/Specificity#The_!important_exception"
completions
buildPropertyValueCompletion: (value, propertyName, addSemicolon) ->
text = value
text += ';' if addSemicolon
{
type: 'value'
text: text
displayText: value
description: "#{value} value for the #{propertyName} property"
descriptionMoreURL: "#{cssDocsURL}/#{propertyName}#Values"
}
getPropertyNamePrefix: (bufferPosition, editor) ->
line = editor.getTextInRange([[bufferPosition.row, 0], bufferPosition])
propertyNamePrefixPattern.exec(line)?[0]
getPropertyNameCompletions: ({bufferPosition, editor, scopeDescriptor, activatedManually}) ->
# Don't autocomplete property names in SASS on root level
scopes = scopeDescriptor.getScopesArray()
line = editor.getTextInRange([[bufferPosition.row, 0], bufferPosition])
return [] if hasScope(scopes, 'source.sass', true) and not line.match(/^(\s|\t)/)
prefix = @getPropertyNamePrefix(bufferPosition, editor)
return [] unless activatedManually or prefix
completions = []
for property, options of @properties when not prefix or firstCharsEqual(property, prefix)
completions.push(@buildPropertyNameCompletion(property, prefix, options))
completions
buildPropertyNameCompletion: (propertyName, prefix, {description}) ->
type: 'property'
text: "#{propertyName}: "
displayText: propertyName
replacementPrefix: prefix
description: description
descriptionMoreURL: "#{cssDocsURL}/#{propertyName}"
getPseudoSelectorPrefix: (editor, bufferPosition) ->
line = editor.getTextInRange([[bufferPosition.row, 0], bufferPosition])
line.match(pseudoSelectorPrefixPattern)?[0]
getPseudoSelectorCompletions: ({bufferPosition, editor}) ->
prefix = @getPseudoSelectorPrefix(editor, bufferPosition)
return null unless prefix
completions = []
for pseudoSelector, options of @pseudoSelectors when firstCharsEqual(pseudoSelector, prefix)
completions.push(@buildPseudoSelectorCompletion(pseudoSelector, prefix, options))
completions
buildPseudoSelectorCompletion: (pseudoSelector, prefix, {argument, description}) ->
completion =
type: 'pseudo-selector'
replacementPrefix: prefix
description: description
descriptionMoreURL: "#{cssDocsURL}/#{pseudoSelector}"
if argument?
completion.snippet = "#{pseudoSelector}(${1:#{argument}})"
else
completion.text = pseudoSelector
completion
getTagSelectorPrefix: (editor, bufferPosition) ->
line = editor.getTextInRange([[bufferPosition.row, 0], bufferPosition])
tagSelectorPrefixPattern.exec(line)?[2]
getTagCompletions: ({bufferPosition, editor, prefix}) ->
completions = []
if prefix
for tag in @tags when firstCharsEqual(tag, prefix)
completions.push(@buildTagCompletion(tag))
completions
buildTagCompletion: (tag) ->
type: 'tag'
text: tag
description: "Selector for <#{tag}> elements"
lineEndsWithSemicolon = (bufferPosition, editor) ->
{row} = bufferPosition
line = editor.lineTextForBufferRow(row)
/;\s*$/.test(line)
hasScope = (scopesArray, scope, checkEmbedded = false) ->
scopesArray.indexOf(scope) isnt -1 or
(checkEmbedded and scopesArray.indexOf("#{scope}.embedded.html") isnt -1)
firstCharsEqual = (str1, str2) ->
str1[0].toLowerCase() is str2[0].toLowerCase()

View File

@ -0,0 +1,873 @@
{
"name": "autocomplete-css",
"version": "0.17.5",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "autocomplete-css",
"version": "0.17.5",
"license": "MIT",
"devDependencies": {
"request": "^2.53.0"
},
"engines": {
"atom": ">=0.174.0 <2.0.0"
}
},
"node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/asn1": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
"dev": true,
"dependencies": {
"safer-buffer": "~2.1.0"
}
},
"node_modules/assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
"dev": true,
"engines": {
"node": ">=0.8"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"dev": true
},
"node_modules/aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
"integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==",
"dev": true,
"engines": {
"node": "*"
}
},
"node_modules/aws4": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
"integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
"dev": true
},
"node_modules/bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
"dev": true,
"dependencies": {
"tweetnacl": "^0.14.3"
}
},
"node_modules/caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
"dev": true
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dev": true,
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
"dev": true
},
"node_modules/dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
"integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
"dev": true,
"dependencies": {
"assert-plus": "^1.0.0"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"dev": true,
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
"integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
"dev": true,
"dependencies": {
"jsbn": "~0.1.0",
"safer-buffer": "^2.1.0"
}
},
"node_modules/extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
"dev": true
},
"node_modules/extsprintf": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
"integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
"dev": true,
"engines": [
"node >=0.6.0"
]
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
},
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true
},
"node_modules/forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
"integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==",
"dev": true,
"engines": {
"node": "*"
}
},
"node_modules/form-data": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
"dev": true,
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 0.12"
}
},
"node_modules/getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
"integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
"dev": true,
"dependencies": {
"assert-plus": "^1.0.0"
}
},
"node_modules/har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
"integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==",
"dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/har-validator": {
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
"deprecated": "this library is no longer supported",
"dev": true,
"dependencies": {
"ajv": "^6.12.3",
"har-schema": "^2.0.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
"integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==",
"dev": true,
"dependencies": {
"assert-plus": "^1.0.0",
"jsprim": "^1.2.2",
"sshpk": "^1.7.0"
},
"engines": {
"node": ">=0.8",
"npm": ">=1.3.7"
}
},
"node_modules/is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
"dev": true
},
"node_modules/isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
"dev": true
},
"node_modules/jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
"dev": true
},
"node_modules/json-schema": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
"dev": true
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
},
"node_modules/json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
"dev": true
},
"node_modules/jsprim": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
"integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
"dev": true,
"dependencies": {
"assert-plus": "1.0.0",
"extsprintf": "1.3.0",
"json-schema": "0.4.0",
"verror": "1.10.0"
},
"engines": {
"node": ">=0.6.0"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true,
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dev": true,
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/oauth-sign": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
"dev": true,
"engines": {
"node": "*"
}
},
"node_modules/performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
"dev": true
},
"node_modules/psl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
"dev": true
},
"node_modules/punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/qs": {
"version": "6.5.3",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
"integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
"dev": true,
"engines": {
"node": ">=0.6"
}
},
"node_modules/request": {
"version": "2.88.2",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
"deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142",
"dev": true,
"dependencies": {
"aws-sign2": "~0.7.0",
"aws4": "^1.8.0",
"caseless": "~0.12.0",
"combined-stream": "~1.0.6",
"extend": "~3.0.2",
"forever-agent": "~0.6.1",
"form-data": "~2.3.2",
"har-validator": "~5.1.3",
"http-signature": "~1.2.0",
"is-typedarray": "~1.0.0",
"isstream": "~0.1.2",
"json-stringify-safe": "~5.0.1",
"mime-types": "~2.1.19",
"oauth-sign": "~0.9.0",
"performance-now": "^2.1.0",
"qs": "~6.5.2",
"safe-buffer": "^5.1.2",
"tough-cookie": "~2.5.0",
"tunnel-agent": "^0.6.0",
"uuid": "^3.3.2"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true
},
"node_modules/sshpk": {
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz",
"integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==",
"dev": true,
"dependencies": {
"asn1": "~0.2.3",
"assert-plus": "^1.0.0",
"bcrypt-pbkdf": "^1.0.0",
"dashdash": "^1.12.0",
"ecc-jsbn": "~0.1.1",
"getpass": "^0.1.1",
"jsbn": "~0.1.0",
"safer-buffer": "^2.0.2",
"tweetnacl": "~0.14.0"
},
"bin": {
"sshpk-conv": "bin/sshpk-conv",
"sshpk-sign": "bin/sshpk-sign",
"sshpk-verify": "bin/sshpk-verify"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/tough-cookie": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
"dev": true,
"dependencies": {
"psl": "^1.1.28",
"punycode": "^2.1.1"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
"dev": true,
"dependencies": {
"safe-buffer": "^5.0.1"
},
"engines": {
"node": "*"
}
},
"node_modules/tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
"dev": true
},
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true,
"dependencies": {
"punycode": "^2.1.0"
}
},
"node_modules/uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"dev": true,
"bin": {
"uuid": "bin/uuid"
}
},
"node_modules/verror": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
"integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
"dev": true,
"engines": [
"node >=0.6.0"
],
"dependencies": {
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "^1.2.0"
}
}
},
"dependencies": {
"ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"asn1": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
"dev": true,
"requires": {
"safer-buffer": "~2.1.0"
}
},
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
"dev": true
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"dev": true
},
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
"integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==",
"dev": true
},
"aws4": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
"integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
"dev": true
},
"bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
"dev": true,
"requires": {
"tweetnacl": "^0.14.3"
}
},
"caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
"dev": true
},
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dev": true,
"requires": {
"delayed-stream": "~1.0.0"
}
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
"dev": true
},
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
"integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
"dev": true,
"requires": {
"assert-plus": "^1.0.0"
}
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"dev": true
},
"ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
"integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
"dev": true,
"requires": {
"jsbn": "~0.1.0",
"safer-buffer": "^2.1.0"
}
},
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
"dev": true
},
"extsprintf": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
"integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
"dev": true
},
"fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
},
"fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true
},
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
"integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==",
"dev": true
},
"form-data": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
"dev": true,
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
"mime-types": "^2.1.12"
}
},
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
"integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
"dev": true,
"requires": {
"assert-plus": "^1.0.0"
}
},
"har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
"integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==",
"dev": true
},
"har-validator": {
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
"dev": true,
"requires": {
"ajv": "^6.12.3",
"har-schema": "^2.0.0"
}
},
"http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
"integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==",
"dev": true,
"requires": {
"assert-plus": "^1.0.0",
"jsprim": "^1.2.2",
"sshpk": "^1.7.0"
}
},
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
"dev": true
},
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
"dev": true
},
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
"dev": true
},
"json-schema": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
"dev": true
},
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
},
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
"dev": true
},
"jsprim": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
"integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
"dev": true,
"requires": {
"assert-plus": "1.0.0",
"extsprintf": "1.3.0",
"json-schema": "0.4.0",
"verror": "1.10.0"
}
},
"mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true
},
"mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dev": true,
"requires": {
"mime-db": "1.52.0"
}
},
"oauth-sign": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
"dev": true
},
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
"dev": true
},
"psl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
"dev": true
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true
},
"qs": {
"version": "6.5.3",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
"integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
"dev": true
},
"request": {
"version": "2.88.2",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
"dev": true,
"requires": {
"aws-sign2": "~0.7.0",
"aws4": "^1.8.0",
"caseless": "~0.12.0",
"combined-stream": "~1.0.6",
"extend": "~3.0.2",
"forever-agent": "~0.6.1",
"form-data": "~2.3.2",
"har-validator": "~5.1.3",
"http-signature": "~1.2.0",
"is-typedarray": "~1.0.0",
"isstream": "~0.1.2",
"json-stringify-safe": "~5.0.1",
"mime-types": "~2.1.19",
"oauth-sign": "~0.9.0",
"performance-now": "^2.1.0",
"qs": "~6.5.2",
"safe-buffer": "^5.1.2",
"tough-cookie": "~2.5.0",
"tunnel-agent": "^0.6.0",
"uuid": "^3.3.2"
}
},
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true
},
"sshpk": {
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz",
"integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==",
"dev": true,
"requires": {
"asn1": "~0.2.3",
"assert-plus": "^1.0.0",
"bcrypt-pbkdf": "^1.0.0",
"dashdash": "^1.12.0",
"ecc-jsbn": "~0.1.1",
"getpass": "^0.1.1",
"jsbn": "~0.1.0",
"safer-buffer": "^2.0.2",
"tweetnacl": "~0.14.0"
}
},
"tough-cookie": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
"dev": true,
"requires": {
"psl": "^1.1.28",
"punycode": "^2.1.1"
}
},
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
"dev": true,
"requires": {
"safe-buffer": "^5.0.1"
}
},
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
"dev": true
},
"uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true,
"requires": {
"punycode": "^2.1.0"
}
},
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"dev": true
},
"verror": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
"integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
"dev": true,
"requires": {
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "^1.2.0"
}
}
}
}

View File

@ -0,0 +1,21 @@
{
"name": "autocomplete-css",
"version": "0.17.5",
"description": "CSS property name and value autocompletions",
"main": "./lib/main",
"license": "MIT",
"repository": "https://github.com/pulsar-edit/pulsar",
"engines": {
"atom": ">=0.174.0 <2.0.0"
},
"providedServices": {
"autocomplete.provider": {
"versions": {
"2.0.0": "getProvider"
}
}
},
"devDependencies": {
"request": "^2.53.0"
}
}

View File

@ -0,0 +1,138 @@
{
"::after": {
"description": "Matches a virtual last child of the selected element."
},
"::before": {
"description": "Creates a pseudo-element that is the first child of the element matched."
},
"::first-letter": {
"description": "Matches the first letter of the first line of a block, if it is not preceded by any other content."
},
"::first-line": {
"description": "Applies styles only to the first line of an element."
},
"::selection": {
"description": "Applies rules to the portion of a document that has been highlighted."
},
":active": {
"description": "Matches when an element is being activated by the user."
},
":checked": {
"description": "Matches any radio input, checkbox input or option element that is checked or toggled to an on state."
},
":default": {
"description": "Matches any user interface element that is the default among a group of similar elements"
},
":dir": {
"argument": "direction",
"description": "Matches elements based on the directionality of the text contained in it."
},
":disabled": {
"description": "Matches any disabled element."
},
":empty": {
"description": "Matches any element that has no children at all."
},
":enabled": {
"description": "Matches any enabled element."
},
":first": {
"description": "Describes the styling of the first page when printing a document."
},
":first-child": {
"description": "Matches any element that is the first child element of its parent."
},
":first-of-type": {
"description": "Matches the first sibling of its type in the list of children of its parent element."
},
":focus": {
"description": "Matches an element that has focus."
},
":fullscreen": {
"description": "Applies to any element that's currently being displayed in full-screen mode."
},
":hover": {
"description": "Matches when the user designates an element with a pointing device, but does not necessarily activate it."
},
":indeterminate": {
"description": "Matches any checkbox input whose indeterminate DOM property is set to true by JavaScript."
},
":invalid": {
"description": "Matches any <input> or <form> element whose content fails to validate according to the input's type setting."
},
":lang": {
"argument": "language",
"description": "Matches elements based on the language the element is determined to be in."
},
":last-child": {
"description": "Matches any element that is the last child element of its parent."
},
":last-of-type": {
"description": "Matches the last sibling with the given element name in the list of children of its parent element."
},
":left": {
"description": "Matches any left page when printing a page."
},
":link": {
"description": "Matches links inside elements."
},
":not": {
"argument": "selector",
"description": "Matches an element that is not represented by the argument."
},
":nth-child": {
"argument": "an+b",
"description": "Matches an element that has an+b-1 siblings before it in the document tree."
},
":nth-last-child": {
"argument": "an+b",
"description": "Matches an element that has an+b-1 siblings after it in the document tree."
},
":nth-last-of-type": {
"argument": "an+b",
"description": "Matches an element that has an+b-1 siblings with the same element name after it in the document tree."
},
":nth-of-type": {
"argument": "an+b",
"description": "Matches an element that has an+b-1 siblings with the same element name before it in the document tree"
},
":only-child": {
"description": "Matches any element which is the only child of its parent."
},
":only-of-type": {
"description": "Matches any element that has no siblings of the given type."
},
":optional": {
"description": "Matches any <input> element that does not have the required attribute set on it."
},
":out-of-range": {
"description": "Matches when an element has its value attribute outside the specified range limitations for this element."
},
":read-only": {
"description": "Matches when an element is not writable by the user."
},
":read-write": {
"description": "Matches when an element is editable by user like text input element."
},
":required": {
"description": "Matches any <input> element that has the required attribute set on it."
},
":right": {
"description": "Matches any right page when printing a page. It allows to describe the styling of right-side page."
},
":root": {
"description": "Matches the root element of a tree representing the document."
},
":scope": {
"description": "Matches the elements that are a reference point for selectors to match against."
},
":target": {
"description": "Matches the unique element, if any, with an id matching the fragment identifier of the URI of the document."
},
":valid": {
"description": "Matches any <input> or <form> element whose content validates correctly according to the input's type setting"
},
":visited": {
"description": "Matches links that have been visited."
}
}

View File

@ -0,0 +1,462 @@
[
"width",
"height",
"margin",
"margin-left",
"margin-right",
"margin-top",
"margin-bottom",
"padding",
"padding-left",
"padding-right",
"padding-top",
"padding-bottom",
"font",
"font-size",
"font-style",
"font-weight",
"font-family",
"border",
"border-radius",
"border-top",
"border-bottom",
"border-left",
"border-right",
"border-color",
"border-width",
"position",
"text-align",
"background",
"background-color",
"background-position",
"background-repeat",
"background-image",
"background-size",
"background-clip",
"right",
"left",
"top",
"bottom",
"overflow",
"overflow-x",
"overflow-y",
"opacity",
"cursor",
"display",
"color",
"visibility",
"float",
"text-decoration",
"line-height",
"z-index",
"vertical-align",
"box-sizing",
"clear",
"white-space",
"max-width",
"outline",
"content",
"min-width",
"min-height",
"list-style",
"box-shadow",
"webkit-border-radius",
"webkit-user-select",
"webkit-box-shadow",
"text-shadow",
"text-indent",
"max-height",
"text-overflow",
"border-style",
"border-spacing",
"border-collapse",
"border-left-color",
"border-left-style",
"border-left-width",
"border-right-color",
"border-right-style",
"border-right-width",
"border-top-color",
"border-top-style",
"border-top-width",
"border-bottom-color",
"border-bottom-style",
"border-bottom-width",
"border-top-left-radius",
"border-top-right-radius",
"border-bottom-left-radius",
"border-bottom-right-radius",
"user-select",
"text-transform",
"webkit-transition",
"zoom",
"list-style-type",
"word-wrap",
"webkit-transform",
"transition",
"webkit-appearance",
"letter-spacing",
"transform",
"pointer-events",
"webkit-font-smoothing",
"webkit-animation",
"direction",
"clip",
"table-layout",
"src",
"webkit-tap-highlight-color",
"resize",
"webkit-transform-origin",
"word-break",
"webkit-background-clip",
"webkit-background-size",
"filter",
"transform-origin",
"font-variant",
"webkit-filter",
"quotes",
"unicode-bidi",
"word-spacing",
"text-rendering",
"fill",
"webkit-backface-visibility",
"webkit-transition-duration",
"outline-color",
"list-style-position",
"webkit-box-orient",
"webkit-animation-timing-function",
"outline-offset",
"webkit-transition-property",
"webkit-animation-duration",
"webkit-animation-name",
"orphans",
"outline-style",
"outline-width",
"flex",
"flex-grow",
"flex-direction",
"flex-flow",
"flex-wrap",
"flex-shrink",
"flex-basis",
"list-style-image",
"unicode-range",
"align-items",
"transition-delay",
"webkit-animation-fill-mode",
"transition-duration",
"justify-content",
"transition-property",
"webkit-animation-iteration-count",
"webkit-line-clamp",
"webkit-transition-timing-function",
"order",
"transition-timing-function",
"background-attachment",
"background-position-y",
"background-origin",
"background-position-x",
"backface-visibility",
"page-break-inside",
"page-break-after",
"speak",
"stroke",
"webkit-box-flex",
"webkit-transition-delay",
"widows",
"webkit-perspective",
"stroke-width",
"webkit-animation-direction",
"fill-opacity",
"webkit-box-pack",
"webkit-user-drag",
"overflow-wrap",
"webkit-box-align",
"webkit-animation-play-state",
"counter-increment",
"counter-reset",
"webkit-animation-delay",
"image-rendering",
"perspective-origin",
"webkit-perspective-origin",
"perspective",
"webkit-margin-start",
"webkit-transform-style",
"empty-cells",
"stroke-opacity",
"caption-side",
"webkit-mask-image",
"webkit-margin-end",
"transform-style",
"border-image",
"touch-action",
"webkit-box-ordinal-group",
"webkit-column-count",
"font-stretch",
"webkit-print-color-adjust",
"webkit-mask-size",
"webkit-column-gap",
"webkit-margin-top-collapse",
"webkit-border-image",
"will-change",
"webkit-padding-start",
"webkit-mask-repeat",
"webkit-text-fill-color",
"webkit-margin-before",
"webkit-mask-box-image",
"webkit-border-horizontal-spacing",
"animation",
"webkit-column-break-inside",
"page-break-before",
"webkit-margin-after",
"webkit-user-modify",
"webkit-font-feature-settings",
"webkit-line-break",
"webkit-mask-position",
"align-self",
"webkit-box-direction",
"size",
"align-content",
"webkit-text-stroke",
"webkit-padding-end",
"webkit-text-stroke-width",
"border-image-slice",
"border-image-width",
"webkit-column-width",
"border-image-outset",
"webkit-columns",
"border-image-repeat",
"tab-size",
"stop-color",
"object-fit",
"fill-rule",
"writing-mode",
"clip-rule",
"shape-rendering",
"stroke-dasharray",
"webkit-text-stroke-color",
"font-kerning",
"webkit-background-origin",
"stroke-linecap",
"webkit-box-reflect",
"animation-name",
"text-anchor",
"animation-duration",
"stop-opacity",
"webkit-border-vertical-spacing",
"webkit-perspective-origin-y",
"border-image-source",
"stroke-linejoin",
"webkit-perspective-origin-x",
"animation-fill-mode",
"webkit-padding-before",
"webkit-column-rule-color",
"webkit-column-span",
"webkit-column-rule",
"animation-timing-function",
"mask",
"webkit-mask",
"stroke-miterlimit",
"webkit-text-security",
"webkit-box-lines",
"webkit-padding-after",
"webkit-border-end",
"webkit-text-emphasis-color",
"webkit-border-start-color",
"webkit-border-start-width",
"animation-iteration-count",
"stroke-dashoffset",
"animation-delay",
"webkit-rtl-ordering",
"page",
"webkit-margin-collapse",
"webkit-border-start",
"webkit-transform-origin-y",
"webkit-writing-mode",
"alignment-baseline",
"dominant-baseline",
"webkit-column-rule-style",
"webkit-column-rule-width",
"baseline-shift",
"webkit-highlight",
"font-variant-ligatures",
"webkit-transform-origin-x",
"webkit-app-region",
"webkit-clip-path",
"background-blend-mode",
"clip-path",
"object-position",
"webkit-box-decoration-break",
"x",
"webkit-border-end-color",
"enable-background",
"webkit-hyphenate-character",
"mask-type",
"webkit-column-break-before",
"webkit-column-break-after",
"mix-blend-mode",
"webkit-text-decorations-in-effect",
"webkit-box-flex-group",
"webkit-line-box-contain",
"webkit-mask-composite",
"vector-effect",
"marker-start",
"marker-end",
"webkit-border-end-width",
"webkit-mask-clip",
"flood-color",
"flood-opacity",
"webkit-background-composite",
"marker-mid",
"webkit-mask-origin",
"webkit-text-emphasis-style",
"color-rendering",
"color-interpolation-filters",
"webkit-margin-before-collapse",
"color-interpolation",
"webkit-border-after-color",
"webkit-border-before-color",
"webkit-text-orientation",
"webkit-border-after-width",
"background-repeat-y",
"webkit-border-before-width",
"glyph-orientation-vertical",
"lighting-color",
"glyph-orientation-horizontal",
"webkit-mask-box-image-source",
"webkit-mask-box-image-repeat",
"shape-outside",
"webkit-mask-box-image-slice",
"paint-order",
"webkit-text-combine",
"webkit-text-emphasis-position",
"shape-margin",
"webkit-mask-box-image-width",
"webkit-mask-box-image-outset",
"webkit-margin-after-collapse",
"isolation",
"buffered-rendering",
"shape-image-threshold",
"background-repeat-x",
"animation-direction",
"animation-play-state",
"webkit-locale",
"webkit-border-end-style",
"webkit-margin-bottom-collapse",
"all",
"marker",
"webkit-border-after",
"y",
"rx",
"ry",
"cx",
"cy",
"r",
"webkit-border-start-style",
"webkit-mask-position-x",
"webkit-border-fit",
"webkit-transform-origin-z",
"text-justify",
"column-fill",
"text-align-last",
"webkit-min-logical-height",
"text-decoration-color",
"webkit-min-logical-width",
"webkit-logical-height",
"text-decoration-style",
"text-decoration-line",
"webkit-mask-position-y",
"min-zoom",
"max-zoom",
"webkit-max-logical-height",
"webkit-border-before",
"webkit-text-emphasis",
"webkit-max-logical-width",
"webkit-logical-width",
"user-zoom",
"webkit-border-after-style",
"font-size-adjust",
"text-underline-style",
"orientation",
"webkit-font-size-delta",
"text-underline-position",
"webkit-border-before-style",
"text-underline-color",
"touch-action-delay",
"webkit-ruby-position",
"webkit-mask-repeat-x",
"webkit-mask-repeat-y",
"scroll-behavior",
"justify-self",
"text-overline-width",
"grid-column",
"grid-row",
"grid-template",
"text-line-through-width",
"caret-color",
"justify-items",
"grid-template-columns",
"grid-auto-columns",
"grid-auto-flow",
"mask-source-type",
"grid-auto-rows",
"grid-column-start",
"grid-template-rows",
"scroll-blocks-on",
"grid-row-end",
"grid-column-end",
"grid-row-start",
"text-line-through-style",
"text-line-through-mode",
"webkit-wrap-flow",
"webkit-wrap-through",
"text-line-through-color",
"text-overline-color",
"webkit-aspect-ratio",
"text-underline-width",
"text-underline-mode",
"box-decoration-break",
"break-after",
"break-before",
"break-inside",
"columns",
"column-count",
"column-gap",
"column-rule",
"column-rule-color",
"column-rule-style",
"column-rule-width",
"column-span",
"column-width",
"flow-into",
"flow-from",
"font-feature-settings",
"font-language-override",
"font-synthesis",
"font-variant-alternates",
"font-variant-caps",
"font-variant-east-asian",
"font-variant-numeric",
"font-variant-position",
"hyphens",
"image-orientation",
"image-resolution",
"region-break-after",
"region-break-before",
"region-break-inside",
"region-fragment",
"shape-inside",
"text-decoration-skip",
"text-emphasis",
"text-emphasis-color",
"text-emphasis-position",
"text-emphasis-style",
"font-display",
"grid",
"grid-area",
"grid-column-gap",
"grid-gap",
"grid-row-gap",
"grid-template-areas",
"hanging-punctuation"
]

View File

@ -0,0 +1,907 @@
packagesToTest =
CSS:
name: 'language-css'
file: 'test.css'
SCSS:
name: 'language-sass'
file: 'test.scss'
Less:
name: 'language-less'
file: 'test.less'
PostCSS:
name: 'language-postcss'
file: 'test.postcss'
Object.keys(packagesToTest).forEach (packageLabel) ->
unless atom.packages.getAvailablePackageNames().includes(packagesToTest[packageLabel].name)
console.warn "Skipping tests for #{packageLabel} because it is not installed"
delete packagesToTest[packageLabel]
describe "CSS property name and value autocompletions", ->
[editor, provider] = []
getCompletions = (options={}) ->
cursor = editor.getLastCursor()
start = cursor.getBeginningOfCurrentWordBufferPosition()
end = cursor.getBufferPosition()
prefix = editor.getTextInRange([start, end])
request =
editor: editor
bufferPosition: end
scopeDescriptor: cursor.getScopeDescriptor()
prefix: prefix
activatedManually: options.activatedManually ? true
provider.getSuggestions(request)
beforeEach ->
waitsForPromise -> atom.packages.activatePackage('autocomplete-css')
waitsForPromise -> atom.packages.activatePackage('language-css') # Used in all CSS languages
runs ->
provider = atom.packages.getActivePackage('autocomplete-css').mainModule.getProvider()
waitsFor -> Object.keys(provider.properties).length > 0
Object.keys(packagesToTest).forEach (packageLabel) ->
describe "#{packageLabel} files", ->
beforeEach ->
waitsForPromise -> atom.packages.activatePackage(packagesToTest[packageLabel].name)
waitsForPromise -> atom.workspace.open(packagesToTest[packageLabel].file)
runs -> editor = atom.workspace.getActiveTextEditor()
it "returns tag completions when not in a property list", ->
editor.setText('')
expect(getCompletions()).toBe null
editor.setText('d')
editor.setCursorBufferPosition([0, 0])
expect(getCompletions()).toBe null
editor.setCursorBufferPosition([0, 1])
completions = getCompletions()
expect(completions).toHaveLength 9
for completion in completions
expect(completion.text.length).toBeGreaterThan 0
expect(completion.type).toBe 'tag'
it "autocompletes property names without a prefix when activated manually", ->
editor.setText """
body {
}
"""
editor.setCursorBufferPosition([1, 0])
completions = getCompletions(activatedManually: true)
expect(completions.length).toBe 237
for completion in completions
expect(completion.text.length).toBeGreaterThan 0
expect(completion.type).toBe 'property'
expect(completion.descriptionMoreURL.length).toBeGreaterThan 0
it "does not autocomplete property names without a prefix when not activated manually", ->
editor.setText """
body {
}
"""
editor.setCursorBufferPosition([1, 0])
completions = getCompletions(activatedManually: false)
expect(completions).toEqual []
it "autocompletes property names with a prefix", ->
editor.setText """
body {
d
}
"""
editor.setCursorBufferPosition([1, 3])
completions = getCompletions()
expect(completions[0].text).toBe 'display: '
expect(completions[0].displayText).toBe 'display'
expect(completions[0].type).toBe 'property'
expect(completions[0].replacementPrefix).toBe 'd'
expect(completions[0].description.length).toBeGreaterThan 0
expect(completions[0].descriptionMoreURL.length).toBeGreaterThan 0
expect(completions[1].text).toBe 'direction: '
expect(completions[1].displayText).toBe 'direction'
expect(completions[1].type).toBe 'property'
expect(completions[1].replacementPrefix).toBe 'd'
editor.setText """
body {
D
}
"""
editor.setCursorBufferPosition([1, 3])
completions = getCompletions()
expect(completions.length).toBe 2
expect(completions[0].text).toBe 'display: '
expect(completions[1].text).toBe 'direction: '
expect(completions[1].replacementPrefix).toBe 'D'
editor.setText """
body {
d:
}
"""
editor.setCursorBufferPosition([1, 3])
completions = getCompletions()
expect(completions[0].text).toBe 'display: '
expect(completions[1].text).toBe 'direction: '
editor.setText """
body {
bord
}
"""
editor.setCursorBufferPosition([1, 6])
completions = getCompletions()
expect(completions[0].text).toBe 'border: '
expect(completions[0].displayText).toBe 'border'
expect(completions[0].replacementPrefix).toBe 'bord'
it "does not autocomplete when at a terminator", ->
editor.setText """
body {
.somemixin();
}
"""
editor.setCursorBufferPosition([1, 15])
completions = getCompletions()
expect(completions).toBe null
it "does not autocomplete property names when preceding a {", ->
editor.setText """
body,{
}
"""
editor.setCursorBufferPosition([0, 5])
completions = getCompletions()
expect(completions).toBe null
editor.setText """
body,{}
"""
editor.setCursorBufferPosition([0, 5])
completions = getCompletions()
expect(completions).toBe null
editor.setText """
body
{
}
"""
editor.setCursorBufferPosition([1, 0])
completions = getCompletions()
expect(completions).toBe null
it "does not autocomplete property names when immediately after a }", ->
editor.setText """
body{}
"""
editor.setCursorBufferPosition([0, 6])
completions = getCompletions()
expect(completions).toBe null
editor.setText """
body{
}
"""
editor.setCursorBufferPosition([1, 1])
completions = getCompletions()
expect(completions).toBe null
it "autocompletes property names when the cursor is up against the punctuation inside the property list", ->
editor.setText """
body {
}
"""
editor.setCursorBufferPosition([0, 6])
completions = getCompletions()
expect(completions[0].displayText).toBe 'width'
editor.setText """
body {
}
"""
editor.setCursorBufferPosition([1, 0])
completions = getCompletions()
expect(completions[0].displayText).toBe 'width'
editor.setText """
body { }
"""
editor.setCursorBufferPosition([0, 6])
completions = getCompletions()
expect(completions[0].displayText).toBe 'width'
editor.setText """
body { }
"""
editor.setCursorBufferPosition([0, 7])
completions = getCompletions()
expect(completions[0].displayText).toBe 'width'
it "triggers autocomplete when an property name has been inserted", ->
spyOn(atom.commands, 'dispatch')
suggestion = {type: 'property', text: 'whatever'}
provider.onDidInsertSuggestion({editor, suggestion})
advanceClock 1
expect(atom.commands.dispatch).toHaveBeenCalled()
args = atom.commands.dispatch.mostRecentCall.args
expect(args[0].tagName.toLowerCase()).toBe 'atom-text-editor'
expect(args[1]).toBe 'autocomplete-plus:activate'
it "autocompletes property values without a prefix", ->
editor.setText """
body {
display:
}
"""
editor.setCursorBufferPosition([1, 10])
completions = getCompletions()
expect(completions.length).toBe 24
for completion in completions
expect(completion.text.length).toBeGreaterThan 0
expect(completion.description.length).toBeGreaterThan 0
expect(completion.descriptionMoreURL.length).toBeGreaterThan 0
editor.setText """
body {
display:
}
"""
editor.setCursorBufferPosition([2, 0])
completions = getCompletions()
expect(completions.length).toBe 24
for completion in completions
expect(completion.text.length).toBeGreaterThan 0
it "autocompletes property values with a prefix", ->
editor.setText """
body {
display: i
}
"""
editor.setCursorBufferPosition([1, 12])
completions = getCompletions()
expect(completions[0].text).toBe 'inline;'
expect(completions[0].description.length).toBeGreaterThan 0
expect(completions[0].descriptionMoreURL.length).toBeGreaterThan 0
expect(completions[1].text).toBe 'inline-block;'
expect(completions[2].text).toBe 'inline-flex;'
expect(completions[3].text).toBe 'inline-grid;'
expect(completions[4].text).toBe 'inline-table;'
expect(completions[5].text).toBe 'inherit;'
editor.setText """
body {
display: I
}
"""
editor.setCursorBufferPosition([1, 12])
completions = getCompletions()
expect(completions.length).toBe 6
expect(completions[0].text).toBe 'inline;'
expect(completions[1].text).toBe 'inline-block;'
expect(completions[2].text).toBe 'inline-flex;'
expect(completions[3].text).toBe 'inline-grid;'
expect(completions[4].text).toBe 'inline-table;'
expect(completions[5].text).toBe 'inherit;'
editor.setText """
body {
display:
i
}
"""
editor.setCursorBufferPosition([2, 5])
completions = getCompletions()
expect(completions[0].text).toBe 'inline;'
expect(completions[1].text).toBe 'inline-block;'
expect(completions[2].text).toBe 'inline-flex;'
expect(completions[3].text).toBe 'inline-grid;'
expect(completions[4].text).toBe 'inline-table;'
expect(completions[5].text).toBe 'inherit;'
editor.setText """
body {
text-align:
}
"""
editor.setCursorBufferPosition([1, 13])
completions = getCompletions()
expect(completions).toHaveLength 5
expect(completions[0].text).toBe 'center;'
expect(completions[1].text).toBe 'left;'
expect(completions[2].text).toBe 'justify;'
expect(completions[3].text).toBe 'right;'
expect(completions[4].text).toBe 'inherit;'
editor.setText """
body {
text-align: c
}
"""
editor.setCursorBufferPosition([1, 15])
completions = getCompletions()
expect(completions).toHaveLength 1
expect(completions[0].text).toBe 'center;'
it "does not complete property values after percentage signs", ->
editor.setText """
body {
width: 100%
}
"""
editor.setCursorBufferPosition([1, 13])
completions = getCompletions()
expect(completions).toHaveLength 0
it "it doesn't add semicolon after a property if one is already present", ->
editor.setText """
body {
display: i;
}
"""
editor.setCursorBufferPosition([1, 12])
completions = getCompletions()
completions.forEach (completion) ->
expect(completion.text).not.toMatch(/;\s*$/)
it "autocompletes inline property values", ->
editor.setText "body { display: }"
editor.setCursorBufferPosition([0, 16])
completions = getCompletions()
expect(completions).toHaveLength 24
expect(completions[0].text).toBe 'block;'
editor.setText """
body {
display: block; float:
}
"""
editor.setCursorBufferPosition([1, 24])
completions = getCompletions()
expect(completions).toHaveLength 4
expect(completions[0].text).toBe 'left;'
it "autocompletes more than one inline property value", ->
editor.setText "body { display: block; float: }"
editor.setCursorBufferPosition([0, 30])
completions = getCompletions()
expect(completions).toHaveLength 4
expect(completions[0].text).toBe 'left;'
editor.setText "body { display: block; float: left; cursor: alias; text-decoration: }"
editor.setCursorBufferPosition([0, 68])
completions = getCompletions()
expect(completions).toHaveLength 5
expect(completions[0].text).toBe 'line-through;'
it "autocompletes inline property values with a prefix", ->
editor.setText "body { display: i }"
editor.setCursorBufferPosition([0, 17])
completions = getCompletions()
expect(completions).toHaveLength 6
expect(completions[0].text).toBe 'inline;'
expect(completions[1].text).toBe 'inline-block;'
expect(completions[2].text).toBe 'inline-flex;'
expect(completions[3].text).toBe 'inline-grid;'
expect(completions[4].text).toBe 'inline-table;'
expect(completions[5].text).toBe 'inherit;'
editor.setText "body { display: i}"
editor.setCursorBufferPosition([0, 17])
completions = getCompletions()
expect(completions).toHaveLength 6
expect(completions[0].text).toBe 'inline;'
expect(completions[1].text).toBe 'inline-block;'
expect(completions[2].text).toBe 'inline-flex;'
expect(completions[3].text).toBe 'inline-grid;'
expect(completions[4].text).toBe 'inline-table;'
expect(completions[5].text).toBe 'inherit;'
it "autocompletes inline property values that aren't at the end of the line", ->
editor.setText "body { float: display: inline; font-weight: bold; }"
editor.setCursorBufferPosition([0, 14]) # right before display
completions = getCompletions()
expect(completions).toHaveLength 4
expect(completions[0].text).toBe 'left;'
expect(completions[1].text).toBe 'right;'
expect(completions[2].text).toBe 'none;'
expect(completions[3].text).toBe 'inherit;'
it "autocompletes !important in property-value scope", ->
editor.setText """
body {
display: inherit !im
}
"""
editor.setCursorBufferPosition([1, 22])
completions = getCompletions()
important = null
for c in completions
important = c if c.displayText is '!important'
expect(important.displayText).toBe '!important'
it "does not autocomplete !important in property-name scope", ->
editor.setText """
body {
!im
}
"""
editor.setCursorBufferPosition([1, 5])
completions = getCompletions()
important = null
for c in completions
important = c if c.displayText is '!important'
expect(important).toBe null
describe "tags", ->
it "autocompletes with a prefix", ->
editor.setText """
ca {
}
"""
editor.setCursorBufferPosition([0, 2])
completions = getCompletions()
expect(completions.length).toBe 7
expect(completions[0].text).toBe 'canvas'
expect(completions[0].type).toBe 'tag'
expect(completions[0].description).toBe 'Selector for <canvas> elements'
expect(completions[1].text).toBe 'code'
editor.setText """
canvas,ca {
}
"""
editor.setCursorBufferPosition([0, 9])
completions = getCompletions()
expect(completions.length).toBe 7
expect(completions[0].text).toBe 'canvas'
editor.setText """
canvas ca {
}
"""
editor.setCursorBufferPosition([0, 9])
completions = getCompletions()
expect(completions.length).toBe 7
expect(completions[0].text).toBe 'canvas'
editor.setText """
canvas, ca {
}
"""
editor.setCursorBufferPosition([0, 10])
completions = getCompletions()
expect(completions.length).toBe 7
expect(completions[0].text).toBe 'canvas'
it "does not autocompletes when prefix is preceded by class or id char", ->
editor.setText """
.ca {
}
"""
editor.setCursorBufferPosition([0, 3])
completions = getCompletions()
expect(completions).toBe null
editor.setText """
#ca {
}
"""
editor.setCursorBufferPosition([0, 3])
completions = getCompletions()
expect(completions).toBe null
describe "pseudo selectors", ->
it "autocompletes without a prefix", ->
editor.setText """
div: {
}
"""
editor.setCursorBufferPosition([0, 4])
completions = getCompletions()
expect(completions.length).toBe 43
for completion in completions
text = (completion.text or completion.snippet)
expect(text.length).toBeGreaterThan 0
expect(completion.type).toBe 'pseudo-selector'
# TODO: Enable these tests when we can enable autocomplete and test the
# entire path.
xit "autocompletes with a prefix", ->
editor.setText """
div:f {
}
"""
editor.setCursorBufferPosition([0, 5])
completions = getCompletions()
expect(completions.length).toBe 5
expect(completions[0].text).toBe ':first'
expect(completions[0].type).toBe 'pseudo-selector'
expect(completions[0].description.length).toBeGreaterThan 0
expect(completions[0].descriptionMoreURL.length).toBeGreaterThan 0
xit "autocompletes with arguments", ->
editor.setText """
div:nth {
}
"""
editor.setCursorBufferPosition([0, 7])
completions = getCompletions()
expect(completions.length).toBe 4
expect(completions[0].snippet).toBe ':nth-child(${1:an+b})'
expect(completions[0].type).toBe 'pseudo-selector'
expect(completions[0].description.length).toBeGreaterThan 0
expect(completions[0].descriptionMoreURL.length).toBeGreaterThan 0
xit "autocompletes when nothing precedes the colon", ->
editor.setText """
:f {
}
"""
editor.setCursorBufferPosition([0, 2])
completions = getCompletions()
expect(completions.length).toBe 5
expect(completions[0].text).toBe ':first'
Object.keys(packagesToTest).forEach (packageLabel) ->
unless packagesToTest[packageLabel].name is 'language-css'
describe "#{packageLabel} files", ->
beforeEach ->
waitsForPromise -> atom.packages.activatePackage(packagesToTest[packageLabel].name)
waitsForPromise -> atom.workspace.open(packagesToTest[packageLabel].file)
runs -> editor = atom.workspace.getActiveTextEditor()
it "autocompletes tags and properties when nesting inside the property list", ->
editor.setText """
.ca {
di
}
"""
editor.setCursorBufferPosition([1, 4])
completions = getCompletions()
expect(completions[0].text).toBe 'display: '
expect(completions[1].text).toBe 'direction: '
expect(completions[2].text).toBe 'div'
# FIXME: This is an issue with the grammar. It thinks nested
# pseudo-selectors are meta.property-value.scss/less
xit "autocompletes pseudo selectors when nested in LESS and SCSS files", ->
editor.setText """
.some-class {
.a:f
}
"""
editor.setCursorBufferPosition([1, 6])
completions = getCompletions()
expect(completions.length).toBe 5
expect(completions[0].text).toBe ':first'
it "does not show property names when in a class selector", ->
editor.setText """
body {
.a
}
"""
editor.setCursorBufferPosition([1, 4])
completions = getCompletions()
expect(completions).toBe null
it "does not show property names when in an id selector", ->
editor.setText """
body {
#a
}
"""
editor.setCursorBufferPosition([1, 4])
completions = getCompletions()
expect(completions).toBe null
it "does not show property names when in a parent selector", ->
editor.setText """
body {
&
}
"""
editor.setCursorBufferPosition([1, 4])
completions = getCompletions()
expect(completions).toBe null
it "does not show property names when in a parent selector with a prefix", ->
editor.setText """
body {
&a
}
"""
editor.setCursorBufferPosition([1, 4])
completions = getCompletions()
expect(completions).toBe null
describe "SASS files", ->
beforeEach ->
waitsForPromise -> atom.packages.activatePackage('language-sass')
waitsForPromise -> atom.workspace.open('test.sass')
runs -> editor = atom.workspace.getActiveTextEditor()
it "autocompletes property names with a prefix", ->
editor.setText """
body
d
"""
editor.setCursorBufferPosition([1, 3])
completions = getCompletions()
expect(completions[0].text).toBe 'display: '
expect(completions[0].displayText).toBe 'display'
expect(completions[0].type).toBe 'property'
expect(completions[0].replacementPrefix).toBe 'd'
expect(completions[0].description.length).toBeGreaterThan 0
expect(completions[0].descriptionMoreURL.length).toBeGreaterThan 0
expect(completions[1].text).toBe 'direction: '
expect(completions[1].displayText).toBe 'direction'
expect(completions[1].type).toBe 'property'
expect(completions[1].replacementPrefix).toBe 'd'
editor.setText """
body
D
"""
editor.setCursorBufferPosition([1, 3])
completions = getCompletions()
expect(completions.length).toBe 11
expect(completions[0].text).toBe 'display: '
expect(completions[1].text).toBe 'direction: '
expect(completions[1].replacementPrefix).toBe 'D'
editor.setText """
body
d:
"""
editor.setCursorBufferPosition([1, 3])
completions = getCompletions()
expect(completions[0].text).toBe 'display: '
expect(completions[1].text).toBe 'direction: '
editor.setText """
body
bord
"""
editor.setCursorBufferPosition([1, 6])
completions = getCompletions()
expect(completions[0].text).toBe 'border: '
expect(completions[0].displayText).toBe 'border'
expect(completions[0].replacementPrefix).toBe 'bord'
it "triggers autocomplete when an property name has been inserted", ->
spyOn(atom.commands, 'dispatch')
suggestion = {type: 'property', text: 'whatever'}
provider.onDidInsertSuggestion({editor, suggestion})
advanceClock 1
expect(atom.commands.dispatch).toHaveBeenCalled()
args = atom.commands.dispatch.mostRecentCall.args
expect(args[0].tagName.toLowerCase()).toBe 'atom-text-editor'
expect(args[1]).toBe 'autocomplete-plus:activate'
it "autocompletes property values without a prefix", ->
editor.setText """
body
display:
"""
editor.setCursorBufferPosition([1, 10])
completions = getCompletions()
expect(completions.length).toBe 24
for completion in completions
expect(completion.text.length).toBeGreaterThan 0
expect(completion.description.length).toBeGreaterThan 0
expect(completion.descriptionMoreURL.length).toBeGreaterThan 0
editor.setText """
body
display:
"""
editor.setCursorBufferPosition([2, 0])
completions = getCompletions()
expect(completions.length).toBe 24
for completion in completions
expect(completion.text.length).toBeGreaterThan 0
it "autocompletes property values with a prefix", ->
editor.setText """
body
display: i
"""
editor.setCursorBufferPosition([1, 12])
completions = getCompletions()
expect(completions[0].text).toBe 'inline'
expect(completions[0].description.length).toBeGreaterThan 0
expect(completions[0].descriptionMoreURL.length).toBeGreaterThan 0
expect(completions[1].text).toBe 'inline-block'
expect(completions[2].text).toBe 'inline-flex'
expect(completions[3].text).toBe 'inline-grid'
expect(completions[4].text).toBe 'inline-table'
expect(completions[5].text).toBe 'inherit'
editor.setText """
body
display: I
"""
editor.setCursorBufferPosition([1, 12])
completions = getCompletions()
expect(completions.length).toBe 6
expect(completions[0].text).toBe 'inline'
expect(completions[1].text).toBe 'inline-block'
expect(completions[2].text).toBe 'inline-flex'
expect(completions[3].text).toBe 'inline-grid'
expect(completions[4].text).toBe 'inline-table'
expect(completions[5].text).toBe 'inherit'
it "autocompletes !important in property-value scope", ->
editor.setText """
body
display: inherit !im
"""
editor.setCursorBufferPosition([1, 22])
completions = getCompletions()
important = null
for c in completions
important = c if c.displayText is '!important'
expect(important.displayText).toBe '!important'
it "does not autocomplete when indented and prefix is not a char", ->
editor.setText """
body
.
"""
editor.setCursorBufferPosition([1, 3])
completions = getCompletions(activatedManually: false)
expect(completions).toBe null
editor.setText """
body
#
"""
editor.setCursorBufferPosition([1, 3])
completions = getCompletions(activatedManually: false)
expect(completions).toBe null
editor.setText """
body
.foo,
"""
editor.setCursorBufferPosition([1, 7])
completions = getCompletions(activatedManually: false)
expect(completions).toBe null
editor.setText """
body
foo -
"""
editor.setCursorBufferPosition([1, 8])
completions = getCompletions(activatedManually: false)
expect(completions).toBe null
# As spaces at end of line will be removed, we'll test with a char
# after the space and with the cursor before that char.
editor.setCursorBufferPosition([1, 7])
completions = getCompletions(activatedManually: false)
expect(completions).toBe null
it 'does not autocomplete when inside a nth-child selector', ->
editor.setText """
body
&:nth-child(4
"""
editor.setCursorBufferPosition([1, 15])
completions = getCompletions(activatedManually: false)
expect(completions).toBe null
it 'autocompletes a property name with a dash', ->
editor.setText """
body
border-
"""
editor.setCursorBufferPosition([1, 9])
completions = getCompletions(activatedManually: false)
expect(completions).not.toBe null
expect(completions[0].text).toBe 'border: '
expect(completions[0].displayText).toBe 'border'
expect(completions[0].replacementPrefix).toBe 'border-'
expect(completions[1].text).toBe 'border-radius: '
expect(completions[1].displayText).toBe 'border-radius'
expect(completions[1].replacementPrefix).toBe 'border-'
it "does not autocomplete !important in property-name scope", ->
editor.setText """
body {
!im
}
"""
editor.setCursorBufferPosition([1, 5])
completions = getCompletions()
important = null
for c in completions
important = c if c.displayText is '!important'
expect(important).toBe null
describe "tags", ->
it "autocompletes with a prefix", ->
editor.setText """
ca
"""
editor.setCursorBufferPosition([0, 2])
completions = getCompletions()
expect(completions.length).toBe 7
expect(completions[0].text).toBe 'canvas'
expect(completions[0].type).toBe 'tag'
expect(completions[0].description).toBe 'Selector for <canvas> elements'
expect(completions[1].text).toBe 'code'
editor.setText """
canvas,ca
"""
editor.setCursorBufferPosition([0, 9])
completions = getCompletions()
expect(completions.length).toBe 7
expect(completions[0].text).toBe 'canvas'
editor.setText """
canvas ca
"""
editor.setCursorBufferPosition([0, 9])
completions = getCompletions()
expect(completions.length).toBe 7
expect(completions[0].text).toBe 'canvas'
editor.setText """
canvas, ca
"""
editor.setCursorBufferPosition([0, 10])
completions = getCompletions()
expect(completions.length).toBe 7
expect(completions[0].text).toBe 'canvas'
it "does not autocomplete when prefix is preceded by class or id char", ->
editor.setText """
.ca
"""
editor.setCursorBufferPosition([0, 3])
completions = getCompletions()
expect(completions).toBe null
editor.setText """
#ca
"""
editor.setCursorBufferPosition([0, 3])
completions = getCompletions()
expect(completions).toBe null
describe "pseudo selectors", ->
it "autocompletes without a prefix", ->
editor.setText """
div:
"""
editor.setCursorBufferPosition([0, 4])
completions = getCompletions()
expect(completions.length).toBe 43
for completion in completions
text = (completion.text or completion.snippet)
expect(text.length).toBeGreaterThan 0
expect(completion.type).toBe 'pseudo-selector'

View File

@ -0,0 +1,41 @@
# Run this to update the static list of completions stored in the completions.json
# file at the root of this repository.
path = require 'path'
fs = require 'fs'
request = require 'request'
fetchPropertyDescriptions = require './fetch-property-docs'
PropertiesURL = 'https://raw.githubusercontent.com/adobe/brackets/master/src/extensions/default/CSSCodeHints/CSSProperties.json'
propertiesPromise = new Promise (resolve) ->
request {json: true, url: PropertiesURL}, (error, response, properties) ->
if error?
console.error(error.message)
resolve(null)
if response.statusCode isnt 200
console.error("Request for CSSProperties.json failed: #{response.statusCode}")
resolve(null)
resolve(properties)
propertyDescriptionsPromise = fetchPropertyDescriptions()
Promise.all([propertiesPromise, propertyDescriptionsPromise]).then (values) ->
properties = {}
propertiesRaw = values[0]
propertyDescriptions = values[1]
sortedPropertyNames = JSON.parse(fs.readFileSync(path.join(__dirname, 'sorted-property-names.json')))
for propertyName in sortedPropertyNames
continue unless metadata = propertiesRaw[propertyName]
metadata.description = propertyDescriptions[propertyName]
properties[propertyName] = metadata
console.warn "No description for property #{propertyName}" unless propertyDescriptions[propertyName]?
for propertyName of propertiesRaw
console.warn "Ignoring #{propertyName}; not in sorted-property-names.json" if sortedPropertyNames.indexOf(propertyName) < 0
tags = JSON.parse(fs.readFileSync(path.join(__dirname, 'html-tags.json')))
pseudoSelectors = JSON.parse(fs.readFileSync(path.join(__dirname, 'pseudo-selectors.json')))
completions = {tags, properties, pseudoSelectors}
fs.writeFileSync(path.join(__dirname, 'completions.json'), "#{JSON.stringify(completions, null, ' ')}\n")

1
packages/autocomplete-html/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

View File

@ -0,0 +1,10 @@
# HTML Autocomplete package
HTML tag and attribute autocompletions in Pulsar.
Tag and attribute autocompletions are powered by the list of HTML tags [here](https://github.com/adobe/brackets/blob/master/src/extensions/default/HTMLCodeHints/HtmlTags.json) and HTML attributes [here](https://github.com/adobe/brackets/blob/master/src/extensions/default/HTMLCodeHints/HtmlAttributes.json).
Descriptions are powered by [MDN](https://developer.mozilla.org).
![html-completions](https://cloud.githubusercontent.com/assets/2766036/25668197/ffd24928-2ff3-11e7-85fc-b327ac2287e6.gif)
You can update the prebuilt list of tags and attributes names and values by running the `update.js` file at the root of the repository and then checking-in the changed `completions.json` file.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,113 @@
const path = require('path')
const fs = require('fs')
const request = require('request')
const mdnHTMLURL = 'https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes'
const mdnJSONAPI = 'https://developer.mozilla.org/en-US/search.json?topic=html&highlight=false'
const AttributesURL = 'https://raw.githubusercontent.com/adobe/brackets/master/src/extensions/default/HTMLCodeHints/HtmlAttributes.json'
const fetch = () => {
const attributesPromise = new Promise((resolve) => {
request({json: true, url: AttributesURL}, (error, response, attributes) => {
if (error) {
console.error(error.message)
resolve(null)
}
if (response.statusCode !== 200) {
console.error(`Request for HtmlAttributes.json failed: ${response.statusCode}`)
resolve(null)
}
resolve(attributes)
})
})
attributesPromise.then((attributes) => {
if (!attributes) return
const MAX = 10
const queue = []
for (let attribute in attributes) {
// MDN is missing docs for aria attributes and on* event handlers
const options = attributes[attribute]
if (options.global && !attribute.startsWith('aria') && !attribute.startsWith('on') && (attribute !== 'role')) {
queue.push(attribute)
}
}
const running = []
const docs = {}
return new Promise((resolve) => {
const checkEnd = () => {
if ((queue.length === 0) && (running.length === 0)) resolve(docs)
}
const removeRunning = (attributeName) => {
const index = running.indexOf(attributeName)
if (index > -1) { running.splice(index, 1) }
}
const runNext = () => {
checkEnd()
if (queue.length !== 0) {
const attributeName = queue.pop()
running.push(attributeName)
run(attributeName)
}
}
var run = (attributeName) => {
const url = `${mdnJSONAPI}&q=${attributeName}`
request({json: true, url}, (error, response, searchResults) => {
if (!error && response.statusCode === 200) {
handleRequest(attributeName, searchResults)
} else {
console.error(`Req failed ${url}; ${response.statusCode}, ${error}`)
}
removeRunning(attributeName)
runNext()
})
}
var handleRequest = (attributeName, searchResults) => {
if (searchResults.documents) {
for (let doc of searchResults.documents) {
if (doc.url === `${mdnHTMLURL}/${attributeName}`) {
docs[attributeName] = filterExcerpt(attributeName, doc.excerpt)
return
}
}
}
console.log(`Could not find documentation for ${attributeName}`)
}
for (let i = 0; i <= MAX; i++) runNext()
})
})
}
var filterExcerpt = (attributeName, excerpt) => {
const beginningPattern = /^the [a-z-]+ global attribute (is )?(\w+)/i
excerpt = excerpt.replace(beginningPattern, (match) => {
const matches = beginningPattern.exec(match)
const firstWord = matches[2]
return firstWord[0].toUpperCase() + firstWord.slice(1)
})
const periodIndex = excerpt.indexOf('.')
if (periodIndex > -1) { excerpt = excerpt.slice(0, periodIndex + 1) }
return excerpt
}
// Save a file if run from the command line
if (require.main === module) {
fetch().then((docs) => {
if (docs) {
fs.writeFileSync(path.join(__dirname, 'global-attribute-docs.json'), `${JSON.stringify(docs, null, ' ')}\n`)
} else {
console.error('No docs')
}
})
}
module.exports = fetch

View File

@ -0,0 +1,120 @@
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* DS202: Simplify dynamic range loops
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
const path = require('path')
const fs = require('fs')
const request = require('request')
const mdnHTMLURL = 'https://developer.mozilla.org/en-US/docs/Web/HTML/Element'
const mdnJSONAPI = 'https://developer.mozilla.org/en-US/search.json?topic=html&highlight=false'
const TagsURL = 'https://raw.githubusercontent.com/adobe/brackets/master/src/extensions/default/HTMLCodeHints/HtmlTags.json'
const fetch = () => {
const tagsPromise = new Promise((resolve) => {
request({json: true, url: TagsURL}, (error, response, tags) => {
if (error != null) {
console.error(error.message)
resolve(null)
}
if (response.statusCode !== 200) {
console.error(`Request for HtmlTags.json failed: ${response.statusCode}`)
resolve(null)
}
resolve(tags)
})
})
return tagsPromise.then((tags) => {
if (!tags) return
const MAX = 10
const queue = Object.keys(tags)
const running = []
const docs = {}
return new Promise((resolve) => {
const checkEnd = () => {
if ((queue.length === 0) && (running.length === 0)) resolve(docs)
}
const removeRunning = (tagName) => {
const index = running.indexOf(tagName)
if (index > -1) { return running.splice(index, 1) }
}
const runNext = () => {
checkEnd()
if (queue.length !== 0) {
const tagName = queue.pop()
running.push(tagName)
run(tagName)
}
}
var run = (tagName) => {
const url = `${mdnJSONAPI}&q=${tagName}`
request({json: true, url}, (error, response, searchResults) => {
if ((error == null) && (response.statusCode === 200)) {
handleRequest(tagName, searchResults)
} else {
console.error(`Req failed ${url}; ${response.statusCode}, ${error}`)
}
removeRunning(tagName)
runNext()
})
}
var handleRequest = (tagName, searchResults) => {
if (searchResults.documents != null) {
for (let doc of searchResults.documents) {
// MDN groups h1 through h6 under a single "Heading Elements" page
if ((doc.url === `${mdnHTMLURL}/${tagName}`) || (/^h\d$/.test(tagName) && (doc.url === `${mdnHTMLURL}/Heading_Elements`))) {
if (doc.tags.includes('Obsolete')) {
docs[tagName] = `The ${tagName} element is obsolete. Avoid using it and update existing code if possible.`
} else if (doc.tags.includes('Deprecated')) {
docs[tagName] = `The ${tagName} element is deprecated. Avoid using it and update existing code if possible.`
} else {
docs[tagName] = filterExcerpt(tagName, doc.excerpt)
}
return
}
}
}
console.log(`Could not find documentation for ${tagName}`)
}
for (let i = 0; i <= MAX; i++) { runNext() }
})
})
}
var filterExcerpt = (tagName, excerpt) => {
const beginningPattern = /^the html [a-z-]+ element (\([^)]+\) )?(is )?(\w+)/i
excerpt = excerpt.replace(beginningPattern, (match) => {
const matches = beginningPattern.exec(match)
const firstWord = matches[3]
return firstWord[0].toUpperCase() + firstWord.slice(1)
})
const periodIndex = excerpt.indexOf('.')
if (periodIndex > -1) { excerpt = excerpt.slice(0, periodIndex + 1) }
return excerpt
}
// Save a file if run from the command line
if (require.main === module) {
fetch().then((docs) => {
if (docs != null) {
fs.writeFileSync(path.join(__dirname, 'tag-docs.json'), `${JSON.stringify(docs, null, ' ')}\n`)
} else {
console.error('No docs')
}
})
}
module.exports = fetch

View File

@ -0,0 +1,132 @@
const COMPLETIONS = require('../completions.json')
function getTagNameCompletions (prefix) {
const completions = []
for (const tag in COMPLETIONS.tags) {
const options = COMPLETIONS.tags[tag]
if (firstCharsEqual(tag, prefix)) {
const {description} = options
completions.push({
text: tag,
type: 'tag',
description: description || `HTML <${tag}> tag`,
descriptionMoreURL: description ? getTagDocsURL(tag) : null
})
}
}
return completions
}
function getAttributeNameCompletions (tag, prefix) {
const completions = []
const tagAttributes = getTagAttributes(tag)
for (const attribute of tagAttributes) {
if (firstCharsEqual(attribute, prefix)) {
const options = COMPLETIONS.attributes[attribute]
completions.push({
snippet: (options && options.type === 'flag') ? attribute : `${attribute}="$1"$0`,
displayText: attribute,
type: 'attribute',
rightLabel: `<${tag}>`,
description: `${attribute} attribute local to <${tag}> tags`,
descriptionMoreURL: getLocalAttributeDocsURL(attribute, tag)
})
}
}
for (const attribute in COMPLETIONS.attributes) {
const options = COMPLETIONS.attributes[attribute]
if (options.global && firstCharsEqual(attribute, prefix)) {
completions.push({
snippet: options.type === 'flag' ? attribute : `${attribute}="$1"$0`,
displayText: attribute,
type: 'attribute',
description: options.description ? options.description : `Global ${attribute} attribute`,
descriptionMoreURL: options.description ? getGlobalAttributeDocsURL(attribute) : null
})
}
}
return completions
}
function getAttributeValueCompletions (tag, attribute, prefix) {
const completions = []
const values = getAttributeValues(tag, attribute)
for (const value of values) {
if (firstCharsEqual(value, prefix)) {
completions.push(buildAttributeValueCompletion(tag, attribute, value))
}
}
if (
completions.length === 0 &&
COMPLETIONS.attributes[attribute] &&
COMPLETIONS.attributes[attribute].type === 'boolean'
) {
completions.push(buildAttributeValueCompletion(tag, attribute, 'true'))
completions.push(buildAttributeValueCompletion(tag, attribute, 'false'))
}
return completions
}
function buildAttributeValueCompletion (tag, attribute, value) {
if (COMPLETIONS.attributes[attribute].global) {
return {
text: value,
type: 'value',
description: `${value} value for global ${attribute} attribute`,
descriptionMoreURL: getGlobalAttributeDocsURL(attribute)
}
} else {
return {
text: value,
type: 'value',
rightLabel: `<${tag}>`,
description: `${value} value for ${attribute} attribute local to <${tag}>`,
descriptionMoreURL: getLocalAttributeDocsURL(attribute, tag)
}
}
}
function getAttributeValues (tag, attribute) {
// Some local attributes are valid for multiple tags but have different attribute values
// To differentiate them, they are identified in the completions file as tag/attribute
let result = COMPLETIONS.attributes[`${tag}/${attribute}`]
if (result && result.attribOption) return result.attribOption
result = COMPLETIONS.attributes[attribute]
if (result && result.attribOption) return result.attribOption
return []
}
function getTagAttributes (tag) {
let result = COMPLETIONS.tags[tag]
if (result && result.attributes) return result.attributes
return []
}
function getLocalAttributeDocsURL (attribute, tag) {
return `${getTagDocsURL(tag)}#attr-${attribute}`
}
function getGlobalAttributeDocsURL (attribute) {
return `https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/${attribute}`
}
function getTagDocsURL (tag) {
return `https://developer.mozilla.org/en-US/docs/Web/HTML/Element/${tag}`
}
function firstCharsEqual (a, b) {
if (b.length === 0) return true
return a[0].toLowerCase() === b[0].toLowerCase()
}
module.exports = {
getTagNameCompletions,
getAttributeNameCompletions,
getAttributeValueCompletions
}

View File

@ -0,0 +1,36 @@
const getSuggestionsWithTreeSitter = require('./tree-sitter-provider')
const getSuggestionsWithTextMate = require('./text-mate-provider')
const provider = {
selector: '.text.html',
disableForSelector: '.text.html .comment',
priority: 1,
filterSuggestions: true,
getSuggestions (request) {
if (request.editor.getBuffer().getLanguageMode().tree) {
return getSuggestionsWithTreeSitter(request)
} else {
return getSuggestionsWithTextMate(request)
}
},
onDidInsertSuggestion ({editor, suggestion}) {
if (suggestion.type === 'attribute') {
setTimeout(this.triggerAutocomplete.bind(this, editor), 1)
}
},
triggerAutocomplete (editor) {
atom.commands.dispatch(
editor.getElement(),
'autocomplete-plus:activate',
{activatedManually: false}
)
}
}
module.exports = {
activate () {},
getProvider () { return provider }
}

View File

@ -0,0 +1,127 @@
const {
getTagNameCompletions,
getAttributeNameCompletions,
getAttributeValueCompletions
} = require('./helpers')
const attributePattern = /\s+([a-zA-Z][-a-zA-Z]*)\s*=\s*$/
const tagPattern = /<([a-zA-Z][-a-zA-Z]*)(?:\s|$)/
module.exports = function (request) {
let {editor, bufferPosition, prefix} = request
prefix = prefix.trim()
if (isAttributeValueStart(request)) {
const tag = getPreviousTag(editor, bufferPosition)
const attribute = getPreviousAttribute(editor, bufferPosition)
return getAttributeValueCompletions(tag, attribute, prefix)
}
if (isAttributeStart(request)) {
const tag = getPreviousTag(editor, bufferPosition)
return getAttributeNameCompletions(tag, prefix)
}
if (isTagStart(request)) {
const ignorePrefix = editor.getTextInRange([
[bufferPosition.row, bufferPosition.column - 1],
bufferPosition
]) === '<'
return getTagNameCompletions(ignorePrefix ? '' : prefix)
}
return []
}
function isTagStart ({prefix, scopeDescriptor, bufferPosition, editor}) {
if (prefix.trim() && (prefix.indexOf('<') === -1)) {
return hasTagScope(scopeDescriptor.getScopesArray())
}
// autocomplete-plus's default prefix setting does not capture <. Manually check for it.
prefix = editor.getTextInRange([[bufferPosition.row, bufferPosition.column - 1], bufferPosition])
const scopes = scopeDescriptor.getScopesArray()
// Don't autocomplete in embedded languages
return (prefix === '<') && (scopes[0] === 'text.html.basic') && (scopes.length === 1)
}
function isAttributeStart ({prefix, scopeDescriptor, bufferPosition, editor}) {
const scopes = scopeDescriptor.getScopesArray()
if (!getPreviousAttribute(editor, bufferPosition) && prefix && !prefix.trim()) {
return hasTagScope(scopes)
}
const previousBufferPosition = [bufferPosition.row, Math.max(0, bufferPosition.column - 1)]
const previousScopes = editor.scopeDescriptorForBufferPosition(previousBufferPosition)
const previousScopesArray = previousScopes.getScopesArray()
if (previousScopesArray.includes('entity.other.attribute-name.html')) return true
if (!hasTagScope(scopes)) return false
// autocomplete here: <tag |>
// not here: <tag >|
return (
scopes.includes('punctuation.definition.tag.end.html') &&
!previousScopesArray.includes('punctuation.definition.tag.end.html')
)
}
function isAttributeValueStart ({scopeDescriptor, bufferPosition, editor}) {
const scopes = scopeDescriptor.getScopesArray()
const previousBufferPosition = [bufferPosition.row, Math.max(0, bufferPosition.column - 1)]
const previousScopes = editor.scopeDescriptorForBufferPosition(previousBufferPosition)
const previousScopesArray = previousScopes.getScopesArray()
// autocomplete here: attribute="|"
// not here: attribute=|""
// or here: attribute=""|
// or here: attribute="""|
return (
hasStringScope(scopes) &&
hasStringScope(previousScopesArray) &&
!previousScopesArray.includes('punctuation.definition.string.end.html') &&
hasTagScope(scopes) &&
getPreviousAttribute(editor, bufferPosition) != null
)
}
function hasTagScope (scopes) {
for (let scope of scopes) {
if (scope.startsWith('meta.tag.') && scope.endsWith('.html')) return true
}
return false
}
function hasStringScope (scopes) {
return (
scopes.includes('string.quoted.double.html') ||
scopes.includes('string.quoted.single.html')
)
}
function getPreviousTag (editor, bufferPosition) {
let {row} = bufferPosition
while (row >= 0) {
const match = tagPattern.exec(editor.lineTextForBufferRow(row))
const tag = match && match[1]
if (tag) return tag
row--
}
}
function getPreviousAttribute (editor, bufferPosition) {
// Remove everything until the opening quote (if we're in a string)
let quoteIndex = bufferPosition.column - 1 // Don't start at the end of the line
while (quoteIndex) {
const scopes = editor.scopeDescriptorForBufferPosition([bufferPosition.row, quoteIndex])
const scopesArray = scopes.getScopesArray()
if (!hasStringScope(scopesArray) || (scopesArray.indexOf('punctuation.definition.string.begin.html') !== -1)) break
quoteIndex--
}
const match = attributePattern.exec(editor.getTextInRange([[bufferPosition.row, 0], [bufferPosition.row, quoteIndex]]))
return match && match[1]
}

View File

@ -0,0 +1,126 @@
const {
getTagNameCompletions,
getAttributeNameCompletions,
getAttributeValueCompletions
} = require('./helpers')
module.exports = function ({editor, bufferPosition}) {
let node = tokenBeforePosition(editor, bufferPosition)
if (!node) return []
switch (node.type) {
case '<': {
if (!bufferPosition.isEqual(node.endPosition)) break
return getTagNameCompletions('')
}
case 'tag_name': {
if (bufferPosition.isEqual(node.endPosition)) {
const {previousSibling} = node
if (previousSibling && previousSibling.endIndex === node.startIndex) {
return getTagNameCompletions(node.text)
}
} else {
return getAttributeNameCompletions(node.text, '')
}
break
}
case 'attribute_name': {
if (!bufferPosition.isEqual(node.endPosition)) break
const tagNode = node.parent.parent
const tagNameNode = tagNode.child(1)
if (tagNameNode) {
return getAttributeNameCompletions(tagNameNode.text, node.text)
}
break
}
case 'attribute_value':
case '"':
case '\'': {
let prefix = ''
if (node.type === 'attribute_value') {
prefix = node.text
node = node.previousSibling
}
const predecessor = tokenBefore(node)
if (!predecessor || predecessor.type !== '=') return []
const containerNode = node.closest(['start_tag', 'self_closing_tag', 'ERROR'])
const tagNameNode = containerNode.descendantsOfType(
'tag_name'
)[0]
// Get the last attribute name before the quote
const attributeNameNode = containerNode.descendantsOfType(
'attribute_name',
null,
node.startPosition
).pop()
if (tagNameNode && attributeNameNode) {
return getAttributeValueCompletions(tagNameNode.text, attributeNameNode.text, prefix)
}
break
}
}
return []
}
function tokenBeforePosition (editor, position) {
const languageMode = editor.getBuffer().getLanguageMode()
let node = languageMode.getSyntaxNodeAtPosition(
position,
(node, grammar) => grammar.scopeName === 'text.html.basic'
)
if (!node) return null
node = lastDescendant(node)
while (
position.isLessThan(node.endPosition) ||
node.isMissing() ||
node.type === 'text'
) {
node = tokenBefore(node)
if (!node) return null
}
return node
}
const nodesToSearch = new Set([
'<',
'tag_name',
'attribute_name',
'attribute_value',
'"',
'\''
])
function tokenBefore (node) {
for (;;) {
const {previousSibling} = node
if (previousSibling) {
return lastDescendant(previousSibling)
}
const {parent} = node
if (parent) {
node = parent
if(nodesToSearch.has(node.type)) return node
continue
}
return null
}
}
function lastDescendant (node) {
let {lastChild} = node
while (lastChild) {
node = lastChild
lastChild = node.lastChild
}
return node
}

View File

@ -0,0 +1,873 @@
{
"name": "autocomplete-html",
"version": "0.8.9",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "autocomplete-html",
"version": "0.8.9",
"license": "MIT",
"devDependencies": {
"request": "^2.53.0"
},
"engines": {
"atom": ">=0.174.0 <2.0.0"
}
},
"node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/asn1": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
"dev": true,
"dependencies": {
"safer-buffer": "~2.1.0"
}
},
"node_modules/assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
"dev": true,
"engines": {
"node": ">=0.8"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"dev": true
},
"node_modules/aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
"integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==",
"dev": true,
"engines": {
"node": "*"
}
},
"node_modules/aws4": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
"integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
"dev": true
},
"node_modules/bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
"dev": true,
"dependencies": {
"tweetnacl": "^0.14.3"
}
},
"node_modules/caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
"dev": true
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dev": true,
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
"dev": true
},
"node_modules/dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
"integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
"dev": true,
"dependencies": {
"assert-plus": "^1.0.0"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"dev": true,
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
"integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
"dev": true,
"dependencies": {
"jsbn": "~0.1.0",
"safer-buffer": "^2.1.0"
}
},
"node_modules/extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
"dev": true
},
"node_modules/extsprintf": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
"integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
"dev": true,
"engines": [
"node >=0.6.0"
]
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
},
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true
},
"node_modules/forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
"integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==",
"dev": true,
"engines": {
"node": "*"
}
},
"node_modules/form-data": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
"dev": true,
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 0.12"
}
},
"node_modules/getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
"integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
"dev": true,
"dependencies": {
"assert-plus": "^1.0.0"
}
},
"node_modules/har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
"integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==",
"dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/har-validator": {
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
"deprecated": "this library is no longer supported",
"dev": true,
"dependencies": {
"ajv": "^6.12.3",
"har-schema": "^2.0.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
"integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==",
"dev": true,
"dependencies": {
"assert-plus": "^1.0.0",
"jsprim": "^1.2.2",
"sshpk": "^1.7.0"
},
"engines": {
"node": ">=0.8",
"npm": ">=1.3.7"
}
},
"node_modules/is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
"dev": true
},
"node_modules/isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
"dev": true
},
"node_modules/jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
"dev": true
},
"node_modules/json-schema": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
"dev": true
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
},
"node_modules/json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
"dev": true
},
"node_modules/jsprim": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
"integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
"dev": true,
"dependencies": {
"assert-plus": "1.0.0",
"extsprintf": "1.3.0",
"json-schema": "0.4.0",
"verror": "1.10.0"
},
"engines": {
"node": ">=0.6.0"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true,
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dev": true,
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/oauth-sign": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
"dev": true,
"engines": {
"node": "*"
}
},
"node_modules/performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
"dev": true
},
"node_modules/psl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
"dev": true
},
"node_modules/punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/qs": {
"version": "6.5.3",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
"integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
"dev": true,
"engines": {
"node": ">=0.6"
}
},
"node_modules/request": {
"version": "2.88.2",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
"deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142",
"dev": true,
"dependencies": {
"aws-sign2": "~0.7.0",
"aws4": "^1.8.0",
"caseless": "~0.12.0",
"combined-stream": "~1.0.6",
"extend": "~3.0.2",
"forever-agent": "~0.6.1",
"form-data": "~2.3.2",
"har-validator": "~5.1.3",
"http-signature": "~1.2.0",
"is-typedarray": "~1.0.0",
"isstream": "~0.1.2",
"json-stringify-safe": "~5.0.1",
"mime-types": "~2.1.19",
"oauth-sign": "~0.9.0",
"performance-now": "^2.1.0",
"qs": "~6.5.2",
"safe-buffer": "^5.1.2",
"tough-cookie": "~2.5.0",
"tunnel-agent": "^0.6.0",
"uuid": "^3.3.2"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true
},
"node_modules/sshpk": {
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz",
"integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==",
"dev": true,
"dependencies": {
"asn1": "~0.2.3",
"assert-plus": "^1.0.0",
"bcrypt-pbkdf": "^1.0.0",
"dashdash": "^1.12.0",
"ecc-jsbn": "~0.1.1",
"getpass": "^0.1.1",
"jsbn": "~0.1.0",
"safer-buffer": "^2.0.2",
"tweetnacl": "~0.14.0"
},
"bin": {
"sshpk-conv": "bin/sshpk-conv",
"sshpk-sign": "bin/sshpk-sign",
"sshpk-verify": "bin/sshpk-verify"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/tough-cookie": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
"dev": true,
"dependencies": {
"psl": "^1.1.28",
"punycode": "^2.1.1"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
"dev": true,
"dependencies": {
"safe-buffer": "^5.0.1"
},
"engines": {
"node": "*"
}
},
"node_modules/tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
"dev": true
},
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true,
"dependencies": {
"punycode": "^2.1.0"
}
},
"node_modules/uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"dev": true,
"bin": {
"uuid": "bin/uuid"
}
},
"node_modules/verror": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
"integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
"dev": true,
"engines": [
"node >=0.6.0"
],
"dependencies": {
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "^1.2.0"
}
}
},
"dependencies": {
"ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"asn1": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
"dev": true,
"requires": {
"safer-buffer": "~2.1.0"
}
},
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
"dev": true
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"dev": true
},
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
"integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==",
"dev": true
},
"aws4": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
"integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
"dev": true
},
"bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
"dev": true,
"requires": {
"tweetnacl": "^0.14.3"
}
},
"caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
"dev": true
},
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dev": true,
"requires": {
"delayed-stream": "~1.0.0"
}
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
"dev": true
},
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
"integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
"dev": true,
"requires": {
"assert-plus": "^1.0.0"
}
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"dev": true
},
"ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
"integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
"dev": true,
"requires": {
"jsbn": "~0.1.0",
"safer-buffer": "^2.1.0"
}
},
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
"dev": true
},
"extsprintf": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
"integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
"dev": true
},
"fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
},
"fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true
},
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
"integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==",
"dev": true
},
"form-data": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
"dev": true,
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
"mime-types": "^2.1.12"
}
},
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
"integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
"dev": true,
"requires": {
"assert-plus": "^1.0.0"
}
},
"har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
"integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==",
"dev": true
},
"har-validator": {
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
"dev": true,
"requires": {
"ajv": "^6.12.3",
"har-schema": "^2.0.0"
}
},
"http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
"integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==",
"dev": true,
"requires": {
"assert-plus": "^1.0.0",
"jsprim": "^1.2.2",
"sshpk": "^1.7.0"
}
},
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
"dev": true
},
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
"dev": true
},
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
"dev": true
},
"json-schema": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
"dev": true
},
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
},
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
"dev": true
},
"jsprim": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
"integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
"dev": true,
"requires": {
"assert-plus": "1.0.0",
"extsprintf": "1.3.0",
"json-schema": "0.4.0",
"verror": "1.10.0"
}
},
"mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true
},
"mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dev": true,
"requires": {
"mime-db": "1.52.0"
}
},
"oauth-sign": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
"dev": true
},
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
"dev": true
},
"psl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
"dev": true
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true
},
"qs": {
"version": "6.5.3",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
"integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
"dev": true
},
"request": {
"version": "2.88.2",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
"dev": true,
"requires": {
"aws-sign2": "~0.7.0",
"aws4": "^1.8.0",
"caseless": "~0.12.0",
"combined-stream": "~1.0.6",
"extend": "~3.0.2",
"forever-agent": "~0.6.1",
"form-data": "~2.3.2",
"har-validator": "~5.1.3",
"http-signature": "~1.2.0",
"is-typedarray": "~1.0.0",
"isstream": "~0.1.2",
"json-stringify-safe": "~5.0.1",
"mime-types": "~2.1.19",
"oauth-sign": "~0.9.0",
"performance-now": "^2.1.0",
"qs": "~6.5.2",
"safe-buffer": "^5.1.2",
"tough-cookie": "~2.5.0",
"tunnel-agent": "^0.6.0",
"uuid": "^3.3.2"
}
},
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true
},
"sshpk": {
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz",
"integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==",
"dev": true,
"requires": {
"asn1": "~0.2.3",
"assert-plus": "^1.0.0",
"bcrypt-pbkdf": "^1.0.0",
"dashdash": "^1.12.0",
"ecc-jsbn": "~0.1.1",
"getpass": "^0.1.1",
"jsbn": "~0.1.0",
"safer-buffer": "^2.0.2",
"tweetnacl": "~0.14.0"
}
},
"tough-cookie": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
"dev": true,
"requires": {
"psl": "^1.1.28",
"punycode": "^2.1.1"
}
},
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
"dev": true,
"requires": {
"safe-buffer": "^5.0.1"
}
},
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
"dev": true
},
"uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true,
"requires": {
"punycode": "^2.1.0"
}
},
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"dev": true
},
"verror": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
"integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
"dev": true,
"requires": {
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "^1.2.0"
}
}
}
}

View File

@ -0,0 +1,21 @@
{
"name": "autocomplete-html",
"version": "0.8.9",
"description": "HTML element and attribute autocompletions",
"main": "./lib/main",
"license": "MIT",
"repository": "https://github.com/pulsar-edit/pulsar",
"engines": {
"atom": ">=0.174.0 <2.0.0"
},
"providedServices": {
"autocomplete.provider": {
"versions": {
"2.0.0": "getProvider"
}
}
},
"devDependencies": {
"request": "^2.53.0"
}
}

View File

@ -0,0 +1,521 @@
describe('HTML autocompletions', () => {
let editor, provider
function getCompletions () {
const cursor = editor.getLastCursor()
const bufferPosition = cursor.getBufferPosition()
const scopeDescriptor = cursor.getScopeDescriptor()
const line = editor.getTextInRange([[bufferPosition.row, 0], bufferPosition])
// https://github.com/atom/autocomplete-plus/blob/9506a5c5fafca29003c59566cfc2b3ac37080973/lib/autocomplete-manager.js#L57
const prefixMatch = /(\b|['"~`!@#$%^&*(){}[\]=+,/?>])((\w+[\w-]*)|([.:;[{(< ]+))$/.exec(line)
const prefix = prefixMatch ? prefixMatch[2] : ''
return provider.getSuggestions({editor, bufferPosition, scopeDescriptor, prefix})
}
beforeEach(() => {
waitsForPromise(() => atom.packages.activatePackage('autocomplete-html'))
waitsForPromise(() => atom.packages.activatePackage('language-html'))
waitsForPromise(() => atom.workspace.open('test.html'))
runs(() => provider = atom.packages.getActivePackage('autocomplete-html').mainModule.getProvider())
runs(() => editor = atom.workspace.getActiveTextEditor())
})
it('returns no completions when not at the start of a tag', () => {
editor.setText('')
expect(getCompletions().length).toBe(0)
editor.setText('d')
editor.setCursorBufferPosition([0, 0])
expect(getCompletions().length).toBe(0)
editor.setCursorBufferPosition([0, 1])
expect(getCompletions().length).toBe(0)
})
it('returns no completions in style tags', () => {
editor.setText(`\
<style>
<
</style>\
`
)
editor.setCursorBufferPosition([1, 1])
expect(getCompletions().length).toBe(0)
})
it('returns no completions in script tags', () => {
editor.setText(`\
<script>
<
</script>\
`
)
editor.setCursorBufferPosition([1, 1])
expect(getCompletions().length).toBe(0)
})
it('autcompletes tag names without a prefix', () => {
editor.setText('<')
editor.setCursorBufferPosition([0, 1])
const completions = getCompletions()
expect(completions.length).toBe(113)
expect(completions[0].description).toContain('Creates a hyperlink to other web pages')
expect(completions[0].descriptionMoreURL.endsWith('/HTML/Element/a')).toBe(true)
for (let completion of completions) {
expect(completion.text.length).toBeGreaterThan(0)
expect(completion.description.length).toBeGreaterThan(0)
expect(completion.type).toBe('tag')
}
})
it('autocompletes tag names with a prefix', () => {
editor.setText('<d')
editor.setCursorBufferPosition([0, 2])
let completions = getCompletions()
expect(completions.length).toBe(9)
expect(completions[0].text).toBe('datalist')
expect(completions[0].type).toBe('tag')
expect(completions[0].descriptionMoreURL.endsWith('/HTML/Element/datalist')).toBe(true)
expect(completions[1].text).toBe('dd')
expect(completions[2].text).toBe('del')
expect(completions[3].text).toBe('details')
expect(completions[4].text).toBe('dfn')
expect(completions[5].text).toBe('dialog')
expect(completions[6].text).toBe('div')
expect(completions[7].text).toBe('dl')
expect(completions[8].text).toBe('dt')
editor.setText('<D')
editor.setCursorBufferPosition([0, 2])
completions = getCompletions()
expect(completions.length).toBe(9)
expect(completions[0].text).toBe('datalist')
expect(completions[0].type).toBe('tag')
expect(completions[1].text).toBe('dd')
expect(completions[2].text).toBe('del')
expect(completions[3].text).toBe('details')
expect(completions[4].text).toBe('dfn')
expect(completions[5].text).toBe('dialog')
expect(completions[6].text).toBe('div')
expect(completions[7].text).toBe('dl')
expect(completions[8].text).toBe('dt')
})
it("does not autocomplete tag names if there's a space after the <", () => {
editor.setText('< ')
editor.setCursorBufferPosition([0, 2])
let completions = getCompletions()
expect(completions.length).toBe(0)
editor.setText('< h')
editor.setCursorBufferPosition([0, 2])
completions = getCompletions()
expect(completions.length).toBe(0)
})
it('does not provide a descriptionMoreURL if the tag does not have a unique description', () => {
// ilayer does not have an associated MDN page as of April 27, 2017
editor.setText('<i')
editor.setCursorBufferPosition([0, 2])
const completions = getCompletions()
expect(completions[2].text).toBe('ilayer')
expect(completions[2].description).toBe('HTML <ilayer> tag')
expect(completions[2].descriptionMoreURL).toBeNull()
})
it('autocompletes attribute names without a prefix', () => {
editor.setText('<div ')
editor.setCursorBufferPosition([0, 5])
let completions = getCompletions()
expect(completions.length).toBe(86)
expect(completions[0].description).toContain('Provides a hint for generating a keyboard shortcut')
expect(completions[0].descriptionMoreURL.endsWith('/HTML/Global_attributes/accesskey')).toBe(true)
for (var completion of completions) {
expect(completion.snippet.length).toBeGreaterThan(0)
expect(completion.displayText.length).toBeGreaterThan(0)
expect(completion.description.length).toBeGreaterThan(0)
expect(completion.type).toBe('attribute')
}
editor.setText('<marquee ')
editor.setCursorBufferPosition([0, 9])
completions = getCompletions()
expect(completions.length).toBe(98)
expect(completions[0].rightLabel).toBe('<marquee>')
expect(completions[0].descriptionMoreURL.endsWith('/HTML/Element/marquee#attr-align')).toBe(true)
for (completion of completions) {
expect(completion.snippet.length).toBeGreaterThan(0)
expect(completion.displayText.length).toBeGreaterThan(0)
expect(completion.description.length).toBeGreaterThan(0)
expect(completion.type).toBe('attribute')
}
editor.setText('<div >')
editor.setCursorBufferPosition([0, 5])
completions = getCompletions()
expect(completions.length).toBeGreaterThan(0)
for (completion of completions) { expect(completion.type).toBe('attribute') }
editor.setText('<div >')
editor.setCursorBufferPosition([0, 5])
completions = getCompletions()
expect(completions.length).toBeGreaterThan(0)
for (completion of completions) {
expect(completion.type).toBe('attribute')
}
})
it('autocompletes attribute names with a prefix', () => {
editor.setText('<div c')
editor.setCursorBufferPosition([0, 6])
let completions = getCompletions()
expect(completions.length).toBe(3)
expect(completions[0].snippet).toBe('class="$1"$0')
expect(completions[0].displayText).toBe('class')
expect(completions[0].type).toBe('attribute')
expect(completions[1].displayText).toBe('contenteditable')
expect(completions[2].displayText).toBe('contextmenu')
editor.setText('<div C')
editor.setCursorBufferPosition([0, 6])
completions = getCompletions()
expect(completions.length).toBe(3)
expect(completions[0].displayText).toBe('class')
expect(completions[1].displayText).toBe('contenteditable')
expect(completions[2].displayText).toBe('contextmenu')
editor.setText('<div c>')
editor.setCursorBufferPosition([0, 6])
completions = getCompletions()
expect(completions.length).toBe(3)
expect(completions[0].displayText).toBe('class')
expect(completions[1].displayText).toBe('contenteditable')
expect(completions[2].displayText).toBe('contextmenu')
editor.setText('<div c></div>')
editor.setCursorBufferPosition([0, 6])
completions = getCompletions()
expect(completions.length).toBe(3)
expect(completions[0].displayText).toBe('class')
expect(completions[1].displayText).toBe('contenteditable')
expect(completions[2].displayText).toBe('contextmenu')
editor.setText('<marquee di')
editor.setCursorBufferPosition([0, 12])
completions = getCompletions()
expect(completions[0].displayText).toBe('direction')
expect(completions[1].displayText).toBe('dir')
editor.setText('<marquee dI')
editor.setCursorBufferPosition([0, 12])
completions = getCompletions()
expect(completions[0].displayText).toBe('direction')
expect(completions[1].displayText).toBe('dir')
})
it('autocompletes attribute names without a prefix surrounded by whitespace', () => {
editor.setText('<select autofocus')
editor.setCursorBufferPosition([0, 8])
const completions = getCompletions()
for (let completion of completions) { expect(completion.type).toBe('attribute') }
expect(completions[0].displayText).toBe('autofocus')
})
it('autocompletes attribute names with a prefix surrounded by whitespace', () => {
editor.setText('<select o autofocus')
editor.setCursorBufferPosition([0, 9])
const completions = getCompletions()
for (let completion of completions) { expect(completion.type).toBe('attribute') }
expect(completions[0].displayText).toBe('onabort')
})
it("respects the 'flag' type when autocompleting attribute names", () => {
editor.setText('<select ')
editor.setCursorBufferPosition([0, 8])
const completions = getCompletions()
expect(completions[0].snippet).toBe('autofocus')
})
it('does not autocomplete attribute names in between an attribute name and value', () => {
editor.setText('<select autofocus=""')
editor.setCursorBufferPosition([0, 18])
let completions = getCompletions()
expect(completions.length).toBe(0)
editor.setText('<select autofocus= ""')
editor.setCursorBufferPosition([0, 18])
completions = getCompletions()
expect(completions.length).toBe(0)
editor.setText('<select autofocus= ""')
editor.setCursorBufferPosition([0, 19])
completions = getCompletions()
expect(completions.length).toBe(0)
editor.setText('<select autofocus= ""')
editor.setCursorBufferPosition([0, 19])
completions = getCompletions()
expect(completions.length).toBe(0)
})
it('does not autocomplete attribute names outside of a tag', () => {
editor.setText('<kbd>')
editor.setCursorBufferPosition([0, 0])
expect(getCompletions().length).toBe(0)
editor.setCursorBufferPosition([0, 5])
expect(getCompletions().length).toBe(0)
})
it('does not throw when a local attribute is not in the attributes list', () => {
// Some tags, like body, have local attributes that are not present in the top-level attributes array
editor.setText('<body ')
editor.setCursorBufferPosition([0, 6])
const completions = getCompletions()
expect(completions[0].displayText).toBe('onafterprint')
})
it('does not provide a descriptionMoreURL if the attribute does not have a unique description', () => {
editor.setText('<input on')
editor.setCursorBufferPosition([0, 9])
const completions = getCompletions()
expect(completions[0].displayText).toBe('onabort')
expect(completions[0].description).toBe('Global onabort attribute')
expect(completions[0].descriptionMoreURL).toBeNull()
})
it('autocompletes attribute values without a prefix', () => {
editor.setText('<marquee behavior=""')
editor.setCursorBufferPosition([0, 19])
let completions = getCompletions()
expect(completions.length).toBe(3)
expect(completions[0].text).toBe('scroll')
expect(completions[0].type).toBe('value')
expect(completions[0].description.length).toBeGreaterThan(0)
expect(completions[0].descriptionMoreURL.endsWith('/HTML/Element/marquee#attr-behavior')).toBe(true)
expect(completions[1].text).toBe('slide')
expect(completions[2].text).toBe('alternate')
editor.setText('<marquee behavior="')
editor.setCursorBufferPosition([0, 19])
completions = getCompletions()
expect(completions.length).toBe(3)
expect(completions[0].text).toBe('scroll')
expect(completions[1].text).toBe('slide')
expect(completions[2].text).toBe('alternate')
editor.setText('<marquee behavior=\'')
editor.setCursorBufferPosition([0, 19])
completions = getCompletions()
expect(completions.length).toBe(3)
expect(completions[0].text).toBe('scroll')
expect(completions[1].text).toBe('slide')
expect(completions[2].text).toBe('alternate')
editor.setText('<marquee behavior=\'\'')
editor.setCursorBufferPosition([0, 19])
completions = getCompletions()
expect(completions.length).toBe(3)
expect(completions[0].text).toBe('scroll')
expect(completions[1].text).toBe('slide')
expect(completions[2].text).toBe('alternate')
})
it('autocompletes attribute values with a prefix', () => {
editor.setText('<html behavior="" lang="e"')
editor.setCursorBufferPosition([0, 25])
let completions = getCompletions()
expect(completions.length).toBe(6)
expect(completions[0].text).toBe('eu')
expect(completions[0].type).toBe('value')
expect(completions[1].text).toBe('en')
expect(completions[2].text).toBe('eo')
expect(completions[3].text).toBe('et')
expect(completions[4].text).toBe('el')
expect(completions[5].text).toBe('es')
editor.setText('<html behavior="" lang="E"')
editor.setCursorBufferPosition([0, 25])
completions = getCompletions()
expect(completions.length).toBe(6)
expect(completions[0].text).toBe('eu')
expect(completions[1].text).toBe('en')
expect(completions[2].text).toBe('eo')
expect(completions[3].text).toBe('et')
expect(completions[4].text).toBe('el')
expect(completions[5].text).toBe('es')
editor.setText('<html behavior="" lang=\'e\'')
editor.setCursorBufferPosition([0, 25])
completions = getCompletions()
expect(completions.length).toBe(6)
expect(completions[0].text).toBe('eu')
expect(completions[1].text).toBe('en')
expect(completions[2].text).toBe('eo')
expect(completions[3].text).toBe('et')
expect(completions[4].text).toBe('el')
expect(completions[5].text).toBe('es')
})
it('autocompletes ambiguous attribute values', () => {
editor.setText('<button type=""')
editor.setCursorBufferPosition([0, 14])
let completions = getCompletions()
expect(completions.length).toBe(3)
expect(completions[0].text).toBe('button')
expect(completions[0].type).toBe('value')
expect(completions[0].description.length).toBeGreaterThan(0)
expect(completions[0].descriptionMoreURL.endsWith('/HTML/Element/button#attr-type')).toBe(true)
expect(completions[1].text).toBe('reset')
expect(completions[2].text).toBe('submit')
editor.setText('<link rel=""')
editor.setCursorBufferPosition([0, 11])
completions = getCompletions()
expect(completions.length).toBe(13)
expect(completions.map(_ => _.text)).toContain('stylesheet')
expect(completions[0].text).toBe('alternate')
expect(completions[0].type).toBe('value')
expect(completions[0].description.length).toBeGreaterThan(0)
expect(completions[0].descriptionMoreURL.endsWith('/HTML/Element/link#attr-rel')).toBe(true)
})
it("provides 'true' and 'false' suggestions when autocompleting boolean attributes", () => {
editor.setText('<html contenteditable=""')
editor.setCursorBufferPosition([0, 23])
const completions = getCompletions()
expect(completions.length).toBe(2)
expect(completions[0].text).toBe('true')
expect(completions[1].text).toBe('false')
})
it('does not attempt to autocomplete values before the beginning of a string', () => {
editor.setText('<button type=""')
editor.setCursorBufferPosition([0, 13])
let completions = []
expect(() => completions = getCompletions()).not.toThrow()
expect(completions.length).toBe(0)
})
it('does not attempt to autocomplete values after the end of a string', () => {
editor.setText('<button type=""')
editor.setCursorBufferPosition([0, 15])
let completions = []
expect(() => completions = getCompletions()).not.toThrow()
expect(completions.length).toBe(0)
})
it('does not throw when quotes are in the attribute value', () => {
editor.setText('<button type="\'"')
editor.setCursorBufferPosition([0, 15])
expect(() => getCompletions()).not.toThrow()
})
it("does not autocomplete attribute values if there isn't a corresponding attribute", () => {
editor.setText('<button type="""')
editor.setCursorBufferPosition([0, 16])
let completions = []
expect(() => completions = getCompletions()).not.toThrow()
expect(completions.length).toBe(0)
})
it('does not throw when attempting to autocomplete values for nonexistent attributes', () => {
editor.setText('<button typ=""')
editor.setCursorBufferPosition([0, 13])
let completions = []
expect(() => completions = getCompletions()).not.toThrow()
expect(completions.length).toBe(0)
})
it('triggers autocomplete when an attibute has been inserted', () => {
spyOn(atom.commands, 'dispatch')
const suggestion = {type: 'attribute', text: 'whatever'}
provider.onDidInsertSuggestion({editor, suggestion})
advanceClock(1)
expect(atom.commands.dispatch).toHaveBeenCalled()
const { args } = atom.commands.dispatch.mostRecentCall
expect(args[0].tagName.toLowerCase()).toBe('atom-text-editor')
expect(args[1]).toBe('autocomplete-plus:activate')
})
it('does not error in EJS documents', () => {
waitsForPromise(async () => {
await atom.workspace.open('test.html.ejs')
editor = atom.workspace.getActiveTextEditor()
editor.setText('<span><% a = ""; %></span>')
})
waitsForPromise(() => {
return atom.packages.activatePackage('language-javascript')
})
runs(() => {
editor.setCursorBufferPosition([0, editor.getText().indexOf('""') + 1])
expect(() => getCompletions()).not.toThrow()
})
})
})

View File

@ -0,0 +1,73 @@
const path = require('path')
const fs = require('fs')
const request = require('request')
const fetchTagDescriptions = require('./fetch-tag-docs')
const fetchGlobalAttributeDescriptions = require('./fetch-global-attribute-docs')
const TagsURL = 'https://raw.githubusercontent.com/adobe/brackets/master/src/extensions/default/HTMLCodeHints/HtmlTags.json'
const AttributesURL = 'https://raw.githubusercontent.com/adobe/brackets/master/src/extensions/default/HTMLCodeHints/HtmlAttributes.json'
const tagsPromise = new Promise((resolve) => {
request({json: true, url: TagsURL}, (error, response, tags) => {
if (error != null) {
console.error(error.message)
resolve(null)
}
if (response.statusCode !== 200) {
console.error(`Request for HtmlTags.json failed: ${response.statusCode}`)
resolve(null)
}
for (let tag in tags) {
const options = tags[tag]
if ((options.attributes != null ? options.attributes.length : undefined) === 0) { delete options.attributes }
}
resolve(tags)
})
})
const tagDescriptionsPromise = fetchTagDescriptions()
const attributesPromise = new Promise((resolve) => {
return request({json: true, url: AttributesURL}, (error, response, attributes) => {
if (error != null) {
console.error(error.message)
resolve(null)
}
if (response.statusCode !== 200) {
console.error(`Request for HtmlAttributes.json failed: ${response.statusCode}`)
resolve(null)
}
for (let attribute in attributes) {
const options = attributes[attribute]
if ((options.attribOption != null ? options.attribOption.length : undefined) === 0) { delete options.attribOption }
}
resolve(attributes)
})
})
const globalAttributeDescriptionsPromise = fetchGlobalAttributeDescriptions()
Promise.all([tagsPromise, tagDescriptionsPromise, attributesPromise, globalAttributeDescriptionsPromise]).then((values) => {
const tags = values[0]
const tagDescriptions = values[1]
const attributes = values[2]
const attributeDescriptions = values[3]
for (let tag in tags) {
tags[tag].description = tagDescriptions[tag]
}
for (let attribute in attributes) {
const options = attributes[attribute]
if (options.global) { attributes[attribute].description = attributeDescriptions[attribute] }
}
const completions = {tags, attributes}
fs.writeFileSync(path.join(__dirname, 'completions.json'), `${JSON.stringify(completions, null, ' ')}\n`)
})

View File

@ -0,0 +1 @@
spec/fixtures

3
packages/autocomplete-plus/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
node_modules
.DS_Store
npm-debug.log

View File

@ -0,0 +1,16 @@
pairs:
ns: Nathan Sobo; nathan
cj: Corey Johnson; cj
dg: David Graham; dgraham
ks: Kevin Sawicki; kevin
jc: Jerry Cheung; jerry
bl: Brian Lopez; brian
jp: Justin Palmer; justin
gt: Garen Torikian; garen
mc: Matt Colyer; mcolyer
bo: Ben Ogle; benogle
jr: Jason Rudolph; jasonrudolph
jl: Jessica Lord; jlord
email:
domain: github.com
#global: true

View File

@ -0,0 +1,109 @@
# Autocomplete+ package
Displays possible autocomplete suggestions on keystroke (or manually by typing `ctrl-space`) and inserts a suggestion in the editor if confirmed.
![autocomplete+](https://cloud.githubusercontent.com/assets/744740/7656861/9fb8bcc4-faea-11e4-9814-9dca218ded93.png)
[Changelog](https://github.com/atom/autocomplete-plus/releases)
## Installation
`autocomplete+` is bundled with Pulsar. You don't have to do anything to install it.
## Providers
`autocomplete+` has a powerful autocomplete provider API, allowing provider authors to add language-specific behavior to this package.
You should *definitely* install additional providers (the default provider bundled with this package is somewhat crude): https://github.com/atom/autocomplete-plus/wiki/Autocomplete-Providers
## Usage
Just type some stuff, and autocomplete+ will automatically show you some suggestions.
Press `UP` and `DOWN` to select another suggestion, press `TAB` or `ENTER` to confirm your selection. You can change the default keymap in `Preferences`:
* Keymap For Confirming A Suggestion
Additionally, the confirm keymap can be customized in your keymap.cson:
```coffeescript
'atom-text-editor.autocomplete-active':
'tab': 'unset!'
'ctrl-shift-a': 'autocomplete-plus:confirm'
```
If setting custom keybindings, use the `none` setting for the confirmation keymap. All this option does is not set any other keybindings. This allows the `TAB` and `ENTER` keys to be used like normal, without side effects.
### Remapping Movement Commands
By default, autocomplete-plus commandeers the editor's core movement commands when the suggestion list is open. You may want to change these movement commands to use your own keybindings.
First you need to set the `autocomplete-plus.useCoreMovementCommands` setting to `false`, which you can do from the `autocomplete-plus` settings in the settings view.
![core-movement](https://cloud.githubusercontent.com/assets/69169/8839134/72a9c7e6-3087-11e5-9d1f-8d3d15961327.jpg)
Or by adding this to your config file:
```coffee
"*":
"autocomplete-plus":
"useCoreMovementCommands": false
```
Then add these to your keymap file:
```coffeescript
'body atom-text-editor.autocomplete-active':
'ctrl-p': 'autocomplete-plus:move-up'
'ctrl-n': 'autocomplete-plus:move-down'
'pageup': 'autocomplete-plus:page-up'
'pagedown': 'autocomplete-plus:page-down'
'home': 'autocomplete-plus:move-to-top'
'end': 'autocomplete-plus:move-to-bottom'
```
## Features
* Shows suggestions while typing
* Includes a default provider (`SymbolProvider`):
* Wordlist generation happens when you open a file, while editing the file, and on save
* Suggestions are calculated using `fuzzaldrin`
* Exposes a provider API which can be used to extend the functionality of the package and provide targeted / contextually correct suggestions
* Disable autocomplete for file(s) via blacklisting, e.g. `*.md` to blacklist Markdown files
* Disable autocomplete for editor scope(s) via blacklisting
* Expands a snippet if an autocomplete+ provider includes one in a suggestion
* Allows external editors to register for autocompletions
## Provider API
Great autocomplete depends on having great autocomplete providers. If there is not already a great provider for the language / grammar that you are working in, please consider creating a provider.
[Read the `Provider API` documentation](https://github.com/atom/autocomplete-plus/wiki/Provider-API) to learn how to create a new autocomplete provider.
## `SymbolProvider` Configuration
If the default `SymbolProvider` is missing useful information for the language / grammar you're working with, please take a look at the [`SymbolProvider` Config API](https://github.com/atom/autocomplete-plus/wiki/SymbolProvider-Config-API).
## The `watchEditor` API
The `watchEditor` method on the `AutocompleteManager` object is exposed as a [provided service](http://flight-manual.atom.io/behind-atom/sections/interacting-with-other-packages-via-services/), named `autocomplete.watchEditor`. The method allows external editors to register for autocompletions from providers with a given set of labels. Disposing the returned object will undo this request. External packages can access this service with the following code.
In `package.json`:
```
{
"consumedServices": {
"autocomplete.watchEditor": {
"versions": {
"1.0.0": "consumeAutocompleteWatchEditor"
}
}
}
}
```
In the main module file:
```
consumeAutocompleteWatchEditor(watchEditor) {
this.autocompleteDisposable = watchEditor(
this.editor, ['symbol-provider']
)
}
```

View File

@ -0,0 +1,37 @@
{
"max_line_length": {
"level": "ignore"
},
"no_empty_param_list": {
"level": "error"
},
"arrow_spacing": {
"level": "error"
},
"no_interpolation_in_single_quotes": {
"level": "error"
},
"no_debugger": {
"level": "error"
},
"prefer_english_operator": {
"level": "error"
},
"colon_assignment_spacing": {
"spacing": {
"left": 0,
"right": 1
},
"level": "error"
},
"braces_spacing": {
"spaces": 0,
"level": "error"
},
"spacing_after_comma": {
"level": "error"
},
"no_stand_alone_at": {
"level": "error"
}
}

View File

@ -0,0 +1,6 @@
'atom-text-editor':
'ctrl-space': 'autocomplete-plus:activate'
'atom-text-editor.autocomplete-active':
'escape': 'autocomplete-plus:cancel'
'f1': 'autocomplete-plus:navigate-to-description-more-link'

View File

@ -0,0 +1,799 @@
const {CompositeDisposable, Disposable, Point, Range} = require('atom')
const path = require('path')
const fuzzaldrin = require('fuzzaldrin')
const fuzzaldrinPlus = require('fuzzaldrin-plus')
const ProviderManager = require('./provider-manager')
const SuggestionList = require('./suggestion-list')
const {UnicodeLetters} = require('./unicode-helpers')
const getAdditionalWordCharacters = require('./get-additional-word-characters')
const MAX_LEGACY_PREFIX_LENGTH = 80
const wordCharacterRegexCache = new Map()
// Deferred requires
let minimatch = null
let grim = null
module.exports =
class AutocompleteManager {
constructor () {
this.autosaveEnabled = false
this.backspaceTriggersAutocomplete = true
this.autoConfirmSingleSuggestionEnabled = true
this.bracketMatcherPairs = ['()', '[]', '{}', '""', "''", '``', '“”', '', '«»', '']
this.buffer = null
this.compositionInProgress = false
this.disposed = false
this.editor = null
this.editorLabels = null
this.editorSubscriptions = null
this.editorView = null
this.providerManager = null
this.ready = false
this.subscriptions = null
this.suggestionList = null
this.suppressForClasses = []
this.shouldDisplaySuggestions = false
this.prefixRegex = null
this.wordPrefixRegex = null
this.updateCurrentEditor = this.updateCurrentEditor.bind(this)
this.handleCommands = this.handleCommands.bind(this)
this.findSuggestions = this.findSuggestions.bind(this)
this.getSuggestionsFromProviders = this.getSuggestionsFromProviders.bind(this)
this.displaySuggestions = this.displaySuggestions.bind(this)
this.hideSuggestionList = this.hideSuggestionList.bind(this)
this.showOrHideSuggestionListForBufferChanges = this.showOrHideSuggestionListForBufferChanges.bind(this)
this.providerManager = new ProviderManager()
this.suggestionList = new SuggestionList()
this.watchedEditors = new WeakSet()
}
initialize () {
this.subscriptions = new CompositeDisposable()
this.providerManager.initialize()
this.suggestionList.initialize()
this.subscriptions.add(atom.config.observe('autocomplete-plus.enableExtendedUnicodeSupport', enableExtendedUnicodeSupport => {
if (enableExtendedUnicodeSupport) {
this.prefixRegex = new RegExp(`(['"~\`!@#\\$%^&*\\(\\)\\{\\}\\[\\]=+,/\\?>])?(([${UnicodeLetters}\\d_]+[${UnicodeLetters}\\d_-]*)|([.:;[{(< ]+))$`)
this.wordPrefixRegex = new RegExp(`^[${UnicodeLetters}\\d_]+[${UnicodeLetters}\\d_-]*$`)
} else {
this.prefixRegex = /(\b|['"~`!@#$%^&*(){}[\]=+,/?>])((\w+[\w-]*)|([.:;[{(< ]+))$/
this.wordPrefixRegex = /^\w+[\w-]*$/
}
}
))
this.subscriptions.add(this.providerManager)
this.handleEvents()
this.handleCommands()
this.subscriptions.add(this.suggestionList) // We're adding this last so it is disposed after events
this.ready = true
}
setSnippetsManager (snippetsManager) {
this.snippetsManager = snippetsManager
}
updateCurrentEditor (currentEditor, labels) {
if (currentEditor === this.editor) { return }
if (this.editorSubscriptions) {
this.editorSubscriptions.dispose()
}
this.editorSubscriptions = null
// Stop tracking editor + buffer
this.editor = null
this.editorView = null
this.buffer = null
this.isCurrentFileBlackListedCache = null
if (!this.editorIsValid(currentEditor)) { return }
// Track the new editor, editorView, and buffer and set
// the labels for its providers.
this.editor = currentEditor
this.editorLabels = labels
this.editorView = atom.views.getView(this.editor)
this.buffer = this.editor.getBuffer()
this.editorSubscriptions = new CompositeDisposable()
// Subscribe to buffer events:
this.editorSubscriptions.add(this.buffer.onDidSave((e) => { this.bufferSaved(e) }))
this.editorSubscriptions.add(this.buffer.onDidChangeText(this.showOrHideSuggestionListForBufferChanges))
// Watch IME Events To Allow IME To Function Without The Suggestion List Showing
const compositionStart = () => {
this.compositionInProgress = true
}
const compositionEnd = () => {
this.compositionInProgress = false
}
this.editorView.addEventListener('compositionstart', compositionStart)
this.editorView.addEventListener('compositionend', compositionEnd)
this.editorSubscriptions.add(new Disposable(() => {
if (this.editorView) {
this.editorView.removeEventListener('compositionstart', compositionStart)
this.editorView.removeEventListener('compositionend', compositionEnd)
}
}))
// Subscribe to editor events:
// Close the overlay when the cursor moved without changing any text
this.editorSubscriptions.add(this.editor.onDidChangeCursorPosition((e) => { this.cursorMoved(e) }))
return this.editorSubscriptions.add(this.editor.onDidChangePath(() => {
this.isCurrentFileBlackListedCache = null
}))
}
editorIsValid (editor) {
// TODO: remove conditional when `isTextEditor` is shipped.
if (typeof atom.workspace.isTextEditor === 'function') {
return atom.workspace.isTextEditor(editor)
} else {
if (!editor) { return false }
// Should we disqualify TextEditors with the Grammar text.plain.null-grammar?
return (editor.getText != null)
}
}
// Makes the autocomplete manager watch the `editor`.
// When the watched `editor` is focused, it will provide autocompletions from
// providers with the given `labels`.
//
// Returns a {Disposable} to stop watching the `editor`.
watchEditor (editor, labels) {
if (this.watchedEditors.has(editor)) return
let view = atom.views.getView(editor)
if (view.hasFocus()) {
this.updateCurrentEditor(editor, labels)
}
let focusListener = (element) => this.updateCurrentEditor(editor, labels)
view.addEventListener('focus', focusListener)
let blurListener = (element) => this.hideSuggestionList()
view.addEventListener('blur', blurListener)
let disposable = new Disposable(() => {
view.removeEventListener('focus', focusListener)
view.removeEventListener('blur', blurListener)
if (this.editor === editor) {
this.updateCurrentEditor(null)
}
})
this.watchedEditors.add(editor)
this.subscriptions.add(disposable)
return new Disposable(() => {
disposable.dispose()
if (this.subscriptions != null) {
this.subscriptions.remove(disposable)
}
this.watchedEditors.delete(editor)
})
}
handleEvents () {
this.subscriptions.add(atom.workspace.observeTextEditors((editor) => {
const disposable = this.watchEditor(editor, ['workspace-center'])
editor.onDidDestroy(() => disposable.dispose())
}))
// Watch config values
this.subscriptions.add(atom.config.observe('autosave.enabled', (value) => { this.autosaveEnabled = value }))
this.subscriptions.add(atom.config.observe('autocomplete-plus.backspaceTriggersAutocomplete', (value) => { this.backspaceTriggersAutocomplete = value }))
this.subscriptions.add(atom.config.observe('autocomplete-plus.enableAutoActivation', (value) => { this.autoActivationEnabled = value }))
this.subscriptions.add(atom.config.observe('autocomplete-plus.enableAutoConfirmSingleSuggestion', (value) => { this.autoConfirmSingleSuggestionEnabled = value }))
this.subscriptions.add(atom.config.observe('autocomplete-plus.consumeSuffix', (value) => { this.consumeSuffix = value }))
this.subscriptions.add(atom.config.observe('autocomplete-plus.useAlternateScoring', (value) => { this.useAlternateScoring = value }))
this.subscriptions.add(atom.config.observe('autocomplete-plus.fileBlacklist', (value) => {
if (value) {
this.fileBlacklist = value.map((s) => { return s.trim() })
}
this.isCurrentFileBlackListedCache = null
}))
this.subscriptions.add(atom.config.observe('autocomplete-plus.suppressActivationForEditorClasses', value => {
this.suppressForClasses = []
for (let i = 0; i < value.length; i++) {
const selector = value[i]
const classes = (selector.trim().split('.').filter((className) => className.trim()).map((className) => className.trim()))
if (classes.length) { this.suppressForClasses.push(classes) }
}
}))
// Handle events from suggestion list
this.subscriptions.add(this.suggestionList.onDidConfirm((e) => { this.confirm(e) }))
this.subscriptions.add(this.suggestionList.onDidCancel(this.hideSuggestionList))
this.subscriptions.add(this.suggestionList.onDidSelect(suggestion => { this.getDetailsOnSelect(suggestion) }))
}
handleCommands () {
return this.subscriptions.add(atom.commands.add('atom-text-editor', {
'autocomplete-plus:activate': (event) => {
this.shouldDisplaySuggestions = true
let activatedManually = true
if (event.detail && event.detail.activatedManually !== null && typeof event.detail.activatedManually !== 'undefined') {
activatedManually = event.detail.activatedManually
}
this.findSuggestions(activatedManually)
},
'autocomplete-plus:navigate-to-description-more-link': () => {
let suggestionListView = atom.views.getView(this.editor)
let descriptionContainer = suggestionListView.querySelector('.suggestion-description')
if (descriptionContainer !== null && descriptionContainer.style.display === 'block') {
let descriptionMoreLink = descriptionContainer.querySelector('.suggestion-description-more-link')
require('electron').shell.openExternal(descriptionMoreLink.href)
}
}
}))
}
// Private: Finds suggestions for the current prefix, sets the list items,
// positions the overlay and shows it
findSuggestions (activatedManually) {
if (this.disposed) { return }
if ((this.providerManager == null) || (this.editor == null) || (this.buffer == null)) { return }
if (this.isCurrentFileBlackListed()) { return }
const cursor = this.editor.getLastCursor()
if (cursor == null) { return }
const bufferPosition = cursor.getBufferPosition()
const scopeDescriptor = cursor.getScopeDescriptor()
const prefix = this.getPrefix(this.editor, bufferPosition, scopeDescriptor) // Passed to providers with API version >= 4.0.0
const legacyPrefix = this.getLegacyPrefix(this.editor, bufferPosition) // Passed to providers with API version < 4.0.0
return this.getSuggestionsFromProviders({editor: this.editor, bufferPosition, scopeDescriptor, prefix, legacyPrefix, activatedManually})
}
getSuggestionsFromProviders (options) {
let suggestionsPromise
const providers = this.providerManager.applicableProviders(this.editorLabels, options.scopeDescriptor)
const providerPromises = []
providers.forEach(provider => {
const apiVersion = this.providerManager.apiVersionForProvider(provider)
let getSuggestions
let upgradedOptions
if (apiVersion === 1) {
getSuggestions = provider.requestHandler.bind(provider)
upgradedOptions = {
editor: options.editor,
prefix: options.prefix,
bufferPosition: options.bufferPosition,
position: options.bufferPosition,
scope: options.scopeDescriptor,
scopeChain: options.scopeDescriptor.getScopeChain(),
buffer: options.editor.getBuffer(),
cursor: options.editor.getLastCursor()
}
} else {
getSuggestions = provider.getSuggestions.bind(provider)
if (apiVersion < 4) {
upgradedOptions = Object.assign({}, options)
upgradedOptions.prefix = options.legacyPrefix
} else {
upgradedOptions = Object.assign({}, options)
delete upgradedOptions.legacyPrefix
}
}
return providerPromises.push(Promise.resolve(getSuggestions(upgradedOptions)).then(providerSuggestions => {
if (providerSuggestions == null) { return }
// TODO API: remove upgrading when 1.0 support is removed
let hasDeprecations = false
if (apiVersion > 1 && providerSuggestions.length) {
hasDeprecations = this.deprecateForSuggestion(provider, providerSuggestions[0])
}
if (hasDeprecations || apiVersion === 1) {
providerSuggestions = providerSuggestions.map((suggestion) => {
const newSuggestion = {
text: suggestion.text != null ? suggestion.text : suggestion.word,
snippet: suggestion.snippet,
replacementPrefix: suggestion.replacementPrefix != null ? suggestion.replacementPrefix : suggestion.prefix,
className: suggestion.className,
type: suggestion.type
}
if ((newSuggestion.rightLabelHTML == null) && suggestion.renderLabelAsHtml) { newSuggestion.rightLabelHTML = suggestion.label }
if ((newSuggestion.rightLabel == null) && !suggestion.renderLabelAsHtml) { newSuggestion.rightLabel = suggestion.label }
return newSuggestion
})
}
let hasEmpty = false // Optimization: only create another array when there are empty items
for (let i = 0; i < providerSuggestions.length; i++) {
const suggestion = providerSuggestions[i]
if (!suggestion.snippet && !suggestion.text) { hasEmpty = true }
// Suggestions are mutable and are updated with a new replacement prefix. In order to
// distinguish between suggestion that had original prefix and assigned one, we use
// `isPrefixModified` flag. If it is `true`, we reset replacement prefix.
if (suggestion.replacementPrefix == null || !!suggestion.isPrefixModified) {
if (apiVersion < 4) {
suggestion.replacementPrefix = this.wordPrefixRegex.test(options.prefix) ? options.prefix : ''
} else {
suggestion.replacementPrefix = options.prefix
}
suggestion.isPrefixModified = true
}
suggestion.provider = provider
}
if (hasEmpty) {
const res = []
for (const s of providerSuggestions) {
if (s.snippet || s.text) {
res.push(s)
}
}
providerSuggestions = res
}
if (provider.filterSuggestions) {
providerSuggestions = this.filterSuggestions(providerSuggestions, options)
}
return providerSuggestions
}))
})
if (!providerPromises || !providerPromises.length) {
return
}
suggestionsPromise = Promise.all(providerPromises)
this.currentSuggestionsPromise = suggestionsPromise
return this.currentSuggestionsPromise
.then(this.mergeSuggestionsFromProviders)
.then(suggestions => {
if (this.currentSuggestionsPromise !== suggestionsPromise) { return }
if (options.activatedManually && this.shouldDisplaySuggestions && this.autoConfirmSingleSuggestionEnabled && suggestions.length === 1) {
// When there is one suggestion in manual mode, just confirm it
return this.confirm(suggestions[0])
} else {
return this.displaySuggestions(suggestions, options)
}
}
)
}
filterSuggestions (suggestions, {prefix}) {
const results = []
const fuzzaldrinProvider = this.useAlternateScoring ? fuzzaldrinPlus : fuzzaldrin
for (let i = 0; i < suggestions.length; i++) {
// sortScore mostly preserves in the original sorting. The function is
// chosen such that suggestions with a very high match score can break out.
let score
const suggestion = suggestions[i]
suggestion.sortScore = Math.max((-i / 10) + 3, 0) + 1
suggestion.score = null
const text = (suggestion.snippet || suggestion.text)
const suggestionPrefix = suggestion.replacementPrefix != null ? suggestion.replacementPrefix : prefix
const prefixIsEmpty = !suggestionPrefix || suggestionPrefix === ' '
const firstCharIsMatch = !prefixIsEmpty && suggestionPrefix[0].toLowerCase() === text[0].toLowerCase()
if (prefixIsEmpty) {
results.push(suggestion)
}
if (firstCharIsMatch && (score = fuzzaldrinProvider.score(text, suggestionPrefix)) > 0) {
suggestion.score = score * suggestion.sortScore
results.push(suggestion)
}
}
results.sort(this.reverseSortOnScoreComparator)
return results
}
reverseSortOnScoreComparator (a, b) {
let bscore = b.score
if (!bscore) {
bscore = b.sortScore
}
let ascore = a.score
if (!ascore) {
ascore = b.sortScore
}
return bscore - ascore
}
// providerSuggestions - array of arrays of suggestions provided by all called providers
mergeSuggestionsFromProviders (providerSuggestions) {
return providerSuggestions.reduce((suggestions, providerSuggestions) => {
if (providerSuggestions && providerSuggestions.length) {
suggestions = suggestions.concat(providerSuggestions)
}
return suggestions
}, [])
}
deprecateForSuggestion (provider, suggestion) {
let hasDeprecations = false
if (suggestion.word != null) {
hasDeprecations = true
if (typeof grim === 'undefined' || grim === null) { grim = require('grim') }
grim.deprecate(`Autocomplete provider '${provider.constructor.name}(${provider.id})'
returns suggestions with a \`word\` attribute.
The \`word\` attribute is now \`text\`.
See https://github.com/atom/autocomplete-plus/wiki/Provider-API`
)
}
if (suggestion.prefix != null) {
hasDeprecations = true
if (typeof grim === 'undefined' || grim === null) { grim = require('grim') }
grim.deprecate(`Autocomplete provider '${provider.constructor.name}(${provider.id})'
returns suggestions with a \`prefix\` attribute.
The \`prefix\` attribute is now \`replacementPrefix\` and is optional.
See https://github.com/atom/autocomplete-plus/wiki/Provider-API`
)
}
if (suggestion.label != null) {
hasDeprecations = true
if (typeof grim === 'undefined' || grim === null) { grim = require('grim') }
grim.deprecate(`Autocomplete provider '${provider.constructor.name}(${provider.id})'
returns suggestions with a \`label\` attribute.
The \`label\` attribute is now \`rightLabel\` or \`rightLabelHTML\`.
See https://github.com/atom/autocomplete-plus/wiki/Provider-API`
)
}
if (suggestion.onWillConfirm != null) {
hasDeprecations = true
if (typeof grim === 'undefined' || grim === null) { grim = require('grim') }
grim.deprecate(`Autocomplete provider '${provider.constructor.name}(${provider.id})'
returns suggestions with a \`onWillConfirm\` callback.
The \`onWillConfirm\` callback is no longer supported.
See https://github.com/atom/autocomplete-plus/wiki/Provider-API`
)
}
if (suggestion.onDidConfirm != null) {
hasDeprecations = true
if (typeof grim === 'undefined' || grim === null) { grim = require('grim') }
grim.deprecate(`Autocomplete provider '${provider.constructor.name}(${provider.id})'
returns suggestions with a \`onDidConfirm\` callback.
The \`onDidConfirm\` callback is now a \`onDidInsertSuggestion\` callback on the provider itself.
See https://github.com/atom/autocomplete-plus/wiki/Provider-API`
)
}
return hasDeprecations
}
displaySuggestions (suggestions, options) {
switch (atom.config.get('autocomplete-plus.similarSuggestionRemoval')) {
case 'textOrSnippet': {
suggestions = this.getUniqueSuggestions(suggestions, (suggestion) => suggestion.text + suggestion.snippet)
break
}
}
if (this.shouldDisplaySuggestions && suggestions.length) {
return this.showSuggestionList(suggestions, options)
} else {
return this.hideSuggestionList()
}
}
getUniqueSuggestions (suggestions, uniqueKeyFunction) {
const seen = {}
const result = []
for (let i = 0; i < suggestions.length; i++) {
const suggestion = suggestions[i]
const val = uniqueKeyFunction(suggestion)
if (!seen[val]) {
result.push(suggestion)
seen[val] = true
}
}
return result
}
getPrefix (editor, position, scopeDescriptor) {
const wordCharacterRegex = this.getWordCharacterRegex(scopeDescriptor)
const line = editor.getBuffer().getTextInRange([[position.row, 0], position])
let startColumn = position.column
while (startColumn > 0) {
let prevChar = line[startColumn - 1]
if (wordCharacterRegex.test(prevChar)) {
startColumn--
} else {
break
}
}
return line.slice(startColumn)
}
getWordCharacterRegex (scopeDescriptor) {
const additionalWordChars = getAdditionalWordCharacters(scopeDescriptor)
let regex = wordCharacterRegexCache.get(additionalWordChars)
if (!regex) {
regex = new RegExp(`[${UnicodeLetters}${additionalWordChars.replace(']', '\\]')}\\d]`)
wordCharacterRegexCache.set(additionalWordChars, regex)
}
return regex
}
getLegacyPrefix (editor, bufferPosition) {
const {row, column} = bufferPosition
const line = editor.getTextInRange(new Range(
new Point(row, Math.max(0, column - MAX_LEGACY_PREFIX_LENGTH)),
bufferPosition
))
const prefix = this.prefixRegex.exec(line)
if (!prefix || !prefix[2] || prefix[2].length === MAX_LEGACY_PREFIX_LENGTH) return ''
return prefix[2]
}
// Private: Gets called when the user successfully confirms a suggestion
//
// match - An {Object} representing the confirmed suggestion
confirm (suggestion) {
if ((this.editor == null) || (suggestion == null) || !!this.disposed) { return }
const apiVersion = this.providerManager.apiVersionForProvider(suggestion.provider)
const triggerPosition = this.editor.getLastCursor().getBufferPosition()
// TODO API: Remove as this is no longer used
if (suggestion.onWillConfirm) {
suggestion.onWillConfirm()
}
const selections = this.editor.getSelections()
if (selections && selections.length) {
for (const s of selections) {
if (s && s.clear) {
s.clear()
}
}
}
this.hideSuggestionList()
this.replaceTextWithMatch(suggestion)
if (apiVersion > 1) {
if (suggestion.provider && suggestion.provider.onDidInsertSuggestion) {
suggestion.provider.onDidInsertSuggestion({editor: this.editor, suggestion, triggerPosition})
}
} else {
if (suggestion.onDidConfirm) {
suggestion.onDidConfirm()
}
}
}
getDetailsOnSelect (suggestion) {
if (suggestion != null && suggestion.provider && suggestion.provider.getSuggestionDetailsOnSelect) {
Promise.resolve(suggestion.provider.getSuggestionDetailsOnSelect(suggestion))
.then(detailedSuggestion => {
this.suggestionList.replaceItem(suggestion, detailedSuggestion)
})
}
}
showSuggestionList (suggestions, options) {
if (this.disposed) { return }
this.suggestionList.changeItems(suggestions)
return this.suggestionList.show(this.editor, options)
}
hideSuggestionList () {
if (this.disposed) { return }
this.suggestionList.changeItems(null)
this.suggestionList.hide()
this.shouldDisplaySuggestions = false
}
requestHideSuggestionList (command) {
if (this.hideTimeout == null) {
this.hideTimeout = setTimeout(() => {
this.hideSuggestionList()
this.hideTimeout = null
}, 0)
}
this.shouldDisplaySuggestions = false
}
cancelHideSuggestionListRequest () {
clearTimeout(this.hideTimeout)
this.hideTimeout = null
}
// Private: Replaces the current prefix with the given match.
//
// match - The match to replace the current prefix with
replaceTextWithMatch (suggestion) {
if (this.editor == null) { return }
const cursors = this.editor.getCursors()
if (cursors == null) { return }
return this.editor.transact(() => {
for (let i = 0; i < cursors.length; i++) {
const cursor = cursors[i]
const endPosition = cursor.getBufferPosition()
const beginningPosition = [endPosition.row, endPosition.column - suggestion.replacementPrefix.length]
if (this.editor.getTextInBufferRange([beginningPosition, endPosition]) === suggestion.replacementPrefix) {
const suffix = this.consumeSuffix ? this.getSuffix(this.editor, endPosition, suggestion) : ''
if (suffix.length) { cursor.moveRight(suffix.length) }
cursor.selection.selectLeft(suggestion.replacementPrefix.length + suffix.length)
if ((suggestion.snippet != null) && (this.snippetsManager != null)) {
this.snippetsManager.insertSnippet(suggestion.snippet, this.editor, cursor)
} else {
cursor.selection.insertText(suggestion.text != null ? suggestion.text : suggestion.snippet, {
autoIndentNewline: this.editor.shouldAutoIndent(),
autoDecreaseIndent: this.editor.shouldAutoIndent()
})
}
}
}
}
)
}
getSuffix (editor, bufferPosition, suggestion) {
// This just chews through the suggestion and tries to match the suggestion
// substring with the lineText starting at the cursor. There is probably a
// more efficient way to do this.
let suffix = (suggestion.snippet != null ? suggestion.snippet : suggestion.text)
const endPosition = [bufferPosition.row, bufferPosition.column + suffix.length]
const endOfLineText = editor.getTextInBufferRange([bufferPosition, endPosition])
const nonWordCharacters = new Set(atom.config.get('editor.nonWordCharacters').split(''))
while (suffix) {
if (endOfLineText.startsWith(suffix) && !nonWordCharacters.has(suffix[0])) { break }
suffix = suffix.slice(1)
}
return suffix
}
// Private: Checks whether the current file is blacklisted.
//
// Returns {Boolean} that defines whether the current file is blacklisted
isCurrentFileBlackListed () {
// minimatch is slow. Not necessary to do this computation on every request for suggestions
let left
if (this.isCurrentFileBlackListedCache != null) { return this.isCurrentFileBlackListedCache }
if ((this.fileBlacklist == null) || this.fileBlacklist.length === 0) {
this.isCurrentFileBlackListedCache = false
return this.isCurrentFileBlackListedCache
}
if (typeof minimatch === 'undefined' || minimatch === null) { minimatch = require('minimatch') }
const fileName = path.basename((left = this.buffer.getPath()) != null ? left : '')
for (let i = 0; i < this.fileBlacklist.length; i++) {
const blacklistGlob = this.fileBlacklist[i]
if (minimatch(fileName, blacklistGlob)) {
this.isCurrentFileBlackListedCache = true
return this.isCurrentFileBlackListedCache
}
}
this.isCurrentFileBlackListedCache = false
return this.isCurrentFileBlackListedCache
}
// Private: Gets called when the content has been modified
requestNewSuggestions () {
let delay = atom.config.get('autocomplete-plus.autoActivationDelay')
if (this.delayTimeout != null) {
clearTimeout(this.delayTimeout)
}
if (delay) {
this.delayTimeout = setTimeout(this.findSuggestions, delay)
} else {
this.findSuggestions()
}
this.shouldDisplaySuggestions = true
}
cancelNewSuggestionsRequest () {
if (this.delayTimeout != null) {
clearTimeout(this.delayTimeout)
}
this.shouldDisplaySuggestions = false
}
// Private: Gets called when the cursor has moved. Cancels the autocompletion if
// the text has not been changed.
//
// data - An {Object} containing information on why the cursor has been moved
cursorMoved ({textChanged}) {
// The delay is a workaround for the backspace case. The way atom implements
// backspace is to select left 1 char, then delete. This results in a
// cursorMoved event with textChanged == false. So we delay, and if the
// bufferChanged handler decides to show suggestions, it will cancel the
// hideSuggestionList request. If there is no bufferChanged event,
// suggestionList will be hidden.
if (!textChanged) this.requestHideSuggestionList()
}
// Private: Gets called when the user saves the document. Cancels the
// autocompletion.
bufferSaved () {
if (!this.autosaveEnabled) { return this.hideSuggestionList() }
}
showOrHideSuggestionListForBufferChanges ({changes}) {
if (this.disposed) return
const lastCursorPosition = this.editor.getLastCursor().getBufferPosition()
const changeOccurredNearLastCursor = changes.some(({newRange}) => {
return newRange.containsPoint(lastCursorPosition)
})
if (!changeOccurredNearLastCursor) return
let shouldActivate = false
if (this.autoActivationEnabled || this.suggestionList.isActive() && !this.compositionInProgress) {
shouldActivate = changes.some(({oldText, newText}) => {
if (this.autoActivationEnabled || this.suggestionList.isActive()) {
if (newText.length > 0) {
// Activate on space, a non-whitespace character, or a bracket-matcher pair.
if (newText === ' ' ||
newText.trim().length === 1 ||
(newText.length === 2 && this.bracketMatcherPairs.includes(newText))) return true
} else if (oldText.length > 0 && (this.backspaceTriggersAutocomplete || this.suggestionList.isActive())) {
// Suggestion list must be either active or backspaceTriggersAutocomplete must be true for activation to occur.
// Activate on removal of a space, a non-whitespace character, or a bracket-matcher pair.
if (oldText === ' ' ||
oldText.trim().length === 1 ||
(oldText.length === 2 && this.bracketMatcherPairs.includes(oldText))) return true
}
}
})
if (shouldActivate && this.shouldSuppressActivationForEditorClasses()) shouldActivate = false
}
if (shouldActivate) {
this.cancelHideSuggestionListRequest()
this.requestNewSuggestions()
} else {
this.cancelNewSuggestionsRequest()
this.hideSuggestionList()
}
}
shouldSuppressActivationForEditorClasses () {
for (let i = 0; i < this.suppressForClasses.length; i++) {
const classNames = this.suppressForClasses[i]
let containsCount = 0
for (let j = 0; j < classNames.length; j++) {
const className = classNames[j]
if (this.editorView.classList.contains(className)) { containsCount += 1 }
}
if (containsCount === classNames.length) { return true }
}
return false
}
// Public: Clean up, stop listening to events
dispose () {
this.hideSuggestionList()
this.disposed = true
this.ready = false
if (this.editorSubscriptions) {
this.editorSubscriptions.dispose()
}
this.editorSubscriptions = null
if (this.subscriptions) {
this.subscriptions.dispose()
}
this.subscriptions = null
this.suggestionList = null
this.providerManager = null
}
}

View File

@ -0,0 +1,13 @@
const POSSIBLE_WORD_CHARACTERS = '/\\()"\':,.;<>~!@#$%^&*|+=[]{}`?_-…'.split('')
module.exports =
function getAdditionalWordCharacters (scopeDescriptor) {
const nonWordCharacters = atom.config.get('editor.nonWordCharacters', {scope: scopeDescriptor})
let result = atom.config.get('autocomplete-plus.extraWordCharacters', {scope: scopeDescriptor})
POSSIBLE_WORD_CHARACTERS.forEach(character => {
if (!nonWordCharacters.includes(character)) {
result += character
}
})
return result
}

View File

@ -0,0 +1,88 @@
const {CompositeDisposable} = require('atom')
const AutocompleteManager = require('./autocomplete-manager')
module.exports = {
subscriptions: null,
autocompleteManager: new AutocompleteManager(),
// Public: Creates AutocompleteManager instances for all active and future editors (soon, just a single AutocompleteManager)
activate () {
this.subscriptions = new CompositeDisposable()
if (!this.autocompleteManager) this.autocompleteManager = new AutocompleteManager()
this.subscriptions.add(this.autocompleteManager)
this.autocompleteManager.initialize()
},
// Public: Cleans everything up, removes all AutocompleteManager instances
deactivate () {
if (this.subscriptions) {
this.subscriptions.dispose()
}
this.subscriptions = null
this.autocompleteManager = null
},
provideWatchEditor () {
return this.autocompleteManager.watchEditor.bind(this.autocompleteManager)
},
consumeSnippets (snippetsManager) {
this.autocompleteManager.setSnippetsManager(snippetsManager)
},
/*
Section: Provider API
*/
// 1.0.0 API
// service - {provider: provider1}
consumeProvider_1 (service) {
if (!service || !service.provider) {
return
}
return this.consumeProvider([service.provider], 1)
},
// 1.1.0 API
// service - {providers: [provider1, provider2, ...]}
consumeProvider_1_1 (service) {
if (!service || !service.providers) {
return
}
return this.consumeProvider(service.providers, 1)
},
// 2.0.0 API
consumeProvider_2 (providers) {
return this.consumeProvider(providers, 2)
},
// 3.0.0 API
consumeProvider_3 (providers) {
return this.consumeProvider(providers, 3)
},
// 4.0.0 API Simplifies prefix computation
consumeProvider_4 (providers) {
return this.consumeProvider(providers, 4)
},
consumeProvider (providers, apiVersion = 3) {
if (!providers) {
return
}
if (providers && !Array.isArray(providers)) {
providers = [providers]
}
if (!providers.length > 0) {
return
}
const registrations = new CompositeDisposable()
for (let i = 0; i < providers.length; i++) {
const provider = providers[i]
registrations.add(this.autocompleteManager.providerManager.registerProvider(provider, apiVersion))
}
return registrations
}
}

View File

@ -0,0 +1,4 @@
'use babel'
const API_VERSION = Symbol('Private property: Semantic version of the service endpoint.')
export {API_VERSION}

Some files were not shown because too many files have changed in this diff Show More