Merge master

This commit is contained in:
Andrew Dupont 2023-03-23 11:20:56 -07:00
commit 5be09d79fb
33 changed files with 26304 additions and 2851 deletions

View File

@ -6,7 +6,7 @@ linux_task:
container:
image: node:16-slim
memory: 8G
test_script:
prepare_script:
- apt-get update
- export DEBIAN_FRONTEND="noninteractive"
- apt-get install -y
@ -26,9 +26,16 @@ linux_task:
- 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:
@ -37,10 +44,6 @@ linux_task:
path: report.xml
type: text/xml
format: junit
build_binary_script:
- yarn dist || yarn dist
binary_artifacts:
path: ./binaries/*
arm_linux_task:
alias: linux
@ -77,7 +80,9 @@ arm_linux_task:
- 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
- rm -Rf node-modules/electron && yarn install --check-files
@ -107,16 +112,27 @@ silicon_mac_task:
APPLEID: ENCRYPTED[549ce052bd5666dba5245f4180bf93b74ed206fe5e6e7c8f67a8596d3767c1f682b84e347b326ac318c62a07c8844a57]
APPLEID_PASSWORD: ENCRYPTED[774c3307fd3b62660ecf5beb8537a24498c76e8d90d7f28e5bc816742fd8954a34ffed13f9aa2d1faf66ce08b4496e6f]
TEAM_ID: ENCRYPTED[11f3fedfbaf4aff1859bf6c105f0437ace23d84f5420a2c1cea884fbfa43b115b7834a463516d50cb276d4c4d9128b49]
test_script:
prepare_script:
- brew install node@16 yarn git python@$PYTHON_VERSION
- git submodule init
- git submodule update
- ln -s /opt/homebrew/bin/python$PYTHON_VERSION /opt/homebrew/bin/python
- export PATH="/opt/homebrew/bin:/opt/homebrew/opt/node@16/bin:$PATH"
- sed -i -e "s/[0-9]*-dev/`date -u +%Y%m%d%H`/g" package.json
install_script:
- export PATH="/opt/homebrew/bin:/opt/homebrew/opt/node@16/bin:$PATH"
- yarn install || yarn install
build_script:
- export PATH="/opt/homebrew/bin:/opt/homebrew/opt/node@16/bin:$PATH"
- yarn build
- yarn run build:apm
build_binary_script:
- export PATH="/opt/homebrew/bin:/opt/homebrew/opt/node@16/bin:$PATH"
- yarn dist || yarn dist
binary_artifacts:
path: ./binaries/*
test_script:
- export PATH="/opt/homebrew/bin:/opt/homebrew/opt/node@16/bin:$PATH"
- PLAYWRIGHT_JUNIT_OUTPUT_NAME=report.xml npx playwright test --reporter=junit,list
always:
videos_artifacts:
@ -125,11 +141,6 @@ silicon_mac_task:
path: report.xml
type: text/xml
format: junit
build_arm_binary_script:
- export PATH="/opt/homebrew/bin:/opt/homebrew/opt/node@16/bin:$PATH"
- yarn dist || yarn dist
binary_artifacts:
path: ./binaries/*
intel_mac_task:
alias: mac
@ -142,7 +153,7 @@ intel_mac_task:
APPLEID: ENCRYPTED[549ce052bd5666dba5245f4180bf93b74ed206fe5e6e7c8f67a8596d3767c1f682b84e347b326ac318c62a07c8844a57]
APPLEID_PASSWORD: ENCRYPTED[774c3307fd3b62660ecf5beb8537a24498c76e8d90d7f28e5bc816742fd8954a34ffed13f9aa2d1faf66ce08b4496e6f]
TEAM_ID: ENCRYPTED[11f3fedfbaf4aff1859bf6c105f0437ace23d84f5420a2c1cea884fbfa43b115b7834a463516d50cb276d4c4d9128b49]
dist_script:
prepare_script:
- sudo rm -rf /Library/Developer/CommandLineTools
- echo A | softwareupdate --install-rosetta
- arch -x86_64 xcode-select --install
@ -153,9 +164,14 @@ intel_mac_task:
- 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:
@ -178,27 +194,18 @@ windows_task:
env:
CIRRUS_SHELL: bash
PATH: C:\Python310\Scripts\;C:\Python310\;%PATH%;C:\Program Files\nodejs\;C:\Program Files\Git\cmd;C:\Users\User\AppData\Local\Microsoft\WindowsApps;C:\Users\User\AppData\Roaming\npm;C:\Program Files\Microsoft Visual Studio\2022\Community\Msbuild\Current\Bin\
install_deps_script:
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'
build_apm_script:
- cd ppm; npm install
install_with_scripts_script:
install_script:
- npx yarn install --ignore-engines || sleep 1 && npx yarn install --ignore-engines || echo "There is a reason for so many tries"
#install_without_scripts_script:
# - npx yarn install --ignore-scripts --ignore-engines || sleep 1 && npx yarn install --ignore-engines --ignore-scripts || sleep 2 && npx yarn cache clean; npx yarn install --ignore-engines --ignore-scripts || sleep 2 && npx yarn install --ignore-engines --ignore-scripts || echo "Giving up"
rebuild_for_electron_script:
build_script:
- npx yarn build:apm
- npx yarn build || npx yarn build || npx yarn build
# install_only_electron_script:
# - rm -R node_modules/electron
# - npx yarn install --ignore-engines || npx yarn install --ignore-engines || npx yarn cache clean; npx yarn install --ignore-engines || npx yarn install --ignore-engines
# - npx playwright test --reporter=list
videos_artifacts:
path: tests\videos\**
build_binary_script:
- sed -i -e "s/[0-9]*-dev/`date -u +%Y%m%d%H`/g" package.json
- npx yarn dist || npx yarn dist || npx yarn dist

View File

@ -37,7 +37,7 @@ jobs:
uses: actions/cache@v3
with:
path: pulsar.deb
key: pulsar-$env:GITHUB_SHA
key: pulsar-${{ github.sha }}
test:
name: Package
@ -159,7 +159,7 @@ jobs:
uses: actions/cache@v3
with:
path: pulsar.deb
key: pulsar-$env:GITHUB_SHA
key: pulsar-${{ github.sha }}
- name: Install Pulsar
run: sudo dpkg -i pulsar.deb && sudo apt-get -f install -y

View File

@ -8,13 +8,53 @@
- The settings-view package now lists a packages snippets more accurately
## 1.103.0
- Added a new feature to Search for Pulsar's settings
- Updated the completions provided by `autocomplete-css` to be as bleeding edge as possible.
- Updated the instructions and look of the login flow for the `github` package.
- Snippet transformations no longer have an implied global flag, bringing them into compatibility with snippets in most other editors.
- Snippets can now be given command names instead of tab triggers, and thus can now be assigned to key shortcuts in `keymap.cson`.
### Pulsar
- Added: feature: Implement Search Settings Ability [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/416)
- Added: Show Settings Icon in Status Bar [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/421)
- Added: Add Automated updated of `autocomplete-css` `completions.json` [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/398)
- Bumped: ppm: Update submodule to 9af239277180f2a9ee9e86714 [@Spiker985](https://github.com/pulsar-edit/pulsar/pull/403)
- Bumped: ppm: Update submodule to 915cbf6e5f9ea1141ef5dcaf8 [@DeeDeeG](https://github.com/pulsar-edit/pulsar/pull/418)
- Bumped: deps: Bump github to v0.36.15-pretranspiled [@DeeDeeG](https://github.com/pulsar-edit/pulsar/pull/415)
- Added: actually cache based on sha [@Meadowsys](https://github.com/pulsar-edit/pulsar/pull/412)
- Bumped: Bump `snippets` to bb00f9 [@savetheclocktower](https://github.com/pulsar-edit/pulsar/pull/408)
- Added: [skip-ci] Small Readme Touchup [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/404)
- Added: json language - add .har extension [@wesinator](https://github.com/pulsar-edit/pulsar/pull/396)
- Added: Bundle `markdown-preview`, `styleguide`, `wrap-guide` [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/374)
- Added: Add GitHub Token to Doc CI [@Spiker985](https://github.com/pulsar-edit/pulsar/pull/400)
- Added: Add Setup Node to Package Tests [@confused-Techie](https://github.com/pulsar-edit/pulsar/pull/399)
- Added: feat: add dev.pulsar_edit.Pulsar.metainfo.xml [@cat-master21](https://github.com/pulsar-edit/pulsar/pull/380)
### Snippets
- Added: Add `command` property that registers a command name for a snippet [@savetheclocktower](https://github.com/pulsar-edit/snippets/pull/10)
- Removed: Remove implicit `g` flag from snippet transformations [@savetheclocktower](https://github.com/pulsar-edit/snippets/pull/7)
- Fixed: Fix failing specs [@mauricioszabo](https://github.com/pulsar-edit/snippets/pull/6)
- Added: cleanup and rename [@Sertonix](https://github.com/pulsar-edit/snippets/pull/5)
### Github
- Added: rebrand git-tab-view [@icecream17](https://github.com/pulsar-edit/github/pull/17)
- Added: lib: Update login instructions for PATs, not OAuth [@DeeDeeG](https://github.com/pulsar-edit/github/pull/15)
### PPM
- Fixed: src: Update default Pulsar install paths [@DeeDeeG](https://github.com/pulsar-edit/ppm/pull/49)
- Bumped: deps: Upgrade npm to 6.14.18 [@DeeDeeG](https://github.com/pulsar-edit/ppm/pull/53)
- Fixed: Fix installing with yarn on Windows [@DeeDeeG](https://github.com/pulsar-edit/ppm/pull/58)
- Fixed: Fix inability to notice newer versions of git-installed packages [@savetheclocktower](https://github.com/pulsar-edit/ppm/pull/59)
- Added: meta: Actually sync yarn.lock [@DeeDeeG](https://github.com/pulsar-edit/ppm/pull/60)
## 1.102.0
- Fixed a bug where `pulsar` on Windows could never trigger
- Fixed `github` package shelling out to `git` on macOS
- Fixed minor bugs found during fixes to tests
- Improved our testing infastructure to aide in finding and fixing further bugs
- Improved our testing infrastructure to aide in finding and fixing further bugs
- Updated many dependencies of Pulsar and its core packages
- New Pulsar Icon on macOS
- Selected text is styled by default

View File

@ -14,6 +14,90 @@
<dl>
<dt><a href="#etch">etch</a></dt>
<dd></dd>
<dt><a href="#css">css</a></dt>
<dd><p>This file will manage the updating of <code>autocomplete-css</code> <code>completions.json</code>.
We will mainly utilize <code>@webref/css</code>.listAll() function that returns a full CSS
list of all properties seperated by their spec shortname. An example
of this format is defined below for ease of future modifications.</p>
<p> Some important notes about the data contained here:
- Often times the <code>value</code> within the <code>property</code> will be in the following format:
<code>&lt;valueGroupName&gt;</code> or even <code>&lt;valueGroupName&gt; | value | value2</code> or just <code>value | value2</code>
It will be important to build a parser that can handle this format.
The <code>&lt;valueGroupName&gt;</code> then can be realized via that specs <code>values</code> where
<code>values[x].name</code> will match the <code>&lt;valueGroupName&gt;</code>. Another important note about
handling values here is that oftentimes <code>values[x].values[]</code> won&#39;t actually
contain all possible values. And instead this must be handled by checking
<code>values[x].value</code> which is another string of <code>&lt;valueGroupName&gt; | value</code>.
So this should be handled by the same parser.
- Additionally an important note is that nowhere in this data do we get any kind
of description about the data that could lend a hand in being documentation.
So the documentation must be gathered seperatly. Likely the best way to collect
our documentation data is via <code>mdn/content</code>.
Within <code>content/files/en-us/web/css</code> is a directory of folders titled
by the name of properties.</p>
<pre><code>The last important thing to note here:
MDN doesn&#39;t have docs on everything. And that&#39;s a good thing. But it means
many of our items don&#39;t have any kind of description. For this situation
we have `manual-property-desc.json` which is a list of manually updated
descriptions for properties where there are none. This was a last resort
intended to provide the highest quality of completions possible.
Overtime many items on this list will likely be able to be removed just as
new ones are added. After running the update script you&#39;ll see a warning
saying how many properties are without completions that would then need to
be added to the JSON file.
</code></pre>
<p> &quot;spec-shortname&quot;: {
&quot;spec&quot;: {
&quot;title&quot;: &quot;&quot;,
&quot;url&quot;: &quot;&quot;
},
&quot;properties&quot;: [
{
&quot;name&quot;: &quot;&quot;,
&quot;value&quot;: &quot;&quot;,
&quot;initial&quot;: &quot;&quot;,
&quot;appliesTo&quot;: &quot;&quot;,
&quot;percentages&quot;: &quot;&quot;,
&quot;computedValue&quot;: &quot;&quot;,
&quot;canonicalOrder&quot;: &quot;&quot;,
&quot;animationType&quot;: &quot;&quot;,
&quot;media&quot;: &quot;&quot;,
&quot;styleDeclaration&quot;: [ &quot;&quot;, &quot;&quot;, &quot;&quot; ]
}
],
&quot;atrules&quot;: [
{
&quot;name&quot;: &quot;&quot;,
&quot;descriptors&quot;: [
{
&quot;name&quot;: &quot;&quot;,
&quot;for&quot;: &quot;&quot;,
&quot;value&quot;: &quot;&quot;,
&quot;type&quot;: &quot;&quot;
}
]
}
],
&quot;selectors&quot;: [],
&quot;values&quot;: [
{
&quot;name&quot;: &quot;&quot;,
&quot;type&quot;: &quot;&quot;,
&quot;prose&quot;: &quot;Optional description&quot;,
&quot;value&quot;: &quot;&quot;,
&quot;values&quot;: [
{
&quot;name&quot;: &quot;&quot;,
&quot;prose&quot;: &quot;Optional Description&quot;,
&quot;type&quot;: &quot;&quot;,
&quot;value&quot;: &quot;&quot;
}
]
}
],
&quot;warnings&quot;: []
}</p>
</dd>
<dt><a href="#fs">fs</a></dt>
<dd></dd>
<dt><a href="#dalek">dalek</a></dt>
@ -204,6 +288,95 @@ console.log(atom.clipboard.read());
## etch
**Kind**: global constant
**Jsx**: etch.dom
<a name="css"></a>
## css
This file will manage the updating of `autocomplete-css` `completions.json`.
We will mainly utilize `@webref/css`.listAll() function that returns a full CSS
list of all properties seperated by their spec shortname. An example
of this format is defined below for ease of future modifications.
Some important notes about the data contained here:
- Often times the `value` within the `property` will be in the following format:
`<valueGroupName>` or even `<valueGroupName> | value | value2` or just `value | value2`
It will be important to build a parser that can handle this format.
The `<valueGroupName>` then can be realized via that specs `values` where
`values[x].name` will match the `<valueGroupName>`. Another important note about
handling values here is that oftentimes `values[x].values[]` won't actually
contain all possible values. And instead this must be handled by checking
`values[x].value` which is another string of `<valueGroupName> | value`.
So this should be handled by the same parser.
- Additionally an important note is that nowhere in this data do we get any kind
of description about the data that could lend a hand in being documentation.
So the documentation must be gathered seperatly. Likely the best way to collect
our documentation data is via `mdn/content`.
Within `content/files/en-us/web/css` is a directory of folders titled
by the name of properties.
The last important thing to note here:
MDN doesn't have docs on everything. And that's a good thing. But it means
many of our items don't have any kind of description. For this situation
we have `manual-property-desc.json` which is a list of manually updated
descriptions for properties where there are none. This was a last resort
intended to provide the highest quality of completions possible.
Overtime many items on this list will likely be able to be removed just as
new ones are added. After running the update script you'll see a warning
saying how many properties are without completions that would then need to
be added to the JSON file.
"spec-shortname": {
"spec": {
"title": "",
"url": ""
},
"properties": [
{
"name": "",
"value": "",
"initial": "",
"appliesTo": "",
"percentages": "",
"computedValue": "",
"canonicalOrder": "",
"animationType": "",
"media": "",
"styleDeclaration": [ "", "", "" ]
}
],
"atrules": [
{
"name": "",
"descriptors": [
{
"name": "",
"for": "",
"value": "",
"type": ""
}
]
}
],
"selectors": [],
"values": [
{
"name": "",
"type": "",
"prose": "Optional description",
"value": "",
"values": [
{
"name": "",
"prose": "Optional Description",
"type": "",
"value": ""
}
]
}
],
"warnings": []
}
**Kind**: global constant
<a name="fs"></a>
## fs

View File

@ -14,6 +14,90 @@
<dl>
<dt><a href="#etch">etch</a></dt>
<dd></dd>
<dt><a href="#css">css</a></dt>
<dd><p>This file will manage the updating of <code>autocomplete-css</code> <code>completions.json</code>.
We will mainly utilize <code>@webref/css</code>.listAll() function that returns a full CSS
list of all properties seperated by their spec shortname. An example
of this format is defined below for ease of future modifications.</p>
<p> Some important notes about the data contained here:
- Often times the <code>value</code> within the <code>property</code> will be in the following format:
<code>&lt;valueGroupName&gt;</code> or even <code>&lt;valueGroupName&gt; | value | value2</code> or just <code>value | value2</code>
It will be important to build a parser that can handle this format.
The <code>&lt;valueGroupName&gt;</code> then can be realized via that specs <code>values</code> where
<code>values[x].name</code> will match the <code>&lt;valueGroupName&gt;</code>. Another important note about
handling values here is that oftentimes <code>values[x].values[]</code> won&#39;t actually
contain all possible values. And instead this must be handled by checking
<code>values[x].value</code> which is another string of <code>&lt;valueGroupName&gt; | value</code>.
So this should be handled by the same parser.
- Additionally an important note is that nowhere in this data do we get any kind
of description about the data that could lend a hand in being documentation.
So the documentation must be gathered seperatly. Likely the best way to collect
our documentation data is via <code>mdn/content</code>.
Within <code>content/files/en-us/web/css</code> is a directory of folders titled
by the name of properties.</p>
<pre><code>The last important thing to note here:
MDN doesn&#39;t have docs on everything. And that&#39;s a good thing. But it means
many of our items don&#39;t have any kind of description. For this situation
we have `manual-property-desc.json` which is a list of manually updated
descriptions for properties where there are none. This was a last resort
intended to provide the highest quality of completions possible.
Overtime many items on this list will likely be able to be removed just as
new ones are added. After running the update script you&#39;ll see a warning
saying how many properties are without completions that would then need to
be added to the JSON file.
</code></pre>
<p> &quot;spec-shortname&quot;: {
&quot;spec&quot;: {
&quot;title&quot;: &quot;&quot;,
&quot;url&quot;: &quot;&quot;
},
&quot;properties&quot;: [
{
&quot;name&quot;: &quot;&quot;,
&quot;value&quot;: &quot;&quot;,
&quot;initial&quot;: &quot;&quot;,
&quot;appliesTo&quot;: &quot;&quot;,
&quot;percentages&quot;: &quot;&quot;,
&quot;computedValue&quot;: &quot;&quot;,
&quot;canonicalOrder&quot;: &quot;&quot;,
&quot;animationType&quot;: &quot;&quot;,
&quot;media&quot;: &quot;&quot;,
&quot;styleDeclaration&quot;: [ &quot;&quot;, &quot;&quot;, &quot;&quot; ]
}
],
&quot;atrules&quot;: [
{
&quot;name&quot;: &quot;&quot;,
&quot;descriptors&quot;: [
{
&quot;name&quot;: &quot;&quot;,
&quot;for&quot;: &quot;&quot;,
&quot;value&quot;: &quot;&quot;,
&quot;type&quot;: &quot;&quot;
}
]
}
],
&quot;selectors&quot;: [],
&quot;values&quot;: [
{
&quot;name&quot;: &quot;&quot;,
&quot;type&quot;: &quot;&quot;,
&quot;prose&quot;: &quot;Optional description&quot;,
&quot;value&quot;: &quot;&quot;,
&quot;values&quot;: [
{
&quot;name&quot;: &quot;&quot;,
&quot;prose&quot;: &quot;Optional Description&quot;,
&quot;type&quot;: &quot;&quot;,
&quot;value&quot;: &quot;&quot;
}
]
}
],
&quot;warnings&quot;: []
}</p>
</dd>
<dt><a href="#fs">fs</a></dt>
<dd></dd>
<dt><a href="#dalek">dalek</a></dt>
@ -212,6 +296,95 @@ console.log(atom.clipboard.read());
## etch
**Kind**: global constant
**Jsx**: etch.dom
<a name="css"></a>
## css
This file will manage the updating of `autocomplete-css` `completions.json`.
We will mainly utilize `@webref/css`.listAll() function that returns a full CSS
list of all properties seperated by their spec shortname. An example
of this format is defined below for ease of future modifications.
Some important notes about the data contained here:
- Often times the `value` within the `property` will be in the following format:
`<valueGroupName>` or even `<valueGroupName> | value | value2` or just `value | value2`
It will be important to build a parser that can handle this format.
The `<valueGroupName>` then can be realized via that specs `values` where
`values[x].name` will match the `<valueGroupName>`. Another important note about
handling values here is that oftentimes `values[x].values[]` won't actually
contain all possible values. And instead this must be handled by checking
`values[x].value` which is another string of `<valueGroupName> | value`.
So this should be handled by the same parser.
- Additionally an important note is that nowhere in this data do we get any kind
of description about the data that could lend a hand in being documentation.
So the documentation must be gathered seperatly. Likely the best way to collect
our documentation data is via `mdn/content`.
Within `content/files/en-us/web/css` is a directory of folders titled
by the name of properties.
The last important thing to note here:
MDN doesn't have docs on everything. And that's a good thing. But it means
many of our items don't have any kind of description. For this situation
we have `manual-property-desc.json` which is a list of manually updated
descriptions for properties where there are none. This was a last resort
intended to provide the highest quality of completions possible.
Overtime many items on this list will likely be able to be removed just as
new ones are added. After running the update script you'll see a warning
saying how many properties are without completions that would then need to
be added to the JSON file.
"spec-shortname": {
"spec": {
"title": "",
"url": ""
},
"properties": [
{
"name": "",
"value": "",
"initial": "",
"appliesTo": "",
"percentages": "",
"computedValue": "",
"canonicalOrder": "",
"animationType": "",
"media": "",
"styleDeclaration": [ "", "", "" ]
}
],
"atrules": [
{
"name": "",
"descriptors": [
{
"name": "",
"for": "",
"value": "",
"type": ""
}
]
}
],
"selectors": [],
"values": [
{
"name": "",
"type": "",
"prose": "Optional description",
"value": "",
"values": [
{
"name": "",
"prose": "Optional Description",
"type": "",
"value": ""
}
]
}
],
"warnings": []
}
**Kind**: global constant
<a name="fs"></a>
## fs

View File

@ -2,7 +2,7 @@
"name": "pulsar",
"author": "Pulsar Community <noreply@pulsar-edit.com>",
"productName": "Pulsar",
"version": "1.102.0-dev",
"version": "1.103.0-dev",
"description": "A Community-led Hyper-Hackable Text Editor",
"branding": {
"id": "pulsar",
@ -70,7 +70,7 @@
"fuzzy-finder": "https://codeload.github.com/atom/fuzzy-finder/legacy.tar.gz/refs/tags/v1.14.3",
"git-diff": "file:packages/git-diff",
"git-utils": "5.7.1",
"github": "https://codeload.github.com/pulsar-edit/github/tar.gz/refs/tags/v0.36.14-pretranspiled-take-2",
"github": "https://codeload.github.com/pulsar-edit/github/tar.gz/refs/tags/v0.36.15-pretranspiled",
"glob": "^7.1.1",
"go-to-line": "file:packages/go-to-line",
"grammar-selector": "file:packages/grammar-selector",
@ -149,7 +149,7 @@
"service-hub": "^0.7.4",
"settings-view": "file:packages/settings-view",
"sinon": "9.2.1",
"snippets": "https://github.com/pulsar-edit/snippets.git#fe00fd6",
"snippets": "github:pulsar-edit/snippets#bb00f909c6c645b173f27346875d8fa0c7af09f7",
"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",

View File

@ -7,6 +7,4 @@ This is powered by the list of CSS property and values [here](https://github.com
![css-completions](https://cloud.githubusercontent.com/assets/671378/6357910/b9ecbe7c-bc1c-11e4-89b1-033e626c891f.gif)
You can update the prebuilt list of property names and values by running the `update.coffee` file at the root of the repository and then checking in the changed `properties.json` file.
`sorted-property-names.json` is updated manually - take a look at https://developer.microsoft.com/en-us/microsoft-edge/platform/usage/ and https://www.chromestatus.com/metrics/css/popularity for guidance.
You can update the prebuilt list of completions by running `node update.js` at the root of this package and checking for changes within `completions.json`. This does rely on having dev dependencies installed, so ensure you install all dependencies before doing so.

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -16,6 +16,8 @@
}
},
"devDependencies": {
"@webref/css": "^6.3.4",
"content": "github:mdn/content",
"request": "^2.53.0"
}
}

View File

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

View File

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

View File

@ -12,6 +12,15 @@ packagesToTest =
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"
@ -33,6 +42,12 @@ describe "CSS property name and value autocompletions", ->
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
@ -59,7 +74,7 @@ describe "CSS property name and value autocompletions", ->
editor.setCursorBufferPosition([0, 1])
completions = getCompletions()
expect(completions).toHaveLength 9
expect(completions.length).toBeGreaterThan 9 # #398
for completion in completions
expect(completion.text.length).toBeGreaterThan 0
expect(completion.type).toBe 'tag'
@ -72,7 +87,7 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([1, 0])
completions = getCompletions(activatedManually: true)
expect(completions.length).toBe 237
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'
@ -96,14 +111,15 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([1, 3])
completions = getCompletions()
expect(completions[0].text).toBe 'display: '
expect(completions[0].displayText).toBe 'display'
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].text).toBe 'direction: '
expect(completions[1].displayText).toBe 'direction'
expect(completions[1].type).toBe 'property'
expect(completions[1].replacementPrefix).toBe 'd'
@ -114,9 +130,10 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([1, 3])
completions = getCompletions()
expect(completions.length).toBe 2
expect(completions[0].text).toBe 'display: '
expect(completions[1].text).toBe 'direction: '
expect(completions.length).toBeGreaterThan 2 # #398
expect(isValueInCompletions('display: ', completions)).toBe true
expect(isValueInCompletions('direction: ', completions)).toBe true
expect(completions[1].replacementPrefix).toBe 'D'
editor.setText """
@ -126,8 +143,8 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([1, 3])
completions = getCompletions()
expect(completions[0].text).toBe 'display: '
expect(completions[1].text).toBe 'direction: '
expect(isValueInCompletions('display: ', completions)).toBe true
expect(isValueInCompletions('direction: ', completions)).toBe true
editor.setText """
body {
@ -136,8 +153,8 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([1, 6])
completions = getCompletions()
expect(completions[0].text).toBe 'border: '
expect(completions[0].displayText).toBe 'border'
expect(isValueInCompletions('border: ', completions)).toBe true
expect(completions[0].replacementPrefix).toBe 'bord'
it "does not autocomplete when at a terminator", ->
@ -198,7 +215,7 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([0, 6])
completions = getCompletions()
expect(completions[0].displayText).toBe 'width'
expect(isValueInCompletions('width: ', completions)).toBe true
editor.setText """
body {
@ -206,21 +223,21 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([1, 0])
completions = getCompletions()
expect(completions[0].displayText).toBe 'width'
expect(isValueInCompletions('width: ', completions)).toBe true
editor.setText """
body { }
"""
editor.setCursorBufferPosition([0, 6])
completions = getCompletions()
expect(completions[0].displayText).toBe 'width'
expect(isValueInCompletions('width: ', completions)).toBe true
editor.setText """
body { }
"""
editor.setCursorBufferPosition([0, 7])
completions = getCompletions()
expect(completions[0].displayText).toBe 'width'
expect(isValueInCompletions('width: ', completions)).toBe true
it "triggers autocomplete when an property name has been inserted", ->
spyOn(atom.commands, 'dispatch')
@ -242,7 +259,7 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([1, 10])
completions = getCompletions()
expect(completions.length).toBe 24
expect(completions.length).toBeGreaterThan 24 # #398
for completion in completions
expect(completion.text.length).toBeGreaterThan 0
expect(completion.description.length).toBeGreaterThan 0
@ -256,7 +273,7 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([2, 0])
completions = getCompletions()
expect(completions.length).toBe 24
expect(completions.length).toBeGreaterThan 24 # #398
for completion in completions
expect(completion.text.length).toBeGreaterThan 0
@ -268,14 +285,14 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([1, 12])
completions = getCompletions()
expect(completions[0].text).toBe 'inline;'
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
expect(completions[1].text).toBe 'inline-block;'
expect(completions[2].text).toBe 'inline-flex;'
expect(completions[3].text).toBe 'inline-grid;'
expect(completions[4].text).toBe 'inline-table;'
expect(completions[5].text).toBe 'inherit;'
editor.setText """
body {
@ -284,13 +301,13 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([1, 12])
completions = getCompletions()
expect(completions.length).toBe 6
expect(completions[0].text).toBe 'inline;'
expect(completions[1].text).toBe 'inline-block;'
expect(completions[2].text).toBe 'inline-flex;'
expect(completions[3].text).toBe 'inline-grid;'
expect(completions[4].text).toBe 'inline-table;'
expect(completions[5].text).toBe 'inherit;'
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 {
@ -300,12 +317,12 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([2, 5])
completions = getCompletions()
expect(completions[0].text).toBe 'inline;'
expect(completions[1].text).toBe 'inline-block;'
expect(completions[2].text).toBe 'inline-flex;'
expect(completions[3].text).toBe 'inline-grid;'
expect(completions[4].text).toBe 'inline-table;'
expect(completions[5].text).toBe 'inherit;'
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 {
@ -314,12 +331,12 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([1, 13])
completions = getCompletions()
expect(completions).toHaveLength 5
expect(completions[0].text).toBe 'center;'
expect(completions[1].text).toBe 'left;'
expect(completions[2].text).toBe 'justify;'
expect(completions[3].text).toBe 'right;'
expect(completions[4].text).toBe 'inherit;'
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 {
@ -356,8 +373,8 @@ describe "CSS property name and value autocompletions", ->
editor.setText "body { display: }"
editor.setCursorBufferPosition([0, 16])
completions = getCompletions()
expect(completions).toHaveLength 24
expect(completions[0].text).toBe 'block;'
expect(completions.length).toBeGreaterThan 24 # #398
expect(isValueInCompletions('block;', completions)).toBe true
editor.setText """
body {
@ -366,54 +383,54 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([1, 24])
completions = getCompletions()
expect(completions).toHaveLength 4
expect(completions[0].text).toBe 'left;'
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).toHaveLength 4
expect(completions[0].text).toBe 'left;'
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).toHaveLength 5
expect(completions[0].text).toBe 'line-through;'
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).toHaveLength 6
expect(completions[0].text).toBe 'inline;'
expect(completions[1].text).toBe 'inline-block;'
expect(completions[2].text).toBe 'inline-flex;'
expect(completions[3].text).toBe 'inline-grid;'
expect(completions[4].text).toBe 'inline-table;'
expect(completions[5].text).toBe 'inherit;'
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).toHaveLength 6
expect(completions[0].text).toBe 'inline;'
expect(completions[1].text).toBe 'inline-block;'
expect(completions[2].text).toBe 'inline-flex;'
expect(completions[3].text).toBe 'inline-grid;'
expect(completions[4].text).toBe 'inline-table;'
expect(completions[5].text).toBe 'inherit;'
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).toHaveLength 4
expect(completions[0].text).toBe 'left;'
expect(completions[1].text).toBe 'right;'
expect(completions[2].text).toBe 'none;'
expect(completions[3].text).toBe 'inherit;'
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 """
@ -453,11 +470,12 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([0, 2])
completions = getCompletions()
expect(completions.length).toBe 7
expect(completions[0].text).toBe 'canvas'
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).toBe 'Selector for <canvas> elements'
expect(completions[1].text).toBe 'code'
expect(completions[0].description.length).toBeGreaterThan 0
editor.setText """
canvas,ca {
@ -465,7 +483,7 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([0, 9])
completions = getCompletions()
expect(completions.length).toBe 7
expect(completions.length).toBeGreaterThan 7 # #398
expect(completions[0].text).toBe 'canvas'
editor.setText """
@ -474,7 +492,7 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([0, 9])
completions = getCompletions()
expect(completions.length).toBe 7
expect(completions.length).toBeGreaterThan 7 # #398
expect(completions[0].text).toBe 'canvas'
editor.setText """
@ -483,7 +501,7 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([0, 10])
completions = getCompletions()
expect(completions.length).toBe 7
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", ->
@ -526,7 +544,7 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([0, 5])
completions = getCompletions()
expect(completions.length).toBe 5
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
@ -539,7 +557,7 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([0, 7])
completions = getCompletions()
expect(completions.length).toBe 4
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
@ -571,9 +589,9 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([1, 4])
completions = getCompletions()
expect(completions[0].text).toBe 'display: '
expect(completions[1].text).toBe 'direction: '
expect(completions[2].text).toBe 'div'
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
@ -641,14 +659,14 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([1, 3])
completions = getCompletions()
expect(completions[0].text).toBe 'display: '
expect(completions[0].displayText).toBe 'display'
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].text).toBe 'direction: '
expect(completions[1].displayText).toBe 'direction'
expect(completions[1].type).toBe 'property'
expect(completions[1].replacementPrefix).toBe 'd'
@ -658,9 +676,10 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([1, 3])
completions = getCompletions()
expect(completions.length).toBe 11
expect(completions[0].text).toBe 'display: '
expect(completions[1].text).toBe 'direction: '
expect(completions.length).toBeGreaterThan 11 # #398
expect(isValueInCompletions('display: ', completions)).toBe true
expect(isValueInCompletions('direction: ', completions)).toBe true
expect(completions[1].replacementPrefix).toBe 'D'
editor.setText """
@ -669,8 +688,8 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([1, 3])
completions = getCompletions()
expect(completions[0].text).toBe 'display: '
expect(completions[1].text).toBe 'direction: '
expect(isValueInCompletions('display: ', completions)).toBe true
expect(isValueInCompletions('direction: ', completions)).toBe true
editor.setText """
body
@ -678,8 +697,8 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([1, 6])
completions = getCompletions()
expect(completions[0].text).toBe 'border: '
expect(completions[0].displayText).toBe 'border'
expect(isValueInCompletions('border: ', completions)).toBe true
expect(completions[0].replacementPrefix).toBe 'bord'
it "triggers autocomplete when an property name has been inserted", ->
@ -701,7 +720,7 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([1, 10])
completions = getCompletions()
expect(completions.length).toBe 24
expect(completions.length).toBeGreaterThan 24 # #398
for completion in completions
expect(completion.text.length).toBeGreaterThan 0
expect(completion.description.length).toBeGreaterThan 0
@ -713,7 +732,7 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([2, 0])
completions = getCompletions()
expect(completions.length).toBe 24
expect(completions.length).toBeGreaterThan 24 # #398
for completion in completions
expect(completion.text.length).toBeGreaterThan 0
@ -724,14 +743,16 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([1, 12])
completions = getCompletions()
expect(completions[0].text).toBe 'inline'
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
expect(completions[1].text).toBe 'inline-block'
expect(completions[2].text).toBe 'inline-flex'
expect(completions[3].text).toBe 'inline-grid'
expect(completions[4].text).toBe 'inline-table'
expect(completions[5].text).toBe 'inherit'
editor.setText """
body
@ -739,13 +760,14 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([1, 12])
completions = getCompletions()
expect(completions.length).toBe 6
expect(completions[0].text).toBe 'inline'
expect(completions[1].text).toBe 'inline-block'
expect(completions[2].text).toBe 'inline-flex'
expect(completions[3].text).toBe 'inline-grid'
expect(completions[4].text).toBe 'inline-table'
expect(completions[5].text).toBe 'inherit'
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 """
@ -818,12 +840,11 @@ describe "CSS property name and value autocompletions", ->
completions = getCompletions(activatedManually: false)
expect(completions).not.toBe null
expect(completions[0].text).toBe 'border: '
expect(completions[0].displayText).toBe 'border'
expect(isValueInCompletions('border: ', completions)).toBe true
expect(isValueInCompletions('border-radius: ', completions)).toBe true
expect(completions[0].replacementPrefix).toBe 'border-'
expect(completions[1].text).toBe 'border-radius: '
expect(completions[1].displayText).toBe 'border-radius'
expect(completions[1].replacementPrefix).toBe 'border-'
it "does not autocomplete !important in property-name scope", ->
@ -848,18 +869,19 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([0, 2])
completions = getCompletions()
expect(completions.length).toBe 7
expect(completions[0].text).toBe 'canvas'
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).toBe 'Selector for <canvas> elements'
expect(completions[1].text).toBe 'code'
expect(completions[0].description.length).toBeGreaterThan 0
editor.setText """
canvas,ca
"""
editor.setCursorBufferPosition([0, 9])
completions = getCompletions()
expect(completions.length).toBe 7
expect(completions.length).toBeGreaterThan 7 # #398
expect(completions[0].text).toBe 'canvas'
editor.setText """
@ -867,7 +889,7 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([0, 9])
completions = getCompletions()
expect(completions.length).toBe 7
expect(completions.length).toBeGreaterThan 7 # #398
expect(completions[0].text).toBe 'canvas'
editor.setText """
@ -875,7 +897,7 @@ describe "CSS property name and value autocompletions", ->
"""
editor.setCursorBufferPosition([0, 10])
completions = getCompletions()
expect(completions.length).toBe 7
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", ->

View File

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

View File

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

View File

@ -75,6 +75,13 @@ module.exports = {
statusView.initialize(statusBar, packageManager, updates)
}
})
// Attach a settings button to the status bar
if (atom.config.get("settings-view.showSettingsIconInStatusBar")) {
const SettingsIconStatusView = require('./settings-icon-status-view')
statusViewIcon = new SettingsIconStatusView(statusBar)
statusViewIcon.attach()
}
},
consumeSnippets (snippets) {

View File

@ -0,0 +1,12 @@
const _ = require("underscore-plus")
module.exports = {
getSettingTitle (keyPath, name) {
if (name == null) {
name = ''
}
const schema = atom.config.getSchema(keyPath)
const title = schema != null ? schema.title : null
return title || _.uncamelcase(name).split('.').map(_.capitalize).join(' ')
}
}

View File

@ -0,0 +1,104 @@
/** @babel */
/** @jsx etch.dom */
import etch from 'etch'
import { shell } from 'electron'
import { Disposable, CompositeDisposable } from 'atom'
import { getSettingTitle } from './rich-title'
export default class SearchSettingView {
constructor(setting, settingsView) {
this.settingsView = settingsView
this.setting = setting
this.disposables = new CompositeDisposable()
etch.initialize(this)
this.handleButtonEvents()
}
render () {
const title = this.setting.title ?? getSettingTitle(this.setting.path, this.setting.path.split(".")[1]);
const path = atom.config.get("settings-view.searchSettingsMetadata") ? this.setting.path + ": " : "";
const description = this.setting.description ?? "";
const packageName = this.setting.path.split(".")[0];
const icon = this.getIcon(packageName);
const score = atom.config.get("settings-view.searchSettingsMetadata") ? this.setting.rank.totalScore.toFixed(2) + " Search Score" : "";
return (
<div className='search-result col-lg-8'>
<span className='search-package-name pull-right'>
<span className={icon}></span>
{packageName}
</span>
<div className='body'>
<h4 className='card-name'>
<a ref='settingLink'>
<span className='search-name'>{title}</span>
<span className='search-id'>{path}{score}</span>
</a>
</h4>
<span className='search-description'>{description}</span>
</div>
</div>
)
}
update () {}
destroy () {
this.disposables.dispose()
return etch.destroy(this)
}
getIcon(namespace) {
// Takes a setting namespace and returns the appropriate icon for it.
switch(namespace) {
case "core":
return "icon icon-settings search-result-icon";
break;
case "editor":
return "icon icon-code search-result-icon";
break;
default:
return "icon icon-package search-result-icon";
break;
}
}
handleButtonEvents () {
const settingsClickHandler = (event) => {
event.stopPropagation()
// Lets check if the setting we want to open is built in or from a package
const settingLocation = this.setting.path.split(".")[0]
// The above is the location where the setting exists, such as Core, or a packages name
switch(settingLocation) {
case "core":
// There are some special cases of settings broken off into other panels
let settingName = this.setting.path.split(".")[1]
if (settingName === 'uriHandlerRegistration') {
// the URI handler doesn't have any registered uri to actually reach it
// funnily enough. So we will prompt a notification to go there
atom.notifications.addInfo("Sorry, Pulsar is unable to link to this setting. Please select 'URI Handling' on the sidebar.")
} else {
atom.workspace.open("atom://config/core")
}
break;
case "editor":
atom.workspace.open("atom://config/editor")
break;
default:
// The handling for any packages name
atom.workspace.open(`atom://config/packages/${settingLocation}`)
break;
}
}
this.refs.settingLink.addEventListener('click', settingsClickHandler)
this.disposables.add(new Disposable(() => { this.refs.settingLink.removeEventListener('click', settingsClickHandler) }))
}
}

