Merge remote-tracking branch 'origin/master' into bump-electron-13-with-wasm-superstring

This commit is contained in:
Maurício Szabo 2023-04-10 18:13:36 -03:00
commit 69ae6ab3e2
1326 changed files with 136755 additions and 62185 deletions

View File

@ -1,12 +1,16 @@
env:
PYTHON_VERSION: 3.10
linux_task:
alias: linux
container:
image: node:lts-slim
image: node:16-slim
memory: 8G
install_deps_script:
prepare_script:
- apt-get update
- export DEBIAN_FRONTEND="noninteractive"
- apt-get install -y
ffmpeg
rpm
build-essential
git
@ -23,7 +27,9 @@ linux_task:
- git submodule init
- git submodule update
- sed -i -e "s/[0-9]*-dev/`date -u +%Y%m%d%H`/g" package.json
- yarn install || yarn install
install_script:
- yarn install --ignore-engines || yarn install --ignore-engines
build_script:
- yarn build
- yarn run build:apm
build_binary_script:
@ -31,6 +37,68 @@ linux_task:
binary_artifacts:
path: ./binaries/*
test_script:
- rm -R node_modules/electron; yarn install --check-files
- ./binaries/*AppImage --appimage-extract
- export BINARY_NAME='squashfs-root/pulsar'
- mkdir -p ./tests/videos
- Xvfb -screen 0 1024x768x24+32 :99 & nohup ffmpeg -video_size 1024x768 -f x11grab -i :99.0 ./tests/videos/out.mpg & DISPLAY=:99 PLAYWRIGHT_JUNIT_OUTPUT_NAME=report.xml npx playwright test --reporter=junit,list
always:
videos_artifacts:
path: ./tests/videos/**
junit_artifacts:
path: report.xml
type: text/xml
format: junit
arm_linux_task:
alias: linux
arm_container:
image: node:16-slim
memory: 8G
env:
USE_SYSTEM_FPM: 'true'
prepare_script:
- apt-get update
- export DEBIAN_FRONTEND="noninteractive"
- apt-get install -y
gnupg2
procps
curl
rpm
build-essential
git
libsecret-1-dev
fakeroot
libx11-dev
libxkbfile-dev
libgdk-pixbuf2.0-dev
libgtk-3-dev
libxss-dev
libasound2-dev
libnss3
xvfb
- gpg2 --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
- \curl -sSL https://get.rvm.io | bash -s stable
- source /etc/profile.d/rvm.sh
- rvm install ruby
- gem install fpm
- git submodule init
- git submodule update
- sed -i -e "s/[0-9]*-dev/`date -u +%Y%m%d%H`/g" package.json
install_script:
- yarn install --ignore-engines || yarn install --ignore-engines
build_script:
- yarn build
- yarn run build:apm
build_binary_script:
- source /etc/profile.d/rvm.sh
- yarn dist || yarn dist
binary_artifacts:
path: ./binaries/*
test_script:
- rm -R node_modules/electron; yarn install --check-files
- ./binaries/*AppImage --appimage-extract
- export BINARY_NAME='squashfs-root/pulsar'
- Xvfb :99 & DISPLAY=:99 PLAYWRIGHT_JUNIT_OUTPUT_NAME=report.xml npx playwright test --reporter=junit,list
always:
videos_artifacts:
@ -40,27 +108,41 @@ linux_task:
type: text/xml
format: junit
macos_task:
silicon_mac_task:
alias: mac
macos_instance:
image: ghcr.io/cirruslabs/macos-monterey-base:latest
image: ghcr.io/cirruslabs/macos-monterey-xcode:14
memory: 8G
install_deps_script:
- sed -i -e "s/[0-9]*-dev/`date -u +%Y%m%d%H`/g" package.json
- brew install node@16 yarn git python
env:
CSC_LINK: ENCRYPTED[0078015a03bb6cfdbd80113ae5bbb6f448fd4bbbc40efd81bf2cb1554373046b475a4d7c77e3e3e82ac1ce2f7e3d2da5]
CSC_KEY_PASSWORD: ENCRYPTED[82bb72653d39578035ed1860ab4978703d50bd326d925a146ff08782f987ceb37ac2d8dbace52dec2b0e2ef92debf097]
APPLEID: ENCRYPTED[549ce052bd5666dba5245f4180bf93b74ed206fe5e6e7c8f67a8596d3767c1f682b84e347b326ac318c62a07c8844a57]
APPLEID_PASSWORD: ENCRYPTED[774c3307fd3b62660ecf5beb8537a24498c76e8d90d7f28e5bc816742fd8954a34ffed13f9aa2d1faf66ce08b4496e6f]
TEAM_ID: ENCRYPTED[11f3fedfbaf4aff1859bf6c105f0437ace23d84f5420a2c1cea884fbfa43b115b7834a463516d50cb276d4c4d9128b49]
prepare_script:
- brew install node@16 yarn git python@$PYTHON_VERSION
- git submodule init
- git submodule update
- ln -s /opt/homebrew/bin/python3 /opt/homebrew/bin/python
- ln -s /opt/homebrew/bin/python$PYTHON_VERSION /opt/homebrew/bin/python
- export PATH="/opt/homebrew/bin:/opt/homebrew/opt/node@16/bin:$PATH"
- sed -i -e "s/[0-9]*-dev/`date -u +%Y%m%d%H`/g" package.json
install_script:
- export PATH="/opt/homebrew/bin:/opt/homebrew/opt/node@16/bin:$PATH"
- yarn install --ignore-engines || yarn install --ignore-engines
build_script:
- export PATH="/opt/homebrew/bin:/opt/homebrew/opt/node@16/bin:$PATH"
- yarn install || yarn install
- yarn build
- yarn run build:apm
build_arm_binary_script:
build_binary_script:
- export PATH="/opt/homebrew/bin:/opt/homebrew/opt/node@16/bin:$PATH"
- yarn dist || yarn dist
arm_binary_artifacts:
binary_artifacts:
path: ./binaries/*
test_script:
- export PATH="/opt/homebrew/bin:/opt/homebrew/opt/node@16/bin:$PATH"
- rm -R node_modules/electron; yarn install --check-files
- hdiutil mount binaries/Pulsar*dmg
- export BINARY_NAME=`ls /Volumes/Pulsar*/Pulsar.app/Contents/MacOS/Pulsar`
- PLAYWRIGHT_JUNIT_OUTPUT_NAME=report.xml npx playwright test --reporter=junit,list
always:
videos_artifacts:
@ -69,52 +151,90 @@ macos_task:
path: report.xml
type: text/xml
format: junit
build_x86_dependencies_script:
- echo A | softwareupdate --install-rosetta
- arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
- export PATH="/usr/local/bin:$PATH"
- arch -x86_64 brew install node@16 yarn git python
- export PATH="/usr/local/opt/node@16/bin:/usr/local/bin/python3:$PATH"
- yarn install || yarn install
- ln -s /usr/local/opt/python3 /usr/local/bin/python
- arch -x86_64 npx yarn install || arch -x86_64 npx yarn install
- arch -x86_64 npx yarn build
- arch -x86_64 npx yarn run build:apm
dist_x86_binary_script:
- export PATH="/opt/homebrew/bin:/opt/homebrew/opt/node@16/bin:$PATH"
intel_mac_task:
alias: mac
macos_instance:
image: ghcr.io/cirruslabs/macos-monterey-xcode:14
memory: 8G
env:
CSC_LINK: ENCRYPTED[0078015a03bb6cfdbd80113ae5bbb6f448fd4bbbc40efd81bf2cb1554373046b475a4d7c77e3e3e82ac1ce2f7e3d2da5]
CSC_KEY_PASSWORD: ENCRYPTED[82bb72653d39578035ed1860ab4978703d50bd326d925a146ff08782f987ceb37ac2d8dbace52dec2b0e2ef92debf097]
APPLEID: ENCRYPTED[549ce052bd5666dba5245f4180bf93b74ed206fe5e6e7c8f67a8596d3767c1f682b84e347b326ac318c62a07c8844a57]
APPLEID_PASSWORD: ENCRYPTED[774c3307fd3b62660ecf5beb8537a24498c76e8d90d7f28e5bc816742fd8954a34ffed13f9aa2d1faf66ce08b4496e6f]
TEAM_ID: ENCRYPTED[11f3fedfbaf4aff1859bf6c105f0437ace23d84f5420a2c1cea884fbfa43b115b7834a463516d50cb276d4c4d9128b49]
prepare_script:
- sudo rm -rf /Library/Developer/CommandLineTools
- echo A | softwareupdate --install-rosetta
- arch -x86_64 xcode-select --install
- arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
- export PATH="/usr/local/opt/node@16/bin:/usr/local/bin:$PATH"
- arch -x86_64 brew install node@16 yarn git python@$PYTHON_VERSION
- ln -s /usr/local/bin/python$PYTHON_VERSION /usr/local/bin/python
- git submodule init
- git submodule update
- sed -i -e "s/[0-9]*-dev/`date -u +%Y%m%d%H`/g" package.json
install_script:
- export PATH="/usr/local/opt/node@16/bin:/usr/local/bin:$PATH"
- arch -x86_64 npx yarn install --ignore-engines || arch -x86_64 npx yarn install --ignore-engines
build_script:
- export PATH="/usr/local/opt/node@16/bin:/usr/local/bin:$PATH"
- arch -x86_64 npx yarn build
- arch -x86_64 yarn run build:apm
build_binary_script:
- export PATH="/usr/local/opt/node@16/bin:/usr/local/bin:$PATH"
- arch -x86_64 npx yarn dist || arch -x86_64 npx yarn dist
x86_binary_artifacts:
binary_artifacts:
path: ./binaries/*
test_script:
- export PATH="/usr/local/opt/node@16/bin:/usr/local/bin:$PATH"
- rm -R node_modules/electron; yarn install --check-files
- hdiutil mount binaries/Pulsar*dmg
- export BINARY_NAME=`ls /Volumes/Pulsar*/Pulsar.app/Contents/MacOS/Pulsar`
- PLAYWRIGHT_JUNIT_OUTPUT_NAME=report.xml arch -x86_64 npx playwright test --reporter=junit,list
always:
videos_artifacts:
path: ./tests/videos/**
junit_artifacts:
path: report.xml
type: text/xml
format: junit
windows_task:
alias: windows
timeout_in: 90m
windows_container:
image: cirrusci/windowsservercore:visualstudio2022-2022.06.23
env:
CIRRUS_SHELL: bash
PATH: C:\Python310\Scripts\;C:\Python310\;%PATH%;C:\Program Files\nodejs\;C:\Program Files\Git\cmd;C:\Users\User\AppData\Local\Microsoft\WindowsApps;C:\Users\User\AppData\Roaming\npm;C:\Program Files\Microsoft Visual Studio\2022\Community\Msbuild\Current\Bin\
install_deps_script:
- choco install nodejs --version=14.15.0 -y
- choco install python git visualstudio2019-workload-vctools -y
prepare_script:
- choco install nodejs --version=16.16.0 -y
- choco install python --version=3.10.3 -y
- choco install git visualstudio2019-workload-vctools -y
- git submodule init
- git submodule update
- npm config set python 'C:\Python310\python.exe'
build_apm_script:
- cd ppm; npx yarn install --ignore-engines || npx yarn install --ignore-engines || npx yarn cache clean; npx yarn install --ignore-engines || npx yarn install --ignore-engines
install_without_scripts_script:
- npx yarn install --ignore-scripts --ignore-engines || sleep 1 && npx yarn install --ignore-engines --ignore-scripts || sleep 2 && npx yarn cache clean; npx yarn install --ignore-engines --ignore-scripts || sleep 2 && npx yarn install --ignore-engines --ignore-scripts || echo "Giving up"
rebuild_for_electron_script:
install_script:
- npx yarn install --ignore-engines
|| rm -R node_modules && npx yarn install --ignore-engines
|| rm -R node_modules && npx yarn install --ignore-engines
build_script:
- npx yarn build:apm
- npx yarn build || npx yarn build || npx yarn build
# install_only_electron_script:
# - rm -R node_modules/electron
# - npx yarn install --ignore-engines || npx yarn install --ignore-engines || npx yarn cache clean; npx yarn install --ignore-engines || npx yarn install --ignore-engines
# - npx playwright test --reporter=list
videos_artifacts:
path: tests\videos\**
build_binary_script:
- sed -i -e "s/[0-9]*-dev/`date -u +%Y%m%d%H`/g" package.json
- npx yarn dist || npx yarn dist || npx yarn dist
binary_artifacts:
path: .\binaries\*
test_script:
- mkdir extracted; tar -xf binaries/*zip -C ./extracted/
- export BINARY_NAME=./extracted/Pulsar.exe
- PLAYWRIGHT_JUNIT_OUTPUT_NAME=report.xml npx playwright test --reporter=junit,list || echo "Yeah, tests failed, Windows is like this"
always:
videos_artifacts:
path: ./tests/videos/**
junit_artifacts:
path: report.xml
type: text/xml
format: junit

33
.codacy.yaml Normal file
View File

@ -0,0 +1,33 @@
---
engines:
duplication:
minTokenMatch: 80
exclude_paths:
- "spec/fixtures/**"
- "packages/*/spec/fixtures/**"
# Since Codacy exposes significantly little on the config.
# We can use the rest of this document to solidify our settings.
#
# Quality Settings: (Below are changed values, rest are default)
# * Pull requests won't pass the quality gate when:
# - New issues are over: 15 'critical'
# * Commits won't pass the quality gate:
# - New issues are over: 15 'critical'
# * The repository is below the quality goals when:
# - Issues are over: 20%
# - Complexity is over: 10%
# - File is complex when over: 20
# - Duplication is over: 30
# - File is duplicated when over: 1 'cloned block'
# - Coverage is under: 60%
# Enabled Engines:
# - CSSLint 1.0.5
# - CoffeeLint 2.1.0
# - ESLint 8.23.1
# - Hadolint 1.18.2
# - Jackson Linter 2.10.2
# - PMD 6.48.0
# - SpellCheck 0.8.0
# - Stylelint 14.2.0
#

View File

@ -1,4 +0,0 @@
**/spec/fixtures/**/*.js
node_modules
/vendor/
/out/

30
.eslintrc.js Normal file
View File

@ -0,0 +1,30 @@
module.exports = {
env: {
browser: true,
commonjs: true,
es2021: true,
},
extends: [
"eslint:recommended",
"plugin:node/recommended",
"plugin:jsdoc/recommended"
],
overrides: [],
parserOptions: {
ecmaVersion: "latest"
},
rules: {
"node/no-unpublished-require": [
"error",
{
allowModules: ["electron"]
}
]
},
plugins: [
"jsdoc"
],
globals: {
atom: "writeable"
}
};

View File

@ -1,44 +0,0 @@
{
"extends": [
"./script/node_modules/eslint-config-standard/eslintrc.json",
"./script/node_modules/eslint-config-prettier/index.js",
"./script/node_modules/eslint-config-prettier/standard.js"
],
"plugins": [
"prettier"
],
"env": {
"browser": true,
"node": true
},
"parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 8,
"ecmaFeatures": {
"jsx": true
}
},
"globals": {
"atom": true,
"snapshotResult": true
},
"rules": {
"standard/no-callback-literal": ["off"],
"node/no-deprecated-api": ["off"],
"prettier/prettier": ["error"]
},
"overrides": [
{
"files": ["spec/**", "**-spec.js", "**.test.js"],
"env": {
"jasmine": true
},
"globals": {
"advanceClock": true,
"fakeClearInterval": true,
"fakeSetInterval": true,
"waitsForPromise": true
}
}
]
}

10
.gitattributes vendored
View File

@ -15,13 +15,3 @@ spec/fixtures/sample.txt text eol=lf
# Windows bash scripts are also Unix LF endings
*.sh eol=lf
# The script executables should be LF so they can be edited on Windows
script/bootstrap text eol=lf
script/build text eol=lf
script/cibuild text eol=lf
script/clean text eol=lf
script/lint text eol=lf
script/postprocess-junit-results text eol=lf
script/test text eol=lf
script/verify-snapshot-script text eol=lf

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,13 +1,20 @@
name: Editor tests
on:
- push
- pull_request
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ATOM_JASMINE_REPORTER: list
jobs:
tests:
name: tests
if: |
!startsWith(github.event.pull_request.title, '[skip-ci]') &&
!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:
@ -28,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,20 +1,17 @@
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: setup
name: Build Editor
if: |
!startsWith(github.event.pull_request.title, '[skip-ci]') &&
!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
@ -23,69 +20,153 @@ jobs:
with:
node-version: 16
- name: install dependencies
- name: Install Dependencies
run: yarn install
- name: build dependencies
- name: Build Dependencies
run: yarn build
- name: build dependencies
run: yarn build:apm
- name: cache node module
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 module
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: 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
fail-fast: false
- 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"
name: test ${{ matrix.descr }} packages
runs-on: ubuntu-20.04
needs: setup
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 module
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: cache APM module
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 ${{ matrix.descr }} packages' tests
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 }}/)"

2
.gitignore vendored
View File

@ -18,4 +18,6 @@ docs/includes
spec/fixtures/evil-files/
!spec/fixtures/packages/package-with-incompatible-native-module-loaded-conditionally/node_modules/
out/
dist/
binaries/
/electron/

2
.nvmrc
View File

@ -1 +1 @@
12.18
16

View File

@ -1,3 +0,0 @@
{
"singleQuote": true
}

View File

@ -1 +1,351 @@
See https://atom.io/releases
# 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]
- The settings-view package now lists a packages snippets more accurately
- Fixed some issues with some packages with WebComponents v0 (tablr package
should work now) by internalizing and patching document-register-element
- Migrated away from `node-oniguruma` in favor of `vscode-oniguruma` (WASM
version). This fixes issues with Electron 21
## 1.103.0
- Added a new feature to Search for Pulsar's settings
- Updated the completions provided by `autocomplete-css` to be as bleeding edge as possible.
- Updated the instructions and look of the login flow for the `github` package.
- Snippet transformations no longer have an implied global flag, bringing them into compatibility with snippets in most other editors.
- Snippets can now be given command names instead of tab triggers, and thus can now be assigned to key shortcuts in `keymap.cson`.
### Pulsar
- Added: feature: Implement Search Settings Ability [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/416)
- Added: Show Settings Icon in Status Bar [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/421)
- Added: Add Automated updated of `autocomplete-css` `completions.json` [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/398)
- Bumped: ppm: Update submodule to 9af239277180f2a9ee9e86714 [@Spiker985](https://github.com/pulsar-edit/pulsar/pull/403)
- Bumped: ppm: Update submodule to 915cbf6e5f9ea1141ef5dcaf8 [@DeeDeeG](https://github.com/pulsar-edit/pulsar/pull/418)
- Bumped: deps: Bump github to v0.36.15-pretranspiled [@DeeDeeG](https://github.com/pulsar-edit/pulsar/pull/415)
- Added: actually cache based on sha [@Meadowsys](https://github.com/pulsar-edit/pulsar/pull/412)
- Bumped: Bump `snippets` to bb00f9 [@savetheclocktower](https://github.com/pulsar-edit/pulsar/pull/408)
- Added: [skip-ci] Small Readme Touchup [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/404)
- Added: json language - add .har extension [@wesinator](https://github.com/pulsar-edit/pulsar/pull/396)
- Added: Bundle `markdown-preview`, `styleguide`, `wrap-guide` [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/374)
- Added: Add GitHub Token to Doc CI [@Spiker985](https://github.com/pulsar-edit/pulsar/pull/400)
- Added: Add Setup Node to Package Tests [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/399)
- Added: feat: add dev.pulsar_edit.Pulsar.metainfo.xml [@cat-master21](https://github.com/pulsar-edit/pulsar/pull/380)
### Snippets
- Added: Add `command` property that registers a command name for a snippet [@savetheclocktower](https://github.com/pulsar-edit/snippets/pull/10)
- Removed: Remove implicit `g` flag from snippet transformations [@savetheclocktower](https://github.com/pulsar-edit/snippets/pull/7)
- Fixed: Fix failing specs [@mauricioszabo](https://github.com/pulsar-edit/snippets/pull/6)
- Added: cleanup and rename [@Sertonix](https://github.com/pulsar-edit/snippets/pull/5)
### Github
- Added: rebrand git-tab-view [@icecream17](https://github.com/pulsar-edit/github/pull/17)
- Added: lib: Update login instructions for PATs, not OAuth [@DeeDeeG](https://github.com/pulsar-edit/github/pull/15)
### PPM
- Fixed: src: Update default Pulsar install paths [@DeeDeeG](https://github.com/pulsar-edit/ppm/pull/49)
- Bumped: deps: Upgrade npm to 6.14.18 [@DeeDeeG](https://github.com/pulsar-edit/ppm/pull/53)
- Fixed: Fix installing with yarn on Windows [@DeeDeeG](https://github.com/pulsar-edit/ppm/pull/58)
- Fixed: Fix inability to notice newer versions of git-installed packages [@savetheclocktower](https://github.com/pulsar-edit/ppm/pull/59)
- Added: meta: Actually sync yarn.lock [@DeeDeeG](https://github.com/pulsar-edit/ppm/pull/60)
## 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 infrastructure 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 rebranding API
- Removed experimental file watchers on the editor
- Ability to install packages from git repositories
- 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 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
### 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

@ -145,7 +145,7 @@ Include details about your configuration and environment:
* **What's the name and version of the OS you're using**?
* **Are you running Atom in a virtual machine?** If so, which VM software are you using and which operating systems and versions are used for the host and the guest?
* **Which [packages](#atom-and-packages) do you have installed?** You can get that list by running `apm list --installed`.
* **Are you using [local configuration files](https://flight-manual.atom.io/using-atom/sections/basic-customization/)** `config.cson`, `keymap.cson`, `snippets.cson`, `styles.less` and `init.coffee` to customize Atom? If so, provide the contents of those files, preferably in a [code block](https://help.github.com/articles/markdown-basics/#multiple-lines) or with a link to a [gist](https://gist.github.com/).
* **Are you using [local configuration files](https://flight-manual.atom.io/using-atom/sections/basic-customization/)** `config.cson`, `keymap.cson`, `snippets.cson`, `styles.less` and `init.js` to customize Atom? If so, provide the contents of those files, preferably in a [code block](https://help.github.com/articles/markdown-basics/#multiple-lines) or with a link to a [gist](https://gist.github.com/).
* **Are you using Atom with multiple monitors?** If so, can you reproduce the problem when you use a single monitor?
* **Which keyboard layout are you using?** Are you using a US layout or some other layout?

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,20 +1,22 @@
Copyright (c) 2011-2022 GitHub Inc.
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-2023 Pulsar-Edit
Original work copyright (c) 2011-2022 GitHub Inc.
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,69 +1,85 @@
<br>
<div align = center>
<!-- ![Banner] -->
<br>
[![Badge Discussions]][Discussions]
[![Badge Discord]][Discord]
[![Badge Reddit]][Reddit]
[![Badge Status]][Status]
[![Badge License]][License]
<br>
# Pulsar
<br>
[![Button Install]][Install]
[![Button Documentation]][Documentation]
[![Button Build]][Build]
[![Badge License]][License]
[![Badge Guidelines]][Guidelines]
[![Badge Status]][Status]
[![Badge Sunset]][Retired]
[![Badge Discord]][Discord]
[![Badge Codacy]][Codacy]
[![Badge Crowdin]][Crowdin]
[![Badge OpenCollective]][OpenCollective]
<br>
*A hackable text editor for the 21st century, built on **[Electron]**,* <br>
*and based on everything we love about our favorite editors.*
*We designed it to be deeply customizable, but still* <br>
*A Community-led Hyper-Hackable Text Editor,*
*Forked from [Atom], built on [Electron].*
*Designed to be deeply customizable, but still*
*approachable using the default configuration.*
<br>
<br>
![Preview]
[![Preview]][#]
</div>
<!---------------------------{ Links }--------------------------->
[Guidelines]: https://github.com/logos 'Branding Guidelines'
[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
[Codacy]: https://app.codacy.com/gh/pulsar-edit/pulsar
[Reddit]: https://www.reddit.com/r/pulsaredit/
[#]: #
<!---------------------------{ Files }--------------------------->
[Documentation]: docs/Documentation.md 'Information how to use & work with Pulsar.'
[Install]: docs/Installation.md 'How to install Pulsar on your system.'
[Retired]: docs/Retirement.md 'Check out what happened to the original Atom project.'
<!---------------------------{ Documents }--------------------------->
[Documentation]: https://pulsar-edit.dev/docs/ 'Information how to use & work with Pulsar.'
[Install]: https://pulsar-edit.dev/docs/launch-manual/sections/getting-started/#installing-pulsar 'How to install Pulsar on your system.'
[Build]: https://pulsar-edit.dev/docs/launch-manual/sections/core-hacking/#building-pulsar 'Instructions on how to build Pulsar by yourself.'
[License]: LICENSE.md
[Build]: docs/Building.md 'Instructions on how to build Pulsar by yourself.'
<!---------------------------{ Images }--------------------------->
[Preview]: https://user-images.githubusercontent.com/378023/49132478-f4b77680-f31f-11e8-9e10-e8454d8d9b7e.png 'Preview of the editor.'
[Banner]: https://user-images.githubusercontent.com/378023/49132477-f4b77680-f31f-11e8-8357-ac6491761c6c.png
[Preview]: resources/readme.png 'Preview of the editor.'
<!---------------------------{ Badges }--------------------------->
[Badge Guidelines]: https://img.shields.io/badge/Logo-Guidelines-d36e2d.svg?style=for-the-badge&labelColor=323232
[Badge Retired]: https://img.shields.io/badge/Retired-bb3c1f.svg?style=for-the-badge&labelColor=323232&label=Upstream%20Status
[Badge Sunset]: https://img.shields.io/badge/Sunset-orange.svg?style=for-the-badge&labelColor=323232&label=Upstream%20Status
[Badge Discord]: https://img.shields.io/badge/Discord-6399c4.svg?style=for-the-badge&labelColor=323232&logoColor=white&logo=Discord
[Badge License]: https://img.shields.io/badge/License-MIT-e5ab42.svg?style=for-the-badge&labelColor=323232
[Badge Status]: https://img.shields.io/cirrus/github/pulsar-edit/pulsar?style=for-the-badge&labelColor=323232&label=Build%20Status
[Badge OpenCollective]: https://opencollective.com/pulsar-edit/tiers/badge.svg
[Badge Discussions]: https://img.shields.io/github/discussions/pulsar-edit/.github?style=for-the-badge&labelColor=78af9f&color=5a8377
[Badge Upstream]: https://img.shields.io/badge/Upstream_Status-Sunset-966227.svg?style=for-the-badge&labelColor=c38033
[Badge Discord]: https://img.shields.io/badge/Discord-4b7494.svg?style=for-the-badge&labelColor=6399c4&logoColor=white&logo=Discord
[Badge License]: https://img.shields.io/badge/License-MIT-ac7f31.svg?style=for-the-badge&labelColor=e5ab42
[Badge Crowdin]: https://badges.crowdin.net/pulsar-edit/localized.svg
[Badge Codacy]: https://app.codacy.com/project/badge/Grade/24873ecb93dc4c1d865202ce5b24efc1
[Badge Reddit]: https://img.shields.io/reddit/subreddit-subscribers/pulsaredit?style=for-the-badge&label=Reddit&logoColor=white&logo=Reddit&labelColor=e05d44&color=b14835
[Badge Status]: https://img.shields.io/cirrus/github/pulsar-edit/pulsar?style=for-the-badge&labelColor=c77b7f&label=Build%20Status&color=8d575a
<!--------------------------{ Buttons }--------------------------->
@ -71,4 +87,3 @@
[Button Documentation]: https://img.shields.io/badge/Documentation-6399c4?style=for-the-badge&logoColor=white&logo=GitBook
[Button Install]: https://img.shields.io/badge/Install-78af9f?style=for-the-badge&logoColor=white&logo=DocuSign
[Button Build]: https://img.shields.io/badge/Building-e5ab42?style=for-the-badge&logoColor=white&logo=GNUBash

View File

@ -1,75 +0,0 @@
const Chart = require('chart.js');
const glob = require('glob');
const fs = require('fs-plus');
const path = require('path');
module.exports = async ({ test, benchmarkPaths }) => {
document.body.style.backgroundColor = '#ffffff';
document.body.style.overflow = 'auto';
let paths = [];
for (const benchmarkPath of benchmarkPaths) {
if (fs.isDirectorySync(benchmarkPath)) {
paths = paths.concat(
glob.sync(path.join(benchmarkPath, '**', '*.bench.js'))
);
} else {
paths.push(benchmarkPath);
}
}
while (paths.length > 0) {
const benchmark = require(paths.shift())({ test });
let results;
if (benchmark instanceof Promise) {
results = await benchmark;
} else {
results = benchmark;
}
const dataByBenchmarkName = {};
for (const { name, duration, x } of results) {
dataByBenchmarkName[name] = dataByBenchmarkName[name] || { points: [] };
dataByBenchmarkName[name].points.push({ x, y: duration });
}
const benchmarkContainer = document.createElement('div');
document.body.appendChild(benchmarkContainer);
for (const key in dataByBenchmarkName) {
const data = dataByBenchmarkName[key];
if (data.points.length > 1) {
const canvas = document.createElement('canvas');
benchmarkContainer.appendChild(canvas);
// eslint-disable-next-line no-new
new Chart(canvas, {
type: 'line',
data: {
datasets: [{ label: key, fill: false, data: data.points }]
},
options: {
showLines: false,
scales: { xAxes: [{ type: 'linear', position: 'bottom' }] }
}
});
const textualOutput =
`${key}:\n\n` + data.points.map(p => `${p.x}\t${p.y}`).join('\n');
console.log(textualOutput);
} else {
const title = document.createElement('h2');
title.textContent = key;
benchmarkContainer.appendChild(title);
const duration = document.createElement('p');
duration.textContent = `${data.points[0].y}ms`;
benchmarkContainer.appendChild(duration);
const textualOutput = `${key}: ${data.points[0].y}`;
console.log(textualOutput);
}
await global.atom.reset();
}
}
return 0;
};

View File

@ -1,100 +0,0 @@
const { TextEditor, TextBuffer } = require('atom');
const MIN_SIZE_IN_KB = 0 * 1024;
const MAX_SIZE_IN_KB = 10 * 1024;
const SIZE_STEP_IN_KB = 1024;
const LINE_TEXT = 'Lorem ipsum dolor sit amet\n';
const TEXT = LINE_TEXT.repeat(
Math.ceil((MAX_SIZE_IN_KB * 1024) / LINE_TEXT.length)
);
module.exports = async ({ test }) => {
const data = [];
document.body.appendChild(atom.workspace.getElement());
atom.packages.loadPackages();
await atom.packages.activate();
for (let pane of atom.workspace.getPanes()) {
pane.destroy();
}
for (
let sizeInKB = MIN_SIZE_IN_KB;
sizeInKB < MAX_SIZE_IN_KB;
sizeInKB += SIZE_STEP_IN_KB
) {
const text = TEXT.slice(0, sizeInKB * 1024);
console.log(text.length / 1024);
let t0 = window.performance.now();
const buffer = new TextBuffer({ text });
const editor = new TextEditor({
buffer,
autoHeight: false,
largeFileMode: true
});
atom.grammars.autoAssignLanguageMode(buffer);
atom.workspace.getActivePane().activateItem(editor);
let t1 = window.performance.now();
data.push({
name: 'Opening a large file',
x: sizeInKB,
duration: t1 - t0
});
const tickDurations = [];
for (let i = 0; i < 20; i++) {
await timeout(50);
t0 = window.performance.now();
await timeout(0);
t1 = window.performance.now();
tickDurations[i] = t1 - t0;
}
data.push({
name: 'Max time event loop was blocked after opening a large file',
x: sizeInKB,
duration: Math.max(...tickDurations)
});
t0 = window.performance.now();
editor.setCursorScreenPosition(
editor.element.screenPositionForPixelPosition({
top: 100,
left: 30
})
);
t1 = window.performance.now();
data.push({
name: 'Clicking the editor after opening a large file',
x: sizeInKB,
duration: t1 - t0
});
t0 = window.performance.now();
editor.element.setScrollTop(editor.element.getScrollTop() + 100);
t1 = window.performance.now();
data.push({
name: 'Scrolling down after opening a large file',
x: sizeInKB,
duration: t1 - t0
});
editor.destroy();
buffer.destroy();
await timeout(10000);
}
atom.workspace.getElement().remove();
return data;
};
function timeout(duration) {
return new Promise(resolve => setTimeout(resolve, duration));
}

View File

@ -1,105 +0,0 @@
const path = require('path');
const fs = require('fs');
const { TextEditor, TextBuffer } = require('atom');
const SIZES_IN_KB = [512, 1024, 2048];
const REPEATED_TEXT = fs
.readFileSync(
path.join(__dirname, '..', 'spec', 'fixtures', 'sample.js'),
'utf8'
)
.replace(/\n/g, '');
const TEXT = REPEATED_TEXT.repeat(
Math.ceil((SIZES_IN_KB[SIZES_IN_KB.length - 1] * 1024) / REPEATED_TEXT.length)
);
module.exports = async ({ test }) => {
const data = [];
const workspaceElement = atom.workspace.getElement();
document.body.appendChild(workspaceElement);
atom.packages.loadPackages();
await atom.packages.activate();
console.log(atom.getLoadSettings().resourcePath);
for (let pane of atom.workspace.getPanes()) {
pane.destroy();
}
for (const sizeInKB of SIZES_IN_KB) {
const text = TEXT.slice(0, sizeInKB * 1024);
console.log(text.length / 1024);
let t0 = window.performance.now();
const buffer = new TextBuffer({ text });
const editor = new TextEditor({
buffer,
autoHeight: false,
largeFileMode: true
});
atom.grammars.assignLanguageMode(buffer, 'source.js');
atom.workspace.getActivePane().activateItem(editor);
let t1 = window.performance.now();
data.push({
name: 'Opening a large single-line file',
x: sizeInKB,
duration: t1 - t0
});
const tickDurations = [];
for (let i = 0; i < 20; i++) {
await timeout(50);
t0 = window.performance.now();
await timeout(0);
t1 = window.performance.now();
tickDurations[i] = t1 - t0;
}
data.push({
name:
'Max time event loop was blocked after opening a large single-line file',
x: sizeInKB,
duration: Math.max(...tickDurations)
});
t0 = window.performance.now();
editor.setCursorScreenPosition(
editor.element.screenPositionForPixelPosition({
top: 100,
left: 30
})
);
t1 = window.performance.now();
data.push({
name: 'Clicking the editor after opening a large single-line file',
x: sizeInKB,
duration: t1 - t0
});
t0 = window.performance.now();
editor.element.setScrollTop(editor.element.getScrollTop() + 100);
t1 = window.performance.now();
data.push({
name: 'Scrolling down after opening a large single-line file',
x: sizeInKB,
duration: t1 - t0
});
editor.destroy();
buffer.destroy();
await timeout(10000);
}
workspaceElement.remove();
return data;
};
function timeout(duration) {
return new Promise(resolve => setTimeout(resolve, duration));
}

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,601 @@
## 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="#css">css</a></dt>
<dd><p>This file will manage the updating of <code>autocomplete-css</code> <code>completions.json</code>.
We will mainly utilize <code>@webref/css</code>.listAll() function that returns a full CSS
list of all properties seperated by their spec shortname. An example
of this format is defined below for ease of future modifications.</p>
<p> Some important notes about the data contained here:
- Often times the <code>value</code> within the <code>property</code> will be in the following format:
<code>&lt;valueGroupName&gt;</code> or even <code>&lt;valueGroupName&gt; | value | value2</code> or just <code>value | value2</code>
It will be important to build a parser that can handle this format.
The <code>&lt;valueGroupName&gt;</code> then can be realized via that specs <code>values</code> where
<code>values[x].name</code> will match the <code>&lt;valueGroupName&gt;</code>. Another important note about
handling values here is that oftentimes <code>values[x].values[]</code> won&#39;t actually
contain all possible values. And instead this must be handled by checking
<code>values[x].value</code> which is another string of <code>&lt;valueGroupName&gt; | value</code>.
So this should be handled by the same parser.
- Additionally an important note is that nowhere in this data do we get any kind
of description about the data that could lend a hand in being documentation.
So the documentation must be gathered seperatly. Likely the best way to collect
our documentation data is via <code>mdn/content</code>.
Within <code>content/files/en-us/web/css</code> is a directory of folders titled
by the name of properties.</p>
<pre><code>The last important thing to note here:
MDN doesn&#39;t have docs on everything. And that&#39;s a good thing. But it means
many of our items don&#39;t have any kind of description. For this situation
we have `manual-property-desc.json` which is a list of manually updated
descriptions for properties where there are none. This was a last resort
intended to provide the highest quality of completions possible.
Overtime many items on this list will likely be able to be removed just as
new ones are added. After running the update script you&#39;ll see a warning
saying how many properties are without completions that would then need to
be added to the JSON file.
</code></pre>
<p> &quot;spec-shortname&quot;: {
&quot;spec&quot;: {
&quot;title&quot;: &quot;&quot;,
&quot;url&quot;: &quot;&quot;
},
&quot;properties&quot;: [
{
&quot;name&quot;: &quot;&quot;,
&quot;value&quot;: &quot;&quot;,
&quot;initial&quot;: &quot;&quot;,
&quot;appliesTo&quot;: &quot;&quot;,
&quot;percentages&quot;: &quot;&quot;,
&quot;computedValue&quot;: &quot;&quot;,
&quot;canonicalOrder&quot;: &quot;&quot;,
&quot;animationType&quot;: &quot;&quot;,
&quot;media&quot;: &quot;&quot;,
&quot;styleDeclaration&quot;: [ &quot;&quot;, &quot;&quot;, &quot;&quot; ]
}
],
&quot;atrules&quot;: [
{
&quot;name&quot;: &quot;&quot;,
&quot;descriptors&quot;: [
{
&quot;name&quot;: &quot;&quot;,
&quot;for&quot;: &quot;&quot;,
&quot;value&quot;: &quot;&quot;,
&quot;type&quot;: &quot;&quot;
}
]
}
],
&quot;selectors&quot;: [],
&quot;values&quot;: [
{
&quot;name&quot;: &quot;&quot;,
&quot;type&quot;: &quot;&quot;,
&quot;prose&quot;: &quot;Optional description&quot;,
&quot;value&quot;: &quot;&quot;,
&quot;values&quot;: [
{
&quot;name&quot;: &quot;&quot;,
&quot;prose&quot;: &quot;Optional Description&quot;,
&quot;type&quot;: &quot;&quot;,
&quot;value&quot;: &quot;&quot;
}
]
}
],
&quot;warnings&quot;: []
}</p>
</dd>
<dt><a href="#chromiumElementsShim">chromiumElementsShim</a></dt>
<dd><p>This file will manage the updating of <code>autocomplete-html</code> <code>completions.json</code>
We will partially utilize <code>@webref/elements</code> <code>.listAll()</code> function that returns
a full list of HTML Elements along with a defined <code>interface</code>.
To use this <code>interface</code> in any meaningful way, we will utilize the dataset
of Attributes that apply to each <code>interface</code> from Chromiums DevTools resource
<code>https://github.com/ChromeDevTools/devtools-frontend</code>.
Finally from here we will utilize <code>https://github.com/mdn/content</code> to parse
the Markdown docs of MDN&#39;s website to retreive descriptions for each element.</p>
<p> Now for a summary of our <code>completions.json</code> file we aim to generate.
There are two top level elements, <code>tags</code> and <code>attributes</code>, both objects.
Within <code>tags</code> we expect the following:
&quot;tags&quot;: {
&quot;a&quot;: {
&quot;attributes&quot;: [ &quot;href&quot;, &quot;hreflang&quot;, &quot;media&quot;, &quot;rel&quot;, &quot;target&quot;, &quot;type&quot; ],
&quot;description&quot;: &quot;.....&quot;
}
};</p>
<p> When an entry contains no <code>attributes</code> there is no empty array, the element
simply doesn&#39;t exist.</p>
<p> The <code>attributes</code> object contains keys of different elements that themselves
are objects that can contain several valid keys.</p>
<ul>
<li>global: Seems to be used exclusively for Global Attributes. Is a boolean
which when false, the key does not appear.</li>
<li>type: A ?type? for the attribute. It&#39;s meaning is not immediately known.
Nor a way to reliabley programatically collect it. Some discovered values:</li>
</ul>
<p>cssStyle: Exclusively used for <code>class</code> attribute
boolean: Attributes that only accept <code>true</code> or <code>false</code>
flag: For attributes that don&#39;t require or accept values. eg autoplay
cssId: Exclusively used for the <code>id</code> attribute
color: Exclusively used for the <code>bgcolor</code> attribute
style: Exclusively used for the <code>style</code> attribute</p>
<ul>
<li>description: A text description of the attribute</li>
<li>attribOption: A string array of valid values that can exist within the attribute.
Such as the case with <code>rel</code> where only so many valid options exist.</li>
</ul>
<p> Although with our data sources mentioned above, we are able to collect nearly
all the data needed. Except the <code>type</code> that is defined within our
<code>completions.json</code> as well as the <code>attribOption</code> within our completions.</p>
<p> Studying these closer reveals that all attributes listing with our <code>completions.json</code>
do not appear elsewhere, and are nearly all global attributes.</p>
<p> In this case since there is no sane way to collect this data, we will leave this
list as a manually maintained section of our <code>completions.json</code>.
This does mean that <code>curated-attributes.json</code> is a static document that
will require manual updating in the future. Or most ideally, will find a way
to automatically generate the needed data.</p>
</dd>
<dt><a href="#update">update</a></dt>
<dd><p>This file aims to run some short simple tests against <code>update.js</code>. Focusing
mainly on the Regex used within <code>sanitizeDescription()</code></p>
</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="#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="css"></a>
## css
This file will manage the updating of `autocomplete-css` `completions.json`.
We will mainly utilize `@webref/css`.listAll() function that returns a full CSS
list of all properties seperated by their spec shortname. An example
of this format is defined below for ease of future modifications.
Some important notes about the data contained here:
- Often times the `value` within the `property` will be in the following format:
`<valueGroupName>` or even `<valueGroupName> | value | value2` or just `value | value2`
It will be important to build a parser that can handle this format.
The `<valueGroupName>` then can be realized via that specs `values` where
`values[x].name` will match the `<valueGroupName>`. Another important note about
handling values here is that oftentimes `values[x].values[]` won't actually
contain all possible values. And instead this must be handled by checking
`values[x].value` which is another string of `<valueGroupName> | value`.
So this should be handled by the same parser.
- Additionally an important note is that nowhere in this data do we get any kind
of description about the data that could lend a hand in being documentation.
So the documentation must be gathered seperatly. Likely the best way to collect
our documentation data is via `mdn/content`.
Within `content/files/en-us/web/css` is a directory of folders titled
by the name of properties.
The last important thing to note here:
MDN doesn't have docs on everything. And that's a good thing. But it means
many of our items don't have any kind of description. For this situation
we have `manual-property-desc.json` which is a list of manually updated
descriptions for properties where there are none. This was a last resort
intended to provide the highest quality of completions possible.
Overtime many items on this list will likely be able to be removed just as
new ones are added. After running the update script you'll see a warning
saying how many properties are without completions that would then need to
be added to the JSON file.
"spec-shortname": {
"spec": {
"title": "",
"url": ""
},
"properties": [
{
"name": "",
"value": "",
"initial": "",
"appliesTo": "",
"percentages": "",
"computedValue": "",
"canonicalOrder": "",
"animationType": "",
"media": "",
"styleDeclaration": [ "", "", "" ]
}
],
"atrules": [
{
"name": "",
"descriptors": [
{
"name": "",
"for": "",
"value": "",
"type": ""
}
]
}
],
"selectors": [],
"values": [
{
"name": "",
"type": "",
"prose": "Optional description",
"value": "",
"values": [
{
"name": "",
"prose": "Optional Description",
"type": "",
"value": ""
}
]
}
],
"warnings": []
}
**Kind**: global constant
<a name="chromiumElementsShim"></a>
## chromiumElementsShim
This file will manage the updating of `autocomplete-html` `completions.json`
We will partially utilize `@webref/elements` `.listAll()` function that returns
a full list of HTML Elements along with a defined `interface`.
To use this `interface` in any meaningful way, we will utilize the dataset
of Attributes that apply to each `interface` from Chromiums DevTools resource
`https://github.com/ChromeDevTools/devtools-frontend`.
Finally from here we will utilize `https://github.com/mdn/content` to parse
the Markdown docs of MDN's website to retreive descriptions for each element.
Now for a summary of our `completions.json` file we aim to generate.
There are two top level elements, `tags` and `attributes`, both objects.
Within `tags` we expect the following:
"tags": {
"a": {
"attributes": [ "href", "hreflang", "media", "rel", "target", "type" ],
"description": "....."
}
};
When an entry contains no `attributes` there is no empty array, the element
simply doesn't exist.
The `attributes` object contains keys of different elements that themselves
are objects that can contain several valid keys.
- global: Seems to be used exclusively for Global Attributes. Is a boolean
which when false, the key does not appear.
- type: A ?type? for the attribute. It's meaning is not immediately known.
Nor a way to reliabley programatically collect it. Some discovered values:
cssStyle: Exclusively used for `class` attribute
boolean: Attributes that only accept `true` or `false`
flag: For attributes that don't require or accept values. eg autoplay
cssId: Exclusively used for the `id` attribute
color: Exclusively used for the `bgcolor` attribute
style: Exclusively used for the `style` attribute
- description: A text description of the attribute
- attribOption: A string array of valid values that can exist within the attribute.
Such as the case with `rel` where only so many valid options exist.
Although with our data sources mentioned above, we are able to collect nearly
all the data needed. Except the `type` that is defined within our
`completions.json` as well as the `attribOption` within our completions.
Studying these closer reveals that all attributes listing with our `completions.json`
do not appear elsewhere, and are nearly all global attributes.
In this case since there is no sane way to collect this data, we will leave this
list as a manually maintained section of our `completions.json`.
This does mean that `curated-attributes.json` is a static document that
will require manual updating in the future. Or most ideally, will find a way
to automatically generate the needed data.
**Kind**: global constant
<a name="update"></a>
## update
This file aims to run some short simple tests against `update.js`. Focusing
mainly on the Regex used within `sanitizeDescription()`
**Kind**: global constant
<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="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,609 @@
## 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="#css">css</a></dt>
<dd><p>This file will manage the updating of <code>autocomplete-css</code> <code>completions.json</code>.
We will mainly utilize <code>@webref/css</code>.listAll() function that returns a full CSS
list of all properties seperated by their spec shortname. An example
of this format is defined below for ease of future modifications.</p>
<p> Some important notes about the data contained here:
- Often times the <code>value</code> within the <code>property</code> will be in the following format:
<code>&lt;valueGroupName&gt;</code> or even <code>&lt;valueGroupName&gt; | value | value2</code> or just <code>value | value2</code>
It will be important to build a parser that can handle this format.
The <code>&lt;valueGroupName&gt;</code> then can be realized via that specs <code>values</code> where
<code>values[x].name</code> will match the <code>&lt;valueGroupName&gt;</code>. Another important note about
handling values here is that oftentimes <code>values[x].values[]</code> won&#39;t actually
contain all possible values. And instead this must be handled by checking
<code>values[x].value</code> which is another string of <code>&lt;valueGroupName&gt; | value</code>.
So this should be handled by the same parser.
- Additionally an important note is that nowhere in this data do we get any kind
of description about the data that could lend a hand in being documentation.
So the documentation must be gathered seperatly. Likely the best way to collect
our documentation data is via <code>mdn/content</code>.
Within <code>content/files/en-us/web/css</code> is a directory of folders titled
by the name of properties.</p>
<pre><code>The last important thing to note here:
MDN doesn&#39;t have docs on everything. And that&#39;s a good thing. But it means
many of our items don&#39;t have any kind of description. For this situation
we have `manual-property-desc.json` which is a list of manually updated
descriptions for properties where there are none. This was a last resort
intended to provide the highest quality of completions possible.
Overtime many items on this list will likely be able to be removed just as
new ones are added. After running the update script you&#39;ll see a warning
saying how many properties are without completions that would then need to
be added to the JSON file.
</code></pre>
<p> &quot;spec-shortname&quot;: {
&quot;spec&quot;: {
&quot;title&quot;: &quot;&quot;,
&quot;url&quot;: &quot;&quot;
},
&quot;properties&quot;: [
{
&quot;name&quot;: &quot;&quot;,
&quot;value&quot;: &quot;&quot;,
&quot;initial&quot;: &quot;&quot;,
&quot;appliesTo&quot;: &quot;&quot;,
&quot;percentages&quot;: &quot;&quot;,
&quot;computedValue&quot;: &quot;&quot;,
&quot;canonicalOrder&quot;: &quot;&quot;,
&quot;animationType&quot;: &quot;&quot;,
&quot;media&quot;: &quot;&quot;,
&quot;styleDeclaration&quot;: [ &quot;&quot;, &quot;&quot;, &quot;&quot; ]
}
],
&quot;atrules&quot;: [
{
&quot;name&quot;: &quot;&quot;,
&quot;descriptors&quot;: [
{
&quot;name&quot;: &quot;&quot;,
&quot;for&quot;: &quot;&quot;,
&quot;value&quot;: &quot;&quot;,
&quot;type&quot;: &quot;&quot;
}
]
}
],
&quot;selectors&quot;: [],
&quot;values&quot;: [
{
&quot;name&quot;: &quot;&quot;,
&quot;type&quot;: &quot;&quot;,
&quot;prose&quot;: &quot;Optional description&quot;,
&quot;value&quot;: &quot;&quot;,
&quot;values&quot;: [
{
&quot;name&quot;: &quot;&quot;,
&quot;prose&quot;: &quot;Optional Description&quot;,
&quot;type&quot;: &quot;&quot;,
&quot;value&quot;: &quot;&quot;
}
]
}
],
&quot;warnings&quot;: []
}</p>
</dd>
<dt><a href="#chromiumElementsShim">chromiumElementsShim</a></dt>
<dd><p>This file will manage the updating of <code>autocomplete-html</code> <code>completions.json</code>
We will partially utilize <code>@webref/elements</code> <code>.listAll()</code> function that returns
a full list of HTML Elements along with a defined <code>interface</code>.
To use this <code>interface</code> in any meaningful way, we will utilize the dataset
of Attributes that apply to each <code>interface</code> from Chromiums DevTools resource
<code>https://github.com/ChromeDevTools/devtools-frontend</code>.
Finally from here we will utilize <code>https://github.com/mdn/content</code> to parse
the Markdown docs of MDN&#39;s website to retreive descriptions for each element.</p>
<p> Now for a summary of our <code>completions.json</code> file we aim to generate.
There are two top level elements, <code>tags</code> and <code>attributes</code>, both objects.
Within <code>tags</code> we expect the following:
&quot;tags&quot;: {
&quot;a&quot;: {
&quot;attributes&quot;: [ &quot;href&quot;, &quot;hreflang&quot;, &quot;media&quot;, &quot;rel&quot;, &quot;target&quot;, &quot;type&quot; ],
&quot;description&quot;: &quot;.....&quot;
}
};</p>
<p> When an entry contains no <code>attributes</code> there is no empty array, the element
simply doesn&#39;t exist.</p>
<p> The <code>attributes</code> object contains keys of different elements that themselves
are objects that can contain several valid keys.</p>
<ul>
<li>global: Seems to be used exclusively for Global Attributes. Is a boolean
which when false, the key does not appear.</li>
<li>type: A ?type? for the attribute. It&#39;s meaning is not immediately known.
Nor a way to reliabley programatically collect it. Some discovered values:</li>
</ul>
<p>cssStyle: Exclusively used for <code>class</code> attribute
boolean: Attributes that only accept <code>true</code> or <code>false</code>
flag: For attributes that don&#39;t require or accept values. eg autoplay
cssId: Exclusively used for the <code>id</code> attribute
color: Exclusively used for the <code>bgcolor</code> attribute
style: Exclusively used for the <code>style</code> attribute</p>
<ul>
<li>description: A text description of the attribute</li>
<li>attribOption: A string array of valid values that can exist within the attribute.
Such as the case with <code>rel</code> where only so many valid options exist.</li>
</ul>
<p> Although with our data sources mentioned above, we are able to collect nearly
all the data needed. Except the <code>type</code> that is defined within our
<code>completions.json</code> as well as the <code>attribOption</code> within our completions.</p>
<p> Studying these closer reveals that all attributes listing with our <code>completions.json</code>
do not appear elsewhere, and are nearly all global attributes.</p>
<p> In this case since there is no sane way to collect this data, we will leave this
list as a manually maintained section of our <code>completions.json</code>.
This does mean that <code>curated-attributes.json</code> is a static document that
will require manual updating in the future. Or most ideally, will find a way
to automatically generate the needed data.</p>
</dd>
<dt><a href="#update">update</a></dt>
<dd><p>This file aims to run some short simple tests against <code>update.js</code>. Focusing
mainly on the Regex used within <code>sanitizeDescription()</code></p>
</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="#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="css"></a>
## css
This file will manage the updating of `autocomplete-css` `completions.json`.
We will mainly utilize `@webref/css`.listAll() function that returns a full CSS
list of all properties seperated by their spec shortname. An example
of this format is defined below for ease of future modifications.
Some important notes about the data contained here:
- Often times the `value` within the `property` will be in the following format:
`<valueGroupName>` or even `<valueGroupName> | value | value2` or just `value | value2`
It will be important to build a parser that can handle this format.
The `<valueGroupName>` then can be realized via that specs `values` where
`values[x].name` will match the `<valueGroupName>`. Another important note about
handling values here is that oftentimes `values[x].values[]` won't actually
contain all possible values. And instead this must be handled by checking
`values[x].value` which is another string of `<valueGroupName> | value`.
So this should be handled by the same parser.
- Additionally an important note is that nowhere in this data do we get any kind
of description about the data that could lend a hand in being documentation.
So the documentation must be gathered seperatly. Likely the best way to collect
our documentation data is via `mdn/content`.
Within `content/files/en-us/web/css` is a directory of folders titled
by the name of properties.
The last important thing to note here:
MDN doesn't have docs on everything. And that's a good thing. But it means
many of our items don't have any kind of description. For this situation
we have `manual-property-desc.json` which is a list of manually updated
descriptions for properties where there are none. This was a last resort
intended to provide the highest quality of completions possible.
Overtime many items on this list will likely be able to be removed just as
new ones are added. After running the update script you'll see a warning
saying how many properties are without completions that would then need to
be added to the JSON file.
"spec-shortname": {
"spec": {
"title": "",
"url": ""
},
"properties": [
{
"name": "",
"value": "",
"initial": "",
"appliesTo": "",
"percentages": "",
"computedValue": "",
"canonicalOrder": "",
"animationType": "",
"media": "",
"styleDeclaration": [ "", "", "" ]
}
],
"atrules": [
{
"name": "",
"descriptors": [
{
"name": "",
"for": "",
"value": "",
"type": ""
}
]
}
],
"selectors": [],
"values": [
{
"name": "",
"type": "",
"prose": "Optional description",
"value": "",
"values": [
{
"name": "",
"prose": "Optional Description",
"type": "",
"value": ""
}
]
}
],
"warnings": []
}
**Kind**: global constant
<a name="chromiumElementsShim"></a>
## chromiumElementsShim
This file will manage the updating of `autocomplete-html` `completions.json`
We will partially utilize `@webref/elements` `.listAll()` function that returns
a full list of HTML Elements along with a defined `interface`.
To use this `interface` in any meaningful way, we will utilize the dataset
of Attributes that apply to each `interface` from Chromiums DevTools resource
`https://github.com/ChromeDevTools/devtools-frontend`.
Finally from here we will utilize `https://github.com/mdn/content` to parse
the Markdown docs of MDN's website to retreive descriptions for each element.
Now for a summary of our `completions.json` file we aim to generate.
There are two top level elements, `tags` and `attributes`, both objects.
Within `tags` we expect the following:
"tags": {
"a": {
"attributes": [ "href", "hreflang", "media", "rel", "target", "type" ],
"description": "....."
}
};
When an entry contains no `attributes` there is no empty array, the element
simply doesn't exist.
The `attributes` object contains keys of different elements that themselves
are objects that can contain several valid keys.
- global: Seems to be used exclusively for Global Attributes. Is a boolean
which when false, the key does not appear.
- type: A ?type? for the attribute. It's meaning is not immediately known.
Nor a way to reliabley programatically collect it. Some discovered values:
cssStyle: Exclusively used for `class` attribute
boolean: Attributes that only accept `true` or `false`
flag: For attributes that don't require or accept values. eg autoplay
cssId: Exclusively used for the `id` attribute
color: Exclusively used for the `bgcolor` attribute
style: Exclusively used for the `style` attribute
- description: A text description of the attribute
- attribOption: A string array of valid values that can exist within the attribute.
Such as the case with `rel` where only so many valid options exist.
Although with our data sources mentioned above, we are able to collect nearly
all the data needed. Except the `type` that is defined within our
`completions.json` as well as the `attribOption` within our completions.
Studying these closer reveals that all attributes listing with our `completions.json`
do not appear elsewhere, and are nearly all global attributes.
In this case since there is no sane way to collect this data, we will leave this
list as a manually maintained section of our `completions.json`.
This does mean that `curated-attributes.json` is a static document that
will require manual updating in the future. Or most ideally, will find a way
to automatically generate the needed data.
**Kind**: global constant
<a name="update"></a>
## update
This file aims to run some short simple tests against `update.js`. Focusing
mainly on the Regex used within `sanitizeDescription()`
**Kind**: global constant
<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="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

