mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-09-11 11:15:24 +03:00
Merge branch 'master' into pr/264
This commit is contained in:
commit
338361803d
276
.cirrus.yml
276
.cirrus.yml
@ -1,57 +1,66 @@
|
||||
env:
|
||||
PYTHON_VERSION: 3.10
|
||||
GITHUB_TOKEN: ENCRYPTED[!b0ff4671044672be50914a3a10b49af642bd8e0e681a6f4e5855ec5230a5cf9afbc53d9e90239b8d2c79455f014f383f!]
|
||||
# The above token, is a GitHub API Token, that allows us to download RipGrep without concern of API limits
|
||||
|
||||
linux_task:
|
||||
alias: linux
|
||||
container:
|
||||
image: node:16-slim
|
||||
memory: 8G
|
||||
prepare_script:
|
||||
- apt-get update
|
||||
- export DEBIAN_FRONTEND="noninteractive"
|
||||
- apt-get install -y
|
||||
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
|
||||
- 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 || yarn install
|
||||
build_script:
|
||||
- yarn build
|
||||
- yarn run build:apm
|
||||
build_binary_script:
|
||||
- yarn dist || yarn dist
|
||||
binary_artifacts:
|
||||
path: ./binaries/*
|
||||
test_script:
|
||||
- Xvfb :99 & 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
|
||||
# linux_task:
|
||||
# alias: linux
|
||||
# container:
|
||||
# image: node:16-slim
|
||||
# memory: 8G
|
||||
# prepare_script:
|
||||
# - apt-get update
|
||||
# - export DEBIAN_FRONTEND="noninteractive"
|
||||
# - apt-get install -y
|
||||
# ffmpeg
|
||||
# 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
|
||||
# - 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:
|
||||
# - 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'
|
||||
# - 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
|
||||
only_if: $CIRRUS_CRON != "" || $CIRRUS_TAG == "regular_release"
|
||||
arm_container:
|
||||
image: node:16-slim
|
||||
memory: 8G
|
||||
env:
|
||||
USE_SYSTEM_FPM: 'true'
|
||||
ROLLING_UPLOAD_TOKEN: ENCRYPTED[690950798401ec3715e9d20ac29a0859d3c58097038081ff6afeaf4721e661672d34eb952d8a6442bc7410821ab8545a]
|
||||
prepare_script:
|
||||
- apt-get update
|
||||
- export DEBIAN_FRONTEND="noninteractive"
|
||||
@ -59,6 +68,7 @@ arm_linux_task:
|
||||
gnupg2
|
||||
procps
|
||||
curl
|
||||
ruby
|
||||
rpm
|
||||
build-essential
|
||||
git
|
||||
@ -72,27 +82,28 @@ arm_linux_task:
|
||||
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 || yarn install
|
||||
- yarn install --ignore-engines || yarn install --ignore-engines
|
||||
build_script:
|
||||
- yarn build
|
||||
- yarn run build:apm
|
||||
- rm -Rf node-modules/electron && yarn install --check-files
|
||||
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
|
||||
rolling_upload_script:
|
||||
- cd ./script/rolling-release-scripts
|
||||
- npm install
|
||||
- node ./rolling-release-binary-upload.js cirrus
|
||||
always:
|
||||
videos_artifacts:
|
||||
path: ./tests/videos/**
|
||||
@ -103,6 +114,7 @@ arm_linux_task:
|
||||
|
||||
silicon_mac_task:
|
||||
alias: mac
|
||||
only_if: $CIRRUS_CRON != "" || $CIRRUS_TAG == "regular_release"
|
||||
macos_instance:
|
||||
image: ghcr.io/cirruslabs/macos-monterey-xcode:14
|
||||
memory: 8G
|
||||
@ -112,6 +124,7 @@ silicon_mac_task:
|
||||
APPLEID: ENCRYPTED[549ce052bd5666dba5245f4180bf93b74ed206fe5e6e7c8f67a8596d3767c1f682b84e347b326ac318c62a07c8844a57]
|
||||
APPLEID_PASSWORD: ENCRYPTED[774c3307fd3b62660ecf5beb8537a24498c76e8d90d7f28e5bc816742fd8954a34ffed13f9aa2d1faf66ce08b4496e6f]
|
||||
TEAM_ID: ENCRYPTED[11f3fedfbaf4aff1859bf6c105f0437ace23d84f5420a2c1cea884fbfa43b115b7834a463516d50cb276d4c4d9128b49]
|
||||
ROLLING_UPLOAD_TOKEN: ENCRYPTED[690950798401ec3715e9d20ac29a0859d3c58097038081ff6afeaf4721e661672d34eb952d8a6442bc7410821ab8545a]
|
||||
prepare_script:
|
||||
- brew install node@16 yarn git python@$PYTHON_VERSION
|
||||
- git submodule init
|
||||
@ -121,7 +134,7 @@ silicon_mac_task:
|
||||
- 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 || yarn install
|
||||
- yarn install --ignore-engines || yarn install --ignore-engines
|
||||
build_script:
|
||||
- export PATH="/opt/homebrew/bin:/opt/homebrew/opt/node@16/bin:$PATH"
|
||||
- yarn build
|
||||
@ -133,7 +146,15 @@ silicon_mac_task:
|
||||
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
|
||||
rolling_upload_script:
|
||||
- export PATH="/opt/homebrew/bin:/opt/homebrew/opt/node@16/bin:$PATH"
|
||||
- cd ./script/rolling-release-scripts
|
||||
- npm install
|
||||
- node ./rolling-release-binary-upload.js cirrus
|
||||
always:
|
||||
videos_artifacts:
|
||||
path: ./tests/videos/**
|
||||
@ -142,72 +163,89 @@ silicon_mac_task:
|
||||
type: text/xml
|
||||
format: junit
|
||||
|
||||
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 || arch -x86_64 npx yarn install
|
||||
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
|
||||
binary_artifacts:
|
||||
path: ./binaries/*
|
||||
test_script:
|
||||
- export PATH="/usr/local/opt/node@16/bin:/usr/local/bin:$PATH"
|
||||
- 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
|
||||
# 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
|
||||
# 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
|
||||
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\
|
||||
prepare_script:
|
||||
- choco install nodejs --version=14.15.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'
|
||||
install_script:
|
||||
- npx yarn install --ignore-engines || sleep 1 && npx yarn install --ignore-engines || echo "There is a reason for so many tries"
|
||||
build_script:
|
||||
- npx yarn build:apm
|
||||
- npx yarn build || npx yarn build || npx yarn build
|
||||
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\*
|
||||
# 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\
|
||||
# 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'
|
||||
# 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
|
||||
# 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
|
||||
|
@ -7,13 +7,18 @@ module.exports = {
|
||||
extends: [
|
||||
"eslint:recommended",
|
||||
"plugin:node/recommended",
|
||||
"plugin:jsdoc/recommended"
|
||||
// "plugin:jsdoc/recommended"
|
||||
],
|
||||
overrides: [],
|
||||
parserOptions: {
|
||||
ecmaVersion: "latest"
|
||||
},
|
||||
rules: {
|
||||
"space-before-function-paren": ["error", {
|
||||
anonymous: "always",
|
||||
asyncArrow: "always",
|
||||
named: "never"
|
||||
}],
|
||||
"node/no-unpublished-require": [
|
||||
"error",
|
||||
{
|
||||
|
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -1,3 +1,5 @@
|
||||
CHANGELOG.* merge=union
|
||||
|
||||
# Specs depend on character counts, if we don't specify the line endings the
|
||||
# fixtures will vary depending on platform
|
||||
spec/fixtures/**/*.js text eol=lf
|
||||
|
174
.github/renovate.json
vendored
174
.github/renovate.json
vendored
@ -1,20 +1,178 @@
|
||||
{
|
||||
"schedule": ["every weekend"],
|
||||
"extends": [ "config:base", ":dependencyDashboardApproval"],
|
||||
"constraints": {
|
||||
"node": "< 16"
|
||||
},
|
||||
"labels": ["dependencies"],
|
||||
"separateMajorMinor": "false",
|
||||
"ignorePaths": [
|
||||
"packages/autocomplete-atom-api/spec/fixtures/",
|
||||
"packages/dev-live-reload/spec/fixtures/",
|
||||
"packages/incompatible-packages/spec/fixtures/",
|
||||
"packages/settings-view/spec/fixtures/",
|
||||
"spec/fixtures/"
|
||||
],
|
||||
"packageRules": [
|
||||
{
|
||||
"description": "Group all DevDependencies for the Core Editor",
|
||||
"matchDepTypes": ["devDependencies"],
|
||||
"matchUpdateTypes": ["major", "minor", "patch", "pin", "digest", "lockFileMaintenance", "rollback", "bump"],
|
||||
"groupName": "devDependencies",
|
||||
"semanticCommitType": "chore",
|
||||
"automerge": true
|
||||
"groupName": "Core DevDependencies",
|
||||
"matchPaths": ["package.json"]
|
||||
},
|
||||
{
|
||||
"description": "Group all Dependencies for the Core Editor",
|
||||
"matchDepTypes": ["dependencies"],
|
||||
"matchUpdateTypes": ["major", "minor", "patch", "pin", "digest", "lockFileMaintenance", "rollback", "bump"],
|
||||
"groupName": "dependencies",
|
||||
"semanticCommitType": "fix"
|
||||
"groupName": "Core Dependencies",
|
||||
"matchPaths": ["package.json"]
|
||||
},
|
||||
{
|
||||
"description": "Group all of our Syntax Themes and UI Themes",
|
||||
"groupName": "Core Syntax & UI Themes",
|
||||
"matchPaths": [
|
||||
"packages/*-syntax/**", "packages/*-ui/**", "packages/*-theme/**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Group all of our Languages",
|
||||
"groupName": "Core Languages",
|
||||
"matchPaths": [
|
||||
"packages/language-*/**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/about/**" ], "groupName": "about package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/archive-view/**" ], "groupName": "archive-view package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/autocomplete-atom-api/**" ], "groupName": "autocomplete-atom-api"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/autocomplete-css/**" ], "groupName": "autocomplete-css package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/autocomplete-html/**" ], "groupName": "autocomplete-html package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/autocomplete-plus/**" ], "groupName": "autocomplete-plus package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/autocomplete-snippets/**" ], "groupName": "autocomplete-snippets package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/autoflow/**" ], "groupName": "autoflow package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/autosave/**" ], "groupName": "autosave package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/background-tips/**" ], "groupName": "background-tips package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/bookmarks/**" ], "groupName": "bookmarks package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/bracket-matcher/**" ], "groupName": "bracket-matcher package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/command-palette/**" ], "groupName": "command-palette package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/dalek/**" ], "groupName": "dalek package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/deprecation-cop/**" ], "groupName": "deprecation-cop package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/dev-live-reload/**" ], "groupName": "dev-live-reload package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/encoding-selector/**" ], "groupName": "encoding-selector package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/exception-reporting/**" ], "groupName": "exception-reporting package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/find-and-replace/**" ], "groupName": "find-and-replace package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/fuzzy-finder/**" ], "groupName": "fuzzy-finder package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/git-diff/**" ], "groupName": "git-diff package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/go-to-line/**" ], "groupName": "go-to-line package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/grammar-selector/**" ], "groupName": "grammar-selector package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/image-view/**" ], "groupName": "image-view package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/incompatible-packages/**" ], "groupName": "incompatible-packages package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/keybinding-resolver/**" ], "groupName": "keybinding-resolver package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/line-ending-selector/**" ], "groupName": "line-ending-selector package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/link/**" ], "groupName": "link package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/markdown-preview/**" ], "groupName": "markdown-preview package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/notifications/**" ], "groupName": "notifications package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/open-on-github/**" ], "groupName": "open-on-github package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/package-generator/**" ], "groupName": "package-generator package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/pulsar-updater/**" ], "groupName": "pulsar-updater package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/settings-view/**" ], "groupName": "settings-view package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/spell-check/**" ], "groupName": "spell-check package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/status-bar/**" ], "groupName": "status-bar package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/styleguide/**" ], "groupName": "styleguide package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/symbols-view/**" ], "groupName": "symbols-view package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/tabs/**" ], "groupName": "tabs package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/timecop/**" ], "groupName": "timecop package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/tree-view/**" ], "groupName": "tree-view package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/update-package-dependencies/**" ], "groupName": "update-package-dependencies package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/welcome/**" ], "groupName": "welcome package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/whitespace/**" ], "groupName": "whitespace package"
|
||||
},
|
||||
{
|
||||
"matchPaths": [ "packages/wrap-guide/**" ], "groupName": "wrap-guide package"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
145
.github/workflows/build.yml
vendored
Normal file
145
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,145 @@
|
||||
name: Build Pulsar Binaries
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
# Variables needed for build information
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PYTHON_VERSION: '3.10'
|
||||
NODE_VERSION: 16
|
||||
ROLLING_UPLOAD_TOKEN: ${{ secrets.ROLLING_RELEASE_UPLOAD_TOKEN }}
|
||||
# Below variables allow us to quickly control visual tests for each platform
|
||||
RUN_WINDOWS_VT: false
|
||||
RUN_LINUX_VT: true
|
||||
RUN_MACOS_VT: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-latest, windows-latest, macos-latest ]
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout the latest code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
- name: Setup Git Submodule
|
||||
run: |
|
||||
git submodule init
|
||||
git submodule update
|
||||
|
||||
- name: Check Pulsar Version
|
||||
if: ${{ runner.os != 'Windows' }}
|
||||
run: sed -i -e "s/[0-9]*-dev/`date -u +%Y%m%d%H`/g" package.json
|
||||
|
||||
- name: Check Pulsar Version - Windows
|
||||
if: ${{ runner.os == 'Windows' }}
|
||||
run: (Get-Content package.json) -replace '[0-9]*-dev', (date -u +%Y%m%d%H) | Set-Content -Path package.json
|
||||
|
||||
- name: Install Pulsar Dependencies
|
||||
uses: nick-fields/retry@943e742917ac94714d2f408a0e8320f2d1fcafcd
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
retry_on: error
|
||||
command: yarn install --ignore-engines
|
||||
on_retry_command: rm -R node_modules
|
||||
|
||||
- name: Build Pulsar
|
||||
uses: nick-fields/retry@943e742917ac94714d2f408a0e8320f2d1fcafcd
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
retry_on: error
|
||||
command: |
|
||||
yarn build
|
||||
yarn run build:apm
|
||||
|
||||
# macOS Signing Stuff
|
||||
- name: Build Pulsar Binaries (macOS)
|
||||
if: ${{ runner.os == 'macOS' }}
|
||||
env:
|
||||
CSC_LINK: ${{ secrets.CSC_LINK }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
||||
APPLEID: ${{ secrets.APPLEID }}
|
||||
APPLEID_PASSWORD: ${{ secrets.APPLEID_PASSWORD }}
|
||||
TEAM_ID: ${{ secrets.TEAM_ID }}
|
||||
uses: nick-fields/retry@943e742917ac94714d2f408a0e8320f2d1fcafcd
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
retry_on: error
|
||||
command: yarn dist
|
||||
|
||||
- name: Build Pulsar Binaries
|
||||
if: ${{ runner.os != 'macOS' }}
|
||||
uses: nick-fields/retry@943e742917ac94714d2f408a0e8320f2d1fcafcd
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
retry_on: error
|
||||
command: yarn dist
|
||||
|
||||
- name: Upload Binary Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ matrix.os }} Binaries
|
||||
path: ./binaries/*
|
||||
|
||||
- name: Test Binary - Linux
|
||||
if: ${{ (runner.os == 'Linux') && env.RUN_LINUX_VT }}
|
||||
run: |
|
||||
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
|
||||
|
||||
- name: Test Binary - Windows
|
||||
if: runner.os == 'Windows' && env.RUN_WINDOWS_VT == true
|
||||
# TODO: Convert script to PowerShell
|
||||
run: |
|
||||
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"
|
||||
|
||||
- name: Test Binary - macOS
|
||||
if: runner.os == 'macOS' && env.RUN_MACOS_VT == true
|
||||
run: |
|
||||
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
|
||||
|
||||
- name: Add binaries to Rolling Release Repo
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
# We only want to upload rolling binaries if they are a commit to master
|
||||
# Otherwise we want to not upload if it's a PR or manually triggered build
|
||||
run: |
|
||||
cd ./script/rolling-release-scripts
|
||||
npm install
|
||||
node ./rolling-release-binary-upload.js
|
||||
|
||||
- name: Upload Video Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ matrix.os }} Videos
|
||||
path: ./tests/videos/**
|
2
.github/workflows/documentation.yml
vendored
2
.github/workflows/documentation.yml
vendored
@ -4,7 +4,7 @@ on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
18
.github/workflows/editor-tests.yml
vendored
18
.github/workflows/editor-tests.yml
vendored
@ -1,13 +1,15 @@
|
||||
name: Editor tests
|
||||
on:
|
||||
- pull_request
|
||||
pull_request:
|
||||
push:
|
||||
branches: ['master']
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
ATOM_JASMINE_REPORTER: list
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
name: tests
|
||||
name: Tests
|
||||
if: |
|
||||
!startsWith(github.event.pull_request.title, '[skip-ci]') &&
|
||||
!startsWith(github.event.pull_request.title, '[skip-editor-ci]')
|
||||
@ -19,20 +21,20 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout the latest code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2-beta
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- name: install dependencies
|
||||
- name: Install Dependencies
|
||||
run: yarn install
|
||||
|
||||
- name: build dependencies
|
||||
- name: Build Dependencies
|
||||
run: yarn build
|
||||
|
||||
- name: Run tests
|
||||
uses: GabrielBB/xvfb-action@v1
|
||||
- name: Run Tests
|
||||
uses: coactions/setup-xvfb@v1.0.1
|
||||
with:
|
||||
run: node script/run-tests.js spec
|
||||
|
36
.github/workflows/package-tests-linux.yml
vendored
36
.github/workflows/package-tests-linux.yml
vendored
@ -1,6 +1,8 @@
|
||||
name: Package tests for Pulsar on Linux
|
||||
name: Package tests
|
||||
on:
|
||||
- pull_request
|
||||
pull_request:
|
||||
push:
|
||||
branches: ['master']
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
ATOM_JASMINE_REPORTER: list
|
||||
@ -13,10 +15,10 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout the latest code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2-beta
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
@ -39,6 +41,15 @@ jobs:
|
||||
path: pulsar.deb
|
||||
key: pulsar-${{ github.sha }}
|
||||
|
||||
- name: Cache dependencies
|
||||
id: cache-dependencies
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
node_modules
|
||||
packages
|
||||
key: dependencies-${{ github.sha }}
|
||||
|
||||
test:
|
||||
name: Package
|
||||
needs: setup
|
||||
@ -93,12 +104,13 @@ jobs:
|
||||
- package: "notifications"
|
||||
- package: "open-on-github"
|
||||
- package: "package-generator"
|
||||
- package: "pulsar-updater"
|
||||
- package: "settings-view"
|
||||
- package: "snippets"
|
||||
- package: "spell-check"
|
||||
- package: "status-bar"
|
||||
- package: "styleguide"
|
||||
- package: "symbols-view"
|
||||
# - package: "symbols-view"
|
||||
- package: "tabs"
|
||||
- package: "timecop"
|
||||
- package: "tree-view"
|
||||
@ -143,15 +155,27 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout the latest code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup NodeJS
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- name: Restore dependencies from Cache
|
||||
id: restore-dependencies
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
node_modules
|
||||
packages
|
||||
key: dependencies-${{ github.sha }}
|
||||
|
||||
- name: Install Dependencies
|
||||
if: steps.restore-dependencies.outputs.cache-hit != 'true'
|
||||
run: yarn install || yarn install
|
||||
|
||||
- name: Build Dependencies
|
||||
if: steps.restore-dependencies.outputs.cache-hit != 'true'
|
||||
run: yarn build || yarn build
|
||||
|
||||
- name: Restore pulsar from Cache
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -8,6 +8,8 @@ Thumbs.db
|
||||
.nvm-version
|
||||
.vscode
|
||||
.python-version
|
||||
.envrc
|
||||
.tool-versions
|
||||
node_modules
|
||||
*.log
|
||||
/tags
|
||||
|
216
CHANGELOG.md
216
CHANGELOG.md
@ -6,11 +6,227 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## 1.108.0
|
||||
|
||||
- Restored ability for `less` files in packages to use inline JavaScript inside backticks.
|
||||
- Fixed a syntax highlighting issue inside the `styleguide` package.
|
||||
- Fixed an issue with rubygems timing out on ARM Linux workflow.
|
||||
- Rewrote Tree-sitter scope predicates to use `#is?` and `#is-not?` where applicable.
|
||||
- Ensure that project-specific setting overrides don't leak to the user's config file when the settings UI is visited.
|
||||
- Added a feature in `markdown-preview` that adds support for Linguist, Chroma, Rouge, and HighlightJS for language identifiers in fenced code blocks.
|
||||
- Fixed the `TextMate` `language-toml` grammar to properly support whitespace where-ever it may appear.
|
||||
- Added a Tree-Sitter grammar for YAML files.
|
||||
- Added a new core package `pulsar-updater` to help users update Pulsar.
|
||||
- Added `ppm` and `ppm.cmd` binaries/launchers within ppm. This allows easier integration of correctly named binaries on more systems in more contexts (especially Windows). Existing `apm` and `apm.cmd` binaries/launchers are still there for the time being.
|
||||
- Added a modern Tree-Sitter grammar for Markdown files.
|
||||
|
||||
### Pulsar
|
||||
- Added: Add the Tree-Sitter Markdown grammar [@savetheclocktower](https://github.com/pulsar-edit/pulsar/pull/659)
|
||||
- Fixed: [pulsar-updater] Correct deb-get instructions ( + readme change) [@Daeraxa](https://github.com/pulsar-edit/pulsar/pull/669)
|
||||
- Added: Tree-sitter running fixes [@savetheclocktower](https://github.com/pulsar-edit/pulsar/pull/660)
|
||||
- Added: Add `pulsar-updater` as a core bundled Package [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/656)
|
||||
- Added: Manual Decaf Bundle (`autocomplete-atom-api`, `autoflow`, `deprecation-cop`) Source [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/664)
|
||||
- Bumped: [Time Sensitive] Update Cirrus Encrypted token for GitHub Access [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/666)
|
||||
- Added: [core]: Transforming Deprecated Math Usage - Support for Variables [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/653)
|
||||
- Added: Add Tree-sitter grammar for YAML [@savetheclocktower](https://github.com/pulsar-edit/pulsar/pull/634)
|
||||
- Fixed: [language-toml] Add whitespace rule to values [@arite](https://github.com/pulsar-edit/pulsar/pull/646)
|
||||
- Added: [markdown-preview]: Support for nested table objects in Yaml Frontmatter [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/629)
|
||||
- Added: [markdown-preview]: Revamp Fenced Code Block Language Identifiers [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/622)
|
||||
- Bumped: ppm: Update submodule to 49c8ced8f9552bb4aeb279130 [@DeeDeeG](https://github.com/pulsar-edit/pulsar/pull/654)
|
||||
- Fixed: [settings-view] Don't let project-specific settings pollute the UI [@savetheclocktower](https://github.com/pulsar-edit/pulsar/pull/655)
|
||||
- Added: [modern-tree-sitter] Overhaul Tree-sitter scope tests [@savetheclocktower](https://github.com/pulsar-edit/pulsar/pull/652)
|
||||
- Fixed: fix(arm): use rubygems from APT [@cat-master21](https://github.com/pulsar-edit/pulsar/pull/651)
|
||||
- Added: [language-*]: Manual Spec Decaf (Part 1) [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/632)
|
||||
- Fixed: [styleguide] Fix error when styleguide is shown... [@savetheclocktower](https://github.com/pulsar-edit/pulsar/pull/648)
|
||||
- Bumped: Bump `less-cache` to 2.0.1 [@savetheclocktower](https://github.com/pulsar-edit/pulsar/pull/644)
|
||||
|
||||
### ppm
|
||||
|
||||
- Added: Add 'ppm' bins to complement existing 'apm' bins [@DeeDeeG](https://github.com/pulsar-edit/ppm/pull/80)
|
||||
- Fixed: Replace "apm" by "ppm" in help messages. [@azuledu](https://github.com/pulsar-edit/ppm/pull/62)
|
||||
- Bumped: Update OS, actions, node [@Spiker985](https://github.com/pulsar-edit/ppm/pull/57)
|
||||
|
||||
## 1.107.1
|
||||
|
||||
- Updated the `github` package to resolve incompatibility in Style Sheets against Less v4
|
||||
|
||||
### Pulsar
|
||||
|
||||
- Bumped: deps: Update github package to v0.36.17-pretranspiled [@DeeDeeG](https://github.com/pulsar-edit/pulsar/pull/639)
|
||||
|
||||
### github
|
||||
|
||||
- Fixed: Fix Less Syntax [@confused-Techie](https://github.com/pulsar-edit/github/pull/34)
|
||||
|
||||
## 1.107.0
|
||||
|
||||
- Fixed a number of issues with the experimental modern Tree-sitter grammar mode
|
||||
- Pulsar can now be added to the PATH on Windows, via the "System" pane within Settings View.
|
||||
- Bumped `less-cache` to `v2.0.0` which uses `less@4.1.3`. This adds many new features of Less, while causing breaking changes to existing Less StyleSheets. Read more about these changes [here](https://github.com/pulsar-edit/less-cache/releases/tag/v2.0.0). Pulsar will attempt to automatically repair any breaking changes in any package style sheets, while emitting deprecations.
|
||||
- Fixed a bug that would render files unable to be clicked with sticky headers enabled on One-Dark and One-Light themes.
|
||||
- Added a Modern Tree-Sitter TOML Grammar.
|
||||
- Added a new API endpoint within Pulsar of `atom.versionSatisifes()` to allow packages to safely check the version of Pulsar, instead of having to do so themselves.
|
||||
- An issue in a downstream dependency has been resolved that improperly flagged Pulsar as malicious.
|
||||
|
||||
### Pulsar
|
||||
- Added: Improved Windows Install (`PATH`, `ATOM_HOME`, `InstallLocation`) [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/604)
|
||||
- Fixed: Running PR for Tree-Sitter fixes [@savetheclocktower](https://github.com/pulsar-edit/pulsar/pull/555)
|
||||
- Added: [autocomplete-css]: Manual Decaf of Source [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/631)
|
||||
- Fixed: [welcome]: Ensure Changelog Always Shows if enabled, and version hasn't been dismissed [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/607)
|
||||
- Bumped: [autocomplete-plus] Maintenance - Deps bumps, remove CoffeeScript files [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/630)
|
||||
- Fixed: Fix tree-view sticky headers of one-dark & one-light themes [@asiloisad](https://github.com/pulsar-edit/pulsar/pull/599)
|
||||
- Fixed: [spell-check]: Remove usage of reserved word [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/620)
|
||||
- Added: [core]: Implement API on `atom.` to compare Pulsar Versions [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/588)
|
||||
- Added: [settings-view]: Manual Decaf (source) [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/558)
|
||||
- Bumped: [core]: Bump `less-cache` to `v2.0.0` Upgrades `less` to `4.1.3` [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/611)
|
||||
- Added: [core]: Bundle `spell-check` [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/614)
|
||||
- Bumped: Update dependency semver to v7.5.2 [SECURITY] [@renovate](https://github.com/pulsar-edit/pulsar/pull/609)
|
||||
- Added: [modern-tree-sitter] Add TOML tree-sitter grammar [@savetheclocktower](https://github.com/pulsar-edit/pulsar/pull/617)
|
||||
- Fixed: [language-toml]: Allow spaces within Array [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/610)
|
||||
- Fixed: Pin `es5-ext` to `pulsar-edit/es5-ext` removing code flagged as malicious [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/608)
|
||||
- Bumped: [git-diff] Bump all Deps [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/603)
|
||||
- Bumped: Update dependency semver [SECURITY] [@renovate](https://github.com/pulsar-edit/pulsar/pull/605)
|
||||
- Fixed: [autocomplete-css] Get tests passing for new CSS tree-sitter grammar [@savetheclocktower](https://github.com/pulsar-edit/pulsar/pull/601)
|
||||
- Bumped: [dalek] Bump dependencies to latest, fix links [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/602)
|
||||
- Bumped: Update dependency marked to v5.0.3 [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/568)
|
||||
|
||||
### less-cache
|
||||
- Bumped: Bump `1.1.1` => `2.0.0` [@confused-Techie](https://github.com/pulsar-edit/less-cache/pull/5)
|
||||
- Bumped: Bump `less` `3.12.2` => `4.1.3` [@confused-Techie](https://github.com/pulsar-edit/less-cache/pull/4)
|
||||
- Added: Repository Cleanup + CoffeeScript tool Removal (Depends on #2) [@confused-Techie](https://github.com/pulsar-edit/less-cache/pull/3)
|
||||
- Added: Manual decaf of source files [@confused-Techie](https://github.com/pulsar-edit/less-cache/pull/2)
|
||||
- Added: Implement Repo Tests [@confused-Techie](https://github.com/pulsar-edit/less-cache/pull/1)
|
||||
|
||||
## 1.106.0
|
||||
|
||||
- Fixed bug that happens on some systems when trying to launch Pulsar using the Cinnamon desktop environment
|
||||
- Added a modern implementation of Tree-sitter grammars behind an experimental flag. Enable the “Use Modern Tree-Sitter Implementation” in the Core settings to try it out
|
||||
- Bugfix: fixed Clojure indentation on tree-sitter
|
||||
- Improved the Clojure language support by migrating it to tree-sitter and support block comments, quoting, and other advanced features on modern tree-sitter implementation
|
||||
- Fixed a bug that could cause images to not appear the first time opening them
|
||||
- `autocomplete-css` Completions are now sorted in a way that may match what users expect
|
||||
- Added a "Log Out" menu item for the `github` package
|
||||
|
||||
### Pulsar
|
||||
- Updated: deps: Bump github to v0.36.16-pretranspiled [@DeeDeeG](https://github.com/pulsar-edit/pulsar/pull/592)
|
||||
- Removed: Mostly remove `request` [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/474)
|
||||
- Fixed: Fix: Image doesn't appear at first open [@asiloisad](https://github.com/pulsar-edit/pulsar/pull/579)
|
||||
- Removed: Remove specific cinnamon condition [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/563)
|
||||
- Fixed: Fix of Clojure's indentation rules by removing query file [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/584)
|
||||
- Fixed: Update links in settings page [@Daeraxa](https://github.com/pulsar-edit/pulsar/pull/570)
|
||||
- Added: [autocomplete-css] Sort `completions.json` [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/552)
|
||||
- Fixed: Fixes on "comment block" for Clojure grammar [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/573)
|
||||
- Added: Hardcode NSIS GUID [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/566)
|
||||
- Fixed: Make yarn sane [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/567)
|
||||
- Fixed: Huge improvement on Clojure highlighting [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/553)
|
||||
- Removed: Removed unused_require method [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/561)
|
||||
- Bumped: Update dependency underscore to 1.12.1 [SECURITY] [@renovate](https://github.com/pulsar-edit/pulsar/pull/504)
|
||||
- Added: Add modern tree-sitter support behind an experimental flag [@savetheclocktower](https://github.com/pulsar-edit/pulsar/pull/472)
|
||||
- Added: Make CHANGELOG easier to merge and update dompurify [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/537)
|
||||
- Added: js operators [@icecream17](https://github.com/pulsar-edit/pulsar/pull/79)
|
||||
- Bumped: Update dependency postcss to v8.2.13 [SECURITY] [@renovate](https://github.com/pulsar-edit/pulsar/pull/514)
|
||||
|
||||
### github
|
||||
- Added: Add logout menu option [@Daeraxa](https://github.com/pulsar-edit/github/pull/27)
|
||||
- Updated: ci: Bump action dependencies [@Spiker985](https://github.com/pulsar-edit/github/pull/19)
|
||||
|
||||
## 1.105.0
|
||||
|
||||
- Rebranded notifications, using our backend to find new versions of package,
|
||||
and our github repository to find issues on Pulsar. Also fixed the "view issue"
|
||||
and "create issue" buttons that were not working
|
||||
- Bumped to latest version of `second-mate`, fixing a memory usage issue in `vscode-oniguruma`
|
||||
- Removed a cache for native modules - fix bugs where an user rebuilds a native
|
||||
module outside of Pulsar, but Pulsar refuses to load anyway
|
||||
- Removed `nslog` dependency
|
||||
- Fixed an error where the GitHub package tried to interact with a diff view after it was closed
|
||||
- Fixed RPM installation failure when Atom was installed on the same machine
|
||||
- Added a new set of Package `activationHooks`, `...:uri-opened` lets a package activate when any URI is opened within Pulsar, and `...:file-name-opened` lets a package activate when any specific filename is opened within Pulsar.
|
||||
|
||||
### Pulsar
|
||||
- Added: Add new `...:uri-opened` && `...:file-name-opened` Package Activation Hook [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/518)
|
||||
- Fixed: Properly localize Download/Stargazer Counts within `settings-view` [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/526)
|
||||
- Added: Add bookmarks service for consumption by other packages [@savetheclocktower](https://github.com/pulsar-edit/pulsar/pull/525)
|
||||
- Added: Bundle notifications [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/529)
|
||||
- Fixed: Fix Ripgrep download issues in CirrusCI [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/530)
|
||||
- Removed: Revert Incorrect Commit [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/528)
|
||||
- Fixed: Making CI green, hopefully [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/523)
|
||||
- Bumped: Bump `second-mate` to 96866771 [@savetheclocktower](https://github.com/pulsar-edit/pulsar/pull/524)
|
||||
- Removed: Remove cache of incompatible native packages [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/493)
|
||||
- Added: Simplify and bundle fuzzy-finder [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/515)
|
||||
- Added: Bundle find and replace [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/517)
|
||||
- Added: Bundle tree view [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/513)
|
||||
- Added: Bundle `autocomplete-atom-api` [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/476)
|
||||
- Added: Add FPM option to stop rpm buildid clash [@Daeraxa](https://github.com/pulsar-edit/pulsar/pull/505)
|
||||
- Bumped: chore(deps): update dependency minimist [security] [@renovate](https://github.com/pulsar-edit/pulsar/pull/502)
|
||||
- Fixed: Disable Failing Tests [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/477)
|
||||
- Bumped: chore(deps): update dependency ajv to 6.12.3 [security] [@renovate](https://github.com/pulsar-edit/pulsar/pull/501)
|
||||
- Bumped: chore(deps): update dependency async to 3.2.2 [security] [@renovate](https://github.com/pulsar-edit/pulsar/pull/495)
|
||||
- Added: Add "icon only" class to settings view icon [@Daeraxa](https://github.com/pulsar-edit/pulsar/pull/456)
|
||||
- Bumped: chore(deps): update dependency minimatch [security] [@renovate](https://github.com/pulsar-edit/pulsar/pull/496)
|
||||
- Removed: Remove `nslog` dependency [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/494)
|
||||
- Added: Setup Renovate [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/469)
|
||||
- Fixed: Don't mark diff ranges on a destroyed buffer [@savetheclocktower](https://github.com/pulsar-edit/pulsar/pull/481)
|
||||
- Added: First Architectural Design Records [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/480)
|
||||
- Bumped: use pular's `typscript-simple` fork, which bumps `typescript` to 5.0.3 [@Meadowsys](https://github.com/pulsar-edit/pulsar/pull/458)
|
||||
- Added: CI: cache and restore dependencies, plus skip rebuilding all over the place (saves a lot of time) [@DeeDeeG](https://github.com/pulsar-edit/pulsar/pull/492)
|
||||
|
||||
### notifications
|
||||
- Fixed: Cleanup and rename [@Sertonix](https://github.com/pulsar-edit/notifications/pull/1)
|
||||
- Added: reject promise with Error instance [@Sertonix](https://github.com/pulsar-edit/notifications/pull/2)
|
||||
- Added: Add our Testing Action [@confused-Techie](https://github.com/pulsar-edit/notifications/pull/3)
|
||||
- Fixed: Change atom strings to pulsar [@mdibella-dev](https://github.com/pulsar-edit/notifications/pull/4)
|
||||
- Bumped: Bump to v3.2 of action-pulsar-dependency [@confused-Techie](https://github.com/pulsar-edit/notifications/pull/5)
|
||||
- Fixed: Fix all Tests [@confused-Techie](https://github.com/pulsar-edit/notifications/pull/6)
|
||||
|
||||
## 1.104.0
|
||||
|
||||
- The settings-view package now lists a package’s 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
|
||||
- Ensured new WASM packages will work on Apple Silicon
|
||||
- Completions for HTML will now be as bleeding edge as possible.
|
||||
|
||||
### Pulsar
|
||||
- Added: `settings-view` Support for Badges [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/451)
|
||||
- Removed: remove weird duplicate accented fixture file (hopefully?) [@Meadowsys](https://github.com/pulsar-edit/pulsar/pull/488)
|
||||
- Added: Add optional entitlements monkey-patch [@confused-Tecie](https://github.com/pulsar-edit/pulsar/pull/483)
|
||||
- Added: Decaf `wrap-guide` [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/443)
|
||||
- Added: Additional Bundling of Core Packages [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/424)
|
||||
- Added: add allow-jit entitlement (fixes Apple Silicon builds) [@Meadowsys](https://github.com/pulsar-edit/pulsar/pull/454)
|
||||
- Removed: Revert "Create i18n API" [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/471)
|
||||
- Added: Build first, and test later [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/463)
|
||||
- Update: [settings-view] Update package snippets view to reflect new features [@savetheclocktower](https://github.com/pulsar-edit/pulsar/pull/406)
|
||||
- Added: Create i18n API [@Meadowsys](https://github.com/pulsar-edit/pulsar/pull/446)
|
||||
- Added: Add Automated updating of `autocomplete-html` `completions.json` [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/405)
|
||||
- Fixed: docs: fix markdown links in packages README [@oakmac](https://github.com/pulsar-edit/pulsar/pull/450)
|
||||
- Fixed: Patch document register element [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/438)
|
||||
- Added: Using "second-mate" [@mauricioszabo](https://github.com/pulsar-edit/pulsar/pull/435)
|
||||
- Fixed: Fix spacing of PHP's "for ..." snippet [@machitgarha](https://github.com/pulsar-edit/pulsar/pull/440)
|
||||
- Update: Update resources metadata [@Spiker985](https://github.com/pulsar-edit/pulsar/pull/414)
|
||||
- Fixed: Cirrus: Windows: install ppm deps with Yarn [@DeeDeeG](https://github.com/pulsar-edit/pulsar/pull/434)
|
||||
- Added: made cirrus build scripts consistent [@Sertonix](https://github.com/pulsar-edit/pulsar/pull/239)
|
||||
- Update: Update package.json author [@Daeraxa](https://github.com/pulsar-edit/pulsar/pull/432)
|
||||
|
||||
### second-mate
|
||||
- Added: Migrate to vscode-oniguruma [@mauricioszabo](https://github.com/pulsar-edit/second-mate/pull/1)
|
||||
|
||||
### autosave
|
||||
- Removed: removed fs-plus dependency [@Sertonix](https://github.com/pulsar-edit/autosave/pull/2)
|
||||
- Update: Cleanup and rename [@Sertonix](https://github.com/pulsar-edit/autosave/pull/1)
|
||||
|
||||
### bracket-matcher
|
||||
- Fixed: Fixing test that need to run locally [@mauricioszabo](https://github.com/pulsar-edit/bracket-matcher/pull/3)
|
||||
- Update: cleanup .md and rename repo url [@Sertonix](https://github.com/pulsar-edit/bracket-matcher/pull/2)
|
||||
- Update: Rename A[a]tom -> P[p]ulsar [@Spiker985](https://github.com/pulsar-edit/bracket-matcher/pull/1)
|
||||
|
||||
### timecop
|
||||
- Update: cleanup and rename [@Sertonix](https://github.com/pulsar-edit/timecop/pull/1)
|
||||
|
||||
### keybinding-resolver
|
||||
- Update: Cleanup and rename [@Sertonix](https://github.com/pulsar-edit/keybinding-resolver/pull/1)
|
||||
|
||||
## 1.103.0
|
||||
|
||||
|
@ -1,9 +0,0 @@
|
||||
preserve_hierarchy: true
|
||||
commit_message: "[skip ci] Translations from Crowdin"
|
||||
append_commit_message: false
|
||||
|
||||
files:
|
||||
- source: /i18n/en.json
|
||||
translation: /i18n/%locale%.json
|
||||
- source: /packages/**/i18n/en.json
|
||||
translation: /packages/**/i18n/%locale%.json
|
@ -158,6 +158,16 @@ style: Exclusively used for the <code>style</code> attribute</p>
|
||||
<dd></dd>
|
||||
<dt><a href="#assert">assert</a></dt>
|
||||
<dd></dd>
|
||||
<dt><a href="#path">path</a></dt>
|
||||
<dd></dd>
|
||||
<dt><a href="#path">path</a></dt>
|
||||
<dd></dd>
|
||||
<dt><a href="#path">path</a></dt>
|
||||
<dd></dd>
|
||||
<dt><a href="#_">_</a></dt>
|
||||
<dd></dd>
|
||||
<dt><a href="#path">path</a></dt>
|
||||
<dd></dd>
|
||||
</dl>
|
||||
|
||||
## Functions
|
||||
@ -169,6 +179,8 @@ style: Exclusively used for the <code>style</code> attribute</p>
|
||||
<dd></dd>
|
||||
<dt><a href="#conditionPromise">conditionPromise()</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>
|
||||
@ -185,6 +197,12 @@ style: Exclusively used for the <code>style</code> attribute</p>
|
||||
<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>
|
||||
@ -508,6 +526,31 @@ This file aims to run some short simple tests against `update.js`. Focusing
|
||||
## assert
|
||||
**Kind**: global constant
|
||||
**Babel**:
|
||||
<a name="path"></a>
|
||||
|
||||
## path
|
||||
**Kind**: global constant
|
||||
**Babel**:
|
||||
<a name="path"></a>
|
||||
|
||||
## path
|
||||
**Kind**: global constant
|
||||
**Babel**:
|
||||
<a name="path"></a>
|
||||
|
||||
## path
|
||||
**Kind**: global constant
|
||||
**Babel**:
|
||||
<a name="_"></a>
|
||||
|
||||
## \_
|
||||
**Kind**: global constant
|
||||
**Babel**:
|
||||
<a name="path"></a>
|
||||
|
||||
## path
|
||||
**Kind**: global constant
|
||||
**Babel**:
|
||||
<a name="beforeEach"></a>
|
||||
|
||||
## beforeEach()
|
||||
@ -520,6 +563,11 @@ This file aims to run some short simple tests against `update.js`. Focusing
|
||||
**Babel**:
|
||||
<a name="conditionPromise"></a>
|
||||
|
||||
## conditionPromise()
|
||||
**Kind**: global function
|
||||
**Babel**:
|
||||
<a name="conditionPromise"></a>
|
||||
|
||||
## conditionPromise()
|
||||
**Kind**: global function
|
||||
**Babel**:
|
||||
@ -572,6 +620,21 @@ This file aims to run some short simple tests against `update.js`. Focusing
|
||||
**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**:
|
||||
|
@ -1,14 +1,18 @@
|
||||
# Atom Docs
|
||||
# Pulsar Docs
|
||||
|
||||
![Atom](https://cloud.githubusercontent.com/assets/72919/2874231/3af1db48-d3dd-11e3-98dc-6066f8bc766f.png)
|
||||
|
||||
Most of the Atom user and developer documentation is contained in the [Atom Flight Manual](https://github.com/atom/flight-manual.atom.io).
|
||||
Most of the Pulsar/Atom user and developer documentation is contained on the [Pulsar Website](https://pulsar-edit.dev/docs/launch-manual/).
|
||||
|
||||
While the Pulsar website does not yet have the Pulsar API documentation, this is partially available within [Pulsar API Documentation](./Pulsar-API-Documentation.md) or otherwise the original docs are available from community members [here](https://atom-flight-manual-archive.github.io/).
|
||||
|
||||
There is also general guidance on the internal [stucture and behavior](./architecture/README.md) of Pulsar available.
|
||||
|
||||
## Build documentation
|
||||
|
||||
Instructions for building Atom on various platforms from source.
|
||||
|
||||
* Moved to [the Flight Manual](https://flight-manual.atom.io/hacking-atom/sections/hacking-on-atom-core/)
|
||||
* Moved to [the Flight Manual](https://pulsar-edit.dev/docs/launch-manual/sections/core-hacking/)
|
||||
* Linux
|
||||
* macOS
|
||||
* Windows
|
||||
@ -16,5 +20,3 @@ Instructions for building Atom on various platforms from source.
|
||||
## Other documentation
|
||||
|
||||
[Native Profiling on macOS](./native-profiling.md)
|
||||
|
||||
The other documentation that was listed here previously has been moved to [the Flight Manual](https://flight-manual.atom.io).
|
||||
|
@ -158,6 +158,16 @@ style: Exclusively used for the <code>style</code> attribute</p>
|
||||
<dd></dd>
|
||||
<dt><a href="#assert">assert</a></dt>
|
||||
<dd></dd>
|
||||
<dt><a href="#path">path</a></dt>
|
||||
<dd></dd>
|
||||
<dt><a href="#path">path</a></dt>
|
||||
<dd></dd>
|
||||
<dt><a href="#path">path</a></dt>
|
||||
<dd></dd>
|
||||
<dt><a href="#_">_</a></dt>
|
||||
<dd></dd>
|
||||
<dt><a href="#path">path</a></dt>
|
||||
<dd></dd>
|
||||
</dl>
|
||||
|
||||
## Functions
|
||||
@ -169,6 +179,8 @@ style: Exclusively used for the <code>style</code> attribute</p>
|
||||
<dd></dd>
|
||||
<dt><a href="#conditionPromise">conditionPromise()</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>
|
||||
@ -185,6 +197,12 @@ style: Exclusively used for the <code>style</code> attribute</p>
|
||||
<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>
|
||||
@ -516,6 +534,31 @@ This file aims to run some short simple tests against `update.js`. Focusing
|
||||
## assert
|
||||
**Kind**: global constant
|
||||
**Babel**:
|
||||
<a name="path"></a>
|
||||
|
||||
## path
|
||||
**Kind**: global constant
|
||||
**Babel**:
|
||||
<a name="path"></a>
|
||||
|
||||
## path
|
||||
**Kind**: global constant
|
||||
**Babel**:
|
||||
<a name="path"></a>
|
||||
|
||||
## path
|
||||
**Kind**: global constant
|
||||
**Babel**:
|
||||
<a name="_"></a>
|
||||
|
||||
## \_
|
||||
**Kind**: global constant
|
||||
**Babel**:
|
||||
<a name="path"></a>
|
||||
|
||||
## path
|
||||
**Kind**: global constant
|
||||
**Babel**:
|
||||
<a name="beforeEach"></a>
|
||||
|
||||
## beforeEach()
|
||||
@ -528,6 +571,11 @@ This file aims to run some short simple tests against `update.js`. Focusing
|
||||
**Babel**:
|
||||
<a name="conditionPromise"></a>
|
||||
|
||||
## conditionPromise()
|
||||
**Kind**: global function
|
||||
**Babel**:
|
||||
<a name="conditionPromise"></a>
|
||||
|
||||
## conditionPromise()
|
||||
**Kind**: global function
|
||||
**Babel**:
|
||||
@ -580,6 +628,21 @@ This file aims to run some short simple tests against `update.js`. Focusing
|
||||
**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**:
|
||||
|
10
docs/architecture/README.md
Normal file
10
docs/architecture/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
# Pulsar Architecture
|
||||
|
||||
This directory contains a collection of files and diagrams, that aim to easily explain some of the core components or behaviors of Pulsar.
|
||||
|
||||
Remember that keeping this charts up to date is best effort, and the age of these files should be taken into consideration.
|
||||
|
||||
## Contents
|
||||
- [Startup Overview](./overview.md)
|
||||
- ['The World'](./the-world.md)
|
||||
- [Package Preload](./package-preload.md)
|
1
docs/architecture/assets/how-everything-connects.svg
Normal file
1
docs/architecture/assets/how-everything-connects.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 30 KiB |
6
docs/architecture/assets/package-preload.svg
Normal file
6
docs/architecture/assets/package-preload.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 30 KiB |
11
docs/architecture/assets/pulsar-overview.svg
Normal file
11
docs/architecture/assets/pulsar-overview.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 18 KiB |
169
docs/architecture/overview.md
Normal file
169
docs/architecture/overview.md
Normal file
@ -0,0 +1,169 @@
|
||||
# Pulsar's High Level Startup Overview
|
||||
|
||||
It's no secret that Pulsar since inherited from Atom, is a big and complex application.
|
||||
|
||||
With many discrete, moving aspects, that not all developers have a concrete grasp on.
|
||||
The goal of this document is to make the architecture of Pulsar, as well as the logical flow
|
||||
more understandable and approachable.
|
||||
|
||||
This will be accomplished through a series of illustrations detailing the functional decomposition and detailed logical flow of Pulsar and it's parts, as well as lists of what's accomplished in each part.
|
||||
|
||||
This document is aimed at those roughly familiar with the large scale goals and features of Pulsar, as well as those with a basic understanding of the package model used to power much of Pulsar's functionality.
|
||||
|
||||
![Pulsar Overview MermaidJS Image](./assets/pulsar-overview.svg "Pulsar Overview")
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
MermaidJS to create image above
|
||||
</summary>
|
||||
|
||||
```
|
||||
flowchart TD
|
||||
id1("`Initialization
|
||||
*./src/main-process/main.js*`") --> id2("`Startup
|
||||
*./src/main-process/start.js*`")
|
||||
id2 --> id3("`Main Process Tests`")
|
||||
id2 --> id4("`Application Startup
|
||||
*./src/main-process/atom-application.js*`")
|
||||
id2 --> id5("`Startup w/ Squirrel
|
||||
*./src/main-process/squirrel-update.js*`")
|
||||
id4 --> id6("`Test Runner
|
||||
*./src/initialize-test-window.js*`")
|
||||
id4 --> id7("`Initialize Application Window
|
||||
*./src/initialize-application-window.js*`")
|
||||
id7 --> id9("`'The World'
|
||||
*./src/atom-environment.js*`")
|
||||
id7 --> id10("`ApplicationDelegate
|
||||
*./src/application-delegate.js*`")
|
||||
id7 --> id8("`Clipboard
|
||||
*./src/clipboard.js*`")
|
||||
id8 --> id9
|
||||
id10 --> id9
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
To further outline what occurs in the steps above:
|
||||
|
||||
## Initialization
|
||||
|
||||
Startup of Pulsar occurs within `./src/main-process/main.js`.
|
||||
|
||||
Which Determines:
|
||||
- `resourcePath`
|
||||
- `devResourcePath`
|
||||
- `stableResourcePath`
|
||||
- `defaultRepositoryPath`
|
||||
|
||||
Which Sets:
|
||||
- Application Start Time
|
||||
|
||||
Which Does:
|
||||
- Initializes Startup of `./src/main-process/start.js`
|
||||
|
||||
## Startup
|
||||
|
||||
The more general startup handling of Pulsar occurs within `./src/main-process/start.js`.
|
||||
|
||||
Which Sets:
|
||||
- Shell Start Time
|
||||
- `app.allowRendererProcessReuse`: `false`
|
||||
- `app.commandLine.appendSwitch('enable-experimental-web-platform-features')`
|
||||
- `app.commandLine.appendSwitch('force-color-profile', config.get('core.colorProfile'))`
|
||||
- `app.setAppUserModelId()`
|
||||
- `app.on('open-file', $)`
|
||||
- `app.on('open-url', $)`
|
||||
|
||||
Which Does:
|
||||
- Normalizes the `resourcePath` and `devResourcePath`
|
||||
- Uses `Config` to locate and read the config file
|
||||
- `atomPaths.setAtomHome()`
|
||||
- `atomPaths.setUserData()`
|
||||
- May defer to `./src/main-process/squirrel-update.js` to startup if on Windows
|
||||
- May defer to `./spec/main-process/mocha-test-runner.js` to startup main process tests
|
||||
- May call `.open()` on `./src/main-process/atom-application.js`
|
||||
|
||||
## Application Startup
|
||||
|
||||
The proper startup of the Pulsar Application occurs within `./src/main-process/atom-application.js`.
|
||||
|
||||
Which Sets:
|
||||
- `APPLICATION_STATE_VERSION`
|
||||
- Global `atomApplication`
|
||||
|
||||
Which Does:
|
||||
- Does setup of the application socket
|
||||
- Handles deprecated benchmark startup
|
||||
- Ensures to return a new instance of `AtomApplication`
|
||||
- Registers basic application commands
|
||||
- Initializes:
|
||||
* `ApplicationMenu`
|
||||
* `AtomProtocolHandler`
|
||||
* `WindowStack`
|
||||
* `FileRecoveryService`
|
||||
* `Config`
|
||||
* `StorageFolder`
|
||||
* `AutoUpdateManager`
|
||||
- May startup the package test runner
|
||||
- May quit if asked to startup in benchmark mode
|
||||
- May open previously opened files/folders
|
||||
- May open new instance of Pulsar
|
||||
|
||||
## Initialize Application Window
|
||||
|
||||
Begins initialization of an individual Pulsar window, occurs within `./src/initialize-application-window.js`.
|
||||
|
||||
Which Determines:
|
||||
|
||||
Which Sets:
|
||||
- Sets the `global.atom` to a new instance of `AtomEnvironment`
|
||||
|
||||
Which Does:
|
||||
- triggers `.preloadPackages()`
|
||||
- Initializes:
|
||||
* Clipboard
|
||||
* AtomEnvironment
|
||||
* ApplicationDelegate
|
||||
|
||||
## 'The World'
|
||||
|
||||
'The World' refers to being within the Pulsar application, most of the application occurs within here.
|
||||
This code lives within `./src/atom-environment.js`.
|
||||
|
||||
An important note about being initialized within the world, there is no access to the `atom`
|
||||
global, until the initial constructor completes processing. Meaning great care must be taken
|
||||
to ensure if `atom` is available within the initialized modules.
|
||||
|
||||
Which Sets:
|
||||
- `AtomEnvironment.version`: `1` | Possibly a reference to `APPLICATION_STATE_VERSION`?
|
||||
- `AtomEnvironment.saveStateDebounceInterval`: `1000`
|
||||
|
||||
Which Does:
|
||||
- Initializes:
|
||||
* Clipboard | Inherited from 'Initialize Application Window'
|
||||
* ApplicationDelegate | Inherited from 'Initialize Application Window'
|
||||
* DeserializerManager
|
||||
* ViewRegistry
|
||||
* NotificationManager
|
||||
* StateStore
|
||||
* Config
|
||||
* KeymapManager
|
||||
* TooltipManager
|
||||
* CommandRegistry
|
||||
* URIHandlerRegistry
|
||||
* GrammarRegistry
|
||||
* StyleManager
|
||||
* PackageManager
|
||||
* ThemeManager
|
||||
* MenuManager
|
||||
* ContextMenuManager
|
||||
* Project
|
||||
* CommandInstaller
|
||||
* ProtocolHandlerInstaller
|
||||
* TextEditorRegistry
|
||||
* Workspace
|
||||
* AutoUpdateManager
|
||||
* WindowEventHandler
|
||||
* HistoryManager
|
66
docs/architecture/package-preload.md
Normal file
66
docs/architecture/package-preload.md
Normal file
@ -0,0 +1,66 @@
|
||||
# Package Preload
|
||||
|
||||
Pulsar's packages are preloaded, very early on within the startup cycle of Pulsar.
|
||||
|
||||
As it's called immediatly after the `atom` global is initialized, it's important to understand what steps occur during preloading, and what package's are affected.
|
||||
|
||||
![Package Preload Overview](./assets/package-preload.svg "Package Preload Overview")
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
MermaidJS to create image above
|
||||
</summary>
|
||||
|
||||
```
|
||||
flowchart TD
|
||||
iaw["`
|
||||
initialize-application-window.js
|
||||
Called right after global 'atom' is set
|
||||
`"] -->
|
||||
ae["`
|
||||
AtomEnvironment
|
||||
.preloadPackages()
|
||||
`"] -->
|
||||
pl1["preloadPackages()"] -->|if in packageCache| sg1
|
||||
|
||||
subgraph sg1
|
||||
direction LR
|
||||
pl2["preloadPackage()"]
|
||||
pl2 -->|"call .preload()"| tp1["new ThemePackage"]
|
||||
pl2 -->|"call .preload()"| p1["new Package"]
|
||||
|
||||
p1 --> p1Pre["`
|
||||
this.preload() call:
|
||||
Does more than advertised here
|
||||
`"]
|
||||
|
||||
p1Pre --> lk1[".loadKeymaps()"]
|
||||
lk1 --> pcRead1["`Read from packagesCache:
|
||||
If bundled package && in packagesCache`"]
|
||||
lk1 --> fileRead1["`Read from file:
|
||||
If !bundled || !in packagesCache`"]
|
||||
|
||||
p1Pre --> lm1[".loadMenus()"]
|
||||
lm1 --> pcRead1
|
||||
lm1 --> fileRead1
|
||||
|
||||
p1Pre --> acss[".activateCoreStartupServices()"]
|
||||
p1Pre --> rmm[".requireMainModule()"]
|
||||
|
||||
acss --> rmm
|
||||
|
||||
p1Pre --> ls1[".loadSettings()"]
|
||||
ls1 --> pcRead1
|
||||
ls1 --> fileRead1
|
||||
|
||||
p1Pre --> ak1[".activateKeymaps()"]
|
||||
p1Pre --> am1[".activateMenus()"]
|
||||
|
||||
tp1 --> tp1Pre[".preload()"]
|
||||
tp1Pre --> csrol1[".reigsterConfigSchemaFromMetadata()"]
|
||||
end
|
||||
```
|
||||
|
||||
</details>
|
101
docs/architecture/the-world.md
Normal file
101
docs/architecture/the-world.md
Normal file
@ -0,0 +1,101 @@
|
||||
# 'The World'
|
||||
|
||||
While it's difficult to convey the full scope of how Pulsar works internally, just like the previous page [`overview.md`](./overview.md) detailed the general gist of how Pulsar starts up, this document provides a quick reference to how all the interal parts of Pulsar are connected.
|
||||
|
||||
This document is not at all comprehensive, and must ensure to be continually updated. Additionally, this image does not track outside dependency usage, nor dependence on every single internal module. Focusing mostly on modules that are either required during initialization, or are referenced during the constructor of their respective class.
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Details on the creation of this image
|
||||
</summary>
|
||||
|
||||
This image has been created with Plant UML. A Live editor is available [here](https://www.plantuml.com/plantuml/uml).
|
||||
|
||||
The code used to create this image:
|
||||
|
||||
```uml
|
||||
@startwbs
|
||||
* Initialization
|
||||
** Startup
|
||||
***< Main Process Tests
|
||||
*** Startup w/ Squirrel
|
||||
***< Application Startup
|
||||
****< Test Runner
|
||||
**** Initialize Application Window
|
||||
***** 'The World'
|
||||
******< Config
|
||||
******* ScopeDescriptor
|
||||
****** KemapManager
|
||||
****** TolltipManager
|
||||
******* Tooltip
|
||||
******< CommandRegistry
|
||||
******< URIHandlerRegistry
|
||||
****** StyleManager
|
||||
******* createStylesElement
|
||||
******* DEPRECATED_SYNTAX_SELECTORS
|
||||
******< MenuManager
|
||||
******* MenuHelpers
|
||||
****** ContextMenuManager
|
||||
******* MenuHelpers
|
||||
******* sortMenuItems
|
||||
******< TextEditorRegistry
|
||||
******* TextEditor
|
||||
******* ScopeDescriptor
|
||||
****** HistoryManager
|
||||
******< DeserializerManager
|
||||
******< ViewRegistry
|
||||
****** NotificationManager
|
||||
******* Notification
|
||||
****** StateStore
|
||||
******< PackageManager
|
||||
******* Package
|
||||
******* ThemePackage
|
||||
******* ModuleCache
|
||||
****** ThemeManager
|
||||
******* LessCompileCache
|
||||
****** Project
|
||||
******* watchPath
|
||||
******* DefaultDirectoryProvider
|
||||
******* Model
|
||||
******* GitRepositoryProvider
|
||||
******< CommandInstaller
|
||||
******< ProtocolHandlerInstaller
|
||||
******< AutoUpdateManager
|
||||
******< WindowEventHandler
|
||||
******* listen
|
||||
******< GrammarRegistry
|
||||
*******< NodeTreeSitterLanguageMode
|
||||
******** TreeIndenter
|
||||
********< TextMateLanguageMode
|
||||
******** TokenizedLine
|
||||
********< ScopeDescriptor
|
||||
******** matcherForSelector
|
||||
********< Token
|
||||
******* WASMTreeSitterLanguageNode
|
||||
******** Parser
|
||||
******* TextMateLanguageMode
|
||||
******** TokenizedLine
|
||||
********< TokenIterator
|
||||
******** ScopeDescriptor
|
||||
********< NullGrammar
|
||||
*******< ScopeDescriptor
|
||||
******* Token
|
||||
****** Workspace
|
||||
******* DefaultDirectorySearcher
|
||||
*******< RipgrepDirectorySearcher
|
||||
******* WorkspaceCenter
|
||||
*******< createWorkspaceElement
|
||||
******* PanelContainer
|
||||
*******< StateStore
|
||||
******* TextEditor
|
||||
*******< Panel
|
||||
******* Task
|
||||
*******< Dock
|
||||
@endwbs
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
![Pulsar 'How Everything Connects' UML Image](./assets/how-everything-connects.svg "Pulsar 'How Everything Connects'")
|
79
docs/decisions/000-adr-template.md
Normal file
79
docs/decisions/000-adr-template.md
Normal file
@ -0,0 +1,79 @@
|
||||
---
|
||||
# These are optional elements. Feel free to remove any of them.
|
||||
status: {proposed | rejected | accepted | deprecated | … | superseded by ADR-0005 <0005-example.md>}
|
||||
date: {YYYY-MM-DD when the decision was last updated}
|
||||
deciders: {list everyone involved in the decision}
|
||||
consulted: {list everyone whose opinions are sought (typically subject-matter experts); and with whom there is a two-way communication}
|
||||
informed: {list everyone who is kept up-to-date on progress; and with whom there is a one-way communication}
|
||||
---
|
||||
# {short title of solved problem and solution}
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
{Describe the context and problem statement, e.g., in free form using two to three sentences or in the form of an illustrative story.
|
||||
You may want to articulate the problem in form of a question and add links to collaboration boards or issue management systems.}
|
||||
|
||||
<!-- This is an optional element. Feel free to remove. -->
|
||||
## Decision Drivers
|
||||
|
||||
* {decision driver 1, e.g., a force, facing concern, …}
|
||||
* {decision driver 2, e.g., a force, facing concern, …}
|
||||
* … <!-- numbers of drivers can vary -->
|
||||
|
||||
## Considered Options
|
||||
|
||||
* {title of option 1}
|
||||
* {title of option 2}
|
||||
* {title of option 3}
|
||||
* … <!-- numbers of options can vary -->
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
Chosen option: "{title of option 1}", because
|
||||
{justification. e.g., only option, which meets k.o. criterion decision driver | which resolves force {force} | … | comes out best (see below)}.
|
||||
|
||||
<!-- This is an optional element. Feel free to remove. -->
|
||||
### Consequences
|
||||
|
||||
* Good, because {positive consequence, e.g., improvement of one or more desired qualities, …}
|
||||
* Bad, because {negative consequence, e.g., compromising one or more desired qualities, …}
|
||||
* … <!-- numbers of consequences can vary -->
|
||||
|
||||
<!-- This is an optional element. Feel free to remove. -->
|
||||
## Validation
|
||||
|
||||
{describe how the implementation of/compliance with the ADR is validated. E.g., by a review or an ArchUnit test}
|
||||
|
||||
<!-- This is an optional element. Feel free to remove. -->
|
||||
## Pros and Cons of the Options
|
||||
|
||||
### {title of option 1}
|
||||
|
||||
<!-- This is an optional element. Feel free to remove. -->
|
||||
{example | description | pointer to more information | …}
|
||||
|
||||
* Good, because {argument a}
|
||||
* Good, because {argument b}
|
||||
<!-- use "neutral" if the given argument weights neither for good nor bad -->
|
||||
* Neutral, because {argument c}
|
||||
* Bad, because {argument d}
|
||||
* … <!-- numbers of pros and cons can vary -->
|
||||
|
||||
### {title of other option}
|
||||
|
||||
{example | description | pointer to more information | …}
|
||||
|
||||
* Good, because {argument a}
|
||||
* Good, because {argument b}
|
||||
* Neutral, because {argument c}
|
||||
* Bad, because {argument d}
|
||||
* …
|
||||
|
||||
<!-- This is an optional element. Feel free to remove. -->
|
||||
## More Information
|
||||
|
||||
{You might want to provide additional evidence/confidence for the decision outcome here and/or
|
||||
document the team agreement on the decision and/or
|
||||
define when and how this decision should be realized and if/when it should be re-visited and/or
|
||||
how the decision is validated.
|
||||
Links to other decisions and resources might appear here as well.}
|
48
docs/decisions/001-change-of-build-method.md
Normal file
48
docs/decisions/001-change-of-build-method.md
Normal file
@ -0,0 +1,48 @@
|
||||
---
|
||||
status: accepted
|
||||
date: 2022-08-01
|
||||
---
|
||||
# Removal of the old build scripts, V8 Snapshots, and migration to `yarn`
|
||||
|
||||
## Build scripts on Atom
|
||||
|
||||
It was _really difficult_ to understand the old build scripts. They basically
|
||||
transpiled the old code and copied some dependencies on a directory called
|
||||
`app`; then, some dependencies were installed, some other steps were run, like
|
||||
creating some manifests and metadata; finally, this transpiled code was passed
|
||||
through another script that basically generated a `mksnapshot` script command,
|
||||
and everything was _manually packaged_ into an ASAR file.
|
||||
|
||||
The problems: the script was too big, it depended on `npm` < 7.0, it used to
|
||||
fail constantly (needing multiple "clean" commands), it took too much time, and
|
||||
basically the "snapshots" depended on the source file _structure_ on the disk -
|
||||
if we decided to rename some file, or move some `require`s around, the script
|
||||
would break; also, some of the dependencies of that script were also made
|
||||
in-house, making for new work to keep in Pulsar
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
* Easier to install dependencies
|
||||
* Easier to build binaries, in a way that can work in the first run instead
|
||||
of multiple retries
|
||||
* Make it easier to build and start hacking the editor
|
||||
* Faster to build and hack, specially after bumping dependencies
|
||||
|
||||
## Considered Options
|
||||
|
||||
* Rewrite the scripts
|
||||
* Remove the scripts completely and migrate to electron-build
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
Chosen option: "Remove the scripts". It makes things easier because we don't
|
||||
need to keep any in-house packaging of Electron, ASAR creation, and
|
||||
RPM/DEB/AppImage generation for Linux, or binaries for Mac / Windows.
|
||||
|
||||
### Consequences
|
||||
|
||||
* Faster editor bootstrap, and easier to bump dependencies, specially Electron
|
||||
* The editor is _way slower_ to load
|
||||
* We had some bugs because we are not using V8 Snapshots anymore - basically,
|
||||
with V8 Snapshots, the global object `atom` was present where it wasn't supposed
|
||||
to
|
44
docs/decisions/002-remove-old-watcher-libraries.md
Normal file
44
docs/decisions/002-remove-old-watcher-libraries.md
Normal file
@ -0,0 +1,44 @@
|
||||
---
|
||||
status: accepted
|
||||
---
|
||||
# Removal of experimental watchers on Pulsar
|
||||
|
||||
## Removal of @atom/watcher and others
|
||||
|
||||
Atom had four different ways of watching for filesystem changes. Two used an
|
||||
in-house library called `@atom/watcher` - one for "polling" and other for
|
||||
"experimental". It also had a different library to watch files for changes that
|
||||
basically used the Tree-View package's watch mechanism
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
* We don't know what "experimental watcher" means, and we have no way of knowing
|
||||
* We don't want to support another "in-house" library to watch files, specially
|
||||
considering that it's a binary library
|
||||
* We know that tree-view _can use_ Pulsar's watcher mechanism to watch files,
|
||||
meaning we enter somekind of weird loop
|
||||
|
||||
## Considered Options
|
||||
|
||||
* Use `nsfw` only
|
||||
* Use `chokidar` only
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
Use only `nsfw` library to watch for changes, and use the community version of
|
||||
that library. Make the config for "watch" in Pulsar reflect that we only have
|
||||
`nsfw` as an option (Native operating system APIs).
|
||||
|
||||
## Validation
|
||||
|
||||
Files are still being watched. Config failed to watch, but that's because of a
|
||||
different issue - see ADR 001
|
||||
|
||||
## More Information
|
||||
|
||||
We still have another "watch" library called Pathwatcher. It is used by
|
||||
TextBuffer, atom-keymap and tree-view. It may be possible to remove this library
|
||||
to use only Pulsar's watch mechanism (it is available under the public API) so
|
||||
every code will basically use the public API and they will all respect the
|
||||
choice of what Pulsar defined, if we decide to introduce new file watcher
|
||||
libraries in the future
|
22
docs/decisions/003-use-global-pulsar-on-pathwatcher.md
Normal file
22
docs/decisions/003-use-global-pulsar-on-pathwatcher.md
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
status: rejected
|
||||
---
|
||||
# Make a compatibility layer on Pulsar's "pathwatcher"
|
||||
|
||||
## Remove the binary dependency on "pathwatcher" and a compatibility layer to use Pulsar's public API
|
||||
|
||||
The idea was to keep 100% API compatibility with Pathwatcher using Pulsar's public API (using `require('atom').watchPath` code). The experiment lives on [Pulsar
|
||||
Pathwatcher](https://github.com/pulsar-edit/pulsar-pathwatcher) repository
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
* Avoid another watch library
|
||||
* Avoid another binary dependency
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
Because of the cyclic dependency between TextBuffer and Pulsar, together with
|
||||
the "exports" library and other issues, this experiment didn't go far. Some
|
||||
infinite loops happened, sometimes the renderer process crashed, and sometimes
|
||||
other weird issues appeared depending on the order things got loaded, so
|
||||
this will probably be revisited in the future, if at all.
|
87
docs/decisions/004-change-oniguruma-implementation.md
Normal file
87
docs/decisions/004-change-oniguruma-implementation.md
Normal file
@ -0,0 +1,87 @@
|
||||
---
|
||||
status: accepted
|
||||
date: 2023-03-28
|
||||
deciders: '@mauricioszabo, @confused-Techie, @savetheclocktower'
|
||||
---
|
||||
# Use a different Oniguruma implementation for TextMate grammars
|
||||
|
||||
## Current version of Oniguruma is in-house and crashes with Electron >= 21
|
||||
|
||||
To tokenize TextMate grammars, Pulsar uses a NPM binary library called
|
||||
Oniguruma. Unfortunately, while we could make the native library work on
|
||||
Electron versions 14 up to 20, when upgrading to Electron 21 it breaks because
|
||||
of the new V8 Memory Model (sandboxed pointers).
|
||||
|
||||
## Considered Options
|
||||
|
||||
* Migrate to a pure-JS version of Oniguruma
|
||||
* Migrate to [WASM Oniguruma](https://github.com/rebornix/wasm-oniguruma)
|
||||
* Migrate to [VSCode Oniguruma](https://github.com/microsoft/vscode-oniguruma/)
|
||||
* Migrate **all tokenization** to [VSCode Textmate](https://github.com/microsoft/vscode-textmate)
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
Chosen option: "Migrate fo VSCode Oniguruma", because the pure JS version we found was not up-to-date with Oniguruma and could not parse most of the Regexp that our grammars need; second, WASM Oniguruma didn't see any update in the last 6 years, so we end up with vscode-oniguruma.
|
||||
|
||||
We basically duplicated the
|
||||
[first-mate](https://github.com/pulsar-edit/first-mate/) repository and created
|
||||
[second-mate](https://github.com/pulsar-edit/second-mate/), that uses the new
|
||||
VSCode oniguruma version
|
||||
|
||||
## Pros and Cons of the Options
|
||||
|
||||
### Bump to Electron 23
|
||||
|
||||
It was confirmed that new vscode-oniguruma works with Electron 23.
|
||||
|
||||
### Faster
|
||||
|
||||
This is maybe counter-intuitive, but for some reason, the tokenizer got faster
|
||||
when we migrated to vscode-oniguruma (by about 50% in some cases). We had also
|
||||
lower deviation - the slowest case and the fastest one are now closer in time
|
||||
|
||||
### macOS problems
|
||||
|
||||
This broke Silicon macOS builds because if we use WASM, we need to add [Allow
|
||||
JIT entitlement to the plist
|
||||
file](https://github.com/pulsar-edit/pulsar/pull/454). We solved this, but with
|
||||
this entitlement, Intel macOS builds also got slower ([Electron issue](https://github.com/electron/electron/issues/26143))
|
||||
|
||||
### Memory leaks
|
||||
|
||||
We found out that in some situations, we could have memory leaks - basically,
|
||||
WASM doesn't have garbage collection like Node, so every time we created new
|
||||
`OnigScanner` objects (discarding the old ones) that caused a new memory
|
||||
allocation without any deallocation of the old memory.
|
||||
|
||||
To mitigate this, we basically implemented a cache of `OnigScanner`s - when
|
||||
someone instantiates this class, it'll first see if a previous version was
|
||||
instantiated, and if so, it'll reuse the same object. This doesn't _actually
|
||||
solve_ the memory leak, but makes it controllable - TextMate grammars do not use
|
||||
too many regular expressions, so everytime one opens Pulsar, even if the user
|
||||
have multiple TextMate grammars only it'll only leak a couple kilobytes of
|
||||
memory, so it's considered a non-issue for now.
|
||||
|
||||
## Usage of VSCode TextMate
|
||||
|
||||
VSCode TextMate is a library that basically highlights all code in VSCode. One
|
||||
idea was to migrate away completely from `first-mate` and use this library as
|
||||
the tokenizer. Unfortunately, `first-mate` is actually _more correct_ than
|
||||
VSCode's implementation - for example, Pulsar is able to higlight XML namespaces
|
||||
on attributes, like for example `<e foo:bar="attr">` (the `foo` gets
|
||||
highlighted)
|
||||
|
||||
## Second-Mate, and VSCode-Oniguruma patches
|
||||
|
||||
VSCode-Oniguruma works a little bit differently than Atom's version. On VSCode's
|
||||
version, when it doesn't find a match (length=0 on the result) VSCode's version
|
||||
return a meaningless value on `start` and `end`, whereas Atom's return the
|
||||
latest `end` from the previous match. We had to normalize this on Second-Mate.
|
||||
|
||||
Also, Atom's version of Oniguruma had an object called `OnigRegExp` that the new
|
||||
library doesn't have. The only usage of that object was a method called `test` -
|
||||
fortunatelly, this translates 100% do using `OnigScanner#findNextMatchSync`, so
|
||||
we moved all usages to this new API - meaning, `second-mate` is 90% compatible
|
||||
with `first-mate`, with the exception of the `firstLineRegex` and
|
||||
`contentsRegex`, that now return an object of `OnigScanner` instead of
|
||||
`OnigRegExp`.
|
81
docs/decisions/005-remove-auto-update-functionality.md
Normal file
81
docs/decisions/005-remove-auto-update-functionality.md
Normal file
@ -0,0 +1,81 @@
|
||||
---
|
||||
status: accepted
|
||||
date: 2023-08-10
|
||||
deciders: '@confused-Techie, @Daeraxa, @savetheclocktower, @mauricioszabo'
|
||||
---
|
||||
# Remove original AutoUpdate Functionality
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
To provide users with some form of autoupdate functionality, many thought we could
|
||||
adopt the original method used by the Atom team, and de-facto method within Electron
|
||||
applications, Squirrel. While Squirrel is great for AutoUpdating, it has some big
|
||||
issues, mainly it requires that every platform has signed binaries, which can be
|
||||
prohibitively expensive. Additionally, it only supports Windows and macOS.
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
* Users need a way to fulfill AutoUpdate functionality
|
||||
|
||||
## Considered Options
|
||||
|
||||
* Sign Windows and macOS (Already do) binaries, and setup Squirrel.
|
||||
* Remove AutoUpdate totally.
|
||||
* Use core package to assist in update installation.
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
Chosen option: "Use core package to assist in update installation", to allow similar
|
||||
behavior, we opted to create a core package that could help alert users of updates
|
||||
to Pulsar being available, while technically not actually preforming any installation.
|
||||
This new core package was added to Pulsar in [`pulsar-edit/pulsar#656`](https://github.com/pulsar-edit/pulsar/pull/656).
|
||||
This package alerts and assists in users installing new updates to Pulsar, while no
|
||||
longer relying on any form of AutoUpdate functionality from Squirrel.
|
||||
|
||||
This means, that we can now remove all Squirrel and AutoUpdate functionality that's
|
||||
built right into Pulsar, hopefully cutting down on startup time.
|
||||
|
||||
<!-- This is an optional element. Feel free to remove. -->
|
||||
### Consequences
|
||||
|
||||
* Good, because this allows a semblence of AutoUpdate functionality without ever having to hit our own backend.
|
||||
* Good, because it allows users to be more in control, and automatically notified of new versions.
|
||||
* Good, because it sections off the logic to a package that can be disabled or replaced as needed.
|
||||
* Bad, because it does not actually preform any autoupdates.
|
||||
|
||||
## Pros and Cons of the Options
|
||||
|
||||
### Sign Windows and macOS binaries, and setup Squirrel
|
||||
|
||||
This would return things to the status quo, of Atom AutoUpdate functionality.
|
||||
|
||||
* Good, because users would know what to expect.
|
||||
* Good, because it would provide AutoUpdates to Windows and macOS users.
|
||||
* Bad, because it would be prohibitively expensive, and would fail to work if we ever
|
||||
didn't have the funds for expensive binary signing costs.
|
||||
* Bad, because this would leave Linux users with still zero support for AutoUpdates.
|
||||
* Bad, because it would add additional complexity into our CI for signing Windows binaries.
|
||||
|
||||
### Remove AutoUpdates totally
|
||||
|
||||
This is essentially, what we have been doing since we took over Atom. Provided zero methodology
|
||||
for users to preform autoupdates within Pulsar.
|
||||
|
||||
* Good, because it requires zero effort on our end.
|
||||
* Good, because there are no signing costs.
|
||||
* Bad, because it provides users no method to easily update.
|
||||
* Bad, because users would be misinformed about Atom's ability to autoupdate being lost.
|
||||
* Bad, because autoupdates of some kind is an expected and standard feature in any modern application.
|
||||
|
||||
## More Information
|
||||
|
||||
This decision is not one taken lightly, and could still stir some controversy on best implementations.
|
||||
|
||||
There was also additional concerns about not deleting the code used for Squirrel's AutoUpdate functionality
|
||||
in case we ever did want to return to that behavior, since it already works perfectly, if properly setup.
|
||||
|
||||
For that reason, instead of keeping the code within the repo, below will be details about where the code that comprises
|
||||
of the Squirrel AutoUpdate logic will be kept within Git, so that it can always be retrieved if ever needed.
|
||||
|
||||
* Last Commit Before Removal: `bf60fbe6fc267b737a70d5d39c03cad1629ea128`
|
||||
* PR Where it was Removed: [`pulsar-edit/pulsar#668`](https://github.com/pulsar-edit/pulsar/pull/668)
|
169
i18n/en.json
169
i18n/en.json
@ -1,169 +0,0 @@
|
||||
{
|
||||
"menu": {
|
||||
"pulsar": {
|
||||
"about": "About Pulsar",
|
||||
"view-license": "View License",
|
||||
"version": "Version VERSION",
|
||||
"restart-and-install-update": "Restart and Install Update",
|
||||
"check-for-update": "Check for Update",
|
||||
"checking-for-update": "Checking for Update",
|
||||
"downloading-update": "Downloading Update",
|
||||
"preferences": "Preferences",
|
||||
"config": "Config",
|
||||
"init-script": "Init Script",
|
||||
"keymap": "Keymap",
|
||||
"snippets": "Snippets",
|
||||
"stylesheet": "Stylesheet",
|
||||
"install-shell-commands": "Install Shell Commands",
|
||||
"quit": "Quit Pulsar"
|
||||
},
|
||||
"macos": {
|
||||
"services": "Services",
|
||||
"hide-self": "Hide Pulsar",
|
||||
"hide-others": "Hide Others",
|
||||
"show-all": "Show All"
|
||||
},
|
||||
"file": {
|
||||
"self": "File",
|
||||
"new-window": "New Window",
|
||||
"new-file": "New File",
|
||||
"open": "Open...",
|
||||
"open-file": "Open File...",
|
||||
"open-folder": "Open Folder...",
|
||||
"add-project-folder": "Add Project Folder",
|
||||
"project-history": {
|
||||
"reopen-project": "Reopen Project",
|
||||
"clear": "Clear Project History"
|
||||
},
|
||||
"reopen-last-item": "Reopen Last Item",
|
||||
"save": "Save",
|
||||
"save-as": "Save As...",
|
||||
"save-all": "Save All",
|
||||
"close-tab": "Close Tab",
|
||||
"close-pane": "Close Pane",
|
||||
"close-window": "Close Window"
|
||||
},
|
||||
"edit": {
|
||||
"self": "Edit",
|
||||
"undo": "Undo",
|
||||
"redo": "Redo",
|
||||
"cut": "Cut",
|
||||
"copy": "Copy",
|
||||
"copy-path": "Copy Path",
|
||||
"paste": "Paste",
|
||||
"paste-without-reformatting": "Paste Without Reformatting",
|
||||
"select-all": "Select All",
|
||||
"toggle-comments": "Toggle Comments",
|
||||
"lines": {
|
||||
"self": "Lines",
|
||||
"indent": "Indent",
|
||||
"outdent": "Outdent",
|
||||
"auto-indent": "Auto Indent",
|
||||
"move-up": "Move Line Up",
|
||||
"move-down": "Move Line Down",
|
||||
"duplicate": "Duplicate Lines",
|
||||
"delete": "Delete Line",
|
||||
"join": "Join Lines"
|
||||
},
|
||||
"columns": {
|
||||
"self": "Columns",
|
||||
"move-selection-left": "Move Selection Left",
|
||||
"move-selection-right": "Move Selection Right"
|
||||
},
|
||||
"text": {
|
||||
"self": "Text",
|
||||
"upper-case": "Upper Case",
|
||||
"lower-case": "Lower Case",
|
||||
"delete-to-end-of-word": "Delete to End of Word",
|
||||
"delete-to-previous-word-boundary": "Delete to Previous Word Boundary",
|
||||
"delete-to-next-word-boundary": "Delete to Next Word Boundary",
|
||||
"delete-line": "Delete Line",
|
||||
"transpose": "Transpose"
|
||||
},
|
||||
"folding": {
|
||||
"self": "Folding",
|
||||
"fold": "Fold",
|
||||
"unfold": "Unfold",
|
||||
"fold-all": "Fold All",
|
||||
"unfold-all": "Unfold All",
|
||||
"fold-level-1": "Fold Level 1",
|
||||
"fold-level-2": "Fold Level 2",
|
||||
"fold-level-3": "Fold Level 3",
|
||||
"fold-level-4": "Fold Level 4",
|
||||
"fold-level-5": "Fold Level 5",
|
||||
"fold-level-6": "Fold Level 6",
|
||||
"fold-level-7": "Fold Level 7",
|
||||
"fold-level-8": "Fold Level 8",
|
||||
"fold-level-9": "Fold Level 9"
|
||||
}
|
||||
},
|
||||
"view": {
|
||||
"self": "View",
|
||||
"toggle-full-screen": "Toggle Full Screen",
|
||||
"toggle-menu-bar": "Toggle Menu Bar",
|
||||
"panes": {
|
||||
"self": "Panes",
|
||||
"split-up": "Split Up",
|
||||
"split-down": "Split Down",
|
||||
"split-left": "Split Left",
|
||||
"split-right": "Split Right",
|
||||
"focus-next": "Focus Next Pane",
|
||||
"focus-previous": "Focus Previous Pane",
|
||||
"focus-above": "Focus Pane Above",
|
||||
"focus-below": "Focus Pane Below",
|
||||
"focus-on-left": "Focus Pane On Left",
|
||||
"focus-on-right": "Focus Pane On Right",
|
||||
"close": "Close Pane"
|
||||
},
|
||||
"developer": {
|
||||
"self": "Developer",
|
||||
"open-in-dev-mode": "Open In Dev Mode",
|
||||
"reload-window": "Reload Window",
|
||||
"run-package-specs": "Run Package Specs",
|
||||
"toggle-dev-tools": "Toggle Developer Tools"
|
||||
},
|
||||
"increase-font-size": "Increase Font Size",
|
||||
"decrease-font-size": "Decrease Font Size",
|
||||
"reset-font-size": "Reset Font Size",
|
||||
"toggle-soft-wrap": "Toggle Soft Wrap"
|
||||
},
|
||||
"selection": {
|
||||
"self": "Selection",
|
||||
"add-above": "Add Selection Above",
|
||||
"add-below": "Add Selection Below",
|
||||
"single": "Single Selection",
|
||||
"split-into-lines": "Split into Lines",
|
||||
"to-top": "Select to Top",
|
||||
"to-bottom": "Select to Bottom",
|
||||
"line": "Select Line",
|
||||
"word": "Select Word",
|
||||
"to-beginning-of-word": "Select to Beginning of Word",
|
||||
"to-beginning-of-line": "Select to Beginning of Line",
|
||||
"to-first-char-of-line": "Select to First Character of Line",
|
||||
"to-end-of-word": "Select to End of Word",
|
||||
"to-end-of-line": "Select to End of Line"
|
||||
},
|
||||
"find": {
|
||||
"self": "Find"
|
||||
},
|
||||
"packages": {
|
||||
"self": "Packages",
|
||||
"open-package-manager": "Open Package Manager"
|
||||
},
|
||||
"window": {
|
||||
"self": "Window",
|
||||
"minimise": "Minimise",
|
||||
"zoom": "Zoom",
|
||||
"bring-all-to-front": "Bring All to Front"
|
||||
},
|
||||
"help": {
|
||||
"self": "Help",
|
||||
"terms-of-use": "Terms of Use",
|
||||
"docs": "Documentation",
|
||||
"faq": "Frequently Asked Questions",
|
||||
"community-discussions": "Community Discussions",
|
||||
"report-issue": "Report Issue",
|
||||
"search-issues": "Search Issues"
|
||||
}
|
||||
}
|
||||
}
|
@ -14,6 +14,11 @@ async function openAtom(profilePath, videoName) {
|
||||
env: env,
|
||||
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)
|
||||
|
@ -2,227 +2,227 @@
|
||||
{
|
||||
label: 'Pulsar'
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.pulsar.about', command: 'application:about' }
|
||||
{ localisedLabel: 'core.menu.pulsar.view-license', command: 'application:open-license' }
|
||||
{ localisedLabel: 'core.menu.pulsar.version', enabled: false }
|
||||
{ localisedLabel: 'core.menu.pulsar.restart-and-install-update', command: 'application:install-update', visible: false }
|
||||
{ localisedLabel: 'core.menu.pulsar.check-for-update', command: 'application:check-for-update', visible: false }
|
||||
{ localisedLabel: 'core.menu.pulsar.checking-for-update', enabled: false, visible: false }
|
||||
{ localisedLabel: 'core.menu.pulsar.downloading-update', enabled: false, visible: false }
|
||||
{ label: 'About Pulsar', command: 'application:about' }
|
||||
{ label: 'View License', command: 'application:open-license' }
|
||||
{ label: 'VERSION', enabled: false }
|
||||
{ label: 'Restart and Install Update', command: 'application:install-update', visible: false}
|
||||
{ label: 'Check for Update', command: 'application:check-for-update', visible: false}
|
||||
{ label: 'Checking for Update', enabled: false, visible: false}
|
||||
{ label: 'Downloading Update', enabled: false, visible: false}
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.pulsar.preferences', command: 'application:show-settings' }
|
||||
{ label: 'Preferences…', command: 'application:show-settings' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.pulsar.config', command: 'application:open-your-config' }
|
||||
{ localisedLabel: 'core.menu.pulsar.init-script', command: 'application:open-your-init-script' }
|
||||
{ localisedLabel: 'core.menu.pulsar.keymap', command: 'application:open-your-keymap' }
|
||||
{ localisedLabel: 'core.menu.pulsar.snippets', command: 'application:open-your-snippets' }
|
||||
{ localisedLabel: 'core.menu.pulsar.stylesheet', command: 'application:open-your-stylesheet' }
|
||||
{ label: 'Config…', command: 'application:open-your-config' }
|
||||
{ label: 'Init Script…', command: 'application:open-your-init-script' }
|
||||
{ label: 'Keymap…', command: 'application:open-your-keymap' }
|
||||
{ label: 'Snippets…', command: 'application:open-your-snippets' }
|
||||
{ label: 'Stylesheet…', command: 'application:open-your-stylesheet' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.pulsar.install-shell-commands', command: 'window:install-shell-commands' }
|
||||
{ label: 'Install Shell Commands', command: 'window:install-shell-commands' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.macos.services', role: 'services', submenu: [] }
|
||||
{ label: 'Services', role: 'services', submenu: [] }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.macos.hide-self', command: 'application:hide' }
|
||||
{ localisedLabel: 'core.menu.macos.hide-others', command: 'application:hide-other-applications' }
|
||||
{ localisedLabel: 'core.menu.macos.show-all', command: 'application:unhide-all-applications' }
|
||||
{ label: 'Hide Pulsar', command: 'application:hide' }
|
||||
{ label: 'Hide Others', command: 'application:hide-other-applications' }
|
||||
{ label: 'Show All', command: 'application:unhide-all-applications' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.pulsar.quit', command: 'application:quit' }
|
||||
{ label: 'Quit Pulsar', command: 'application:quit' }
|
||||
]
|
||||
}
|
||||
{
|
||||
localisedLabel: 'core.menu.file.self'
|
||||
label: 'File'
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.file.new-window', command: 'application:new-window' }
|
||||
{ localisedLabel: 'core.menu.file.new-file', command: 'application:new-file' }
|
||||
{ localisedLabel: 'core.menu.file.open', command: 'application:open' }
|
||||
{ localisedLabel: 'core.menu.file.add-project-folder', command: 'application:add-project-folder' }
|
||||
{ label: 'New Window', command: 'application:new-window' }
|
||||
{ label: 'New File', command: 'application:new-file' }
|
||||
{ label: 'Open…', command: 'application:open' }
|
||||
{ label: 'Add Project Folder…', command: 'application:add-project-folder' }
|
||||
{
|
||||
localisedLabel: 'core.menu.file.project-history.reopen-project'
|
||||
label: 'Reopen Project',
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.file.project-history.clear', command: 'application:clear-project-history' }
|
||||
{ label: 'Clear Project History', command: 'application:clear-project-history' }
|
||||
{ type: 'separator' }
|
||||
]
|
||||
}
|
||||
{ localisedLabel: 'core.menu.file.reopen-last-item', command: 'pane:reopen-closed-item' }
|
||||
{ label: 'Reopen Last Item', command: 'pane:reopen-closed-item' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.file.save', command: 'core:save' }
|
||||
{ localisedLabel: 'core.menu.file.save-as', command: 'core:save-as' }
|
||||
{ localisedLabel: 'core.menu.file.save-all', command: 'window:save-all' }
|
||||
{ label: 'Save', command: 'core:save' }
|
||||
{ label: 'Save As…', command: 'core:save-as' }
|
||||
{ label: 'Save All', command: 'window:save-all' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.file.close-tab', command: 'core:close' }
|
||||
{ localisedLabel: 'core.menu.file.close-pane', command: 'pane:close' }
|
||||
{ localisedLabel: 'core.menu.file.close-window', command: 'window:close' }
|
||||
{ label: 'Close Tab', command: 'core:close' }
|
||||
{ label: 'Close Pane', command: 'pane:close' }
|
||||
{ label: 'Close Window', command: 'window:close' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
localisedLabel: 'core.menu.edit.self'
|
||||
label: 'Edit'
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.edit.undo', command: 'core:undo' }
|
||||
{ localisedLabel: 'core.menu.edit.redo', command: 'core:redo' }
|
||||
{ label: 'Undo', command: 'core:undo' }
|
||||
{ label: 'Redo', command: 'core:redo' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.edit.cut', command: 'core:cut' }
|
||||
{ localisedLabel: 'core.menu.edit.copy', command: 'core:copy' }
|
||||
{ localisedLabel: 'core.menu.edit.copy-path', command: 'editor:copy-path' }
|
||||
{ localisedLabel: 'core.menu.edit.paste', command: 'core:paste' }
|
||||
{ localisedLabel: 'core.menu.edit.paste-without-reformatting', command: 'editor:paste-without-reformatting' }
|
||||
{ localisedLabel: 'core.menu.edit.select-all', command: 'core:select-all' }
|
||||
{ label: 'Cut', command: 'core:cut' }
|
||||
{ label: 'Copy', command: 'core:copy' }
|
||||
{ label: 'Copy Path', command: 'editor:copy-path' }
|
||||
{ label: 'Paste', command: 'core:paste' }
|
||||
{ label: 'Paste Without Reformatting', command: 'editor:paste-without-reformatting' }
|
||||
{ label: 'Select All', command: 'core:select-all' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.edit.toggle-comments', command: 'editor:toggle-line-comments' }
|
||||
{ label: 'Toggle Comments', command: 'editor:toggle-line-comments' }
|
||||
{
|
||||
localisedLabel: 'core.menu.edit.lines.self',
|
||||
label: 'Lines',
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.edit.lines.indent', command: 'editor:indent-selected-rows' }
|
||||
{ localisedLabel: 'core.menu.edit.lines.outdent', command: 'editor:outdent-selected-rows' }
|
||||
{ localisedLabel: 'core.menu.edit.lines.auto-indent', command: 'editor:auto-indent' }
|
||||
{ label: 'Indent', command: 'editor:indent-selected-rows' }
|
||||
{ label: 'Outdent', command: 'editor:outdent-selected-rows' }
|
||||
{ label: 'Auto Indent', command: 'editor:auto-indent' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.edit.lines.move-up', command: 'editor:move-line-up' }
|
||||
{ localisedLabel: 'core.menu.edit.lines.move-down', command: 'editor:move-line-down' }
|
||||
{ localisedLabel: 'core.menu.edit.lines.duplicate', command: 'editor:duplicate-lines' }
|
||||
{ localisedLabel: 'core.menu.edit.lines.delete', command: 'editor:delete-line' }
|
||||
{ localisedLabel: 'core.menu.edit.lines.join', command: 'editor:join-lines' }
|
||||
{ label: 'Move Line Up', command: 'editor:move-line-up' }
|
||||
{ label: 'Move Line Down', command: 'editor:move-line-down' }
|
||||
{ label: 'Duplicate Lines', command: 'editor:duplicate-lines' }
|
||||
{ label: 'Delete Line', command: 'editor:delete-line' }
|
||||
{ label: 'Join Lines', command: 'editor:join-lines' }
|
||||
]
|
||||
}
|
||||
{
|
||||
localisedLabel: 'core.menu.edit.columns.self'
|
||||
label: 'Columns',
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.edit.columns.move-selection-left', command: 'editor:move-selection-left' }
|
||||
{ localisedLabel: 'core.menu.edit.columns.move-selection-right', command: 'editor:move-selection-right' }
|
||||
{ label: 'Move Selection Left', command: 'editor:move-selection-left' }
|
||||
{ label: 'Move Selection Right', command: 'editor:move-selection-right' }
|
||||
]
|
||||
}
|
||||
{
|
||||
localisedLabel: 'core.menu.edit.text.self'
|
||||
label: 'Text',
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.edit.text.upper-case', command: 'editor:upper-case' }
|
||||
{ localisedLabel: 'core.menu.edit.text.lower-case', command: 'editor:lower-case' }
|
||||
{ label: 'Upper Case', command: 'editor:upper-case' }
|
||||
{ label: 'Lower Case', command: 'editor:lower-case' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.edit.text.delete-to-end-of-word', command: 'editor:delete-to-end-of-word' }
|
||||
{ localisedLabel: 'core.menu.edit.text.delete-to-previous-word-boundary', command: 'editor:delete-to-previous-word-boundary' }
|
||||
{ localisedLabel: 'core.menu.edit.text.delete-to-next-word-boundary', command: 'editor:delete-to-next-word-boundary' }
|
||||
{ localisedLabel: 'core.menu.edit.text.delete-line', command: 'editor:delete-line' }
|
||||
{ label: 'Delete to End of Word', command: 'editor:delete-to-end-of-word' }
|
||||
{ label: 'Delete to Previous Word Boundary', command: 'editor:delete-to-previous-word-boundary' }
|
||||
{ label: 'Delete to Next Word Boundary', command: 'editor:delete-to-next-word-boundary' }
|
||||
{ label: 'Delete Line', command: 'editor:delete-line' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.edit.text.transpose', command: 'editor:transpose' }
|
||||
{ label: 'Transpose', command: 'editor:transpose' }
|
||||
]
|
||||
}
|
||||
{
|
||||
localisedLabel: 'core.menu.edit.folding.self'
|
||||
label: 'Folding',
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.edit.folding.fold', command: 'editor:fold-current-row' }
|
||||
{ localisedLabel: 'core.menu.edit.folding.unfold', command: 'editor:unfold-current-row' }
|
||||
{ localisedLabel: 'core.menu.edit.folding.fold-all', command: 'editor:fold-all' }
|
||||
{ localisedLabel: 'core.menu.edit.folding.unfold-all', command: 'editor:unfold-all' }
|
||||
{ label: 'Fold', command: 'editor:fold-current-row' }
|
||||
{ label: 'Unfold', command: 'editor:unfold-current-row' }
|
||||
{ label: 'Fold All', command: 'editor:fold-all' }
|
||||
{ label: 'Unfold All', command: 'editor:unfold-all' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.edit.folding.fold-level-1', command: 'editor:fold-at-indent-level-1' }
|
||||
{ localisedLabel: 'core.menu.edit.folding.fold-level-2', command: 'editor:fold-at-indent-level-2' }
|
||||
{ localisedLabel: 'core.menu.edit.folding.fold-level-3', command: 'editor:fold-at-indent-level-3' }
|
||||
{ localisedLabel: 'core.menu.edit.folding.fold-level-4', command: 'editor:fold-at-indent-level-4' }
|
||||
{ localisedLabel: 'core.menu.edit.folding.fold-level-5', command: 'editor:fold-at-indent-level-5' }
|
||||
{ localisedLabel: 'core.menu.edit.folding.fold-level-6', command: 'editor:fold-at-indent-level-6' }
|
||||
{ localisedLabel: 'core.menu.edit.folding.fold-level-7', command: 'editor:fold-at-indent-level-7' }
|
||||
{ localisedLabel: 'core.menu.edit.folding.fold-level-8', command: 'editor:fold-at-indent-level-8' }
|
||||
{ localisedLabel: 'core.menu.edit.folding.fold-level-9', command: 'editor:fold-at-indent-level-9' }
|
||||
{ label: 'Fold Level 1', command: 'editor:fold-at-indent-level-1' }
|
||||
{ label: 'Fold Level 2', command: 'editor:fold-at-indent-level-2' }
|
||||
{ label: 'Fold Level 3', command: 'editor:fold-at-indent-level-3' }
|
||||
{ label: 'Fold Level 4', command: 'editor:fold-at-indent-level-4' }
|
||||
{ label: 'Fold Level 5', command: 'editor:fold-at-indent-level-5' }
|
||||
{ label: 'Fold Level 6', command: 'editor:fold-at-indent-level-6' }
|
||||
{ label: 'Fold Level 7', command: 'editor:fold-at-indent-level-7' }
|
||||
{ label: 'Fold Level 8', command: 'editor:fold-at-indent-level-8' }
|
||||
{ label: 'Fold Level 9', command: 'editor:fold-at-indent-level-9' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
localisedLabel: 'core.menu.view.self'
|
||||
label: 'View'
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.view.toggle-full-screen', command: 'window:toggle-full-screen' }
|
||||
{ label: 'Toggle Full Screen', command: 'window:toggle-full-screen' }
|
||||
{
|
||||
localisedLabel: 'core.menu.view.panes.self'
|
||||
label: 'Panes'
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.view.panes.split-up', command: 'pane:split-up-and-copy-active-item' }
|
||||
{ localisedLabel: 'core.menu.view.panes.split-down', command: 'pane:split-down-and-copy-active-item' }
|
||||
{ localisedLabel: 'core.menu.view.panes.split-left', command: 'pane:split-left-and-copy-active-item' }
|
||||
{ localisedLabel: 'core.menu.view.panes.split-right', command: 'pane:split-right-and-copy-active-item' }
|
||||
{ label: 'Split Up', command: 'pane:split-up-and-copy-active-item' }
|
||||
{ label: 'Split Down', command: 'pane:split-down-and-copy-active-item' }
|
||||
{ label: 'Split Left', command: 'pane:split-left-and-copy-active-item' }
|
||||
{ label: 'Split Right', command: 'pane:split-right-and-copy-active-item' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.view.panes.focus-next', command: 'window:focus-next-pane' }
|
||||
{ localisedLabel: 'core.menu.view.panes.focus-previous', command: 'window:focus-previous-pane' }
|
||||
{ label: 'Focus Next Pane', command: 'window:focus-next-pane' }
|
||||
{ label: 'Focus Previous Pane', command: 'window:focus-previous-pane' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.view.panes.focus-above', command: 'window:focus-pane-above' }
|
||||
{ localisedLabel: 'core.menu.view.panes.focus-below', command: 'window:focus-pane-below' }
|
||||
{ localisedLabel: 'core.menu.view.panes.focus-on-left', command: 'window:focus-pane-on-left' }
|
||||
{ localisedLabel: 'core.menu.view.panes.focus-on-right', command: 'window:focus-pane-on-right' }
|
||||
{ label: 'Focus Pane Above', command: 'window:focus-pane-above' }
|
||||
{ label: 'Focus Pane Below', command: 'window:focus-pane-below' }
|
||||
{ label: 'Focus Pane On Left', command: 'window:focus-pane-on-left' }
|
||||
{ label: 'Focus Pane On Right', command: 'window:focus-pane-on-right' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.view.panes.close', command: 'pane:close' }
|
||||
{ label: 'Close Pane', command: 'pane:close' }
|
||||
]
|
||||
}
|
||||
{
|
||||
localisedLabel: 'core.menu.view.developer.self'
|
||||
label: 'Developer'
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.view.developer.open-in-dev-mode', command: 'application:open-dev' }
|
||||
{ localisedLabel: 'core.menu.view.developer.reload-window', command: 'window:reload' }
|
||||
{ localisedLabel: 'core.menu.view.developer.run-package-specs', command: 'window:run-package-specs' }
|
||||
{ localisedLabel: 'core.menu.view.developer.toggle-dev-tools', command: 'window:toggle-dev-tools' }
|
||||
{ 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: 'Toggle Developer Tools', command: 'window:toggle-dev-tools' }
|
||||
]
|
||||
}
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.view.increase-font-size', command: 'window:increase-font-size' }
|
||||
{ localisedLabel: 'core.menu.view.decrease-font-size', command: 'window:decrease-font-size' }
|
||||
{ localisedLabel: 'core.menu.view.reset-font-size', command: 'window:reset-font-size' }
|
||||
{ label: 'Increase Font Size', command: 'window:increase-font-size' }
|
||||
{ label: 'Decrease Font Size', command: 'window:decrease-font-size' }
|
||||
{ label: 'Reset Font Size', command: 'window:reset-font-size' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.view.toggle-soft-wrap', command: 'editor:toggle-soft-wrap' }
|
||||
{ label: 'Toggle Soft Wrap', command: 'editor:toggle-soft-wrap' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
localisedLabel: 'core.menu.selection.self'
|
||||
label: 'Selection'
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.selection.add-above', command: 'editor:add-selection-above' }
|
||||
{ localisedLabel: 'core.menu.selection.add-below', command: 'editor:add-selection-below' }
|
||||
{ localisedLabel: 'core.menu.selection.single', command: 'editor:consolidate-selections' }
|
||||
{ localisedLabel: 'core.menu.selection.split-into-lines', command: 'editor:split-selections-into-lines' }
|
||||
{ label: 'Add Selection Above', command: 'editor:add-selection-above' }
|
||||
{ label: 'Add Selection Below', command: 'editor:add-selection-below' }
|
||||
{ label: 'Single Selection', command: 'editor:consolidate-selections'}
|
||||
{ label: 'Split into Lines', command: 'editor:split-selections-into-lines'}
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.selection.to-top', command: 'core:select-to-top' }
|
||||
{ localisedLabel: 'core.menu.selection.to-bottom', command: 'core:select-to-bottom' }
|
||||
{ label: 'Select to Top', command: 'core:select-to-top' }
|
||||
{ label: 'Select to Bottom', command: 'core:select-to-bottom' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.selection.line', command: 'editor:select-line' }
|
||||
{ localisedLabel: 'core.menu.selection.word', command: 'editor:select-word' }
|
||||
{ localisedLabel: 'core.menu.selection.to-beginning-of-word', command: 'editor:select-to-beginning-of-word' }
|
||||
{ localisedLabel: 'core.menu.selection.to-beginning-of-line', command: 'editor:select-to-beginning-of-line' }
|
||||
{ localisedLabel: 'core.menu.selection.to-first-char-of-line', command: 'editor:select-to-first-character-of-line' }
|
||||
{ localisedLabel: 'core.menu.selection.to-end-of-word', command: 'editor:select-to-end-of-word' }
|
||||
{ localisedLabel: 'core.menu.selection.to-end-of-line', command: 'editor:select-to-end-of-line' }
|
||||
{ label: 'Select Line', command: 'editor:select-line' }
|
||||
{ label: 'Select Word', command: 'editor:select-word' }
|
||||
{ label: 'Select to Beginning of Word', command: 'editor:select-to-beginning-of-word' }
|
||||
{ label: 'Select to Beginning of Line', command: 'editor:select-to-beginning-of-line' }
|
||||
{ label: 'Select to First Character of Line', command: 'editor:select-to-first-character-of-line' }
|
||||
{ label: 'Select to End of Word', command: 'editor:select-to-end-of-word' }
|
||||
{ label: 'Select to End of Line', command: 'editor:select-to-end-of-line' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
localisedLabel: 'core.menu.find.self'
|
||||
label: 'Find'
|
||||
submenu: []
|
||||
}
|
||||
|
||||
{
|
||||
localisedLabel: 'core.menu.packages.self'
|
||||
label: 'Packages'
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.packages.open-package-manager', command: 'settings-view:view-installed-packages' }
|
||||
{ label: 'Open Package Manager', command: 'settings-view:view-installed-packages' }
|
||||
{ type: 'separator' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
localisedLabel: 'core.menu.window.self'
|
||||
label: 'Window'
|
||||
role: 'window'
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.window.minimise', command: 'application:minimize' }
|
||||
{ localisedLabel: 'core.menu.window.zoom', command: 'application:zoom' }
|
||||
{ label: 'Minimize', command: 'application:minimize' }
|
||||
{ label: 'Zoom', command: 'application:zoom' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.window.bring-all-to-front', command: 'application:bring-all-windows-to-front' }
|
||||
{ label: 'Bring All to Front', command: 'application:bring-all-windows-to-front' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
localisedLabel: 'core.menu.help.self'
|
||||
label: 'Help'
|
||||
role: 'help'
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.help.terms-of-use', command: 'application:open-terms-of-use' }
|
||||
{ localisedLabel: 'core.menu.help.docs', command: 'application:open-documentation' }
|
||||
{ localisedLabel: 'core.menu.help.faq', command: 'application:open-faq' }
|
||||
{ label: 'Terms of Use', command: 'application:open-terms-of-use' }
|
||||
{ label: 'Documentation', command: 'application:open-documentation' }
|
||||
{ label: 'Frequently Asked Questions', command: 'application:open-faq' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.help.community-discussions', command: 'application:open-discussions' }
|
||||
{ localisedLabel: 'core.menu.help.report-issue', command: 'application:report-issue' }
|
||||
{ localisedLabel: 'core.menu.help.search-issues', command: 'application:search-issues' }
|
||||
{ label: 'Community Discussions', command: 'application:open-discussions' }
|
||||
{ label: 'Report Issue', command: 'application:report-issue' }
|
||||
{ label: 'Search Issues', command: 'application:search-issues' }
|
||||
{ type: 'separator' }
|
||||
]
|
||||
}
|
||||
|
234
menus/linux.cson
234
menus/linux.cson
@ -1,204 +1,204 @@
|
||||
'menu': [
|
||||
{
|
||||
localisedLabel: 'core.menu.file.self' # accelerator F
|
||||
label: '&File'
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.file.new-window', command: 'application:new-window' } # accelerator W
|
||||
{ localisedLabel: 'core.menu.file.new-file', command: 'application:new-file' } # accelerator N
|
||||
{ localisedLabel: 'core.menu.file.open-file', command: 'application:open-file' } # accelerator O
|
||||
{ localisedLabel: 'core.menu.file.open-folder', command: 'application:open-folder' }
|
||||
{ localisedLabel: 'core.menu.file.add-project-folder', command: 'application:add-project-folder' }
|
||||
{ label: 'New &Window', command: 'application:new-window' }
|
||||
{ label: '&New File', command: 'application:new-file' }
|
||||
{ label: '&Open File…', command: 'application:open-file' }
|
||||
{ label: 'Open Folder…', command: 'application:open-folder' }
|
||||
{ label: 'Add Project Folder…', command: 'application:add-project-folder' }
|
||||
{
|
||||
localisedLabel: 'core.menu.file.project-history.reopen-project'
|
||||
label: 'Reopen Project',
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.file.project-history.clear', command: 'application:clear-project-history' }
|
||||
{ label: 'Clear Project History', command: 'application:clear-project-history' }
|
||||
{ type: 'separator' }
|
||||
]
|
||||
}
|
||||
{ localisedLabel: 'core.menu.file.reopen-last-item', command: 'pane:reopen-closed-item' } # accelerator I
|
||||
{ label: 'Reopen Last &Item', command: 'pane:reopen-closed-item' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.file.save', command: 'core:save' } # accelerator S
|
||||
{ localisedLabel: 'core.menu.file.save-as', command: 'core:save-as' } # accelerator A
|
||||
{ localisedLabel: 'core.menu.file.save-all', command: 'window:save-all' } # accelerator L
|
||||
{ label: '&Save', command: 'core:save' }
|
||||
{ label: 'Save &As…', command: 'core:save-as' }
|
||||
{ label: 'Save A&ll', command: 'window:save-all' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.file.close-tab', command: 'core:close' } # accelerator C
|
||||
{ localisedLabel: 'core.menu.file.close-pane', command: 'pane:close' } # accelerator P
|
||||
{ localisedLabel: 'core.menu.file.close-window', command: 'window:close' } # accelerator E
|
||||
{ label: '&Close Tab', command: 'core:close' }
|
||||
{ label: 'Close &Pane', command: 'pane:close' }
|
||||
{ label: 'Clos&e Window', command: 'window:close' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.pulsar.quit', command: 'application:quit' }
|
||||
{ label: 'Quit', command: 'application:quit' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
localisedLabel: 'core.menu.edit.self' # accelerator E
|
||||
label: '&Edit'
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.edit.undo', command: 'core:undo' } # accelerator U
|
||||
{ localisedLabel: 'core.menu.edit.redo', command: 'core:redo' } # accelerator R
|
||||
{ label: '&Undo', command: 'core:undo' }
|
||||
{ label: '&Redo', command: 'core:redo' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.edit.cut', command: 'core:cut' } # accelerator C
|
||||
{ localisedLabel: 'core.menu.edit.copy', command: 'core:copy' } # accelerator O
|
||||
{ localisedLabel: 'core.menu.edit.copy-path', command: 'editor:copy-path' } # accelerator H
|
||||
{ localisedLabel: 'core.menu.edit.paste', command: 'core:paste' } # accelerator P
|
||||
{ localisedLabel: 'core.menu.edit.paste-without-reformatting', command: 'editor:paste-without-reformatting' }
|
||||
{ localisedLabel: 'core.menu.edit.select-all', command: 'core:select-all' } # accelerator A
|
||||
{ label: '&Cut', command: 'core:cut' }
|
||||
{ label: 'C&opy', command: 'core:copy' }
|
||||
{ label: 'Copy Pat&h', command: 'editor:copy-path' }
|
||||
{ label: '&Paste', command: 'core:paste' }
|
||||
{ label: 'Paste Without Reformatting', command: 'editor:paste-without-reformatting' }
|
||||
{ label: 'Select &All', command: 'core:select-all' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.edit.toggle-comments', command: 'editor:toggle-line-comments' } # accelerator T
|
||||
{ label: '&Toggle Comments', command: 'editor:toggle-line-comments' }
|
||||
{
|
||||
localisedLabel: 'core.menu.edit.lines.self'
|
||||
label: 'Lines',
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.edit.lines.indent', command: 'editor:indent-selected-rows' } # accelerator I
|
||||
{ localisedLabel: 'core.menu.edit.lines.outdent', command: 'editor:outdent-selected-rows' } # accelerator O
|
||||
{ localisedLabel: 'core.menu.edit.lines.auto-indent', command: 'editor:auto-indent' } # accelerator A
|
||||
{ label: '&Indent', command: 'editor:indent-selected-rows' }
|
||||
{ label: '&Outdent', command: 'editor:outdent-selected-rows' }
|
||||
{ label: '&Auto Indent', command: 'editor:auto-indent' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.edit.lines.move-up', command: 'editor:move-line-up' } # accelerator U
|
||||
{ localisedLabel: 'core.menu.edit.lines.move-down', command: 'editor:move-line-down' } # accelerator D
|
||||
{ localisedLabel: 'core.menu.edit.lines.duplicate', command: 'editor:duplicate-lines' } # accelerator P
|
||||
{ localisedLabel: 'core.menu.edit.lines.delete', command: 'editor:delete-line' } # accelerator E
|
||||
{ localisedLabel: 'core.menu.edit.lines.join', command: 'editor:join-lines' } # accelerator J
|
||||
{ label: 'Move Line &Up', command: 'editor:move-line-up' }
|
||||
{ label: 'Move Line &Down', command: 'editor:move-line-down' }
|
||||
{ label: 'Du&plicate Lines', command: 'editor:duplicate-lines' }
|
||||
{ label: 'D&elete Line', command: 'editor:delete-line' }
|
||||
{ label: '&Join Lines', command: 'editor:join-lines' }
|
||||
]
|
||||
}
|
||||
{
|
||||
localisedLabel: 'core.menu.edit.columns.self'
|
||||
label: 'Columns',
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.edit.columns.move-selection-left', command: 'editor:move-selection-left' } # accelerator L
|
||||
{ localisedLabel: 'core.menu.edit.columns.move-selection-right', command: 'editor:move-selection-right' } # accelerator R
|
||||
{ label: 'Move Selection &Left', command: 'editor:move-selection-left' }
|
||||
{ label: 'Move Selection &Right', command: 'editor:move-selection-right' }
|
||||
]
|
||||
}
|
||||
{
|
||||
localisedLabel: 'core.menu.edit.text.self'
|
||||
label: 'Text',
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.edit.text.upper-case', command: 'editor:upper-case' } # accelerator U
|
||||
{ localisedLabel: 'core.menu.edit.text.lower-case', command: 'editor:lower-case' } # accelerator L
|
||||
{ label: '&Upper Case', command: 'editor:upper-case' }
|
||||
{ label: '&Lower Case', command: 'editor:lower-case' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.edit.text.delete-to-end-of-word', command: 'editor:delete-to-end-of-word' } # accelerator W
|
||||
{ localisedLabel: 'core.menu.edit.text.delete-to-previous-word-boundary', command: 'editor:delete-to-previous-word-boundary' }
|
||||
{ localisedLabel: 'core.menu.edit.text.delete-to-next-word-boundary', command: 'editor:delete-to-next-word-boundary' }
|
||||
{ localisedLabel: 'core.menu.edit.text.delete-line', command: 'editor:delete-line' } # accelerator D
|
||||
{ label: 'Delete to End of &Word', command: 'editor:delete-to-end-of-word' }
|
||||
{ label: 'Delete to Previous Word Boundary', command: 'editor:delete-to-previous-word-boundary' }
|
||||
{ label: 'Delete to Next Word Boundary', command: 'editor:delete-to-next-word-boundary' }
|
||||
{ label: '&Delete Line', command: 'editor:delete-line' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.edit.text.transpose', command: 'editor:transpose' } # accelerator T
|
||||
{ label: '&Transpose', command: 'editor:transpose' }
|
||||
]
|
||||
}
|
||||
{
|
||||
localisedLabel: 'core.menu.edit.folding.self'
|
||||
label: 'Folding',
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.edit.folding.fold', command: 'editor:fold-current-row' } # accelerator F
|
||||
{ localisedLabel: 'core.menu.edit.folding.unfold', command: 'editor:unfold-current-row' } # accelerator U
|
||||
{ localisedLabel: 'core.menu.edit.folding.fold-all', command: 'editor:fold-all' } # accelerator D
|
||||
{ localisedLabel: 'core.menu.edit.folding.unfold-all', command: 'editor:unfold-all' } # accelerator A
|
||||
{ label: '&Fold', command: 'editor:fold-current-row' }
|
||||
{ label: '&Unfold', command: 'editor:unfold-current-row' }
|
||||
{ label: 'Fol&d All', command: 'editor:fold-all' }
|
||||
{ label: 'Unfold &All', command: 'editor:unfold-all' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.edit.folding.fold-level-1', command: 'editor:fold-at-indent-level-1' }
|
||||
{ localisedLabel: 'core.menu.edit.folding.fold-level-2', command: 'editor:fold-at-indent-level-2' }
|
||||
{ localisedLabel: 'core.menu.edit.folding.fold-level-3', command: 'editor:fold-at-indent-level-3' }
|
||||
{ localisedLabel: 'core.menu.edit.folding.fold-level-4', command: 'editor:fold-at-indent-level-4' }
|
||||
{ localisedLabel: 'core.menu.edit.folding.fold-level-5', command: 'editor:fold-at-indent-level-5' }
|
||||
{ localisedLabel: 'core.menu.edit.folding.fold-level-6', command: 'editor:fold-at-indent-level-6' }
|
||||
{ localisedLabel: 'core.menu.edit.folding.fold-level-7', command: 'editor:fold-at-indent-level-7' }
|
||||
{ localisedLabel: 'core.menu.edit.folding.fold-level-8', command: 'editor:fold-at-indent-level-8' }
|
||||
{ localisedLabel: 'core.menu.edit.folding.fold-level-9', command: 'editor:fold-at-indent-level-9' }
|
||||
{ label: 'Fold Level 1', command: 'editor:fold-at-indent-level-1' }
|
||||
{ label: 'Fold Level 2', command: 'editor:fold-at-indent-level-2' }
|
||||
{ label: 'Fold Level 3', command: 'editor:fold-at-indent-level-3' }
|
||||
{ label: 'Fold Level 4', command: 'editor:fold-at-indent-level-4' }
|
||||
{ label: 'Fold Level 5', command: 'editor:fold-at-indent-level-5' }
|
||||
{ label: 'Fold Level 6', command: 'editor:fold-at-indent-level-6' }
|
||||
{ label: 'Fold Level 7', command: 'editor:fold-at-indent-level-7' }
|
||||
{ label: 'Fold Level 8', command: 'editor:fold-at-indent-level-8' }
|
||||
{ label: 'Fold Level 9', command: 'editor:fold-at-indent-level-9' }
|
||||
]
|
||||
}
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.pulsar.preferences', command: 'application:show-settings' } # accelerator P
|
||||
{ label: '&Preferences', command: 'application:show-settings' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.pulsar.config', command: 'application:open-your-config' }
|
||||
{ localisedLabel: 'core.menu.pulsar.init-script', command: 'application:open-your-init-script' }
|
||||
{ localisedLabel: 'core.menu.pulsar.keymap', command: 'application:open-your-keymap' }
|
||||
{ localisedLabel: 'core.menu.pulsar.snippets', command: 'application:open-your-snippets' }
|
||||
{ localisedLabel: 'core.menu.pulsar.stylesheet', command: 'application:open-your-stylesheet' }
|
||||
{ label: 'Config…', command: 'application:open-your-config' }
|
||||
{ label: 'Init Script…', command: 'application:open-your-init-script' }
|
||||
{ label: 'Keymap…', command: 'application:open-your-keymap' }
|
||||
{ label: 'Snippets…', command: 'application:open-your-snippets' }
|
||||
{ label: 'Stylesheet…', command: 'application:open-your-stylesheet' }
|
||||
{ type: 'separator' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
localisedLabel: 'core.menu.view.self' # accelerator V
|
||||
label: '&View'
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.view.toggle-full-screen', command: 'window:toggle-full-screen' } # accelerator F
|
||||
{ localisedLabel: 'core.menu.view.toggle-menu-bar', command: 'window:toggle-menu-bar' }
|
||||
{ label: 'Toggle &Full Screen', command: 'window:toggle-full-screen' }
|
||||
{ label: 'Toggle Menu Bar', command: 'window:toggle-menu-bar' }
|
||||
{
|
||||
localisedLabel: 'core.menu.view.panes.self'
|
||||
label: 'Panes'
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.view.panes.split-up', command: 'pane:split-up-and-copy-active-item' }
|
||||
{ localisedLabel: 'core.menu.view.panes.split-down', command: 'pane:split-down-and-copy-active-item' }
|
||||
{ localisedLabel: 'core.menu.view.panes.split-left', command: 'pane:split-left-and-copy-active-item' }
|
||||
{ localisedLabel: 'core.menu.view.panes.split-right', command: 'pane:split-right-and-copy-active-item' }
|
||||
{ label: 'Split Up', command: 'pane:split-up-and-copy-active-item' }
|
||||
{ label: 'Split Down', command: 'pane:split-down-and-copy-active-item' }
|
||||
{ label: 'Split Left', command: 'pane:split-left-and-copy-active-item' }
|
||||
{ label: 'Split Right', command: 'pane:split-right-and-copy-active-item' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.view.panes.focus-next', command: 'window:focus-next-pane' }
|
||||
{ localisedLabel: 'core.menu.view.panes.focus-previous', command: 'window:focus-previous-pane' }
|
||||
{ label: 'Focus Next Pane', command: 'window:focus-next-pane' }
|
||||
{ label: 'Focus Previous Pane', command: 'window:focus-previous-pane' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.view.panes.focus-above', command: 'window:focus-pane-above' }
|
||||
{ localisedLabel: 'core.menu.view.panes.focus-below', command: 'window:focus-pane-below' }
|
||||
{ localisedLabel: 'core.menu.view.panes.focus-on-left', command: 'window:focus-pane-on-left' }
|
||||
{ localisedLabel: 'core.menu.view.panes.focus-on-right', command: 'window:focus-pane-on-right' }
|
||||
{ label: 'Focus Pane Above', command: 'window:focus-pane-above' }
|
||||
{ label: 'Focus Pane Below', command: 'window:focus-pane-below' }
|
||||
{ label: 'Focus Pane On Left', command: 'window:focus-pane-on-left' }
|
||||
{ label: 'Focus Pane On Right', command: 'window:focus-pane-on-right' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.view.panes.close', command: 'pane:close' }
|
||||
{ label: 'Close Pane', command: 'pane:close' }
|
||||
]
|
||||
}
|
||||
{
|
||||
localisedLabel: 'core.menu.view.developer.self'
|
||||
label: 'Developer'
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.view.developer.open-in-dev-mode', command: 'application:open-dev' } # accelerator D
|
||||
{ localisedLabel: 'core.menu.view.developer.reload-window', command: 'window:reload' } # accelerator R
|
||||
{ localisedLabel: 'core.menu.view.developer.run-package-specs', command: 'window:run-package-specs' } # accelerator S
|
||||
{ localisedLabel: 'core.menu.view.developer.toggle-dev-tools', command: 'window:toggle-dev-tools' } # accelerator T
|
||||
{ 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: 'Toggle Developer &Tools', command: 'window:toggle-dev-tools' }
|
||||
]
|
||||
}
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.view.increase-font-size', command: 'window:increase-font-size' } # accelerator I
|
||||
{ localisedLabel: 'core.menu.view.decrease-font-size', command: 'window:decrease-font-size' } # accelerator D
|
||||
{ localisedLabel: 'core.menu.view.reset-font-size', command: 'window:reset-font-size' } # accelerator S
|
||||
{ label: '&Increase Font Size', command: 'window:increase-font-size' }
|
||||
{ label: '&Decrease Font Size', command: 'window:decrease-font-size' }
|
||||
{ label: 'Re&set Font Size', command: 'window:reset-font-size' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.view.toggle-soft-wrap', command: 'editor:toggle-soft-wrap' } # accelerator W
|
||||
{ label: 'Toggle Soft &Wrap', command: 'editor:toggle-soft-wrap' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
localisedLabel: 'core.menu.selection.self' # accelerator S
|
||||
label: '&Selection'
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.selection.add-above', command: 'editor:add-selection-above' } # accelerator A
|
||||
{ localisedLabel: 'core.menu.selection.add-below', command: 'editor:add-selection-below' } # accelerator B
|
||||
{ localisedLabel: 'core.menu.selection.single', command: 'editor:consolidate-selections' }
|
||||
{ localisedLabel: 'core.menu.selection.split-into-lines', command: 'editor:split-selections-into-lines' } # accelerator P
|
||||
{ label: 'Add Selection &Above', command: 'editor:add-selection-above' }
|
||||
{ label: 'Add Selection &Below', command: 'editor:add-selection-below' }
|
||||
{ label: 'S&plit into Lines', command: 'editor:split-selections-into-lines'}
|
||||
{ label: 'Single Selection', command: 'editor:consolidate-selections'}
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.selection.to-top', command: 'core:select-to-top' } # accelerator T
|
||||
{ localisedLabel: 'core.menu.selection.to-bottom', command: 'core:select-to-bottom' } # accelerator M
|
||||
{ label: 'Select to &Top', command: 'core:select-to-top' }
|
||||
{ label: 'Select to Botto&m', command: 'core:select-to-bottom' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.selection.line', command: 'editor:select-line' } # accelerator L
|
||||
{ localisedLabel: 'core.menu.selection.word', command: 'editor:select-word' } # accelerator W
|
||||
{ localisedLabel: 'core.menu.selection.to-beginning-of-word', command: 'editor:select-to-beginning-of-word' } # accelerator O
|
||||
{ localisedLabel: 'core.menu.selection.to-beginning-of-line', command: 'editor:select-to-beginning-of-line' } # accelerator I
|
||||
{ localisedLabel: 'core.menu.selection.to-first-char-of-line', command: 'editor:select-to-first-character-of-line' } # accelerator C
|
||||
{ localisedLabel: 'core.menu.selection.to-end-of-word', command: 'editor:select-to-end-of-word' } # accelerator D
|
||||
{ localisedLabel: 'core.menu.selection.to-end-of-line', command: 'editor:select-to-end-of-line' } # accelerator E
|
||||
{ label: 'Select &Line', command: 'editor:select-line' }
|
||||
{ label: 'Select &Word', command: 'editor:select-word' }
|
||||
{ label: 'Select to Beginning of W&ord', command: 'editor:select-to-beginning-of-word' }
|
||||
{ label: 'Select to Beginning of L&ine', command: 'editor:select-to-beginning-of-line' }
|
||||
{ label: 'Select to First &Character of Line', command: 'editor:select-to-first-character-of-line' }
|
||||
{ label: 'Select to End of Wor&d', command: 'editor:select-to-end-of-word' }
|
||||
{ label: 'Select to End of Lin&e', command: 'editor:select-to-end-of-line' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
localisedLabel: 'core.menu.find.self' # accelerator I
|
||||
label: 'F&ind'
|
||||
submenu: []
|
||||
}
|
||||
|
||||
{
|
||||
localisedLabel: 'core.menu.packages.self' # accelerator P
|
||||
label: '&Packages'
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.packages.open-package-manager', command: 'settings-view:view-installed-packages' }
|
||||
{ label: 'Open Package Manager', command: 'settings-view:view-installed-packages' }
|
||||
{ type: 'separator' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
localisedLabel: 'core.menu.help.self' # accelerator H
|
||||
label: '&Help'
|
||||
submenu: [
|
||||
{ localisedLabel: 'core.menu.help.terms-of-use', command: 'application:open-terms-of-use' } # accelerator T
|
||||
{ localisedLabel: 'core.menu.pulsar.view-license', command: 'application:open-license' } # accelerator L
|
||||
{ localisedLabel: 'core.menu.pulsar.version', enabled: false }
|
||||
{ label: 'View &Terms of Use', command: 'application:open-terms-of-use' }
|
||||
{ label: 'View &License', command: 'application:open-license' }
|
||||
{ label: "VERSION", enabled: false }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.help.docs', command: 'application:open-documentation' } # accelerator D
|
||||
{ localisedLabel: 'core.menu.help.faq', command: 'application:open-faq' }
|
||||
{ label: '&Documentation', command: 'application:open-documentation' }
|
||||
{ label: 'Frequently Asked Questions', command: 'application:open-faq' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.help.community-discussions', command: 'application:open-discussions' }
|
||||
{ localisedLabel: 'core.menu.help.report-issue', 'application:report-issue' }
|
||||
{ localisedLabel: 'core.menu.help.search-issues', command: 'application:search-issues' }
|
||||
{ label: 'Community Discussions', command: 'application:open-discussions' }
|
||||
{ label: 'Report Issue', command: 'application:report-issue' }
|
||||
{ label: 'Search Issues', command: 'application:search-issues' }
|
||||
{ type: 'separator' }
|
||||
{ localisedLabel: 'core.menu.pulsar.about', command: 'application:about' }
|
||||
{ label: 'About Pulsar', command: 'application:about' }
|
||||
{ type: 'separator' }
|
||||
]
|
||||
}
|
||||
|
73
package.json
73
package.json
@ -2,7 +2,7 @@
|
||||
"name": "pulsar",
|
||||
"author": "Pulsar-Edit <admin@pulsar-edit.dev>",
|
||||
"productName": "Pulsar",
|
||||
"version": "1.103.0-dev",
|
||||
"version": "1.108.0-dev",
|
||||
"description": "A Community-led Hyper-Hackable Text Editor",
|
||||
"branding": {
|
||||
"id": "pulsar",
|
||||
@ -19,12 +19,17 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/pulsar-edit/pulsar/issues"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"license": "MIT",
|
||||
"electronVersion": "12.2.3",
|
||||
"resolutions": {
|
||||
"es5-ext": "https://github.com/pulsar-edit/es5-ext#169f6ae9b2675675269a0ba265f83c29c7b56244"
|
||||
},
|
||||
"dependencies": {
|
||||
"@atom/source-map-support": "^0.3.4",
|
||||
"@babel/core": "7.18.6",
|
||||
"@formatjs/icu-messageformat-parser": "^2.3.0",
|
||||
"about": "file:packages/about",
|
||||
"archive-view": "file:packages/archive-view",
|
||||
"async": "3.2.4",
|
||||
@ -34,13 +39,13 @@
|
||||
"atom-light-syntax": "file:packages/atom-light-syntax",
|
||||
"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-atom-api": "file:packages/autocomplete-atom-api",
|
||||
"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": "file:packages/background-tips",
|
||||
"base16-tomorrow-dark-theme": "file:packages/base16-tomorrow-dark-theme",
|
||||
@ -61,29 +66,28 @@
|
||||
"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-and-replace": "file:packages/find-and-replace",
|
||||
"find-parent-dir": "^0.3.0",
|
||||
"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",
|
||||
"fuzzy-finder": "https://codeload.github.com/atom/fuzzy-finder/legacy.tar.gz/refs/tags/v1.14.3",
|
||||
"functional-red-black-tree": "^1.0.1",
|
||||
"fuzzy-finder": "file:packages/fuzzy-finder",
|
||||
"git-diff": "file:packages/git-diff",
|
||||
"git-utils": "5.7.1",
|
||||
"github": "https://codeload.github.com/pulsar-edit/github/tar.gz/refs/tags/v0.36.15-pretranspiled",
|
||||
"github": "https://codeload.github.com/pulsar-edit/github/tar.gz/refs/tags/v0.36.17-pretranspiled",
|
||||
"glob": "^7.1.1",
|
||||
"go-to-line": "file:packages/go-to-line",
|
||||
"grammar-selector": "file:packages/grammar-selector",
|
||||
"grim": "2.0.3",
|
||||
"image-view": "file:packages/image-view",
|
||||
"incompatible-packages": "file:packages/incompatible-packages",
|
||||
"intl-messageformat": "^10.3.3",
|
||||
"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",
|
||||
@ -118,7 +122,7 @@
|
||||
"language-typescript": "file:packages/language-typescript",
|
||||
"language-xml": "file:packages/language-xml",
|
||||
"language-yaml": "file:packages/language-yaml",
|
||||
"less-cache": "1.1.0",
|
||||
"less-cache": "pulsar-edit/less-cache#v2.0.1",
|
||||
"line-ending-selector": "file:packages/line-ending-selector",
|
||||
"line-top-index": "0.3.1",
|
||||
"link": "file:packages/link",
|
||||
@ -129,9 +133,8 @@
|
||||
"mocha-multi-reporters": "^1.1.4",
|
||||
"mock-spawn": "^0.2.6",
|
||||
"normalize-package-data": "3.0.2",
|
||||
"notifications": "https://codeload.github.com/atom/notifications/legacy.tar.gz/refs/tags/v0.72.1",
|
||||
"notifications": "file:./packages/notifications",
|
||||
"nsfw": "2.2.2",
|
||||
"nslog": "^3.0.0",
|
||||
"one-dark-syntax": "file:packages/one-dark-syntax",
|
||||
"one-dark-ui": "file:packages/one-dark-ui",
|
||||
"one-light-syntax": "file:packages/one-light-syntax",
|
||||
@ -139,22 +142,24 @@
|
||||
"open-on-github": "file:packages/open-on-github",
|
||||
"package-generator": "file:packages/package-generator",
|
||||
"pathwatcher": "^8.1.2",
|
||||
"postcss": "8.2.10",
|
||||
"postcss": "8.2.13",
|
||||
"postcss-selector-parser": "6.0.4",
|
||||
"property-accessors": "^1.1.3",
|
||||
"pulsar-updater": "file:packages/pulsar-updater",
|
||||
"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.8",
|
||||
"second-mate": "https://github.com/pulsar-edit/second-mate.git#9686771",
|
||||
"semver": "7.5.2",
|
||||
"service-hub": "^0.7.4",
|
||||
"settings-view": "file:packages/settings-view",
|
||||
"sinon": "9.2.1",
|
||||
"snippets": "github:pulsar-edit/snippets#bb00f909c6c645b173f27346875d8fa0c7af09f7",
|
||||
"snippets": "github:pulsar-edit/snippets#ba70705",
|
||||
"solarized-dark-syntax": "file:packages/solarized-dark-syntax",
|
||||
"solarized-light-syntax": "file:packages/solarized-light-syntax",
|
||||
"spell-check": "https://codeload.github.com/atom/spell-check/legacy.tar.gz/refs/tags/v0.77.1",
|
||||
"spell-check": "file:packages/spell-check",
|
||||
"status-bar": "file:packages/status-bar",
|
||||
"styleguide": "file:./packages/styleguide",
|
||||
"superstring": "^2.4.4",
|
||||
@ -162,15 +167,16 @@
|
||||
"tabs": "file:packages/tabs",
|
||||
"temp": "0.9.4",
|
||||
"text-buffer": "^13.18.6",
|
||||
"timecop": "https://codeload.github.com/atom/timecop/legacy.tar.gz/refs/tags/v0.36.2",
|
||||
"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",
|
||||
"tree-view": "file:packages/tree-view",
|
||||
"typescript-simple": "github:pulsar-edit/typescript-simple#ccb03e558217030e8f261339281f1d69147934f7",
|
||||
"underscore-plus": "^1.7.0",
|
||||
"update-package-dependencies": "file:./packages/update-package-dependencies",
|
||||
"vscode-ripgrep": "1.9.0",
|
||||
"web-tree-sitter": "^0.20.7",
|
||||
"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": "file:./packages/wrap-guide",
|
||||
"yargs": "17.6.2"
|
||||
@ -190,49 +196,50 @@
|
||||
"solarized-light-syntax": "file:./packages/solarized-light-syntax",
|
||||
"about": "file:./packages/about",
|
||||
"archive-view": "file:./packages/archive-view",
|
||||
"autocomplete-atom-api": "0.10.7",
|
||||
"autocomplete-atom-api": "file:packages/autocomplete-atom-api",
|
||||
"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",
|
||||
"autosave": "file:./packages/autosave",
|
||||
"background-tips": "file:./packages/background-tips",
|
||||
"bookmarks": "file:./packages/bookmarks",
|
||||
"bracket-matcher": "0.92.0",
|
||||
"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": "file:./packages/encoding-selector",
|
||||
"exception-reporting": "file:./packages/exception-reporting",
|
||||
"find-and-replace": "0.220.1",
|
||||
"fuzzy-finder": "1.14.3",
|
||||
"github": "0.36.14",
|
||||
"find-and-replace": "file:./packages/find-and-replace",
|
||||
"fuzzy-finder": "file:packages/fuzzy-finder",
|
||||
"github": "0.36.17",
|
||||
"git-diff": "file:./packages/git-diff",
|
||||
"go-to-line": "file:./packages/go-to-line",
|
||||
"grammar-selector": "file:./packages/grammar-selector",
|
||||
"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": "file:./packages/markdown-preview",
|
||||
"notifications": "0.72.1",
|
||||
"notifications": "file:./packages/notifications",
|
||||
"open-on-github": "file:./packages/open-on-github",
|
||||
"package-generator": "file:./packages/package-generator",
|
||||
"pulsar-updater": "file:./packages/pulsar-updater",
|
||||
"settings-view": "file:./packages/settings-view",
|
||||
"snippets": "1.6.1",
|
||||
"spell-check": "0.77.1",
|
||||
"spell-check": "file:./packages/spell-check",
|
||||
"status-bar": "file:./packages/status-bar",
|
||||
"styleguide": "file:./packages/styleguide",
|
||||
"symbols-view": "0.118.4",
|
||||
"tabs": "file:./packages/tabs",
|
||||
"timecop": "0.36.2",
|
||||
"tree-view": "0.229.1",
|
||||
"timecop": "file:./packages/timecop",
|
||||
"tree-view": "file:./packages/tree-view",
|
||||
"update-package-dependencies": "file:./packages/update-package-dependencies",
|
||||
"welcome": "file:./packages/welcome",
|
||||
"whitespace": "0.37.8",
|
||||
"whitespace": "file:./packages/whitespace",
|
||||
"wrap-guide": "file:./packages/wrap-guide",
|
||||
"language-c": "file:./packages/language-c",
|
||||
"language-clojure": "file:./packages/language-clojure",
|
||||
|
@ -13,33 +13,33 @@ See [RFC 003](https://github.com/atom/atom/blob/master/docs/rfcs/003-consolidate
|
||||
| **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-atom-api** | [`./autocomplete-atom-api`](./autocomplete-atom-api) | |
|
||||
| **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** | [`pulsar-edit/autosave`][autosave] | [#17834](https://github.com/atom/atom/issues/17834) |
|
||||
| **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** | [`atom/bracket-matcher`][bracket-matcher] | |
|
||||
| **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] | |
|
||||
| **find-and-replace** | [`./find-and-replace`](./find-and-replace) | |
|
||||
| **fuzzy-finder** | [`./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** | [`atom/keybinding-resolver`][keybinding-resolver] | [#18275](https://github.com/atom/atom/issues/18275) |
|
||||
| **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) | |
|
||||
@ -77,7 +77,7 @@ See [RFC 003](https://github.com/atom/atom/blob/master/docs/rfcs/003-consolidate
|
||||
| **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) |
|
||||
| **notifications** | [`./notifications`](./notifications) | |
|
||||
| **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) | |
|
||||
@ -85,31 +85,22 @@ See [RFC 003](https://github.com/atom/atom/blob/master/docs/rfcs/003-consolidate
|
||||
| **open-on-github** | [`./open-on-github`](./open-on-github) | |
|
||||
| **settings-view** | [`./settings-view`](./settings-view) | |
|
||||
| **package-generator** | [`./package-generator`](./package-generator) | |
|
||||
| **pulsar-updater** | [`./pulsar-updater`](./pulsar-updater) | |
|
||||
| **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] | |
|
||||
| **spell-check** | [`./spell-check`](./spell-check) | |
|
||||
| **status-bar** | [`./status-bar`](./status-bar) | |
|
||||
| **styleguide** | [`./styleguide`](./styleguide) | |
|
||||
| **symbols-view** | [`pulsar-edit/symbols-view`][symbols-view] | |
|
||||
| **tabs** | [`./tabs`](./tabs) | |
|
||||
| **timecop** | [`pulsar-edit/timecop`][timecop] | [#18272](https://github.com/atom/atom/issues/18272) |
|
||||
| **tree-view** | [`pulsar-edit/tree-view`][tree-view] | |
|
||||
| **timecop** | [`./timecop`](./timecop) | |
|
||||
| **tree-view** | [`./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) | |
|
||||
|
||||
[autocomplete-atom-api]: https://github.com/pulsar-edit/autocomplete-atom-api
|
||||
[autosave]: https://github.com/pulsar-edit/autosave
|
||||
[bracket-matcher]: https://github.com/pulsar-edit/bracket-matcher
|
||||
[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
|
||||
[timecop]: https://github.com/pulsar-edit/timecop
|
||||
[tree-view]: https://github.com/pulsar-edit/tree-view
|
||||
|
@ -36,7 +36,6 @@ module.exports = class About {
|
||||
if (this.views.aboutView) this.views.aboutView.destroy();
|
||||
this.views.aboutView = null;
|
||||
|
||||
if (this.state.updateManager) this.state.updateManager.dispose();
|
||||
this.setState({ updateManager: null });
|
||||
|
||||
this.subscriptions.dispose();
|
||||
@ -69,8 +68,7 @@ module.exports = class About {
|
||||
currentAtomVersion: this.state.currentAtomVersion,
|
||||
currentElectronVersion: this.state.currentElectronVersion,
|
||||
currentChromeVersion: this.state.currentChromeVersion,
|
||||
currentNodeVersion: this.state.currentNodeVersion,
|
||||
availableVersion: this.state.updateManager.getAvailableVersion()
|
||||
currentNodeVersion: this.state.currentNodeVersion
|
||||
});
|
||||
this.handleStateChanges();
|
||||
}
|
||||
@ -86,14 +84,9 @@ module.exports = class About {
|
||||
currentAtomVersion: this.state.currentAtomVersion,
|
||||
currentElectronVersion: this.state.currentElectronVersion,
|
||||
currentChromeVersion: this.state.currentChromeVersion,
|
||||
currentNodeVersion: this.state.currentNodeVersion,
|
||||
availableVersion: this.state.updateManager.getAvailableVersion()
|
||||
currentNodeVersion: this.state.currentNodeVersion
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.state.updateManager.onDidChange(() => {
|
||||
this.didChange();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -1,38 +0,0 @@
|
||||
const { CompositeDisposable } = require('atom');
|
||||
const etch = require('etch');
|
||||
const EtchComponent = require('../etch-component');
|
||||
|
||||
const $ = etch.dom;
|
||||
|
||||
module.exports = class AboutStatusBar extends EtchComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.subscriptions = new CompositeDisposable();
|
||||
|
||||
this.subscriptions.add(
|
||||
atom.tooltips.add(this.element, {
|
||||
title:
|
||||
'An update will be installed the next time Pulsar is relaunched.<br/><br/>Click the squirrel icon for more information.'
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
handleClick() {
|
||||
atom.workspace.open('atom://about');
|
||||
}
|
||||
|
||||
render() {
|
||||
return $.div(
|
||||
{
|
||||
className: 'about-release-notes inline-block',
|
||||
onclick: this.handleClick.bind(this)
|
||||
},
|
||||
$.span({ type: 'button', className: 'icon icon-squirrel' })
|
||||
);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
super.destroy();
|
||||
this.subscriptions.dispose();
|
||||
}
|
||||
};
|
@ -3,7 +3,6 @@ const etch = require('etch');
|
||||
const { shell } = require('electron');
|
||||
const AtomLogo = require('./atom-logo');
|
||||
const EtchComponent = require('../etch-component');
|
||||
const UpdateView = require('./update-view');
|
||||
|
||||
const $ = etch.dom;
|
||||
|
||||
@ -31,7 +30,7 @@ module.exports = class AboutView extends EtchComponent {
|
||||
handleReleaseNotesClick(e) {
|
||||
e.preventDefault();
|
||||
shell.openExternal(
|
||||
this.props.updateManager.getReleaseNotesURLForAvailableVersion() //update-manager.js will need updating when we decide how to do the changelog
|
||||
this.props.updateManager.getReleaseNotesURLForCurrentVersion()
|
||||
);
|
||||
}
|
||||
|
||||
@ -52,7 +51,15 @@ module.exports = class AboutView extends EtchComponent {
|
||||
handleHowToUpdateClick(e) {
|
||||
e.preventDefault();
|
||||
shell.openExternal(
|
||||
'https://pulsar-edit.dev/docs/launch-manual/sections/getting-started/#installing-pulsar'
|
||||
'https://github.com/pulsar-edit/pulsar/tree/master/packages/pulsar-updater#readme'
|
||||
);
|
||||
}
|
||||
|
||||
executeUpdateAction(e) {
|
||||
e.preventDefault();
|
||||
atom.commands.dispatch(
|
||||
atom.views.getView(atom.workspace),
|
||||
'pulsar-updater:check-for-update'
|
||||
);
|
||||
}
|
||||
|
||||
@ -160,12 +167,31 @@ module.exports = class AboutView extends EtchComponent {
|
||||
)
|
||||
),
|
||||
|
||||
$(UpdateView, {
|
||||
updateManager: this.props.updateManager,
|
||||
availableVersion: this.props.availableVersion,
|
||||
viewUpdateReleaseNotes: this.handleReleaseNotesClick.bind(this),
|
||||
viewUpdateInstructions: this.handleHowToUpdateClick.bind(this)
|
||||
}),
|
||||
$.div(
|
||||
{ className: 'about-updates group-start' },
|
||||
$.div(
|
||||
{ className: 'about-updates-box' },
|
||||
$.div(
|
||||
{ className: 'about-updates-status' },
|
||||
$.div(
|
||||
{ className: 'about-updates-item app-unsupported' },
|
||||
$.span(
|
||||
{ className: 'about-updates-label is-strong' },
|
||||
'Updates have been moved to the package ', $.code({}, 'pulsar-updater'), '.',
|
||||
$.br()
|
||||
),
|
||||
$.a(
|
||||
{
|
||||
className: 'about-updates-instructions',
|
||||
onclick: this.handleHowToUpdateClick.bind(this)
|
||||
},
|
||||
'How to update'
|
||||
)
|
||||
)
|
||||
),
|
||||
this.renderUpdateChecker()
|
||||
)
|
||||
),
|
||||
|
||||
$.div(
|
||||
{ className: 'about-actions group-item' },
|
||||
@ -201,6 +227,29 @@ module.exports = class AboutView extends EtchComponent {
|
||||
);
|
||||
}
|
||||
|
||||
renderUpdateChecker() {
|
||||
if (atom.packages.isPackageDisabled("pulsar-updater")) {
|
||||
return $.div(
|
||||
{ className: 'about-updates-item app-unsupported' },
|
||||
$.span(
|
||||
{ className: 'about-updates-label is-strong' },
|
||||
'Enable `pulsar-updater` to check for updates'
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return $.button(
|
||||
{
|
||||
className: 'btn about-update-action-button',
|
||||
onclick: this.executeUpdateAction.bind(this),
|
||||
style: {
|
||||
display: 'block'
|
||||
}
|
||||
},
|
||||
'Check Now'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
serialize() {
|
||||
return {
|
||||
deserializer: this.constructor.name,
|
||||
|
@ -1,181 +0,0 @@
|
||||
const etch = require('etch');
|
||||
const EtchComponent = require('../etch-component');
|
||||
const UpdateManager = require('../update-manager');
|
||||
|
||||
const $ = etch.dom;
|
||||
|
||||
module.exports = class UpdateView extends EtchComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
if (
|
||||
this.props.updateManager.getAutoUpdatesEnabled() &&
|
||||
this.props.updateManager.getState() === UpdateManager.State.Idle
|
||||
) {
|
||||
this.props.updateManager.checkForUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
handleAutoUpdateCheckbox(e) {
|
||||
atom.config.set('core.automaticallyUpdate', e.target.checked);
|
||||
}
|
||||
|
||||
shouldUpdateActionButtonBeDisabled() {
|
||||
let { state } = this.props.updateManager;
|
||||
return (
|
||||
state === UpdateManager.State.CheckingForUpdate ||
|
||||
state === UpdateManager.State.DownloadingUpdate
|
||||
);
|
||||
}
|
||||
|
||||
executeUpdateAction() {
|
||||
if (
|
||||
this.props.updateManager.state ===
|
||||
UpdateManager.State.UpdateAvailableToInstall
|
||||
) {
|
||||
this.props.updateManager.restartAndInstallUpdate();
|
||||
} else {
|
||||
this.props.updateManager.checkForUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
renderUpdateStatus() {
|
||||
let updateStatus = '';
|
||||
|
||||
switch (this.props.updateManager.state) {
|
||||
case UpdateManager.State.Idle:
|
||||
updateStatus = $.div(
|
||||
{
|
||||
className:
|
||||
'about-updates-item is-shown about-default-update-message'
|
||||
},
|
||||
this.props.updateManager.getAutoUpdatesEnabled()
|
||||
? 'Pulsar will check for updates automatically'
|
||||
: 'Automatic updates are disabled please check manually'
|
||||
);
|
||||
break;
|
||||
case UpdateManager.State.CheckingForUpdate:
|
||||
updateStatus = $.div(
|
||||
{ className: 'about-updates-item app-checking-for-updates' },
|
||||
$.span(
|
||||
{ className: 'about-updates-label icon icon-search' },
|
||||
'Checking for updates...'
|
||||
)
|
||||
);
|
||||
break;
|
||||
case UpdateManager.State.DownloadingUpdate:
|
||||
updateStatus = $.div(
|
||||
{ className: 'about-updates-item app-downloading-update' },
|
||||
$.span({ className: 'loading loading-spinner-tiny inline-block' }),
|
||||
$.span({ className: 'about-updates-label' }, 'Downloading update')
|
||||
);
|
||||
break;
|
||||
case UpdateManager.State.UpdateAvailableToInstall:
|
||||
updateStatus = $.div(
|
||||
{ className: 'about-updates-item app-update-available-to-install' },
|
||||
$.span(
|
||||
{ className: 'about-updates-label icon icon-squirrel' },
|
||||
'New update'
|
||||
),
|
||||
$.span(
|
||||
{ className: 'about-updates-version' },
|
||||
this.props.availableVersion
|
||||
),
|
||||
$.a(
|
||||
{
|
||||
className: 'about-updates-release-notes',
|
||||
onclick: this.props.viewUpdateReleaseNotes
|
||||
},
|
||||
'Release Notes'
|
||||
)
|
||||
);
|
||||
break;
|
||||
case UpdateManager.State.UpToDate:
|
||||
updateStatus = $.div(
|
||||
{ className: 'about-updates-item app-up-to-date' },
|
||||
$.span({ className: 'icon icon-check' }),
|
||||
$.span(
|
||||
{ className: 'about-updates-label is-strong' },
|
||||
'Pulsar is up to date!'
|
||||
)
|
||||
);
|
||||
break;
|
||||
case UpdateManager.State.Unsupported:
|
||||
updateStatus = $.div(
|
||||
{ className: 'about-updates-item app-unsupported' },
|
||||
$.span(
|
||||
{ className: 'about-updates-label is-strong' },
|
||||
'Your system does not support automatic updates'
|
||||
),
|
||||
$.a(
|
||||
{
|
||||
className: 'about-updates-instructions',
|
||||
onclick: this.props.viewUpdateInstructions
|
||||
},
|
||||
'How to update'
|
||||
)
|
||||
);
|
||||
break;
|
||||
case UpdateManager.State.Error:
|
||||
updateStatus = $.div(
|
||||
{ className: 'about-updates-item app-update-error' },
|
||||
$.span({ className: 'icon icon-x' }),
|
||||
$.span(
|
||||
{ className: 'about-updates-label app-error-message is-strong' },
|
||||
this.props.updateManager.getErrorMessage()
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return updateStatus;
|
||||
}
|
||||
|
||||
render() {
|
||||
return $.div(
|
||||
{ className: 'about-updates group-start' },
|
||||
$.div(
|
||||
{ className: 'about-updates-box' },
|
||||
$.div({ className: 'about-updates-status' }, this.renderUpdateStatus()),
|
||||
$.button(
|
||||
{
|
||||
className: 'btn about-update-action-button',
|
||||
disabled: this.shouldUpdateActionButtonBeDisabled(),
|
||||
onclick: this.executeUpdateAction.bind(this),
|
||||
style: {
|
||||
display:
|
||||
this.props.updateManager.state ===
|
||||
UpdateManager.State.Unsupported
|
||||
? 'none'
|
||||
: 'block'
|
||||
}
|
||||
},
|
||||
this.props.updateManager.state === 'update-available'
|
||||
? 'Restart and install'
|
||||
: 'Check now'
|
||||
)
|
||||
),
|
||||
$.div(
|
||||
{
|
||||
className: 'about-auto-updates',
|
||||
style: {
|
||||
display:
|
||||
this.props.updateManager.state === UpdateManager.State.Unsupported
|
||||
? 'none'
|
||||
: 'block'
|
||||
}
|
||||
},
|
||||
$.label(
|
||||
{},
|
||||
$.input({
|
||||
className: 'input-checkbox',
|
||||
type: 'checkbox',
|
||||
checked: this.props.updateManager.getAutoUpdatesEnabled(),
|
||||
onchange: this.handleAutoUpdateCheckbox.bind(this)
|
||||
}),
|
||||
$.span({}, 'Automatically download updates')
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
@ -1,69 +1,24 @@
|
||||
const { CompositeDisposable } = require('atom');
|
||||
const semver = require('semver');
|
||||
|
||||
const UpdateManager = require('./update-manager');
|
||||
const About = require('./about');
|
||||
const StatusBarView = require('./components/about-status-bar');
|
||||
let updateManager;
|
||||
|
||||
// The local storage key for the available update version.
|
||||
const AvailableUpdateVersion = 'about:version-available';
|
||||
const AboutURI = 'atom://about';
|
||||
|
||||
module.exports = {
|
||||
activate() {
|
||||
this.subscriptions = new CompositeDisposable();
|
||||
|
||||
this.createModel();
|
||||
|
||||
let availableVersion = window.localStorage.getItem(AvailableUpdateVersion);
|
||||
if (
|
||||
atom.getReleaseChannel() === 'dev' ||
|
||||
(availableVersion && semver.lte(availableVersion, atom.getVersion()))
|
||||
) {
|
||||
this.clearUpdateState();
|
||||
}
|
||||
|
||||
this.subscriptions.add(
|
||||
updateManager.onDidChange(() => {
|
||||
if (
|
||||
updateManager.getState() ===
|
||||
UpdateManager.State.UpdateAvailableToInstall
|
||||
) {
|
||||
window.localStorage.setItem(
|
||||
AvailableUpdateVersion,
|
||||
updateManager.getAvailableVersion()
|
||||
);
|
||||
this.showStatusBarIfNeeded();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
this.subscriptions.add(
|
||||
atom.commands.add('atom-workspace', 'about:clear-update-state', () => {
|
||||
this.clearUpdateState();
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
deactivate() {
|
||||
this.model.destroy();
|
||||
if (this.statusBarTile) this.statusBarTile.destroy();
|
||||
|
||||
if (updateManager) {
|
||||
updateManager.dispose();
|
||||
updateManager = undefined;
|
||||
}
|
||||
},
|
||||
|
||||
clearUpdateState() {
|
||||
window.localStorage.removeItem(AvailableUpdateVersion);
|
||||
},
|
||||
|
||||
consumeStatusBar(statusBar) {
|
||||
this.statusBar = statusBar;
|
||||
this.showStatusBarIfNeeded();
|
||||
},
|
||||
|
||||
deserializeAboutView(state) {
|
||||
if (!this.model) {
|
||||
this.createModel();
|
||||
@ -83,27 +38,6 @@ module.exports = {
|
||||
currentNodeVersion: process.version,
|
||||
updateManager: updateManager
|
||||
});
|
||||
},
|
||||
|
||||
isUpdateAvailable() {
|
||||
let availableVersion = window.localStorage.getItem(AvailableUpdateVersion);
|
||||
return availableVersion && semver.gt(availableVersion, atom.getVersion());
|
||||
},
|
||||
|
||||
showStatusBarIfNeeded() {
|
||||
if (this.isUpdateAvailable() && this.statusBar) {
|
||||
let statusBarView = new StatusBarView();
|
||||
|
||||
if (this.statusBarTile) {
|
||||
this.statusBarTile.destroy();
|
||||
}
|
||||
|
||||
this.statusBarTile = this.statusBar.addRightTile({
|
||||
item: statusBarView,
|
||||
priority: -100
|
||||
});
|
||||
|
||||
return this.statusBarTile;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -1,127 +1,13 @@
|
||||
const { Emitter, CompositeDisposable } = require('atom');
|
||||
|
||||
const Unsupported = 'unsupported';
|
||||
const Idle = 'idle';
|
||||
const CheckingForUpdate = 'checking';
|
||||
const DownloadingUpdate = 'downloading';
|
||||
const UpdateAvailableToInstall = 'update-available';
|
||||
const UpToDate = 'no-update-available';
|
||||
const ErrorState = 'error';
|
||||
|
||||
let UpdateManager = class UpdateManager {
|
||||
constructor() {
|
||||
this.emitter = new Emitter();
|
||||
this.currentVersion = atom.getVersion();
|
||||
this.availableVersion = atom.getVersion();
|
||||
this.resetState();
|
||||
this.listenForAtomEvents();
|
||||
}
|
||||
|
||||
listenForAtomEvents() {
|
||||
this.subscriptions = new CompositeDisposable();
|
||||
|
||||
this.subscriptions.add(
|
||||
atom.autoUpdater.onDidBeginCheckingForUpdate(() => {
|
||||
this.setState(CheckingForUpdate);
|
||||
}),
|
||||
atom.autoUpdater.onDidBeginDownloadingUpdate(() => {
|
||||
this.setState(DownloadingUpdate);
|
||||
}),
|
||||
atom.autoUpdater.onDidCompleteDownloadingUpdate(({ releaseVersion }) => {
|
||||
this.setAvailableVersion(releaseVersion);
|
||||
}),
|
||||
atom.autoUpdater.onUpdateNotAvailable(() => {
|
||||
this.setState(UpToDate);
|
||||
}),
|
||||
atom.autoUpdater.onUpdateError(() => {
|
||||
this.setState(ErrorState);
|
||||
}),
|
||||
atom.config.observe('core.automaticallyUpdate', value => {
|
||||
this.autoUpdatesEnabled = value;
|
||||
this.emitDidChange();
|
||||
})
|
||||
);
|
||||
|
||||
// TODO: When https://github.com/atom/electron/issues/4587 is closed we can add this support.
|
||||
// atom.autoUpdater.onUpdateAvailable =>
|
||||
// @find('.about-updates-item').removeClass('is-shown')
|
||||
// @updateAvailable.addClass('is-shown')
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.subscriptions.dispose();
|
||||
}
|
||||
|
||||
onDidChange(callback) {
|
||||
return this.emitter.on('did-change', callback);
|
||||
}
|
||||
|
||||
emitDidChange() {
|
||||
this.emitter.emit('did-change');
|
||||
}
|
||||
|
||||
getAutoUpdatesEnabled() {
|
||||
return (
|
||||
this.autoUpdatesEnabled && this.state !== UpdateManager.State.Unsupported
|
||||
);
|
||||
}
|
||||
|
||||
setAutoUpdatesEnabled(enabled) {
|
||||
return atom.config.set('core.automaticallyUpdate', enabled);
|
||||
}
|
||||
|
||||
getErrorMessage() {
|
||||
return atom.autoUpdater.getErrorMessage();
|
||||
}
|
||||
|
||||
getState() {
|
||||
return this.state;
|
||||
}
|
||||
|
||||
setState(state) {
|
||||
this.state = state;
|
||||
this.emitDidChange();
|
||||
}
|
||||
|
||||
resetState() {
|
||||
this.state = atom.autoUpdater.platformSupportsUpdates()
|
||||
? atom.autoUpdater.getState()
|
||||
: Unsupported;
|
||||
this.emitDidChange();
|
||||
}
|
||||
|
||||
getAvailableVersion() {
|
||||
return this.availableVersion;
|
||||
}
|
||||
|
||||
setAvailableVersion(version) {
|
||||
this.availableVersion = version;
|
||||
|
||||
if (this.availableVersion !== this.currentVersion) {
|
||||
this.state = UpdateAvailableToInstall;
|
||||
} else {
|
||||
this.state = UpToDate;
|
||||
}
|
||||
|
||||
this.emitDidChange();
|
||||
}
|
||||
|
||||
checkForUpdate() {
|
||||
atom.autoUpdater.checkForUpdate();
|
||||
}
|
||||
|
||||
restartAndInstallUpdate() {
|
||||
atom.autoUpdater.restartAndInstallUpdate();
|
||||
}
|
||||
|
||||
getReleaseNotesURLForCurrentVersion() {
|
||||
return this.getReleaseNotesURLForVersion(this.currentVersion);
|
||||
}
|
||||
|
||||
getReleaseNotesURLForAvailableVersion() {
|
||||
return this.getReleaseNotesURLForVersion(this.availableVersion);
|
||||
}
|
||||
|
||||
getReleaseNotesURLForVersion(appVersion) {
|
||||
// Dev versions will not have a releases page
|
||||
if (appVersion.indexOf('dev') > -1) {
|
||||
@ -138,14 +24,4 @@ let UpdateManager = class UpdateManager {
|
||||
}
|
||||
};
|
||||
|
||||
UpdateManager.State = {
|
||||
Unsupported: Unsupported,
|
||||
Idle: Idle,
|
||||
CheckingForUpdate: CheckingForUpdate,
|
||||
DownloadingUpdate: DownloadingUpdate,
|
||||
UpdateAvailableToInstall: UpdateAvailableToInstall,
|
||||
UpToDate: UpToDate,
|
||||
Error: ErrorState
|
||||
};
|
||||
|
||||
module.exports = UpdateManager;
|
||||
|
33
packages/about/package-lock.json
generated
33
packages/about/package-lock.json
generated
@ -9,8 +9,7 @@
|
||||
"version": "1.9.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"etch": "^0.14.1",
|
||||
"semver": "^7.3.8"
|
||||
"etch": "^0.14.1"
|
||||
},
|
||||
"engines": {
|
||||
"atom": ">=1.7 <2.0.0"
|
||||
@ -20,36 +19,6 @@
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/etch/-/etch-0.14.1.tgz",
|
||||
"integrity": "sha512-+IwqSDBhaQFMUHJu4L/ir0dhDoW5IIihg4Z9lzsIxxne8V0PlSg0gnk2STaKWjGJQnDR4cxpA+a/dORX9kycTA=="
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.3.8",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
|
||||
"integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,15 +10,7 @@
|
||||
"atom": ">=1.7 <2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"etch": "^0.14.1",
|
||||
"semver": "^7.3.8"
|
||||
},
|
||||
"consumedServices": {
|
||||
"status-bar": {
|
||||
"versions": {
|
||||
"^1.0.0": "consumeStatusBar"
|
||||
}
|
||||
}
|
||||
"etch": "^0.14.1"
|
||||
},
|
||||
"deserializers": {
|
||||
"AboutView": "deserializeAboutView"
|
||||
|
@ -2,15 +2,6 @@ describe('About', () => {
|
||||
let workspaceElement;
|
||||
|
||||
beforeEach(async () => {
|
||||
let storage = {};
|
||||
|
||||
spyOn(window.localStorage, 'setItem').andCallFake((key, value) => {
|
||||
storage[key] = value;
|
||||
});
|
||||
spyOn(window.localStorage, 'getItem').andCallFake(key => {
|
||||
return storage[key];
|
||||
});
|
||||
|
||||
workspaceElement = atom.views.getView(atom.workspace);
|
||||
await atom.packages.activatePackage('about');
|
||||
});
|
||||
@ -100,4 +91,16 @@ describe('About', () => {
|
||||
expect(atom.clipboard.read()).toBe(process.version);
|
||||
});
|
||||
});
|
||||
|
||||
describe('check for update appears', () => {
|
||||
it('when "pulsar-updater" is enabled', async () => {
|
||||
atom.packages.activatePackage('pulsar-updater');
|
||||
await atom.workspace.open('atom://about');
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
|
||||
let aboutElement = workspaceElement.querySelector('.about');
|
||||
let updateContainer = aboutElement.querySelector('.about-update-action-button');
|
||||
expect(updateContainer.innerText).toBe('Check Now');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,184 +0,0 @@
|
||||
const { conditionPromise } = require('./helpers/async-spec-helpers');
|
||||
const MockUpdater = require('./mocks/updater');
|
||||
|
||||
describe('the status bar', () => {
|
||||
let atomVersion;
|
||||
let workspaceElement;
|
||||
|
||||
beforeEach(async () => {
|
||||
let storage = {};
|
||||
|
||||
spyOn(window.localStorage, 'setItem').andCallFake((key, value) => {
|
||||
storage[key] = value;
|
||||
});
|
||||
spyOn(window.localStorage, 'getItem').andCallFake(key => {
|
||||
return storage[key];
|
||||
});
|
||||
spyOn(atom, 'getVersion').andCallFake(() => {
|
||||
return atomVersion;
|
||||
});
|
||||
|
||||
workspaceElement = atom.views.getView(atom.workspace);
|
||||
|
||||
await atom.packages.activatePackage('status-bar');
|
||||
await atom.workspace.open('sample.js');
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await atom.packages.deactivatePackage('about');
|
||||
await atom.packages.deactivatePackage('status-bar');
|
||||
});
|
||||
|
||||
describe('on a stable version', function() {
|
||||
beforeEach(async () => {
|
||||
atomVersion = '1.2.3';
|
||||
|
||||
await atom.packages.activatePackage('about');
|
||||
});
|
||||
|
||||
describe('with no update', () => {
|
||||
it('does not show the view', () => {
|
||||
expect(workspaceElement).not.toContain('.about-release-notes');
|
||||
});
|
||||
});
|
||||
|
||||
describe('with an update', () => {
|
||||
it('shows the view when the update finishes downloading', () => {
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0');
|
||||
expect(workspaceElement).toContain('.about-release-notes');
|
||||
});
|
||||
|
||||
describe('clicking on the status', () => {
|
||||
it('opens the about page', async () => {
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0');
|
||||
workspaceElement.querySelector('.about-release-notes').click();
|
||||
await conditionPromise(() =>
|
||||
workspaceElement.querySelector('.about')
|
||||
);
|
||||
expect(workspaceElement.querySelector('.about')).toExist();
|
||||
});
|
||||
});
|
||||
|
||||
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');
|
||||
|
||||
await atom.packages.deactivatePackage('about');
|
||||
expect(workspaceElement).not.toContain('.about-release-notes');
|
||||
|
||||
await atom.packages.activatePackage('about');
|
||||
await Promise.resolve(); // Service consumption hooks are deferred until the next tick
|
||||
expect(workspaceElement).toContain('.about-release-notes');
|
||||
|
||||
await atom.packages.deactivatePackage('about');
|
||||
expect(workspaceElement).not.toContain('.about-release-notes');
|
||||
|
||||
atomVersion = '42.0.0';
|
||||
await atom.packages.activatePackage('about');
|
||||
|
||||
await Promise.resolve(); // Service consumption hooks are deferred until the next tick
|
||||
expect(workspaceElement).not.toContain('.about-release-notes');
|
||||
});
|
||||
|
||||
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');
|
||||
|
||||
atomVersion = '43.0.0';
|
||||
await atom.packages.activatePackage('about');
|
||||
|
||||
await Promise.resolve(); // Service consumption hooks are deferred until the next tick
|
||||
expect(workspaceElement).not.toContain('.about-release-notes');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('on a beta version', function() {
|
||||
beforeEach(async () => {
|
||||
atomVersion = '1.2.3-beta4';
|
||||
|
||||
await atom.packages.activatePackage('about');
|
||||
});
|
||||
|
||||
describe('with no update', () => {
|
||||
it('does not show the view', () => {
|
||||
expect(workspaceElement).not.toContain('.about-release-notes');
|
||||
});
|
||||
});
|
||||
|
||||
describe('with an update', () => {
|
||||
it('shows the view when the update finishes downloading', () => {
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0');
|
||||
expect(workspaceElement).toContain('.about-release-notes');
|
||||
});
|
||||
|
||||
describe('clicking on the status', () => {
|
||||
it('opens the about page', async () => {
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0');
|
||||
workspaceElement.querySelector('.about-release-notes').click();
|
||||
await conditionPromise(() =>
|
||||
workspaceElement.querySelector('.about')
|
||||
);
|
||||
expect(workspaceElement.querySelector('.about')).toExist();
|
||||
});
|
||||
});
|
||||
|
||||
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');
|
||||
|
||||
await atom.packages.deactivatePackage('about');
|
||||
expect(workspaceElement).not.toContain('.about-release-notes');
|
||||
|
||||
await atom.packages.activatePackage('about');
|
||||
await Promise.resolve(); // Service consumption hooks are deferred until the next tick
|
||||
expect(workspaceElement).toContain('.about-release-notes');
|
||||
|
||||
await atom.packages.deactivatePackage('about');
|
||||
expect(workspaceElement).not.toContain('.about-release-notes');
|
||||
|
||||
atomVersion = '42.0.0';
|
||||
await atom.packages.activatePackage('about');
|
||||
|
||||
await Promise.resolve(); // Service consumption hooks are deferred until the next tick
|
||||
expect(workspaceElement).not.toContain('.about-release-notes');
|
||||
});
|
||||
|
||||
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');
|
||||
|
||||
atomVersion = '43.0.0';
|
||||
await atom.packages.activatePackage('about');
|
||||
|
||||
await Promise.resolve(); // Service consumption hooks are deferred until the next tick
|
||||
expect(workspaceElement).not.toContain('.about-release-notes');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('on a development version', function() {
|
||||
beforeEach(async () => {
|
||||
atomVersion = '1.2.3-dev-0123abcd';
|
||||
|
||||
await atom.packages.activatePackage('about');
|
||||
});
|
||||
|
||||
describe('with no update', () => {
|
||||
it('does not show the view', () => {
|
||||
expect(workspaceElement).not.toContain('.about-release-notes');
|
||||
});
|
||||
});
|
||||
|
||||
describe('with a previously downloaded update', () => {
|
||||
it('does not show the view', () => {
|
||||
window.localStorage.setItem('about:version-available', '42.0.0');
|
||||
|
||||
expect(workspaceElement).not.toContain('.about-release-notes');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,23 +0,0 @@
|
||||
module.exports = {
|
||||
updateError() {
|
||||
atom.autoUpdater.emitter.emit('update-error');
|
||||
},
|
||||
|
||||
checkForUpdate() {
|
||||
atom.autoUpdater.emitter.emit('did-begin-checking-for-update');
|
||||
},
|
||||
|
||||
updateNotAvailable() {
|
||||
atom.autoUpdater.emitter.emit('update-not-available');
|
||||
},
|
||||
|
||||
downloadUpdate() {
|
||||
atom.autoUpdater.emitter.emit('did-begin-downloading-update');
|
||||
},
|
||||
|
||||
finishDownloadingUpdate(releaseVersion) {
|
||||
atom.autoUpdater.emitter.emit('did-complete-downloading-update', {
|
||||
releaseVersion
|
||||
});
|
||||
}
|
||||
};
|
@ -1,387 +0,0 @@
|
||||
const { shell } = require('electron');
|
||||
const main = require('../lib/main');
|
||||
const AboutView = require('../lib/components/about-view');
|
||||
const UpdateView = require('../lib/components/update-view');
|
||||
const MockUpdater = require('./mocks/updater');
|
||||
|
||||
describe('UpdateView', () => {
|
||||
let aboutElement;
|
||||
let updateManager;
|
||||
let workspaceElement;
|
||||
let scheduler;
|
||||
|
||||
beforeEach(async () => {
|
||||
let storage = {};
|
||||
|
||||
spyOn(window.localStorage, 'setItem').andCallFake((key, value) => {
|
||||
storage[key] = value;
|
||||
});
|
||||
spyOn(window.localStorage, 'getItem').andCallFake(key => {
|
||||
return storage[key];
|
||||
});
|
||||
|
||||
workspaceElement = atom.views.getView(atom.workspace);
|
||||
await atom.packages.activatePackage('about');
|
||||
spyOn(atom.autoUpdater, 'getState').andReturn('idle');
|
||||
spyOn(atom.autoUpdater, 'checkForUpdate');
|
||||
spyOn(atom.autoUpdater, 'platformSupportsUpdates').andReturn(true);
|
||||
});
|
||||
|
||||
describe('when the About page is open', () => {
|
||||
beforeEach(async () => {
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
await atom.workspace.open('atom://about');
|
||||
aboutElement = workspaceElement.querySelector('.about');
|
||||
updateManager = main.model.state.updateManager;
|
||||
scheduler = AboutView.getScheduler();
|
||||
});
|
||||
|
||||
describe('when the updates are not supported by the platform', () => {
|
||||
beforeEach(async () => {
|
||||
atom.autoUpdater.platformSupportsUpdates.andReturn(false);
|
||||
updateManager.resetState();
|
||||
await scheduler.getNextUpdatePromise();
|
||||
});
|
||||
|
||||
it('hides the auto update UI and shows the update instructions link', async () => {
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button')
|
||||
).not.toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.about-auto-updates')
|
||||
).not.toBeVisible();
|
||||
});
|
||||
|
||||
it('opens the update instructions page when the instructions link is clicked', async () => {
|
||||
spyOn(shell, 'openExternal');
|
||||
let link = aboutElement.querySelector(
|
||||
'.app-unsupported .about-updates-instructions'
|
||||
);
|
||||
link.click();
|
||||
|
||||
let args = shell.openExternal.mostRecentCall.args;
|
||||
expect(shell.openExternal).toHaveBeenCalled();
|
||||
expect(args[0]).toContain('installing-pulsar');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when updates are supported by the platform', () => {
|
||||
beforeEach(async () => {
|
||||
atom.autoUpdater.platformSupportsUpdates.andReturn(true);
|
||||
updateManager.resetState();
|
||||
await scheduler.getNextUpdatePromise();
|
||||
});
|
||||
|
||||
it('shows the auto update UI', () => {
|
||||
expect(aboutElement.querySelector('.about-updates')).toBeVisible();
|
||||
});
|
||||
|
||||
it('shows the correct panels when the app checks for updates and there is no update available', async () => {
|
||||
expect(
|
||||
aboutElement.querySelector('.about-default-update-message')
|
||||
).toBeVisible();
|
||||
|
||||
MockUpdater.checkForUpdate();
|
||||
await scheduler.getNextUpdatePromise();
|
||||
expect(aboutElement.querySelector('.app-up-to-date')).not.toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.app-checking-for-updates')
|
||||
).toBeVisible();
|
||||
|
||||
MockUpdater.updateNotAvailable();
|
||||
await scheduler.getNextUpdatePromise();
|
||||
expect(aboutElement.querySelector('.app-up-to-date')).toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.app-checking-for-updates')
|
||||
).not.toBeVisible();
|
||||
});
|
||||
|
||||
it('shows the correct panels when the app checks for updates and encounters an error', async () => {
|
||||
expect(
|
||||
aboutElement.querySelector('.about-default-update-message')
|
||||
).toBeVisible();
|
||||
|
||||
MockUpdater.checkForUpdate();
|
||||
await scheduler.getNextUpdatePromise();
|
||||
expect(aboutElement.querySelector('.app-up-to-date')).not.toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.app-checking-for-updates')
|
||||
).toBeVisible();
|
||||
|
||||
spyOn(atom.autoUpdater, 'getErrorMessage').andReturn(
|
||||
'an error message'
|
||||
);
|
||||
MockUpdater.updateError();
|
||||
await scheduler.getNextUpdatePromise();
|
||||
expect(aboutElement.querySelector('.app-update-error')).toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.app-error-message').textContent
|
||||
).toBe('an error message');
|
||||
expect(
|
||||
aboutElement.querySelector('.app-checking-for-updates')
|
||||
).not.toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').disabled
|
||||
).toBe(false);
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').textContent
|
||||
).toBe('Check now');
|
||||
});
|
||||
|
||||
it('shows the correct panels and button states when the app checks for updates and an update is downloaded', async () => {
|
||||
expect(
|
||||
aboutElement.querySelector('.about-default-update-message')
|
||||
).toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').disabled
|
||||
).toBe(false);
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').textContent
|
||||
).toBe('Check now');
|
||||
|
||||
MockUpdater.checkForUpdate();
|
||||
await scheduler.getNextUpdatePromise();
|
||||
|
||||
expect(aboutElement.querySelector('.app-up-to-date')).not.toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.app-checking-for-updates')
|
||||
).toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').disabled
|
||||
).toBe(true);
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').textContent
|
||||
).toBe('Check now');
|
||||
|
||||
MockUpdater.downloadUpdate();
|
||||
await scheduler.getNextUpdatePromise();
|
||||
expect(
|
||||
aboutElement.querySelector('.app-checking-for-updates')
|
||||
).not.toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.app-downloading-update')
|
||||
).toBeVisible();
|
||||
// TODO: at some point it would be nice to be able to cancel an update download, and then this would be a cancel button
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').disabled
|
||||
).toBe(true);
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').textContent
|
||||
).toBe('Check now');
|
||||
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0');
|
||||
await scheduler.getNextUpdatePromise();
|
||||
expect(
|
||||
aboutElement.querySelector('.app-downloading-update')
|
||||
).not.toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.app-update-available-to-install')
|
||||
).toBeVisible();
|
||||
|
||||
expect(
|
||||
aboutElement.querySelector(
|
||||
'.app-update-available-to-install .about-updates-version'
|
||||
).textContent
|
||||
).toBe('42.0.0');
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').disabled
|
||||
).toBe(false);
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').textContent
|
||||
).toBe('Restart and install');
|
||||
});
|
||||
|
||||
it('opens the release notes for the downloaded release when the release notes link are clicked', async () => {
|
||||
MockUpdater.finishDownloadingUpdate('1.2.3');
|
||||
await scheduler.getNextUpdatePromise();
|
||||
|
||||
spyOn(shell, 'openExternal');
|
||||
let link = aboutElement.querySelector(
|
||||
'.app-update-available-to-install .about-updates-release-notes'
|
||||
);
|
||||
link.click();
|
||||
|
||||
let args = shell.openExternal.mostRecentCall.args;
|
||||
expect(shell.openExternal).toHaveBeenCalled();
|
||||
expect(args[0]).toContain('/v1.2.3');
|
||||
});
|
||||
|
||||
it('executes checkForUpdate() when the check for update button is clicked', () => {
|
||||
let button = aboutElement.querySelector('.about-update-action-button');
|
||||
button.click();
|
||||
expect(atom.autoUpdater.checkForUpdate).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('executes restartAndInstallUpdate() when the restart and install button is clicked', async () => {
|
||||
spyOn(atom.autoUpdater, 'restartAndInstallUpdate');
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0');
|
||||
await scheduler.getNextUpdatePromise();
|
||||
|
||||
let button = aboutElement.querySelector('.about-update-action-button');
|
||||
button.click();
|
||||
expect(atom.autoUpdater.restartAndInstallUpdate).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("starts in the same state as atom's AutoUpdateManager", async () => {
|
||||
atom.autoUpdater.getState.andReturn('downloading');
|
||||
updateManager.resetState();
|
||||
|
||||
await scheduler.getNextUpdatePromise();
|
||||
expect(
|
||||
aboutElement.querySelector('.app-checking-for-updates')
|
||||
).not.toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.app-downloading-update')
|
||||
).toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').disabled
|
||||
).toBe(true);
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').textContent
|
||||
).toBe('Check now');
|
||||
});
|
||||
|
||||
// describe('when core.automaticallyUpdate is toggled', () => {
|
||||
// beforeEach(async () => {
|
||||
// expect(atom.config.get('core.automaticallyUpdate')).toBe(true);
|
||||
// atom.autoUpdater.checkForUpdate.reset();
|
||||
// });
|
||||
//
|
||||
// it('shows the auto update UI', async () => {
|
||||
// expect(
|
||||
// aboutElement.querySelector('.about-auto-updates input').checked
|
||||
// ).toBe(true);
|
||||
// expect(
|
||||
// aboutElement.querySelector('.about-default-update-message')
|
||||
// ).toBeVisible();
|
||||
// expect(
|
||||
// aboutElement.querySelector('.about-default-update-message')
|
||||
// .textContent
|
||||
// ).toBe('Pulsar will check for updates automatically');
|
||||
//
|
||||
// atom.config.set('core.automaticallyUpdate', false);
|
||||
// await scheduler.getNextUpdatePromise();
|
||||
//
|
||||
// expect(
|
||||
// aboutElement.querySelector('.about-auto-updates input').checked
|
||||
// ).toBe(false);
|
||||
// expect(
|
||||
// aboutElement.querySelector('.about-default-update-message')
|
||||
// ).toBeVisible();
|
||||
// expect(
|
||||
// aboutElement.querySelector('.about-default-update-message')
|
||||
// .textContent
|
||||
// ).toBe('Automatic updates are disabled please check manually');
|
||||
// });
|
||||
//
|
||||
// it('updates config and the UI when the checkbox is used to toggle', async () => {
|
||||
// expect(
|
||||
// aboutElement.querySelector('.about-auto-updates input').checked
|
||||
// ).toBe(true);
|
||||
//
|
||||
// aboutElement.querySelector('.about-auto-updates input').click();
|
||||
// await scheduler.getNextUpdatePromise();
|
||||
//
|
||||
// expect(atom.config.get('core.automaticallyUpdate')).toBe(false);
|
||||
// expect(
|
||||
// aboutElement.querySelector('.about-auto-updates input').checked
|
||||
// ).toBe(false);
|
||||
// expect(
|
||||
// aboutElement.querySelector('.about-default-update-message')
|
||||
// ).toBeVisible();
|
||||
// expect(
|
||||
// aboutElement.querySelector('.about-default-update-message')
|
||||
// .textContent
|
||||
// ).toBe('Automatic updates are disabled please check manually');
|
||||
//
|
||||
// aboutElement.querySelector('.about-auto-updates input').click();
|
||||
// await scheduler.getNextUpdatePromise();
|
||||
//
|
||||
// expect(atom.config.get('core.automaticallyUpdate')).toBe(true);
|
||||
// expect(
|
||||
// aboutElement.querySelector('.about-auto-updates input').checked
|
||||
// ).toBe(true);
|
||||
// expect(
|
||||
// aboutElement.querySelector('.about-default-update-message')
|
||||
// ).toBeVisible();
|
||||
// expect(
|
||||
// aboutElement.querySelector('.about-default-update-message')
|
||||
// .textContent
|
||||
// ).toBe('Pulsar will check for updates automatically');
|
||||
// });
|
||||
//
|
||||
// describe('checking for updates', function() {
|
||||
// afterEach(() => {
|
||||
// this.updateView = null;
|
||||
// });
|
||||
//
|
||||
// it('checks for update when the about page is shown', () => {
|
||||
// expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled();
|
||||
//
|
||||
// this.updateView = new UpdateView({
|
||||
// updateManager: updateManager,
|
||||
// availableVersion: '9999.0.0',
|
||||
// viewUpdateReleaseNotes: () => {}
|
||||
// });
|
||||
//
|
||||
// expect(atom.autoUpdater.checkForUpdate).toHaveBeenCalled();
|
||||
// });
|
||||
//
|
||||
// it('does not check for update when the about page is shown and the update manager is not in the idle state', () => {
|
||||
// atom.autoUpdater.getState.andReturn('downloading');
|
||||
// updateManager.resetState();
|
||||
// expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled();
|
||||
//
|
||||
// this.updateView = new UpdateView({
|
||||
// updateManager: updateManager,
|
||||
// availableVersion: '9999.0.0',
|
||||
// viewUpdateReleaseNotes: () => {}
|
||||
// });
|
||||
//
|
||||
// expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled();
|
||||
// });
|
||||
//
|
||||
// it('does not check for update when the about page is shown and auto updates are turned off', () => {
|
||||
// atom.config.set('core.automaticallyUpdate', false);
|
||||
// expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled();
|
||||
//
|
||||
// this.updateView = new UpdateView({
|
||||
// updateManager: updateManager,
|
||||
// availableVersion: '9999.0.0',
|
||||
// viewUpdateReleaseNotes: () => {}
|
||||
// });
|
||||
//
|
||||
// expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled();
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the About page is not open and an update is downloaded', () => {
|
||||
it('should display the new version when it is opened', async () => {
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0');
|
||||
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
await atom.workspace.open('atom://about');
|
||||
aboutElement = workspaceElement.querySelector('.about');
|
||||
updateManager = main.model.state.updateManager;
|
||||
scheduler = AboutView.getScheduler();
|
||||
|
||||
expect(
|
||||
aboutElement.querySelector('.app-update-available-to-install')
|
||||
).toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector(
|
||||
'.app-update-available-to-install .about-updates-version'
|
||||
).textContent
|
||||
).toBe('42.0.0');
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').disabled
|
||||
).toBe(false);
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').textContent
|
||||
).toBe('Restart and install');
|
||||
});
|
||||
});
|
||||
});
|
@ -94,7 +94,7 @@
|
||||
|
||||
.about-updates {
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
//max-width: 510px;
|
||||
}
|
||||
|
||||
.about-updates-box {
|
||||
@ -102,14 +102,14 @@
|
||||
align-items: center;
|
||||
padding: @component-padding;
|
||||
border: 1px solid @base-border-color;
|
||||
border-radius: @component-border-radius * 2;
|
||||
border-radius: (@component-border-radius * 2);
|
||||
background-color: @background-color-highlight;
|
||||
}
|
||||
|
||||
.about-updates-status {
|
||||
flex: 1;
|
||||
margin-left: .5em;
|
||||
text-align: left;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.about-updates-item,
|
||||
|
@ -242,12 +242,17 @@
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.syntax--punctuation.syntax--definition.syntax--list-item {
|
||||
color: #C6C5FE;
|
||||
}
|
||||
|
||||
.syntax--variable.syntax--list,
|
||||
.syntax--support.syntax--quote {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.syntax--link .syntax--entity {
|
||||
.syntax--link .syntax--entity,
|
||||
.syntax--meta.syntax--link.syntax--text {
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
|
@ -202,11 +202,6 @@
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
// - item
|
||||
&.syntax--list {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
// > quote
|
||||
&.syntax--quote {
|
||||
color: #555;
|
||||
@ -226,6 +221,14 @@
|
||||
&.syntax--alt {
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
&.syntax--bold {
|
||||
font-style: bold;
|
||||
}
|
||||
|
||||
&.syntax--italic {
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
// /* comment */
|
||||
|
@ -4,7 +4,7 @@ atom-text-editor[mini] {
|
||||
border: 1px solid @input-border-color;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
|
||||
border-radius: @component-border-radius;
|
||||
padding-left: @component-padding/2;
|
||||
padding-left: (@component-padding/2);
|
||||
|
||||
.cursor { border-color: #fff; }
|
||||
.selection .region { background-color: lighten(@input-background-color, 10%); }
|
||||
|
@ -91,16 +91,16 @@
|
||||
.select-list.popover-list {
|
||||
background-color: @overlay-background-color;
|
||||
box-shadow: 0 0 10px @base-border-color;
|
||||
padding: @component-padding/2;
|
||||
padding: (@component-padding/2);
|
||||
border-radius: @component-border-radius;
|
||||
border: 1px solid @overlay-border-color;
|
||||
|
||||
atom-text-editor {
|
||||
margin-bottom: @component-padding/2;
|
||||
margin-bottom: (@component-padding/2);
|
||||
}
|
||||
|
||||
.list-group li {
|
||||
padding-left: @component-padding/2;
|
||||
padding-left: (@component-padding/2);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ atom-panel.modal, .overlay {
|
||||
padding: @component-padding;
|
||||
border-bottom: 1px solid @overlay-background-color;
|
||||
|
||||
&.two-lines { padding: @component-padding/2 @component-padding; }
|
||||
&.two-lines { padding: (@component-padding/2) @component-padding; }
|
||||
|
||||
.status.icon {
|
||||
float: right;
|
||||
|
@ -8,7 +8,7 @@
|
||||
@tab-max-width: 160px;
|
||||
|
||||
.tab-bar {
|
||||
height: @tab-height + @tab-top-padding + @tab-bottom-border-height;
|
||||
height: (@tab-height + @tab-top-padding + @tab-bottom-border-height);
|
||||
background: @tab-bar-background-color;
|
||||
box-shadow: inset 0 -8px 8px -4px rgba(0,0,0, .15);
|
||||
padding: 0 10px 0 25px;
|
||||
@ -75,7 +75,7 @@
|
||||
|
||||
&.modified:not(:hover) .close-icon {
|
||||
right: 0;
|
||||
top: @tab-height/2 - @modified-icon-width/2 + 1px;
|
||||
top: (@tab-height/2 - @modified-icon-width/2 + 1px);
|
||||
width: @modified-icon-width;
|
||||
height: @modified-icon-width;
|
||||
}
|
||||
@ -99,13 +99,13 @@
|
||||
box-shadow: inset -1px 1px 0 @tab-border-color, 4px -4px 4px rgba(0,0,0,.1);
|
||||
|
||||
.close-icon {
|
||||
line-height: @tab-height - 1px;
|
||||
line-height: (@tab-height - 1px);
|
||||
color: @text-color;
|
||||
}
|
||||
|
||||
&, &:before, &:after {
|
||||
background-image: -webkit-linear-gradient(top, lighten(@tab-background-color-active, 7%), @tab-background-color-active);
|
||||
height: @tab-height + 1px;
|
||||
height: (@tab-height + 1px);
|
||||
}
|
||||
|
||||
&:before {
|
||||
@ -130,13 +130,13 @@
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
height: @tab-height + @tab-top-padding + @tab-bottom-border-height;
|
||||
height: (@tab-height + @tab-top-padding + @tab-bottom-border-height);
|
||||
pointer-events: none;
|
||||
&:before {
|
||||
margin-left: -9px; // center between tabs
|
||||
}
|
||||
&:after {
|
||||
top: @tab-height + @tab-top-padding + @tab-bottom-border-height - 2px;
|
||||
top: (@tab-height + @tab-top-padding + @tab-bottom-border-height - 2px);
|
||||
margin-left: -10px; // center between tabs
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,8 @@
|
||||
border-radius: @component-border-radius;
|
||||
margin-left: @component-icon-padding;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: @font-size - 1px;
|
||||
padding: @component-padding / 2;
|
||||
font-size: (@font-size - 1px);
|
||||
padding: (@component-padding / 2);
|
||||
}
|
||||
|
||||
.badge {
|
||||
|
@ -170,9 +170,14 @@
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.syntax--variable.syntax--list {
|
||||
.syntax--variable.syntax--list,
|
||||
.syntax--punctuation.syntax--definition.syntax--list-item {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.syntax--meta.syntax--link.syntax--text {
|
||||
color: #555;
|
||||
}
|
||||
}
|
||||
|
||||
.syntax--markdown {
|
||||
|
@ -190,6 +190,14 @@
|
||||
&.syntax--link {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
&.syntax--bold {
|
||||
font-style: bold;
|
||||
}
|
||||
|
||||
&.syntax--italic {
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
// /* comment */
|
||||
|
@ -5,7 +5,7 @@ atom-text-editor[mini] {
|
||||
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
|
||||
border-radius: @component-border-radius;
|
||||
padding-left: @component-padding/2;
|
||||
padding-left: (@component-padding/2);
|
||||
|
||||
.cursor { border-color: #000; }
|
||||
.selection .region { background-color: rgba(0, 0, 0, .2); }
|
||||
|
@ -90,16 +90,16 @@
|
||||
.select-list.popover-list {
|
||||
background-color: @overlay-background-color;
|
||||
box-shadow: 0 0 10px @base-border-color;
|
||||
padding: @component-padding/2;
|
||||
padding: (@component-padding/2);
|
||||
border-radius: @component-border-radius;
|
||||
border: 1px solid @overlay-border-color;
|
||||
|
||||
atom-text-editor {
|
||||
margin-bottom: @component-padding/2;
|
||||
margin-bottom: (@component-padding/2);
|
||||
}
|
||||
|
||||
.list-group li {
|
||||
padding-left: @component-padding/2;
|
||||
padding-left: (@component-padding/2);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ atom-panel.modal, .overlay {
|
||||
border-right: 1px solid @inset-panel-border-color;
|
||||
&:last-child { border-bottom: 1px solid @inset-panel-border-color; }
|
||||
|
||||
&.two-lines { padding: @component-padding/2 @component-padding; }
|
||||
&.two-lines { padding: (@component-padding/2) @component-padding; }
|
||||
&.selected {
|
||||
color: @text-color;
|
||||
background-color: @background-color-highlight;
|
||||
|
@ -40,7 +40,7 @@ atom-panel, .tool-panel {
|
||||
|
||||
.panel-heading {
|
||||
border-bottom: none;
|
||||
padding: @component-padding - 2px @component-padding;
|
||||
padding: (@component-padding - 2px) @component-padding;
|
||||
|
||||
background-color: transparent;
|
||||
background-image: -webkit-linear-gradient(@panel-heading-background-color, darken(@panel-heading-background-color, 10%));
|
||||
|
@ -8,7 +8,7 @@
|
||||
@tab-max-width: 160px;
|
||||
|
||||
.tab-bar {
|
||||
height: @tab-height + @tab-top-padding + @tab-bottom-border-height;
|
||||
height: (@tab-height + @tab-top-padding + @tab-bottom-border-height);
|
||||
background-image: -webkit-linear-gradient(top, @tab-bar-background-color, lighten(@tab-bar-background-color, 9%));
|
||||
box-shadow: inset 0 -8px 8px -4px rgba(0,0,0, .15);
|
||||
padding: 0 10px 0 25px;
|
||||
@ -77,7 +77,7 @@
|
||||
|
||||
&.modified:not(:hover) .close-icon {
|
||||
right: 0;
|
||||
top: @tab-height/2 - @modified-icon-width/2 + 1px;
|
||||
top: (@tab-height/2 - @modified-icon-width/2 + 1px);
|
||||
width: @modified-icon-width;
|
||||
height: @modified-icon-width;
|
||||
}
|
||||
@ -89,8 +89,8 @@
|
||||
.title {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin-top: -@tab-top-padding - 1px;
|
||||
padding-top: @tab-top-padding + 1px;
|
||||
margin-top: (-@tab-top-padding - 1px);
|
||||
padding-top: (@tab-top-padding + 1px);
|
||||
padding-right: 10px;
|
||||
}
|
||||
}
|
||||
@ -100,13 +100,13 @@
|
||||
color: @text-color-highlight;
|
||||
|
||||
.close-icon {
|
||||
line-height: @tab-height - 1px;
|
||||
line-height: (@tab-height - 1px);
|
||||
color: @text-color;
|
||||
}
|
||||
|
||||
&, &:before, &:after {
|
||||
background: @tab-background-color-active;
|
||||
height: @tab-height + 1px;
|
||||
height: (@tab-height + 1px);
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
@ -124,13 +124,13 @@
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
height: @tab-height + @tab-top-padding + @tab-bottom-border-height;
|
||||
height: (@tab-height + @tab-top-padding + @tab-bottom-border-height);
|
||||
pointer-events: none;
|
||||
&:before {
|
||||
margin-left: -9px; // center between tabs
|
||||
}
|
||||
&:after {
|
||||
top: @tab-height + @tab-top-padding + @tab-bottom-border-height - 2px;
|
||||
top: (@tab-height + @tab-top-padding + @tab-bottom-border-height - 2px);
|
||||
margin-left: -10px; // center between tabs
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,8 @@
|
||||
border-radius: @component-border-radius;
|
||||
margin-left: @component-icon-padding;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: @font-size - 1px;
|
||||
padding: @component-padding / 2;
|
||||
font-size: (@font-size - 1px);
|
||||
padding: (@component-padding / 2);
|
||||
}
|
||||
|
||||
.badge {
|
||||
|
2
packages/autocomplete-atom-api/.gitignore
vendored
Normal file
2
packages/autocomplete-atom-api/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
npm-debug.log
|
5
packages/autocomplete-atom-api/README.md
Normal file
5
packages/autocomplete-atom-api/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Atom API Autocomplete package
|
||||
|
||||
Provides autocompletions for properties and methods available from the `atom.` global.
|
||||
|
||||
![autocomplete-atom-api](https://cloud.githubusercontent.com/assets/69169/7211322/9c402ea2-e50e-11e4-9d74-56ab91aa101d.gif)
|
8817
packages/autocomplete-atom-api/completions.json
Normal file
8817
packages/autocomplete-atom-api/completions.json
Normal file
File diff suppressed because it is too large
Load Diff
7
packages/autocomplete-atom-api/lib/main.js
Normal file
7
packages/autocomplete-atom-api/lib/main.js
Normal file
@ -0,0 +1,7 @@
|
||||
const provider = require('./provider');
|
||||
|
||||
module.exports = {
|
||||
activate() { return provider.load(); },
|
||||
|
||||
getProvider() { return provider; }
|
||||
};
|
125
packages/autocomplete-atom-api/lib/provider.js
Normal file
125
packages/autocomplete-atom-api/lib/provider.js
Normal file
@ -0,0 +1,125 @@
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const CLASSES = require('../completions.json');
|
||||
|
||||
const propertyPrefixPattern = /(?:^|\[|\(|,|=|:|\s)\s*(atom\.(?:[a-zA-Z]+\.?){0,2})$/;
|
||||
|
||||
module.exports = {
|
||||
selector: '.source.coffee, .source.js',
|
||||
filterSuggestions: true,
|
||||
|
||||
getSuggestions({bufferPosition, editor}) {
|
||||
if (!this.isEditingAnAtomPackageFile(editor)) { return; }
|
||||
const line = editor.getTextInRange([[bufferPosition.row, 0], bufferPosition]);
|
||||
return this.getCompletions(line);
|
||||
},
|
||||
|
||||
load() {
|
||||
this.loadCompletions();
|
||||
atom.project.onDidChangePaths(() => this.scanProjectDirectories());
|
||||
return this.scanProjectDirectories();
|
||||
},
|
||||
|
||||
scanProjectDirectories() {
|
||||
this.packageDirectories = [];
|
||||
atom.project.getDirectories().forEach(directory => {
|
||||
if (directory == null) { return; }
|
||||
this.readMetadata(directory, (error, metadata) => {
|
||||
if (this.isAtomPackage(metadata) || this.isAtomCore(metadata)) {
|
||||
this.packageDirectories.push(directory);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
readMetadata(directory, callback) {
|
||||
fs.readFile(path.join(directory.getPath(), 'package.json'), function(error, contents) {
|
||||
let metadata;
|
||||
if (error == null) {
|
||||
try {
|
||||
metadata = JSON.parse(contents);
|
||||
} catch (parseError) {
|
||||
error = parseError;
|
||||
}
|
||||
}
|
||||
return callback(error, metadata);
|
||||
});
|
||||
},
|
||||
|
||||
isAtomPackage(metadata) {
|
||||
return metadata?.engines?.atom?.length > 0;
|
||||
},
|
||||
|
||||
isAtomCore(metadata) {
|
||||
return metadata?.name === 'atom';
|
||||
},
|
||||
|
||||
isEditingAnAtomPackageFile(editor) {
|
||||
const editorPath = editor.getPath();
|
||||
if (editorPath != null) {
|
||||
const parsedPath = path.parse(editorPath);
|
||||
const basename = path.basename(parsedPath.dir);
|
||||
if ((basename === '.atom') || (basename === '.pulsar')) {
|
||||
if ((parsedPath.base === 'init.coffee') || (parsedPath.base === 'init.js')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let directory of (this.packageDirectories != null ? this.packageDirectories : [])) {
|
||||
if (directory.contains(editorPath)) { return true; }
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
loadCompletions() {
|
||||
if (this.completions == null) { this.completions = {}; }
|
||||
return this.loadProperty('atom', 'AtomEnvironment', CLASSES);
|
||||
},
|
||||
|
||||
getCompletions(line) {
|
||||
const completions = [];
|
||||
const match = propertyPrefixPattern.exec(line)?.[1];
|
||||
if (!match) { return completions; }
|
||||
|
||||
let segments = match.split('.');
|
||||
const prefix = segments.pop() ?? '';
|
||||
segments = segments.filter(segment => segment);
|
||||
const property = segments[segments.length - 1];
|
||||
const propertyCompletions = this.completions[property]?.completions != null ? this.completions[property]?.completions : [];
|
||||
for (let completion of propertyCompletions) {
|
||||
if (!prefix || firstCharsEqual(completion.name, prefix)) {
|
||||
completions.push(clone(completion));
|
||||
}
|
||||
}
|
||||
return completions;
|
||||
},
|
||||
|
||||
getPropertyClass(name) {
|
||||
return atom[name]?.constructor?.name;
|
||||
},
|
||||
|
||||
loadProperty(propertyName, className, classes, parent) {
|
||||
const classCompletions = classes[className];
|
||||
if (classCompletions == null) { return; }
|
||||
|
||||
this.completions[propertyName] = {completions: []};
|
||||
|
||||
for (let completion of classCompletions) {
|
||||
this.completions[propertyName].completions.push(completion);
|
||||
if (completion.type === 'property') {
|
||||
const propertyClass = this.getPropertyClass(completion.name);
|
||||
this.loadProperty(completion.name, propertyClass, classes);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const clone = function(obj) {
|
||||
const newObj = {};
|
||||
for (let k in obj) { const v = obj[k]; newObj[k] = v; }
|
||||
return newObj;
|
||||
};
|
||||
|
||||
const firstCharsEqual = (str1, str2) => str1[0].toLowerCase() === str2[0].toLowerCase();
|
1104
packages/autocomplete-atom-api/package-lock.json
generated
Normal file
1104
packages/autocomplete-atom-api/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
packages/autocomplete-atom-api/package.json
Normal file
22
packages/autocomplete-atom-api/package.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "autocomplete-atom-api",
|
||||
"version": "0.10.7",
|
||||
"description": "Pulsar API autocompletions",
|
||||
"main": "./lib/main",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/pulsar-edit/pulsar",
|
||||
"engines": {
|
||||
"atom": ">=0.174.0 <2.0.0"
|
||||
},
|
||||
"providedServices": {
|
||||
"autocomplete.provider": {
|
||||
"versions": {
|
||||
"2.0.0": "getProvider"
|
||||
}
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"request": "^2.53.0",
|
||||
"temp": "^0.8.1"
|
||||
}
|
||||
}
|
6
packages/autocomplete-atom-api/spec/fixtures/package.json
vendored
Normal file
6
packages/autocomplete-atom-api/spec/fixtures/package.json
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "a-package",
|
||||
"engines": {
|
||||
"atom": "*"
|
||||
}
|
||||
}
|
119
packages/autocomplete-atom-api/spec/provider-spec.js
Normal file
119
packages/autocomplete-atom-api/spec/provider-spec.js
Normal file
@ -0,0 +1,119 @@
|
||||
const temp = require('temp');
|
||||
|
||||
describe("Atom API autocompletions", () => {
|
||||
let [editor, provider] = [];
|
||||
|
||||
const getCompletions = function() {
|
||||
const cursor = editor.getLastCursor();
|
||||
const start = cursor.getBeginningOfCurrentWordBufferPosition();
|
||||
const end = cursor.getBufferPosition();
|
||||
const prefix = editor.getTextInRange([start, end]);
|
||||
const request = {
|
||||
editor,
|
||||
bufferPosition: end,
|
||||
scopeDescriptor: cursor.getScopeDescriptor(),
|
||||
prefix
|
||||
};
|
||||
return provider.getSuggestions(request);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
waitsForPromise(() => atom.packages.activatePackage('autocomplete-atom-api'));
|
||||
runs(() => provider = atom.packages.getActivePackage('autocomplete-atom-api').mainModule.getProvider());
|
||||
waitsFor(() => Object.keys(provider.completions).length > 0);
|
||||
waitsFor(() => provider.packageDirectories?.length > 0);
|
||||
waitsForPromise(() => atom.workspace.open('test.js'));
|
||||
runs(() => editor = atom.workspace.getActiveTextEditor());
|
||||
});
|
||||
|
||||
it("only includes completions in files that are in an Atom package or Atom core", () => {
|
||||
const emptyProjectPath = temp.mkdirSync('atom-project-');
|
||||
atom.project.setPaths([emptyProjectPath]);
|
||||
|
||||
waitsForPromise(() => atom.workspace.open('empty.js'));
|
||||
|
||||
runs(() => {
|
||||
expect(provider.packageDirectories.length).toBe(0);
|
||||
editor = atom.workspace.getActiveTextEditor();
|
||||
editor.setText('atom.');
|
||||
editor.setCursorBufferPosition([0, Infinity]);
|
||||
|
||||
expect(getCompletions()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
it("only includes completions in .atom/init", () => {
|
||||
const emptyProjectPath = temp.mkdirSync('some-guy');
|
||||
atom.project.setPaths([emptyProjectPath]);
|
||||
|
||||
waitsForPromise(() => atom.workspace.open('.atom/init.coffee'));
|
||||
|
||||
runs(() => {
|
||||
expect(provider.packageDirectories.length).toBe(0);
|
||||
editor = atom.workspace.getActiveTextEditor();
|
||||
editor.setText('atom.');
|
||||
editor.setCursorBufferPosition([0, Infinity]);
|
||||
|
||||
expect(getCompletions()).not.toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
it("does not fail when no editor path", () => {
|
||||
const emptyProjectPath = temp.mkdirSync('some-guy');
|
||||
atom.project.setPaths([emptyProjectPath]);
|
||||
|
||||
waitsForPromise(() => atom.workspace.open());
|
||||
|
||||
runs(() => {
|
||||
expect(provider.packageDirectories.length).toBe(0);
|
||||
editor = atom.workspace.getActiveTextEditor();
|
||||
editor.setText('atom.');
|
||||
editor.setCursorBufferPosition([0, Infinity]);
|
||||
expect(getCompletions()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
it("includes properties and functions on the atom global", () => {
|
||||
editor.setText('atom.');
|
||||
editor.setCursorBufferPosition([0, Infinity]);
|
||||
|
||||
expect(getCompletions().length).toBe(53);
|
||||
expect(getCompletions()[0].text).toBe('clipboard');
|
||||
|
||||
editor.setText('var c = atom.');
|
||||
editor.setCursorBufferPosition([0, Infinity]);
|
||||
|
||||
expect(getCompletions().length).toBe(53);
|
||||
expect(getCompletions()[0].text).toBe('clipboard');
|
||||
|
||||
editor.setText('atom.c');
|
||||
editor.setCursorBufferPosition([0, Infinity]);
|
||||
expect(getCompletions().length).toBe(7);
|
||||
expect(getCompletions()[0].text).toBe('clipboard');
|
||||
expect(getCompletions()[0].type).toBe('property');
|
||||
expect(getCompletions()[0].leftLabel).toBe('Clipboard');
|
||||
expect(getCompletions()[1].text).toBe('commands');
|
||||
expect(getCompletions()[2].text).toBe('config');
|
||||
expect(getCompletions()[6].snippet).toBe('confirm(${1:options})');
|
||||
expect(getCompletions()[6].type).toBe('method');
|
||||
expect(getCompletions()[6].leftLabel).toBe('Number');
|
||||
expect(getCompletions()[6].descriptionMoreURL).toBe('https://atom.io/docs/api/latest/AtomEnvironment#instance-confirm');
|
||||
});
|
||||
|
||||
it("includes methods on atom global properties", () => {
|
||||
editor.setText('atom.clipboard.');
|
||||
editor.setCursorBufferPosition([0, Infinity]);
|
||||
|
||||
expect(getCompletions().length).toBe(3);
|
||||
expect(getCompletions()[0].text).toBe('read()');
|
||||
expect(getCompletions()[1].text).toBe('readWithMetadata()');
|
||||
expect(getCompletions()[2].snippet).toBe('write(${1:text}, ${2:metadata})');
|
||||
|
||||
editor.setText('atom.clipboard.rea');
|
||||
editor.setCursorBufferPosition([0, Infinity]);
|
||||
|
||||
expect(getCompletions().length).toBe(2);
|
||||
expect(getCompletions()[0].text).toBe('read()');
|
||||
expect(getCompletions()[1].text).toBe('readWithMetadata()');
|
||||
});
|
||||
});
|
84
packages/autocomplete-atom-api/update.coffee
Normal file
84
packages/autocomplete-atom-api/update.coffee
Normal file
@ -0,0 +1,84 @@
|
||||
# Run this to update the static list of properties stored in the
|
||||
# completions.json file at the root of this repository.
|
||||
|
||||
fs = require 'fs'
|
||||
request = require 'request'
|
||||
|
||||
requestOptions =
|
||||
url: 'https://api.github.com/repos/atom/atom/releases/latest'
|
||||
json: true
|
||||
headers:
|
||||
'User-Agent': 'agent'
|
||||
|
||||
request requestOptions, (error, response, release) ->
|
||||
if error?
|
||||
console.error(error.message)
|
||||
return process.exit(1)
|
||||
|
||||
[apiAsset] = release.assets.filter ({name}) -> name is 'atom-api.json'
|
||||
|
||||
unless apiAsset?.browser_download_url
|
||||
console.error('No atom-api.json asset found in latest release')
|
||||
return process.exit(1)
|
||||
|
||||
apiRequestOptions =
|
||||
json: true
|
||||
url: apiAsset.browser_download_url
|
||||
|
||||
request apiRequestOptions, (error, response, atomApi) ->
|
||||
if error?
|
||||
console.error(error.message)
|
||||
return process.exit(1)
|
||||
|
||||
{classes} = atomApi
|
||||
|
||||
publicClasses = {}
|
||||
for name, {instanceProperties, instanceMethods} of classes
|
||||
pluckPropertyAttributes = convertPropertyToSuggestion.bind(this, name)
|
||||
pluckMethodAttributes = convertMethodToSuggestion.bind(this, name)
|
||||
properties = instanceProperties.filter(isVisible).map(pluckPropertyAttributes).sort(textComparator)
|
||||
methods = instanceMethods.filter(isVisible).map(pluckMethodAttributes).sort(textComparator)
|
||||
|
||||
if properties?.length > 0 or methods.length > 0
|
||||
publicClasses[name] = properties.concat(methods)
|
||||
|
||||
fs.writeFileSync('completions.json', JSON.stringify(publicClasses, null, ' '))
|
||||
|
||||
isVisible = ({visibility}) ->
|
||||
visibility in ['Essential', 'Extended', 'Public']
|
||||
|
||||
convertMethodToSuggestion = (className, method) ->
|
||||
{name, summary, returnValues} = method
|
||||
args = method['arguments']
|
||||
|
||||
snippets = []
|
||||
if args?.length
|
||||
for arg, i in args
|
||||
snippets.push("${#{i+1}:#{arg.name}}")
|
||||
|
||||
text = null
|
||||
snippet = null
|
||||
if snippets.length
|
||||
snippet = "#{name}(#{snippets.join(', ')})"
|
||||
else
|
||||
text = "#{name}()"
|
||||
|
||||
returnValue = returnValues?[0]?.type
|
||||
description = summary
|
||||
descriptionMoreURL = getDocLink(className, name)
|
||||
{name, text, snippet, description, descriptionMoreURL, leftLabel: returnValue, type: 'method'}
|
||||
|
||||
convertPropertyToSuggestion = (className, {name, summary}) ->
|
||||
text = name
|
||||
returnValue = summary?.match(/\{(\w+)\}/)?[1]
|
||||
description = summary
|
||||
descriptionMoreURL = getDocLink(className, name)
|
||||
{name, text, description, descriptionMoreURL, leftLabel: returnValue, type: 'property'}
|
||||
|
||||
getDocLink = (className, instanceName) ->
|
||||
"https://atom.io/docs/api/latest/#{className}#instance-#{instanceName}"
|
||||
|
||||
textComparator = (a, b) ->
|
||||
return 1 if a.name > b.name
|
||||
return -1 if a.name < b.name
|
||||
0
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +0,0 @@
|
||||
provider = require './provider'
|
||||
|
||||
module.exports =
|
||||
activate: ->
|
||||
|
||||
getProvider: -> provider
|
7
packages/autocomplete-css/lib/main.js
Normal file
7
packages/autocomplete-css/lib/main.js
Normal file
@ -0,0 +1,7 @@
|
||||
const provider = require('./provider.js');
|
||||
|
||||
module.exports = {
|
||||
activate() {},
|
||||
|
||||
getProvider() { return provider; }
|
||||
};
|
@ -1,318 +0,0 @@
|
||||
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()
|
380
packages/autocomplete-css/lib/provider.js
Normal file
380
packages/autocomplete-css/lib/provider.js
Normal file
@ -0,0 +1,380 @@
|
||||
const COMPLETIONS = require('../completions.json');
|
||||
|
||||
const firstInlinePropertyNameWithColonPattern = /{\s*(\S+)\s*:/; // .example { display: }
|
||||
const inlinePropertyNameWithColonPattern = /(?:;.+?)*;\s*(\S+)\s*:/; // .example { display: block; float: left; color: } (match the last one)
|
||||
const propertyNameWithColonPattern = /^\s*(\S+)\s*:/; // display:
|
||||
const propertyNamePrefixPattern = /[a-zA-Z]+[-a-zA-Z]*$/;
|
||||
const pseudoSelectorPrefixPattern = /:(:)?([a-z]+[a-z-]*)?$/;
|
||||
const tagSelectorPrefixPattern = /(^|\s|,)([a-z]+)?$/;
|
||||
const importantPrefixPattern = /(![a-z]+)$/;
|
||||
const 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) {
|
||||
let completions = null;
|
||||
const scopes = request.scopeDescriptor.getScopesArray();
|
||||
const isSass = hasScope(scopes, 'source.sass');
|
||||
|
||||
if (this.isCompletingValue(request)) {
|
||||
completions = this.getPropertyValueCompletions(request);
|
||||
} else if (this.isCompletingPseudoSelector(request)) {
|
||||
completions = this.getPseudoSelectorCompletions(request);
|
||||
} else {
|
||||
if (isSass && this.isCompletingNameOrTag(request)) {
|
||||
completions = this.getPropertyNameCompletions(request)
|
||||
.concat(this.getTagCompletions(request));
|
||||
} else if (!isSass && this.isCompletingName(request)) {
|
||||
completions = this.getPropertyNameCompletions(request);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isSass && this.isCompletingTagSelector(request)) {
|
||||
const tagCompletions = this.getTagCompletions(request);
|
||||
if (tagCompletions?.length) {
|
||||
if (completions == null) { completions = []; }
|
||||
completions = completions.concat(tagCompletions);
|
||||
}
|
||||
}
|
||||
|
||||
return completions;
|
||||
},
|
||||
|
||||
onDidInsertSuggestion({editor, suggestion}) {
|
||||
if (suggestion.type === 'property') { return setTimeout(this.triggerAutocomplete.bind(this, editor), 1); }
|
||||
},
|
||||
|
||||
triggerAutocomplete(editor) {
|
||||
return atom.commands.dispatch(atom.views.getView(editor), 'autocomplete-plus:activate', {activatedManually: false});
|
||||
},
|
||||
|
||||
isCompletingValue({scopeDescriptor, bufferPosition, prefix, editor}) {
|
||||
const scopes = scopeDescriptor.getScopesArray();
|
||||
|
||||
const beforePrefixBufferPosition = [bufferPosition.row, Math.max(0, bufferPosition.column - prefix.length - 1)];
|
||||
const beforePrefixScopes = editor.scopeDescriptorForBufferPosition(beforePrefixBufferPosition);
|
||||
const beforePrefixScopesArray = beforePrefixScopes.getScopesArray();
|
||||
|
||||
const previousBufferPosition = [bufferPosition.row, Math.max(0, bufferPosition.column - 1)];
|
||||
const previousScopes = editor.scopeDescriptorForBufferPosition(previousBufferPosition);
|
||||
const previousScopesArray = previousScopes.getScopesArray();
|
||||
|
||||
const inMetaPropertyList = hasScope(scopes, 'meta.property-list', ['css', 'scss', 'postcss']) && (prefix.trim() === ":");
|
||||
const inMetaSelector = hasScope(scopes, 'meta.block.inside-selector', ['css', 'scss', 'postcss']) && (prefix.trim() === ':');
|
||||
|
||||
return (inMetaPropertyList || inMetaSelector) ||
|
||||
(hasScope(previousScopesArray, 'meta.property-value', ['css', 'scss', 'postcss'])) ||
|
||||
(hasScope(scopes, 'source.sass') && (hasScope(scopes, 'meta.property-value.sass') ||
|
||||
(!hasScope(beforePrefixScopesArray, 'entity.name.tag.css') && (prefix.trim() === ":"))
|
||||
));
|
||||
},
|
||||
|
||||
isCompletingName({scopeDescriptor, bufferPosition, prefix, editor}) {
|
||||
const scopes = scopeDescriptor.getScopesArray();
|
||||
const isAtTerminator = prefix.endsWith(';');
|
||||
const isAtParentSymbol = prefix.endsWith('&');
|
||||
const isVariable = hasScope(scopes, 'variable', ['css', 'scss', 'postcss']);
|
||||
const isInPropertyList = !isAtTerminator &&
|
||||
(hasScope(scopes, 'meta.property-list') ||
|
||||
hasScope(scopes, 'meta.block.inside-selector'));
|
||||
|
||||
if (!isInPropertyList) { return false; }
|
||||
if (isAtParentSymbol || isVariable) { return false; }
|
||||
|
||||
const previousBufferPosition = [bufferPosition.row, Math.max(0, bufferPosition.column - prefix.length - 1)];
|
||||
const previousScopes = editor.scopeDescriptorForBufferPosition(previousBufferPosition);
|
||||
const previousScopesArray = previousScopes.getScopesArray();
|
||||
|
||||
if (hasScope(previousScopesArray, 'entity.other.attribute-name.class') ||
|
||||
hasScope(previousScopesArray, 'entity.other.attribute-name.id') ||
|
||||
hasScope(previousScopesArray, 'entity.other.attribute-name.parent-selector') ||
|
||||
hasScope(previousScopesArray, 'entity.name.tag.reference', ['scss', 'postcss']) ||
|
||||
hasScope(previousScopesArray, 'entity.name.tag', ['scss', 'postcss'])) { return false; }
|
||||
|
||||
const isAtBeginScopePunctuation = hasScope(scopes, 'punctuation.section.property-list.begin', ['css', 'scss', 'postcss']) || hasScope(scopes, 'punctuation.definition.property-list.begin', ['css', 'scss', 'postcss']);
|
||||
|
||||
const isAtEndScopePunctuation = hasScope(scopes, 'punctuation.section.property-list.end', ['css', 'scss', 'postcss']) || hasScope(scopes, 'punctuation.definition.property-list.end', ['css', 'scss', 'postcss']);
|
||||
|
||||
if (isAtBeginScopePunctuation) {
|
||||
// * Disallow here: `canvas,|{}`
|
||||
// * Allow here: `canvas,{| }`
|
||||
return prefix.endsWith('{');
|
||||
} else if (isAtEndScopePunctuation) {
|
||||
// * Disallow here: `canvas,{}|`
|
||||
// * Allow here: `canvas,{ |}`
|
||||
return !prefix.endsWith('}');
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
isCompletingNameOrTag({scopeDescriptor, bufferPosition, editor}) {
|
||||
const scopes = scopeDescriptor.getScopesArray();
|
||||
const prefix = this.getPropertyNamePrefix(bufferPosition, editor);
|
||||
return this.isPropertyNamePrefix(prefix) &&
|
||||
hasScope(scopes, 'meta.selector.css') &&
|
||||
!hasScope(scopes, 'entity.other.attribute-name.id.css.sass') &&
|
||||
!hasScope(scopes, 'entity.other.attribute-name.class.sass');
|
||||
},
|
||||
|
||||
isCompletingTagSelector({editor, scopeDescriptor, bufferPosition}) {
|
||||
const scopes = scopeDescriptor.getScopesArray();
|
||||
const tagSelectorPrefix = this.getTagSelectorPrefix(editor, bufferPosition);
|
||||
if (!(tagSelectorPrefix != null ? tagSelectorPrefix.length : undefined)) { return false; }
|
||||
|
||||
const previousBufferPosition = [bufferPosition.row, Math.max(0, bufferPosition.column - 1)];
|
||||
const previousScopes = editor.scopeDescriptorForBufferPosition(previousBufferPosition);
|
||||
const previousScopesArray = previousScopes.getScopesArray();
|
||||
|
||||
if (hasScope(scopes, 'meta.selector.css') || hasScope(previousScopesArray, 'meta.selector.css')) {
|
||||
return true;
|
||||
} else if (hasScope(scopes, 'source.css', ['scss', 'less', 'postcss']) || hasScope(scopes, 'source.css')) {
|
||||
return !hasScope(previousScopesArray, 'meta.property-value', ['scss', 'css', 'postcss']) &&
|
||||
!hasScope(previousScopesArray, 'support.type.property-value.css');
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
isCompletingPseudoSelector({editor, scopeDescriptor, bufferPosition}) {
|
||||
const scopes = scopeDescriptor.getScopesArray();
|
||||
let previousBufferPosition = [bufferPosition.row, Math.max(0, bufferPosition.column - 1)];
|
||||
let previousScopes = editor.scopeDescriptorForBufferPosition(previousBufferPosition);
|
||||
let previousScopesArray = previousScopes.getScopesArray();
|
||||
if ((hasScope(scopes, 'meta.selector.css') || hasScope(previousScopesArray, 'meta.selector.css')) && !hasScope(scopes, 'source.sass')) {
|
||||
return true;
|
||||
} else if (hasScope(scopes, 'source.css', ['scss', 'less', 'postcss']) || hasScope(scopes, 'source.sass')) {
|
||||
const prefix = this.getPseudoSelectorPrefix(editor, bufferPosition);
|
||||
if (prefix) {
|
||||
previousBufferPosition = [bufferPosition.row, Math.max(0, bufferPosition.column - prefix.length - 1)];
|
||||
previousScopes = editor.scopeDescriptorForBufferPosition(previousBufferPosition);
|
||||
previousScopesArray = previousScopes.getScopesArray();
|
||||
return !hasScope(previousScopesArray, 'meta.property-name.scss') &&
|
||||
!hasScope(previousScopesArray, 'meta.property-value.scss') &&
|
||||
!hasScope(previousScopesArray, 'meta.property-value.postcss') &&
|
||||
!hasScope(previousScopesArray, 'support.type.property-name.css') &&
|
||||
!hasScope(previousScopesArray, 'support.type.property-value.css') &&
|
||||
!hasScope(previousScopesArray, 'support.type.property-name.postcss');
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
isPropertyValuePrefix(prefix) {
|
||||
prefix = prefix.trim();
|
||||
return (prefix.length > 0) && (prefix !== ':');
|
||||
},
|
||||
|
||||
isPropertyNamePrefix(prefix) {
|
||||
if (prefix == null) { return false; }
|
||||
prefix = prefix.trim();
|
||||
return (prefix.length > 0) && prefix.match(/^[a-zA-Z-]+$/);
|
||||
},
|
||||
|
||||
getImportantPrefix(editor, bufferPosition) {
|
||||
const line = editor.getTextInRange([[bufferPosition.row, 0], bufferPosition]);
|
||||
let importantPrefixPatternExec = importantPrefixPattern.exec(line);
|
||||
return (typeof importantPrefixPatternExec !== "undefined" && importantPrefixPatternExec !== null) ? importantPrefixPatternExec[1] : undefined;
|
||||
},
|
||||
|
||||
getPreviousPropertyName(bufferPosition, editor) {
|
||||
let {row, column} = bufferPosition;
|
||||
while (row >= 0) {
|
||||
let line = editor.lineTextForBufferRow(row);
|
||||
if (row === bufferPosition.row) { line = line.substr(0, column); }
|
||||
let propertyName = (typeof inlinePropertyNameWithColonPattern.exec(line) !== "undefined" && inlinePropertyNameWithColonPattern.exec(line) !== null) ? inlinePropertyNameWithColonPattern.exec(line)[1] : undefined;
|
||||
if (propertyName == null) {
|
||||
propertyName = (typeof firstInlinePropertyNameWithColonPattern.exec(line) !== "undefined" && firstInlinePropertyNameWithColonPattern.exec(line) !== null) ? firstInlinePropertyNameWithColonPattern.exec(line)[1] : undefined;
|
||||
}
|
||||
if (propertyName == null) {
|
||||
propertyName = (typeof propertyNameWithColonPattern.exec(line) !== "undefined" && propertyNameWithColonPattern.exec(line) !== null) ? propertyNameWithColonPattern.exec(line)[1] : undefined;
|
||||
}
|
||||
if (propertyName) { return propertyName; }
|
||||
row--;
|
||||
}
|
||||
},
|
||||
|
||||
getPropertyValueCompletions({bufferPosition, editor, prefix, scopeDescriptor}) {
|
||||
let importantPrefix, value;
|
||||
const property = this.getPreviousPropertyName(bufferPosition, editor);
|
||||
const values = this.properties[property] != null ? this.properties[property].values : undefined;
|
||||
if (values == null) { return null; }
|
||||
|
||||
const scopes = scopeDescriptor.getScopesArray();
|
||||
const addSemicolon = !lineEndsWithSemicolon(bufferPosition, editor) && !hasScope(scopes, 'source.sass');
|
||||
|
||||
const completions = [];
|
||||
if (this.isPropertyValuePrefix(prefix)) {
|
||||
for (value of Array.from(values)) {
|
||||
if (firstCharsEqual(value, prefix)) {
|
||||
completions.push(this.buildPropertyValueCompletion(value, property, addSemicolon));
|
||||
}
|
||||
}
|
||||
} else if (!hasScope(scopes, 'keyword.other.unit.percentage.css')) { // CSS
|
||||
// Don't complete here: `width: 100%|`
|
||||
for (value of Array.from(values)) {
|
||||
completions.push(this.buildPropertyValueCompletion(value, property, addSemicolon));
|
||||
}
|
||||
}
|
||||
|
||||
if (importantPrefix = this.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`
|
||||
});
|
||||
}
|
||||
|
||||
return completions;
|
||||
},
|
||||
|
||||
buildPropertyValueCompletion(value, propertyName, addSemicolon) {
|
||||
let text = value;
|
||||
if (addSemicolon) { text += ';'; }
|
||||
|
||||
return {
|
||||
type: 'value',
|
||||
text,
|
||||
displayText: value,
|
||||
description: `${value} value for the ${propertyName} property`,
|
||||
descriptionMoreURL: `${cssDocsURL}/${propertyName}#Values`
|
||||
};
|
||||
},
|
||||
|
||||
getPropertyNamePrefix(bufferPosition, editor) {
|
||||
const line = editor.getTextInRange([[bufferPosition.row, 0], bufferPosition]);
|
||||
return (typeof propertyNamePrefixPattern.exec(line) !== "undefined" && propertyNamePrefixPattern.exec(line) !== null) ? propertyNamePrefixPattern.exec(line)[0] : undefined;
|
||||
},
|
||||
|
||||
getPropertyNameCompletions({bufferPosition, editor, scopeDescriptor, activatedManually}) {
|
||||
// Don't autocomplete property names in SASS on root level
|
||||
const scopes = scopeDescriptor.getScopesArray();
|
||||
const line = editor.getTextInRange([[bufferPosition.row, 0], bufferPosition]);
|
||||
if (hasScope(scopes, 'source.sass') && !line.match(/^(\s|\t)/)) { return []; }
|
||||
|
||||
const prefix = this.getPropertyNamePrefix(bufferPosition, editor);
|
||||
if (!activatedManually && !prefix) { return []; }
|
||||
|
||||
const completions = [];
|
||||
for (let property in this.properties) {
|
||||
const options = this.properties[property];
|
||||
if (!prefix || firstCharsEqual(property, prefix)) {
|
||||
completions.push(this.buildPropertyNameCompletion(property, prefix, options));
|
||||
}
|
||||
}
|
||||
return completions;
|
||||
},
|
||||
|
||||
buildPropertyNameCompletion(propertyName, prefix, {description}) {
|
||||
return {
|
||||
type: 'property',
|
||||
text: `${propertyName}: `,
|
||||
displayText: propertyName,
|
||||
replacementPrefix: prefix,
|
||||
description,
|
||||
descriptionMoreURL: `${cssDocsURL}/${propertyName}`
|
||||
};
|
||||
},
|
||||
|
||||
getPseudoSelectorPrefix(editor, bufferPosition) {
|
||||
const line = editor.getTextInRange([[bufferPosition.row, 0], bufferPosition]);
|
||||
return (typeof line.match(pseudoSelectorPrefixPattern) !== "undefined" && line.match(pseudoSelectorPrefixPattern) !== null) ? line.match(pseudoSelectorPrefixPattern)[0] : undefined;
|
||||
},
|
||||
|
||||
getPseudoSelectorCompletions({bufferPosition, editor}) {
|
||||
const prefix = this.getPseudoSelectorPrefix(editor, bufferPosition);
|
||||
if (!prefix) { return null; }
|
||||
|
||||
const completions = [];
|
||||
for (let pseudoSelector in this.pseudoSelectors) {
|
||||
const options = this.pseudoSelectors[pseudoSelector];
|
||||
if (firstCharsEqual(pseudoSelector, prefix)) {
|
||||
completions.push(this.buildPseudoSelectorCompletion(pseudoSelector, prefix, options));
|
||||
}
|
||||
}
|
||||
return completions;
|
||||
},
|
||||
|
||||
buildPseudoSelectorCompletion(pseudoSelector, prefix, {argument, description}) {
|
||||
const completion = {
|
||||
type: 'pseudo-selector',
|
||||
replacementPrefix: prefix,
|
||||
description,
|
||||
descriptionMoreURL: `${cssDocsURL}/${pseudoSelector}`
|
||||
};
|
||||
|
||||
if (argument != null) {
|
||||
completion.snippet = `${pseudoSelector}(\${1:${argument}})`;
|
||||
} else {
|
||||
completion.text = pseudoSelector;
|
||||
}
|
||||
return completion;
|
||||
},
|
||||
|
||||
getTagSelectorPrefix(editor, bufferPosition) {
|
||||
const line = editor.getTextInRange([[bufferPosition.row, 0], bufferPosition]);
|
||||
return (typeof tagSelectorPrefixPattern.exec(line) !== "undefined" && tagSelectorPrefixPattern.exec(line) !== null) ? tagSelectorPrefixPattern.exec(line)[2] : undefined;
|
||||
},
|
||||
|
||||
getTagCompletions({bufferPosition, editor, prefix}) {
|
||||
const completions = [];
|
||||
if (prefix) {
|
||||
for (let tag of Array.from(this.tags)) {
|
||||
if (firstCharsEqual(tag, prefix)) {
|
||||
completions.push(this.buildTagCompletion(tag));
|
||||
}
|
||||
}
|
||||
}
|
||||
return completions;
|
||||
},
|
||||
|
||||
buildTagCompletion(tag) {
|
||||
return {
|
||||
type: 'tag',
|
||||
text: tag,
|
||||
description: `Selector for <${tag}> elements`
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const lineEndsWithSemicolon = function(bufferPosition, editor) {
|
||||
const {row} = bufferPosition;
|
||||
const line = editor.lineTextForBufferRow(row);
|
||||
return /;\s*$/.test(line);
|
||||
};
|
||||
|
||||
// Checks if the given scope descriptor includes a scope that _starts with_ the
|
||||
// given string, or is the given string exactly. Can optionally include a list
|
||||
// of final path segments for checking against multiple of css/sass/less/postcss
|
||||
// at once.
|
||||
const hasScope = function(scopesArray, scope, endsWith = null) {
|
||||
if (endsWith && (typeof endsWith === 'string')) {
|
||||
endsWith = [endsWith];
|
||||
}
|
||||
for (var otherScope of Array.from(scopesArray)) {
|
||||
if (endsWith && Array.isArray(endsWith)) {
|
||||
if (!endsWith.some(ending => otherScope.endsWith(`.${ending}`))) { continue; }
|
||||
}
|
||||
if (otherScope === scope) { return true; }
|
||||
if (otherScope.startsWith(`${scope}.`)) { return true; }
|
||||
}
|
||||
};
|
||||
|
||||
const firstCharsEqual = (str1, str2) => str1[0].toLowerCase() === str2[0].toLowerCase();
|
869
packages/autocomplete-css/package-lock.json
generated
869
packages/autocomplete-css/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@
|
||||
"name": "autocomplete-css",
|
||||
"version": "0.17.5",
|
||||
"description": "CSS property name and value autocompletions",
|
||||
"main": "./lib/main",
|
||||
"main": "./lib/main.js",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/pulsar-edit/pulsar",
|
||||
"engines": {
|
||||
@ -18,6 +18,6 @@
|
||||
"devDependencies": {
|
||||
"@webref/css": "^6.3.4",
|
||||
"content": "github:mdn/content",
|
||||
"request": "^2.53.0"
|
||||
"superagent": "^8.0.9"
|
||||
}
|
||||
}
|
||||
|
9
packages/autocomplete-css/spec/.eslintrc.js
Normal file
9
packages/autocomplete-css/spec/.eslintrc.js
Normal file
@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
env: { jasmine: true },
|
||||
rules: {
|
||||
"node/no-unpublished-require": "off",
|
||||
"node/no-extraneous-require": "off",
|
||||
"no-unused-vars": "off",
|
||||
"no-empty": "off"
|
||||
}
|
||||
};
|
@ -1,929 +0,0 @@
|
||||
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'
|
1196
packages/autocomplete-css/spec/provider-spec.js
Normal file
1196
packages/autocomplete-css/spec/provider-spec.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -87,6 +87,7 @@
|
||||
|
||||
const css = require("@webref/css");
|
||||
const fs = require("fs");
|
||||
const superagent = require("superagent");
|
||||
const CSSParser = require("./cssValueDefinitionSyntaxExtractor.js");
|
||||
const manualPropertyDesc = require("./manual-property-desc.json");
|
||||
|
||||
@ -103,8 +104,11 @@ async function update(params) {
|
||||
pseudoSelectors: pseudoSelectors
|
||||
};
|
||||
|
||||
completions.properties = sortByLength(completions.properties);
|
||||
completions.properties = await sortByPopularity(completions.properties);
|
||||
|
||||
// Now to write out our updated file
|
||||
fs.writeFileSync("completions.json", JSON.stringify(completions, null, 2));
|
||||
fs.writeFileSync("./completions.json", JSON.stringify(completions, null, 2));
|
||||
|
||||
// Now to determine how many properties have empty descriptions.
|
||||
|
||||
@ -134,6 +138,65 @@ async function update(params) {
|
||||
};
|
||||
}
|
||||
|
||||
function sortByLength(obj) {
|
||||
let keys = Object.keys(obj);
|
||||
|
||||
keys.sort((a, b) => {
|
||||
if (a.length > b.length) {
|
||||
return 1;
|
||||
} else if (a.length < b.length) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
let newObj = {};
|
||||
|
||||
// Now rebuild the object according to our new keys
|
||||
for (i in keys) {
|
||||
newObj[keys[i]] = obj[keys[i]];
|
||||
}
|
||||
|
||||
return newObj;
|
||||
}
|
||||
|
||||
async function sortByPopularity(obj) {
|
||||
try {
|
||||
const res = await superagent.get("https://chromestatus.com/data/csspopularity");
|
||||
if (res.status !== 200) {
|
||||
console.error(res);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let newObj = {};
|
||||
|
||||
for (prop in res.body) {
|
||||
let property = res.body[prop].property_name;
|
||||
|
||||
if (typeof obj[property] === "object") {
|
||||
newObj[property] = obj[property];
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(obj).length === Object.keys(newObj).length) {
|
||||
return newObj;
|
||||
}
|
||||
|
||||
for (prop in obj) {
|
||||
if (typeof newObj[prop] !== "object") {
|
||||
newObj[prop] = obj[prop];
|
||||
}
|
||||
}
|
||||
|
||||
return newObj;
|
||||
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
@ -346,7 +409,7 @@ 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");
|
||||
let existingCompletions = require("../completions.json");
|
||||
|
||||
return existingCompletions.pseudoSelectors;
|
||||
}
|
2569
packages/autocomplete-html/package-lock.json
generated
2569
packages/autocomplete-html/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,7 @@ Displays possible autocomplete suggestions on keystroke (or manually by typing `
|
||||
|
||||
![autocomplete+](https://cloud.githubusercontent.com/assets/744740/7656861/9fb8bcc4-faea-11e4-9814-9dca218ded93.png)
|
||||
|
||||
[Changelog](https://github.com/atom/autocomplete-plus/releases)
|
||||
[Changelog](https://github.com/pulsar-edit/pulsar/blob/master/CHANGELOG.md)
|
||||
|
||||
## Installation
|
||||
|
||||
@ -85,7 +85,7 @@ If the default `SymbolProvider` is missing useful information for the language /
|
||||
|
||||
## The `watchEditor` API
|
||||
|
||||
The `watchEditor` method on the `AutocompleteManager` object is exposed as a [provided service](http://flight-manual.atom.io/behind-atom/sections/interacting-with-other-packages-via-services/), named `autocomplete.watchEditor`. The method allows external editors to register for autocompletions from providers with a given set of labels. Disposing the returned object will undo this request. External packages can access this service with the following code.
|
||||
The `watchEditor` method on the `AutocompleteManager` object is exposed as a [provided service](https://pulsar-edit.dev/docs/launch-manual/sections/behind-pulsar/#interacting-with-other-packages-via-services), named `autocomplete.watchEditor`. The method allows external editors to register for autocompletions from providers with a given set of labels. Disposing the returned object will undo this request. External packages can access this service with the following code.
|
||||
|
||||
In `package.json`:
|
||||
```
|
||||
|
@ -1,37 +0,0 @@
|
||||
{
|
||||
"max_line_length": {
|
||||
"level": "ignore"
|
||||
},
|
||||
"no_empty_param_list": {
|
||||
"level": "error"
|
||||
},
|
||||
"arrow_spacing": {
|
||||
"level": "error"
|
||||
},
|
||||
"no_interpolation_in_single_quotes": {
|
||||
"level": "error"
|
||||
},
|
||||
"no_debugger": {
|
||||
"level": "error"
|
||||
},
|
||||
"prefer_english_operator": {
|
||||
"level": "error"
|
||||
},
|
||||
"colon_assignment_spacing": {
|
||||
"spacing": {
|
||||
"left": 0,
|
||||
"right": 1
|
||||
},
|
||||
"level": "error"
|
||||
},
|
||||
"braces_spacing": {
|
||||
"spaces": 0,
|
||||
"level": "error"
|
||||
},
|
||||
"spacing_after_comma": {
|
||||
"level": "error"
|
||||
},
|
||||
"no_stand_alone_at": {
|
||||
"level": "error"
|
||||
}
|
||||
}
|
@ -141,7 +141,9 @@ module.exports = class SuggestionListElement {
|
||||
marked(item.descriptionMarkdown, {
|
||||
gfm: true,
|
||||
breaks: true,
|
||||
sanitize: false
|
||||
sanitize: false,
|
||||
mangle: false,
|
||||
headerIds: false
|
||||
})
|
||||
)
|
||||
this.setDescriptionMoreLink(item)
|
||||
|
@ -4,7 +4,7 @@ const SuggestionListElement = require('./suggestion-list-element')
|
||||
|
||||
module.exports =
|
||||
class SuggestionList {
|
||||
constructor () {
|
||||
constructor() {
|
||||
this.wordPrefixRegex = null
|
||||
this.cancel = this.cancel.bind(this)
|
||||
this.confirm = this.confirm.bind(this)
|
||||
@ -16,9 +16,10 @@ class SuggestionList {
|
||||
this.hide = this.hide.bind(this)
|
||||
this.destroyOverlay = this.destroyOverlay.bind(this)
|
||||
this.activeEditor = null
|
||||
this.lastActiveAt = 0
|
||||
}
|
||||
|
||||
initialize () {
|
||||
initialize() {
|
||||
this.emitter = new Emitter()
|
||||
this.subscriptions = new CompositeDisposable()
|
||||
|
||||
@ -37,7 +38,7 @@ class SuggestionList {
|
||||
}))
|
||||
}
|
||||
|
||||
get suggestionListElement () {
|
||||
get suggestionListElement() {
|
||||
if (!this._suggestionListElement) {
|
||||
this._suggestionListElement = new SuggestionListElement(this)
|
||||
}
|
||||
@ -45,7 +46,7 @@ class SuggestionList {
|
||||
return this._suggestionListElement
|
||||
}
|
||||
|
||||
addBindings (editor) {
|
||||
addBindings(editor) {
|
||||
if (this.bindings && this.bindings.dispose) {
|
||||
this.bindings.dispose()
|
||||
}
|
||||
@ -124,47 +125,47 @@ class SuggestionList {
|
||||
Section: Event Triggers
|
||||
*/
|
||||
|
||||
cancel () {
|
||||
cancel() {
|
||||
return this.emitter.emit('did-cancel')
|
||||
}
|
||||
|
||||
confirm (match) {
|
||||
confirm(match) {
|
||||
return this.emitter.emit('did-confirm', match)
|
||||
}
|
||||
|
||||
confirmSelection () {
|
||||
confirmSelection() {
|
||||
return this.emitter.emit('did-confirm-selection')
|
||||
}
|
||||
|
||||
confirmSelectionIfNonDefault (event) {
|
||||
confirmSelectionIfNonDefault(event) {
|
||||
return this.emitter.emit('did-confirm-selection-if-non-default', event)
|
||||
}
|
||||
|
||||
select (suggestion) {
|
||||
select(suggestion) {
|
||||
return this.emitter.emit('did-select', suggestion)
|
||||
}
|
||||
|
||||
selectNext () {
|
||||
selectNext() {
|
||||
return this.emitter.emit('did-select-next')
|
||||
}
|
||||
|
||||
selectPrevious () {
|
||||
selectPrevious() {
|
||||
return this.emitter.emit('did-select-previous')
|
||||
}
|
||||
|
||||
selectPageUp () {
|
||||
selectPageUp() {
|
||||
return this.emitter.emit('did-select-page-up')
|
||||
}
|
||||
|
||||
selectPageDown () {
|
||||
selectPageDown() {
|
||||
return this.emitter.emit('did-select-page-down')
|
||||
}
|
||||
|
||||
selectTop () {
|
||||
selectTop() {
|
||||
return this.emitter.emit('did-select-top')
|
||||
}
|
||||
|
||||
selectBottom () {
|
||||
selectBottom() {
|
||||
return this.emitter.emit('did-select-bottom')
|
||||
}
|
||||
|
||||
@ -172,67 +173,67 @@ class SuggestionList {
|
||||
Section: Events
|
||||
*/
|
||||
|
||||
onDidConfirmSelection (fn) {
|
||||
onDidConfirmSelection(fn) {
|
||||
return this.emitter.on('did-confirm-selection', fn)
|
||||
}
|
||||
|
||||
onDidconfirmSelectionIfNonDefault (fn) {
|
||||
onDidconfirmSelectionIfNonDefault(fn) {
|
||||
return this.emitter.on('did-confirm-selection-if-non-default', fn)
|
||||
}
|
||||
|
||||
onDidConfirm (fn) {
|
||||
onDidConfirm(fn) {
|
||||
return this.emitter.on('did-confirm', fn)
|
||||
}
|
||||
|
||||
onDidSelect (fn) {
|
||||
onDidSelect(fn) {
|
||||
return this.emitter.on('did-select', fn)
|
||||
}
|
||||
|
||||
onDidSelectNext (fn) {
|
||||
onDidSelectNext(fn) {
|
||||
return this.emitter.on('did-select-next', fn)
|
||||
}
|
||||
|
||||
onDidSelectPrevious (fn) {
|
||||
onDidSelectPrevious(fn) {
|
||||
return this.emitter.on('did-select-previous', fn)
|
||||
}
|
||||
|
||||
onDidSelectPageUp (fn) {
|
||||
onDidSelectPageUp(fn) {
|
||||
return this.emitter.on('did-select-page-up', fn)
|
||||
}
|
||||
|
||||
onDidSelectPageDown (fn) {
|
||||
onDidSelectPageDown(fn) {
|
||||
return this.emitter.on('did-select-page-down', fn)
|
||||
}
|
||||
|
||||
onDidSelectTop (fn) {
|
||||
onDidSelectTop(fn) {
|
||||
return this.emitter.on('did-select-top', fn)
|
||||
}
|
||||
|
||||
onDidSelectBottom (fn) {
|
||||
onDidSelectBottom(fn) {
|
||||
return this.emitter.on('did-select-bottom', fn)
|
||||
}
|
||||
|
||||
onDidCancel (fn) {
|
||||
onDidCancel(fn) {
|
||||
return this.emitter.on('did-cancel', fn)
|
||||
}
|
||||
|
||||
onDidDispose (fn) {
|
||||
onDidDispose(fn) {
|
||||
return this.emitter.on('did-dispose', fn)
|
||||
}
|
||||
|
||||
onDidChangeItems (fn) {
|
||||
onDidChangeItems(fn) {
|
||||
return this.emitter.on('did-change-items', fn)
|
||||
}
|
||||
|
||||
onDidChangeItem (fn) {
|
||||
onDidChangeItem(fn) {
|
||||
return this.emitter.on('did-change-item', fn)
|
||||
}
|
||||
|
||||
isActive () {
|
||||
isActive() {
|
||||
return (this.activeEditor != null)
|
||||
}
|
||||
|
||||
show (editor, options) {
|
||||
show(editor, options) {
|
||||
if (atom.config.get('autocomplete-plus.suggestionListFollows') === 'Cursor') {
|
||||
return this.showAtCursorPosition(editor, options)
|
||||
} else {
|
||||
@ -250,7 +251,7 @@ class SuggestionList {
|
||||
}
|
||||
}
|
||||
|
||||
showAtBeginningOfPrefix (editor, prefix, followRawPrefix = false) {
|
||||
showAtBeginningOfPrefix(editor, prefix, followRawPrefix = false) {
|
||||
let bufferPosition
|
||||
if (editor) {
|
||||
bufferPosition = editor.getCursorBufferPosition()
|
||||
@ -275,6 +276,7 @@ class SuggestionList {
|
||||
this.overlayDecoration = editor.decorateMarker(marker, {type: 'overlay', item: this.suggestionListElement, position: 'tail', class: 'autocomplete-plus'})
|
||||
const editorElement = atom.views.getView(this.activeEditor)
|
||||
if (editorElement && editorElement.classList) {
|
||||
this.lastActiveAt = performance.now()
|
||||
editorElement.classList.add('autocomplete-active')
|
||||
}
|
||||
|
||||
@ -283,7 +285,7 @@ class SuggestionList {
|
||||
}
|
||||
}
|
||||
|
||||
showAtCursorPosition (editor) {
|
||||
showAtCursorPosition(editor) {
|
||||
if (this.activeEditor === editor || (editor == null)) { return }
|
||||
this.destroyOverlay()
|
||||
|
||||
@ -295,6 +297,7 @@ class SuggestionList {
|
||||
this.activeEditor = editor
|
||||
const editorElement = atom.views.getView(this.activeEditor)
|
||||
if (editorElement && editorElement.classList) {
|
||||
this.lastActiveAt = performance.now()
|
||||
editorElement.classList.add('autocomplete-active')
|
||||
}
|
||||
|
||||
@ -303,7 +306,7 @@ class SuggestionList {
|
||||
}
|
||||
}
|
||||
|
||||
hide () {
|
||||
hide() {
|
||||
this.destroyOverlay()
|
||||
if (this.activeEditor === null) {
|
||||
return
|
||||
@ -317,7 +320,7 @@ class SuggestionList {
|
||||
return this.activeEditor
|
||||
}
|
||||
|
||||
destroyOverlay () {
|
||||
destroyOverlay() {
|
||||
if (this.suggestionMarker && this.suggestionMarker.destroy) {
|
||||
this.suggestionMarker.destroy()
|
||||
} else if (this.overlayDecoration && this.overlayDecoration.destroy) {
|
||||
@ -325,7 +328,11 @@ class SuggestionList {
|
||||
}
|
||||
const editorElement = atom.views.getView(this.activeEditor)
|
||||
if (editorElement && editorElement.classList) {
|
||||
let timestamp = this.lastActiveAt
|
||||
atom.views.updateDocument(() => {
|
||||
// A newer timestamp here means that the menu is open again and we
|
||||
// shouldn't remove this class name anymore.
|
||||
if (this.lastActiveAt > timestamp) return
|
||||
editorElement.classList.remove('autocomplete-active')
|
||||
})
|
||||
}
|
||||
@ -334,12 +341,12 @@ class SuggestionList {
|
||||
return this.overlayDecoration
|
||||
}
|
||||
|
||||
changeItems (items) {
|
||||
changeItems(items) {
|
||||
this.items = items
|
||||
return this.emitter.emit('did-change-items', this.items)
|
||||
}
|
||||
|
||||
replaceItem (oldSuggestion, newSuggestion) {
|
||||
replaceItem(oldSuggestion, newSuggestion) {
|
||||
if (newSuggestion == null) {
|
||||
return
|
||||
}
|
||||
@ -368,7 +375,7 @@ class SuggestionList {
|
||||
}
|
||||
|
||||
// Public: Clean up, stop listening to events
|
||||
dispose () {
|
||||
dispose() {
|
||||
if (this.subscriptions) {
|
||||
this.subscriptions.dispose()
|
||||
}
|
||||
|
3054
packages/autocomplete-plus/package-lock.json
generated
3054
packages/autocomplete-plus/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,30 +1,28 @@
|
||||
{
|
||||
"name": "autocomplete-plus",
|
||||
"version": "2.42.6",
|
||||
"main": "./lib/main",
|
||||
"main": "./lib/main.js",
|
||||
"description": "Display possible completions in the editor while typing",
|
||||
"repository": "https://github.com/atom/autocomplete-plus",
|
||||
"repository": "https://github.com/pulsar-edit/pulsar",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"atom": ">=0.189.0 <2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"atom-slick": "^2.0.0",
|
||||
"dompurify": "^2.2.6",
|
||||
"dompurify": "^3.0.4",
|
||||
"fuzzaldrin": "^2.1.0",
|
||||
"fuzzaldrin-plus": "^0.6.0",
|
||||
"grim": "^2.0.1",
|
||||
"marked": "^4.0.10",
|
||||
"marked": "^5.1.1",
|
||||
"minimatch": "^3.0.3",
|
||||
"selector-kit": "^0.1",
|
||||
"stable": "^0.1.5",
|
||||
"underscore-plus": "^1.6.6"
|
||||
"selector-kit": "^0.1.0",
|
||||
"stable": "^0.1.8",
|
||||
"underscore-plus": "^1.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"coffeelint": "^1.9.7",
|
||||
"fs-plus": ">=2.4.0",
|
||||
"standard": "^8.3.0",
|
||||
"temp": ">=0.7.0"
|
||||
"fs-plus": "^3.1.1",
|
||||
"temp": "^0.9.4"
|
||||
},
|
||||
"providedServices": {
|
||||
"autocomplete.watchEditor": {
|
||||
@ -88,7 +86,7 @@
|
||||
},
|
||||
"useCoreMovementCommands": {
|
||||
"title": "Use Core Movement Commands",
|
||||
"description": "Disable this if you want to bind your own keystrokes to move around the suggestion list. You will also need to add definitions to your keymap. See: https://github.com/atom/autocomplete-plus#remapping-movement-commands",
|
||||
"description": "Disable this if you want to bind your own keystrokes to move around the suggestion list. You will also need to add definitions to your keymap. See: https://github.com/pulsar-edit/pulsar/blob/master/packages/autocomplete-plus/README.md#remapping-movement-commands",
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"order": 5
|
||||
@ -107,7 +105,7 @@
|
||||
},
|
||||
"scopeBlacklist": {
|
||||
"title": "Scope Blacklist",
|
||||
"description": "Suggestions will not be provided for scopes matching this list. See: http://flight-manual.atom.io/behind-atom/sections/scoped-settings-scopes-and-scope-descriptors/",
|
||||
"description": "Suggestions will not be provided for scopes matching this list. See: https://pulsar-edit.dev/docs/launch-manual/sections/behind-pulsar/#scoped-settings-scopes-and-scope-descriptors",
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"items": {
|
||||
@ -241,18 +239,5 @@
|
||||
"default": false,
|
||||
"order": 23
|
||||
}
|
||||
},
|
||||
"standard": {
|
||||
"globals": [
|
||||
"CustomEvent",
|
||||
"HTMLElement",
|
||||
"advanceClock",
|
||||
"atom",
|
||||
"getComputedStyle",
|
||||
"waitsForPromise"
|
||||
],
|
||||
"ignore": [
|
||||
"**/spec/fixtures/**"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ autocomplete-suggestion-list.select-list.popover-list {
|
||||
}
|
||||
|
||||
.suggestion-description-content {
|
||||
font-size: @font-size + 1px;
|
||||
font-size: (@font-size + 1px);
|
||||
font-family: @font-family;
|
||||
max-height: 33vh;
|
||||
display: block;
|
||||
@ -51,7 +51,7 @@ autocomplete-suggestion-list.select-list.popover-list {
|
||||
}
|
||||
|
||||
.suggestion-description-more-link {
|
||||
font-size: @font-size + 1px;
|
||||
font-size: (@font-size + 1px);
|
||||
font-family: @font-family;
|
||||
color: @text-color-info;
|
||||
}
|
||||
|
@ -1,142 +0,0 @@
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
CharacterPattern = ///
|
||||
[
|
||||
^\s
|
||||
]
|
||||
///
|
||||
|
||||
module.exports =
|
||||
activate: ->
|
||||
@commandDisposable = atom.commands.add 'atom-text-editor',
|
||||
'autoflow:reflow-selection': (event) =>
|
||||
@reflowSelection(event.currentTarget.getModel())
|
||||
|
||||
deactivate: ->
|
||||
@commandDisposable?.dispose()
|
||||
@commandDisposable = null
|
||||
|
||||
reflowSelection: (editor) ->
|
||||
range = editor.getSelectedBufferRange()
|
||||
range = editor.getCurrentParagraphBufferRange() if range.isEmpty()
|
||||
return unless range?
|
||||
|
||||
reflowOptions =
|
||||
wrapColumn: @getPreferredLineLength(editor)
|
||||
tabLength: @getTabLength(editor)
|
||||
reflowedText = @reflow(editor.getTextInRange(range), reflowOptions)
|
||||
editor.getBuffer().setTextInRange(range, reflowedText)
|
||||
|
||||
reflow: (text, {wrapColumn, tabLength}) ->
|
||||
paragraphs = []
|
||||
# Convert all \r\n and \r to \n. The text buffer will normalize them later
|
||||
text = text.replace(/\r\n?/g, '\n')
|
||||
|
||||
leadingVerticalSpace = text.match(/^\s*\n/)
|
||||
if leadingVerticalSpace
|
||||
text = text.substr(leadingVerticalSpace.length)
|
||||
else
|
||||
leadingVerticalSpace = ''
|
||||
|
||||
trailingVerticalSpace = text.match(/\n\s*$/)
|
||||
if trailingVerticalSpace
|
||||
text = text.substr(0, text.length - trailingVerticalSpace.length)
|
||||
else
|
||||
trailingVerticalSpace = ''
|
||||
|
||||
paragraphBlocks = text.split(/\n\s*\n/g)
|
||||
if tabLength
|
||||
tabLengthInSpaces = Array(tabLength + 1).join(' ')
|
||||
else
|
||||
tabLengthInSpaces = ''
|
||||
|
||||
for block in paragraphBlocks
|
||||
blockLines = block.split('\n')
|
||||
|
||||
# For LaTeX tags surrounding the text, we simply ignore them, and
|
||||
# reproduce them verbatim in the wrapped text.
|
||||
beginningLinesToIgnore = []
|
||||
endingLinesToIgnore = []
|
||||
latexTagRegex = /^\s*\\\w+(\[.*\])?\{\w+\}(\[.*\])?\s*$/g # e.g. \begin{verbatim}
|
||||
latexTagStartRegex = /^\s*\\\w+\s*\{\s*$/g # e.g. \item{
|
||||
latexTagEndRegex = /^\s*\}\s*$/g # e.g. }
|
||||
while blockLines.length > 0 and (
|
||||
blockLines[0].match(latexTagRegex) or
|
||||
blockLines[0].match(latexTagStartRegex))
|
||||
beginningLinesToIgnore.push(blockLines[0])
|
||||
blockLines.shift()
|
||||
while blockLines.length > 0 and (
|
||||
blockLines[blockLines.length - 1].match(latexTagRegex) or
|
||||
blockLines[blockLines.length - 1].match(latexTagEndRegex))
|
||||
endingLinesToIgnore.unshift(blockLines[blockLines.length - 1])
|
||||
blockLines.pop()
|
||||
|
||||
# The paragraph might be a LaTeX section with no text, only tags:
|
||||
# \documentclass{article}
|
||||
# In that case, we have nothing to reflow.
|
||||
# Push the tags verbatim and continue to the next paragraph.
|
||||
unless blockLines.length > 0
|
||||
paragraphs.push(block)
|
||||
continue
|
||||
|
||||
# TODO: this could be more language specific. Use the actual comment char.
|
||||
# Remember that `-` has to be the last character in the character class.
|
||||
linePrefix = blockLines[0].match(/^\s*(\/\/|\/\*|;;|#'|\|\|\||--|[#%*>-])?\s*/g)[0]
|
||||
linePrefixTabExpanded = linePrefix
|
||||
if tabLengthInSpaces
|
||||
linePrefixTabExpanded = linePrefix.replace(/\t/g, tabLengthInSpaces)
|
||||
|
||||
if linePrefix
|
||||
escapedLinePrefix = _.escapeRegExp(linePrefix)
|
||||
blockLines = blockLines.map (blockLine) ->
|
||||
blockLine.replace(///^#{escapedLinePrefix}///, '')
|
||||
|
||||
blockLines = blockLines.map (blockLine) ->
|
||||
blockLine.replace(/^\s+/, '')
|
||||
|
||||
lines = []
|
||||
currentLine = []
|
||||
currentLineLength = linePrefixTabExpanded.length
|
||||
|
||||
wrappedLinePrefix = linePrefix
|
||||
.replace(/^(\s*)\/\*/, '$1 ')
|
||||
.replace(/^(\s*)-(?!-)/, '$1 ')
|
||||
|
||||
firstLine = true
|
||||
for segment in @segmentText(blockLines.join(' '))
|
||||
if @wrapSegment(segment, currentLineLength, wrapColumn)
|
||||
|
||||
# Independent of line prefix don't mess with it on the first line
|
||||
if firstLine isnt true
|
||||
# Handle C comments
|
||||
if linePrefix.search(/^\s*\/\*/) isnt -1 or linePrefix.search(/^\s*-(?!-)/) isnt -1
|
||||
linePrefix = wrappedLinePrefix
|
||||
lines.push(linePrefix + currentLine.join(''))
|
||||
currentLine = []
|
||||
currentLineLength = linePrefixTabExpanded.length
|
||||
firstLine = false
|
||||
currentLine.push(segment)
|
||||
currentLineLength += segment.length
|
||||
lines.push(linePrefix + currentLine.join(''))
|
||||
|
||||
wrappedLines = beginningLinesToIgnore.concat(lines.concat(endingLinesToIgnore))
|
||||
paragraphs.push(wrappedLines.join('\n').replace(/\s+\n/g, '\n'))
|
||||
|
||||
return leadingVerticalSpace + paragraphs.join('\n\n') + trailingVerticalSpace
|
||||
|
||||
getTabLength: (editor) ->
|
||||
atom.config.get('editor.tabLength', scope: editor.getRootScopeDescriptor()) ? 2
|
||||
|
||||
getPreferredLineLength: (editor) ->
|
||||
atom.config.get('editor.preferredLineLength', scope: editor.getRootScopeDescriptor())
|
||||
|
||||
wrapSegment: (segment, currentLineLength, wrapColumn) ->
|
||||
CharacterPattern.test(segment) and
|
||||
(currentLineLength + segment.length > wrapColumn) and
|
||||
(currentLineLength > 0 or segment.length < wrapColumn)
|
||||
|
||||
segmentText: (text) ->
|
||||
segments = []
|
||||
re = /[\s]+|[^\s]+/g
|
||||
segments.push(match[0]) while match = re.exec(text)
|
||||
segments
|
165
packages/autoflow/lib/autoflow.js
Normal file
165
packages/autoflow/lib/autoflow.js
Normal file
@ -0,0 +1,165 @@
|
||||
|
||||
const _ = require('underscore-plus');
|
||||
|
||||
const CharacterPattern = new RegExp(/[^\s]/);
|
||||
|
||||
module.exports = {
|
||||
activate() {
|
||||
this.commandDisposable = atom.commands.add('atom-text-editor', {
|
||||
'autoflow:reflow-selection': event => {
|
||||
this.reflowSelection(event.currentTarget.getModel());
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
deactivate() {
|
||||
this.commandDisposable?.dispose();
|
||||
this.commandDisposable = null;
|
||||
},
|
||||
|
||||
reflowSelection(editor) {
|
||||
let range = editor.getSelectedBufferRange();
|
||||
if (range.isEmpty()) { range = editor.getCurrentParagraphBufferRange(); }
|
||||
if (range == null) { return; }
|
||||
|
||||
const reflowOptions = {
|
||||
wrapColumn: this.getPreferredLineLength(editor),
|
||||
tabLength: this.getTabLength(editor)
|
||||
};
|
||||
const reflowedText = this.reflow(editor.getTextInRange(range), reflowOptions);
|
||||
return editor.getBuffer().setTextInRange(range, reflowedText);
|
||||
},
|
||||
|
||||
reflow(text, {wrapColumn, tabLength}) {
|
||||
let tabLengthInSpaces;
|
||||
const paragraphs = [];
|
||||
// Convert all \r\n and \r to \n. The text buffer will normalize them later
|
||||
text = text.replace(/\r\n?/g, '\n');
|
||||
|
||||
let leadingVerticalSpace = text.match(/^\s*\n/);
|
||||
if (leadingVerticalSpace) {
|
||||
text = text.substr(leadingVerticalSpace.length);
|
||||
} else {
|
||||
leadingVerticalSpace = '';
|
||||
}
|
||||
|
||||
let trailingVerticalSpace = text.match(/\n\s*$/);
|
||||
if (trailingVerticalSpace) {
|
||||
text = text.substr(0, text.length - trailingVerticalSpace.length);
|
||||
} else {
|
||||
trailingVerticalSpace = '';
|
||||
}
|
||||
|
||||
const paragraphBlocks = text.split(/\n\s*\n/g);
|
||||
if (tabLength) {
|
||||
tabLengthInSpaces = Array(tabLength + 1).join(' ');
|
||||
} else {
|
||||
tabLengthInSpaces = '';
|
||||
}
|
||||
|
||||
for (let block of paragraphBlocks) {
|
||||
let blockLines = block.split('\n');
|
||||
|
||||
// For LaTeX tags surrounding the text, we simply ignore them, and
|
||||
// reproduce them verbatim in the wrapped text.
|
||||
const beginningLinesToIgnore = [];
|
||||
const endingLinesToIgnore = [];
|
||||
const latexTagRegex = /^\s*\\\w+(\[.*\])?\{\w+\}(\[.*\])?\s*$/g; // e.g. \begin{verbatim}
|
||||
const latexTagStartRegex = /^\s*\\\w+\s*\{\s*$/g; // e.g. \item{
|
||||
const latexTagEndRegex = /^\s*\}\s*$/g; // e.g. }
|
||||
while ((blockLines.length > 0) && (
|
||||
blockLines[0].match(latexTagRegex) ||
|
||||
blockLines[0].match(latexTagStartRegex))) {
|
||||
beginningLinesToIgnore.push(blockLines[0]);
|
||||
blockLines.shift();
|
||||
}
|
||||
while ((blockLines.length > 0) && (
|
||||
blockLines[blockLines.length - 1].match(latexTagRegex) ||
|
||||
blockLines[blockLines.length - 1].match(latexTagEndRegex))) {
|
||||
endingLinesToIgnore.unshift(blockLines[blockLines.length - 1]);
|
||||
blockLines.pop();
|
||||
}
|
||||
|
||||
// The paragraph might be a LaTeX section with no text, only tags:
|
||||
// \documentclass{article}
|
||||
// In that case, we have nothing to reflow.
|
||||
// Push the tags verbatim and continue to the next paragraph.
|
||||
if (!(blockLines.length > 0)) {
|
||||
paragraphs.push(block);
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: this could be more language specific. Use the actual comment char.
|
||||
// Remember that `-` has to be the last character in the character class.
|
||||
let linePrefix = blockLines[0].match(/^\s*(\/\/|\/\*|;;|#'|\|\|\||--|[#%*>-])?\s*/g)[0];
|
||||
let linePrefixTabExpanded = linePrefix;
|
||||
if (tabLengthInSpaces) {
|
||||
linePrefixTabExpanded = linePrefix.replace(/\t/g, tabLengthInSpaces);
|
||||
}
|
||||
|
||||
if (linePrefix) {
|
||||
var escapedLinePrefix = _.escapeRegExp(linePrefix);
|
||||
blockLines = blockLines.map(blockLine => blockLine.replace(new RegExp(`^${escapedLinePrefix}`), ''));
|
||||
}
|
||||
|
||||
blockLines = blockLines.map(blockLine => blockLine.replace(/^\s+/, ''));
|
||||
|
||||
const lines = [];
|
||||
let currentLine = [];
|
||||
let currentLineLength = linePrefixTabExpanded.length;
|
||||
|
||||
const wrappedLinePrefix = linePrefix
|
||||
.replace(/^(\s*)\/\*/, '$1 ')
|
||||
.replace(/^(\s*)-(?!-)/, '$1 ');
|
||||
|
||||
let firstLine = true;
|
||||
for (let segment of this.segmentText(blockLines.join(' '))) {
|
||||
if (this.wrapSegment(segment, currentLineLength, wrapColumn)) {
|
||||
|
||||
// Independent of line prefix don't mess with it on the first line
|
||||
if (firstLine !== true) {
|
||||
// Handle C comments
|
||||
if ((linePrefix.search(/^\s*\/\*/) !== -1) || (linePrefix.search(/^\s*-(?!-)/) !== -1)) {
|
||||
linePrefix = wrappedLinePrefix;
|
||||
}
|
||||
}
|
||||
lines.push(linePrefix + currentLine.join(''));
|
||||
currentLine = [];
|
||||
currentLineLength = linePrefixTabExpanded.length;
|
||||
firstLine = false;
|
||||
}
|
||||
currentLine.push(segment);
|
||||
currentLineLength += segment.length;
|
||||
}
|
||||
lines.push(linePrefix + currentLine.join(''));
|
||||
|
||||
const wrappedLines = beginningLinesToIgnore.concat(lines.concat(endingLinesToIgnore));
|
||||
paragraphs.push(wrappedLines.join('\n').replace(/\s+\n/g, '\n'));
|
||||
}
|
||||
|
||||
return leadingVerticalSpace + paragraphs.join('\n\n') + trailingVerticalSpace;
|
||||
},
|
||||
|
||||
getTabLength(editor) {
|
||||
return atom.config.get('editor.tabLength', { scope: editor.getRootScopeDescriptor() }) ?? 2;
|
||||
},
|
||||
|
||||
getPreferredLineLength(editor) {
|
||||
return atom.config.get('editor.preferredLineLength', {scope: editor.getRootScopeDescriptor()});
|
||||
},
|
||||
|
||||
wrapSegment(segment, currentLineLength, wrapColumn) {
|
||||
return CharacterPattern.test(segment) &&
|
||||
((currentLineLength + segment.length) > wrapColumn) &&
|
||||
((currentLineLength > 0) || (segment.length < wrapColumn));
|
||||
},
|
||||
|
||||
segmentText(text) {
|
||||
let match;
|
||||
const segments = [];
|
||||
const re = /[\s]+|[^\s]+/g;
|
||||
while ((match = re.exec(text))) { segments.push(match[0]); }
|
||||
return segments;
|
||||
}
|
||||
};
|
@ -1,639 +0,0 @@
|
||||
describe "Autoflow package", ->
|
||||
[autoflow, editor, editorElement] = []
|
||||
tabLength = 4
|
||||
|
||||
describe "autoflow:reflow-selection", ->
|
||||
beforeEach ->
|
||||
activationPromise = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open()
|
||||
|
||||
runs ->
|
||||
editor = atom.workspace.getActiveTextEditor()
|
||||
editorElement = atom.views.getView(editor)
|
||||
|
||||
atom.config.set('editor.preferredLineLength', 30)
|
||||
atom.config.set('editor.tabLength', tabLength)
|
||||
|
||||
activationPromise = atom.packages.activatePackage('autoflow')
|
||||
|
||||
atom.commands.dispatch editorElement, 'autoflow:reflow-selection'
|
||||
|
||||
waitsForPromise ->
|
||||
activationPromise
|
||||
|
||||
it "uses the preferred line length based on the editor's scope", ->
|
||||
atom.config.set('editor.preferredLineLength', 4, scopeSelector: '.text.plain.null-grammar')
|
||||
editor.setText("foo bar")
|
||||
editor.selectAll()
|
||||
atom.commands.dispatch editorElement, 'autoflow:reflow-selection'
|
||||
|
||||
expect(editor.getText()).toBe """
|
||||
foo
|
||||
bar
|
||||
"""
|
||||
|
||||
it "rearranges line breaks in the current selection to ensure lines are shorter than config.editor.preferredLineLength honoring tabLength", ->
|
||||
editor.setText "\t\tThis is the first paragraph and it is longer than the preferred line length so it should be reflowed.\n\n\t\tThis is a short paragraph.\n\n\t\tAnother long paragraph, it should also be reflowed with the use of this single command."
|
||||
|
||||
editor.selectAll()
|
||||
atom.commands.dispatch editorElement, 'autoflow:reflow-selection'
|
||||
|
||||
exedOut = editor.getText().replace(/\t/g, Array(tabLength+1).join 'X')
|
||||
expect(exedOut).toBe "XXXXXXXXThis is the first\nXXXXXXXXparagraph and it is\nXXXXXXXXlonger than the\nXXXXXXXXpreferred line length\nXXXXXXXXso it should be\nXXXXXXXXreflowed.\n\nXXXXXXXXThis is a short\nXXXXXXXXparagraph.\n\nXXXXXXXXAnother long\nXXXXXXXXparagraph, it should\nXXXXXXXXalso be reflowed with\nXXXXXXXXthe use of this single\nXXXXXXXXcommand."
|
||||
|
||||
it "rearranges line breaks in the current selection to ensure lines are shorter than config.editor.preferredLineLength", ->
|
||||
editor.setText """
|
||||
This is the first paragraph and it is longer than the preferred line length so it should be reflowed.
|
||||
|
||||
This is a short paragraph.
|
||||
|
||||
Another long paragraph, it should also be reflowed with the use of this single command.
|
||||
"""
|
||||
|
||||
editor.selectAll()
|
||||
atom.commands.dispatch editorElement, 'autoflow:reflow-selection'
|
||||
|
||||
expect(editor.getText()).toBe """
|
||||
This is the first paragraph
|
||||
and it is longer than the
|
||||
preferred line length so it
|
||||
should be reflowed.
|
||||
|
||||
This is a short paragraph.
|
||||
|
||||
Another long paragraph, it
|
||||
should also be reflowed with
|
||||
the use of this single
|
||||
command.
|
||||
"""
|
||||
|
||||
it "is not confused when the selection boundary is between paragraphs", ->
|
||||
editor.setText """
|
||||
v--- SELECTION STARTS AT THE BEGINNING OF THE NEXT LINE (pos 1,0)
|
||||
|
||||
The preceding newline should not be considered part of this paragraph.
|
||||
|
||||
The newline at the end of this paragraph should be preserved and not
|
||||
converted into a space.
|
||||
|
||||
^--- SELECTION ENDS AT THE BEGINNING OF THE PREVIOUS LINE (pos 6,0)
|
||||
"""
|
||||
|
||||
editor.setCursorBufferPosition([1, 0])
|
||||
editor.selectToBufferPosition([6, 0])
|
||||
atom.commands.dispatch editorElement, 'autoflow:reflow-selection'
|
||||
|
||||
expect(editor.getText()).toBe """
|
||||
v--- SELECTION STARTS AT THE BEGINNING OF THE NEXT LINE (pos 1,0)
|
||||
|
||||
The preceding newline should
|
||||
not be considered part of this
|
||||
paragraph.
|
||||
|
||||
The newline at the end of this
|
||||
paragraph should be preserved
|
||||
and not converted into a
|
||||
space.
|
||||
|
||||
^--- SELECTION ENDS AT THE BEGINNING OF THE PREVIOUS LINE (pos 6,0)
|
||||
"""
|
||||
|
||||
it "reflows the current paragraph if nothing is selected", ->
|
||||
editor.setText """
|
||||
This is a preceding paragraph, which shouldn't be modified by a reflow of the following paragraph.
|
||||
|
||||
The quick brown fox jumps over the lazy
|
||||
dog. The preceding sentence contains every letter
|
||||
in the entire English alphabet, which has absolutely no relevance
|
||||
to this test.
|
||||
|
||||
This is a following paragraph, which shouldn't be modified by a reflow of the preciding paragraph.
|
||||
|
||||
"""
|
||||
|
||||
editor.setCursorBufferPosition([3, 5])
|
||||
atom.commands.dispatch editorElement, 'autoflow:reflow-selection'
|
||||
|
||||
expect(editor.getText()).toBe """
|
||||
This is a preceding paragraph, which shouldn't be modified by a reflow of the following paragraph.
|
||||
|
||||
The quick brown fox jumps over
|
||||
the lazy dog. The preceding
|
||||
sentence contains every letter
|
||||
in the entire English
|
||||
alphabet, which has absolutely
|
||||
no relevance to this test.
|
||||
|
||||
This is a following paragraph, which shouldn't be modified by a reflow of the preciding paragraph.
|
||||
|
||||
"""
|
||||
|
||||
it "allows for single words that exceed the preferred wrap column length", ->
|
||||
editor.setText("this-is-a-super-long-word-that-shouldn't-break-autoflow and these are some smaller words")
|
||||
|
||||
editor.selectAll()
|
||||
atom.commands.dispatch editorElement, 'autoflow:reflow-selection'
|
||||
|
||||
expect(editor.getText()).toBe """
|
||||
this-is-a-super-long-word-that-shouldn't-break-autoflow
|
||||
and these are some smaller
|
||||
words
|
||||
"""
|
||||
|
||||
describe "reflowing text", ->
|
||||
beforeEach ->
|
||||
autoflow = require("../lib/autoflow")
|
||||
|
||||
it 'respects current paragraphs', ->
|
||||
text = '''
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida nibh id magna ullamcorper sagittis. Maecenas
|
||||
et enim eu orci tincidunt adipiscing
|
||||
aliquam ligula.
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
Phasellus gravida
|
||||
nibh id magna ullamcorper
|
||||
tincidunt adipiscing lacinia a dui. Etiam quis erat dolor.
|
||||
rutrum nisl fermentum rhoncus. Duis blandit ligula facilisis fermentum.
|
||||
'''
|
||||
|
||||
res = '''
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida nibh
|
||||
id magna ullamcorper sagittis. Maecenas et enim eu orci tincidunt adipiscing
|
||||
aliquam ligula.
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida nibh
|
||||
id magna ullamcorper tincidunt adipiscing lacinia a dui. Etiam quis erat dolor.
|
||||
rutrum nisl fermentum rhoncus. Duis blandit ligula facilisis fermentum.
|
||||
'''
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it 'respects indentation', ->
|
||||
text = '''
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida nibh id magna ullamcorper sagittis. Maecenas
|
||||
et enim eu orci tincidunt adipiscing
|
||||
aliquam ligula.
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
Phasellus gravida
|
||||
nibh id magna ullamcorper
|
||||
tincidunt adipiscing lacinia a dui. Etiam quis erat dolor.
|
||||
rutrum nisl fermentum rhoncus. Duis blandit ligula facilisis fermentum
|
||||
'''
|
||||
|
||||
res = '''
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida nibh
|
||||
id magna ullamcorper sagittis. Maecenas et enim eu orci tincidunt adipiscing
|
||||
aliquam ligula.
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida
|
||||
nibh id magna ullamcorper tincidunt adipiscing lacinia a dui. Etiam quis
|
||||
erat dolor. rutrum nisl fermentum rhoncus. Duis blandit ligula facilisis
|
||||
fermentum
|
||||
'''
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it 'respects prefixed text (comments!)', ->
|
||||
text = '''
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida nibh id magna ullamcorper sagittis. Maecenas
|
||||
et enim eu orci tincidunt adipiscing
|
||||
aliquam ligula.
|
||||
|
||||
# Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
# Phasellus gravida
|
||||
# nibh id magna ullamcorper
|
||||
# tincidunt adipiscing lacinia a dui. Etiam quis erat dolor.
|
||||
# rutrum nisl fermentum rhoncus. Duis blandit ligula facilisis fermentum
|
||||
'''
|
||||
|
||||
res = '''
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida nibh
|
||||
id magna ullamcorper sagittis. Maecenas et enim eu orci tincidunt adipiscing
|
||||
aliquam ligula.
|
||||
|
||||
# Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida
|
||||
# nibh id magna ullamcorper tincidunt adipiscing lacinia a dui. Etiam quis
|
||||
# erat dolor. rutrum nisl fermentum rhoncus. Duis blandit ligula facilisis
|
||||
# fermentum
|
||||
'''
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it 'respects multiple prefixes (js/c comments)', ->
|
||||
text = '''
|
||||
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida
|
||||
et enim eu orci tincidunt adipiscing
|
||||
aliquam ligula.
|
||||
'''
|
||||
|
||||
res = '''
|
||||
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida et
|
||||
// enim eu orci tincidunt adipiscing aliquam ligula.
|
||||
'''
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it 'properly handles * prefix', ->
|
||||
text = '''
|
||||
* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida
|
||||
et enim eu orci tincidunt adipiscing
|
||||
aliquam ligula.
|
||||
|
||||
* soidjfiojsoidj foi
|
||||
'''
|
||||
|
||||
res = '''
|
||||
* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida et
|
||||
* enim eu orci tincidunt adipiscing aliquam ligula.
|
||||
|
||||
* soidjfiojsoidj foi
|
||||
'''
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it "does not throw invalid regular expression errors (regression)", ->
|
||||
text = '''
|
||||
*** Lorem ipsum dolor sit amet
|
||||
'''
|
||||
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual text
|
||||
|
||||
it 'handles different initial indentation', ->
|
||||
text = '''
|
||||
Magna ea magna fugiat nisi minim in id duis. Culpa sit sint consequat quis elit magna pariatur incididunt
|
||||
proident laborum deserunt est aliqua reprehenderit. Occaecat et ex non do Lorem irure adipisicing mollit excepteur
|
||||
eu ullamco consectetur. Ex ex Lorem duis labore quis ad exercitation elit dolor non adipisicing. Pariatur commodo ullamco
|
||||
culpa dolor sunt enim. Ullamco dolore do ea nulla ut commodo minim consequat cillum ad velit quis.
|
||||
'''
|
||||
|
||||
res = '''
|
||||
Magna ea magna fugiat nisi minim in id duis. Culpa sit sint consequat quis elit
|
||||
magna pariatur incididunt proident laborum deserunt est aliqua reprehenderit.
|
||||
Occaecat et ex non do Lorem irure adipisicing mollit excepteur eu ullamco
|
||||
consectetur. Ex ex Lorem duis labore quis ad exercitation elit dolor non
|
||||
adipisicing. Pariatur commodo ullamco culpa dolor sunt enim. Ullamco dolore do
|
||||
ea nulla ut commodo minim consequat cillum ad velit quis.
|
||||
'''
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it 'properly handles CRLF', ->
|
||||
text = "This is the first line and it is longer than the preferred line length so it should be reflowed.\r\nThis is a short line which should\r\nbe reflowed with the following line.\rAnother long line, it should also be reflowed with everything above it when it is all reflowed."
|
||||
|
||||
res =
|
||||
'''
|
||||
This is the first line and it is longer than the preferred line length so it
|
||||
should be reflowed. This is a short line which should be reflowed with the
|
||||
following line. Another long line, it should also be reflowed with everything
|
||||
above it when it is all reflowed.
|
||||
'''
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it 'handles cyrillic text', ->
|
||||
text = '''
|
||||
В начале июля, в чрезвычайно жаркое время, под вечер, один молодой человек вышел из своей каморки, которую нанимал от жильцов в С-м переулке, на улицу и медленно, как бы в нерешимости, отправился к К-ну мосту.
|
||||
'''
|
||||
|
||||
res = '''
|
||||
В начале июля, в чрезвычайно жаркое время, под вечер, один молодой человек вышел
|
||||
из своей каморки, которую нанимал от жильцов в С-м переулке, на улицу и
|
||||
медленно, как бы в нерешимости, отправился к К-ну мосту.
|
||||
'''
|
||||
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it 'handles `yo` character properly', ->
|
||||
# Because there're known problems with this character in major regex engines
|
||||
text = 'Ё Ё Ё'
|
||||
|
||||
res = '''
|
||||
Ё
|
||||
Ё
|
||||
Ё
|
||||
'''
|
||||
|
||||
expect(autoflow.reflow(text, wrapColumn: 2)).toEqual res
|
||||
|
||||
it 'properly reflows // comments ', ->
|
||||
text =
|
||||
'''
|
||||
// Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade, tacos pickled fanny pack literally meh pinterest slow-carb. Meditation microdosing distillery 8-bit humblebrag migas.
|
||||
'''
|
||||
|
||||
res =
|
||||
'''
|
||||
// Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard
|
||||
// sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical
|
||||
// fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest
|
||||
// quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro
|
||||
// actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia
|
||||
// sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher
|
||||
// direct trade, tacos pickled fanny pack literally meh pinterest slow-carb.
|
||||
// Meditation microdosing distillery 8-bit humblebrag migas.
|
||||
'''
|
||||
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it 'properly reflows /* comments ', ->
|
||||
text =
|
||||
'''
|
||||
/* Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade, tacos pickled fanny pack literally meh pinterest slow-carb. Meditation microdosing distillery 8-bit humblebrag migas. */
|
||||
'''
|
||||
|
||||
res =
|
||||
'''
|
||||
/* Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard
|
||||
sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical
|
||||
fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest
|
||||
quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro
|
||||
actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia
|
||||
sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher
|
||||
direct trade, tacos pickled fanny pack literally meh pinterest slow-carb.
|
||||
Meditation microdosing distillery 8-bit humblebrag migas. */
|
||||
'''
|
||||
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it 'properly reflows pound comments ', ->
|
||||
text =
|
||||
'''
|
||||
# Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade, tacos pickled fanny pack literally meh pinterest slow-carb. Meditation microdosing distillery 8-bit humblebrag migas.
|
||||
'''
|
||||
|
||||
res =
|
||||
'''
|
||||
# Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha
|
||||
# banh mi, cold-pressed retro whatever ethical man braid asymmetrical
|
||||
# fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa
|
||||
# leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually
|
||||
# aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial
|
||||
# letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade,
|
||||
# tacos pickled fanny pack literally meh pinterest slow-carb. Meditation
|
||||
# microdosing distillery 8-bit humblebrag migas.
|
||||
'''
|
||||
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it 'properly reflows - list items ', ->
|
||||
text =
|
||||
'''
|
||||
- Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade, tacos pickled fanny pack literally meh pinterest slow-carb. Meditation microdosing distillery 8-bit humblebrag migas.
|
||||
'''
|
||||
|
||||
res =
|
||||
'''
|
||||
- Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha
|
||||
banh mi, cold-pressed retro whatever ethical man braid asymmetrical
|
||||
fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa
|
||||
leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually
|
||||
aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial
|
||||
letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade,
|
||||
tacos pickled fanny pack literally meh pinterest slow-carb. Meditation
|
||||
microdosing distillery 8-bit humblebrag migas.
|
||||
'''
|
||||
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it 'properly reflows % comments ', ->
|
||||
text =
|
||||
'''
|
||||
% Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade, tacos pickled fanny pack literally meh pinterest slow-carb. Meditation microdosing distillery 8-bit humblebrag migas.
|
||||
'''
|
||||
|
||||
res =
|
||||
'''
|
||||
% Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha
|
||||
% banh mi, cold-pressed retro whatever ethical man braid asymmetrical
|
||||
% fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa
|
||||
% leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually
|
||||
% aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial
|
||||
% letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade,
|
||||
% tacos pickled fanny pack literally meh pinterest slow-carb. Meditation
|
||||
% microdosing distillery 8-bit humblebrag migas.
|
||||
'''
|
||||
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it "properly reflows roxygen comments ", ->
|
||||
text =
|
||||
'''
|
||||
#' Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade, tacos pickled fanny pack literally meh pinterest slow-carb. Meditation microdosing distillery 8-bit humblebrag migas.
|
||||
'''
|
||||
|
||||
res =
|
||||
'''
|
||||
#' Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard
|
||||
#' sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical
|
||||
#' fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest
|
||||
#' quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro
|
||||
#' actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia
|
||||
#' sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher
|
||||
#' direct trade, tacos pickled fanny pack literally meh pinterest slow-carb.
|
||||
#' Meditation microdosing distillery 8-bit humblebrag migas.
|
||||
'''
|
||||
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it "properly reflows -- comments ", ->
|
||||
text =
|
||||
'''
|
||||
-- Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade, tacos pickled fanny pack literally meh pinterest slow-carb. Meditation microdosing distillery 8-bit humblebrag migas.
|
||||
'''
|
||||
|
||||
res =
|
||||
'''
|
||||
-- Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard
|
||||
-- sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical
|
||||
-- fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest
|
||||
-- quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro
|
||||
-- actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia
|
||||
-- sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher
|
||||
-- direct trade, tacos pickled fanny pack literally meh pinterest slow-carb.
|
||||
-- Meditation microdosing distillery 8-bit humblebrag migas.
|
||||
'''
|
||||
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it "properly reflows ||| comments ", ->
|
||||
text =
|
||||
'''
|
||||
||| Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade, tacos pickled fanny pack literally meh pinterest slow-carb. Meditation microdosing distillery 8-bit humblebrag migas.
|
||||
'''
|
||||
|
||||
res =
|
||||
'''
|
||||
||| Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard
|
||||
||| sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical
|
||||
||| fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest
|
||||
||| quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro
|
||||
||| actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia
|
||||
||| sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher
|
||||
||| direct trade, tacos pickled fanny pack literally meh pinterest slow-carb.
|
||||
||| Meditation microdosing distillery 8-bit humblebrag migas.
|
||||
'''
|
||||
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it 'properly reflows ;; comments ', ->
|
||||
text =
|
||||
'''
|
||||
;; Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade, tacos pickled fanny pack literally meh pinterest slow-carb. Meditation microdosing distillery 8-bit humblebrag migas.
|
||||
'''
|
||||
|
||||
res =
|
||||
'''
|
||||
;; Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard
|
||||
;; sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical
|
||||
;; fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest
|
||||
;; quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro
|
||||
;; actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia
|
||||
;; sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher
|
||||
;; direct trade, tacos pickled fanny pack literally meh pinterest slow-carb.
|
||||
;; Meditation microdosing distillery 8-bit humblebrag migas.
|
||||
'''
|
||||
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it 'does not treat lines starting with a single semicolon as ;; comments', ->
|
||||
text =
|
||||
'''
|
||||
;! Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade, tacos pickled fanny pack literally meh pinterest slow-carb. Meditation microdosing distillery 8-bit humblebrag migas.
|
||||
'''
|
||||
|
||||
res =
|
||||
'''
|
||||
;! Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard
|
||||
sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical
|
||||
fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa
|
||||
leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually
|
||||
aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial
|
||||
letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade,
|
||||
tacos pickled fanny pack literally meh pinterest slow-carb. Meditation
|
||||
microdosing distillery 8-bit humblebrag migas.
|
||||
'''
|
||||
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it 'properly reflows > ascii email inclusions ', ->
|
||||
text =
|
||||
'''
|
||||
> Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade, tacos pickled fanny pack literally meh pinterest slow-carb. Meditation microdosing distillery 8-bit humblebrag migas.
|
||||
'''
|
||||
|
||||
res =
|
||||
'''
|
||||
> Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha
|
||||
> banh mi, cold-pressed retro whatever ethical man braid asymmetrical
|
||||
> fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa
|
||||
> leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually
|
||||
> aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial
|
||||
> letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade,
|
||||
> tacos pickled fanny pack literally meh pinterest slow-carb. Meditation
|
||||
> microdosing distillery 8-bit humblebrag migas.
|
||||
'''
|
||||
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it "doesn't allow special characters to surpass wrapColumn", ->
|
||||
test =
|
||||
'''
|
||||
Imagine that I'm writing some LaTeX code. I start a comment, but change my mind. %
|
||||
|
||||
Now I'm just kind of trucking along, doing some math and stuff. For instance, $3 + 4 = 7$. But maybe I'm getting really crazy and I use subtraction. It's kind of an obscure technique, but often it goes a bit like this: let $x = 2 + 2$, so $x - 1 = 3$ (quick maths).
|
||||
|
||||
That's OK I guess, but now look at this cool thing called set theory: $\\{n + 42 : n \\in \\mathbb{N}\\}$. Wow. Neat. But we all know why we're really here. If you peer deep down into your heart, and you stare into the depths of yourself: is $P = NP$? Beware, though; many have tried and failed to answer this question. It is by no means for the faint of heart.
|
||||
'''
|
||||
|
||||
res =
|
||||
'''
|
||||
Imagine that I'm writing some LaTeX code. I start a comment, but change my mind.
|
||||
%
|
||||
|
||||
Now I'm just kind of trucking along, doing some math and stuff. For instance, $3
|
||||
+ 4 = 7$. But maybe I'm getting really crazy and I use subtraction. It's kind of
|
||||
an obscure technique, but often it goes a bit like this: let $x = 2 + 2$, so $x
|
||||
- 1 = 3$ (quick maths).
|
||||
|
||||
That's OK I guess, but now look at this cool thing called set theory: $\\{n + 42
|
||||
: n \\in \\mathbb{N}\\}$. Wow. Neat. But we all know why we're really here. If you
|
||||
peer deep down into your heart, and you stare into the depths of yourself: is $P
|
||||
= NP$? Beware, though; many have tried and failed to answer this question. It is
|
||||
by no means for the faint of heart.
|
||||
'''
|
||||
|
||||
expect(autoflow.reflow(test, wrapColumn: 80)).toEqual res
|
||||
|
||||
describe 'LaTeX', ->
|
||||
it 'properly reflows text around LaTeX tags', ->
|
||||
text =
|
||||
'''
|
||||
\\begin{verbatim}
|
||||
Lorem ipsum dolor sit amet, nisl odio amet, et tempor netus neque at at blandit, vel vestibulum libero dolor, semper lobortis ligula praesent. Eget condimentum integer, porta sagittis nam, fusce vitae a vitae augue. Nec semper quis sed ut, est porttitor praesent. Nisl velit quam dolore velit quam, elementum neque pellentesque pulvinar et vestibulum.
|
||||
\\end{verbatim}
|
||||
'''
|
||||
|
||||
res =
|
||||
'''
|
||||
\\begin{verbatim}
|
||||
Lorem ipsum dolor sit amet, nisl odio amet, et tempor netus neque at at
|
||||
blandit, vel vestibulum libero dolor, semper lobortis ligula praesent. Eget
|
||||
condimentum integer, porta sagittis nam, fusce vitae a vitae augue. Nec
|
||||
semper quis sed ut, est porttitor praesent. Nisl velit quam dolore velit
|
||||
quam, elementum neque pellentesque pulvinar et vestibulum.
|
||||
\\end{verbatim}
|
||||
'''
|
||||
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it 'properly reflows text inside LaTeX tags', ->
|
||||
text =
|
||||
'''
|
||||
\\item{
|
||||
Lorem ipsum dolor sit amet, nisl odio amet, et tempor netus neque at at blandit, vel vestibulum libero dolor, semper lobortis ligula praesent. Eget condimentum integer, porta sagittis nam, fusce vitae a vitae augue. Nec semper quis sed ut, est porttitor praesent. Nisl velit quam dolore velit quam, elementum neque pellentesque pulvinar et vestibulum.
|
||||
}
|
||||
'''
|
||||
|
||||
res =
|
||||
'''
|
||||
\\item{
|
||||
Lorem ipsum dolor sit amet, nisl odio amet, et tempor netus neque at at
|
||||
blandit, vel vestibulum libero dolor, semper lobortis ligula praesent. Eget
|
||||
condimentum integer, porta sagittis nam, fusce vitae a vitae augue. Nec
|
||||
semper quis sed ut, est porttitor praesent. Nisl velit quam dolore velit
|
||||
quam, elementum neque pellentesque pulvinar et vestibulum.
|
||||
}
|
||||
'''
|
||||
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it 'properly reflows text inside nested LaTeX tags', ->
|
||||
text =
|
||||
'''
|
||||
\\begin{enumerate}[label=(\\alph*)]
|
||||
\\item{
|
||||
Lorem ipsum dolor sit amet, nisl odio amet, et tempor netus neque at at blandit, vel vestibulum libero dolor, semper lobortis ligula praesent. Eget condimentum integer, porta sagittis nam, fusce vitae a vitae augue. Nec semper quis sed ut, est porttitor praesent. Nisl velit quam dolore velit quam, elementum neque pellentesque pulvinar et vestibulum.
|
||||
}
|
||||
\\end{enumerate}
|
||||
'''
|
||||
|
||||
res =
|
||||
'''
|
||||
\\begin{enumerate}[label=(\\alph*)]
|
||||
\\item{
|
||||
Lorem ipsum dolor sit amet, nisl odio amet, et tempor netus neque at at
|
||||
blandit, vel vestibulum libero dolor, semper lobortis ligula praesent.
|
||||
Eget condimentum integer, porta sagittis nam, fusce vitae a vitae augue.
|
||||
Nec semper quis sed ut, est porttitor praesent. Nisl velit quam dolore
|
||||
velit quam, elementum neque pellentesque pulvinar et vestibulum.
|
||||
}
|
||||
\\end{enumerate}
|
||||
'''
|
||||
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it 'does not attempt to reflow a selection that contains only LaTeX tags and nothing else', ->
|
||||
text =
|
||||
'''
|
||||
\\begin{enumerate}
|
||||
\\end{enumerate}
|
||||
'''
|
||||
|
||||
expect(autoflow.reflow(text, wrapColumn: 5)).toEqual text
|
683
packages/autoflow/spec/autoflow-spec.js
Normal file
683
packages/autoflow/spec/autoflow-spec.js
Normal file
@ -0,0 +1,683 @@
|
||||
|
||||
describe("Autoflow package", () => {
|
||||
let [autoflow, editor, editorElement] = [];
|
||||
const tabLength = 4;
|
||||
|
||||
describe("autoflow:reflow-selection", () => {
|
||||
beforeEach(() => {
|
||||
let activationPromise = null;
|
||||
|
||||
waitsForPromise(() => atom.workspace.open());
|
||||
|
||||
runs(() => {
|
||||
editor = atom.workspace.getActiveTextEditor();
|
||||
editorElement = atom.views.getView(editor);
|
||||
|
||||
atom.config.set('editor.preferredLineLength', 30);
|
||||
atom.config.set('editor.tabLength', tabLength);
|
||||
|
||||
activationPromise = atom.packages.activatePackage('autoflow');
|
||||
|
||||
atom.commands.dispatch(editorElement, 'autoflow:reflow-selection');
|
||||
});
|
||||
|
||||
waitsForPromise(() => activationPromise);
|
||||
});
|
||||
|
||||
it("uses the preferred line length based on the editor's scope", () => {
|
||||
atom.config.set('editor.preferredLineLength', 4, {scopeSelector: '.text.plain.null-grammar'});
|
||||
editor.setText("foo bar");
|
||||
editor.selectAll();
|
||||
atom.commands.dispatch(editorElement, 'autoflow:reflow-selection');
|
||||
|
||||
expect(editor.getText()).toBe(`\
|
||||
foo
|
||||
bar\
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
it("rearranges line breaks in the current selection to ensure lines are shorter than config.editor.preferredLineLength honoring tabLength", () => {
|
||||
editor.setText("\t\tThis is the first paragraph and it is longer than the preferred line length so it should be reflowed.\n\n\t\tThis is a short paragraph.\n\n\t\tAnother long paragraph, it should also be reflowed with the use of this single command.");
|
||||
|
||||
editor.selectAll();
|
||||
atom.commands.dispatch(editorElement, 'autoflow:reflow-selection');
|
||||
|
||||
const exedOut = editor.getText().replace(/\t/g, Array(tabLength+1).join('X'));
|
||||
expect(exedOut).toBe("XXXXXXXXThis is the first\nXXXXXXXXparagraph and it is\nXXXXXXXXlonger than the\nXXXXXXXXpreferred line length\nXXXXXXXXso it should be\nXXXXXXXXreflowed.\n\nXXXXXXXXThis is a short\nXXXXXXXXparagraph.\n\nXXXXXXXXAnother long\nXXXXXXXXparagraph, it should\nXXXXXXXXalso be reflowed with\nXXXXXXXXthe use of this single\nXXXXXXXXcommand.");
|
||||
});
|
||||
|
||||
it("rearranges line breaks in the current selection to ensure lines are shorter than config.editor.preferredLineLength", () => {
|
||||
editor.setText(`\
|
||||
This is the first paragraph and it is longer than the preferred line length so it should be reflowed.
|
||||
|
||||
This is a short paragraph.
|
||||
|
||||
Another long paragraph, it should also be reflowed with the use of this single command.\
|
||||
`
|
||||
);
|
||||
|
||||
editor.selectAll();
|
||||
atom.commands.dispatch(editorElement, 'autoflow:reflow-selection');
|
||||
|
||||
expect(editor.getText()).toBe(`\
|
||||
This is the first paragraph
|
||||
and it is longer than the
|
||||
preferred line length so it
|
||||
should be reflowed.
|
||||
|
||||
This is a short paragraph.
|
||||
|
||||
Another long paragraph, it
|
||||
should also be reflowed with
|
||||
the use of this single
|
||||
command.\
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
it("is not confused when the selection boundary is between paragraphs", () => {
|
||||
editor.setText(`\
|
||||
v--- SELECTION STARTS AT THE BEGINNING OF THE NEXT LINE (pos 1,0)
|
||||
|
||||
The preceding newline should not be considered part of this paragraph.
|
||||
|
||||
The newline at the end of this paragraph should be preserved and not
|
||||
converted into a space.
|
||||
|
||||
^--- SELECTION ENDS AT THE BEGINNING OF THE PREVIOUS LINE (pos 6,0)\
|
||||
`
|
||||
);
|
||||
|
||||
editor.setCursorBufferPosition([1, 0]);
|
||||
editor.selectToBufferPosition([6, 0]);
|
||||
atom.commands.dispatch(editorElement, 'autoflow:reflow-selection');
|
||||
|
||||
expect(editor.getText()).toBe(`\
|
||||
v--- SELECTION STARTS AT THE BEGINNING OF THE NEXT LINE (pos 1,0)
|
||||
|
||||
The preceding newline should
|
||||
not be considered part of this
|
||||
paragraph.
|
||||
|
||||
The newline at the end of this
|
||||
paragraph should be preserved
|
||||
and not converted into a
|
||||
space.
|
||||
|
||||
^--- SELECTION ENDS AT THE BEGINNING OF THE PREVIOUS LINE (pos 6,0)\
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
it("reflows the current paragraph if nothing is selected", () => {
|
||||
editor.setText(`\
|
||||
This is a preceding paragraph, which shouldn't be modified by a reflow of the following paragraph.
|
||||
|
||||
The quick brown fox jumps over the lazy
|
||||
dog. The preceding sentence contains every letter
|
||||
in the entire English alphabet, which has absolutely no relevance
|
||||
to this test.
|
||||
|
||||
This is a following paragraph, which shouldn't be modified by a reflow of the preciding paragraph.
|
||||
\
|
||||
`
|
||||
);
|
||||
|
||||
editor.setCursorBufferPosition([3, 5]);
|
||||
atom.commands.dispatch(editorElement, 'autoflow:reflow-selection');
|
||||
|
||||
expect(editor.getText()).toBe(`\
|
||||
This is a preceding paragraph, which shouldn't be modified by a reflow of the following paragraph.
|
||||
|
||||
The quick brown fox jumps over
|
||||
the lazy dog. The preceding
|
||||
sentence contains every letter
|
||||
in the entire English
|
||||
alphabet, which has absolutely
|
||||
no relevance to this test.
|
||||
|
||||
This is a following paragraph, which shouldn't be modified by a reflow of the preciding paragraph.
|
||||
\
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
it("allows for single words that exceed the preferred wrap column length", () => {
|
||||
editor.setText("this-is-a-super-long-word-that-shouldn't-break-autoflow and these are some smaller words");
|
||||
|
||||
editor.selectAll();
|
||||
atom.commands.dispatch(editorElement, 'autoflow:reflow-selection');
|
||||
|
||||
expect(editor.getText()).toBe(`\
|
||||
this-is-a-super-long-word-that-shouldn't-break-autoflow
|
||||
and these are some smaller
|
||||
words\
|
||||
`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("reflowing text", () => {
|
||||
beforeEach(() => autoflow = require("../lib/autoflow"));
|
||||
|
||||
it('respects current paragraphs', () => {
|
||||
const text = `\
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida nibh id magna ullamcorper sagittis. Maecenas
|
||||
et enim eu orci tincidunt adipiscing
|
||||
aliquam ligula.
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
Phasellus gravida
|
||||
nibh id magna ullamcorper
|
||||
tincidunt adipiscing lacinia a dui. Etiam quis erat dolor.
|
||||
rutrum nisl fermentum rhoncus. Duis blandit ligula facilisis fermentum.\
|
||||
`;
|
||||
|
||||
const res = `\
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida nibh
|
||||
id magna ullamcorper sagittis. Maecenas et enim eu orci tincidunt adipiscing
|
||||
aliquam ligula.
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida nibh
|
||||
id magna ullamcorper tincidunt adipiscing lacinia a dui. Etiam quis erat dolor.
|
||||
rutrum nisl fermentum rhoncus. Duis blandit ligula facilisis fermentum.\
|
||||
`;
|
||||
expect(autoflow.reflow(text, {wrapColumn: 80})).toEqual(res);
|
||||
});
|
||||
|
||||
it('respects indentation', () => {
|
||||
const text = `\
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida nibh id magna ullamcorper sagittis. Maecenas
|
||||
et enim eu orci tincidunt adipiscing
|
||||
aliquam ligula.
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
Phasellus gravida
|
||||
nibh id magna ullamcorper
|
||||
tincidunt adipiscing lacinia a dui. Etiam quis erat dolor.
|
||||
rutrum nisl fermentum rhoncus. Duis blandit ligula facilisis fermentum\
|
||||
`;
|
||||
|
||||
const res = `\
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida nibh
|
||||
id magna ullamcorper sagittis. Maecenas et enim eu orci tincidunt adipiscing
|
||||
aliquam ligula.
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida
|
||||
nibh id magna ullamcorper tincidunt adipiscing lacinia a dui. Etiam quis
|
||||
erat dolor. rutrum nisl fermentum rhoncus. Duis blandit ligula facilisis
|
||||
fermentum\
|
||||
`;
|
||||
expect(autoflow.reflow(text, {wrapColumn: 80})).toEqual(res);
|
||||
});
|
||||
|
||||
it('respects prefixed text (comments!)', () => {
|
||||
const text = `\
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida nibh id magna ullamcorper sagittis. Maecenas
|
||||
et enim eu orci tincidunt adipiscing
|
||||
aliquam ligula.
|
||||
|
||||
# Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
# Phasellus gravida
|
||||
# nibh id magna ullamcorper
|
||||
# tincidunt adipiscing lacinia a dui. Etiam quis erat dolor.
|
||||
# rutrum nisl fermentum rhoncus. Duis blandit ligula facilisis fermentum\
|
||||
`;
|
||||
|
||||
const res = `\
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida nibh
|
||||
id magna ullamcorper sagittis. Maecenas et enim eu orci tincidunt adipiscing
|
||||
aliquam ligula.
|
||||
|
||||
# Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida
|
||||
# nibh id magna ullamcorper tincidunt adipiscing lacinia a dui. Etiam quis
|
||||
# erat dolor. rutrum nisl fermentum rhoncus. Duis blandit ligula facilisis
|
||||
# fermentum\
|
||||
`;
|
||||
expect(autoflow.reflow(text, {wrapColumn: 80})).toEqual(res);
|
||||
});
|
||||
|
||||
it('respects multiple prefixes (js/c comments)', () => {
|
||||
const text = `\
|
||||
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida
|
||||
et enim eu orci tincidunt adipiscing
|
||||
aliquam ligula.\
|
||||
`;
|
||||
|
||||
const res = `\
|
||||
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida et
|
||||
// enim eu orci tincidunt adipiscing aliquam ligula.\
|
||||
`;
|
||||
expect(autoflow.reflow(text, {wrapColumn: 80})).toEqual(res);
|
||||
});
|
||||
|
||||
it('properly handles * prefix', () => {
|
||||
const text = `\
|
||||
* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida
|
||||
et enim eu orci tincidunt adipiscing
|
||||
aliquam ligula.
|
||||
|
||||
* soidjfiojsoidj foi\
|
||||
`;
|
||||
|
||||
const res = `\
|
||||
* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida et
|
||||
* enim eu orci tincidunt adipiscing aliquam ligula.
|
||||
|
||||
* soidjfiojsoidj foi\
|
||||
`;
|
||||
expect(autoflow.reflow(text, {wrapColumn: 80})).toEqual(res);
|
||||
});
|
||||
|
||||
it("does not throw invalid regular expression errors (regression)", () => {
|
||||
const text = `\
|
||||
*** Lorem ipsum dolor sit amet\
|
||||
`;
|
||||
|
||||
expect(autoflow.reflow(text, {wrapColumn: 80})).toEqual(text);
|
||||
});
|
||||
|
||||
it('handles different initial indentation', () => {
|
||||
const text = `\
|
||||
Magna ea magna fugiat nisi minim in id duis. Culpa sit sint consequat quis elit magna pariatur incididunt
|
||||
proident laborum deserunt est aliqua reprehenderit. Occaecat et ex non do Lorem irure adipisicing mollit excepteur
|
||||
eu ullamco consectetur. Ex ex Lorem duis labore quis ad exercitation elit dolor non adipisicing. Pariatur commodo ullamco
|
||||
culpa dolor sunt enim. Ullamco dolore do ea nulla ut commodo minim consequat cillum ad velit quis.\
|
||||
`;
|
||||
|
||||
const res = `\
|
||||
Magna ea magna fugiat nisi minim in id duis. Culpa sit sint consequat quis elit
|
||||
magna pariatur incididunt proident laborum deserunt est aliqua reprehenderit.
|
||||
Occaecat et ex non do Lorem irure adipisicing mollit excepteur eu ullamco
|
||||
consectetur. Ex ex Lorem duis labore quis ad exercitation elit dolor non
|
||||
adipisicing. Pariatur commodo ullamco culpa dolor sunt enim. Ullamco dolore do
|
||||
ea nulla ut commodo minim consequat cillum ad velit quis.\
|
||||
`;
|
||||
expect(autoflow.reflow(text, {wrapColumn: 80})).toEqual(res);
|
||||
});
|
||||
|
||||
it('properly handles CRLF', () => {
|
||||
const text = "This is the first line and it is longer than the preferred line length so it should be reflowed.\r\nThis is a short line which should\r\nbe reflowed with the following line.\rAnother long line, it should also be reflowed with everything above it when it is all reflowed.";
|
||||
|
||||
const res =
|
||||
`\
|
||||
This is the first line and it is longer than the preferred line length so it
|
||||
should be reflowed. This is a short line which should be reflowed with the
|
||||
following line. Another long line, it should also be reflowed with everything
|
||||
above it when it is all reflowed.\
|
||||
`;
|
||||
expect(autoflow.reflow(text, {wrapColumn: 80})).toEqual(res);
|
||||
});
|
||||
|
||||
it('handles cyrillic text', () => {
|
||||
const text = `\
|
||||
В начале июля, в чрезвычайно жаркое время, под вечер, один молодой человек вышел из своей каморки, которую нанимал от жильцов в С-м переулке, на улицу и медленно, как бы в нерешимости, отправился к К-ну мосту.\
|
||||
`;
|
||||
|
||||
const res = `\
|
||||
В начале июля, в чрезвычайно жаркое время, под вечер, один молодой человек вышел
|
||||
из своей каморки, которую нанимал от жильцов в С-м переулке, на улицу и
|
||||
медленно, как бы в нерешимости, отправился к К-ну мосту.\
|
||||
`;
|
||||
|
||||
expect(autoflow.reflow(text, {wrapColumn: 80})).toEqual(res);
|
||||
});
|
||||
|
||||
it('handles `yo` character properly', () => {
|
||||
// Because there're known problems with this character in major regex engines
|
||||
const text = 'Ё Ё Ё';
|
||||
|
||||
const res = `\
|
||||
Ё
|
||||
Ё
|
||||
Ё\
|
||||
`;
|
||||
|
||||
expect(autoflow.reflow(text, {wrapColumn: 2})).toEqual(res);
|
||||
});
|
||||
|
||||
it('properly reflows // comments ', () => {
|
||||
const text =
|
||||
`\
|
||||
// Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade, tacos pickled fanny pack literally meh pinterest slow-carb. Meditation microdosing distillery 8-bit humblebrag migas.\
|
||||
`;
|
||||
|
||||
const res =
|
||||
`\
|
||||
// Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard
|
||||
// sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical
|
||||
// fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest
|
||||
// quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro
|
||||
// actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia
|
||||
// sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher
|
||||
// direct trade, tacos pickled fanny pack literally meh pinterest slow-carb.
|
||||
// Meditation microdosing distillery 8-bit humblebrag migas.\
|
||||
`;
|
||||
|
||||
expect(autoflow.reflow(text, {wrapColumn: 80})).toEqual(res);
|
||||
});
|
||||
|
||||
it('properly reflows /* comments ', () => {
|
||||
const text =
|
||||
`\
|
||||
/* Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade, tacos pickled fanny pack literally meh pinterest slow-carb. Meditation microdosing distillery 8-bit humblebrag migas. */\
|
||||
`;
|
||||
|
||||
const res =
|
||||
`\
|
||||
/* Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard
|
||||
sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical
|
||||
fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest
|
||||
quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro
|
||||
actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia
|
||||
sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher
|
||||
direct trade, tacos pickled fanny pack literally meh pinterest slow-carb.
|
||||
Meditation microdosing distillery 8-bit humblebrag migas. */\
|
||||
`;
|
||||
|
||||
expect(autoflow.reflow(text, {wrapColumn: 80})).toEqual(res);
|
||||
});
|
||||
|
||||
it('properly reflows pound comments ', () => {
|
||||
const text =
|
||||
`\
|
||||
# Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade, tacos pickled fanny pack literally meh pinterest slow-carb. Meditation microdosing distillery 8-bit humblebrag migas.\
|
||||
`;
|
||||
|
||||
const res =
|
||||
`\
|
||||
# Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha
|
||||
# banh mi, cold-pressed retro whatever ethical man braid asymmetrical
|
||||
# fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa
|
||||
# leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually
|
||||
# aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial
|
||||
# letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade,
|
||||
# tacos pickled fanny pack literally meh pinterest slow-carb. Meditation
|
||||
# microdosing distillery 8-bit humblebrag migas.\
|
||||
`;
|
||||
|
||||
expect(autoflow.reflow(text, {wrapColumn: 80})).toEqual(res);
|
||||
});
|
||||
|
||||
it('properly reflows - list items ', () => {
|
||||
const text =
|
||||
`\
|
||||
- Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade, tacos pickled fanny pack literally meh pinterest slow-carb. Meditation microdosing distillery 8-bit humblebrag migas.\
|
||||
`;
|
||||
|
||||
const res =
|
||||
`\
|
||||
- Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha
|
||||
banh mi, cold-pressed retro whatever ethical man braid asymmetrical
|
||||
fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa
|
||||
leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually
|
||||
aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial
|
||||
letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade,
|
||||
tacos pickled fanny pack literally meh pinterest slow-carb. Meditation
|
||||
microdosing distillery 8-bit humblebrag migas.\
|
||||
`;
|
||||
|
||||
expect(autoflow.reflow(text, {wrapColumn: 80})).toEqual(res);
|
||||
});
|
||||
|
||||
it('properly reflows % comments ', () => {
|
||||
const text =
|
||||
`\
|
||||
% Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade, tacos pickled fanny pack literally meh pinterest slow-carb. Meditation microdosing distillery 8-bit humblebrag migas.\
|
||||
`;
|
||||
|
||||
const res =
|
||||
`\
|
||||
% Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha
|
||||
% banh mi, cold-pressed retro whatever ethical man braid asymmetrical
|
||||
% fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa
|
||||
% leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually
|
||||
% aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial
|
||||
% letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade,
|
||||
% tacos pickled fanny pack literally meh pinterest slow-carb. Meditation
|
||||
% microdosing distillery 8-bit humblebrag migas.\
|
||||
`;
|
||||
|
||||
expect(autoflow.reflow(text, {wrapColumn: 80})).toEqual(res);
|
||||
});
|
||||
|
||||
it("properly reflows roxygen comments ", () => {
|
||||
const text =
|
||||
`\
|
||||
#' Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade, tacos pickled fanny pack literally meh pinterest slow-carb. Meditation microdosing distillery 8-bit humblebrag migas.\
|
||||
`;
|
||||
|
||||
const res =
|
||||
`\
|
||||
#' Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard
|
||||
#' sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical
|
||||
#' fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest
|
||||
#' quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro
|
||||
#' actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia
|
||||
#' sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher
|
||||
#' direct trade, tacos pickled fanny pack literally meh pinterest slow-carb.
|
||||
#' Meditation microdosing distillery 8-bit humblebrag migas.\
|
||||
`;
|
||||
|
||||
expect(autoflow.reflow(text, {wrapColumn: 80})).toEqual(res);
|
||||
});
|
||||
|
||||
it("properly reflows -- comments ", () => {
|
||||
const text =
|
||||
`\
|
||||
-- Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade, tacos pickled fanny pack literally meh pinterest slow-carb. Meditation microdosing distillery 8-bit humblebrag migas.\
|
||||
`;
|
||||
|
||||
const res =
|
||||
`\
|
||||
-- Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard
|
||||
-- sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical
|
||||
-- fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest
|
||||
-- quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro
|
||||
-- actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia
|
||||
-- sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher
|
||||
-- direct trade, tacos pickled fanny pack literally meh pinterest slow-carb.
|
||||
-- Meditation microdosing distillery 8-bit humblebrag migas.\
|
||||
`;
|
||||
|
||||
expect(autoflow.reflow(text, {wrapColumn: 80})).toEqual(res);
|
||||
});
|
||||
|
||||
it("properly reflows ||| comments ", () => {
|
||||
const text =
|
||||
`\
|
||||
||| Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade, tacos pickled fanny pack literally meh pinterest slow-carb. Meditation microdosing distillery 8-bit humblebrag migas.\
|
||||
`;
|
||||
|
||||
const res =
|
||||
`\
|
||||
||| Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard
|
||||
||| sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical
|
||||
||| fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest
|
||||
||| quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro
|
||||
||| actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia
|
||||
||| sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher
|
||||
||| direct trade, tacos pickled fanny pack literally meh pinterest slow-carb.
|
||||
||| Meditation microdosing distillery 8-bit humblebrag migas.\
|
||||
`;
|
||||
|
||||
expect(autoflow.reflow(text, {wrapColumn: 80})).toEqual(res);
|
||||
});
|
||||
|
||||
it('properly reflows ;; comments ', () => {
|
||||
const text =
|
||||
`\
|
||||
;; Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade, tacos pickled fanny pack literally meh pinterest slow-carb. Meditation microdosing distillery 8-bit humblebrag migas.\
|
||||
`;
|
||||
|
||||
const res =
|
||||
`\
|
||||
;; Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard
|
||||
;; sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical
|
||||
;; fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest
|
||||
;; quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro
|
||||
;; actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia
|
||||
;; sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher
|
||||
;; direct trade, tacos pickled fanny pack literally meh pinterest slow-carb.
|
||||
;; Meditation microdosing distillery 8-bit humblebrag migas.\
|
||||
`;
|
||||
|
||||
expect(autoflow.reflow(text, {wrapColumn: 80})).toEqual(res);
|
||||
});
|
||||
|
||||
it('does not treat lines starting with a single semicolon as ;; comments', () => {
|
||||
const text =
|
||||
`\
|
||||
;! Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade, tacos pickled fanny pack literally meh pinterest slow-carb. Meditation microdosing distillery 8-bit humblebrag migas.\
|
||||
`;
|
||||
|
||||
const res =
|
||||
`\
|
||||
;! Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard
|
||||
sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical
|
||||
fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa
|
||||
leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually
|
||||
aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial
|
||||
letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade,
|
||||
tacos pickled fanny pack literally meh pinterest slow-carb. Meditation
|
||||
microdosing distillery 8-bit humblebrag migas.\
|
||||
`;
|
||||
|
||||
expect(autoflow.reflow(text, {wrapColumn: 80})).toEqual(res);
|
||||
});
|
||||
|
||||
it('properly reflows > ascii email inclusions ', () => {
|
||||
const text =
|
||||
`\
|
||||
> Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha banh mi, cold-pressed retro whatever ethical man braid asymmetrical fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade, tacos pickled fanny pack literally meh pinterest slow-carb. Meditation microdosing distillery 8-bit humblebrag migas.\
|
||||
`;
|
||||
|
||||
const res =
|
||||
`\
|
||||
> Beard pinterest actually brunch brooklyn jean shorts YOLO. Knausgaard sriracha
|
||||
> banh mi, cold-pressed retro whatever ethical man braid asymmetrical
|
||||
> fingerstache narwhal. Intelligentsia wolf photo booth, tumblr pinterest quinoa
|
||||
> leggings four loko poutine. DIY tattooed drinking vinegar, wolf retro actually
|
||||
> aesthetic austin keffiyeh marfa beard. Marfa trust fund salvia sartorial
|
||||
> letterpress, keffiyeh plaid butcher. Swag try-hard dreamcatcher direct trade,
|
||||
> tacos pickled fanny pack literally meh pinterest slow-carb. Meditation
|
||||
> microdosing distillery 8-bit humblebrag migas.\
|
||||
`;
|
||||
|
||||
expect(autoflow.reflow(text, {wrapColumn: 80})).toEqual(res);
|
||||
});
|
||||
|
||||
it("doesn't allow special characters to surpass wrapColumn", () => {
|
||||
const test =
|
||||
`\
|
||||
Imagine that I'm writing some LaTeX code. I start a comment, but change my mind. %
|
||||
|
||||
Now I'm just kind of trucking along, doing some math and stuff. For instance, $3 + 4 = 7$. But maybe I'm getting really crazy and I use subtraction. It's kind of an obscure technique, but often it goes a bit like this: let $x = 2 + 2$, so $x - 1 = 3$ (quick maths).
|
||||
|
||||
That's OK I guess, but now look at this cool thing called set theory: $\\{n + 42 : n \\in \\mathbb{N}\\}$. Wow. Neat. But we all know why we're really here. If you peer deep down into your heart, and you stare into the depths of yourself: is $P = NP$? Beware, though; many have tried and failed to answer this question. It is by no means for the faint of heart.\
|
||||
`;
|
||||
|
||||
const res =
|
||||
`\
|
||||
Imagine that I'm writing some LaTeX code. I start a comment, but change my mind.
|
||||
%
|
||||
|
||||
Now I'm just kind of trucking along, doing some math and stuff. For instance, $3
|
||||
+ 4 = 7$. But maybe I'm getting really crazy and I use subtraction. It's kind of
|
||||
an obscure technique, but often it goes a bit like this: let $x = 2 + 2$, so $x
|
||||
- 1 = 3$ (quick maths).
|
||||
|
||||
That's OK I guess, but now look at this cool thing called set theory: $\\{n + 42
|
||||
: n \\in \\mathbb{N}\\}$. Wow. Neat. But we all know why we're really here. If you
|
||||
peer deep down into your heart, and you stare into the depths of yourself: is $P
|
||||
= NP$? Beware, though; many have tried and failed to answer this question. It is
|
||||
by no means for the faint of heart.\
|
||||
`;
|
||||
|
||||
expect(autoflow.reflow(test, {wrapColumn: 80})).toEqual(res);
|
||||
});
|
||||
|
||||
describe('LaTeX', () => {
|
||||
it('properly reflows text around LaTeX tags', () => {
|
||||
const text =
|
||||
`\
|
||||
\\begin{verbatim}
|
||||
Lorem ipsum dolor sit amet, nisl odio amet, et tempor netus neque at at blandit, vel vestibulum libero dolor, semper lobortis ligula praesent. Eget condimentum integer, porta sagittis nam, fusce vitae a vitae augue. Nec semper quis sed ut, est porttitor praesent. Nisl velit quam dolore velit quam, elementum neque pellentesque pulvinar et vestibulum.
|
||||
\\end{verbatim}\
|
||||
`;
|
||||
|
||||
const res =
|
||||
`\
|
||||
\\begin{verbatim}
|
||||
Lorem ipsum dolor sit amet, nisl odio amet, et tempor netus neque at at
|
||||
blandit, vel vestibulum libero dolor, semper lobortis ligula praesent. Eget
|
||||
condimentum integer, porta sagittis nam, fusce vitae a vitae augue. Nec
|
||||
semper quis sed ut, est porttitor praesent. Nisl velit quam dolore velit
|
||||
quam, elementum neque pellentesque pulvinar et vestibulum.
|
||||
\\end{verbatim}\
|
||||
`;
|
||||
|
||||
expect(autoflow.reflow(text, {wrapColumn: 80})).toEqual(res);
|
||||
});
|
||||
|
||||
it('properly reflows text inside LaTeX tags', () => {
|
||||
const text =
|
||||
`\
|
||||
\\item{
|
||||
Lorem ipsum dolor sit amet, nisl odio amet, et tempor netus neque at at blandit, vel vestibulum libero dolor, semper lobortis ligula praesent. Eget condimentum integer, porta sagittis nam, fusce vitae a vitae augue. Nec semper quis sed ut, est porttitor praesent. Nisl velit quam dolore velit quam, elementum neque pellentesque pulvinar et vestibulum.
|
||||
}\
|
||||
`;
|
||||
|
||||
const res =
|
||||
`\
|
||||
\\item{
|
||||
Lorem ipsum dolor sit amet, nisl odio amet, et tempor netus neque at at
|
||||
blandit, vel vestibulum libero dolor, semper lobortis ligula praesent. Eget
|
||||
condimentum integer, porta sagittis nam, fusce vitae a vitae augue. Nec
|
||||
semper quis sed ut, est porttitor praesent. Nisl velit quam dolore velit
|
||||
quam, elementum neque pellentesque pulvinar et vestibulum.
|
||||
}\
|
||||
`;
|
||||
|
||||
expect(autoflow.reflow(text, {wrapColumn: 80})).toEqual(res);
|
||||
});
|
||||
|
||||
it('properly reflows text inside nested LaTeX tags', () => {
|
||||
const text =
|
||||
`\
|
||||
\\begin{enumerate}[label=(\\alph*)]
|
||||
\\item{
|
||||
Lorem ipsum dolor sit amet, nisl odio amet, et tempor netus neque at at blandit, vel vestibulum libero dolor, semper lobortis ligula praesent. Eget condimentum integer, porta sagittis nam, fusce vitae a vitae augue. Nec semper quis sed ut, est porttitor praesent. Nisl velit quam dolore velit quam, elementum neque pellentesque pulvinar et vestibulum.
|
||||
}
|
||||
\\end{enumerate}\
|
||||
`;
|
||||
|
||||
const res =
|
||||
`\
|
||||
\\begin{enumerate}[label=(\\alph*)]
|
||||
\\item{
|
||||
Lorem ipsum dolor sit amet, nisl odio amet, et tempor netus neque at at
|
||||
blandit, vel vestibulum libero dolor, semper lobortis ligula praesent.
|
||||
Eget condimentum integer, porta sagittis nam, fusce vitae a vitae augue.
|
||||
Nec semper quis sed ut, est porttitor praesent. Nisl velit quam dolore
|
||||
velit quam, elementum neque pellentesque pulvinar et vestibulum.
|
||||
}
|
||||
\\end{enumerate}\
|
||||
`;
|
||||
|
||||
expect(autoflow.reflow(text, {wrapColumn: 80})).toEqual(res);
|
||||
});
|
||||
|
||||
it('does not attempt to reflow a selection that contains only LaTeX tags and nothing else', () => {
|
||||
const text =
|
||||
`\
|
||||
\\begin{enumerate}
|
||||
\\end{enumerate}\
|
||||
`;
|
||||
|
||||
expect(autoflow.reflow(text, {wrapColumn: 5})).toEqual(text);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
1
packages/autosave/.gitignore
vendored
Normal file
1
packages/autosave/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
node_modules/
|
20
packages/autosave/LICENSE.md
Normal file
20
packages/autosave/LICENSE.md
Normal file
@ -0,0 +1,20 @@
|
||||
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.
|
32
packages/autosave/README.md
Normal file
32
packages/autosave/README.md
Normal file
@ -0,0 +1,32 @@
|
||||
# Autosave package
|
||||
|
||||
Autosaves editor when they lose focus, are destroyed, or when the window is closed.
|
||||
|
||||
This package is disabled by default and can be enabled via the `autosave.enabled` config
|
||||
setting or by checking *Enabled* in the settings for the *autosave* package in the
|
||||
Settings view.
|
||||
|
||||
## Service API
|
||||
The service exposes an object with a function `dontSaveIf`, which accepts a callback.
|
||||
Callbacks will be invoked with each pane item eligible for an autosave and if the callback
|
||||
returns true, the item will be skipped.
|
||||
|
||||
### Usage
|
||||
|
||||
#### package.json
|
||||
``` json
|
||||
"consumedServices": {
|
||||
"autosave": {
|
||||
"versions": {
|
||||
"1.0.0": "consumeAutosave"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### package initialize
|
||||
``` javascript
|
||||
consumeAutosave({dontSaveIf}) {
|
||||
dontSaveIf(paneItem -> paneItem.getPath() === '/dont/autosave/me.coffee')
|
||||
}
|
||||
```
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user