View File

@ -0,0 +1,297 @@
/** @babel */
/** @jsx etch.dom */
import { TextEditor, CompositeDisposable } from 'atom'
import etch from 'etch'
import CollapsibleSectionPanel from './collapsible-section-panel'
import SearchSettingView from './search-setting-view'
export default class SearchSettingsPanel extends CollapsibleSectionPanel {
constructor(settingsView) {
super()
etch.initialize(this)
this.settingsView = settingsView
this.searchResults = []
// Get all available settings
this.settingsSchema = atom.config.schema.properties;
this.subscriptions = new CompositeDisposable()
this.subscriptions.add(this.handleEvents())
this.subscriptions.add(atom.commands.add(this.element, {
'core:move-up': () => { this.scrollUp() },
'core:move-down': () => { this.scrollDown() },
'core:page-up': () => { this.pageUp() },
'core:page-down': () => { this.pageDown() },
'core:move-to-top': () => { this.scrollToTop() },
'core:move-to-bottom': () => { this.scrollToBottom() }
}))
this.subscriptions.add(
this.refs.searchEditor.onDidStopChanging(() => { this.matchSettings() })
)
}
focus () {
this.refs.searchEditor.element.focus()
}
show () {
this.element.style.display = ''
// Don't show the loading for search results as soon as page appears
this.refs.loadingArea.style.display = 'none'
}
destroy () {
this.subscriptions.dispose()
return etch.destroy(this)
}
update () {}
render () {
return (
<div className='panels-item' tabIndex='-1'>
<section className='section'>
<div className='section-container'>
<div className='section-heading icon icon-search-save'>
Search Pulsar's Settings
</div>
<div className='alert alert-warning icon icon-info'>
This feature is experimental.<br />
If you have feedback/suggestions, or you encounter any issues, feel free to report them here:&nbsp;
<a href="https://github.com/orgs/pulsar-edit/discussions/150" style="text-decoration: underline;">
https://github.com/orgs/pulsar-edit/discussions/150
</a>
</div>
<div className='editor-container'>
<TextEditor ref='searchEditor' mini placeholderText='Start Searching for Settings' />
</div>
<section className='sub-section search-results'>
<h3 ref='searchHeader' className='sub-section-heading icon icon-package'>
Search Results
</h3>
<div ref='searchResults' className='container package-container'>
<div ref='loadingArea' className='alert alert-info loading-area icon icon-hourglass'>
Loading Results...
</div>
</div>
</section>
</div>
</section>
</div>
)
}
matchSettings () {
// this is called after the user types.
// So lets show our loading message after removing any previous results
this.clearSearchResults()
this.refs.loadingArea.style.display = ''
this.filterSettings(this.refs.searchEditor.getText())
}
clearSearchResults () {
for (let i = 0; i < this.searchResults.length; i++) {
this.searchResults[i].destroy()
}
this.searchResults = []
}
filterSettings (text) {
let rankedResults = [];
let searchTerm = text;
let namedFilter;
let useFilter = false;
// Now we will check if the user is filtering any results
if (text.startsWith("core: ")) {
searchTerm = text.replace("core: ", "");
namedFilter = "core";
useFilter = true;
}
if (text.startsWith("editor: ")) {
searchTerm = text.replace("editor: ", "");
namedFilter = "editor";
useFilter = true;
}
for (const setting in this.settingsSchema) {
// The top level item should always be an object, but just in case we will check.
// If the top level item returned is not an object it will NOT be listed
if (useFilter) {
if (namedFilter !== setting) {
continue;
// We use this so that we can break out of our current loop iteration
// when using a filter that doesn't match the current namespace.
// But otherwise will process the settings when no filter is set
// or when the filter set matches our current namespace.
// This helps avoid processing any namespace that doesn't match our filter
// or process all of them as default.
}
}
if (this.settingsSchema[setting].type === "object") {
for (const item in this.settingsSchema[setting].properties) {
let schema = this.settingsSchema[setting].properties[item];
schema.rank = this.generateRanks(text, schema.title, schema.description, setting, item)
schema.path = `${setting}.${item}`
rankedResults.push(schema)
}
}
}
this.processRanks(rankedResults)
}
handleSettingsString (string) {
return string?.toLowerCase() ?? "";
}
generateRanks (searchText, title, description, settingName, settingItem) {
// In charge of generating each setting entry's rank
let rankedTitle = this.getScore(this.handleSettingsString(searchText), this.handleSettingsString(title))
let rankedDescription = this.getScore(this.handleSettingsString(searchText), this.handleSettingsString(description))
let rankedSettingName = this.getScore(this.handleSettingsString(searchText), this.handleSettingsString(settingName))
let rankedSettingItem = this.getScore(this.handleSettingsString(searchText), this.handleSettingsString(settingItem))
let rank = {
title: rankedTitle,
description: rankedDescription,
settingName: rankedSettingName,
settingItem: rankedSettingItem
};
// Now to calculate the total score of the search resutls.
// The total score will be a sume of all individual scores,
// with weighted bonus' for higher matches depending on where the match was
let titleBonus = (rank.title.score > 0.8) ? 0.2 : 0;
let perfectTitleBonus = (rank.title.score === 1) ? 0.2 : 0;
let descriptionBonus = (rank.description.score > 0.5) ? 0.1 : 0;
let perfectDescriptionBonus = (rank.title.score === 1) ? 0.1 : 0;
let settingNameBonus = (rank.settingName.score > 0.8) ? 0.2 : 0;
let perfectSettingNameBonus = (rank.settingName.score === 1) ? 0.3 : 0;
let settingItemBonus = (rank.settingItem.score > 0.8) ? 0.2 : 0;
let perfectSettingItemBonus = (rank.settingItem.score === 1) ? 0.1 : 0;
let totalScore =
rank.title.score + titleBonus + perfectTitleBonus
+ rank.description.score + descriptionBonus + perfectDescriptionBonus
+ rank.settingName.score + settingNameBonus + perfectSettingNameBonus
+ rank.settingItem.score + settingItemBonus + perfectSettingItemBonus;
rank.totalScore = totalScore;
return rank;
}
processRanks (ranks) {
// Gets an array of schemas with ranks included
// Removes any scores below a specific limit
let filteredRanks = ranks.filter(item => item.rank.totalScore > atom.config.get("settings-view.searchSettingsMinimumScore"));
// Sorts the array from highest score to lowest score
filteredRanks.sort((a, b) => {
if (a.rank.totalScore < b.rank.totalScore) {
return 1;
}
if (a.rank.totalScore > b.rank.totalScore) {
return -1;
}
return 0;
});
// Remove our loading symbol
this.refs.loadingArea.style.display = 'none'
for (const setting of filteredRanks) {
let searchView = new SearchSettingView(setting, this.settingsView)
this.refs.searchResults.appendChild(searchView.element)
this.searchResults.push(searchView)
}
}
getScore (s1, s2) {
// s1 is the text we are calculating the score against
// s2 is the text the user typed
// Below is an exact implmentation of Longest Common Subsequence
let height = s1.length + 1;
let width = s2.length + 1;
let matrix = Array(height)
.fill(0)
.map(() => Array(width).fill(0));
for (let row = 1; row < height; row++) {
for (let col = 1; col < width; col++) {
if (s1[row - 1] == s2[col - 1]) {
matrix[row][col] = matrix[row - 1][col - 1] + 1;
} else {
matrix[row][col] = Math.max(matrix[row][col - 1], matrix[row - 1][col]);
}
}
}
let longest = this.lcsTraceback(matrix, s1, s2, height, width);
// Now longest is a literal string of the longest common subsequence.
// We will now assign a score to help ranking, but will still return the
// text sequence, in case we want to use that for display purposes
return {
score: longest.length / s1.length,
sequence: longest
};
}
lcsTraceback (matrix, s1, s2, height, width) {
if (height === 0 || width === 0) {
return "";
}
if (s1[height - 1] == s2[width - 1]) {
return (
this.lcsTraceback(matrix, s1, s2, height - 1, width - 1) +
(s1[height - 1] ? s1[height - 1] : "")
);
}
if (matrix[height][width - 1] > matrix[height - 1][width]) {
return this.lcsTraceback(matrix, s1, s2, height, width - 1);
}
return this.lcsTraceback(matrix, s1, s2, height - 1, width);
}
// Boiler Plate Functions
scrollUp () {
this.element.scrollTop -= document.body.offsetHeight / 20
}
scrollDown () {
this.element.scrollTop += document.body.offsetHeight / 20
}
pageUp () {
this.element.scrollTop -= this.element.offsetHeight
}
pageDown () {
this.element.scrollTop += this.element.offsetHeight
}
scrollToTop () {
this.element.scrollTop = 0
}
scrollToBottom () {
this.element.scrollTop = this.element.scrollHeight
}
}