@ -1,11 +0,0 @@
# Your init script
#
# Pulsar will evaluate this file each time a new window is opened. It is run
# after packages are loaded/activated and after the previous editor state
# has been restored.
#
# An example hack to log to the console when each text editor is saved.
#
# atom.workspace.observeTextEditors (editor) ->
# editor.onDidSave ->
# console.log "Saved! #{editor.getPath()}"

17
dot-atom/init.js Normal file
View File

@ -0,0 +1,17 @@
// Your init script
//
// Pulsar will evaluate this file each time a new window is opened. It is run
// after packages are loaded/activated and after the previous editor state
// has been restored.
//
// An example hack to log to the console when each text editor is saved.
//
// atom.workspace.observeTextEditors(editor =>
// editor.onDidSave(() =>
// 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

@ -7,14 +7,18 @@ const { expect } = require('@playwright/test')
async function openAtom(profilePath, videoName) {
const env = process.env
env.ATOM_HOME = path.join("tmp", profilePath)
env.APM_PATH = path.join("apm", "node_modules", "pulsar-package-manager", "bin", "apm")
const config = {
args: ["--no-sandbox", "."],
cwd: ".",
env: env,
timeout: 10000
timeout: 50000
}
if(env.BINARY_NAME) {
config.executablePath = env.BINARY_NAME
config.args = ["--no-sandbox"]
}
if(process.env.CI) {
config.recordVideo = {
dir: path.join('tests', 'videos', videoName)

View File

@ -52,24 +52,32 @@ test.describe('Opening Atom for the first time', () => {
test('the editor opens at the welcome page', async () => {
const workspace = editor.page.locator('atom-workspace')
await expect(workspace).toHaveText(/A hackable text editor/, {
await expect(workspace).toHaveText(/A Community-led Hyper-Hackable Text Editor/, {
useInnerText: true,
})
})
// FIXME: mock backend, Atom's servers are unreliable!
// test('allows to search for packages', async () => {
// await runCommand(editor, 'Settings View: Open')
// await editor.page.locator('a.icon', { hasText: 'Install' }).click()
// await typeInEditor(editor, '.packages', "language-javascript")
// await expect(editor.page.locator('.package-name', { hasText: 'language-javascript' }).first())
// .toBeVisible()
// })
//test('shows core packages', async () => {
// await runCommand(editor, 'Settings View: Open')
// await editor.page.locator('a.icon', { hasText: 'Packages' }).click()
// await expect(editor.page.locator('.package-name', { hasText: 'about' }).first())
// .toBeVisible()
//})
//test('allows to install for packages', async () => {
// await runCommand(editor, 'Settings View: Open')
// await editor.page.locator('a.icon', { hasText: 'Install' }).click()
// await typeInEditor(editor, '.packages', "termination")
// await editor.page.locator('button.install-button:visible', { hasText: 'Install' }).click()
// test.setTimeout(120000);
// await expect(editor.page.locator('button', { hasText: 'Settings' }).first())
// .toBeVisible({ timeout: 120000 })
//})
test.describe('the editor have syntax highlight', async () => {
test.beforeAll(async () => {
const workspace = editor.page.locator('atom-workspace')
await expect(workspace).toHaveText(/A hackable text editor/, {
await expect(workspace).toHaveText(/A Community-led Hyper-Hackable Text Editor/, {
useInnerText: true,
})
await runCommand(editor, 'Tabs: Close All Tabs')

View File

@ -155,7 +155,6 @@
{ label: 'Open In Dev Mode…', command: 'application:open-dev' }
{ label: 'Reload Window', command: 'window:reload' }
{ label: 'Run Package Specs', command: 'window:run-package-specs' }
{ label: 'Run Benchmarks', command: 'window:run-benchmarks' }
{ label: 'Toggle Developer Tools', command: 'window:toggle-dev-tools' }
]
}

View File

@ -138,7 +138,6 @@
{ label: 'Open In &Dev Mode…', command: 'application:open-dev' }
{ label: '&Reload Window', command: 'window:reload' }
{ label: 'Run Package &Specs', command: 'window:run-package-specs' }
{ label: 'Run &Benchmarks', command: 'window:run-benchmarks' }
{ label: 'Toggle Developer &Tools', command: 'window:toggle-dev-tools' }
]
}

View File

@ -137,7 +137,6 @@
{ label: 'Open In &Dev Mode…', command: 'application:open-dev' }
{ label: '&Reload Window', command: 'window:reload' }
{ label: 'Run Package &Specs', command: 'window:run-package-specs' }
{ label: 'Run &Benchmarks', command: 'window:run-benchmarks' }
{ label: 'Toggle Developer &Tools', command: 'window:toggle-dev-tools' }
]
}

11628
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,15 @@
{
"name": "pulsar",
"author": "Pulsar Community <noreply@pulsar-edit.com>",
"author": "Pulsar-Edit <admin@pulsar-edit.dev>",
"productName": "Pulsar",
"version": "1.63.0-dev",
"description": "A hackable text editor for the 21st Century.",
"version": "1.103.0-dev",
"description": "A Community-led Hyper-Hackable Text Editor",
"branding": {
"id": "pulsar",
"name": "Pulsar",
"urlWeb": "https://atom.io/",
"urlGH": "https://github.com/pulsar-edit"
"urlWeb": "https://pulsar-edit.dev/",
"urlGH": "https://github.com/pulsar-edit",
"urlForum": "https://github.com/orgs/pulsar-edit/discussions"
},
"main": "./src/main-process/main.js",
"repository": {
@ -24,7 +25,7 @@
"@atom/fuzzy-native": "^1.2.1",
"@babel/core": "7.18.6",
"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",
@ -33,57 +34,53 @@
"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",
"autosave": "file:./packages/autosave",
"babel-preset-atomic": "^5.0.0",
"background-tips": "https://codeload.github.com/atom/background-tips/legacy.tar.gz/refs/tags/v0.28.0",
"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://codeload.github.com/atom/bracket-matcher/legacy.tar.gz/refs/tags/v0.92.0",
"bookmarks": "file:packages/bookmarks",
"bracket-matcher": "file:packages/bracket-matcher",
"chai": "4.3.4",
"chart.js": "2.9.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",
"document-register-element": "https://github.com/pulsar-edit/document-register-element.git#1f5868f",
"encoding-selector": "file:packages/encoding-selector",
"etch": "0.14.1",
"event-kit": "^2.5.3",
"exception-reporting": "file:packages/exception-reporting",
"find-and-replace": "https://github.com/atom-community/find-and-replace/archive/refs/tags/v0.220.1.tar.gz",
"find-parent-dir": "^0.3.0",
"first-mate": "7.4.3",
"second-mate": "https://github.com/pulsar-edit/second-mate.git#14aa7bd",
"focus-trap": "6.3.0",
"fs-admin": "0.19.0",
"fs-plus": "^3.1.1",
"fstream": "1.0.12",
"fuzzaldrin": "^2.1",
"fuzzy-finder": "https://codeload.github.com/atom/fuzzy-finder/legacy.tar.gz/refs/tags/v1.14.3",
"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",
"jasmine-tagged": "^1.1.4",
"key-path-helpers": "^0.4.0",
"keybinding-resolver": "https://codeload.github.com/atom/keybinding-resolver/legacy.tar.gz/refs/tags/v0.39.1",
"keybinding-resolver": "file:./packages/keybinding-resolver",
"language-c": "file:packages/language-c",
"language-clojure": "file:packages/language-clojure",
"language-coffee-script": "file:packages/language-coffee-script",
@ -122,7 +119,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",
@ -136,45 +133,45 @@
"one-dark-ui": "file:packages/one-dark-ui",
"one-light-syntax": "file:packages/one-light-syntax",
"one-light-ui": "file:packages/one-light-ui",
"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",
"open-on-github": "file:packages/open-on-github",
"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.9",
"settings-view": "file:packages/settings-view",
"sinon": "9.2.1",
"snippets": "https://github.com/atom-community/snippets/archive/cdde11928c19d4969aba9a8dc5b1030ab68c4be2.tar.gz",
"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",
"superstring": "https://github.com/pulsar-edit/superstring.git#708924046f8db9d05901a5372337f249ff5c47bb",
"status-bar": "file:packages/status-bar",
"styleguide": "file:./packages/styleguide",
"superstring": "^2.4.4",
"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",
"text-buffer": "https://github.com/pulsar-edit/text-buffer.git#738c797a5aab45ca78c383e29fa545984e0a2977",
"tree-sitter": "https://github.com/pulsar-edit/node-tree-sitter.git#c304335",
"timecop": "https://codeload.github.com/atom/timecop/legacy.tar.gz/refs/tags/v0.36.2",
"tabs": "file:packages/tabs",
"temp": "0.9.4",
"text-buffer": "^13.18.6",
"timecop": "file:./packages/timecop",
"tree-sitter": "0.20.0",
"tree-view": "https://codeload.github.com/atom/tree-view/legacy.tar.gz/refs/tags/v0.229.1",
"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",
"whitespace": "file:./packages/whitespace",
"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 +187,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",
"bracket-matcher": "0.92.0",
"command-palette": "0.43.5",
"autosave": "file:./packages/autosave",
"background-tips": "file:./packages/background-tips",
"bookmarks": "file:./packages/bookmarks",
"bracket-matcher": "file:./packages/bracket-matcher",
"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",
"keybinding-resolver": "file:./packages/keybinding-resolver",
"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",
"snippets": "1.6.0",
"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",
"timecop": "0.36.2",
"tabs": "file:./packages/tabs",
"timecop": "file:./packages/timecop",
"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",
"whitespace": "file:./packages/whitespace",
"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 +268,23 @@
},
"private": true,
"scripts": {
"preinstall": "node -e 'process.exit(0)'",
"test": "node script/test",
"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",
"snapshotResult"
]
"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": "13.6.9",
"electron-builder": "23.3.1",
"electron-rebuild": "3.2.7",
"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

@ -1,167 +1,112 @@
# Atom Core Packages
# Core Packages
This folder contains core packages that are bundled with Atom releases. Not all Atom core packages are kept here; please
see the table below for the location of every core Atom package.
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 |
|---------|------------------|-----------------|
| **about** | [`./about`](./about) | [#17832](https://github.com/atom/atom/issues/17832) |
| **atom-dark-syntax** | [`./atom-dark-syntax`](./atom-dark-syntax) | [#17849](https://github.com/atom/atom/issues/17849) |
| **atom-dark-ui** | [`./atom-dark-ui`](./atom-dark-ui) | [#17850](https://github.com/atom/atom/issues/17850) |
| **atom-light-syntax** | [`./atom-light-syntax`](./atom-light-syntax) | [#17851](https://github.com/atom/atom/issues/17851) |
| **atom-light-ui** | [`./atom-light-ui`](./atom-light-ui) | [#17852](https://github.com/atom/atom/issues/17852) |
| **about** | [`./about`](./about) | |
| **atom-dark-syntax** | [`./atom-dark-syntax`](./atom-dark-syntax) | |
| **atom-dark-ui** | [`./atom-dark-ui`](./atom-dark-ui) | |
| **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] | |
| **autoflow** | [`./autoflow`](./autoflow) | [#17833](https://github.com/atom/atom/issues/17833) |
| **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) |
| **base16-tomorrow-dark-theme** | [`./base16-tomorrow-dark-theme`](./base16-tomorrow-dark-theme) | [#17836](https://github.com/atom/atom/issues/17836) |
| **base16-tomorrow-light-theme** | [`./base16-tomorrow-light-theme`](./base16-tomorrow-light-theme) | [#17837](https://github.com/atom/atom/issues/17837) |
| **bookmarks** | [`atom/bookmarks`][bookmarks] | [#18273](https://github.com/atom/atom/issues/18273) |
| **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) |
| **deprecation-cop** | [`./deprecation-cop`](./deprecation-cop) | [#17839](https://github.com/atom/atom/issues/17839) |
| **dev-live-reload** | [`./dev-live-reload`](dev-live-reload) | [#17840](https://github.com/atom/atom/issues/17840) |
| **encoding-selector** | [`atom/encoding-selector`][encoding-selector] | [#17841](https://github.com/atom/atom/issues/17841) |
| **exception-reporting** | [`./exception-reporting`](./exception-reporting) | [#17842](https://github.com/atom/atom/issues/17842) |
| **find-and-replace** | [`atom/find-and-replace`][find-and-replace] | |
| **fuzzy-finder** | [`atom/fuzzy-finder`][fuzzy-finder] | |
| **github** | [`atom/github`][github] | |
| **git-diff** | [`./git-diff`](./git-diff) | [#17843](https://github.com/atom/atom/issues/17843) |
| **go-to-line** | [`./go-to-line`](./go-to-line) | [#17844](https://github.com/atom/atom/issues/17844) |
| **grammar-selector** | [`./grammar-selector`](./grammar-selector) | [#17845](https://github.com/atom/atom/issues/17845) |
| **image-view** | [`atom/image-view`][image-view] | [#18274](https://github.com/atom/atom/issues/18274) |
| **incompatible-packages** | [`./incompatible-packages`](./incompatible-packages) | [#17846](https://github.com/atom/atom/issues/17846) |
| **keybinding-resolver** | [`atom/keybinding-resolver`][keybinding-resolver] | [#18275](https://github.com/atom/atom/issues/18275) |
| **language-c** | [`atom/language-c`](./language-c) | |
| **language-clojure** | [`atom/language-clojure`](./language-clojure) | |
| **language-coffee-script** | [`atom/language-coffee-script`](./language-coffee-script) | |
| **language-csharp** | [`atom/language-csharp`](./language-csharp) | |
| **language-css** | [`atom/language-css`](./language-css) | |
| **language-gfm** | [`atom/language-gfm`](./language-gfm) | |
| **language-git** | [`atom/language-git`](./language-git) | |
| **language-go** | [`atom/language-go`](./language-go) | |
| **language-html** | [`atom/language-html`](./language-html) | |
| **language-hyperlink** | [`atom/language-hyperlink`](./language-hyperlink) | |
| **language-java** | [`atom/language-java`](./language-java) | |
| **language-javascript** | [`atom/language-javascript`](./language-javascript) | |
| **language-json** | [`atom/language-json`](./language-json) | |
| **language-less** | [`atom/language-less`](./language-less) | |
| **language-make** | [`atom/language-make`](./language-make) | |
| **language-mustache** | [`atom/language-mustache`](./language-mustache) | |
| **language-objective-c** | [`atom/language-objective-c`](./language-objective-c) | |
| **language-perl** | [`atom/language-perl`](./language-perl) | |
| **language-php** | [`atom/language-php`](./language-php) | |
| **language-property-list** | [`atom/language-property-list`](./language-property-list) | |
| **language-python** | [`atom/language-python`](./language-python) | |
| **language-ruby** | [`atom/language-ruby`](./language-ruby) | |
| **language-ruby-on-rails** | [`atom/language-ruby-on-rails`](./language-ruby-on-rails) | |
| **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** | [`./autosave`](./autosave) | |
| **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** | [`./bookmarks`](./bookmarks) | |
| **bracket-matcher** | [`./bracket-matcher`](./bracket-matcher) | |
| **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** | [`./encoding-selector`](./encoding-selector) | |
| **exception-reporting** | [`./exception-reporting`](./exception-reporting) | |
| **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** | [`./image-view`](./image-view) | |
| **incompatible-packages** | [`./incompatible-packages`](./incompatible-packages) | |
| **keybinding-resolver** | [`./keybinding-resolver`](./keybinding-resolver) | |
| **language-c** | [`./language-c`](./language-c) | |
| **language-clojure** | [`./language-clojure`](./language-clojure) | |
| **language-coffee-script** | [`./language-coffee-script`](./language-coffee-script) | |
| **language-csharp** | [`./language-csharp`](./language-csharp) | |
| **language-css** | [`./language-css`](./language-css) | |
| **language-gfm** | [`./language-gfm`](./language-gfm) | |
| **language-git** | [`./language-git`](./language-git) | |
| **language-go** | [`./language-go`](./language-go) | |
| **language-html** | [`./language-html`](./language-html) | |
| **language-hyperlink** | [`./language-hyperlink`](./language-hyperlink) | |
| **language-java** | [`./language-java`](./language-java) | |
| **language-javascript** | [`./language-javascript`](./language-javascript) | |
| **language-json** | [`./language-json`](./language-json) | |
| **language-less** | [`./language-less`](./language-less) | |
| **language-make** | [`./language-make`](./language-make) | |
| **language-mustache** | [`./language-mustache`](./language-mustache) | |
| **language-objective-c** | [`./language-objective-c`](./language-objective-c) | |
| **language-perl** | [`./language-perl`](./language-perl) | |
| **language-php** | [`./language-php`](./language-php) | |
| **language-property-list** | [`./language-property-list`](./language-property-list) | |
| **language-python** | [`./language-python`](./language-python) | |
| **language-ruby** | [`./language-ruby`](./language-ruby) | |
| **language-ruby-on-rails** | [`./language-ruby-on-rails`](./language-ruby-on-rails) | |
| **language-rust-bundled** | [`./language-rust-bundled`](./language-rust-bundled) | |
| **language-sass** | [`atom/language-sass`](./language-sass) | |
| **language-shellscript** | [`atom/language-shellscript`](./language-shellscript) | |
| **language-source** | [`atom/language-source`](./language-source) | |
| **language-sql** | [`atom/language-sql`](./language-sql) | |
| **language-text** | [`atom/language-text`](./language-text) | |
| **language-todo** | [`atom/language-todo`](./language-todo) | |
| **language-toml** | [`atom/language-toml`](./language-toml) | |
| **language-typescript** | [`atom/language-typescript`](./language-typescript) | |
| **language-xml** | [`atom/language-xml`](./language-xml) | |
| **language-yaml** | [`atom/language-yaml`](./language-yaml) | |
| **line-ending-selector** | [`./packages/line-ending-selector`](./line-ending-selector) | [#17847](https://github.com/atom/atom/issues/17847) |
| **link** | [`./link`](./link) | [#17848](https://github.com/atom/atom/issues/17848) |
| **markdown-preview** | [`atom/markdown-preview`][markdown-preview] | |
| **language-sass** | [`./language-sass`](./language-sass) | |
| **language-shellscript** | [`./language-shellscript`](./language-shellscript) | |
| **language-source** | [`./language-source`](./language-source) | |
| **language-sql** | [`./language-sql`](./language-sql) | |
| **language-text** | [`./language-text`](./language-text) | |
| **language-todo** | [`./language-todo`](./language-todo) | |
| **language-toml** | [`./language-toml`](./language-toml) | |
| **language-typescript** | [`./language-typescript`](./language-typescript) | |
| **language-xml** | [`./language-xml`](./language-xml) | |
| **language-yaml** | [`./language-yaml`](./language-yaml) | |
| **line-ending-selector** | [`./line-ending-selector`](./line-ending-selector) | |
| **link** | [`./link`](./link) | |
| **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) | [#17853](https://github.com/atom/atom/issues/17853) |
| **one-dark-ui** | [`./one-dark-ui`](./one-dark-ui) | [#17854](https://github.com/atom/atom/issues/17854) |
| **one-light-syntax** | [`./one-light-syntax`](./one-light-syntax) | [#17855](https://github.com/atom/atom/issues/17855) |
| **one-light-ui** | [`./one-light-ui`](./one-light-ui) | [#17856](https://github.com/atom/atom/issues/17856) |
| **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] | |
| **solarized-dark-syntax** | [`./solarized-dark-syntax`](./solarized-dark-syntax) | [#18280](https://github.com/atom/atom/issues/18280) |
| **solarized-light-syntax** | [`./solarized-light-syntax`](./solarized-light-syntax) | [#18281](https://github.com/atom/atom/issues/18281) |
| **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** | [`./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] | |
| **update-package-dependencies** | [`./update-package-dependencies`](./update-package-dependencies) | [#18284](https://github.com/atom/atom/issues/18284) |
| **welcome** | [`./welcome`](./welcome) | [#18285](https://github.com/atom/atom/issues/18285) |
| **whitespace** | [`atom/whitespace`][whitespace] | |
| **wrap-guide** | [`atom/wrap-guide`][wrap-guide] | [#18286](https://github.com/atom/atom/issues/18286) |
| **status-bar** | [`./status-bar`](./status-bar) | |
| **styleguide** | [`./styleguide`](./styleguide) | |
| **symbols-view** | [`pulsar-edit/symbols-view`][symbols-view] | |
| **tabs** | [`./tabs`](./tabs) | |
| **timecop** | [`./timecop`](./timecop) | |
| **tree-view** | [`pulsar-edit/tree-view`][tree-view] | |
| **update-package-dependencies** | [`./update-package-dependencies`](./update-package-dependencies) | |
| **welcome** | [`./welcome`](./welcome) | |
| **whitespace** | [`./whitespace`](./whitespace) | |
| **wrap-guide** | [`./wrap-guide`](./wrap-guide) | |
[archive-view]: https://github.com/atom/archive-view
[autocomplete-atom-api]: https://github.com/atom/autocomplete-atom-api
[autocomplete-css]: https://github.com/atom/autocomplete-css
[autocomplete-html]: https://github.com/atom/autocomplete-html
[autocomplete-plus]: https://github.com/atom/autocomplete-plus
[autocomplete-snippets]: https://github.com/atom/autocomplete-snippets
[autosave]: https://github.com/atom/autosave
[background-tips]: https://github.com/atom/background-tips
[bookmarks]: https://github.com/atom/bookmarks
[bracket-matcher]: https://github.com/atom/bracket-matcher
[command-palette]: https://github.com/atom/command-palette
[encoding-selector]: https://github.com/atom/encoding-selector
[find-and-replace]: https://github.com/atom/find-and-replace
[fuzzy-finder]: https://github.com/atom/fuzzy-finder
[github]: https://github.com/atom/github
[image-view]: https://github.com/atom/image-view
[keybinding-resolver]: https://github.com/atom/keybinding-resolver
[language-c]: https://github.com/atom/language-c
[language-clojure]: https://github.com/atom/language-clojure
[language-coffee-script]: https://github.com/atom/language-coffee-script
[language-csharp]: https://github.com/atom/language-csharp
[language-css]: https://github.com/atom/language-css
[language-gfm]: https://github.com/atom/language-gfm
[language-git]: https://github.com/atom/language-git
[language-go]: https://github.com/atom/language-go
[language-html]: https://github.com/atom/language-html
[language-hyperlink]: https://github.com/atom/language-hyperlink
[language-java]: https://github.com/atom/language-java
[language-javascript]: https://github.com/atom/language-javascript
[language-json]: https://github.com/atom/language-json
[language-less]: https://github.com/atom/language-less
[language-make]: https://github.com/atom/language-make
[language-mustache]: https://github.com/atom/language-mustache
[language-objective-c]: https://github.com/atom/language-objective-c
[language-perl]: https://github.com/atom/language-perl
[language-php]: https://github.com/atom/language-php
[language-property-list]: https://github.com/atom/language-property-list
[language-python]: https://github.com/atom/language-python
[language-ruby]: https://github.com/atom/language-ruby
[language-ruby-on-rails]: https://github.com/atom/language-ruby-on-rails
[language-sass]: https://github.com/atom/language-sass
[language-shellscript]: https://github.com/atom/language-shellscript
[language-source]: https://github.com/atom/language-source
[language-sql]: https://github.com/atom/language-sql
[language-text]: https://github.com/atom/language-text
[language-todo]: https://github.com/atom/language-todo
[language-toml]: https://github.com/atom/language-toml
[language-typescript]: https://github.com/atom/language-typescript
[language-xml]: https://github.com/atom/language-xml
[language-yaml]: https://github.com/atom/language-yaml
[markdown-preview]: https://github.com/atom/markdown-preview
[notifications]: https://github.com/atom/notifications
[open-on-github]: https://github.com/atom/open-on-github
[package-generator]: https://github.com/atom/package-generator
[settings-view]: https://github.com/atom/settings-view
[snippets]: https://github.com/atom/snippets
[spell-check]: https://github.com/atom/spell-check
[status-bar]: https://github.com/atom/status-bar
[styleguide]: https://github.com/atom/styleguide
[symbols-view]: https://github.com/atom/symbols-view
[tabs]: https://github.com/atom/tabs
[timecop]: https://github.com/atom/timecop
[tree-view]: https://github.com/atom/tree-view
[whitespace]: https://github.com/atom/whitespace
[wrap-guide]: https://github.com/atom/wrap-guide
[autocomplete-atom-api]: https://github.com/pulsar-edit/autocomplete-atom-api
[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
[keybinding-resolver]: https://github.com/pulsar-edit/keybinding-resolver
[notifications]: https://github.com/pulsar-edit/notifications
[snippets]: https://github.com/pulsar-edit/snippets
[spell-check]: https://github.com/pulsar-edit/spell-check
[symbols-view]: https://github.com/pulsar-edit/symbols-view
[tree-view]: https://github.com/pulsar-edit/tree-view

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 hackable text editor for the 21st Century.
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

@ -31,7 +31,7 @@ module.exports = class AboutView extends EtchComponent {
handleReleaseNotesClick(e) {
e.preventDefault();
shell.openExternal(
this.props.updateManager.getReleaseNotesURLForAvailableVersion()
this.props.updateManager.getReleaseNotesURLForAvailableVersion() //update-manager.js will need updating when we decide how to do the changelog
);
}
@ -45,13 +45,14 @@ module.exports = class AboutView extends EtchComponent {
handleTermsOfUseClick(e) {
e.preventDefault();
shell.openExternal('https://atom.io/terms');
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) {
e.preventDefault();
shell.openExternal(
'https://flight-manual.atom.io/getting-started/sections/installing-atom/'
'https://pulsar-edit.dev/docs/launch-manual/sections/getting-started/#installing-pulsar'
);
}
@ -79,7 +80,7 @@ module.exports = class AboutView extends EtchComponent {
$.header(
{ className: 'about-header' },
$.a(
{ className: 'about-atom-io', href: 'https://atom.io' },
{ className: 'about-atom-io', href: `${atom.branding.urlWeb}`, },
$(AtomLogo)
),
$.div(
@ -177,33 +178,26 @@ module.exports = class AboutView extends EtchComponent {
},
'License'
),
$.button(
//Disabled the below as we don't have this but can reuse if there is the need
/*$.button(
{
className: 'btn terms-of-use',
onclick: this.handleTermsOfUseClick.bind(this)
},
'Terms of Use'
)
)*/
)
),
$.div(
{ className: 'about-love group-start' },
$.span({ className: 'icon icon-code' }),
$.a({ className: 'icon icon-code', href: `${atom.branding.urlGH}` }),
$.span({ className: 'inline' }, ' with '),
$.span({ className: 'icon icon-heart' }),
$.a({ className: 'icon icon-heart', href: `${atom.branding.urlWeb}` + "community" }),
$.span({ className: 'inline' }, ' by '),
$.a({ className: 'icon icon-logo-github', href: 'https://github.com' })
//$.a({ className: 'icon icon-logo-github', href: `${atom.branding.urlWeb}` }) Replace icon with Pulsar word logo and delete following line
$.a({ className: 'inline', href: `${atom.branding.urlWeb}` }, 'Pulsar Team')
),
$.div(
{ className: 'about-credits group-item' },
$.span({ className: 'inline' }, 'And the awesome '),
$.a(
{ href: 'https://github.com/pulsar-edit/pulsar/contributors' },
'Pulsar community'
)
)
);
}

View File

@ -1,3 +1,6 @@
/** @babel */
/** @jsx etch.dom */
const etch = require('etch');
const EtchComponent = require('../etch-component');
@ -5,75 +8,29 @@ const $ = etch.dom;
module.exports = class AtomLogo extends EtchComponent {
render() {
return $.svg(
{
className: 'about-logo',
width: '330px',
height: '68px',
viewBox: '0 0 330 68'
},
$.g(
{
stroke: 'none',
'stroke-width': '1',
fill: 'none',
'fill-rule': 'evenodd'
},
$.g(
{ transform: 'translate(2.000000, 1.000000)' },
$.g(
{
transform: 'translate(96.000000, 8.000000)',
fill: 'currentColor'
},
$.path({
d:
'M185.498,3.399 C185.498,2.417 186.34,1.573 187.324,1.573 L187.674,1.573 C188.447,1.573 189.01,1.995 189.5,2.628 L208.676,30.862 L227.852,2.628 C228.272,1.995 228.905,1.573 229.676,1.573 L230.028,1.573 C231.01,1.573 231.854,2.417 231.854,3.399 L231.854,49.403 C231.854,50.387 231.01,51.231 230.028,51.231 C229.044,51.231 228.202,50.387 228.202,49.403 L228.202,8.246 L210.151,34.515 C209.729,35.148 209.237,35.428 208.606,35.428 C207.973,35.428 207.481,35.148 207.061,34.515 L189.01,8.246 L189.01,49.475 C189.01,50.457 188.237,51.231 187.254,51.231 C186.27,51.231 185.498,50.458 185.498,49.475 L185.498,3.399 L185.498,3.399 Z'
}),
$.path({
d:
'M113.086,26.507 L113.086,26.367 C113.086,12.952 122.99,0.941 137.881,0.941 C152.77,0.941 162.533,12.811 162.533,26.225 L162.533,26.367 C162.533,39.782 152.629,51.792 137.74,51.792 C122.85,51.792 113.086,39.923 113.086,26.507 M158.74,26.507 L158.74,26.367 C158.74,14.216 149.89,4.242 137.74,4.242 C125.588,4.242 116.879,14.075 116.879,26.225 L116.879,26.367 C116.879,38.518 125.729,48.491 137.881,48.491 C150.031,48.491 158.74,38.658 158.74,26.507'
}),
$.path({
d:
'M76.705,5.155 L60.972,5.155 C60.06,5.155 59.287,4.384 59.287,3.469 C59.287,2.556 60.059,1.783 60.972,1.783 L96.092,1.783 C97.004,1.783 97.778,2.555 97.778,3.469 C97.778,4.383 97.005,5.155 96.092,5.155 L80.358,5.155 L80.358,49.405 C80.358,50.387 79.516,51.231 78.532,51.231 C77.55,51.231 76.706,50.387 76.706,49.405 L76.706,5.155 L76.705,5.155 Z'
}),
$.path({
d:
'M0.291,48.562 L21.291,3.05 C21.783,1.995 22.485,1.292 23.75,1.292 L23.891,1.292 C25.155,1.292 25.858,1.995 26.348,3.05 L47.279,48.421 C47.49,48.843 47.56,49.194 47.56,49.546 C47.56,50.458 46.788,51.231 45.803,51.231 C44.961,51.231 44.329,50.599 43.978,49.826 L38.219,37.183 L9.21,37.183 L3.45,49.897 C3.099,50.739 2.538,51.231 1.694,51.231 C0.781,51.231 0.008,50.529 0.008,49.685 C0.009,49.404 0.08,48.983 0.291,48.562 L0.291,48.562 Z M36.673,33.882 L23.749,5.437 L10.755,33.882 L36.673,33.882 L36.673,33.882 Z'
})
),
$.g(
{},
$.path({
d:
'M40.363,32.075 C40.874,34.44 39.371,36.77 37.006,37.282 C34.641,37.793 32.311,36.29 31.799,33.925 C31.289,31.56 32.791,29.23 35.156,28.718 C37.521,28.207 39.851,29.71 40.363,32.075',
fill: 'currentColor'
}),
$.path({
d:
'M48.578,28.615 C56.851,45.587 58.558,61.581 52.288,64.778 C45.822,68.076 33.326,56.521 24.375,38.969 C15.424,21.418 13.409,4.518 19.874,1.221 C22.689,-0.216 26.648,1.166 30.959,4.629',
stroke: 'currentColor',
'stroke-width': '3.08',
'stroke-linecap': 'round'
}),
$.path({
d:
'M7.64,39.45 C2.806,36.94 -0.009,33.915 0.154,30.79 C0.531,23.542 16.787,18.497 36.462,19.52 C56.137,20.544 71.781,27.249 71.404,34.497 C71.241,37.622 68.127,40.338 63.06,42.333',
stroke: 'currentColor',
'stroke-width': '3.08',
'stroke-linecap': 'round'
}),
$.path({
d:
'M28.828,59.354 C23.545,63.168 18.843,64.561 15.902,62.653 C9.814,58.702 13.572,42.102 24.296,25.575 C35.02,9.048 48.649,-1.149 54.736,2.803 C57.566,4.639 58.269,9.208 57.133,15.232',
stroke: 'currentColor',
'stroke-width': '3.08',
'stroke-linecap': 'round'
})
)
)
)
);
return (
<svg className='about-logo' xmlns="http://www.w3.org/2000/svg" version="1.1" x="0px" y="0px" viewBox="0 0 570.40002 191.69999" width="570.40002" height="100">
<defs id="defs400"></defs>
<path
fill="currentColor"
id="path42"
d="m 95.9,72.6 c -12.8,0 -23.2,10.4 -23.2,23.2 0,12.8 10.4,23.2 23.2,23.2 12.8,0 23.2,-10.4 23.2,-23.2 0,-12.8 -10.4,-23.2 -23.2,-23.2 z"
class="st0" />
<path fill="currentColor" id="path44" d="m 67.4,127.7 c 0.8,0.3 1.6,0.5 2.4,0.5 1.6,0 3.1,-0.6 4.3,-1.8 2.4,-2.4 2.4,-6.3 0,-8.7 -1.2,-1.2 -2.8,-1.8 -4.4,-1.8 -1.6,0 -3.2,0.6 -4.4,1.8 -1.8,1.8 -2.3,4.5 -1.3,6.7 l -12.4,4.8 -51.6,61.4 1.1,1.1 61.4,-51.6 z" class="st0" />
<path fill="currentColor" id="path46" d="m 58,152.1 c 5.4,4.6 10.9,8.7 16.7,12.4 2.1,-0.8 6.7,-2.6 8.7,-3.8 -7.1,-4.1 -12.8,-8.3 -19.3,-13.8 z" class="st0" />
<path fill="currentColor" id="path48" d="M 129.2,51.6 124.4,64 c -0.8,-0.3 -1.6,-0.5 -2.3,-0.5 -1.6,0 -3.1,0.6 -4.3,1.8 -2.4,2.4 -2.4,6.3 0,8.7 1.2,1.2 2.8,1.8 4.4,1.8 1.6,0 3.2,-0.6 4.4,-1.8 1.8,-1.8 2.3,-4.5 1.3,-6.7 L 140.3,62.5 191.9,1.1 190.8,0 Z" class="st0" />
<path fill="currentColor" id="path50" d="m 121.3,54.8 c -7.4,-4.6 -16.1,-7.3 -25.4,-7.3 -26.6,0 -48.3,21.7 -48.3,48.3 0,9.3 2.7,18 7.3,25.4 l 2.6,-1 c 0.4,-2.6 1.6,-5 3.5,-7 0.4,-0.4 0.8,-0.7 1.3,-1.1 -2.4,-4.9 -3.8,-10.5 -3.8,-16.3 0,-20.6 16.8,-37.4 37.4,-37.4 5.9,0 11.4,1.4 16.3,3.8 0.3,-0.4 0.6,-0.9 1,-1.3 1.9,-1.9 4.3,-3.1 7,-3.5 z" class="st0" />
<path fill="currentColor" id="path52" d="m 95.9,133.2 c -5.9,0 -11.4,-1.4 -16.3,-3.8 -0.3,0.4 -0.6,0.9 -1,1.3 -1.9,1.9 -4.3,3.1 -7,3.5 l -1,2.6 c 7.4,4.6 16.1,7.3 25.4,7.3 26.6,0 48.3,-21.7 48.3,-48.3 0,-9.3 -2.7,-18 -7.3,-25.4 l -2.6,1 c -0.4,2.6 -1.6,5 -3.5,7 -0.4,0.4 -0.8,0.7 -1.3,1.1 2.4,4.9 3.8,10.5 3.8,16.3 -0.1,20.7 -16.9,37.4 -37.5,37.4 z" class="st0" />
<path fill="currentColor" id="path54" d="m 16.2,95.9 c -10,24.6 -10.5,48.8 0.1,65.7 l 5.3,-6.3 c -7.2,-13.1 -7.2,-30.8 -0.9,-49.5 5,9.7 11.4,19.2 19,28 l 5.1,-6 C 36.3,117.7 29.5,106.9 24.6,95.9 31,81.5 40.9,67 54,53.9 66.8,41.1 81.2,31.1 95.9,24.5 c 10.8,4.8 21.7,11.5 32,20.2 l 6,-5.1 c -9,-7.7 -18.6,-14 -28.1,-18.9 3.2,-1.1 6.3,-2 9.4,-2.7 15.8,-3.6 29.6,-2.3 40.2,3.5 l 6.3,-5.3 C 149.2,8.2 132.4,6.1 113.5,10.4 107.6,11.7 101.7,13.6 95.8,16.1 67.2,4.4 39.2,5.7 22.5,22.4 9.6,35.3 5.3,55.2 10.5,78.3 c 1.3,5.9 3.3,11.8 5.7,17.6 z m 11.7,-68 C 41.1,14.7 62.8,12.8 86,20.6 72.8,27.4 59.9,36.8 48.4,48.4 36.7,60.1 27.4,73 20.7,86 19.7,82.9 18.7,79.8 18,76.6 13.5,56.1 17,38.8 27.9,27.9 Z" class="st0" />
<path fill="currentColor" id="path56" d="m 147.1,63.9 c 8.6,10.3 15.4,21.2 20.2,31.9 l -0.1,0.3 c -6.6,14.7 -16.7,29.1 -29.2,41.6 -18.4,18.4 -40.1,31.1 -61.2,35.9 -15.8,3.6 -29.6,2.3 -40.2,-3.5 l -6.3,5.3 c 8.3,5.3 18.4,8 29.7,8 5.9,0 12.1,-0.7 18.5,-2.2 22.5,-5.1 45.6,-18.5 65,-37.9 11.4,-11.4 20.9,-24.3 27.8,-37.6 7.8,23.2 5.9,44.9 -7.3,58.1 -10,10 -25.7,13.7 -43.8,10.8 -3.4,2.1 -6.8,4 -10.3,5.7 7.7,2 15.1,3.1 22,3.1 15.2,0 28.3,-4.8 37.6,-14.1 C 193.7,145.1 185.6,97 152.3,57.9 Z" class="st0" />
<path fill="currentColor" id="path58" d="m 170.2,36.4 c 4.4,8.1 6.1,18.2 5.1,29.5 2.3,3.6 4.4,7.3 6.2,10.9 3.8,-18.3 1.7,-34.5 -6.1,-46.8 z" class="st0" />
<path fill="currentColor" id="path60" d="m 263.9,70.6 h -41.6 v 56.7 h 9.7 v -19.7 h 0.7 l 31.1,-0.1 c 3,0 5.5,-1.1 7.6,-3.2 2.1,-2.1 3.2,-4.6 3.2,-7.5 V 81.3 c 0,-2.9 -1.1,-5.5 -3.2,-7.5 -2,-2.1 -4.6,-3.2 -7.5,-3.2 z m 1,26.3 c 0,0.6 -0.4,1 -1,1 h -30.8 c -0.6,0 -1,-0.4 -1,-1 V 81.4 c 0,-0.6 0.4,-1 1,-1 h 30.8 c 0.6,0 1,0.4 1,1 z" class="st0" />
<path fill="currentColor" id="path62" d="m 324.5,116.7 c 0,0.7 -0.5,1 -0.9,1 h -30.9 c -0.6,0 -1,-0.4 -1,-1 V 70.6 H 282 v 46.1 c 0,3 1,5.5 3.1,7.6 2.1,2.1 4.6,3.1 7.6,3.1 h 30.9 c 2.9,0 5.5,-1.1 7.5,-3.1 2.1,-2.1 3.2,-4.6 3.2,-7.6 V 70.6 h -9.8 z" class="st0" />
<polygon transform="translate(697.6,5098.3)" fill="currentColor" id="polygon64" points="-353.6,-4970.9 -307.4,-4970.9 -307.4,-4980.7 -343.8,-4980.7 -343.8,-5027.8 -353.6,-5027.8 " class="st0" />
<path fill="currentColor" id="path66" d="m 436.4,70.5 h -30.9 c -3,0 -5.5,1.1 -7.6,3.1 -2.1,2.1 -3.1,4.6 -3.1,7.6 v 11.9 c 0,3 1,5.5 3.1,7.6 2.1,2.1 4.6,3.1 7.6,3.1 h 30.9 c 0.4,0 0.9,0.3 0.9,1 v 11.9 c 0,0.7 -0.5,1 -0.9,1 h -30.9 c -0.6,0 -1,-0.4 -1,-1 v -4 h -9.7 v 4 c 0,3 1,5.5 3.1,7.6 2.1,2.1 4.6,3.1 7.6,3.1 h 30.9 c 2.9,0 5.5,-1.1 7.5,-3.1 2.1,-2.1 3.2,-4.6 3.2,-7.6 v -11.9 c 0,-3 -1.1,-5.5 -3.2,-7.6 -2.1,-2.1 -4.6,-3.1 -7.5,-3.1 h -30.9 c -0.6,0 -1,-0.4 -1,-1 V 81.2 c 0,-0.6 0.4,-1 1,-1 h 30.9 c 0.4,0 0.9,0.3 0.9,1 v 4 h 9.8 v -4 c 0,-3 -1.1,-5.5 -3.2,-7.6 -2,-2 -4.6,-3.1 -7.5,-3.1 z" class="st0" />
<path fill="currentColor" id="path68" d="m 498.3,70.5 h -30.8 c -3,0 -5.5,1.1 -7.6,3.1 -2.1,2.1 -3.1,4.6 -3.1,7.6 v 46.1 h 9.7 v -19.4 h 32.8 v 19.4 h 9.8 V 81.2 c 0,-3 -1.1,-5.5 -3.2,-7.6 -2.1,-2 -4.7,-3.1 -7.6,-3.1 z m 1,27.8 h -32.8 v -17 c 0,-0.6 0.4,-1 1,-1 h 30.8 c 0.6,0 1,0.4 1,1 z" class="st0" />
<path fill="currentColor" id="path70" d="m 567.2,104.4 c 2.1,-2.1 3.2,-4.6 3.2,-7.5 V 81.4 c 0,-2.9 -1.1,-5.5 -3.2,-7.5 -2.1,-2.1 -4.7,-3.2 -7.6,-3.2 h -41.5 v 56.7 h 9.7 v -19.8 h 15 l 0.1,0.2 16.5,19.6 h 10.9 v -2.1 l -14.8,-17.6 h 4 c 3.1,-0.1 5.6,-1.2 7.7,-3.3 z m -38.4,-6.5 c -0.6,0 -1,-0.4 -1,-1 V 81.4 c 0,-0.6 0.4,-1 1,-1 h 30.8 c 0.6,0 1,0.4 1,1 v 15.5 c 0,0.6 -0.4,1 -1,1 z" class="st0" />
</svg>
)
}
};

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

@ -4,21 +4,14 @@
"main": "./lib/main",
"version": "1.9.1",
"description": "View useful information about your Pulsar installation.",
"keywords": [],
"repository": "https://github.com/pulsar-edit/pulsar",
"license": "MIT",
"scripts": {
"lint": "standard"
},
"engines": {
"atom": ">=1.7 <2.0.0"
},
"dependencies": {
"etch": "0.9.0",
"semver": "^5.5.0"
},
"devDependencies": {
"standard": "^11.0.0"
"etch": "^0.14.1",
"semver": "^7.3.8"
},
"consumedServices": {
"status-bar": {
@ -29,16 +22,5 @@
},
"deserializers": {
"AboutView": "deserializeAboutView"
},
"standard": {
"env": [
"browser",
"node",
"atomtest",
"jasmine"
],
"globals": [
"atom"
]
}
}

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('Atom 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('Atom 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,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,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,10 @@
# 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 completions by running `node update.js` at the root of this package and checking for changes within `completions.json`. This does rely on having dev dependencies installed, so ensure you install all dependencies before doing so.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,163 @@
// Due to the complexity of CSS Value Definition Syntax
// https://developer.mozilla.org/en-US/docs/Web/CSS/Value_definition_syntax
// We will go ahead and create a small parser for handling it.
// This parser is only intended to receive some syntax, and spit out an array
// of all valid value identifiers within it. Ignoring all special conventions of
// the syntax.
class CSSParser {
constructor(input) {
this.index = 0;
this.value = input;
this.out = [];
// Manage States
this.buffer = ""; // Used to store uncomplete values while looping.
// Definitions
this.keywords = {
"*": "Asterisk Multiplier",
"+": "Plus Sign Multiplier",
"?": "Question Mark Multiplier",
"#": "Hash Mark Multiplier",
"!": "Exclamation Point Multiplier"
};
this.separators = {
"&&": "Double Ampersand Combinator",
"||": "Double Bar Combinator",
"|": "Single Bar Combinator",
"[": "Open Bracket Combinator",
"]": "Close Bracket Combinator",
" ": "Juxtaposition Combinator",
"/": "Undocumented Seperator?"
};
this.startFold = "<"; // A foldable item would mean to stop parsing within.
this.endFold = ">";
this.startDiscardable = "{";
this.endDiscardable = "}";
}
parse() {
let cur = this.cur();
if (this.index === this.value.length || this.index > this.value.length) {
// We have hit the end of our index. Lets return
this.offLoadBuffer();
return this.out;
}
if (this.isStartDiscardable()) {
// We don't care about what's in here, until we hit the end of our discardable
this.offLoadBuffer();
while(!this.isEndDiscardable()) {
this.next();
}
this.next();
return this.parse();
}
if (this.isKeyword().status) {
// We don't actually care about keywords.
this.offLoadBuffer();
this.next(this.isKeyword().who.length);
return this.parse();
}
if (this.isSeparators().status) {
// We don't actually care about seperators
this.offLoadBuffer();
this.next(this.isSeparators().who.length);
return this.parse();
}
if (this.isStartFold()) {
let tmpValue = "";
while(!this.isEndFold()) {
tmpValue += this.cur();
this.next();
}
tmpValue += this.cur();
this.out.push(tmpValue);
this.next();
return this.parse();
}
if (!this.isStartDiscardable() && !this.isEndDiscardable() && !this.isKeyword().status && !this.isSeparators().status && !this.isStartFold() && !this.isEndFold()) {
this.buffer += this.cur();
this.next();
return this.parse();
}
}
offLoadBuffer() {
if (this.buffer.length > 0) {
this.out.push(this.buffer);
this.buffer = "";
}
}
isKeyword() {
for (const name in this.keywords) {
if (this.keywords.hasOwnProperty(name) && this.value.substr(this.index, name.length) === name) {
return { status: true, who: name };
}
}
return { status: false };
}
isSeparators() {
for (const name in this.separators) {
if (this.separators.hasOwnProperty(name) && this.value.substr(this.index, name.length) === name) {
return { status: true, who: name };
}
}
return { status: false };
}
isStartFold() {
if (this.cur() === this.startFold) {
return true;
} else {
return false;
}
}
isEndFold() {
if (this.cur() === this.endFold) {
return true;
} else {
return false;
}
}
isStartDiscardable() {
if (this.cur() === this.startDiscardable) {
return true;
} else {
return false;
}
}
isEndDiscardable() {
if (this.cur() === this.endDiscardable) {
return true;
} else {
return false;
}
}
cur() {
return this.value.charAt(this.index);
}
next(amount) {
let increase = amount ?? 1;
this.index = this.index + increase;
return this.value.charAt(this.index);
}
}
module.exports = CSSParser;

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,898 @@
{
"-webkit-align-items": {
"desc": "Legacy Alias of align-items.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-align-content": {
"desc": "Legacy Alias of align-content.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-align-self": {
"desc": "Legacy Alias of align-self.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-animation-name": {
"desc": "Legacy Alias of animation-name.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-animation-duration": {
"desc": "Legacy Alias of animation-duration.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-animation-timing-function": {
"desc": "Legacy Alias of animation-timing-function.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-animation-iteration-count": {
"desc": "Legacy Alias of animation-iteration-count.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-animation-direction": {
"desc": "Legacy Alias of animation-direction.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-animation-play-state": {
"desc": "Legacy Alias of animation-play-state.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-animation-delay": {
"desc": "Legacy Alias of animation-delay.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-animation-fill-mode": {
"desc": "Legacy Alias of animation-fill-mode.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-animation": {
"desc": "Legacy Alias of animation.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-backface-visibility": {
"desc": "Legacy Alias of backface-visibility.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-background-clip": {
"desc": "Legacy Alias of background-clip.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-background-origin": {
"desc": "Legacy Alias of background-origin.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-background-size": {
"desc": "Improper implementation of a legacy alias for background-size.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-border-bottom-left-radius": {
"desc": "Legacy Alias of border-bottom-left-radius.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-border-bottom-right-radius": {
"desc": "Legacy Alias of border-bottom-right-radius.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-border-top-left-radius": {
"desc": "Legacy Alias of border-top-left-radius.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-border-top-right-radius": {
"desc": "Legacy Alias of border-top-right-radius.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-border-radius": {
"desc": "Legacy Alias of border-radius.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-box-shadow": {
"desc": "Legacy Alias of box-shadow.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-box-sizing": {
"desc": "Legacy Alias of box-sizing.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-flex": {
"desc": "Legacy Alias of flex.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-flex-basis": {
"desc": "Legacy Alias of flex-basis.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-flex-direction": {
"desc": "Legacy Alias of flex-direction.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-flex-flow": {
"desc": "Legacy Alias of flex-flow.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-flex-grow": {
"desc": "Legacy Alias of flex-grow.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-flex-shrink": {
"desc": "Legacy Alias of flex-shrink.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-flex-wrap": {
"desc": "Legacy Alias of flex-wrap.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-filter": {
"desc": "Legacy Alias of filter.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-justify-content": {
"desc": "Legacy Alias of justify-content.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-mask": {
"desc": "Legacy Alias of mask.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-mask-box-image-outset": {
"desc": "Legacy Alias of mask-border-outset.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-mask-box-image-repeat": {
"desc": "Legacy Alias of mask-border-repeat.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-mask-box-image-slice": {
"desc": "Legacy Alias of mask-border-slice.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-mask-box-image-source": {
"desc": "Legacy Alias of mask-border-source.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-mask-box-image-width": {
"desc": "Legacy Alias of mask-border-width.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-mask-clip": {
"desc": "Legacy Alias of mask-clip.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-mask-image": {
"desc": "Legacy Alias of mask-image.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-mask-origin": {
"desc": "Legacy Alias of mask-origin.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-mask-position": {
"desc": "Legacy Alias of mask-position.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-mask-repeat": {
"desc": "Legacy Alias of mask-repeat.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-mask-size": {
"desc": "Legacy Alias of mask-size.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-order": {
"desc": "Legacy Alias of order.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-perspective": {
"desc": "Legacy Alias of perspective.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-perspective-origin": {
"desc": "Legacy Alias of perspective-origin.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-transform-origin": {
"desc": "Legacy Alias of transform-origin.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-transform-style": {
"desc": "Legacy Alias of transform-style.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-transform": {
"desc": "Legacy Alias of transform.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-transition-delay": {
"desc": "Legacy Alias of transition-delay.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-transition-duration": {
"desc": "Legacy Alias of transition-duration.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-transition-property": {
"desc": "Legacy Alias of transition-property.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-transition-timing-function": {
"desc": "Legacy Alias of transition-timing-function.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-transition": {
"desc": "Legacy Alias of transition.",
"spec": "https://compat.spec.whatwg.org/#css-simple-aliases"
},
"-webkit-appearance": {
"desc": "Legacy Name Alias of appearance.",
"spec": "https://drafts.csswg.org/css-ui/#appearance-switching"
},
"-webkit-user-select": {
"desc": "Alias shorthand property of user-select.",
"spec": "https://drafts.csswg.org/css-ui/#propdef--webkit-user-select"
},
"-webkit-text-size-adjust": {
"desc": "Vendor Prefixed Legacy Name Alias. (-vendorPrefix-)text-size-adjust",
"spec": "https://compat.spec.whatwg.org/#css-prefixed-aliases"
},
"-webkit-box-align": {
"desc": "Vendor prefixed property mapping to align-items.",
"spec": "https://compat.spec.whatwg.org/#css-property-mappings"
},
"-webkit-box-flex": {
"desc": "Vendor prefixed property mapping to flex-grow.",
"spec": "https://compat.spec.whatwg.org/#css-property-mappings"
},
"-webkit-box-ordinal-group": {
"desc": "Vendor prefixed property mapping to order.",
"spec": "https://compat.spec.whatwg.org/#css-property-mappings"
},
"-webkit-box-orient": {
"desc": "Vendor prefixed property mapping to flex-direction.",
"spec": "https://compat.spec.whatwg.org/#css-property-mappings"
},
"-webkit-box-pack": {
"desc": "Vendor prefixed property mapping to justify-content.",
"spec": "https://compat.spec.whatwg.org/#css-property-mappings"
},
"fill-break": {
"desc": "This property specifies how the geometry of a fragmented box is treated for fills.",
"spec": "https://drafts.fxtf.org/fill-stroke-3/#fill-break"
},
"fill-image": {
"desc": "This property sets the fill images of an element. Images are drawn with the first specified one on top (closest to the user) and each subsequent image behind the previous one.",
"spec": "https://drafts.fxtf.org/fill-stroke-3/#fill-image"
},
"fill-origin": {
"desc": "This property specifies the coordinate system of the fill, setting the fill positioning area.",
"spec": "https://drafts.fxtf.org/fill-stroke-3/#fill-origin"
},
"fill-position": {
"desc": "If fill images have been specified, this property specifies their initial position (after any resizing) within their corresponding fill positioning area.",
"spec": "https://drafts.fxtf.org/fill-stroke-3/#fill-position"
},
"fill-size": {
"desc": "Specifies the size of the fill images. Values are interpreted identically to background-size, mutatis mutandi.",
"spec": "https://drafts.fxtf.org/fill-stroke-3/#fill-size"
},
"fill-repeat": {
"desc": "Specifies how fill images are tiled after they have been sized and positioned.",
"spec": "https://drafts.fxtf.org/fill-stroke-3/#fill-repeat"
},
"stroke-align": {
"desc": "This property allows the author to align a stroke along the outline.",
"spec": "https://drafts.fxtf.org/fill-stroke-3/#stroke-align"
},
"stroke-break": {
"desc": "This property specifies how the geometry of a fragmented box is treated for strokes.",
"spec": "https://drafts.fxtf.org/fill-stroke-3/#stroke-break"
},
"stroke-dash-corner": {
"desc": "The stroke-dash-corner property controls whether a dash is always painted at the vertices of a stroked shape.",
"spec": "https://drafts.fxtf.org/fill-stroke-3/#propdef-stroke-dash-corner"
},
"stroke-dash-justify": {
"desc": "The stroke-dash-justify property specifies whether and how a stroke's dash pattern will be adjusted so that it is repeated a whole number of times along each of an element's subpaths.",
"spec": "https://drafts.fxtf.org/fill-stroke-3/#propdef-stroke-dash-justify"
},
"stroke-color": {
"desc": "This property sets the stroke colors of an element.",
"spec": "https://drafts.fxtf.org/fill-stroke-3/#stroke-color"
},
"stroke-image": {
"desc": "This property sets the stroke images of an element.",
"spec": "https://drafts.fxtf.org/fill-stroke-3/#stroke-image"
},
"stroke-origin": {
"desc": "This property specifies the coordinate system of the stroke, setting the stroke positioning area.",
"spec": "https://drafts.fxtf.org/fill-stroke-3/#stroke-origin"
},
"stroke-position": {
"desc": "If stroke images have been specified, this property specifies their initial position (after any resizing) within their corresponding stroke positioning area.",
"spec": "https://drafts.fxtf.org/fill-stroke-3/#stroke-position"
},
"stroke-size": {
"desc": "Specifies the size of the stroke images.",
"spec": "https://drafts.fxtf.org/fill-stroke-3/#stroke-size"
},
"stroke-repeat": {
"desc": "Specifies how stroke fill images are tiled after they have been sized and positioned.",
"spec": "https://drafts.fxtf.org/fill-stroke-3/#stroke-repeat"
},
"shape-subtract": {
"desc": "The shape-subtract property allows one to exclude part of the content area from the wrapping area. The excluded area is the addition of all the areas defined in a list of CSS basic shapes and/or SVG shapes.",
"spec": "https://svgwg.org/svg2-draft/text.html#TextShapeSubtract"
},
"stroke-alignment": {
"desc": "This property allows the author to align a stroke along the outline of the current object.",
"spec": "https://svgwg.org/specs/strokes/#SpecifyingStrokeAlignment"
},
"stroke-dashcorner": {
"desc": "The stroke-dashcorner property controls whether a dash is always painted at the vertices of a stroked shape.",
"spec": "https://svgwg.org/specs/strokes/#StrokeDashcornerProperty"
},
"stroke-dashadjust": {
"desc": "The stroke-dashadjust property specifies whether and how a stroke's dash pattern will be adjusted so that it is repeated a whole number of times along an element's subpaths.",
"spec": "https://svgwg.org/specs/strokes/#StrokeDashadjustProperty"
},
"grid-row-gap": {
"desc": "Shorthand for the row-gap property.",
"spec": "https://drafts.csswg.org/css-align-3/#gap-legacy"
},
"grid-column-gap": {
"desc": "Shorthand for the column-gap property.",
"spec": "https://drafts.csswg.org/css-align-3/#gap-legacy"
},
"grid-gap": {
"desc": "Shorthand for the gap property.",
"spec": "https://drafts.csswg.org/css-align-3/#gap-legacy"
},
"anchor-scroll": {
"desc": "The anchor-scroll property allows an author to compensate for none aligned positioned elements and their anchor without losing the performance benefits of the separate scrolling thread.",
"spec": "https://drafts.csswg.org/css-anchor-position-1/#scroll"
},
"anchor-name": {
"desc": "Allows anchor functions to refer to an anchor element by name.",
"spec": "https://drafts.csswg.org/css-anchor-position-1/#determining"
},
"anchor-default": {
"desc": "The anchor-default property defines the default anchor specifier for all anchor functions on the element.",
"spec": "https://drafts.csswg.org/css-anchor-position-1/#anchor-default"
},
"position-fallback": {
"desc": "Provides blocks of style rules to try out. The first that doesn't cause the element to overflow its containing block is taken as the winner.",
"spec": "https://drafts.csswg.org/css-anchor-position-1/#fallback-property"
},
"background-position-inline": {
"desc": "This property specifies the background position's inline-axis component.",
"spec": "https://drafts.csswg.org/css-backgrounds-4/#background-position-longhands"
},
"background-position-block": {
"desc": "This property specifies the background position's block-axis component.",
"spec": "https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-block"
},
"corner-shape": {
"desc": "The corner-shape property specifies a reinterpretation of the radii to define other corner shapes.",
"spec": "https://drafts.csswg.org/css-backgrounds-4/#corner-shaping"
},
"corners": {
"desc": "The corners shorthand sets corner-shape and border-radius in the same declaration.",
"spec": "https://drafts.csswg.org/css-backgrounds-4/#corners-shorthand"
},
"border-limit": {
"desc": "Specifies which part of the border is rendered.",
"spec": "https://drafts.csswg.org/css-backgrounds-4/#border-limit"
},
"border-clip": {
"desc": "Shorthand for border-clip-top, border-clip-right, border-clip-bottom, and border-clip-left.",
"spec": "https://drafts.csswg.org/css-backgrounds-4/#border-clip"
},
"border-clip-top": {
"desc": "Splits the top border into parts along the border edge.",
"spec": "https://drafts.csswg.org/css-backgrounds-4/#border-clip"
},
"border-clip-right": {
"desc": "Splits the right border into parts along the border edge.",
"spec": "https://drafts.csswg.org/css-backgrounds-4/#border-clip"
},
"border-clip-bottom": {
"desc": "Splits the bottom border into parts along the border edge.",
"spec": "https://drafts.csswg.org/css-backgrounds-4/#border-clip"
},
"border-clip-left": {
"desc": "Splits the left border into parts along the border edge.",
"spec": "https://drafts.csswg.org/css-backgrounds-4/#border-clip"
},
"voice-family": {
"desc": "The voice-family property specifies a prioritized list of component values that are separated by commas to indicate that they are alternatives.",
"spec": "https://drafts.csswg.org/css-speech-1/#voice-props-voice-family"
},
"voice-rate": {
"desc": "The voice-rate property manipulates the rate of generated synthetic speech in terms of words per minute.",
"spec": "https://drafts.csswg.org/css-speech-1/#voice-props-voice-rate"
},
"voice-pitch": {
"desc": "The voice-pitch property specifies the 'baseline' pitch of the generated speech output, which depends on the user 'voice-family' instance, and varies across speech synthesis processors.",
"spec": "https://drafts.csswg.org/css-speech-1/#voice-props-voice-pitch"
},
"voice-range": {
"desc": "The voice-range property specifies the variability in the 'baseline' pitch.",
"spec": "https://drafts.csswg.org/css-speech-1/#voice-props-voice-range"
},
"voice-stress": {
"desc": "The voice-stress property manipulates the strength of emphasis, which is normally applied using a combination of pitch change, timing changes, loudness and other acoustic differences.",
"spec": "https://drafts.csswg.org/css-speech-1/#voice-props-voice-stress"
},
"voice-duration": {
"desc": "The voice-duration property specifies how long it should take to render the selected element's content.",
"spec": "https://drafts.csswg.org/css-speech-1/#mixing-props-voice-duration"
},
"voice-volume": {
"desc": "The voice-volume property allows authors to control the amplitude of the audio waveform generated by the speech synthesizer, and is also used to adjust the relative volume level of audio cues within the aural box model of the selected element.",
"spec": "https://drafts.csswg.org/css-speech-1/#mixing-props-voice-volume"
},
"voice-balance": {
"desc": "The voice-balance property controls the spatial distribution of audio output across a lateral sound stage: one extremity is on the left, the other extremity is on the right hand side, relative to the listeners position.",
"spec": "https://drafts.csswg.org/css-speech-1/#mixing-props-voice-balance"
},
"speak": {
"desc": "The speak property determines whether or not to render text aurally.",
"spec": "https://drafts.csswg.org/css-speech-1/#speaking-props-speak"
},
"speak-as": {
"desc": "The speak-as property determines in what manner text gets rendered aurally, based upon a predefined list of possibilities.",
"spec": "https://drafts.csswg.org/css-speech-1/#speaking-props-speak-as"
},
"pause-before": {
"desc": "The pause-before property specifies a prosodic boundary (silence with a specific duration) that occurs before the speech synthesis rendition of the element.",
"spec": "https://drafts.csswg.org/css-speech-1/#pause-props-pause-before-after"
},
"pause-after": {
"desc": "The pause-after property specifies a prosodic boundary (silence with a specific duration) that occurs after the speech synthesis rendition of the element.",
"spec": "https://drafts.csswg.org/css-speech-1/#pause-props-pause-before-after"
},
"pause": {
"desc": "The pause property is a shorthand property for pause-before and pause-after.",
"spec": "https://drafts.csswg.org/css-speech-1/#pause-props-pause"
},
"rest-before": {
"desc": "The rest-before property specifies a prosodic boundary (silence with a specific duration) that occurs before the speech synthesis rendition of an element.",
"spec": "https://drafts.csswg.org/css-speech-1/#rest-props-rest-before-after"
},
"rest-after": {
"desc": "The rest-after property specifies a prosodic boundary (silence with a specific duration) that occurs after the speech synthesis rendition of an element.",
"spec": "https://drafts.csswg.org/css-speech-1/#rest-props-rest-before-after"
},
"rest": {
"desc": "The rest property is a shorthand for rest-before and rest-after.",
"spec": "https://drafts.csswg.org/css-speech-1/#rest-props-rest"
},
"cue-before": {
"desc": "The cue-before property specifies auditory icons to be played before the element.",
"spec": "https://drafts.csswg.org/css-speech-1/#cue-props-cue-before-after"
},
"cue-after": {
"desc": "The cue-after property specifies auditory icons to be played after the element.",
"spec": "https://drafts.csswg.org/css-speech-1/#cue-props-cue-before-after"
},
"cue": {
"desc": "The cue property is a shorthand for cue-before and cue-after.",
"spec": "https://drafts.csswg.org/css-speech-1/#cue-props-cue"
},
"word-boundary-detection": {
"desc": "This property allows the author to decide whether and how the user agent must analyse the content to determine where word boundaries are, and to insert virtual word boundaries accordingly.",
"spec": "https://drafts.csswg.org/css-text-4/#word-boundary-detection"
},
"word-boundary-expansion": {
"desc": "This property allows transforming certain word-separating characters into other word-separating characters, to accommodate variant typesetting styles.",
"spec": "https://drafts.csswg.org/css-text-4/#word-boundary-expansion"
},
"text-space-trim": {
"desc": "This property allows authors to specify trimming behavior at the beginning and end of a box.",
"spec": "https://drafts.csswg.org/css-text-4/#white-space-trim"
},
"hyphenate-limit-zone": {
"desc": "This property specifies the maximum amount of unfilled space (before justification) that may be left in the line box before hyphenation is triggered to pull part of a word from the next line back up into the current line.",
"spec": "https://drafts.csswg.org/css-text-4/#hyphenate-size-limits"
},
"hyphenate-limit-lines": {
"desc": "This property indicates the maximum number of successive hyphenated lines in an element. The no-limit value means that there is no limit.",
"spec": "https://drafts.csswg.org/css-text-4/#hyphenate-line-limits"
},
"hyphenate-limit-last": {
"desc": "This property indicates hyphenation behavior at the end of elements, column, pages, and spreads.",
"spec": "https://drafts.csswg.org/css-text-4/#hyphenate-line-limits"
},
"word-wrap": {
"desc": "This property specifies whether the UA may break at otherwise disallowed points within a line to prevent overflow, when an otherwise-unbreakable string is too long to fit within the line box.",
"spec": "https://drafts.csswg.org/css-text-4/#overflow-wrap-property"
},
"text-wrap": {
"desc": "This property specifies the mode for text wrapping.",
"spec": "https://drafts.csswg.org/css-text-4/#text-wrap"
},
"wrap-before": {
"desc": "The wrap-before property specifies modifications to break opportunities in line breaking.",
"spec": "https://drafts.csswg.org/css-text-4/#wrap-before"
},
"wrap-after": {
"desc": "The wrap-after property specifies modifications to break opportunities in line breaking.",
"spec": "https://drafts.csswg.org/css-text-4/#wrap-before"
},
"wrap-inside": {
"desc": "Specifies the line breaking behavior within a box. As determed by the line-breaking rules in effect.",
"spec": "https://drafts.csswg.org/css-text-4/#wrap-inside"
},
"text-align-all": {
"desc": "This longhand of the text-align shorthand property specifies the inline alignment of all lines of inline content in the block container, except for last lines overridden by a non-auto value of text-align-last.",
"spec": "https://drafts.csswg.org/css-text-4/#text-align-all-property"
},
"text-group-align": {
"desc": "This property aligns the contents of the line boxes as a group while maintaining their text alignment.",
"spec": "https://drafts.csswg.org/css-text-4/#text-group-align-property"
},
"line-padding": {
"desc": "Adjusts spacing only at the start/end of a line.",
"spec": "https://drafts.csswg.org/css-text-4/#line-padding-property"
},
"text-spacing": {
"desc": "This property is a shorthand for setting text-spacing-trim and text-autospace in a single declaration.",
"spec": "https://drafts.csswg.org/css-text-4/#text-spacing-property"
},
"animation-range-end": {
"desc": "Shifts the end time of the animation (i.e. where keyframes mapped to 100% progress are attached when the iteration count is 1) to the specified position on the timeline.",
"spec": "https://drafts.csswg.org/scroll-animations-1/#valdef-animation-range-start-timeline-range-name-percentage"
},
"animation-range-start": {
"desc": "Shifts the start time of the animation (i.e. where keyframes mapped to 0% progress are attached when the iteration count is 1) to the specified position on the timeline.",
"spec": "https://drafts.csswg.org/scroll-animations-1/#propdef-animation-range-start"
},
"animation-range": {
"desc": "The animation-range property is a shorthand that sets animation-range-start and animation-range-end together in a single declaration.",
"spec": "https://drafts.csswg.org/scroll-animations-1/#propdef-animation-range"
},
"view-timeline": {
"desc": "This property is a shorthand for setting view-timeline-name and view-timeline-axis in a single declaration. It does not set view-timeline-inset.",
"spec": "https://drafts.csswg.org/scroll-animations-1/#propdef-view-timeline"
},
"view-timeline-inset": {
"desc": "Specifies an inset (positive) or outset (negative) adjustment of the scrollport when determining whether the box is in view when setting the bounds of the corresponding view progress timeline.",
"spec": "https://drafts.csswg.org/scroll-animations-1/#propdef-view-timeline-inset"
},
"view-timeline-axis": {
"desc": "Specifies an axis for each named view progress timeline associated with this scroll container.",
"spec": "https://drafts.csswg.org/scroll-animations-1/#propdef-view-timeline-axis"
},
"view-timeline-name": {
"desc": "Specifies names for any view progress timelines associated with this elements principal box.",
"spec": "https://drafts.csswg.org/scroll-animations-1/#propdef-view-timeline-name"
},
"fill-color": {
"desc": "This property sets the fill color of an element. This color is drawn behind any fill images.",
"spec": "https://drafts.fxtf.org/fill-stroke-3/#fill-color"
},
"view-transition-name": {
"desc": "The view-transition-name property 'names' an element as participating in a view transition.",
"spec": "https://drafts.csswg.org/css-view-transitions-1/#view-transition-name-prop"
},
"input-security": {
"desc": "For the purpose of this specification, a sensitive text input is a text input whose purpose is to accept sensitive input, as defined by the host language.",
"spec": "https://drafts.csswg.org/css-ui-4/#input-security"
},
"nav-left": {
"desc": "User agents for devices with directional navigation keys respond by navigating the focus according to four respective nav-* directional navigation properties (nav-up, nav-right, nav-down, nav-left).",
"spec": "https://drafts.csswg.org/css-ui-4/#nav-dir"
},
"nav-down": {
"desc": "User agents for devices with directional navigation keys respond by navigating the focus according to four respective nav-* directional navigation properties (nav-up, nav-right, nav-down, nav-left).",
"spec": "https://drafts.csswg.org/css-ui-4/#nav-dir"
},
"nav-right": {
"desc": "User agents for devices with directional navigation keys respond by navigating the focus according to four respective nav-* directional navigation properties (nav-up, nav-right, nav-down, nav-left).",
"spec": "https://drafts.csswg.org/css-ui-4/#nav-dir"
},
"nav-up": {
"desc": "User agents for devices with directional navigation keys respond by navigating the focus according to four respective nav-* directional navigation properties (nav-up, nav-right, nav-down, nav-left).",
"spec": "https://drafts.csswg.org/css-ui-4/#nav-dir"
},
"caret": {
"desc": "This property is a shorthand for setting caret-color and caret-shape in one declaration.",
"spec": "https://drafts.csswg.org/css-ui-4/#caret"
},
"caret-shape": {
"desc": "This property allows authors to specify the desired shape of the text insertion caret.",
"spec": "https://drafts.csswg.org/css-ui-4/#caret-shape"
},
"text-emphasis-skip": {
"desc": "This property describes for which characters marks are drawn.",
"spec": "https://drafts.csswg.org/css-text-decor-4/#text-emphasis-skip"
},
"text-decoration-skip-spaces": {
"desc": "This property specifies whether text decoration skips any spaces.",
"spec": "https://drafts.csswg.org/css-text-decor-4/#text-decoration-skip-spaces-property"
},
"text-decoration-skip-box": {
"desc": "This property specifies what parts of the elements box area any text decoration affecting the element must skip over.",
"spec": "https://drafts.csswg.org/css-text-decor-4/#text-decoration-skip-box-property"
},
"text-decoration-skip-self": {
"desc": "This property specifies whether any text decoration lines drawn by its ancestors are propagated to or drawn across the element.",
"spec": "https://drafts.csswg.org/css-text-decor-4/#text-decoration-skip-self-property"
},
"text-decoration-trim": {
"desc": "This property adjusts the start and end points of line decorations, allowing the author to shorten, lengthen, or shift the decoration with respect to the text.",
"spec": "https://drafts.csswg.org/css-text-decor-4/#text-decoration-skip-inset-property"
},
"text-space-collapse": {
"desc": "Previous name of 'white-space-collapse'",
"spec": "https://drafts.csswg.org/css-text-4/#changes"
},
"min-intrinsic-sizing": {
"desc": "This property defines whether the min-content contribution of a non-replaced box is “compressed” under certain circumstances.",
"spec": "https://www.w3.org/TR/css-sizing-4/#intrinsic-contribution-override"
},
"shape-padding": {
"desc": "The shape-padding property adds padding to a shape-inside. This defines a new shape where every point is the specified distance from the shape-inside.",
"spec": "https://drafts.csswg.org/css-shapes-2/#propdef-shape-padding"
},
"shape-inside": {
"desc": "The shape-inside property adds one or more exclusion areas to the elements wrapping context.",
"spec": "https://drafts.csswg.org/css-shapes-2/#shape-inside-property"
},
"scroll-start": {
"desc": "This property is a shorthand property that sets all of the scroll-start-* longhands in one declaration.",
"spec": "https://drafts.csswg.org/css-scroll-snap-2/#scroll-start"
},
"scroll-start-target": {
"desc": "This property is a shorthand property that sets all of the scroll-start-target-* longhands in one declaration.",
"spec": "https://drafts.csswg.org/css-scroll-snap-2/#scroll-start-target"
},
"scroll-start-x": {
"desc": "Defines the scroll starting point in the block axis.",
"spec": "https://drafts.csswg.org/css-scroll-snap-2/#scroll-start-longhands-physical"
},
"scroll-start-y": {
"desc": "Defines the scroll starting point in the inline axis.",
"spec": "https://drafts.csswg.org/css-scroll-snap-2/#scroll-start-longhands-physical"
},
"scroll-start-inline": {
"desc": "Defines the flow for 'scroll-start' longhands.",
"spec": "https://drafts.csswg.org/css-scroll-snap-2/#scroll-start-longhands-logical"
},
"scroll-start-block": {
"desc": "Defines the flow for 'scroll-start' longhands.",
"spec": "https://drafts.csswg.org/css-scroll-snap-2/#scroll-start-longhands-logical"
},
"ruby-overhang": {
"desc": "The ruby-overhang property controls whether ruby annotations may overlap adjacent text outside the ruby container.",
"spec": "https://drafts.csswg.org/css-ruby-1/#ruby-overhang"
},
"ruby-merge": {
"desc": "This property controls how ruby annotation boxes should be rendered when there are more than one in a ruby container box: whether each pair should be kept separate, the annotations should be merged and rendered as a group, or the separation should be determined based on the space available.",
"spec": "https://drafts.csswg.org/css-ruby-1/#collapsed-ruby"
},
"border-boundary": {
"desc": "When the border-boundary property on an element is set to 'parent', additional borders of the element could be drawn where the elements area and the borders of its parent are met.",
"spec": "https://drafts.csswg.org/css-round-display-1/#border-boundary-property"
},
"block-step": {
"desc": "This shorthand property allows for setting block-step-size, block-step-insert, block-step-align, and block-step-round in one declaration.",
"spec": "https://www.w3.org/TR/css-rhythm-1/#block-step"
},
"block-step-round": {
"desc": "This property specifies whether adjustments due to block-step-size insert positive or negative space.",
"spec": "https://www.w3.org/TR/css-rhythm-1/#block-step-round"
},
"block-step-align": {
"desc": "This property specifies whether extra spacing derived from applying block-step-size is inserted before, inserted after, or split between both sides of the box.",
"spec": "https://www.w3.org/TR/css-rhythm-1/#block-step-align"
},
"block-step-insert": {
"desc": "This property specifies whether extra spacing derived from applying block-step-size is inserted inside (like padding) or outside (like margin) the boxs border.",
"spec": "https://www.w3.org/TR/css-rhythm-1/#block-step-insert"
},
"block-step-size": {
"desc": "This property defines the step unit for a block-level boxs block size.",
"spec": "https://www.w3.org/TR/css-rhythm-1/#block-step-size"
},
"region-fragment": {
"desc": "The region-fragment property controls the behavior of the last usable region associated with a named flow.",
"spec": "https://drafts.csswg.org/css-regions-1/#propdef-region-fragment"
},
"flow-from": {
"desc": "The flow-from property makes a block container a region and associates it with a named flow.",
"spec": "https://drafts.csswg.org/css-regions-1/#flow-from"
},
"flow-into": {
"desc": "The flow-into property can place an element or its contents into a named flow.",
"spec": "https://drafts.csswg.org/css-regions-1/#the-flow-into-property"
},
"page": {
"desc": "The page property is used to specify a particular type of page (called a named page) on which an element must be displayed.",
"spec": "https://www.w3.org/TR/css-page-3/#using-named-pages"
},
"float-offset": {
"desc": "This property pushes a page float in direction opposite of the where it has been floated with float.",
"spec": "https://drafts.csswg.org/css-page-floats-3/#the-float_offset-property"
},
"float-defer": {
"desc": "This property specifies whether the initial float reference of a page float is the fragmentation container in which the float anchor is placed after previous page floats have been placed, or in another one.",
"spec": "https://drafts.csswg.org/css-page-floats-3/#float-defer-property"
},
"float-reference": {
"desc": "The floats are aligning to the start or end of a float reference, specified by the float-reference attribute.",
"spec": "https://drafts.csswg.org/css-page-floats-3/#float-reference-property"
},
"max-lines": {
"desc": "A property that forces a fragment to break after a specified number of lines.",
"spec": "https://www.w3.org/TR/css-overflow-4/#max-lines"
},
"continue": {
"desc": "The continue property gives authors the ability to request that content that does not fit inside an element be fragmented, and provides alternatives for where the remaining content should continue.",
"spec": "https://www.w3.org/TR/css-overflow-4/#continue"
},
"line-clamp": {
"desc": "The line-clamp property is a shorthand for the max-lines, block-ellipsis, and continue properties.",
"spec": "https://www.w3.org/TR/css-overflow-3/#line-clamp"
},
"block-ellipsis": {
"desc": "This property allows inserting content into the last line box before a (forced or unforced) region break to indicate the continuity of truncated/interrupted content.",
"spec": "https://drafts.csswg.org/css-overflow-4/#block-ellipsis"
},
"overflow-clip-margin-block": {
"desc": "Defines the overflow clip edge of a box.",
"spec": "https://drafts.csswg.org/css-overflow-4/#propdef-overflow-clip-margin-block"
},
"overflow-clip-margin-inline": {
"desc": "Defines the overflow clip edge of a box.",
"spec": "https://drafts.csswg.org/css-overflow-4/#propdef-overflow-clip-margin-inline"
},
"overflow-clip-margin-inline-end": {
"desc": "Defines the overflow clip edge of a box.",
"spec": "https://drafts.csswg.org/css-overflow-4/#propdef-overflow-clip-margin-inline-end"
},
"overflow-clip-margin-block-end": {
"desc": "Defines the overflow clip edge of a box.",
"spec": "https://drafts.csswg.org/css-overflow-4/#propdef-overflow-clip-margin-block-end"
},
"overflow-clip-margin-inline-start": {
"desc": "Defines the overflow clip edge of a box.",
"spec": "https://drafts.csswg.org/css-overflow-4/#propdef-overflow-clip-margin-inline-start"
},
"overflow-clip-margin-block-start": {
"desc": "Defines the overflow clip edge of a box.",
"spec": "https://drafts.csswg.org/css-overflow-4/#propdef-overflow-clip-margin-block-start"
},
"overflow-clip-margin-left": {
"desc": "Defines the overflow clip edge of a box.",
"spec": "https://drafts.csswg.org/css-overflow-4/#propdef-overflow-clip-margin-left"
},
"overflow-clip-margin-bottom": {
"desc": "Defines the overflow clip edge of a box.",
"spec": "https://drafts.csswg.org/css-overflow-4/#propdef-overflow-clip-margin-bottom"
},
"overflow-clip-margin-right": {
"desc": "Defines the overflow clip edge of a box.",
"spec": "https://drafts.csswg.org/css-overflow-4/#propdef-overflow-clip-margin-right"
},
"overflow-clip-margin-top": {
"desc": "Defines the overflow clip edge of a box.",
"spec": "https://drafts.csswg.org/css-overflow-4/#propdef-overflow-clip-margin-top"
},
"spatial-navigation-function": {
"desc": "The default algorithm of spatial navigation specified in the §8 Processing Model may need the fine tune depending on the layout types.",
"spec": "https://www.w3.org/TR/css-nav-1/#css-property-spatialnavigationfunction"
},
"spatial-navigation-action": {
"desc": "When the focus is inside of a scroll container and the user triggers spatial navigation, it is somewhat ambiguous whether they are requesting that the focus be moved in that direction, or whether the document should be scrolled in that direction.",
"spec": "https://www.w3.org/TR/css-nav-1/#css-property-spatialnavigationaction"
},
"spatial-navigation-contain": {
"desc": "Creates an additional spatial navigation container.",
"spec": "https://www.w3.org/TR/css-nav-1/#container"
},
"marker-side": {
"desc": "The marker-side property specifies whether an outside marker box is positioned based on the directionality of the list item itself (i.e. its originating element) or the directionality of the list container (i.e. the originating elements parent).",
"spec": "https://www.w3.org/TR/css-lists-3/#marker-side"
},
"link-parameters": {
"desc": "The link-parameters property is one way to set link parameters on the element itself, and on all external CSS resources specified on the element.",
"spec": "https://drafts.csswg.org/css-link-params-1/#link-param-prop"
},
"box-snap": {
"desc": "Specifies how the block is snapped to the baseline grid.",
"spec": "https://www.w3.org/TR/css-line-grid-1/#box-snap"
},
"line-snap": {
"desc": "This property applies to all the line boxes directly contained by the element, and, when not none, causes each line box to shift (usually downward, possibly by zero) until it snaps to the line grid specified by line-grid.",
"spec": "https://www.w3.org/TR/css-line-grid-1/#line-snap"
},
"line-grid": {
"desc": "Specifies whether this box creates a new baseline grid for its descendants or uses the same baseline grid as its parent.",
"spec": "https://www.w3.org/TR/css-line-grid-1/#line-grid"
},
"initial-letter-wrap": {
"desc": "This property specifies whether lines impacted by an initial letter are shortened to fit the rectangular shape of the initial letter box or the contour of its glyph outline.",
"spec": "https://www.w3.org/TR/css-inline-3/#initial-letter-wrapping"
},
"inline-sizing": {
"desc": "This property specifies how the logical height of the content area of an inline box is measured in relation to its contents.",
"spec": "https://www.w3.org/TR/css-inline-3/#line-fill"
},
"leading-trim": {
"desc": "On inline boxes, specifies whether to trim the content box to match its corresponding text-edge metric.",
"spec": "https://www.w3.org/TR/css-inline-3/#propdef-leading-trim"
},
"text-edge": {
"desc": "Inline boxes, whose primary purpose is to contain text, are sized in the block axis based on their font metrics.",
"spec": "https://www.w3.org/TR/css-inline-3/#propdef-text-edge"
},
"baseline-source": {
"desc": "When an inline-level box has more than one possible source for baseline information (such as for a multi-line inline block or inline flex container) this property specifies whether the first baseline set or last baseline set is preferred for alignment, indicating the boxs baseline alignment preference.",
"spec": "https://www.w3.org/TR/css-inline-3/#baseline-source"
},
"object-view-box": {
"desc": "The object-view-box property specifies a 'view box' over an element, which allows zooming or panning over the elements contents.",
"spec": "https://drafts.csswg.org/css-images-5/#the-object-view-box"
},
"string-set": {
"desc": "The string-set property contains one or more pairs, each consisting of an custom identifier (the name of the named string) followed by a content-list describing how to construct the value of the named string.",
"spec": "https://drafts.csswg.org/css-gcpm-3/#setting-named-strings-the-string-set-pro"
},
"bookmark-level": {
"desc": "The bookmark-level property determines if a bookmark is created, and at what level.",
"spec": "https://drafts.csswg.org/css-content-3/#propdef-bookmark-level"
},
"bookmark-label": {
"desc": "Sets the text content of the bookmark label.",
"spec": "https://drafts.csswg.org/css-content-3/#bookmark-label"
},
"bookmark-state": {
"desc": "The bookmark-state may be open or closed.",
"spec": "https://drafts.csswg.org/css-content-3/#bookmark-state"
},
"footnote-policy": {
"desc": "The footnote-policy property allows authors some influence over the rendering of difficult pages.",
"spec": "https://drafts.csswg.org/css-gcpm-3/#footnote-policy"
},
"footnote-display": {
"desc": "The footnote-display property determines whether a footnote is displayed as a block element or inline element.",
"spec": "https://drafts.csswg.org/css-gcpm-3/#footnote-types"
},
"reading-order": {
"desc": "The reading-order property controls the order in which elements are rendered to speech or are navigated to when using (linear) sequention navigation methods.",
"spec": "https://drafts.csswg.org/css-display-4/#reading-order"
},
"layout-order": {
"desc": "By allowing the layout to be rearranged without affecting the underlying document source order, the layout-order property lets authors keep a documents reading and interaction order matched to the visual perception order in cases where it does not match the layout order.",
"spec": "https://drafts.csswg.org/css-display-4/#layout-order"
},
"font-synthesis-weight": {
"desc": "This property controls whether user agents are allowed to synthesize bold font faces when a font family lacks bold faces.",
"spec": "https://drafts.csswg.org/css-fonts-4/#font-synthesis-weightThis property controls whether user agents are allowed to synthesize bold font faces when a font family lacks bold faces."
},
"font-synthesis-style": {
"desc": "This property controls whether user agents are allowed to synthesize oblique font faces when a font family lacks oblique faces.",
"spec": "https://drafts.csswg.org/css-fonts-4/#font-synthesis-style"
},
"font-synthesis-small-caps": {
"desc": "This property controls whether user agents are allowed to synthesize small caps font faces when a font family lacks small caps faces.",
"spec": "https://drafts.csswg.org/css-fonts-4/#font-synthesis-small-caps"
},
"margin-break": {
"desc": "Controls whether the boxs block-axis margins are discarded or kept at a fragmentation break.",
"spec": "https://www.w3.org/TR/css-break-4/#break-margins"
},
"color-adjust": {
"desc": "The color-adjust shorthand allows an author to set all of the performance-motivated color adjustment properties in one declaration.",
"spec": "https://www.w3.org/TR/css-color-adjust-1/#color-adjust"
},
"wrap-flow": {
"desc": "An element becomes an exclusion when its wrap-flow property has a computed value other than auto.",
"spec": "https://drafts.csswg.org/css-exclusions-1/#wrap-flow-property"
},
"wrap-through": {
"desc": "Setting the wrap-through property to none prevents an element from inheriting its parent wrapping context.",
"spec": "https://drafts.csswg.org/css-exclusions-1/#wrap-through-property"
},
"copy-into": {
"desc": "The copy-into property contains one or more pairs, each consisting of a custom identifier followed by a content-level keyword describing how to construct the value of the named content fragment.",
"spec": "https://drafts.csswg.org/css-gcpm-4/#copy-into-heading"
}
}

17324
packages/autocomplete-css/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,23 @@
{
"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": {
"@webref/css": "^6.3.4",
"content": "github:mdn/content",
"request": "^2.53.0"
}
}

View File

@ -0,0 +1,929 @@
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'
# Throughout the entirety of this test document there are many places that the
# original Atom tests would check for exact values of returned items such as
# matching properties or matching tags. But as the web changes this is
# combersome to maintain, to do Pulsar's best to avoid regressions in this aspect
# these locations will now check for more than the last good value.
# This of course assumes that the web won't start removing matching items faster
# than adding. But locations of this behavior will be marked accordingly with: #398
# https://github.com/pulsar-edit/pulsar/pull/398
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)
isValueInCompletions = (value, completions) ->
completionsNodesText = []
for completion in completions
completionsNodesText.push(completion.text)
return value in completionsNodesText
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.length).toBeGreaterThan 9 # #398
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).toBeGreaterThan 237 # #398 Fun Fact last check this was 673
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(isValueInCompletions('display: ', completions)).toBe true
expect(isValueInCompletions('direction: ', completions)).toBe true
# Then no matter what the top results are there's still some we can expect of them.
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].type).toBe 'property'
expect(completions[1].replacementPrefix).toBe 'd'
editor.setText """
body {
D
}
"""
editor.setCursorBufferPosition([1, 3])
completions = getCompletions()
expect(completions.length).toBeGreaterThan 2 # #398
expect(isValueInCompletions('display: ', completions)).toBe true
expect(isValueInCompletions('direction: ', completions)).toBe true
expect(completions[1].replacementPrefix).toBe 'D'
editor.setText """
body {
d:
}
"""
editor.setCursorBufferPosition([1, 3])
completions = getCompletions()
expect(isValueInCompletions('display: ', completions)).toBe true
expect(isValueInCompletions('direction: ', completions)).toBe true
editor.setText """
body {
bord
}
"""
editor.setCursorBufferPosition([1, 6])
completions = getCompletions()
expect(isValueInCompletions('border: ', completions)).toBe true
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(isValueInCompletions('width: ', completions)).toBe true
editor.setText """
body {
}
"""
editor.setCursorBufferPosition([1, 0])
completions = getCompletions()
expect(isValueInCompletions('width: ', completions)).toBe true
editor.setText """
body { }
"""
editor.setCursorBufferPosition([0, 6])
completions = getCompletions()
expect(isValueInCompletions('width: ', completions)).toBe true
editor.setText """
body { }
"""
editor.setCursorBufferPosition([0, 7])
completions = getCompletions()
expect(isValueInCompletions('width: ', completions)).toBe true
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).toBeGreaterThan 24 # #398
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).toBeGreaterThan 24 # #398
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(isValueInCompletions('inline;', completions)).toBe true
expect(isValueInCompletions('inline-block;', completions)).toBe true
expect(isValueInCompletions('inline-flex;', completions)).toBe true
expect(isValueInCompletions('inline-grid;', completions)).toBe true
expect(isValueInCompletions('inline-table;', completions)).toBe true
expect(isValueInCompletions('inherit;', completions)).toBe true
expect(completions[0].description.length).toBeGreaterThan 0
expect(completions[0].descriptionMoreURL.length).toBeGreaterThan 0
editor.setText """
body {
display: I
}
"""
editor.setCursorBufferPosition([1, 12])
completions = getCompletions()
expect(completions.length).toBeGreaterThan 6 # #398
expect(isValueInCompletions('inline;', completions)).toBe true
expect(isValueInCompletions('inline-block;', completions)).toBe true
expect(isValueInCompletions('inline-flex;', completions)).toBe true
expect(isValueInCompletions('inline-grid;', completions)).toBe true
expect(isValueInCompletions('inline-table;', completions)).toBe true
expect(isValueInCompletions('inherit;', completions)).toBe true
editor.setText """
body {
display:
i
}
"""
editor.setCursorBufferPosition([2, 5])
completions = getCompletions()
expect(isValueInCompletions('inline;', completions)).toBe true
expect(isValueInCompletions('inline-block;', completions)).toBe true
expect(isValueInCompletions('inline-flex;', completions)).toBe true
expect(isValueInCompletions('inline-grid;', completions)).toBe true
expect(isValueInCompletions('inline-table;', completions)).toBe true
expect(isValueInCompletions('inherit;', completions)).toBe true
editor.setText """
body {
text-align:
}
"""
editor.setCursorBufferPosition([1, 13])
completions = getCompletions()
expect(completions.length).toBeGreaterThan 5 # #398
expect(isValueInCompletions('center;', completions)).toBe true
expect(isValueInCompletions('left;', completions)).toBe true
expect(isValueInCompletions('justify;', completions)).toBe true
expect(isValueInCompletions('right;', completions)).toBe true
expect(isValueInCompletions('inherit;', completions)).toBe true
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.length).toBeGreaterThan 24 # #398
expect(isValueInCompletions('block;', completions)).toBe true
editor.setText """
body {
display: block; float:
}
"""
editor.setCursorBufferPosition([1, 24])
completions = getCompletions()
expect(completions.length).toBeGreaterThan 4 # #398
expect(isValueInCompletions('left;', completions)).toBe true
it "autocompletes more than one inline property value", ->
editor.setText "body { display: block; float: }"
editor.setCursorBufferPosition([0, 30])
completions = getCompletions()
expect(completions.length).toBeGreaterThan 4 # #398
expect(isValueInCompletions('left;', completions)).toBe true
editor.setText "body { display: block; float: left; cursor: alias; text-decoration: }"
editor.setCursorBufferPosition([0, 68])
completions = getCompletions()
expect(completions.length).toBeGreaterThan 5 # #398
expect(isValueInCompletions('line-through;', completions)).toBe true
it "autocompletes inline property values with a prefix", ->
editor.setText "body { display: i }"
editor.setCursorBufferPosition([0, 17])
completions = getCompletions()
expect(completions.length).toBeGreaterThan 6 # #398
expect(isValueInCompletions('inline;', completions)).toBe true
expect(isValueInCompletions('inline-block;', completions)).toBe true
expect(isValueInCompletions('inline-flex;', completions)).toBe true
expect(isValueInCompletions('inline-grid;', completions)).toBe true
expect(isValueInCompletions('inline-table;', completions)).toBe true
expect(isValueInCompletions('inherit;', completions)).toBe true
editor.setText "body { display: i}"
editor.setCursorBufferPosition([0, 17])
completions = getCompletions()
expect(completions.length).toBeGreaterThan 6 # #398
expect(isValueInCompletions('inline;', completions)).toBe true
expect(isValueInCompletions('inline-block;', completions)).toBe true
expect(isValueInCompletions('inline-flex;', completions)).toBe true
expect(isValueInCompletions('inline-grid;', completions)).toBe true
expect(isValueInCompletions('inline-table;', completions)).toBe true
expect(isValueInCompletions('inherit;', completions)).toBe true
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.length).toBeGreaterThan 4 # #398
expect(isValueInCompletions('left;', completions)).toBe true
expect(isValueInCompletions('right;', completions)).toBe true
expect(isValueInCompletions('none;', completions)).toBe true
expect(isValueInCompletions('inherit;', completions)).toBe true
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).toBeGreaterThan 7 # #398
expect(isValueInCompletions('canvas', completions)).toBe true
expect(isValueInCompletions('code', completions)).toBe true
expect(completions[0].type).toBe 'tag'
expect(completions[0].description.length).toBeGreaterThan 0
editor.setText """
canvas,ca {
}
"""
editor.setCursorBufferPosition([0, 9])
completions = getCompletions()
expect(completions.length).toBeGreaterThan 7 # #398
expect(completions[0].text).toBe 'canvas'
editor.setText """
canvas ca {
}
"""
editor.setCursorBufferPosition([0, 9])
completions = getCompletions()
expect(completions.length).toBeGreaterThan 7 # #398
expect(completions[0].text).toBe 'canvas'
editor.setText """
canvas, ca {
}
"""
editor.setCursorBufferPosition([0, 10])
completions = getCompletions()
expect(completions.length).toBeGreaterThan 7 # #398
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).toBeGreaterThan 5 # #398
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).toBeGreaterThan 4 # #398
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(isValueInCompletions('display: ', completions)).toBe true
expect(isValueInCompletions('direction: ', completions)).toBe true
expect(isValueInCompletions('div', completions)).toBe true
# 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(isValueInCompletions('display: ', completions)).toBe true
expect(isValueInCompletions('direction: ', completions)).toBe true
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].type).toBe 'property'
expect(completions[1].replacementPrefix).toBe 'd'
editor.setText """
body
D
"""
editor.setCursorBufferPosition([1, 3])
completions = getCompletions()
expect(completions.length).toBeGreaterThan 11 # #398
expect(isValueInCompletions('display: ', completions)).toBe true
expect(isValueInCompletions('direction: ', completions)).toBe true
expect(completions[1].replacementPrefix).toBe 'D'
editor.setText """
body
d:
"""
editor.setCursorBufferPosition([1, 3])
completions = getCompletions()
expect(isValueInCompletions('display: ', completions)).toBe true
expect(isValueInCompletions('direction: ', completions)).toBe true
editor.setText """
body
bord
"""
editor.setCursorBufferPosition([1, 6])
completions = getCompletions()
expect(isValueInCompletions('border: ', completions)).toBe true
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).toBeGreaterThan 24 # #398
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).toBeGreaterThan 24 # #398
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(isValueInCompletions('inline', completions)).toBe true
expect(isValueInCompletions('inline-block', completions)).toBe true
expect(isValueInCompletions('inline-flex', completions)).toBe true
expect(isValueInCompletions('inline-grid', completions)).toBe true
expect(isValueInCompletions('inline-table', completions)).toBe true
expect(isValueInCompletions('inherit', completions)).toBe true
expect(completions[0].description.length).toBeGreaterThan 0
expect(completions[0].descriptionMoreURL.length).toBeGreaterThan 0
editor.setText """
body
display: I
"""
editor.setCursorBufferPosition([1, 12])
completions = getCompletions()
expect(completions.length).toBeGreaterThan 6 # #398
expect(isValueInCompletions('inline', completions)).toBe true
expect(isValueInCompletions('inline-block', completions)).toBe true
expect(isValueInCompletions('inline-flex', completions)).toBe true
expect(isValueInCompletions('inline-grid', completions)).toBe true
expect(isValueInCompletions('inline-table', completions)).toBe true
expect(isValueInCompletions('inherit', completions)).toBe true
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(isValueInCompletions('border: ', completions)).toBe true
expect(isValueInCompletions('border-radius: ', completions)).toBe true
expect(completions[0].replacementPrefix).toBe 'border-'
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).toBeGreaterThan 7 # #398
expect(isValueInCompletions('canvas', completions)).toBe true
expect(isValueInCompletions('code', completions)).toBe true
expect(completions[0].type).toBe 'tag'
expect(completions[0].description.length).toBeGreaterThan 0
editor.setText """
canvas,ca
"""
editor.setCursorBufferPosition([0, 9])
completions = getCompletions()
expect(completions.length).toBeGreaterThan 7 # #398
expect(completions[0].text).toBe 'canvas'
editor.setText """
canvas ca
"""
editor.setCursorBufferPosition([0, 9])
completions = getCompletions()
expect(completions.length).toBeGreaterThan 7 # #398
expect(completions[0].text).toBe 'canvas'
editor.setText """
canvas, ca
"""
editor.setCursorBufferPosition([0, 10])
completions = getCompletions()
expect(completions.length).toBeGreaterThan 7 # #398
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,368 @@
/**
This file will manage the updating of `autocomplete-css` `completions.json`.
We will mainly utilize `@webref/css`.listAll() function that returns a full CSS
list of all properties seperated by their spec shortname. An example
of this format is defined below for ease of future modifications.
Some important notes about the data contained here:
- Often times the `value` within the `property` will be in the following format:
`<valueGroupName>` or even `<valueGroupName> | value | value2` or just `value | value2`
It will be important to build a parser that can handle this format.
The `<valueGroupName>` then can be realized via that specs `values` where
`values[x].name` will match the `<valueGroupName>`. Another important note about
handling values here is that oftentimes `values[x].values[]` won't actually
contain all possible values. And instead this must be handled by checking
`values[x].value` which is another string of `<valueGroupName> | value`.
So this should be handled by the same parser.
- Additionally an important note is that nowhere in this data do we get any kind
of description about the data that could lend a hand in being documentation.
So the documentation must be gathered seperatly. Likely the best way to collect
our documentation data is via `mdn/content`.
Within `content/files/en-us/web/css` is a directory of folders titled
by the name of properties.
The last important thing to note here:
MDN doesn't have docs on everything. And that's a good thing. But it means
many of our items don't have any kind of description. For this situation
we have `manual-property-desc.json` which is a list of manually updated
descriptions for properties where there are none. This was a last resort
intended to provide the highest quality of completions possible.
Overtime many items on this list will likely be able to be removed just as
new ones are added. After running the update script you'll see a warning
saying how many properties are without completions that would then need to
be added to the JSON file.
"spec-shortname": {
"spec": {
"title": "",
"url": ""
},
"properties": [
{
"name": "",
"value": "",
"initial": "",
"appliesTo": "",
"percentages": "",
"computedValue": "",
"canonicalOrder": "",
"animationType": "",
"media": "",
"styleDeclaration": [ "", "", "" ]
}
],
"atrules": [
{
"name": "",
"descriptors": [
{
"name": "",
"for": "",
"value": "",
"type": ""
}
]
}
],
"selectors": [],
"values": [
{
"name": "",
"type": "",
"prose": "Optional description",
"value": "",
"values": [
{
"name": "",
"prose": "Optional Description",
"type": "",
"value": ""
}
]
}
],
"warnings": []
}
*/
const css = require("@webref/css");
const fs = require("fs");
const CSSParser = require("./cssValueDefinitionSyntaxExtractor.js");
const manualPropertyDesc = require("./manual-property-desc.json");
async function update(params) {
const parsedFiles = await css.listAll();
const properties = await buildProperties(parsedFiles);
const tags = await getTagsHTML();
const pseudoSelectors = await getPseudoSelectors();
const completions = {
tags: tags,
properties: properties,
pseudoSelectors: pseudoSelectors
};
// Now to write out our updated file
fs.writeFileSync("completions.json", JSON.stringify(completions, null, 2));
// Now to determine how many properties have empty descriptions.
let count = 0;
let showEmpty = false;
for (const param of params) {
if (param === "--show-empty") {
showEmpty = true;
}
}
for (const prop in completions.properties) {
if (completions.properties[prop].description === "") {
if (showEmpty) {
console.log(prop);
}
count ++;
}
}
console.log("Updated all `autocomplete-css` completions.");
console.log(`Total Completion Properties without a description: ${count}!`);
if (count !== 0) {
console.log("It is not required to fix the above empty completions issue.");
console.log("Use `node update.js --show-empty` to show the empty property names.");
};
}
async function buildProperties(css) {
// This function will take a CSS object of all values from @webref/css
// and will gather descriptions from mdn/content for these properties.
// Returning data in the expected format for the old fashioned `completions.json`
let propertyObj = {};
for (const spec in css) {
// For now we will only retain `properties` in these files. At a later time
// we can revist and looking at adding `atrules`
if (Array.isArray(css[spec].properties)) {
for (const prop of css[spec].properties) {
const propDescription = await getDescriptionOfProp(prop.name);
const propValues = getValuesOfProp(prop.value, css);
if (typeof propertyObj[prop.name] !== "object") {
propertyObj[prop.name] = {
values: dedupPropValues(propValues),
description: propDescription
};
} else {
// So seems this happens way more often than assumed.
// So instead of discard a previously entered entry, we will prioritize
// having values accomponing it. So whoever has the longer array of
// values will be used as the tiebreaker.
if (propertyObj[prop.name].values.length < propValues.length) {
propertyObj[prop.name] = {
values: dedupPropValues(propValues),
description: propDescription
};
}
}
// Unfortunately the no duplication guarantee of @webref/css seems
// inaccurate. As there are duplicate `display` definitions.
// The first containing all the data we want, and the later containing nothing.
// This protects against overriding previously definied definitions.
}
} // else continue our loop
}
return propertyObj;
}
async function getDescriptionOfProp(name) {
// We will gather a description by checking if there's a document written
// on MDN for our property and then extract a summary from there.
// Since not all CSS property definitions will exist within the CSS docs
// While this seems strange, it's because some selectors are part of other
// specs and may not be worth mentioning standalone.
let file;
let filePath = [ "css", "svg/attribute", "svg/element" ].map(path =>
`./node_modules/content/files/en-us/web/${path}/${name}/index.md`
).find(f => fs.existsSync(f));
if (filePath) {
file = fs.readFileSync(filePath, { encoding: "utf8" });
}
if (file) {
// Here we will do a quick and dirty way to parse the markdown file to retreive a raw string
let breaks = file.split("---");
// The first two breaks should be the yaml metadata block
let data = breaks[2].replace(/\{\{\S+\}\}\{\{\S+\}\}/gm, "")
.replace(/\{\{CSSRef\}\}/gm, "")
.replace(/\{\{SVGRef\}\}/gm, "");
let summaryRaw = data.split("\n");
// In case the first few lines is an empty line break
for (let i = 0; i < summaryRaw.length; i++) {
// Filtering the starting character protects agains't collecting accidental
// warnings or other notices within the MDN site.
if (summaryRaw[i].length > 1 && !summaryRaw[i].startsWith("> ") && !summaryRaw[i].startsWith("« ")) {
return summaryRaw[i]
.replace(/\{\{\S+\("(\S+)"\)\}\}/g, '$1')
.replace(/\*/g, "")
.replace(/\`/g, "")
.replace(/\{/g, "")
.replace(/\}/g, "")
.replace(/\"/g, "")
.replace(/\_/g, "")
.replace(/\[([A-Za-z0-9-_* ]+)\]\(\S+\)/g, '$1');
}
}
} else {
// A document doesn't yet exist, let's ensure it's not in our manual list first
if (manualPropertyDesc[name]) {
return manualPropertyDesc[name].desc;
}
// A document doesn't yet exist. And it's not in our manual list
// Let's return an empty value.
return "";
}
}
function getValuesOfProp(value, allValues, appendImplicitValues=true) {
// value holds the value string of the values we expect
// allValues holds all of the values that apply to the spec
// Like mentioned above `value` = "value1 | value2 | <valueGroupName>"
// https://developer.mozilla.org/en-US/docs/Web/CSS/Value_definition_syntax
// We will at least supply the implicitly defined keywords that apply to all CSS properties
let implicitValues = [ "inherit", "initial", "unset" ];
if (!value) {
if (appendImplicitValues === true) {
return implicitValues;
}
return [];
};
let values = [];
let parser = new CSSParser(value);
let rawArrayValues = parser.parse();
for (const val of rawArrayValues) {
if (val.length > 1) {
// Since some values contain `||` some splits leave a zero length string
if (val.trim().startsWith("<") && val.trim().endsWith(">")) {
// This is a valueGroup lookup key
let valueGroup = parseValueGroup(val.trim(), allValues);
values = values.concat(valueGroup);
} else {
values.push(val.trim());
}
}
}
if (appendImplicitValues === true) {
// Add the implicit values to the end...
values = values.concat(implicitValues);
};
return values;
}
function parseValueGroup(valueGroupName, allValues) {
// Will lookup a valueGroup name within allValues and parse it
let resolvedValueGroupString;
// Now we can receive two kinds of Basic Data Types here
// - Non-Terminal Data Types: <'valueGroupName'> which will share the name of a property
// - And the standard that this was built to deal with.
if (valueGroupName.startsWith("<'") && valueGroupName.endsWith("'>")) {
// Non-Terminal Data Types
for (const spec in allValues) {
if (Array.isArray(allValues[spec].properties)) {
for (const prop of allValues[spec].properties) {
if (prop.name === valueGroupName.replace("<'", "").replace("'>", "")) {
resolvedValueGroupString = prop.value;
break;
}
}
}
}
} else {
// Standard handling
for (const spec in allValues) {
if (Array.isArray(allValues[spec].values)) {
for (const val of allValues[spec].values) {
if (val.name === valueGroupName) {
resolvedValueGroupString = val.value;
break;
}
}
}
}
}
return getValuesOfProp(resolvedValueGroupString, allValues=null, appendImplicitValues=false);
}
async function getTagsHTML() {
// This will also use our dep of `mdn/content` to find all tags currently
// within their docs. By simply grabbing all folders of tag docs by their name
// Some of the page titles from MDN's docs don't accurately reflect what we
// would expect to appear. The object below is named after what the name of the
// folder from MDN's docs is called, whose value is then the array we would instead expect.
const replaceTags = {
"heading_elements": [ "h1", "h2", "h3", "h4", "h5", "h6" ],
};
let tags = [];
let files = fs.readdirSync("./node_modules/content/files/en-us/web/html/element");
files.forEach(file => {
if (file != "index.md") {
if (Array.isArray(replaceTags[file])) {
tags = tags.concat(replaceTags[file]);
} else {
tags.push(file);
}
}
});
return tags;
}
async function getPseudoSelectors() {
// For now since there is no best determined way to collect all modern psudoselectors
// We will just grab the existing list for our existing `completions.json`
let existingCompletions = require("./completions.json");
return existingCompletions.pseudoSelectors;
}
function dedupPropValues(values) {
// Takes an array of values and returns an array without any duplicates
let out = [];
let check = {};
for (let i = 0; i < values.length; i++) {
if (!check[values[i]]) {
out.push(values[i]);
check[values[i]] = true;
}
}
return out;
}
update(process.argv.slice(2));

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 `npm run update` at the root of the package and then checking-in the changed `completions.json` file.

File diff suppressed because it is too large Load Diff

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)}#attributes`
}
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
}

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