View File

@ -0,0 +1,40 @@
/** @babel */
import {Disposable, CompositeDisposable} from 'atom'
export default class SettingsIconStatusView {
constructor(statusBar) {
this.statusBar = statusBar
this.disposables = new CompositeDisposable()
this.element = document.createElement('div')
this.element.classList.add('settings-icon', 'inline-block')
const iconPackage = document.createElement('span')
iconPackage.classList.add('icon', 'icon-gear')
this.element.appendChild(iconPackage)
const clickHandler = () => {
atom.workspace.open("atom://config")
}
this.element.addEventListener('click', clickHandler)
this.disposables.add(new Disposable(() => { this.element.removeEventListener('click', clickHandler) }))
}
attach () {
this.tile = this.statusBar.addRightTile({
item: this,
priority: -99
})
}
destroy () {
this.disposables.dispose()
this.element.remove()
if (this.tile) {
this.tile.destroy()
this.tile = null
}
}
}

View File

@ -4,6 +4,7 @@ import {CompositeDisposable, Disposable, TextEditor} from 'atom'
import _ from 'underscore-plus'
import CollapsibleSectionPanel from './collapsible-section-panel'
import {getSettingDescription} from './rich-description'
import {getSettingTitle} from './rich-title'
const SCOPED_SETTINGS = [
'autoIndent',
@ -420,15 +421,6 @@ function elementForSetting (namespace, name, value) {
return controlGroup
}
function getSettingTitle (keyPath, name) {
if (name == null) {
name = ''
}
const schema = atom.config.getSchema(keyPath)
const title = schema != null ? schema.title : null
return title || _.uncamelcase(name).split('.').map(_.capitalize).join(' ')
}
function elementForOptions (namespace, name, value, {radio = false}) {
let keyPath = `${namespace}.${name}`
let schema = atom.config.getSchema(keyPath)

View File

@ -15,6 +15,7 @@ import ThemesPanel from './themes-panel'
import InstalledPackagesPanel from './installed-packages-panel'
import UpdatesPanel from './updates-panel'
import UriHandlerPanel from './uri-handler-panel'
import SearchSettingsPanel from './search-settings-panel'
export default class SettingsView {
constructor ({uri, packageManager, snippetsProvider, activePanel} = {}) {
@ -120,6 +121,10 @@ export default class SettingsView {
this.refs.openDotAtom.addEventListener('click', openDotAtomClickHandler)
this.disposables.add(new Disposable(() => this.refs.openDotAtom.removeEventListener('click', openDotAtomClickHandler)))
if (atom.config.get("settings-view.enableSettingsSearch")) {
this.addCorePanel('Search', 'search', () => new SearchSettingsPanel(this))
}
this.addCorePanel('Core', 'settings', () => new GeneralPanel())
this.addCorePanel('Editor', 'code', () => new EditorPanel())
if (atom.config.getSchema('core.uriHandlerRegistration').type !== 'any') {

View File

@ -14,6 +14,30 @@
"description": "Limit how many processes run simultaneously during package updates. If your machine slows down while updating many packages at once, set this value to a small positive number (e.g., `1` or `2`).",
"type": "integer",
"default": -1
},
"searchSettingsMinimumScore": {
"title": "Search Settings Minimum Score to Display Results",
"description": "Set the minimum similarity score required for a setting to appear in the search results, when searching for settings.",
"type": "integer",
"default": 2
},
"searchSettingsMetadata": {
"title": "Display Search Settings Metadata along with Search Results",
"description": "Whether or not to display search metadata with the search results. Metadata will appear as `{namespace}.{settingName}:{searchScore}`.",
"type": "boolean",
"default": false
},
"enableSettingsSearch": {
"title": "Enable Experimental Settings Search Feature",
"description": "Will enable or disable the new Experimental Settings Search.",
"type": "boolean",
"default": true
},
"showSettingsIconInStatusBar": {
"title": "Show Settings Icon in Status Bar",
"description": "Whether or not to show a settings icon in the Pulsar Status Bar.",
"type": "boolean",
"default": true
}
},
"dependencies": {

View File

@ -0,0 +1,87 @@
const SearchSettingsPanel = require('../lib/search-settings-panel');
describe("SearchSettingsPanel", () => {
describe("handleSettingsString", () => {
let searchSettingsPanel = null;
beforeEach(() => {
searchSettingsPanel = new SearchSettingsPanel(null);
});
it("Is able to handle null values", () => {
let string = searchSettingsPanel.handleSettingsString(null);
expect(string).toBe("");
});
it("Is able to convert upercase to lowercase", () => {
let string = searchSettingsPanel.handleSettingsString("Hello World");
expect(string).toBe("hello world");
});
it("Does nothing to an already lowercase string", () => {
let string = searchSettingsPanel.handleSettingsString("hello world");
expect(string).toBe("hello world");
});
});
describe("getScore", () => {
let searchSettingsPanel = null;
beforeEach(() => {
searchSettingsPanel = new SearchSettingsPanel(null);
});
it("Returns a properly structured object", () => {
let obj = searchSettingsPanel.getScore("hello world", "hello");
expect(typeof obj === "object").toBe(true);
expect(typeof obj.score === "number").toBe(true);
expect(typeof obj.sequence === "string").toBe(true);
});
it("Returns the expected sequence", () => {
let obj = searchSettingsPanel.getScore("hello world", "hello");
expect(obj.sequence).toBe("hello");
});
it("Returns the expected score", () => {
let obj = searchSettingsPanel.getScore("bank", "ba");
expect(obj.score).toBe(0.5)
});
});
describe("generateRanks", () => {
let searchSettingsPanel = null;
beforeEach(() => {
searchSettingsPanel = new SearchSettingsPanel(null);
});
it("Returns a properly structured object", () => {
let obj = searchSettingsPanel.generateRanks(
"tab",
"Tab Setting",
"Just a friendly tab setting",
"tabs",
"tabSetting"
);
expect(typeof obj.title === "object").toBe(true);
expect(typeof obj.title.score === "number").toBe(true);
expect(typeof obj.title.sequence === "string").toBe(true);
expect(typeof obj.description === "object").toBe(true);
expect(typeof obj.description.score === "number").toBe(true);
expect(typeof obj.description.sequence === "string").toBe(true);
expect(typeof obj.settingName === "object").toBe(true);
expect(typeof obj.settingName.score === "number").toBe(true);
expect(typeof obj.settingName.sequence === "string").toBe(true);
expect(typeof obj.settingItem === "object").toBe(true);
expect(typeof obj.settingItem.score === "number").toBe(true);
expect(typeof obj.settingItem.sequence === "string").toBe(true);
expect(typeof obj.totalScore === "number").toBe(true);
});
});
});

View File

@ -103,7 +103,7 @@ describe "SettingsView", ->
expect(settingsView.refs.panelMenu.querySelector('li[name="Panel 1"]')).toExist()
expect(settingsView.refs.panelMenu.querySelector('li[name="Panel 2"]')).toExist()
expect(settingsView.refs.panelMenu.children[0]).toHaveClass 'active'
expect(settingsView.refs.panelMenu.children[1]).toHaveClass 'active'
jasmine.attachToDOM(settingsView.element)
settingsView.refs.panelMenu.querySelector('li[name="Panel 1"] a').click()

View File

@ -0,0 +1,95 @@
@import "octicon-mixins";
@import "ui-variables";
@import "variables";
.settings-view {
.search-result {
padding: @component-padding*1.5;
margin-bottom: @component-padding;
list-style-type: none;
font-size: 1.2em;
border-radius: @component-border-radius*2;
border: 1px solid @base-border-color;
background-color: @package-card-background-color;
overflow: hidden;
-webkit-user-select: none;
&:hover {
background-color: contrast(@package-card-background-color, darken(@package-card-background-color, 2%), lighten(@package-card-background-color, 2%));
}
&:active {
background-color: @package-card-background-color;
}
&.disabled {
background-color: @package-card-disabled-background-color;
.body,
.avatar,
.author,
.stats {
opacity: .5;
}
}
&.col-lg-4 {
min-width: 190px;
padding: @component-padding 0;
padding-left: @component-padding;
padding-right: @component-padding;
}
.search-name {
margin-right: .8em;
font-weight: bolder;
color: @text-color-highlight;
cursor: pointer;
}
.search-id {
font-size: .8em;
margin-right: @component-padding;
display: block; /* Force newline on item to avoid complex line handling */
opacity: 0.5;
}
.search-description {
color: @text-color;
overflow: hidden;
min-height: 38px;
max-height: 38px;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2; /* number of lines to show */
-webkit-box-orient: vertical;
}
.card-name {
font-weight: 300;
margin: 0 0 .2em 0;
font-size: 1.2em;
line-height: 1.4;
.css-truncate-target {
color: $greenDark;
}
}
.body {
.css-truncate-target {
max-width: 100%;
}
}
.search-package-name {
margin-right: .2em;
color: @text-color-highlight;
font-size: 1.2em;
line-height: 1.4;
}
.search-result-icon {
margin-left: .2em;
}
}
}

View File

@ -53,40 +53,19 @@ export default class ChangeLogView {
<p>Feel free to read our <a href="https://github.com/pulsar-edit/pulsar/blob/master/CHANGELOG.md">Full Change Log</a>.</p>
<ul>
<li>
Signed Pulsar macOS Binaries
Added a new feature to Search for Pulsar's settings
</li>
<li>
Fixed a bug where `pulsar` on Windows could never trigger
Updated the completions provided by `autocomplete-css` to be as bleeding edge as possible.
</li>
<li>
Fixed `github` package shelling out to `git` on macOS
Updated the instructions and look of the login flow for the `github` package.
</li>
<li>
Fixed minor bugs found during fixes to tests
Snippet transformations no longer have an implied global flag, bringing them into compatibility with snippets in most other editors.
</li>
<li>
Improved our testing infastructure to aide in finding and fixing further bugs
</li>
<li>
Updated many dependencies of Pulsar and its core packages
</li>
<li>
New Pulsar Icon on macOS
</li>
<li>
Selected text is styled by default
</li>
<li>
Restored `right-clicked` CSS class on tags
</li>
<li>
Fixed syntax highlighting on C++
</li>
<li>
Updated JavaScript snippets to modern ES6 syntax
</li>
<li>
PPM no longer assumes `master` for git branches
Snippets can now be given command names instead of tab triggers, and thus can now be assigned to key shortcuts in `keymap.cson`.
</li>
</ul>

2
ppm

@ -1 +1 @@
Subproject commit 9af239277180f2a9ee9e86714f73fb2c33b1e66b
Subproject commit 915cbf6e5f9ea1141ef5dcaf86343237e17bf380

View File

@ -4965,9 +4965,9 @@ github-from-package@0.0.0:
resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce"
integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==
"github@https://codeload.github.com/pulsar-edit/github/tar.gz/refs/tags/v0.36.14-pretranspiled-take-2":
version "0.36.14"
resolved "https://codeload.github.com/pulsar-edit/github/tar.gz/refs/tags/v0.36.14-pretranspiled-take-2#22158525f8801ecbb084e23ea45ee92ba3d3f9e1"
"github@https://codeload.github.com/pulsar-edit/github/tar.gz/refs/tags/v0.36.15-pretranspiled":
version "0.36.15"
resolved "https://codeload.github.com/pulsar-edit/github/tar.gz/refs/tags/v0.36.15-pretranspiled#6d8f680fb7f337c3ddf0127fe24b9cc6e77a7618"
dependencies:
"@atom/babel-plugin-chai-assert-async" "1.0.0"
"@atom/babel7-transpiler" "1.0.0-1"
@ -8694,9 +8694,9 @@ smart-buffer@^4.0.2, smart-buffer@^4.2.0:
resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==
"snippets@https://github.com/pulsar-edit/snippets.git#fe00fd6":
version "1.6.1"
resolved "https://github.com/pulsar-edit/snippets.git#fe00fd6933fa33f819d14cdd6938d538d25ba1dd"
"snippets@github:pulsar-edit/snippets#bb00f909c6c645b173f27346875d8fa0c7af09f7":
version "1.7.0"
resolved "https://codeload.github.com/pulsar-edit/snippets/tar.gz/bb00f909c6c645b173f27346875d8fa0c7af09f7"
dependencies:
async "~0.2.6"
atom-select-list "^0.7.0"